def populate_with_cash(holdings_effective_date, initial_cash_balance, analyst_scope_code, transaction_portfolio_code, api_factory): # Create a holding adjustment to set our initial cash balance holding_adjustment = [ models.AdjustHoldingRequest( instrument_identifiers={'Instrument/default/Currency': 'GBP'}, tax_lots=[ models.TargetTaxLotRequest(units=initial_cash_balance, cost=models.CurrencyAndAmount( amount=initial_cash_balance, currency='GBP'), portfolio_cost=initial_cash_balance, price=1) ]) ] # Call LUSID to set our initial cash balance set_holdings_response = api_factory.build( lusid.api.TransactionPortfoliosApi).set_holdings( scope=analyst_scope_code, code=transaction_portfolio_code, effective_at=holdings_effective_date, adjust_holding_request=holding_adjustment) # Pretty print our response from LUSID prettyprint.set_holdings_response(set_holdings_response, analyst_scope_code, transaction_portfolio_code)
def build_cash_funds_in_adjust_holdings_request(self, currency, units): return models.AdjustHoldingRequest( instrument_identifiers={ TestDataUtilities.lusid_cash_identifier: currency }, tax_lots=[ models.TargetTaxLotRequest(units=units, price=None, cost=None, portfolio_cost=None, purchase_date=None, settlement_date=None) ])
def build_adjust_holdings_request(self, instrument_id, units, price, currency, trade_date): return models.AdjustHoldingRequest( instrument_identifiers={ TestDataUtilities.lusid_luid_identifier: instrument_id }, tax_lots=[ models.TargetTaxLotRequest(units=units, price=price, cost=models.CurrencyAndAmount(amount=price * units, currency=currency), portfolio_cost=price * units, purchase_date=trade_date, settlement_date=trade_date) ])
def setup_index(analyst_scope_code, reference_portfolio_code, instrument_prices, api_factory): # Set an arbitary index level to start our index with index_level = 1000 # Call LUSID - get the constituents of our index from our reference portfolio constituents = api_factory.build( lusid.api.ReferencePortfolioApi).get_reference_portfolio_constituents( scope=analyst_scope_code, code=reference_portfolio_code, effective_at=datetime.now(pytz.UTC)) # Initialise our list to hold the adjustments we need to make to our index to set it up index_setup = [] # Get our weights from the constituents into a better format to work with weights = { constituent.instrument_uid: constituent.weight for constituent in constituents.constituents } # Iterate over our pricing analytics for index, instrument in instrument_prices.iterrows(): # Get our Lusid Instrument ID Luid = api_factory.build(lusid.api.InstrumentsApi).get_instrument( identifier_type='Figi', identifier=instrument['figi']).lusid_instrument_id # Get the initial price for each constituent of the index from our analytics store inception_price = instrument['price_original'] # Work out how much of the index this constituent should make up using its w index_cost = weights[Luid] * index_level # Work out how many units we should therefore buy index_units = index_cost / inception_price # Create our request for this instrument index_setup.append( models.AdjustHoldingRequest(instrument_identifiers={ 'Instrument/default/Figi': instrument['figi'] }, tax_lots=[ models.TargetTaxLotRequest( units=index_units, cost=models.CurrencyAndAmount( amount=index_cost, currency='GBP'), portfolio_cost=index_cost, price=inception_price) ])) return index_setup
def test_set_target_holdings(self): currency = "GBP" day1 = datetime(2018, 1, 1, tzinfo=pytz.utc) day2 = datetime(2018, 1, 5, tzinfo=pytz.utc) portfolio_code = self.test_data_utilities.create_transaction_portfolio( TestDataUtilities.tutorials_scope) instrument1 = self.instrument_ids[0] instrument2 = self.instrument_ids[1] instrument3 = self.instrument_ids[2] holdings_adjustments = [ # cash balance models.AdjustHoldingRequest( instrument_identifiers={ TestDataUtilities.lusid_cash_identifier: currency }, tax_lots=[models.TargetTaxLotRequest(units=100000.0)]), # instrument 1 models.AdjustHoldingRequest(instrument_identifiers={ TestDataUtilities.lusid_luid_identifier: instrument1 }, tax_lots=[ models.TargetTaxLotRequest( units=100.0, price=101.0, cost=models.CurrencyAndAmount( amount=10100.0, currency=currency), portfolio_cost=10100.0, purchase_date=day1, settlement_date=day1) ]), # instrument 2 models.AdjustHoldingRequest(instrument_identifiers={ TestDataUtilities.lusid_luid_identifier: instrument2 }, tax_lots=[ models.TargetTaxLotRequest( units=100.0, price=102.0, cost=models.CurrencyAndAmount( amount=10200.0, currency=currency), portfolio_cost=10200.0, purchase_date=day1, settlement_date=day1) ]) ] # set the initial holdings on day 1 self.transaction_portfolios_api.set_holdings( scope=TestDataUtilities.tutorials_scope, code=portfolio_code, adjust_holding_request=holdings_adjustments, effective_at=day1) # add subsequent transactions on day 2 transactions = [ self.test_data_utilities.build_transaction_request( instrument_id=instrument1, units=100.0, price=104.0, currency=currency, trade_date=day2, transaction_type="Buy"), self.test_data_utilities.build_transaction_request( instrument_id=instrument3, units=100.0, price=103.0, currency=currency, trade_date=day2, transaction_type="Buy") ] self.transaction_portfolios_api.upsert_transactions( scope=TestDataUtilities.tutorials_scope, code=portfolio_code, transaction_request=transactions) # get the holdings for day 2 holdings = self.transaction_portfolios_api.get_holdings( scope=TestDataUtilities.tutorials_scope, code=portfolio_code, effective_at=day2) # sort to put the cash instrument first holdings.values.sort(key=lambda i: i.instrument_uid) # cash balance + 3 holdings self.assertEqual(len(holdings.values), 4) # remaining cash balance which takes into account the purchase transactions on day 2 # the call to GetHoldings returns the LUID not the identifier we created currency_luid = "CCY_{}".format(currency) # cash self.assertEqual(holdings.values[0].instrument_uid, currency_luid) self.assertEqual(holdings.values[0].units, 79300.0) # instrument 1 - initial holding + transaction on day 2 self.assertEqual(holdings.values[1].instrument_uid, instrument1) self.assertEqual(holdings.values[1].units, 200.0) self.assertEqual(holdings.values[1].cost.amount, 20500.0) # instrument 2 - initial holding self.assertEqual(holdings.values[2].instrument_uid, instrument2) self.assertEqual(holdings.values[2].units, 100.0) self.assertEqual(holdings.values[2].cost.amount, 10200.0) # instrument 3 - transaction on day 2 self.assertEqual(holdings.values[3].instrument_uid, instrument3) self.assertEqual(holdings.values[3].units, 100.0) self.assertEqual(holdings.values[3].cost.amount, 10300.0)
def load_holdings(client, scope, code, data_frame, holdings_mapping_required, holdings_mapping_optional): """ This function sets the holdings for a given portfolio from a set of holdings :param LusidApi client: The LusidApi client to use :param str scope: The scope of the portfolio to upsert the holdings into :param str code: The code of the portfolio to upsert the holdings into :param Pandas DataFrame data_frame: The dataframe containing the data :param dict{str, str} holdings_mapping: The mapping of the fields from the dataframe to LUSID :return: response models.AdjustHolding: The response from LUSID after setting the holdings """ # Initialise a list to hold the requests holding_adjustments = [] # Create a Pandas Series with the column names and data types less the resolved details for property generation dtypes = data_frame.drop( ['resolvable', 'foundWith', 'LusidInstrumentId', 'comment'], axis=1).dtypes # Get all effective dates from the file effective_dates = list( data_frame[holdings_mapping_required['effective_date']].unique()) # If there is more than one throw an error if len(effective_dates) > 1: raise Exception( 'There are {} effective dates in the holding file, need there to be just one' .format(len(effective_dates))) # Convert the effective date to be a timezone aware datetime effective_date = pytz.utc.localize(parser.parse(effective_dates[0])) # Iterate over each holding for index, holding in data_frame.iterrows(): # Set the identifier for the holding which was found earlier if holding['comment'] == 'Resolved as cash with a currency': identifier_key = 'Currency' else: identifier_key = 'LusidInstrumentId' identifiers = { 'Instrument/default/{}'.format(identifier_key): holding['LusidInstrumentId'] } # Set the properties for the holding properties = create_property_values(holding, -1, scope, 'Holding', dtypes) single_cost = models.CurrencyAndAmount(amount=None, currency=None) for lusid_field, column_name in holdings_mapping_optional.items(): if (lusid_field.split(".")[1] == 'cost') and (column_name is not None): setattr(single_cost, lusid_field.split(".")[2], holding[column_name]) if (single_cost.amount is None) and (single_cost.currency is None): single_cost = None single_tax_lot = models.TargetTaxLotRequest( units=holding[holdings_mapping_required['tax_lots.units']], cost=single_cost) for lusid_field, column_name in holdings_mapping_optional.items(): if (lusid_field.split(".")[1] != 'cost') and (column_name is not None): setattr(single_tax_lot, lusid_field.split(".")[2], holding[column_name]) single_holding_adjustment = models.AdjustHoldingRequest( instrument_identifiers=identifiers, tax_lots=[single_tax_lot], properties=properties) # Create the adjust holding request holding_adjustments.append(single_holding_adjustment) # Call LUSID to upsert the transactions response = client.transaction_portfolios.adjust_holdings( scope=scope, code=code, effective_at=effective_date, holding_adjustments=holding_adjustments) return response