Modelling exchange-traded futures in LUSID

Mastering an instrument

There are numerous methods you can use to master an instrument of type Future in the LUSID Security Master.

Some fields are common to all types of instrument, such as an intuitive name, the requirement to specify a set of identifiers, and the facility to store extra information as properties.

Fields in the economic definition object are specific to Future. For more information on these fields, select Future from the definition dropdown in the API Reference:

For example, the following call to the UpsertInstruments API masters an E-mini S&P futures contract in a custom instrument scope using a ClientInternal unique identifier:

curl -X POST 'https://<your-domain>.lusid.com/api/api/instruments?scope=MyCustomInstrScope'
   -H 'Content-Type: application/json-patch+json'
   -H 'Authorization: Bearer <your-API-access-token>'
   -d '{"upsert-request-1": {
    "name": "E-mini S&P500 March 2025",
    "identifiers": {
      "ClientInternal": {
        "value": "EminiS&P500March2025"
        }
      },
    "definition": {
      "instrumentType": "Future",
      "startDate": "2024-09-21T00:00:00.0000000+00:00",
      "maturityDate": "2025-03-21T17:30:00.0000000+00:00",
      "identifiers": {},
      "contractDetails": {
        "domCcy": "USD",
        "contractCode": "ESHS",
        "contractMonth": "H",
        "contractSize": 50,
        "exchangeCode": "GLOBEX",
        "deliveryType": "Cash"
      },
      "contracts": 1,
      "markToMarketEvent": {
         calendarCode: "USD"
      },
      "refSpotPrice": 0,
    }
  }
}'

Note the following:

  • The instrumentType must be Future.

  • The nested definition.identifiers should be set to an empty object: {}.

  • The contractCode must be valid for the exchange.

  • The contracts field should be set to 1 and the amount bought or sold specified on the transaction.

  • The deliveryType defaults to Physical but should be set to Cash if you want to handle instrument events.

  • refSpotPrice should be 0 (or omitted, since this is the default) unless directed otherwise by FINBOURNE Technical Support.

Providing the request is successful, the response:

  • Confirms the globally-unique LUID for the instrument.

  • Generates extra fields that are stored as part of the instrument definition and can be filtered on.

  • Supplies default values for fields not explicitly specified in the request:

{
  "values": {
    "upsert-request-1": {
      "href": "https://<your-domain>.lusid.com/api/api/instruments/LusidInstrumentId/LUID_00003E7V?scope=MyCustomInstrScope",
      "scope": "MyCustomInstrScope",
      "lusidInstrumentId": "LUID_00003E7V",
      "name": "E-mini S&P500 March 2025",
      "identifiers": {
        "LusidInstrumentId": "LUID_00003E7V",
        "ClientInternal": "EminiS&P500March2025"
      },
      "properties": [],
      "instrumentDefinition": {
        "startDate": "2024-09-21T00:00:00.0000000+00:00",
        "maturityDate": "2025-03-21T17:30:00.0000000+00:00",
        "identifiers": {},
        "contractDetails": {
          "domCcy": "USD",
          "assetClass": "Unknown",
          "contractCode": "ESHS",
          "contractMonth": "H",
          "contractSize": 50,
          "country": "",
          "description": "",
          "exchangeCode": "GLOBEX",
          "exchangeName": "",
          "tickerStep": 0,
          "unitValue": 0,
          "deliveryType": "Cash"
        },
        "contracts": 1,
        "refSpotPrice": 0,
        "calculationType": "Invalid",
        "tradingConventions": {
          "priceScaleFactor": 1,
          "minimumOrderSize": 0,
          "minimumOrderIncrement": 0
        },
        "instrumentType": "Future"
      },
      "state": "Active",
      "assetClass": "Unknown",
      "domCcy": "USD",
      "relationships": []
    }
  },
  ...
}

Booking a transaction to establish a position

Once an instrument is mastered, you can book a transaction to record the acquisition of a quantity in a particular transaction portfolio. For example, the following call to the BatchUpsertTransactions API acquires 10 contracts of an instrument identified by LUID:

curl -X POST 'https://<your-domain>.lusid.com/api/api/transactionportfolios/FixedIncome/UK/transactions/$batchUpsert?successMode=Partial&preserveProperties=true'
  -H 'Content-Type: application/json-patch+json'
  -H 'Authorization: Bearer <your-API-access-token>'
  -d '{
  "transactionRequest-1": {
    "transactionId": "my_future_purchase_001",
    "type": "BuyFuture",
    "instrumentIdentifiers": {"Instrument/default/LusidInstrumentId": "LUID_00003E7V"},
    "transactionDate": "2024-09-25T09:00:00.0000000+00:00",
    "settlementDate": "2024-09-25T09:00:00.0000000+00:00",
    "units": 10,
    "transactionPrice": {
      "price": 6000,
      "type": "Price"
    },
    "totalConsideration": {
      "amount": 0,
      "currency": "USD"
    },
    "properties": {
      "Transaction/MyProperties/TradeCommission": {
        "key": "Transaction/MyProperties/TradeCommission",
        "value": {
          "metricValue": {
            "value": 50,
            "unit": "USD"
          }
        }
      }
    }
  }
}'

Note the following:

  • The type field invokes a custom BuyFuture transaction type to confer a particular economic impact (see below).

  • The units field specifies the number of contracts.

  • The transactionPrice object records the market price. This is used by LUSID to automatically calculate gross consideration (see below).

  • The totalConsideration object:

    • Sets the settlement currency to USD.

    • Specifies a cost of 0 to enable LUSID to automatically derive total consideration (see below).

  • The cost of entering into the contract is recorded as a custom property.

Note: This example assumes the transaction, settlement and portfolio currencies are all the same. If not, you can specify exchange rates.

You might create a BuyFuture transaction type as follows:

curl -X PUT 'https://<your-domain>.lusid.com/api/api/transactionconfiguration/types/default/BuyFuture?scope=default'
  -H 'Content-Type: application/json-patch+json'
  -H 'Authorization: Bearer <your-API-access-token>'
  -d '{
  "aliases": [
    {
      "type": "BuyFuture",
      "description": "Transaction type for future purchases",
      "transactionClass": "Basic",
      "transactionRoles": "AllRoles",
      "isDefault": false
    }
  ],
  "movements": [
    {
      "name": "Increase units",
      "movementTypes": "StockMovement",
      "side": "NotionalPurchase",
      "direction": 1
    },
    {
      "name": "Decrease cash",
      "movementTypes": "CashCommitment",
      "side": "Side2",
      "direction": -1
    }
  ],
  "calculations": [
    {
      "type": "Txn:NotionalAmount"
    },
    {
      "type": "Txn:GrossConsideration"
    },
    {
      "type": "DeriveTotalConsideration",
      "formula": "Txn:GrossConsideration + Properties[Transaction/MyProperties/TradeCommission]"
    }
  ]
}'

Note the following:

  • The Txn:NotionalInterest transaction type calculation automatically calculates the notional amount and stores the result in the Transaction/default/NotionalAmount system property, for use in the NotionalPurchase custom side (see below).

  • The Txn:GrossConsideration calculation sets the gross consideration to zero for transactions that increase a future position, and stores the result in the Transaction/default/GrossConsideration system property.

  • The DeriveTotalConsideration calculation calculates totalConsideration.amount according to the given formula, which in this case sums gross consideration and fees; this is the total cost of the transaction.

You might create a NotionalPurchase custom side as follows:

curl -X PUT 'https://<your-domain>.lusid.com/api/api/transactionconfiguration/sides/NotionalPurchase?scope=default'
  -H 'Content-Type: application/json-patch+json'
  -H 'Authorization: Bearer <your-API-access-token>'
  -d '{
  "security": "Txn:LusidInstrumentId",
  "currency": "Txn:TradeCurrency",
  "rate": "Txn:TradeToPortfolioRate",
  "units": "Txn:Units",
  "amount": "Txn:TotalConsideration",
  "notional": "Transaction/default/NotionalAmount"
}'

Note this is the same as the built-in Side1 except:

  • The amount field is set to Txn:TotalConsideration instead of Txn:TradeAmount.

  • The notionalAmount field is set to the Transaction/default/NotionalAmount system property, to handle LUSID’s calculation of notional amount.

Confirming positions

You can call the GetHoldings API to see the impact of the transaction on security and cash holdings, for example using the LUSID web app:

Auditing LUSID’s transaction type calculations

You can call the BuildTransactions API to audit LUSID’s notional amount, gross consideration and total consideration calculations, for example using the LUSID web app in Output mode:

Valuing your position

To value your position, work through our valuation checklist. For a demonstration, see sections 4 and 5 of the accompanying Jupyter Notebook.

Note: The default pricing model is SimpleStatic. We recommend changing it to ConstantTimeValueOfMoney. See how to do this.

Pricing model

Status

Notes

ConstantTimeValueOfMoney

Available

Recommended. Sets the PV to the unrealised gain whether you choose to realise it daily or not (see below).

SimpleStatic

Available

Calculates market value according to the formula quantity held * contract size * scaled price.

Assessing risk

For more on how LUSID calculates exposure, see this article.

LUSID supports both analytic and bump and valuation mechanisms for assessing risk; contact Technical Support if you need more information.

Managing P&L on a daily basis

You can choose between two models for managing P&L in a futures contract.

Mark to market model

This 'close out' mark to market model realises each day’s P&L by booking it as a cash amount and resetting the future’s cost price to today’s close (so the P&L is now 0). You can automate this process by handling the FutureMarkToMarketEvent instrument event (see below).

Cumulative P&L model

This 'non-close out' model maintains P&L as unrealised. The PV shows the lifetime unrealised gain/loss.

Monitoring the lifecycle of the instrument

An instrument representing a futures contract has a maturity date specified when you create that instrument.

As the instrument nears expiry, there are decisions to be made. Futures are almost always closed before last trade date, but you may choose to hold to maturity and take delivery of the underlying.

Note: An instrument cannot ‘expire’ in LUSID; it is still available post-maturity, although the valuation is zero. If you set your holding to zero it no longer appears in reports unless you are deliberately backdating.

Handling automatic lifecycle events

LUSID is transitioning to a system where it automatically emits lifecycle events for supported instruments. We provide default transaction templates that you can use as-is to automatically generate transactions in impacted portfolios in response to these events, and recommendations for transaction types that deliver appropriate economic impacts. More information.

Contact us to turn this feature on in your environment. The following events are available for instruments of type Future:

Instrument event type

Effect of LUSID default transaction template

Recommendations for transaction types

FutureMarkToMarketEvent

A transaction is automatically generated each weekday, with the total consideration reflecting the daily gain/loss.

See this tutorial.

FutureExpiryEvent

Note: LUSID only emits events for cash settlement. Physical settlement must be handled manually.

A transaction is automatically generated on the future's maturity date with the total realised gain/loss calculated.

Manually loading daily and final settlement transactions

If you do not want to turn on automatic instrument lifecycle events you can continue to monitor upcoming cashflows using Dashboards > CashLadder in the LUSID web app, or by calling the GetPortfolioCashLadder API directly.

You can call the GetUpsertablePortfolioCashFlows API to  return imminent cashflows as upsertable DTOs ready to manually load into LUSID as input transactions.

Note: We do not currently handle cheapest-to-deliver methods. Calendar rolls are booked as separate sells/buys.