def setup_transaction(api_factory, transaction_id, transaction_date, settlement_date, instrument_id, portfolio_scope, portfolio_code, units, txn_type="Buy"): # Create a buy transaction in the portfolio price = 1 transaction_request1 = models.TransactionRequest( transaction_id=transaction_id, type=txn_type, instrument_identifiers={ "Instrument/default/ClientInternal": instrument_id }, transaction_date=transaction_date, settlement_date=settlement_date, units=units, transaction_price=models.TransactionPrice(price=price), total_consideration=models.CurrencyAndAmount(amount=units * price, currency="GBP"), transaction_currency="GBP") api_factory.build(lusid.api.TransactionPortfoliosApi).upsert_transactions( scope=portfolio_scope, code=portfolio_code, transaction_request=[transaction_request1])
def test_load_listed_instrument_transaction(self): # create the portfolio portfolio_code = self.test_data_utilities.create_transaction_portfolio(TestDataUtilities.tutorials_scope) trade_date = datetime(2018, 1, 1, tzinfo=pytz.utc) # details of the transaction to be added transaction = models.TransactionRequest( # unique transaction id transaction_id=str(uuid.uuid4()), # transaction type, configured during system setup type="Buy", instrument_identifiers={TestDataUtilities.lusid_luid_identifier: self.instrument_ids[0]}, transaction_date=trade_date, settlement_date=trade_date, units=100, transaction_price=models.TransactionPrice(12.3), total_consideration=models.CurrencyAndAmount(1230, "GBP"), source="Client" ) # add the transaction self.transaction_portfolios_api.upsert_transactions(scope=TestDataUtilities.tutorials_scope, code=portfolio_code, transaction_request=[transaction]) # get the transaction transactions = self.transaction_portfolios_api.get_transactions(scope=TestDataUtilities.tutorials_scope, code=portfolio_code) self.assertEqual(len(transactions.values), 1) self.assertEqual(transactions.values[0].transaction_id, transaction.transaction_id)
def create_txn_with_property(self, instrument_id, property_value): # setup the transaction effective_date = datetime(2020, 12, 1, 0, 0, tzinfo=pytz.utc) txn = models.TransactionRequest( transaction_id="TXN001", type="Buy", instrument_identifiers={"Instrument/default/Figi": instrument_id}, transaction_date=effective_date, settlement_date=effective_date, units=1000, transaction_price=models.TransactionPrice(price=100, type="Price"), total_consideration=models.CurrencyAndAmount(amount=1, currency="GBP"), exchange_rate=1, transaction_currency="GBP", properties={ f"Transaction/{self.scope}/{self.code}": lusid.PerpetualProperty( key=f"Transaction/{self.scope}/{self.code}", value=lusid.PropertyValue(label_value=property_value), ) }, ) return self.transaction_portfolios_api.upsert_transactions( scope=self.scope, code=self.code, transaction_request=[txn])
def build_cash_fundsin_transaction_request(self, units, currency, trade_date): return models.TransactionRequest(transaction_id=str(uuid.uuid4()), type="FundsIn", instrument_identifiers={self.lusid_cash_identifier: currency}, transaction_date=trade_date, settlement_date=trade_date, units=units, total_consideration=models.CurrencyAndAmount(currency=currency), transaction_price=models.TransactionPrice(price=0.0), source="Client")
def build_transaction_request(self, instrument_id, units, price, currency, trade_date, transaction_type): return models.TransactionRequest(transaction_id=str(uuid.uuid4()), type=transaction_type, instrument_identifiers={self.lusid_luid_identifier: instrument_id}, transaction_date=trade_date, settlement_date=trade_date, units=units, transaction_price=models.TransactionPrice(price=price), total_consideration=models.CurrencyAndAmount(amount=price*units, currency=currency), source="Broker")
def test_load_otc_instrument_transaction(self): # create the portfolio portfolio_code = self.test_data_utilities.create_transaction_portfolio( TestDataUtilities.tutorials_scope) trade_date = datetime(2018, 1, 1, tzinfo=pytz.utc) swap_definition = models.InstrumentDefinition( name="10mm 5Y Fixed", identifiers={ "ClientInternal": models.InstrumentIdValue(value="SW-1") }, definition=models.InstrumentEconomicDefinition( instrument_format="CustomFormat", content= "<customFormat>upload in custom xml or JSON format</customFormat>" )) # create the swap swap_response = self.instruments_api.upsert_instruments( requests={"request": swap_definition}) # get the LUID for the created instrument swap_id = list(swap_response.values.values())[0].lusid_instrument_id # details of the transaction to be added transaction = models.TransactionRequest( transaction_id=str(uuid.uuid4()), type="Buy", instrument_identifiers={ TestDataUtilities.lusid_luid_identifier: swap_id }, transaction_date=trade_date, settlement_date=trade_date, units=1, transaction_price=models.TransactionPrice(0.0), total_consideration=models.CurrencyAndAmount(0.0, "GBP"), source="Client") # add the transaction self.transaction_portfolios_api.upsert_transactions( scope=TestDataUtilities.tutorials_scope, code=portfolio_code, transactions=[transaction]) # get the transaction transactions = self.transaction_portfolios_api.get_transactions( scope=TestDataUtilities.tutorials_scope, code=portfolio_code) self.assertEqual(len(transactions.values), 1) self.assertEqual(transactions.values[0].transaction_id, transaction.transaction_id)
def upsert_trades(analyst_transactions, strategy_property_key, scope, portfolio_code, api_factory): # Initialise a list to hold our transactions batch_transaction_requests = [] # Iterate over the transactions for each portfolio for index, transaction in analyst_transactions.iterrows(): if 'Cash' in transaction['instrument_name']: identifier_key = 'Instrument/default/Currency' else: identifier_key = 'Instrument/default/Figi' batch_transaction_requests.append( models.TransactionRequest( transaction_id=transaction['transaction_id'], type=transaction['type'], instrument_identifiers={ identifier_key: transaction['instrument_uid'] }, transaction_date=transaction['transaction_date'], settlement_date=transaction['settlement_date'], units=transaction['units'], transaction_price=models.TransactionPrice( price=transaction['transaction_price'], type='Price'), total_consideration=models.CurrencyAndAmount( amount=transaction['total_cost'], currency=transaction['transaction_currency']), source='Client', transaction_currency=transaction['transaction_currency'], properties={ strategy_property_key: models.PerpetualProperty( key=strategy_property_key, value=models.PropertyValue( label_value=transaction['strategy'])) })) # Call LUSID to upsert our transactions transaction_response = api_factory.build( lusid.api.TransactionPortfoliosApi).upsert_transactions( scope=scope, code=portfolio_code, transaction_request=batch_transaction_requests) # Pretty print the response from LUSID prettyprint.transactions_response(transaction_response, scope, portfolio_code)
def test_add_transaction_to_portfolio(self): # effective date of the portfolio, this is the date the portfolio was created and became live. All dates/times # must be supplied in UTC effective_date = datetime(2018, 1, 1, tzinfo=pytz.utc) # create the portfolio portfolio_id = self.test_data_utilities.create_transaction_portfolio( TestDataUtilities.tutorials_scope) self.id_generator.add_scope_and_code("portfolio", TestDataUtilities.tutorials_scope, portfolio_id) # details of the transaction to be added transaction = models.TransactionRequest( # unique transaction id transaction_id=str(uuid.uuid4()), # transaction type, configured during system setup type="Buy", instrument_identifiers={ TestDataUtilities.lusid_luid_identifier: self.instrument_ids[0] }, transaction_date=effective_date, settlement_date=effective_date, units=100, transaction_price=models.TransactionPrice(12.3), total_consideration=models.CurrencyAndAmount(1230, "GBP"), source="Client") # add the transaction self.transaction_portfolios_api.upsert_transactions( TestDataUtilities.tutorials_scope, portfolio_id, transaction_request=[transaction]) # get the trades trades = self.transaction_portfolios_api.get_transactions( TestDataUtilities.tutorials_scope, portfolio_id) self.assertEqual(len(trades.values), 1) self.assertEqual(trades.values[0].transaction_id, transaction.transaction_id)
def create_upsert_transaction_request(input_transaction, commission_rate, transaction_type, instrument_identifier, properties: dict) -> list: transaction_id_suffix = "_commission" # transaction_type = "Commission" # instrument_identifier = "Instrument/default/Currency" request = models.TransactionRequest( transaction_id=f"{input_transaction.transaction_id}{transaction_id_suffix}", transaction_date=str(input_transaction.transaction_date.isoformat()), # Is this transaction date or settlement date? settlement_date=str(input_transaction.settlement_date.isoformat()), type=transaction_type, instrument_identifiers={instrument_identifier: input_transaction.transaction_currency}, total_consideration=models.CurrencyAndAmount( amount=input_transaction.total_consideration.amount * commission_rate, currency=input_transaction.transaction_currency), units=input_transaction.units * commission_rate, transaction_currency=input_transaction.transaction_currency, properties=properties ) return [request]
def test_load_cash_transaction(self): # create the portfolio portfolio_code = self.test_data_utilities.create_transaction_portfolio( TestDataUtilities.tutorials_scope) trade_date = datetime(2018, 1, 1, tzinfo=pytz.utc) # details of the transaction to be added transaction = models.TransactionRequest( # unique transaction id transaction_id=str(uuid.uuid4()), # transaction type, configured during system setup type="FundsIn", # Cash instruments are identified using CCY_ followed by the ISO currency codes. # Cash instruments do not need to be created before use instrument_identifiers={ TestDataUtilities.lusid_cash_identifier: "GBP" }, transaction_date=trade_date, settlement_date=trade_date, transaction_price=models.TransactionPrice(0.0), units=0, total_consideration=models.CurrencyAndAmount(0, "GBP"), source="Client") # add the transaction self.transaction_portfolios_api.upsert_transactions( scope=TestDataUtilities.tutorials_scope, code=portfolio_code, transactions=[transaction]) # get the transaction transactions = self.transaction_portfolios_api.get_transactions( scope=TestDataUtilities.tutorials_scope, code=portfolio_code) self.assertEqual(len(transactions.values), 1) self.assertEqual(transactions.values[0].transaction_id, transaction.transaction_id)
def test_add_transaction_to_portfolio_with_property(self): guid = str(uuid.uuid4()) property_name = "traderId-{0}".format(guid) # details of the property to be created property_definition = models.CreatePropertyDefinitionRequest( # The domain the property is to be applied to domain="Transaction", # the scope the property will be created in scope=TestDataUtilities.tutorials_scope, life_time="Perpetual", # when the property value is set it will be valid forever and cannot be changed. # properties whose values can change over time should be created with LifeTimeEnum.TIMEVARIANT code=property_name, value_required=False, display_name="Trader Id", data_type_id=models.ResourceId("system", "string")) # create the property definition property_definition_result = self.property_definitions_api.create_property_definition( definition=property_definition) # effective date for which portfolio is created effective_date = datetime(2018, 1, 1, tzinfo=pytz.utc) # create the portfolio portfolio_id = self.test_data_utilities.create_transaction_portfolio( TestDataUtilities.tutorials_scope) property_value_as_string = "A Trader" property_value = models.PropertyValue(property_value_as_string) # details of the transaction to be added transaction = models.TransactionRequest( transaction_id=str(uuid.uuid4()), type="Buy", instrument_identifiers={ TestDataUtilities.lusid_luid_identifier: self.instrument_ids[0] }, transaction_date=effective_date, settlement_date=effective_date, units=100, transaction_price=models.TransactionPrice(12.3), total_consideration=models.CurrencyAndAmount(1230, "GBP"), source="Client", # add the property to the transaction properties={ property_definition_result.key: models.PerpetualProperty(property_definition_result.key, property_value) }) # add the transaction self.transaction_portfolios_api.upsert_transactions( TestDataUtilities.tutorials_scope, portfolio_id, transactions=[transaction]) # get the trades trades = self.transaction_portfolios_api.get_transactions( TestDataUtilities.tutorials_scope, portfolio_id) self.assertEqual(len(trades.values), 1) self.assertEqual(trades.values[0].transaction_id, transaction.transaction_id) self.assertEqual( trades.values[0].properties[ property_definition_result.key].value.label_value, property_value_as_string)
value.name: value.lusid_instrument_id for _, value in instruments_api.get_instruments( identifier_type="Figi", request_body=list( figis_to_create.keys())).values.items() } inverted_instruments = {v: k for k, v in instruments.items()} # Add transactions vodafone_id = instruments["VODAFONE GROUP PLC"] tx1 = models.TransactionRequest( transaction_id=f"Transaction-{uuid.uuid4()}", type="StockIn", instrument_identifiers={ "Instrument/default/LusidInstrumentId": vodafone_id }, transaction_date=datetime.datetime(2021, 3, 27, tzinfo=pytz.utc), settlement_date=datetime.datetime(2021, 3, 28, tzinfo=pytz.utc), units=100, transaction_price=models.TransactionPrice(price=103), total_consideration=models.CurrencyAndAmount(amount=103 * 100, currency="GBP"), source="Broker") tx2 = models.TransactionRequest( transaction_id=f"Transaction-{uuid.uuid4()}", type="StockIn", instrument_identifiers={ "Instrument/default/LusidInstrumentId": vodafone_id }, transaction_date=datetime.datetime(2021, 3, 29, tzinfo=pytz.utc), settlement_date=datetime.datetime(2021, 3, 29, tzinfo=pytz.utc), units=500,
def load_transactions(client, scope, code, data_frame, transaction_mapping_required, transaction_mapping_optional, source): """ This function loads transactions for a given portfolio into LUSID :param LusidApi client: The LusidApi client to use :param str scope: The scope of the portfolio to upsert the transactions into :param str code: The code of the portfolio to upsert the transactions into :param Pandas DataFrame data_frame: The dataframe containing the data :param dict {str, str} transaction_mapping: The mapping of the fields from the dataframe to LUSID :param str source: The source system of the transactions, used to fetch the correct transaction types :return: UpsertPortfolioTransactionsResponse response: Response from LUSID to the upsert request """ # Initialise a list to hold the requests transaction_requests = [] dtypes = data_frame.drop( ['resolvable', 'foundWith', 'LusidInstrumentId', 'comment'], axis=1).dtypes # Iterate over each transaction for index, transaction in data_frame.iterrows(): # Set the identifier for the transaction which was found earlier if transaction['comment'] == 'Resolved as cash with a currency': identifier_key = 'Currency' else: identifier_key = 'LusidInstrumentId' identifiers = { 'Instrument/default/{}'.format(identifier_key): transaction['LusidInstrumentId'] } # Set the properties for the transaction properties = create_property_values(transaction, -1, scope, 'Transaction', dtypes) exchange_rate = None if ('exchange_rate' in transaction_mapping_optional.keys()) and ( transaction_mapping_optional['exchange_rate'] is not None): exchange_rate = transaction[ transaction_mapping_optional['exchange_rate']] properties[ 'Transaction/default/TradeToPortfolioRate'] = models.PerpetualProperty( key='Transaction/default/TradeToPortfolioRate', value=models.PropertyValue(metric_value=models.MetricValue( value=1 / exchange_rate))) # Create the transaction request transaction_requests.append( models.TransactionRequest( transaction_id=make_code_lusid_friendly(transaction[ transaction_mapping_required['transaction_id']]), type=transaction[ transaction_mapping_required['transaction_type']], instrument_identifiers=identifiers, transaction_date=convert_datetime_utc(transaction[ transaction_mapping_required['transaction_date']]), settlement_date=convert_datetime_utc(transaction[ transaction_mapping_required['settlement_date']]), units=transaction[transaction_mapping_required['units']], transaction_price=models.TransactionPrice(price=transaction[ transaction_mapping_required['transaction_price.price']], type='Price'), total_consideration=models.CurrencyAndAmount( amount=transaction[transaction_mapping_required[ 'total_consideration.amount']], currency=make_code_lusid_friendly( transaction[transaction_mapping_required[ 'total_consideration.currency']])), exchange_rate=exchange_rate, transaction_currency=make_code_lusid_friendly(transaction[ transaction_mapping_required['transaction_currency']]), source=source, properties=properties)) # Call LUSID to upsert the transactions response = client.transaction_portfolios.upsert_transactions( scope=scope, code=code, transactions=transaction_requests) return response
def test_name_change_corporate_action(self): """The code below shows how to process a corporate action name change in LUSID: Create two instruments, the original and the updated instrument. Create a portfolio and add a transaction to it for the original instrument. Create a corporate action source, and a corporate action comprising a transition. Upsert the corporate action, then check that the holding instrument was changed. """ # Define details for the corporate action. instrument_name = "instrument-name" instrument_original_figi = "FR0123456789" instrument_updated_figi = "FR5555555555" effective_at_date = datetime(2021, 1, 1, tzinfo=pytz.utc) # Create two instruments: an "original" instrument which # will be renamed and the instrument it will be renamed to. self.instruments_api.upsert_instruments( request_body={ instrument_original_figi: models.InstrumentDefinition( name=instrument_name, identifiers={ "Figi": models.InstrumentIdValue( value=instrument_original_figi) }, ), instrument_updated_figi: models.InstrumentDefinition( name=instrument_name, identifiers={ "Figi": models.InstrumentIdValue(value=instrument_updated_figi) }, ), }) _, scope, portfolio_code = self.id_generator.generate_scope_and_code( "portfolio", scope=TestDataUtilities.tutorials_scope, code_prefix="corporate-actions-portfolio-") try: # Create a transaction portfolio to hold the original instrument. self.transaction_portfolios_api.create_portfolio( scope=scope, create_transaction_portfolio_request=models. CreateTransactionPortfolioRequest( code=portfolio_code, display_name=portfolio_code, base_currency="GBP", created=effective_at_date, ), ) except lusid.ApiException as e: if json.loads(e.body)["name"] == "PortfolioWithIdAlreadyExists": pass # ignore if the portfolio exists # Add a transaction for the original instrument. self.transaction_portfolios_api.upsert_transactions( scope=TestDataUtilities.tutorials_scope, code=portfolio_code, transaction_request=[ models.TransactionRequest( transaction_id=str(uuid.uuid4()), type="Buy", instrument_identifiers={ TestDataUtilities.lusid_figi_identifier: instrument_original_figi }, transaction_date=effective_at_date, settlement_date=effective_at_date, transaction_price=models.TransactionPrice(0.0), units=60000, total_consideration=models.CurrencyAndAmount(0, "GBP"), source="Client", ) ], ) corporate_action_source_code = "name-change-corporate-actions-source" corporate_action_code = "name-change-corporate-action" self.id_generator.add_scope_and_code("ca_source", TestDataUtilities.tutorials_scope, corporate_action_source_code) # Create a corporate actions source. corporate_action_source = models.CreateCorporateActionSourceRequest( scope=TestDataUtilities.tutorials_scope, code=corporate_action_source_code, display_name=corporate_action_source_code, description="Name change corporate actions source", ) try: self.corporate_actions_sources_api.create_corporate_action_source( create_corporate_action_source_request=corporate_action_source) except lusid.ApiException as e: if json.loads(e.body)["name"] == "EntityWithIdAlreadyExists": pass # ignore if the property definition exists # Apply the corporate actions source to the transaction portfolio. self.transaction_portfolios_api.upsert_portfolio_details( scope=TestDataUtilities.tutorials_scope, code=portfolio_code, effective_at=effective_at_date, create_portfolio_details=models.CreatePortfolioDetails( corporate_action_source_id=models.ResourceId( scope=TestDataUtilities.tutorials_scope, code=corporate_action_source_code, )), ) # Create a transition which applies to the original instrument above transition_in = models.CorporateActionTransitionComponentRequest( instrument_identifiers={ TestDataUtilities.lusid_figi_identifier: instrument_original_figi }, cost_factor=1, units_factor=1, ) # and has the effect of changing its FIGI to the updated FIGI rename_figi_transition = models.CorporateActionTransitionComponentRequest( instrument_identifiers={ TestDataUtilities.lusid_figi_identifier: instrument_updated_figi }, cost_factor=1, units_factor=1, ) # while zeroing the original instrument's position. zero_previous_position_transition = ( models.CorporateActionTransitionComponentRequest( instrument_identifiers={ TestDataUtilities.lusid_figi_identifier: instrument_original_figi }, cost_factor=0, units_factor=0, )) # The effect of the corporate action is the transition which # combines the input transition and the output transitions. transition = models.CorporateActionTransitionRequest( input_transition=transition_in, output_transitions=[ rename_figi_transition, zero_previous_position_transition, ], ) self.id_generator.add_scope_and_code("corp_action", TestDataUtilities.tutorials_scope, corporate_action_source_code, [corporate_action_code]) # Create a request to upsert a corporate action with the transition above. corporate_action_request = models.UpsertCorporateActionRequest( corporate_action_code=corporate_action_code, announcement_date=effective_at_date + timedelta(days=1), ex_date=effective_at_date + timedelta(days=1), record_date=effective_at_date + timedelta(days=1), payment_date=effective_at_date + timedelta(days=1), transitions=[transition], ) # Make the request through the CorporateActionSourcesApi. self.corporate_actions_sources_api.batch_upsert_corporate_actions( scope=TestDataUtilities.tutorials_scope, code=corporate_action_source_code, upsert_corporate_action_request=[corporate_action_request], ) # Fetch holdings in portfolio once corporate action is applied. holdings = self.transaction_portfolios_api.get_holdings( scope=TestDataUtilities.tutorials_scope, code=portfolio_code, property_keys=["Instrument/default/Figi"], ) # The holding for the original instrument is now against the new instrument's FIGI. self.assertEqual( holdings.values[0].properties[ TestDataUtilities.lusid_figi_identifier].value.label_value, instrument_updated_figi, )