コード例 #1
0
    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)
コード例 #2
0
def populate_with_cash(holdings_effective_date, initial_cash_balance,
                       analyst_scope_code, transaction_portfolio_code,
                       api_factory):
    # Create a holding adjustment to set our initial cash balance
    holding_adjustment = [
        models.AdjustHoldingRequest(
            instrument_identifiers={'Instrument/default/Currency': 'GBP'},
            tax_lots=[
                models.TargetTaxLotRequest(units=initial_cash_balance,
                                           cost=models.CurrencyAndAmount(
                                               amount=initial_cash_balance,
                                               currency='GBP'),
                                           portfolio_cost=initial_cash_balance,
                                           price=1)
            ])
    ]

    # Call LUSID to set our initial cash balance
    set_holdings_response = api_factory.build(
        lusid.api.TransactionPortfoliosApi).set_holdings(
            scope=analyst_scope_code,
            code=transaction_portfolio_code,
            effective_at=holdings_effective_date,
            adjust_holding_request=holding_adjustment)

    # Pretty print our response from LUSID
    prettyprint.set_holdings_response(set_holdings_response,
                                      analyst_scope_code,
                                      transaction_portfolio_code)
コード例 #3
0
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])
コード例 #4
0
    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])
コード例 #5
0
 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")
コード例 #6
0
 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")
コード例 #7
0
    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)
コード例 #8
0
 def build_adjust_holdings_request(self, instrument_id, units, price, currency, trade_date):
     return models.AdjustHoldingRequest(
         instrument_identifiers={
             TestDataUtilities.lusid_luid_identifier: instrument_id
         },
         tax_lots=[
             models.TargetTaxLotRequest(units=units,
                                        price=price,
                                        cost=models.CurrencyAndAmount(amount=price * units, currency=currency),
                                        portfolio_cost=price * units,
                                        purchase_date=trade_date,
                                        settlement_date=trade_date)
         ])
コード例 #9
0
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)
コード例 #10
0
def setup_index(analyst_scope_code, reference_portfolio_code,
                instrument_prices, api_factory):
    # Set an arbitary index level to start our index with
    index_level = 1000
    # Call LUSID - get the constituents of our index from our reference portfolio
    constituents = api_factory.build(
        lusid.api.ReferencePortfolioApi).get_reference_portfolio_constituents(
            scope=analyst_scope_code,
            code=reference_portfolio_code,
            effective_at=datetime.now(pytz.UTC))
    # Initialise our list to hold the adjustments we need to make to our index to set it up
    index_setup = []
    # Get our weights from the constituents into a better format to work with
    weights = {
        constituent.instrument_uid: constituent.weight
        for constituent in constituents.constituents
    }

    # Iterate over our pricing analytics
    for index, instrument in instrument_prices.iterrows():

        # Get our Lusid Instrument ID
        Luid = api_factory.build(lusid.api.InstrumentsApi).get_instrument(
            identifier_type='Figi',
            identifier=instrument['figi']).lusid_instrument_id
        # Get the initial price for each constituent of the index from our analytics store
        inception_price = instrument['price_original']
        # Work out how much of the index this constituent should make up using its w
        index_cost = weights[Luid] * index_level
        # Work out how many units we should therefore buy
        index_units = index_cost / inception_price
        # Create our request for this instrument
        index_setup.append(
            models.AdjustHoldingRequest(instrument_identifiers={
                'Instrument/default/Figi':
                instrument['figi']
            },
                                        tax_lots=[
                                            models.TargetTaxLotRequest(
                                                units=index_units,
                                                cost=models.CurrencyAndAmount(
                                                    amount=index_cost,
                                                    currency='GBP'),
                                                portfolio_cost=index_cost,
                                                price=inception_price)
                                        ]))
    return index_setup
コード例 #11
0
    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)
コード例 #12
0
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]
コード例 #13
0
    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)
コード例 #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,
        )
コード例 #15
0
def load_holdings(client, scope, code, data_frame, holdings_mapping_required,
                  holdings_mapping_optional):
    """
    This function sets the holdings for a given portfolio from a set of holdings

    :param LusidApi client: The LusidApi client to use
    :param str scope: The scope of the portfolio to upsert the holdings into
    :param str code: The code of the portfolio to upsert the holdings into
    :param Pandas DataFrame data_frame: The dataframe containing the data
    :param dict{str, str} holdings_mapping: The mapping of the fields from the dataframe to LUSID
    :return: response models.AdjustHolding: The response from LUSID after setting the holdings
    """
    # Initialise a list to hold the requests
    holding_adjustments = []
    # Create a Pandas Series with the column names and data types less the resolved details for property generation
    dtypes = data_frame.drop(
        ['resolvable', 'foundWith', 'LusidInstrumentId', 'comment'],
        axis=1).dtypes
    # Get all effective dates from the file
    effective_dates = list(
        data_frame[holdings_mapping_required['effective_date']].unique())
    # If there is more than one throw an error
    if len(effective_dates) > 1:
        raise Exception(
            'There are {} effective dates in the holding file, need there to be just one'
            .format(len(effective_dates)))
    # Convert the effective date to be a timezone aware datetime
    effective_date = pytz.utc.localize(parser.parse(effective_dates[0]))

    # Iterate over each holding
    for index, holding in data_frame.iterrows():

        # Set the identifier for the holding which was found earlier
        if holding['comment'] == 'Resolved as cash with a currency':
            identifier_key = 'Currency'
        else:
            identifier_key = 'LusidInstrumentId'

        identifiers = {
            'Instrument/default/{}'.format(identifier_key):
            holding['LusidInstrumentId']
        }

        # Set the properties for the holding
        properties = create_property_values(holding, -1, scope, 'Holding',
                                            dtypes)

        single_cost = models.CurrencyAndAmount(amount=None, currency=None)

        for lusid_field, column_name in holdings_mapping_optional.items():
            if (lusid_field.split(".")[1] == 'cost') and (column_name
                                                          is not None):
                setattr(single_cost,
                        lusid_field.split(".")[2], holding[column_name])

        if (single_cost.amount is None) and (single_cost.currency is None):
            single_cost = None

        single_tax_lot = models.TargetTaxLotRequest(
            units=holding[holdings_mapping_required['tax_lots.units']],
            cost=single_cost)

        for lusid_field, column_name in holdings_mapping_optional.items():
            if (lusid_field.split(".")[1] != 'cost') and (column_name
                                                          is not None):
                setattr(single_tax_lot,
                        lusid_field.split(".")[2], holding[column_name])

        single_holding_adjustment = models.AdjustHoldingRequest(
            instrument_identifiers=identifiers,
            tax_lots=[single_tax_lot],
            properties=properties)

        # Create the adjust holding request
        holding_adjustments.append(single_holding_adjustment)

    # Call LUSID to upsert the transactions
    response = client.transaction_portfolios.adjust_holdings(
        scope=scope,
        code=code,
        effective_at=effective_date,
        holding_adjustments=holding_adjustments)

    return response
コード例 #16
0
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
コード例 #17
0
    def test_set_target_holdings(self):

        currency = "GBP"

        day1 = datetime(2018, 1, 1, tzinfo=pytz.utc)
        day2 = datetime(2018, 1, 5, tzinfo=pytz.utc)

        portfolio_code = self.test_data_utilities.create_transaction_portfolio(
            TestDataUtilities.tutorials_scope)

        instrument1 = self.instrument_ids[0]
        instrument2 = self.instrument_ids[1]
        instrument3 = self.instrument_ids[2]

        holdings_adjustments = [

            # cash balance
            models.AdjustHoldingRequest(
                instrument_identifiers={
                    TestDataUtilities.lusid_cash_identifier: currency
                },
                tax_lots=[models.TargetTaxLotRequest(units=100000.0)]),

            # instrument 1
            models.AdjustHoldingRequest(instrument_identifiers={
                TestDataUtilities.lusid_luid_identifier:
                instrument1
            },
                                        tax_lots=[
                                            models.TargetTaxLotRequest(
                                                units=100.0,
                                                price=101.0,
                                                cost=models.CurrencyAndAmount(
                                                    amount=10100.0,
                                                    currency=currency),
                                                portfolio_cost=10100.0,
                                                purchase_date=day1,
                                                settlement_date=day1)
                                        ]),

            # instrument 2
            models.AdjustHoldingRequest(instrument_identifiers={
                TestDataUtilities.lusid_luid_identifier:
                instrument2
            },
                                        tax_lots=[
                                            models.TargetTaxLotRequest(
                                                units=100.0,
                                                price=102.0,
                                                cost=models.CurrencyAndAmount(
                                                    amount=10200.0,
                                                    currency=currency),
                                                portfolio_cost=10200.0,
                                                purchase_date=day1,
                                                settlement_date=day1)
                                        ])
        ]

        # set the initial holdings on day 1
        self.transaction_portfolios_api.set_holdings(
            scope=TestDataUtilities.tutorials_scope,
            code=portfolio_code,
            adjust_holding_request=holdings_adjustments,
            effective_at=day1)

        # add subsequent transactions on day 2
        transactions = [
            self.test_data_utilities.build_transaction_request(
                instrument_id=instrument1,
                units=100.0,
                price=104.0,
                currency=currency,
                trade_date=day2,
                transaction_type="Buy"),
            self.test_data_utilities.build_transaction_request(
                instrument_id=instrument3,
                units=100.0,
                price=103.0,
                currency=currency,
                trade_date=day2,
                transaction_type="Buy")
        ]

        self.transaction_portfolios_api.upsert_transactions(
            scope=TestDataUtilities.tutorials_scope,
            code=portfolio_code,
            transaction_request=transactions)

        # get the holdings for day 2
        holdings = self.transaction_portfolios_api.get_holdings(
            scope=TestDataUtilities.tutorials_scope,
            code=portfolio_code,
            effective_at=day2)

        # sort to put the cash instrument first
        holdings.values.sort(key=lambda i: i.instrument_uid)

        # cash balance + 3 holdings
        self.assertEqual(len(holdings.values), 4)

        # remaining cash balance which takes into account the purchase transactions on day 2

        # the call to GetHoldings returns the LUID not the identifier we created
        currency_luid = "CCY_{}".format(currency)

        # cash
        self.assertEqual(holdings.values[0].instrument_uid, currency_luid)
        self.assertEqual(holdings.values[0].units, 79300.0)

        # instrument 1 - initial holding + transaction on day 2
        self.assertEqual(holdings.values[1].instrument_uid, instrument1)
        self.assertEqual(holdings.values[1].units, 200.0)
        self.assertEqual(holdings.values[1].cost.amount, 20500.0)

        # instrument 2 - initial holding
        self.assertEqual(holdings.values[2].instrument_uid, instrument2)
        self.assertEqual(holdings.values[2].units, 100.0)
        self.assertEqual(holdings.values[2].cost.amount, 10200.0)

        # instrument 3 - transaction on day 2
        self.assertEqual(holdings.values[3].instrument_uid, instrument3)
        self.assertEqual(holdings.values[3].units, 100.0)
        self.assertEqual(holdings.values[3].cost.amount, 10300.0)
コード例 #18
0
    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)
コード例 #19
0
}
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,
    transaction_price=models.TransactionPrice(price=98),
    total_consideration=models.CurrencyAndAmount(amount=98 * 500,
                                                 currency="GBP"),
    source="Broker")