def create_property_values(row, null_value, scope, domain, dtypes): """ This function generates the property values for a row in a file :param Pandas Series row: :param number null_value: :param str scope: :param str domain: :return: dict {str, models.PerpetualProperty} properties: """ # Ensure that all data types in the file have been mapped if not (set([str(data_type) for data_type in dtypes.unique()]) <= set( globals['data_type_mapping'])): raise Exception( '''There are data types in the data_frame which have not been mapped to LUSID data types, please ensure that all data types have been mapped before retrying''' ) # Initialise the empty properties dictionary properties = {} # Iterate over each column name and data type for column_name, data_type in dtypes.iteritems(): # Set the data type to be a string so that it is easier to work with string_data_type = str(data_type) # Convert the numpy data type to a LUSID data type using the global mapping lusid_data_type = globals['data_type_mapping'][string_data_type] # Get the value of the column from the row row_value = row[column_name] # Use the correct LUSID property value based on the data type if lusid_data_type == 'string': if pd.isna(row_value): row_value = str(null_value) property_value = models.PropertyValue(label_value=row_value) if lusid_data_type == 'number': # Handle null values given the input null value override if pd.isnull(row_value): row_value = null_value property_value = models.PropertyValue( metric_value=models.MetricValue(value=row_value)) # Set the property properties['{}/{}/{}'.format( domain, scope, make_code_lusid_friendly(column_name))] = models.PerpetualProperty( key='{}/{}/{}'.format(domain, scope, make_code_lusid_friendly(column_name)), value=property_value) return properties
def test_edit_instrument_property(self): property_value = models.PropertyValue(label_value="Insurance") property_key = f"Instrument/{TestDataUtilities.tutorials_scope}/CustomSector" identifier_type = "Figi" identifier = "BBG00KTDTF73" # update the instrument self.instruments_api.upsert_instruments_properties( upsert_instrument_property_request=[ models.UpsertInstrumentPropertyRequest( identifier_type=identifier_type, identifier=identifier, properties=[ models.ModelProperty(key=property_key, value=property_value) ]) ]) # get the instrument with value instrument = self.instruments_api.get_instrument( identifier_type=identifier_type, identifier=identifier, property_keys=[property_key]) self.assertGreaterEqual(len(instrument.properties), 1) prop = list( filter( lambda p: p.key == property_key and p.value.label_value == property_value.label_value, instrument.properties)) self.assertEqual( len(prop), 1, f"cannot find property key=${property_key} value={property_value}")
def upsert_ratings_property(self, figi, fitch_value=None, moodys_value=None): properties = { f"Instrument/{scope}/FitchRating": fitch_value, f"Instrument/{scope}/MoodysRating": moodys_value, } # upsert property definition for key in properties: if properties[key] is not None: property_request = [ models.UpsertInstrumentPropertyRequest( identifier_type="Figi", identifier=figi, properties=[ models.ModelProperty( key=key, value=models.PropertyValue( metric_value=models.MetricValue( value=properties[key] ) ), ) ], ) ] self.instruments_api.upsert_instruments_properties( upsert_instrument_property_request=property_request )
def test_create_portfolio_with_properties(self): _, scope, property_code, _ = self.id_generator.generate_scope_and_code( "property_definition", scope=TestDataUtilities.tutorials_scope, code_prefix="fund-style-", annotations=["Portfolio"]) data_type_id = models.ResourceId("system", "string") # property definition property_definition = models.CreatePropertyDefinitionRequest( domain="Portfolio", scope=TestDataUtilities.tutorials_scope, code=property_code, value_required=False, display_name="Fund Style", life_time="Perpetual", data_type_id=data_type_id) # create the property definition property_definition_result = self.property_definitions_api.create_property_definition( create_property_definition_request=property_definition) # property value property_value = "Active" portfolio_property = models.ModelProperty( key=property_definition_result.key, value=models.PropertyValue(label_value=property_value)) _, scope, portfolio_code = self.id_generator.generate_scope_and_code( "portfolio", scope=TestDataUtilities.tutorials_scope, code_prefix="portfolio-") # details of the portfolio to be created request = models.CreateTransactionPortfolioRequest( display_name=portfolio_code, code=portfolio_code, base_currency="GBP", # set the property value when creating the portfolio properties={property_definition_result.key: portfolio_property}) # create the portfolio portfolio = self.transaction_portfolios_api.create_portfolio( scope=scope, create_transaction_portfolio_request=request) portfolio_code = portfolio.id.code self.assertEqual(portfolio_code, request.code) portfolio_properties = self.portfolios_api.get_portfolio_properties( TestDataUtilities.tutorials_scope, portfolio_code) self.assertEqual(len(portfolio_properties.properties), 1) self.assertEqual( portfolio_properties.properties[ property_definition_result.key].value.label_value, property_value)
def test_create_portfolio_with_metric_property(self): uuid = self.get_guid() effective_date = datetime(year=2018, month=1, day=1, tzinfo=pytz.utc) # details of property to be created metric_property_definition = models.CreatePropertyDefinitionRequest( domain="Portfolio", scope=TestDataUtilities.tutorials_scope, code="fund-NAV-{}".format(uuid), display_name="fund NAV", life_time="Perpetual", value_required=False, data_type_id=models.resource_id.ResourceId( scope="system", code="currencyAndAmount")) # create property definitions metric_property_definition_result = self.property_definitions_api.create_property_definition( metric_property_definition) # create the property values metric_property_value_request = models.PropertyValue( metric_value=models.MetricValue(value=1100000, unit="GBP")) # metric_property_value_request = models.PropertyValue(label_value="Active") # Details of the new portfolio to be created, created here with the minimum set of mandatory fields create_portfolio_request = models.CreateTransactionPortfolioRequest( code="ud-{}".format(uuid), display_name="portfolio-{}".format(uuid), base_currency="GBP", created=effective_date, properties={ metric_property_definition_result.key: models.PerpetualProperty( key=metric_property_definition_result.key, value=metric_property_value_request) }) # Create portfolio portfolio_result = self.transaction_portfolios_api.create_portfolio( scope=TestDataUtilities.tutorials_scope, create_transaction_portfolio_request=create_portfolio_request) portfolio_properties = self.portfolios_api.get_portfolio_properties( scope=TestDataUtilities.tutorials_scope, code=portfolio_result.id.code).properties metric_property = portfolio_properties[ metric_property_definition_result.key] # Perform assertions on codes, keys, values and units self.assertEqual(portfolio_result.id.code, create_portfolio_request.code) self.assertEqual( list(portfolio_properties.keys())[0], metric_property_definition_result.key) self.assertEqual(metric_property.value.metric_value.value, metric_property_value_request.metric_value.value) self.assertEqual(metric_property.value.metric_value.unit, metric_property_value_request.metric_value.unit)
def create_properties_request(input_transaction, transaction_type) -> dict: properties = { "Transaction/generated/Commission": models.PerpetualProperty( key="Transaction/generated/Commission", value=models.PropertyValue(label_value=input_transaction.transaction_id) ), "Transaction/generated/Type": models.PerpetualProperty( key="Transaction/generated/Type", value=models.PropertyValue(label_value=transaction_type) ), # TODO: This property to come from a global config file "Transaction/generated/LinkedTransactionId": models.PerpetualProperty( key="Transaction/generated/LinkedTransactionId", value=models.PropertyValue(label_value=input_transaction.transaction_id) ) } return properties
def test_create_portfolio_with_label_property(self): # Details of property to be created uuid = self.get_guid() effective_date = datetime(year=2018, month=1, day=1, tzinfo=pytz.utc) label_property_definition = models.CreatePropertyDefinitionRequest( domain="Portfolio", scope=TestDataUtilities.tutorials_scope, code="fund-style-{}".format(uuid), display_name="fund style", life_time="Perpetual", value_required=False, data_type_id=models.resource_id.ResourceId(scope="system", code="string")) # create property definition label_property_definition_request = self.property_definitions_api.create_property_definition( label_property_definition) # create property values property_value = models.PropertyValue(label_value="Active") # Details of new portfolio to be created create_portfolio_request = models.CreateTransactionPortfolioRequest( code="ud-{}".format(uuid), display_name="portfolio-{}".format(uuid), base_currency="GBP", created=effective_date, properties={ label_property_definition_request.key: models.PerpetualProperty( key=label_property_definition_request.key, value=property_value) }) # create portfolio portfolio_request = self.transaction_portfolios_api.create_portfolio( scope=TestDataUtilities.tutorials_scope, create_transaction_portfolio_request=create_portfolio_request) # get properties for assertions portfolio_properties = self.portfolios_api.get_portfolio_properties( scope=TestDataUtilities.tutorials_scope, code=portfolio_request.id.code).properties label_property = portfolio_properties[ label_property_definition_request.key] # Perform assertions on keys, codes and values self.assertEqual( list(portfolio_properties.keys())[0], label_property_definition_request.key) self.assertEqual(portfolio_request.id.code, create_portfolio_request.code) self.assertEqual(label_property.value.label_value, property_value.label_value)
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_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)
def property_def(full_property, label_value): prop = models.ModelProperty( key=full_property, value=models.PropertyValue(label_value=label_value)) return prop
def test_create_portfolio_with_mv_property(self): # Details of property to be created effective_date = datetime(year=2018, month=1, day=1, tzinfo=pytz.utc) scope = "MultiValueProperties" code = "MorningstarQuarterlyRating" portfolio_code = "Portfolio-MVP" multi_value_property_definition = models.CreatePropertyDefinitionRequest( domain="Portfolio", scope=scope, code=code, display_name=code, constraint_style="Collection", data_type_id=lusid.ResourceId(scope="system", code="string"), ) # create property definition try: self.property_definitions_api.create_property_definition( create_property_definition_request= multi_value_property_definition) except lusid.ApiException as e: if json.loads(e.body)["name"] == "PropertyAlreadyExists": logging.info( f"Property {multi_value_property_definition.domain}/{multi_value_property_definition.scope}/{multi_value_property_definition.display_name} already exists" ) finally: self.id_generator.add_scope_and_code( "property_definition", multi_value_property_definition.scope, multi_value_property_definition.code, ["Portfolio"]) schedule = [ '{ "2019-12-31" : "5"}', '{ "2020-03-31" : "4"}', '{ "2020-06-30" : "3"}', '{ "2020-09-30" : "3"}', ] # Details of new portfolio to be created create_portfolio_request = models.CreateTransactionPortfolioRequest( code=portfolio_code, display_name=portfolio_code, base_currency="GBP", created=effective_date, ) # create portfolio try: self.transaction_portfolios_api.create_portfolio( scope=scope, create_transaction_portfolio_request=create_portfolio_request, ) except lusid.ApiException as e: if json.loads(e.body)["name"] == "PortfolioWithIdAlreadyExists": logging.info( f"Portfolio {create_portfolio_request.code} already exists" ) finally: self.id_generator.add_scope_and_code("portfolio", scope, portfolio_code) self.portfolios_api.upsert_portfolio_properties( scope=scope, code=portfolio_code, request_body={ f"Portfolio/{scope}/{code}": models.ModelProperty( key=f"Portfolio/{scope}/{code}", value=models.PropertyValue( label_value_set=models.LabelValueSet(values=schedule)), ) }, ) # get properties for assertions portfolio_properties = self.portfolios_api.get_portfolio_properties( scope=scope, code=portfolio_code).properties label_value_set = portfolio_properties[ f"Portfolio/MultiValueProperties/{code}"].value.label_value_set.values self.assertCountEqual(label_value_set, schedule)
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 set_transaction_mapping(client, transaction_mapping): """ Sets the transaction mapping in LUSID so that the system can resolve the transactions into movements :param LusidApi client: The LusidApi client to use :param dict transaction_mapping: The transaction mapping configuration :return: ResourceListOfTransactionConfigurationData response: The response from LUSID """ # Initialise your list of configuration requests, one for each transaction type configuration_requests = [] # Iterate over your configurations in the default mapping for configuration in transaction_mapping['values']: # Initialise your list of aliases for this configuration aliases = [] # Iterate over the aliases in the imported config for alias in configuration['aliases']: # Append the alias to your list aliases.append( models.TransactionConfigurationTypeAlias( type=alias['type'], description=alias['description'], transaction_class=alias['transactionClass'], transaction_group=alias['transactionGroup'], transaction_roles=alias['transactionRoles'])) # Initialise your list of movements for this configuration movements = [] # Iterate over the movements in the impoted config for movement in configuration['movements']: # Add properties if they exist in the config if len(movement['properties']) > 0: key = movement['properties'][0]['key'] value = models.PropertyValue( label_value=movement['properties'][0]['value']) properties = { key: models.PerpetualProperty(key=key, value=value) } else: properties = {} if len(movement['mappings']) > 0: mappings = [ models.TransactionPropertyMappingRequest( property_key=movement['mappings'][0]['propertyKey'], set_to=movement['mappings'][0]['setTo']) ] else: mappings = [] # Append the movement to your list movements.append( models.TransactionConfigurationMovementDataRequest( movement_types=movement['movementTypes'], side=movement['side'], direction=movement['direction'], properties=properties, mappings=mappings)) # Build your configuration for this transaction type configuration_requests.append( models.TransactionConfigurationDataRequest(aliases=aliases, movements=movements, properties=None)) # Call LUSID to set your configuration for our transaction types response = client.system_configuration.set_configuration_transaction_types( types=configuration_requests) return response