def create_instrument_quotes(quotes_effective_date, today, instrument_prices, analyst_scope_code, api_factory): # Create prices via instrument, analytic # Set our quotes effective dates now = datetime.now(pytz.UTC).replace(hour=0, minute=0, second=0, microsecond=0) quotes_effective_date = now - timedelta(days=3) today = now instrument_quotes = {} # Create prices for all instruments except cash for index, instrument in instrument_prices.iterrows(): if 'Cash' in instrument['instrument_name']: continue # Get our Lusid Instrument Id luid = api_factory.build(lusid.api.InstrumentsApi).get_instrument( identifier_type='Figi', identifier=instrument['figi']).lusid_instrument_id instrument_quotes[ luid + str(quotes_effective_date)] = models.UpsertQuoteRequest( quote_id=models.QuoteId(quote_series_id=models.QuoteSeriesId( provider='DataScope', instrument_id=luid, instrument_id_type='LusidInstrumentId', quote_type='Price', field='Mid'), effective_at=quotes_effective_date), metric_value=models.MetricValue( value=instrument["price_original"], unit=instrument["currency"]), lineage='InternalSystem') instrument_quotes[luid + str(today)] = models.UpsertQuoteRequest( quote_id=models.QuoteId(quote_series_id=models.QuoteSeriesId( provider='DataScope', instrument_id=luid, instrument_id_type='LusidInstrumentId', quote_type='Price', field='Mid'), effective_at=today), metric_value=models.MetricValue(value=instrument["price_current"], unit=instrument["currency"]), lineage='InternalSystem') response = api_factory.build(lusid.api.QuotesApi).upsert_quotes( scope=analyst_scope_code, request_body=instrument_quotes) prettyprint.upsert_quotes_response(response)
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_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_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_add_quote(self): request = models.UpsertQuoteRequest(quote_id=models.QuoteId( models.QuoteSeriesId(provider="DataScope", instrument_id="BBG000B9XRY4", instrument_id_type="Figi", quote_type="Price", field="mid"), effective_at=datetime(2019, 4, 15, tzinfo=pytz.utc)), metric_value=models.MetricValue( value=199.23, unit="USD")) self.quotes_api.upsert_quotes(TestDataUtilities.tutorials_scope, request_body={"quote1": request})
def upsert_quotes( api_factory, scope, data_frame, instrument_identifier_mapping, instrument_identifier_heirarchy, required_mapping, ): """ This function takes quotes from a data_frame and upserts them into LUSID param (lusid.utilities.ClientApiFactory) api_factory: The LUSID api factory to use param (str) scope: The LUSID scope to upsert the quotes into param (Pandas DataFrame) data_frame: The DataFrame that the quotes are in param (dict) instrument_identifier_mapping : The dictionary with the instrument identifier mapping between LUSID and the dataframe param (list[str]) instrument_identifier_heirarchy : The heirarchy to use for the LUSID instrument identifiers when upserting quotes param (dict) required_mapping: The mapping of the LUSID required quote fields to the dataframe fields returns (Pandas DataFrame): The succesfully upserted quotes """ # Initialise an empty instrument quotes list to hold the quotes instrument_quotes = {} quote_type_values = { "mid_price": { "quote_type": "Price", "price_side": "Mid", "value": "price", }, "mid_rate": { "quote_type": "Rate", "price_side": "Mid", "value": "rate", }, } # Iterate over the quotes for index, quote in data_frame.iterrows(): quote_type = quote_type_values[quote[ required_mapping["quote_type"]]]["quote_type"] field = quote_type_values[quote[ required_mapping["quote_type"]]]["price_side"] for identifier in instrument_identifier_heirarchy: identifier_value = quote[instrument_identifier_mapping[ "identifier_mapping"][identifier]] if (identifier == "CurrencyPair") and (len(identifier_value) == 6): identifier_value = identifier_value[: 3] + "/" + identifier_value[ 3:] if not pd.isna(identifier_value): break # Add the quote to the list of upsert quote requests effective_date = quote[required_mapping["effective_at"]] instrument_quotes[identifier + "_" + identifier_value + "_" + effective_date] = models.UpsertQuoteRequest( quote_id=models.QuoteId( quote_series_id=models.QuoteSeriesId( provider="DataScope", instrument_id=identifier_value, instrument_id_type=identifier, quote_type=quote_type, field=field, ), effective_at=effective_date, ), metric_value=models.MetricValue( value=quote[required_mapping["value"]], unit=quote[required_mapping["currency"]], ), lineage="InternalSystem", ) # Upsert the quotes into LUSID response = api_factory.build(lusid.api.QuotesApi).upsert_quotes( scope=scope, request_body=instrument_quotes) # Pretty print the response # prettyprint.upsert_quotes_response(response) return prettyprint.upsert_quotes_response(response)
def test_portfolio_aggregation(self): effective_date = datetime(2019, 4, 15, tzinfo=pytz.utc) portfolio_code = self.test_data_utilities.create_transaction_portfolio( TestDataUtilities.tutorials_scope) transactions = [ self.test_data_utilities.build_transaction_request( instrument_id=self.instrument_ids[0], units=100, price=101, currency="GBP", trade_date=effective_date, transaction_type="StockIn"), self.test_data_utilities.build_transaction_request( instrument_id=self.instrument_ids[1], units=100, price=102, currency="GBP", trade_date=effective_date, transaction_type="StockIn"), self.test_data_utilities.build_transaction_request( instrument_id=self.instrument_ids[2], units=100, price=103, currency="GBP", trade_date=effective_date, transaction_type="StockIn") ] self.transaction_portfolios_api.upsert_transactions( scope=TestDataUtilities.tutorials_scope, code=portfolio_code, transactions=transactions) prices = [ models.InstrumentAnalytic(self.instrument_ids[0], 100), models.InstrumentAnalytic(self.instrument_ids[1], 200), models.InstrumentAnalytic(self.instrument_ids[2], 300) ] requests = [] for i in range(3): requests.append( models.UpsertQuoteRequest( quote_id=models.QuoteId(models.QuoteSeriesId( provider="DataScope", instrument_id=self.instrument_ids[i], instrument_id_type="LusidInstrumentId", quote_type="Price", field="mid"), effective_at=effective_date), metric_value=models.MetricValue(value=prices[i].value, unit="GBP"))) self.quotes_api.upsert_quotes( TestDataUtilities.tutorials_scope, quotes={ "quote" + str(request_number): requests[request_number] for request_number in range(len(requests)) }) inline_recipe = models.ConfigurationRecipe( code='quotes_recipe', market=models.MarketContext( market_rules=[], suppliers=models.MarketContextSuppliers(equity='DataScope'), options=models.MarketOptions( default_supplier='DataScope', default_instrument_code_type='LusidInstrumentId', default_scope=TestDataUtilities.tutorials_scope))) aggregation_request = models.AggregationRequest( inline_recipe=inline_recipe, metrics=[ models.AggregateSpec("Instrument/default/Name", "Value"), models.AggregateSpec("Holding/default/PV", "Proportion"), models.AggregateSpec("Holding/default/PV", "Sum") ], group_by=["Instrument/default/Name"], effective_at=effective_date) # do the aggregation aggregation = self.aggregation_api.get_aggregation_by_portfolio( scope=TestDataUtilities.tutorials_scope, code=portfolio_code, request=aggregation_request) for item in aggregation.data: print("\t{}\t{}\t{}".format(item["Instrument/default/Name"], item["Proportion(Holding/default/PV)"], item["Sum(Holding/default/PV)"])) # Asserts self.assertEqual(len(aggregation.data), 3) self.assertEqual(aggregation.data[0]["Sum(Holding/default/PV)"], 10000) self.assertEqual(aggregation.data[1]["Sum(Holding/default/PV)"], 20000) self.assertEqual(aggregation.data[2]["Sum(Holding/default/PV)"], 30000)
def metric(m): return api.models.PropertyValue(metric_value=models.MetricValue( value=float(m)))
def test_portfolio_aggregation(self): effective_date = datetime(2019, 4, 15, tzinfo=pytz.utc) portfolio_code = self.test_data_utilities.create_transaction_portfolio(TestDataUtilities.tutorials_scope) self.id_generator.add_scope_and_code("portfolio", TestDataUtilities.tutorials_scope, portfolio_code) transactions = [ self.test_data_utilities.build_transaction_request(instrument_id=self.instrument_ids[0], units=100, price=101, currency="GBP", trade_date=effective_date, transaction_type="StockIn"), self.test_data_utilities.build_transaction_request(instrument_id=self.instrument_ids[1], units=100, price=102, currency="GBP", trade_date=effective_date, transaction_type="StockIn"), self.test_data_utilities.build_transaction_request(instrument_id=self.instrument_ids[2], units=100, price=103, currency="GBP", trade_date=effective_date, transaction_type="StockIn") ] self.transaction_portfolios_api.upsert_transactions(scope=TestDataUtilities.tutorials_scope, code=portfolio_code, transaction_request=transactions) prices = [ (self.instrument_ids[0], 100), (self.instrument_ids[1], 200), (self.instrument_ids[2], 300) ] requests = [ models.UpsertQuoteRequest( quote_id=models.QuoteId( models.QuoteSeriesId( provider="DataScope", instrument_id=price[0], instrument_id_type="LusidInstrumentId", quote_type="Price", field="mid" ), effective_at=effective_date ), metric_value=models.MetricValue( value=price[1], unit="GBP" ) ) for price in prices ] self.quotes_api.upsert_quotes(TestDataUtilities.tutorials_scope, request_body={"quote" + str(request_number): requests[request_number] for request_number in range(len(requests))}) recipe_scope = 'cs-tutorials' recipe_code = 'quotes_recipe' self.id_generator.add_scope_and_code("recipe", recipe_scope, recipe_code) demo_recipe = models.ConfigurationRecipe( scope=recipe_scope, code=recipe_code, market=models.MarketContext( market_rules=[], suppliers=models.MarketContextSuppliers( equity='DataScope' ), options=models.MarketOptions( default_supplier='DataScope', default_instrument_code_type='LusidInstrumentId', default_scope=TestDataUtilities.tutorials_scope) ) ) upsert_recipe_request = models.UpsertRecipeRequest(demo_recipe) self.recipes_api.upsert_configuration_recipe(upsert_recipe_request) valuation_request = models.ValuationRequest( recipe_id=models.ResourceId(scope=recipe_scope,code=recipe_code), metrics=[ models.AggregateSpec("Instrument/default/Name", "Value"), models.AggregateSpec("Holding/default/PV", "Proportion"), models.AggregateSpec("Holding/default/PV", "Sum") ], group_by=["Instrument/default/Name"], portfolio_entity_ids = [ models.PortfolioEntityId(scope = TestDataUtilities.tutorials_scope, code = portfolio_code) ], valuation_schedule=models.ValuationSchedule(effective_at=effective_date) ) # do the aggregation aggregation = self.aggregation_api.get_valuation(valuation_request=valuation_request) for item in aggregation.data: print("\t{}\t{}\t{}".format(item["Instrument/default/Name"], item["Proportion(Holding/default/PV)"], item["Sum(Holding/default/PV)"])) # Asserts self.assertEqual(len(aggregation.data),3) self.assertEqual(aggregation.data[0]["Sum(Holding/default/PV)"], 10000) self.assertEqual(aggregation.data[1]["Sum(Holding/default/PV)"], 20000) self.assertEqual(aggregation.data[2]["Sum(Holding/default/PV)"], 30000)
def setup_portfolio(cls, effective_date, portfolio_code) -> None: """ Sets up instrument, quotes and portfolio data from TestDataUtilities :param datetime effective_date: The portfolio creation date :param str portfolio_code: The code of the the test portfolio :return: None """ transactions = [ cls.test_data_utilities.build_transaction_request( instrument_id=cls.instrument_ids[0], units=100, price=101, currency="GBP", trade_date=effective_date, transaction_type="StockIn", ), cls.test_data_utilities.build_transaction_request( instrument_id=cls.instrument_ids[1], units=100, price=102, currency="GBP", trade_date=effective_date, transaction_type="StockIn", ), cls.test_data_utilities.build_transaction_request( instrument_id=cls.instrument_ids[2], units=100, price=103, currency="GBP", trade_date=effective_date, transaction_type="StockIn", ), ] cls.transaction_portfolios_api.upsert_transactions( scope=TestDataUtilities.tutorials_scope, code=portfolio_code, transaction_request=transactions, ) prices = [ (cls.instrument_ids[0], 100), (cls.instrument_ids[1], 200), (cls.instrument_ids[2], 300), ] requests = [ models.UpsertQuoteRequest( quote_id=models.QuoteId( models.QuoteSeriesId( provider="Lusid", instrument_id=price[0], instrument_id_type="LusidInstrumentId", quote_type="Price", field="mid", ), effective_at=effective_date, ), metric_value=models.MetricValue(value=price[1], unit="GBP"), ) for price in prices ] cls.quotes_api.upsert_quotes( TestDataUtilities.tutorials_scope, request_body={ "quote" + str(request_number): requests[request_number] for request_number in range(len(requests)) }, )
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