Latest release notes#

For information about platform versions, please see:

For information on previous framework versions, please see:

v9.5#

New to v9?

See Introduction to v9.0 for help updating your code.

Improvements#

Rolling strategies: Improved consistency across rolling strategy classes with RollingStrategyBase class#

Previously, certain methods were only available for RollingFuturesStrategy, but not for other rolling strategy classes.

To improve consistency between rolling strategy classes, RollingStrategyBase base class has been created. It contains the same attributes and methods that are shared between all its subclasses.

The rolling_schedule method and specialized rolling_table property have now been implemented in all rolling strategy classes, including:

  • RollingSwapStrategy

  • RollingOptionStrategy

  • RollingBondStrategy

  • RollingCreditIndexStrategy

  • RollingStructureBasketStrategy

  • RollingFXForwardStrategy

  • RollingOptionsStrategyBase

  • RFPriceIndex

rolling_schedule returns the roll schedule, while rolling_table returns the strategy’s rolling table as a pandas DataFrame.

Code example:

import datetime as dtm
import sigtech.framework as sig

sig.init()

rc = sig.RollingOptionStrategy(
    currency="USD",
    start_date=dtm.date(2018, 1, 4),
    end_date=dtm.date(2019, 1, 4),
    group_name="USDJPY OTC OPTION GROUP",
    option_type="Call",
    maturity="3M",
    rolling_frequencies=["1M"],
    strike_type="SPOT",
    strike="ATM+10%",
)

_rolling_schedule = rc.rolling_schedule())
_rolling_table = rc.rolling_table

Rolling options strategy: Added scaling to strategy cash#

Previously, RollingOptionStrategy would trade one option or one option strategy by default, regardless of how much initial_cash was held.

To improve the behavior of the RollingOptionStrategy class, cash needs to be taken into consideration to size the number of options. To enable this, two new additions have been introduced:

  • New target_type ("PVAsProportionOfNAV") enables the sizing of the number of options depending on the net asset value (NAV), rather than a fixed amount of present value (PV).

  • New parameter greek_neutral_nav_scale enables the sizing of the number of delta-neutral structures that the strategy would hold. This also fixes the issue of when the target_type is a Greek (e.g. `target_type=’Delta’).

As a result, RollingOptionStrategy now sizes the number of options using the cash.

Code example to test PVAsProportionOfNAV:

rc = sig.RollingOptionStrategy(
    currency='USD',
    start_date=dtm.date(2018, 1, 4),
    end_date=dtm.date(2018, 6, 4),
    group_name=g,
    option_type='Call',
    maturity='3M',
    rolling_frequencies=['1M'],
    strike_type='SPOT',
    strike='ATM',
    target_type='PVAsProportionOfNAV',
    target_quantity=1,
)
rc.history()

Code example to test greek_neutral_nav_scale:

butterfly = sig.RollingButterflyOptionsStrategy(
    start_date=dtm.date(2018, 1, 4),
    end_date=dtm.date(2019, 7, 4),
    initial_cash=1000,
    group_name=group.name,
    rolling_frequencies=['3M'],
    maturity='12M',
    option_type='Call',
    butterfly_direction='long',
    strike_type='SPOT',
    strike_1='ATM',
    strike_2='ATM+3%',
    strike_3='ATM+5%',
    target_type='Delta',
    target_quantity=0,
    greek_neutral_nav_scale=1
)
butterfly.history()

Options: metrics method returns vega weighted implied volatility#

Previously when calling the metrics method on an options strategy, it would return the sum of all Greeks, as well as the average implied volatility of the options held.

To improve the output, a ‘Vega Weighted Implied Volatility’ column is now added to any metrics method output. This is particularly useful for portfolios holding options with different expiries.

Code example:

import datetime as dtm
import sigtech.framework as sig
import datetime as dtm

sig.init()

d = dtm.date(2020, 2, 24)
group = sig.obj.get('EURUSD OTC OPTION GROUP')

def basket(strategy, dt, positions, **additional_parameters):
    size_date = strategy.size_date_from_decision_dt(dt)
    call = group.get_option(start_date=size_date, option_type='Call', strike=0.25, strike_type='Delta', maturity='3M')
    put = group.get_option(start_date=size_date, option_type='Put', strike=-0.25, strike_type='Delta', maturity='3M')
    return {call:1, put:-100}

strat = sig.DynamicOptionsStrategy(
    currency='GBP',
    start_date=d,
    rolling_frequencies=['1M'],
    group_name=group.name,
    basket_creation_method=basket,
    initial_cash=0,
    total_return=False,
    t0_option_selection=True,
    close_out_at_roll=True,
    target_quantity=100,
    target_type='Vega',
    net_target_quantity=False,
    data_points=['NEW_YORK_1700']

)

strat.metrics()

Options: Improved calculations by adding net_target_quantity flag variable to get net Greeks in option strategies#

Previously, the greeks and metrics methods for option strategies would only take the sum of all Greeks in an options strategy.

greeks and metrics methods for option strategies now match the net_target_quantity flag. You can choose whether to net Greeks or not by setting the net_target_quantity variable:

  • If set to True, the sum of values is calculated

  • If set to False, the sum of absolute values is calculated

Code example:

import datetime as dtm
import sigtech.framework as sig
import datetime as dtm
sig.init()

d = dtm.date(2020, 2, 24)
group = sig.obj.get('EURUSD OTC OPTION GROUP')

def basket(strategy, dt, positions, **additional_parameters):
    size_date = strategy.size_date_from_decision_dt(dt)
    call = group.get_option(start_date=size_date, option_type='Call', strike=0.25, strike_type='Delta', maturity='3M')
    put = group.get_option(start_date=size_date, option_type='Put', strike=-0.25, strike_type='Delta', maturity='3M')
    return {call:1, put:-1}

strat = sig.DynamicOptionsStrategy(
    currency='USD',
    start_date=d,
    end_date=dtm.date(2020, 3, 30),
    rolling_frequencies=['1BD'],
    group_name=group.name,
    basket_creation_method=basket,
    initial_cash=0,
    total_return=False,
    t0_option_selection=True,
    close_out_at_roll=True,
    target_quantity=100,
    target_type='Vega',
    net_target_quantity=False
)

strat.greeks(dollar_greeks=True, net_greeks=False)

New features#

New methods available for obtaining volatility series#

Added new methods to get historical butterfly volatility, at-the-money volatility, and volatility for a given tenor and delta for equity. You can use these to get a volatility series for certain delta on the volatility surface.

The following code blocks demonstrate the different methods for getting volatility series:

import sigtech.framework as sig
sig.init()

vs = sig.obj.get('SPX INDEX VOLSURFACE')

Get 1Y tenor 50 delta risk reversal:

vs.risk_reversal(tenor='1Y', delta=50).plot()

Get 1Y tenor 50 delta butterfly:

vs.butterfly(tenor='1Y', delta=50).plot()

Get 1Y tenor at-the-money volatility:

vs.atm_vols_for_tenor('1Y').plot()

Get 1Y tenor 25 delta call volatility:

vs.vols_for_tenor(delta=25, tenor='1Y')

Get 1Y tenor 25 delta put volatility:

vs.vols_for_tenor(delta=-25, tenor='1Y')

FX: Implemented volatility and forward curve shifts for FX#

When implementing volatility or forward curve shifts for FX, the change_type parameter can now be set to either of the following arguments:

  • 'RETURN': Percentage change

  • 'DIFF': Absolute value change

The following code blocks demonstrate a volatility curve shift:

import sigtech.framework as sig
from sigtech.framework.infra.data_adapter.simulations.simulation_adapter import SimAdapter
import datetime as dtm
sig.init()

### VOL CURVE SHIFT ###
vol_curve_ticker = 'EURUSD VOLSURFACE'

Shift volatility curve to at-the-money (ATM) strike of 1.25:

sim_adapter = SimAdapter()
sim_adapter.push_to_data_service()
sim_adapter.add_overwrite_vol_curve_modifier(
    instruments=[vol_curve_ticker],
    overwrites={dtm.date(2009, 3, 2): 1.25},
    change_type='PRICE',
)
sig.obj.get(vol_curve_ticker).history()

Shift volatility curve horizontally by 0.1 to the right, so new_ATM_strike = old_ATM_strike + 0.1:

sim_adapter = SimAdapter()
sim_adapter.push_to_data_service()
sim_adapter.add_overwrite_vol_curve_modifier(
    instruments=[vol_curve_ticker],
    overwrites={dtm.date(2009, 3, 2): 0.1},
    change_type='DIFF',
)
sig.obj.get(vol_curve_ticker).history()

Shift volatility curve horizontally by 2% of spot price to the left, so new_ATM_strike = old_ATM_strike - (2% * spot)

sim_adapter = SimAdapter()
sim_adapter.push_to_data_service()
sim_adapter.add_overwrite_vol_curve_modifier(
    instruments=[vol_curve_ticker],
    overwrites={dtm.date(2009, 3, 2): -0.02},
    change_type='RETURN',
)
sig.obj.get(vol_curve_ticker).history()

Forward curve shifts also take an additional overwrite_type parameter:

  • 'POINT': Single value overwrite

  • 'FLAT': Overwrite all tenor values

  • 'PARALLEL': Overwrite all tenors and keep absolute difference between them

  • 'RATIO': Overwrite all tenors and keep relative difference between tenors

The following code blocks demonstrate a forward curve shift:

### FWD CURVE SHIFT ###
fwd_curve_ticker = 'EURUSD FORWARD RATE CURVE'

Overwrite only 3 Feb 2009 tenor to 1.0:

sim_adapter = SimAdapter()
sim_adapter.push_to_data_service()
sim_adapter.add_overwrite_curve_modifier(
    instruments=[fwd_curve_ticker],
    overwrites={dtm.date(2009, 2, 3): 1.0},
    change_type='PRICE',
    overwrite_type='POINT'
)
sig.obj.get(fwd_curve_ticker).history()

Shift all tenor values by 0.098 upwards from 3 Feb 2009 onwards:

sim_adapter = SimAdapter()
sim_adapter.push_to_data_service()
sim_adapter.add_overwrite_curve_modifier(
    instruments=[fwd_curve_ticker],
    overwrites={dtm.date(2009, 2, 3): 0.098},
    change_type='DIFF',
    overwrite_type='PARALLEL'
)
sig.obj.get(fwd_curve_ticker).history()

Overwrite all tenor values on 3 Feb 2009 to 4 Jan 2005 original value +9%:

sim_adapter = SimAdapter()
sim_adapter.push_to_data_service()
sim_adapter.add_overwrite_curve_modifier(
    instruments=[fwd_curve_ticker],
    overwrites={dtm.date(2009, 2, 3): 0.09},
    change_type='RETURN',
    overwrite_type='FLAT'
)
sig.obj.get(fwd_curve_ticker).history()

Overwrite 3 Feb 2009 to 0.97 and adjust all other tenors on 3 Feb 2009 to keep the relative difference between tenors:

sim_adapter = SimAdapter()
sim_adapter.push_to_data_service()
sim_adapter.add_overwrite_curve_modifier(
    instruments=[fwd_curve_ticker],
    overwrites={dtm.date(2009, 2, 3): 0.97},
    change_type='PRICE',
    overwrite_type='RATIO'
)
sig.obj.get(fwd_curve_ticker).history()