Skip to main content

Sample formulas

  • Name - descriptive name.

  • Category - general category as to which the number relates to, for example Performance, Risk, Basic.

  • Description - short description of what the result is saying and its use-case.

  • Formula - the full code of the working formula.

Name

Category

Description

Formula

Recovery Rate

Performance

Days until next highest peak in period after a Maximum Drawdown.

Return value represented in days.

import com.fasolutions.data.dto.analytics.IndexedReturnDataDTO;
import java.time.*
def orig = new IndexedReturnDataDTO()
orig.setIndexValueLowest(100d);
orig.setIndexValueHighest(100d);
orig.setMaxDrawdown(0d);
double previousIndexedValue = 0.0
double  highAfterMdd = 0.0
LocalDate mddTroughDate = LocalDate.now()
LocalDate recoveryDate = LocalDate.now()
int daysAfterMdd = 0
if(data){
    data.each{ date,ird ->
        if(mddTroughDate == null) mddTroughDate = date
        if(recoveryDate == null) recoveryDate = date
        if( ird.getIndexedValue() != 0 ) {
            if( ird.getIndexedValue() < orig.getIndexValueLowest() ) {
                orig.setIndexValueLowest( ird.getIndexedValue());
            }
            if( ird.getIndexedValue() > orig.getIndexValueHighest() ) {
                orig.setIndexValueHighest( ird.getIndexedValue());
            }
            orig.setDrawdown(ird.getIndexedValue() / orig.getIndexValueHighest()  - 1 );
            if( orig.getDrawdown() < orig.getMaxDrawdown() ){
                orig.setMaxDrawdown(orig.getDrawdown());
                mddTroughDate = date
                daysAfterMdd = 0
                highAfterMdd = ird.getIndexedValue()
            } else {
                if(ird.getIndexedValue() > previousIndexedValue ){
                    highAfterMdd = ird.getIndexedValue()
                    daysAfterMdd += 1
                    recoveryDate = date
                }
            } 
        }
        previousIndexedValue = ird.getIndexedValue()
    }
}
return daysAfterMdd

Monthly Volatility

Risk

Monthly volatility as defined by the standard deviation of monthly observations.

sample_formulas.png
import com.fasolutions.analytics.*
import com.fasolutions.data.analytics.*

def monthlyData = AnalyticsUtil.monthlyData(data)
def monthlyIndexedValueChanges = [] 
def prev 
double monthlyVolatility = 0.0
monthlyData.each { k,v ->
    if( prev != null ) {
        monthlyIndexedValueChanges << v.indexedValue / prev.indexedValue - 1
        
    }
    prev = v
}

if(!monthlyIndexedValueChanges?.isEmpty()) monthlyVolatility = AnalyticsUtil.stats(monthlyIndexedValueChanges as double[]).standardDeviation * Math.sqrt(12)

return monthlyVolatility

Key Figure

Utility

Showcase of how to retrieve any key figure from the security object in the context. In this case the “CO2” key figure on date 2018-01-01

Can be used in other formulas by replacing the key and date.

import java.time.LocalDate
import com.fasolutions.data.dto.analytics.CustomColumnValueDTO
String returnString = ""
if(security){
    CustomColumnValueDTO customColumnValue = security.getKeyFigureValue("CO2",LocalDate.of(2018,1,1))
    if(customColumnValue != null) returnString = customColumnValue.getDefaultValue()
} 
return returnString

Market Value (sec)

Basic

Market value of position/group in its local currency.

double marketValueSec = 0.0d;
if(Double.compare( analytics.marketFxRate, 0.0d ) == 0) marketValueSec = analytics.marketValue
else marketValueSec = analytics.marketFxRate * analytics.marketValue
return marketValueSec

Custom Total Profit

Example

For illustrative/example purposes.

Example how to get total profits from the subtree attached to every GrouppedAnalytics with a formula.

Note that since the formula sums up values from its children, it only works on group levels! Therefore you need to use the Apply formula also on group level aggregation setting.

import com.fasolutions.analytics.GrouppedAnalytics
import com.fasolutions.data.dto.analytics.IndexedReturnDataDTO;
import java.time.LocalDate
import java.util.NavigableMap

double profitFromSubtree = 0.0d //value to return = total profits (realized+unrealized+other)

//Sub-tree of underlying GrouppedAnalytics levels wrt current level
Map<String,GrouppedAnalytics> subGrouppedAnalyticsTree = ga?.subGrouppedAnalyticsTree

//Navigate all underlying groups (e.g. securities)
subGrouppedAnalyticsTree?.each{ orderedCode, subGrouppedAnalytics -> 

    //Get underlying data for current group and navigate the daily positions
    NavigableMap<LocalDate,IndexedReturnDataDTO> dataFromGA = subGrouppedAnalytics?.getData()
    dataFromGA?.each{ date,ird -> 
        profitFromSubtree += ird.dSoldProfit + ird.dValueChange + ird.dProfit //realized+unrealized+other
    }
}
return profitFromSubtree

TWR Gross

Testing / Reconciliation

For testing purposes when changes are done to TWR.

Can be modified and used in future changes to TWR calculation

import com.fasolutions.data.dto.analytics.IndexedReturnDataDTO
import java.util.NavigableMap
import com.fasolutions.math.var.Tools
import java.lang.Math


double twrGross = 0.0d;
if (data) {
    IndexedReturnDataDTO sIrd = ((NavigableMap)data).firstEntry()?.getValue()
    IndexedReturnDataDTO eIrd = ((NavigableMap)data).lastEntry()?.getValue()
    double previousIndexedValue = 100.0d;
    double previousGrossIndexedValue = 100.0d;
    double previousCostIndexedValue = 100.0d;
    double grossMarketValueStart = sIrd.getMarketValueStart();
    
    data?.each{ date, ird -> 
        double startMarketValue = ird.getMarketValueStart() +  ird.getPosNetCashflow();
        double baseMarketValue =  Math.abs(startMarketValue);
        double grossIndexedValueChangeDaily  = 0;
        double dailyCosts = 0d;
        
        
        if( baseMarketValue != 0 ) {
            dailyCosts = ird.getdCost() + ird.getdOtherCost() + ird.getdImplicitCost();
            grossIndexedValueChangeDaily = Tools.change2logYield(dailyCosts, baseMarketValue);
            if (Double.isInfinite(grossIndexedValueChangeDaily )) {
                grossIndexedValueChangeDaily  = 0;
            }
        }
        
        
        double grossMarketValue = grossMarketValueStart + ird.getdMarketValue() + ird.getdCost() + ird.getdOtherCost() + ird.getdImplicitCost() // replace implicit with ird.getdTotalExPostSecCosts() to get previous method
        double dGrossMarketValue = grossMarketValue - grossMarketValueStart
        double costIndexedValue = previousCostIndexedValue + Tools.logYield2perYield(grossIndexedValueChangeDaily) * previousIndexedValue; //replace previousIndexedValue with 100.0d to get previous method (as of 2020-04-15)
        double grossIndexedValue = ird.getIndexedValue() + costIndexedValue - 100d ;

        previousIndexedValue = ird.getIndexedValue();
        previousGrossIndexedValue = grossIndexedValue
        previousCostIndexedValue = costIndexedValue
        grossMarketValueStart = grossMarketValue
        
        
    }
    
    twrGross = previousGrossIndexedValue / 100.0d - 1;
}


return twrGross

Options Call Delta

Options/Template

Template for calcuating Black-Scholes Option Greeks using Normal distribution from stats library.

Can be configured and extended to fetch strike price, rate and/or price of underlying via other components such as key figures

import com.fasolutions.math.stat.Normal
import java.time.LocalDate;
import java.time.temporal.ChronoUnit

//Components
double price_underlying, strike, time, volatility, rate //input
double d1, d2, p1, p2, pdf1, pdf2 //calcs
double deltac, deltap, gamma, vega //output

//Vars needed
strike = 100.0 //could be retrieved with security?.maturityPrice
price_underlying = analytics?.marketUnitPrice
volatility = analytics?.volAnn
rate = 0.01 //1 %

//Date range - also needed
LocalDate fromDate = analytics?.startDate //analytics?.marketUnitPriceDate or endDate
LocalDate toDate = analytics?.endDate //security?.callDate


if(fromDate && toDate) {
    //Calculate time
    float years = fromDate?.until(toDate, ChronoUnit.DAYS) / 365.2425f
    time = (double)years
    
    //Main params calculation
    double priceOverStrike = strike != 0.0 ? price_underlying.div(strike) : price_underlying
    d1 = ( Math.log(priceOverStrike) + time * (rate + Math.pow(volatility,2))?.div(2) )?.div(volatility*Math.sqrt(time))
    d2 = d1 - volatility*Math.sqrt(time);
    p1 = Normal.cdf(d1);
    p2 = Normal.cdf(d2);
    pdf1 = Normal.pdf(d1);
    pdf2 = Normal.pdf(d2);
    
    
    //Call/Put price
    callprice = price_underlying*Math.exp(time*(rate))*p1 - strike*Math.exp(-rate*time)*p2;
    //putprice = strike*Math.exp(-rate*time)*Normal.cdf(-d2) - price_underlying*Math.exp(time*(rate))*Normal.cdf(-d1);
    
    //Greeks calculation
    deltac = Math.exp(time*(rate))*p1;
    //deltap = Math.exp(time*(rate))*(p1-1);
    //gamma = pdf1*Math.exp(time*(rate))/(price_underlying*volatility*Math.sqrt(time));
    //vega = price_underlying*Math.exp(time*(rate))*pdf1*Math.sqrt(time);
    
}


return deltac

Key Figure

Utility

Showcase of using a key figure in calculation

import java.time.LocalDate
import com.fasolutions.data.dto.analytics.CustomColumnValueDTO
double delta = 0.0 //value to return
double marketValue = analytics?.marketValue //value to use in calculation

//Assume Delta is some value stored on security level
CustomColumnValueDTO customColumnValue = security?.getKeyFigureValue("Delta")
if(customColumnValue != null){
 delta = customColumnValue.getDoubleValue() * marketValue  //use in calc
}

return delta 

TWR (pre 3.8)

Reconciliation

Showcase of how TWR is calculated for non-future instruments in version before FA 3.8

Can be used to test and reconcile TWR (and was in FA 3.8 testing when method changed)

import com.fasolutions.data.dto.analytics.IndexedReturnDataDTO
import java.util.NavigableMap
import com.fasolutions.math.var.Tools
import java.lang.Math


double twrOrig = 0.0d;
if (data) {
    IndexedReturnDataDTO sIrd = ((NavigableMap)data).firstEntry()?.getValue()
    IndexedReturnDataDTO eIrd = ((NavigableMap)data).lastEntry()?.getValue()
    double previousIndexedValue = 100.0d;
    double marketValueStart = sIrd.getMarketValueStart();
    
    
    data?.each{ date, ird -> 
        double startMarketValue = ird.getMarketValueStart() +  ird.getPosNetCashflow();
        double baseMarketValue =  Math.abs(startMarketValue);
        double indexedValueChangeDaily = ird.getIndexedValueChange();
        double dailyTotalProfitAndLoss = ird.getdMarketValue() -ird.getNegNetCashflow() - ird.getPosNetCashflow();
        
        if( baseMarketValue != 0 ) {
            indexedValueChangeDaily = Tools.change2logYield(dailyTotalProfitAndLoss,baseMarketValue);
        } else {
            indexedValueChangeDaily = 0;
        }
        
        if (Double.isInfinite(indexedValueChangeDaily)) {
            indexedValueChangeDaily = 0;
        }
        
        
        double indexedValue = Tools.logYield2value(indexedValueChangeDaily, previousIndexedValue);
        previousIndexedValue = indexedValue
    }
    
    twrOrig = previousIndexedValue / 100.0d - 1;
}


return twrOrig

Money Weighted Return

Performance

Money weighted return retrieved from Internal Rate of Return (IRR) and scaled to the holding period.

Note that IRR is only calculated when the column is selected or when using "calculateIrr": true setting when using Analytics over API. Since this custom column uses IRR, it is also only shown if IRR is calculated.

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;

double mwr = 0.0d
double investmentPeriod = 0.0d;
double daysInYear = 365.0d;

//Start/End by default position-based
LocalDate startDate = analytics.startDate
LocalDate endDate = analytics.endDate

if(startDate && endDate)
  investmentPeriod = ChronoUnit.DAYS.between(startDate, endDate) / daysInYear

if(investmentPeriod > 0)
    mwr = Math.pow(1+analytics.irr, investmentPeriod) - 1 //(1+IRR)^(t/365) -1

return mwr