Adding fees to a fund and automatically posting fee transactions

In this tutorial we'll see how to use LUSID to perform the following task:

“I want to add different kinds of administration fee to a fund in order to calculate fund NAV correctly.”

We'll see how to:

A Jupyter Notebook containing code examples is coming soon.

Creating a fee type to determine how LUSID should generate fee transactions

LUSID can automatically generate transactions in a particular portfolio to post fees accrued to date, and also payments to settle those fees.

We can call the CreateFeeType API to create a fee type for administration fees with two templates: one for generating fee accrual transactions, and one for generating fee payment transactions. More on creating fee types.

For example:

curl -X POST 'https://<your-domain>.lusid.com/api/api/feetypes/MyFeeTypes'
  -H 'Content-Type: application/json-patch+json'
  -H 'Authorization: Bearer <your-API-access-token>'
  -d '{
 "code": "AdminFees",
 "name": "Admin fees",
 "description": "Generating transactions to accrue and settle admin fees for funds",
 "componentTransactions": [
   {
     "displayName": "Transaction for admin fee accrual",
     "transactionFieldMap": {
       "transactionId": "{{FundFee.defaultFeeTransactionId}}-Accrual",
       "type": "FeeAccrual",
       "source": "default",
       "instrument": "{{FundFee.feeInstrument}}",
       "transactionDate": "{{FundFee.valuationPointDate}}",
       "settlementDate": "{{FundFee.valuationPointDate}}",
       "units": "{{FundFee.amount}}",
       "transactionPrice": {
         "price": "1.0",
         "type": "Price"
       },
       "transactionCurrency": "{{FundFee.feeCurrency}}",
       "exchangeRate": "1.0",
       "totalConsideration": {
         "currency": "{{FundFee.feeCurrency}}",
         "amount": "{{FundFee.amount}}"
       }
     },
     "transactionPropertyMap": []
   },
   {
     "displayName": "Transaction for admin fee payment",
     "transactionFieldMap": {
       "transactionId": "{{FundFee.defaultFeeTransactionId}}-Payable",
       "type": "FeePayment",
       "source": "default",
       "instrument": "{{FundFee.feeInstrument}}",
       "transactionDate": "{{FundFee.valuationPointDate}}",
       "settlementDate": "{{FundFee.valuationPointDate}}",
       "units": "{{FundFee.amount}}",
       "transactionPrice": {
         "price": "1.0",
         "type": "Price"
       },
       "transactionCurrency": "{{FundFee.feeCurrency}}",
       "exchangeRate": "1.0",
       "totalConsideration": {
         "currency": "{{FundFee.feeCurrency}}",
         "amount": "{{FundFee.amount}}"
       }
     },
     "transactionPropertyMap": []
   }
 ]
}'

Note the following:

  • The fee type has a unique ID consisting of a MyFeeTypes scope (specified in the URL) and an AdminFees code (specified in the request).

  • The first template in the componentTransactions collection defines transactions for fee accruals. Note the transaction type is FeeAccrualdomiciled in a source of default.

  • The second template defines transactions for fee payments. The transaction type is FeePayment, domiciled in the same default source.

All the other fields are the same for both templates. Most use Mustache template variables to populate the mandatory fields for a transaction in LUSID with data from a fee at run time:

  • The {{FundFee.defaultFeeTransactionId}} variable consists of the fee code and valuation point date. It is designed to generate a unique identifier for a transaction in a portfolio.

  • The {{FundFee.feeInstrument}} and {{FundFee.feeCurrency}} variables represent the accrual currency of the fee, for example GBP.

  • The {{FundFee.valuationPointDate}} variable represents the point in time at which you finalise the fund valuation and trigger LUSID to generate fee transactions. Note the transaction and settlement dates must be the same, so no temporary holdings nor ‘commitment’ journal entry lines are generated.

  • The {{FundFee.feeAmount}} variable is the amount accrued (and thus payable) since the last valuation.

  • The fields in the transactionPrice object should always be 1.0 and Price. These fields are informational only and not used in calculations by LUSID.

  • The exchangeRate field should always be set to 1.0. It is not possible to have different transaction and settlement currencies for fees.

Creating transaction types for fee transactions that confer a suitable economic impact

To meet the requirements of the transaction templates, we can call the SetTransactionType API to create FeeAccrual and FeePayment transaction types domiciled in the default source. More on creating transaction types.

FeeAccrual transaction type

FeePayment transaction type

curl -X PUT 'https://<your-domain>.lusid.com/api/api/transactionconfiguration/
types/default/FeeAccrual?scope=default'
  -H 'Content-Type: application/json-patch+json'
  -H 'Authorization: Bearer <your-API-access-token>'
  -d '{
  "aliases": [
    {
      "type": "FeeAccrual",
      "description": "Type for fund fee accruals",
      "transactionClass": "Basic",
      "transactionRoles": "AllRoles",
      "isDefault": false
    }
  ],
  "movements": [
    {
      "movementTypes": "CashAccrual",
      "side": "Side1",
      "direction": -1,
      "mappings": [
        {
          "propertyKey": "Transaction/SHKs/AdminFees",
          "setTo": "Admin fees"
        }
      ]
    },
    {
      "movementTypes": "Fee",
      "side": "Side1",
      "direction": -1
    }
  ]
}'
curl -X PUT 'https://<your-domain>.lusid.com/api/api/transactionconfiguration/types/default/FeePayment?scope=default'
  -H 'Content-Type: application/json-patch+json'
  -H 'Authorization: Bearer <your-API-access-token>'
  -d '{
  "aliases": [
    {
      "type": "FeePayment",
      "description": "Type for fund fee payments",
      "transactionClass": "Basic",
      "transactionRoles": "AllRoles",
      "isDefault": false
    }
  ],
  "movements": [
    {
      "movementTypes": "CashAccrual",
      "side": "Side1",
      "direction": 1,
      "mappings": [
        {
          "propertyKey": "Transaction/SHKs/AdminFees",
          "setTo": "Admin fees"
        }
      ]
    },
    {
      "movementTypes": "CashCommitment",
      "side": "Side1",
      "direction": -1,
      "mappings": [
        {
          "propertyKey": "Transaction/SHKs/CashForFees",
          "setTo": "Cash for fees"
        }
      ]
    }
  ]
}'

The first movement decreases a cash holding managed by a Transaction/SHKs/AdminFees sub-holding key (SHK) by the fee accrual amount, and generates a NA_COST journal entry line for a credit of that amount. Note the use of SHKs in this example is optional, but improves the comprehensibility of portfolio holdings.
 

The second movement generates a PL_Fees journal entry line for a debit of the fee accrual amount, to balance the above.

The first movement increases a cash holding managed by a Transaction/SHKs/AdminFees SHK by the fee payment amount, and generates a NA_COST journal entry line for a debit of that amount.
 

The second movement decreases a cash holding managed by a Transaction/SHKs/CashForFees SHK by the fee payment amount, and generates a NA_COST journal entry line for a credit of that amount, to balance the above.

Adding different kinds of administration fee to the fund

Let’s imagine we have a GBP-denominated fund with an inception date of 1 January 2024 that we value at the end of each month.

The following annual administration fees apply to this fund:

  1. A fixed printing fee of £2,500.

  2. A variable management fee of 1% of fund GAV.

  3. A variable custody fee of 0.5% of fund NAV (which is GAV minus all other fees).

To add a fee, we call the CreateFee API, specifying the scope and code of the fee type, and:

  • For a fixed fee, a totalAnnualAccrualAmount, in this case 2500.

  • For a percentage fee:

    • A feeRatePercentage expressed as a decimal, for example 0.01 to represent 1% annually, or 0.005 to represent 0.5%.

    • A calculationBase to determine the amount to calculate a percentage from, for example the entire fund GAV, or an expression to first subtract one or more fees (see below).

More information on adding fees.

For example:

Fee

API call

Fixed printing fee

curl -X POST 'https://<your-domain>.lusid.com/api/api/funds/Growth/UKEquities/fees' 
  -H 'Content-Type: application/json-patch+json'
  -H 'Authorization: Bearer <your-API-access-token>'
  -d '{
  "code": "FixedPrintingFee",
  "feeType": {"scope": "MyFeeTypes", "code": "AdminFees"},
  "name": "Fixed printing fee",
  "accrualCurrency": "GBP",
  "treatment": "Monthly",
  "totalAnnualAccrualAmount": 2500,
  "payableFrequency": "Quarterly",
  "businessDayConvention": "None",
  "startDate": "2024-01-01T00:00:00.0000000+00:00",
  "endDate": "2024-12-31T23:59:59.0000000+00:00",
}'

Variable management fee

curl -X POST 'https://<your-domain>.lusid.com/api/api/funds/Growth/UKEquities/fees'
  -H 'Content-Type: application/json-patch+json'
  -H 'Authorization: Bearer <your-API-access-token>'
  -d '{
  "code": "VariableManagementFee",
  "feeType": {"scope": "MyFeeTypes", "code": "AdminFees"},
  "name": "Variable management fee",
  "accrualCurrency": "GBP",
  "treatment": "Monthly",
  "feeRatePercentage": 0.01,
  "calculationBase": "GAV",
  "payableFrequency": "Quarterly",
  "businessDayConvention": "None",
  "startDate": "2024-01-01T00:00:00.0000000+00:00",
  "endDate": "2024-12-31T23:59:59.0000000+00:00",
}'

Variable custody fee

curl -X POST 'https://<your-domain>.lusid.com/api/api/funds/Growth/UKEquities/fees'
  -H 'Content-Type: application/json-patch+json'
  -H 'Authorization: Bearer <your-API-access-token>'
  -d '{
  "code": "VariableCustodyFee",
  "feeType": {"scope": "MyFeeTypes", "code": "AdminFees"},
  "name": "Variable custody fee",
  "accrualCurrency": "GBP",
  "treatment": "Monthly",
  "feeRatePercentage": 0.005,
  "calculationBase": "GAV-Fees[FixedPrintingFee].Amount-Fees[VariableManagementFee].Amount",
  "payableFrequency": "Quarterly",
  "businessDayConvention": "None",
  "startDate": "2024-01-01T00:00:00.0000000+00:00",
  "endDate": "2024-12-31T23:59:59.0000000+00:00",
}'

Note the following:

  • All fees reference the feeType created in the first section.

  • All fees have a treatment of Monthly, which means LUSID calculates the amount accrued to date by dividing the annual amount by 12 and multiplying by the day number of the valuation point. The alternative is Daily, in which case LUSID divides the annual amount by 365 days (or 366 in a leap year) and multiplies by the day number of the valuation point. The two treatments generate slightly different accrual amounts for a given date (see the examples below). 

  • All fees have a paymentFrequency of Quarterly, which means that payment transactions are generated four times a year. In our example, the anchor date is the default of 1 January, so payment transactions are generated on 1 April, 1 July, 1 October, and 1 January the following year.

  • All fees have a businessDayConvention of None, which means LUSID treats weekends as normal business days for the purposes of calculating fee accruals.

  • The variable management fee has a calculationBase of GAV, which means LUSID uses the entire fund GAV at the valuation point time as the annual basis for calculating 1%.

  • The variable custody fee has a calculationBase of GAV-Fees[FixedPrintingFee].Amount-Fees[VariableManagementFee].Amount, which means LUSID first subtracts the other fees accrued to date before using the remainder at the valuation point time as the annual basis for calculating 0.5%.

Publishing a fund valuation at end January

On 31 January at 5pm we call the GetValuationPointData API to generate an estimate of fund value. More on valuing funds.

For example:

We can see that the fund GAV on 31 January is 400000.

With this GAV in mind, LUSID calculates fees as follows:

Fee

Since fee treatment is Monthly...

If fee treatment was Daily...

Calculation

Accrual on 31 January

Calculation (2024 being a leap year)

Accrual on 31 January

Fixed printing fee

2500 / 12

208.33

(2500 / 366) * 31

211.75

Variable management fee

(400000 x 0.01) / 12

333.33

((400000 * 0.01) / 366) * 31

338.80

Variable custody fee

((400000 - 208.33 - 333.33) * 0.005) / 12

166.44

(((400000 - 211.75 - 338.80) * 0.005) / 366) * 31

169.17

TOTAL

 

708.10

 

719.72

The fund NAV on 31 January is calculated as GAV minus fees accrued to date, so:

  • Since fee treatment is Monthly, this is 400000 - 208.33 - 333.33 - 166.44 = 399291.90.

  • If fee treatment was Daily, this would be 400000 - 211.75 - 338.80 - 169.17 = 399280.28.

If this estimate is correct we call the AcceptEstimateValuationPoint API to finalise the valuation for January. Providing nothing has changed, LUSID persists the data and automatically generates fee transactions.

Examining the impact of fee transactions at end January

When the fund valuation is finalised, LUSID automatically generates one fee accrual transaction dated 31 January for each fee, for the amount accrued to date.

In our example, LUSID does not create corresponding fee payment transactions on 31 January because the payableFrequency for all fees is Quarterly, anchored from 1 January 2024. This means the first accrual payment transactions are due on 1 April.

Transactions

LUSID generates fee accrual transactions as input transactions in a particular transaction portfolio underlying the fund; we can call the GetTransactions API to examine them (in this case using the LUSID web app):

Note: LUSID simultaneously transforms these input transactions to output transactions, with extra information such as realised gain/loss.

Holdings

We can call the GetHoldings API to examine the impact of fee accrual transactions on holdings for the underlying portfolio:

Journal entry lines

We can call the GetJournalEntryLines API to examine the balanced pairs of journal entry lines generated by fee accrual transactions: