def check_or_create_property(api_factory, property_key):
    split_prop = property_key.split("/")
    if len(split_prop) != 3:
        raise ValueError(
            f"Invalid Value: {property_key}. The property key must have exactly two '/' slashes!"
        )

    domain = split_prop[0]
    scope = split_prop[1]
    code = split_prop[2]

    prop_def_req = models.CreatePropertyDefinitionRequest(
        domain=domain,
        scope=scope,
        code=code,
        display_name=f"{code}",
        data_type_id=models.ResourceId(scope="system", code="string"))

    try:
        api_factory.build(
            lusid.api.PropertyDefinitionsApi).create_property_definition(
                create_property_definition_request=prop_def_req)
    except ApiException:
        logging.info(
            f"Property definition {domain}/{scope}/{code} already exists.")
示例#2
0
    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)
示例#3
0
    def test_upsert_simple_order(self):
        """Makes a request for a single order."""

        orders_scope = "Orders-SimpleUpsert-TestScope"
        # Create unique order id
        order_id = str(uuid.uuid4())

        # Construct order arguments
        instrument_identifiers = {
            TestDataUtilities.lusid_luid_identifier: self.instrument_ids[0]
        }
        ##Create ResourceId for order
        order_resource_id = models.ResourceId(orders_scope, order_id)
        portfolio_id = ResourceId(orders_scope, "OrdersTestPortfolio")
        properties = {
            f"Order/{orders_scope}/TIF":
            PerpetualProperty(f"Order/{orders_scope}/TIF",
                              PropertyValue("GTC")),
            f"Order/{orders_scope}/OrderBook":
            PerpetualProperty(f"Order/{orders_scope}/OrderBook",
                              PropertyValue("UK Test Orders")),
            f"Order/{orders_scope}/PortfolioManager":
            PerpetualProperty(f"Order/{orders_scope}/PortfolioManager",
                              PropertyValue("F Bar")),
            f"Order/{orders_scope}/Account":
            PerpetualProperty(f"Order/{orders_scope}/Account",
                              PropertyValue("J Wilson")),
            f"Order/{orders_scope}/Strategy":
            PerpetualProperty(f"Order/{orders_scope}/Strategy",
                              PropertyValue("RiskArb"))
        }

        order_book_id = ResourceId(orders_scope, "OrdersTestBook")
        quantity = 100
        # Construct request
        order_request = OrderRequest(
            properties=properties,
            instrument_identifiers=instrument_identifiers,
            quantity=quantity,
            side='buy',
            order_book_id=order_book_id,
            portfolio_id=portfolio_id,
            id=order_resource_id)

        order_set_request = OrderSetRequest(order_requests=[order_request])

        upsert_result = self.orders_api.upsert_orders(
            order_set_request=order_set_request)

        self.assertEqual(len(upsert_result.values), 1)
        response = upsert_result.values[0].to_dict()
        self.assertEqual(response['id']['code'], order_id)
        self.assertEqual(response['lusid_instrument_id'],
                         self.instrument_ids[0])
        self.assertEqual(response['quantity'], 100)
        self.assertEqual(
            response['properties'][f"Order/{orders_scope}/TIF"]['key'],
            f"Order/{orders_scope}/TIF")
示例#4
0
    def ensure_property_definition(cls, code):

        try:
            cls.property_definitions_api.get_property_definition(
                domain="Instrument",
                scope=TestDataUtilities.tutorials_scope,
                code=code)
        except ApiException as e:
            # property definition doesn't exist (returns 404), so create one
            property_definition = models.CreatePropertyDefinitionRequest(
                domain="Instrument",
                scope=TestDataUtilities.tutorials_scope,
                life_time="Perpetual",
                code=code,
                value_required=False,
                data_type_id=models.ResourceId("system", "string"))

            # create the property
            cls.property_definitions_api.create_property_definition(
                definition=property_definition)
示例#5
0
def request_define_property(domain, scope, code, display_name, api_factory):
    # Create a request to define our strategy property
    property_request = models.CreatePropertyDefinitionRequest(
        domain='Transaction',
        scope=scope,
        code='strategy',
        value_required=False,
        display_name='strategy',
        data_type_id=models.ResourceId(scope='system', code='string'))

    # Call LUSID to create our new property
    property_response = api_factory.build(
        lusid.api.PropertyDefinitionsApi).create_property_definition(
            create_property_definition_request=property_request)

    # Grab the key off the response to use when referencing this property in other LUSID calls
    strategy_property_key = property_response.key

    # Pretty print our strategy property key
    prettyprint.heading('Strategy Property Key: ', strategy_property_key)
    return strategy_property_key
示例#6
0
    def test_aggregation(self, _, in_line_recipe, recipe_scope,
                         recipe_code) -> None:
        """
        General valuation/aggregation test
        """
        # create recipe (provides model parameters, locations to use in resolving market data etc.
        # and push it into LUSID. Only needs to happen once each time when updated, or first time run to create.
        recipe = self.create_configuration_recipe(recipe_scope, recipe_code)
        self.upsert_recipe_request(recipe)

        # Set valuation result key
        valuation_key = "Sum(Holding/default/PV)"

        # create valuation 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"],
            valuation_schedule=models.ValuationSchedule(
                effective_at=self.effective_date),
            portfolio_entity_ids=[
                models.PortfolioEntityId(
                    scope=TestDataUtilities.tutorials_scope,
                    code=self.portfolio_code)
            ])

        # Complete aggregation
        aggregation = self.aggregation_api.get_valuation(
            valuation_request=valuation_request)

        # Asserts
        self.assertEqual(len(aggregation.data), 3)
        self.assertEqual(aggregation.data[0][valuation_key], 10000)
        self.assertEqual(aggregation.data[1][valuation_key], 20000)
        self.assertEqual(aggregation.data[2][valuation_key], 30000)
示例#7
0
def valuation(api_factory, marketdata_scope, portfolio_group, time):

    time_parts = [time[:10], time[11:]]

    if time_parts[1] == "LSE_market_close":
        time_parts[1] = "16:30:00.000000+00:00"
    elif time_parts[1] == "NYSE_market_close":
        time_parts[1] = "21:00:00.000000+00:00"

    time = "T".join(time_parts)

    # Create a recipe to perform a valuation
    configuration_recipe = models.ConfigurationRecipe(
        scope="User",
        code="quotes_recipe",
        market=models.MarketContext(
            market_rules=[
                models.MarketDataKeyRule(
                    key="Equity.Figi.*",
                    supplier="DataScope",
                    data_scope=marketdata_scope,
                    quote_type="Price",
                    field="Mid",
                ),
                models.MarketDataKeyRule(
                    key="Equity.Isin.*",
                    supplier="DataScope",
                    data_scope=marketdata_scope,
                    quote_type="Price",
                    field="Mid",
                ),
                models.MarketDataKeyRule(
                    key="Equity.LusidInstrumentId.*",
                    supplier="DataScope",
                    data_scope=marketdata_scope,
                    quote_type="Price",
                    field="Mid",
                ),
                models.MarketDataKeyRule(
                    key="Fx.CurrencyPair.*",
                    supplier="DataScope",
                    data_scope=marketdata_scope,
                    quote_type="Rate",
                    field="Mid",
                ),
            ],
            suppliers=models.MarketContextSuppliers(
                commodity="DataScope",
                credit="DataScope",
                equity="DataScope",
                fx="DataScope",
                rates="DataScope",
            ),
            options=models.MarketOptions(
                default_supplier="DataScope",
                default_instrument_code_type="Figi",
                default_scope=marketdata_scope,
            ),
        ),
    )

    upsert_configuration_recipe_response = api_factory.build(
        lusid.api.ConfigurationRecipeApi).upsert_configuration_recipe(
            upsert_recipe_request=models.UpsertRecipeRequest(
                configuration_recipe=configuration_recipe))

    # Create the valuation request
    valuation_request = models.ValuationRequest(
        recipe_id=models.ResourceId(scope="User", code="quotes_recipe"),
        metrics=[
            models.AggregateSpec(key="Instrument/default/LusidInstrumentId",
                                 op="Value"),
            models.AggregateSpec(key="Instrument/default/Name", op="Value"),
            models.AggregateSpec(key="Holding/default/Units", op="Sum"),
            models.AggregateSpec(key="Holding/default/Cost", op="Sum"),
            models.AggregateSpec(key="Holding/default/PV", op="Sum"),
            models.AggregateSpec(key="Holding/default/PV", op="Proportion"),
        ],
        group_by=["Instrument/default/LusidInstrumentId"],
        portfolio_entity_ids=[
            models.PortfolioEntityId(
                scope=portfolio_group.scope,
                code=portfolio_group.code,
                portfolio_entity_type="GroupPortfolio",
            )
        ],
        valuation_schedule=models.ValuationSchedule(effective_at=time),
    )

    # Perform a valuation
    response = api_factory.build(lusid.api.AggregationApi).get_valuation(
        valuation_request=valuation_request)

    dataframe = prettyprint.aggregation_responses_generic_df([response])
    dataframe = dataframe.append(dataframe.sum(numeric_only=True),
                                 ignore_index=True)
    return dataframe
示例#8
0
            name="name1",
            identifiers=["Luid", "Figi"],
            state="Active",
        )
    },
)

portfolio_success = models.Portfolio(
    links=[],
    version="1",
    type="Transaction",
    display_name="name2",
    description="test portfolio",
    created="2019-01-01",
    href="https://www.tes.lusid/code/portfolio?asAt=2019-12-05T10%3A25%3A45.6141270%2B00%3A00",
    id=models.ResourceId(code="ID00001", scope="default test"),
)

transaction_success = models.UpsertPortfolioTransactionsResponse(
    href="https://www.notadonaim.lusid.com/api/api/code/transactions?asAt=2019-12-05T10%3A25%3A45.6141270%2B00%3A00",
    links=[],
    version="1",
)

quote_success = models.UpsertQuotesResponse(
    failed={
        "BBG001MM1KV4-Figi_2019-10-28": models.Quote(
            as_at="2019-01-16",
            quote_id=models.QuoteId(
                quote_series_id=models.QuoteSeriesId(
                    provider="Default",
示例#9
0
    def test_reconcile_portfolio(self):
        # create the portfolio
        scope = TestDataUtilities.tutorials_scope
        portfolio_code = self.test_data_utilities.create_transaction_portfolio(
            scope)
        self.id_generator.add_scope_and_code("portfolio", scope,
                                             portfolio_code)

        today = datetime.now().astimezone(tz=pytz.utc)
        yesterday = today - timedelta(1)

        # create transactions for yesterday
        yesterdays_transactions = [
            self.test_data_utilities.build_transaction_request(
                instrument_id=self.instrument_ids[0],
                units=1000.0,
                price=100.0,
                currency="GBP",
                trade_date=yesterday + timedelta(hours=8),
                transaction_type="StockIn"),
            self.test_data_utilities.build_transaction_request(
                instrument_id=self.instrument_ids[0],
                units=2300.0,
                price=101.0,
                currency="GBP",
                trade_date=yesterday + timedelta(hours=12),
                transaction_type="StockIn"),
            self.test_data_utilities.build_transaction_request(
                instrument_id=self.instrument_ids[1],
                units=-1000.0,
                price=102.0,
                currency="GBP",
                trade_date=yesterday + timedelta(hours=9),
                transaction_type="StockIn"),
            self.test_data_utilities.build_transaction_request(
                instrument_id=self.instrument_ids[2],
                units=1200.0,
                price=103.0,
                currency="GBP",
                trade_date=yesterday + timedelta(hours=16),
                transaction_type="StockIn"),
            self.test_data_utilities.build_transaction_request(
                instrument_id=self.instrument_ids[3],
                units=2000.0,
                price=103.0,
                currency="GBP",
                trade_date=yesterday + timedelta(hours=9),
                transaction_type="StockIn"),
        ]

        # add the transactions to LUSID
        self.transaction_portfolios_api.upsert_transactions(
            scope=TestDataUtilities.tutorials_scope,
            code=portfolio_code,
            transaction_request=yesterdays_transactions)

        # transactions for today
        todays_transactions = [

            # net long 300
            self.test_data_utilities.build_transaction_request(
                instrument_id=self.instrument_ids[0],
                units=-3000.0,
                price=101.78,
                currency="GBP",
                trade_date=today + timedelta(hours=8),
                transaction_type="StockIn"),

            # net long 1800
            self.test_data_utilities.build_transaction_request(
                instrument_id=self.instrument_ids[0],
                units=1500.0,
                price=101.78,
                currency="GBP",
                trade_date=today + timedelta(hours=12),
                transaction_type="StockIn"),

            # flat
            self.test_data_utilities.build_transaction_request(
                instrument_id=self.instrument_ids[1],
                units=1000.0,
                price=102.0,
                currency="GBP",
                trade_date=today + timedelta(hours=12),
                transaction_type="StockIn"),

            # net long 2400
            self.test_data_utilities.build_transaction_request(
                instrument_id=self.instrument_ids[2],
                units=1200.0,
                price=103.0,
                currency="GBP",
                trade_date=today + timedelta(hours=16),
                transaction_type="StockIn"),

            # net long 3000
            self.test_data_utilities.build_transaction_request(
                instrument_id=self.instrument_ids[3],
                units=1000.0,
                price=103.0,
                currency="GBP",
                trade_date=today + timedelta(hours=9),
                transaction_type="StockIn"),

            # net long 5000
            self.test_data_utilities.build_transaction_request(
                instrument_id=self.instrument_ids[3],
                units=2000.0,
                price=103.0,
                currency="GBP",
                trade_date=today + timedelta(hours=20),
                transaction_type="StockIn"),
        ]

        # add the transactions to LUSID
        transactions_response = self.transaction_portfolios_api.upsert_transactions(
            scope=TestDataUtilities.tutorials_scope,
            code=portfolio_code,
            transaction_request=todays_transactions)

        # get the time of the last update
        last_as_at = transactions_response.version.as_at_date

        # We now have the portfolio with 2 days worth of transactions, going to reconcile from T-1 20:00 to now,
        # this should reflect breaks for each instrument equal to the transactions from yesterday till 20:00 today
        reconciliation_request = models.PortfoliosReconciliationRequest(
            left=models.PortfolioReconciliationRequest(
                portfolio_id=models.ResourceId(
                    scope=TestDataUtilities.tutorials_scope,
                    code=portfolio_code),
                effective_at=yesterday + timedelta(hours=20),
                as_at=last_as_at),
            right=models.PortfolioReconciliationRequest(
                portfolio_id=models.ResourceId(
                    scope=TestDataUtilities.tutorials_scope,
                    code=portfolio_code),
                effective_at=today + timedelta(hours=16),
                as_at=last_as_at),
            instrument_property_keys=[TestDataUtilities.lusid_luid_identifier])

        breaks = self.reconciliations_api.reconcile_holdings(
            portfolios_reconciliation_request=reconciliation_request)

        for rec_break in breaks.values:
            print("{}\t{}\t{}".format(rec_break.instrument_uid,
                                      rec_break.difference_units,
                                      rec_break.difference_cost.amount))

        rec_map = {b.instrument_uid: b for b in breaks.values}

        self.assertEqual(-1500,
                         rec_map[self.instrument_ids[0]].difference_units)
        self.assertEqual(1000,
                         rec_map[self.instrument_ids[1]].difference_units)
        self.assertEqual(1200,
                         rec_map[self.instrument_ids[2]].difference_units)
        self.assertEqual(1000,
                         rec_map[self.instrument_ids[3]].difference_units)
    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)
示例#11
0
    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)
示例#12
0
    def test_Perform_a_reconciliation(self):

        # load sheet
        sht = self.get_sheet("Perform a reconciliation")

        # get parameters from excel
        LeftScope = "F24"
        LeftCode = "F25"
        LeftEffectiveAt = "F26"
        LeftAsAt = "F27"
        RightScope = "F28"
        RightCode = "F29"
        RightEffectiveAt = "F30"
        RightAsAt = "F31"

        left_scope = sht.range(LeftScope).value
        left_code = sht.range(LeftCode).value
        left_effective_at = get_date(LeftEffectiveAt, sht)
        left_as_at = get_date(LeftAsAt, sht)
        right_scope = sht.range(RightScope).value
        right_code = sht.range(RightCode).value
        right_effective_at = get_date(RightEffectiveAt, sht)
        right_as_at = get_date(RightAsAt, sht)

        # get validation data
        request_reconciliation = models.PortfoliosReconciliationRequest(
            left=models.PortfolioReconciliationRequest(
                portfolio_id=models.ResourceId(scope=left_scope, code=left_code),
                effective_at=left_effective_at,
                as_at=left_as_at
            ),
            right=models.PortfolioReconciliationRequest(
                portfolio_id=models.ResourceId(scope=right_scope, code=right_code),
                effective_at=right_effective_at,
                as_at=right_as_at
            ),
            instrument_property_keys=['Instrument/default/LusidInstrumentId']
        )

        reconciliation_validation_response = self.reconciliations_api.reconcile_holdings(
            request=request_reconciliation
        ).values

        # format validation data
        reconciliation_validation = [
            {
                "Instrument Uid": data.instrument_uid,
                "Left Units": data.left_units,
                "Right Units": data.right_units,
                "Units Difference": data.difference_units,
                "Left Cost Amount": data.left_cost.amount,
                "Right Cost Amount": data.right_cost.amount,
            }
            for data in reconciliation_validation_response
        ]

        # get excel data
        reconciliation_response_excel = sht.range("E39", "O49").value
        headers = reconciliation_response_excel.pop(0)

        # format excel data
        reconciliation_excel = [
            {
                "Instrument Uid": data[headers.index('Instrument Uid')],
                "Left Units": data[headers.index("Left Units")],
                "Right Units": data[headers.index("Right Units")],
                "Units Difference": data[headers.index("Units Difference")],
                "Left Cost Amount": data[headers.index("Left Cost Amount")],
                "Right Cost Amount": data[headers.index("Right Cost Amount")]
            }
            for data in reconciliation_response_excel if (data[0] != "" and data[0] is not None)
        ]
        # assert excel data exists within validation set
        [self.assertIn(sample, reconciliation_validation) for sample in reconciliation_excel]
示例#13
0
def create_property_definitions_from_file(client, scope, domain, data_frame,
                                          missing_property_columns):
    """
    Creates the property definitions for all the columns in a file

    :param LusidApi client: The LusidApi client to use
    :param str scope: The scope to create the property definitions in
    :param str domain: The domain to create the property definitions in
    :param Pandas Series data_frame_dtypes: The dataframe dtypes to add definitions for
    :return: dict property_key_mapping: A mapping of data_frame columns to property keys
    """

    missing_property_data_frame = data_frame.loc[:, missing_property_columns]

    # Ensure that all data types in the file have been mapped
    if not (set([
            str(data_type)
            for data_type in missing_property_data_frame.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 a dictionary to hold the keys
    property_key_mapping = {}

    # Iterate over the each column and its data type
    for column_name, data_type in missing_property_data_frame.dtypes.iteritems(
    ):

        # Make the column name LUSID friendly
        lusid_friendly_code = make_code_lusid_friendly(column_name)

        # If there is no data Pandas infers a type of float, would prefer to infer object
        if missing_property_data_frame[column_name].isnull().all():
            print('{} is null'.format(column_name))
            data_type = 'object'
            data_frame[column_name] = data_frame[column_name].astype(
                'object', copy=False)

        # Create a request to define the property, assumes value_required is false for all
        property_request = models.CreatePropertyDefinitionRequest(
            domain=domain,
            scope=scope,
            code=lusid_friendly_code,
            value_required=False,
            display_name=column_name,
            data_type_id=models.ResourceId(
                scope='default',
                code=globals['data_type_mapping'][str(data_type)]))

        # Call LUSID to create the new property
        property_response = client.property_definitions.create_property_definition(
            definition=property_request)

        print('Created - {} - with datatype {}'.format(
            property_response.key, property_response.data_type_id.code))

        # Grab the key off the response to use when referencing this property in other LUSID calls
        property_key_mapping[column_name] = property_response.key

    return property_key_mapping, data_frame
示例#14
0
    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,
        )