Framework v9

Framework v9#

New to v9?

See Guide to v9.0 for help with updating your code.

v9.5#

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()

v9.4#

Improvements#

data_dict and data_dict_group methods return all object attributes#

data_dict and data_dict_group methods can be used on any framework object to return a dictionary of available attributes for that object. This makes it easier for you to see the required parameters of a building block, as it provides a clearer idea of the available functionality.

data_dict method now accepts three arguments: include_private, include_optional, and include_inherited. You can use this method on every framework object to return a dictionary containing static data and the required fields needed by the class constructor. By default, optional and inherited attributes are included.

Code example:

sig.obj.get('SPX INDEX').data_dict(include_optional=False)

New method data_dict_group has been added. You can also use the method on any framework object to return the static data in a dictionary. The attributes are grouped by attribute group type: AttrGroup.OPTIONAL, AttrGroup.INHERITED, and AttrGroup.PRIVATE.

Code example:

from sigtech.framework.infra.objects.dtypes import AttrGroup

sig.obj.get('SPX INDEX').data_dict_group(AttrGroup.INHERITED)

New features#

otr_series: Added country-dependent tenors#

Country-dependent bond tenors have been introduced to on-the-run (OTR) series, making them more flexible.

otr_series can be used to select specific OTR bonds from each country. Code example to test:

import sigtech.framework as sig
sig.init()
sig.obj.get('BE GOVT BOND GROUP').otr_series('8Y')

Below is a dictionary of the available tenors for the new otr_series:

bond_dict = {
    'US':['2Y','3Y','5Y','7Y','10Y', '20Y', '30Y'],
    'GB':['3Y','5Y','7Y','10Y', '30Y'],
    'JP':['2Y','3Y','5Y','10Y', '20Y', '30Y', '40Y'],
    'DE':['2Y','5Y','7Y','10Y', '30Y'],
    'FR':['3Y','5Y', '10Y', '30Y'],
    'IT':['2Y','3Y','5Y','7Y','10Y', '30Y'],
    'CA':['2Y','5Y','10Y','30Y'],
    'ES':['3Y','5Y','7Y','10Y', '15Y', '30Y'],
    'BE':['5Y','8Y','10Y', '20Y', '30Y'],
    'SE':['10Y'],
    'NO':['10Y'],
}

Strategies can now take objects instead of strings as parameters#

Added a new input field that allows you to pass either strategy objects or strategy object names when building a strategy. As a result, you can now pass the strategy object directly, instead of having to use the strategy string name (constituent_names).

This feature is available with the following strategy classes:

  • BasketStrategy

  • DeltaHedgingStrategy

  • FeeStrategy

  • FXForwardHedgingStrategy

The code example below shows a basket strategy that takes objects instead of string names:

from sigtech.framework.default_strategy_objects.rolling_futures import es_index_front, bo_comdty_f_0
import sigtech.framework as sig

env = sig.init(log_level='WARNING')

basket = sig.BasketStrategy(
    constituents=[es_index_front(), bo_comdty_f_0()],
    weights=[0.6, 0.4],
    rebalance_frequency='EOM',
)
basket_2.history()

Fixes#

  • Used contract names in RollSchedule.get_first_valid_series_contract.

  • Set net_target_quantity=False for target_type PV, SpotNotional and SpotNotionalAsProportionOfNAV.

  • More accurate error messages.

  • Fixed ReinvestmentStrategy bug when single stock is delisted.

  • Fixed RFPriceIndex and RollingFutureStrategy price_series bug when no roll-out contracts.

  • Reordered dates in DynamicOptionStrategy dependencies.

Single stocks: Created cash_index for MXN, EAD, DKK and IND currencies#

Previously when building reinvestment strategies using single stocks, there would be a warning saying the cash index was missing. The following currencies experienced this issue: MXN, EAD, DKK, IND.

cash_index has now been configured for those currencies. Code example to test:

ss = sig.obj.get('7146413.SINGLE_STOCK.TRADABLE')
test = sig.ReinvestmentStrategy(
        currency=ss.currency,
        underlyer=ss.name,
        start_date=dtm.date(2018, 1, 1),
    )
test.history()

v9.3#

Improvements#

Improved swap_details method for cross currency swaps#

More details have been added to the swap_details method for cross currency swaps. As a result, this matches the cash flow view of Bloomberg, improves usability in line with industry standards, and enables better understanding of the pricing.

For example, the ability to display results as a dataframe using the as_df feature has been added, making cross currency swaps more consistent with other swaps instruments on the SigTech platform.

import datetime as dtm
import numpy as np
import pandas as pd

import sigtech.framework as sig

env = sig.init()

swap = sig.CrossCurrencySwap(
    currency="USD",
    pay_currency="EUR",
    tenor=dtm.date(2025, 7, 5),
    pay_notional=1000000,
    trade_date=dtm.date(2021, 7, 5),
    swap_type='FloatFloat',
    use_notional_reset=True,
)
swap.swap_details(as_df=True)

Options: Added volatility and forward curve shift for absolute and relative change in equity index options#

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

RETURN: Percentage change DIFF: Absolute value change

The following code examples explore volatility curve shift. Run each code block in a separate cell and reinitialize the environment for each example:

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

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

env = sig.init(repeat_mode="reinit")

vol_curve_ticker = "SPX INDEX VOLSURFACE"
fwd_curve_ticker = "SPX INDEX FORWARD RATE CURVE"

sim_adapter = SimAdapter()
sim_adapter.push_to_data_service()
sim_adapter.add_overwrite_vol_curve_modifier(
    instruments=[vol_curve_ticker],
    overwrites={dtm.date(2005, 1, 3): 799.0},
    change_type="PRICE",
)
sig.obj.get(vol_curve_ticker).history()

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

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

env = sig.init(repeat_mode="reinit")

vol_curve_ticker = "SPX INDEX VOLSURFACE"
fwd_curve_ticker = "SPX INDEX FORWARD RATE CURVE"

sim_adapter = SimAdapter()
sim_adapter.push_to_data_service()
sim_adapter.add_overwrite_vol_curve_modifier(
    instruments=[vol_curve_ticker],
    overwrites={dtm.date(2005, 1, 3): 50.0},
    change_type="DIFF",
)
sig.obj.get(vol_curve_ticker).history()

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

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

env = sig.init(repeat_mode="reinit")

vol_curve_ticker = "SPX INDEX VOLSURFACE"
fwd_curve_ticker = "SPX INDEX FORWARD RATE CURVE"

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

For forward curve shift, you can also set the overwrite_type parameter with any of the following arguments:

  • POINT: Overwrite single value

  • FLAT: Overwrite all tenor values

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

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

The code blocks below explore forward curve shift.

Overwrite only 04 Jan 2005 tenor to 600:

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

env = sig.init(repeat_mode="reinit")

vol_curve_ticker = "SPX INDEX VOLSURFACE"
fwd_curve_ticker = "SPX INDEX FORWARD RATE CURVE"

sim_adapter = SimAdapter()
sim_adapter.push_to_data_service()
sim_adapter.add_overwrite_curve_modifier(
    instruments=[fwd_curve_ticker],
    overwrites={dtm.date(2005, 1, 4): 600.0},
    change_type="PRICE",
    overwrite_type="POINT"
)
sig.obj.get(fwd_curve_ticker).history()

Shift all tenor values by 95 upwards from 04 Jan 2005 onwards:

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

env = sig.init(repeat_mode="reinit")

vol_curve_ticker = "SPX INDEX VOLSURFACE"
fwd_curve_ticker = "SPX INDEX FORWARD RATE CURVE"

sim_adapter = SimAdapter()
sim_adapter.push_to_data_service()
sim_adapter.add_overwrite_curve_modifier(
    instruments=[fwd_curve_ticker],
    overwrites={dtm.date(2005, 1, 4): 95.0},
    change_type="DIFF",
    overwrite_type="PARALLEL"
)
sig.obj.get(fwd_curve_ticker).history()

Overwrite all tenor values on 04 Jan 2005 to 04 Jan 2005 original value +5%:

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

env = sig.init(repeat_mode="reinit")

vol_curve_ticker = "SPX INDEX VOLSURFACE"
fwd_curve_ticker = "SPX INDEX FORWARD RATE CURVE"

sim_adapter = SimAdapter()
sim_adapter.push_to_data_service()
sim_adapter.add_overwrite_curve_modifier(
    instruments=[fwd_curve_ticker],
    overwrites={dtm.date(2005, 1, 4): 0.05},
    change_type="RETURN",
    overwrite_type="FLAT"
)
sig.obj.get(fwd_curve_ticker).history()

Overwrite 04 Jan 2005 to 300 and adjust all other tenors on 04 Jan 2005 to keep the relative difference between tenors:

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

env = sig.init(repeat_mode="reinit")

vol_curve_ticker = "SPX INDEX VOLSURFACE"
fwd_curve_ticker = "SPX INDEX FORWARD RATE CURVE"

sim_adapter = SimAdapter()
sim_adapter.push_to_data_service()
sim_adapter.add_overwrite_curve_modifier(
    instruments=[fwd_curve_ticker],
    overwrites={dtm.date(2005, 1, 4): 300.0},
    change_type="PRICE",
    overwrite_type="RATIO"
)
sig.obj.get(fwd_curve_ticker).history()

Options: Added volatility curve shift for FX options#

FX options now support volatility curve shifts. As a result, you can now use the simulation adapter (SimAdapter) to simulate shocks on FX options by changing the underlying spot value.

Code examples below—run each code block in a separate cell and reinitialize the environment each time.

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

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

env = sig.init(repeat_mode="reinit")

vol_curve_ticker = "SPX INDEX VOLSURFACE"
fwd_curve_ticker = "SPX INDEX FORWARD RATE CURVE"

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:

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

env = sig.init(repeat_mode="reinit")

vol_curve_ticker = "SPX INDEX VOLSURFACE"
fwd_curve_ticker = "SPX INDEX FORWARD RATE CURVE"

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 1% of spot price to the left, so new_ATM_strike = old_ATM_strike - (1% * spot):

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

env = sig.init(repeat_mode="reinit")

vol_curve_ticker = "SPX INDEX VOLSURFACE"
fwd_curve_ticker = "SPX INDEX FORWARD RATE CURVE"

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.01},
    change_type="RETURN",
)
sig.obj.get(vol_curve_ticker).history()

v9.2#

New features#

Options: Added risk_scenario function for single options and option strategies#

Simulate spot and implied volatility changes for both single options and option strategies. Shift spot prices over the entire duration of a single option or strategy, or a single day.

Code examples below—run each code block as a separate cell:

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

group = sig.obj.get("USDJPY OTC OPTION GROUP")

Risk scenario for single option:

opt = group.get_option(
    start_date=dtm.date(2023, 1, 2),
    strike_type="Delta",
    strike=0.5,
    maturity=dtm.date(2023, 4, 3),
    option_type="Call",
)

# shift spot by +5% and calculate change in greeks
opt.risk_scenario(
    shift_type="spot",
    shift_bps=500,
    delta_from_base=True)

# shift spot by +5% and calculate new greeks
opt.risk_scenario(
    shift_type="spot",
    shift_bps=500,
    delta_from_base=False)

# shift vol by +5 vol points and calculate change in greeks
opt.risk_scenario(
    shift_type="spot",
    shift_bps=500,
    delta_from_base=True)

# shift vol by +5 vol points and calculate new greeks
opt.risk_scenario(
    shift_type="spot",
    shift_bps=500,
    delta_from_base=False)

Risk scenario for option strategy:

bf = sig.RollingButterflyOptionsStrategy(
    start_date=dtm.date(2021, 1, 4),
    end_date=dtm.date(2023, 1, 4),
    currency=group.underlying_obj.currency,
    group_name=group.name,
    rolling_frequencies=["3M"],
    maturity="12M",
    strike_type="SPOT",
    strike_1="ATM",
    strike_2="ATM+3%",
    strike_3="ATM+5%",
    option_type="Call",
    direction="long",
)

# shift spot by +5% and calculate change in greeks
bf.risk_scenario(
    shift_type="spot",
    shift_bps=500,
    delta_from_base=True)

# shift spot by +5% and calculate new greeks
bf.risk_scenario(
    shift_type="spot",
    shift_bps=500,
    delta_from_base=False)

# shift vol by +5 vol points and calculate change in greeks
bf.risk_scenario(
    shift_type="spot",
    shift_bps=500,
    delta_from_base=True)

# shift vol by +5 vol points and calculate new greeks
bf.risk_scenario(
    shift_type="spot",
    shift_bps=500,
    delta_from_base=False)

Options: Added risk matrix for single options#

Single options are now more sensitive to spot or implied volatility changes.

With the .risk_matrix function, users can simulate price shocks by shifting either spot or volatility (shift_type). This function calculates the change in Greeks and P&L (profit and loss explained), enabling users to observe the effect of spot or implied volatility changes on single options.

Code examples—run each code block in a separate cell:

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

group = sig.obj.get("EURUSD OTC OPTION GROUP")
opt = group.get_option(
    start_date=dtm.date(2023, 1, 2),
    strike_type="Delta",
    strike=0.5,
    maturity=dtm.date(2023, 4, 3),
    option_type="Call",
)
# shift spot from -5% to +5% and calculate change in greeks
opt.risk_matrix(
    value_date=dtm.date(2023, 1, 2),
    shift_type="spot",
    num_steps=5,
    max_shift_bps=500,
    delta_from_base=True)
# shift spot from -5% to +5% and calculate new greeks
opt.risk_matrix(
    value_date=dtm.date(2023, 1, 2),
    shift_type="spot",
    num_steps=5,
    max_shift_bps=500,
    delta_from_base=False)
# shift vol from -5 vol points to +5 vol points and calculate change in greeks
opt.risk_matrix(
    value_date=dtm.date(2023, 1, 2),
    shift_type="vol",
    num_steps=5,
    max_shift_bps=500,
    delta_from_base=True)
# shift spot from -5 vol points to +5 vol points and calculate new greeks
opt.risk_matrix(
    value_date=dtm.date(2023, 1, 2),
    shift_type="vol",
    num_steps=5,
    max_shift_bps=500,
    delta_from_base=False)

Options: Added risk matrix for option strategies#

Options strategies are now more sensitive to spot or implied volatility changes.

With the .risk_matrix function, users can simulate price shocks by shifting either spot or volatility (shift_type). This function calculates the change in Greeks and P&L (profit and loss explained), enabling users to observe the effect of spot or implied volatility changes on option strategies.

Code examples—run each code block in a separate cell:

import datetime as dtm
import sigtech.framework as sig

env = sig.init()
group = sig.obj.get("USDJPY OTC OPTION GROUP")
bf = sig.RollingButterflyOptionsStrategy(
    start_date=dtm.date(2021, 1, 4),
    end_date=dtm.date(2023, 1, 4),
    currency=group.underlying_obj.currency,
    group_name=group.name,
    rolling_frequencies=["3M"],
    maturity="12M",
    strike_type="SPOT",
    strike_1="ATM",
    strike_2="ATM+3%",
    strike_3="ATM+5%",
    option_type="Call",
    direction="long",
)
bf.history().tail()
# shift spot from -5% to +5% and calculate change in greeks
bf.risk_matrix(
    value_date=dtm.date(2023, 1, 2),
    shift_type="spot",
    num_steps=5,
    max_shift_bps=500,
    delta_from_base=True)
# shift spot from -5% to +5% and calculate new greeks
bf.risk_matrix(
    value_date=dtm.date(2023, 1, 2),
    shift_type="spot",
    num_steps=5,
    max_shift_bps=500,
    delta_from_base=False)
# shift vol from -5 vol points to +5 vol points and calculate change in greeks
bf.risk_matrix(
    value_date=dtm.date(2023, 1, 2),
    shift_type="vol",
    num_steps=5,
    max_shift_bps=500,
    delta_from_base=True)
# shift vol from -5 vol points to +5 vol points and calculate new greeks
bf.risk_matrix(
    value_date=dtm.date(2023, 1, 2),
    shift_type="vol",
    num_steps=5,
    max_shift_bps=500,
    delta_from_base=False)

Futures: New parameter roll_on_calendar_days added to rolling rules#

You can now choose to roll on calendar days instead of business days when using the RollingFutureStrategy and RFPriceIndex classes. For example, you could choose to roll on the 20th of every month instead of the 20th business day.

When the roll_on_calendar_days parameter is set to True, the monthly_roll_days are taken as calendar days rather than business days.

Roll all contracts on the 20th calendar day of the month:

import datetime as dtm
import sigtech.framework as sig

env = sig.init()

rfs = sig.RollingFutureStrategy(
    start_date=dtm.date(2018, 10, 23),
    contract_code="NG",
    contract_sector="COMDTY",
    rolling_rule="f_0",
    roll_on_calendar_days=True,
    monthly_roll_days="20,20",
    total_return=False,
)
rfs.rolling_table

Improvements#

Futures: Added calculation_start_date method to RFPriceIndex#

Previously, users could create a RollingFutureStrategy without a start date, but not a RFPriceIndex. By adding a calculation_start_date method to the RFPriceIndex class, users no longer need to provide a start_date.

This ensures more consistent behavior between RollingFutureStrategy and RFPriceIndex.

Code example:

rfs = sig.RFPriceIndex(
        currency="USD",
        contract_code="ES",
        contract_sector="INDEX",
        rolling_rule="front",
        front_offset="-3,-2"
    )
rfs.history().tail()

Bug fixes#

Futures: Added holidays property to all futures instruments#

The holidays property, which contains a list of holiday calendars, has been added to futures classes. Previously it was missing, causing an error when calling .history with a futures class.

Code example:

sig.obj.get("ESH24 INDEX").holidays

Futures: Added rounded_units parameter to historic_positions and evaluate_positions methods#

Previously, the historic_positions method would return an error, as it would call the evaluate_trades method without setting the new rounded_units parameter.

To solve this problem, the rounded_units parameter has been added to the methods. Set rounded_units to False to obtain a more precise result.

Code example:

import datetime as dtm
import sigtech.framework as sig

rfs = sig.RollingFutureStrategy(
    currency='USD',
    start_date=dtm.date(2023, 1, 1),
    end_date=dtm.date(2024, 1, 1),
    contract_code='ES',
    contract_sector='INDEX',
    rolling_rule='front',
    front_offset='-6,-5'
)
rfs.inspect.historic_positions("TOP_ORDER_PTS", rounded_units=False)

Discounting and forecasting curves: Added new methods to calculate any forward and zero rate#

New forward_rate and zero_rate methods for discounting and forecasting curves let users get forward rate and zero rates:

Code examples—run each code block in a separate cell:

import sigtech.framework as sig
from sigtech.framework.instruments.ir_otc import IRSwapMarket
import datetime as dtm
env = sig.init()

Get USD discounting curve:

discounting_curve = sig.obj.get(IRSwapMarket.discounting_curve_name("USD")
discounting_curve

Get forward rate:

discounting_curve.forward_rate(
  effective_date=dtm.date(2024, 3, 13),
  start_date=dtm.date(2024, 3, 22),
  end_date=dtm.date(2024, 3, 28))

Get zero rate:

discounting_curve.zero_rate(
  effective_date=dtm.date(2024, 3, 13),
  end_date=dtm.date(2024, 3, 28))

v9.1#

New features#

All asset classes now support volatility plot_surface method#

Added support for plot surface for equity and commodity asset classes. Previously, this method was only available with FX. smile and vol_term_structure methods have also been added for FX, equity and commodities.

Each plot function should be run in a separate cell. Code example:

import sigtech.framework as sig
import datetime as dtm
sig.init()
# plot vol surface dor EURUSD
vs_fx = sig.obj.get("EURUSD VOLSURFACE")
vs_fx.plot_surface(d=vs_fx.history_dates_actual()[-1], z_name="Vol")
# plot EURUSD smile for 5Y tenor
vs_fx.smile(d=vs_fx.history_dates_actual()[-1], tenor="5Y").plot()
# plot EURUSD vol term structure for 25 delta call
vs_fx.vol_term_structure(d=vs_fx.history_dates_actual()[-1], delta="25C").plot()
# plot surface for SPX INDEX
vs_spx = sig.obj.get("SPX INDEX VOLSURFACE")
vs_spx.plot_surface(d=vs_spx.history_dates_actual()[-1])
# plot SPX INDEX smile for expiry 21 Jun 2024
vs_spx.smile(d=vs_spx.history_dates_actual()[-1], expiry=dtm.date(2024, 6, 21)).plot()
# plot SPX INDEX vol term structure for 100% moneyness
vs_spx.vol_term_structure(d=vs_spx.history_dates_actual()[-1], moneyness=100).plot()

Average backtest statistics available for multiple strategy runs#

Users can run multiple strategies at the same time and view the results all at once. The generated performance report shows average backtest statistics across all strategies, allowing for easy comparison.

Code example:

import sigtech.framework as sig
import datetime as dtm
from sigtech.framework.analytics.performance.performance_report import View

sig.init()

irs_1 = sig.InterestRateSwap(
    currency="USD",
    trade_date=dtm.date(2022, 4, 6),
    start_date=dtm.date(2022, 4, 8),
    tenor="5Y",
)

irs_2 = sig.InterestRateSwap(
    currency="USD",
    trade_date=dtm.date(2022, 4, 6),
    start_date=dtm.date(2022, 4, 8),
    tenor="1Y",
)

report = sig.PerformanceReport([irs_1, irs_2], views=[View.SUMMARY_MULTIPLE_CCY])
report.report()

Dependency updates#

Upgraded QuantLib to 1.34#

QuantLib Python package has been upgraded to version 1.34. Changes include:

  • Prevented Calendar::advance from returning the business end of month (instead of the calendar end) when endOfMonth is true and convention is Unadjusted.

  • Added good Friday holiday for SOFR fixing.

  • Properly restricted Sao Paulo city holiday to years before 2022. Updated holidays for 2023 and 2024 in calendars for India, Thailand, Singapore and South Africa.

  • Fixed past payment dates and added support for OIS in LinearTsrPricer. Swaptions can now take an OIS as underlying. So far, only BlackSwaptionEngine manages OIS explicitly; other engines might work and return approximated values.

  • More methods in MakeOIS and MakeVanillaSwap.

  • More methods in the BondFunctions class now support either clean or dirty prices.

  • Previously, the basisPointValue and yieldValueBasisPoint methods in BondFunctions didn’t always manage the settlement date correctly; this is now fixed.

  • Fixed calculation of year fraction under Actual/365 Canadian convention in FuturesRateHelper.

  • Fixed settlement date calculation in cross-currency basis-swap rate helpers in some cases.

For more information, refer to [QuantLib’s 1.34 release notes] (https://github.com/lballabio/QuantLib/releases/tag/v1.34).

Bug fixes#

  • Removed greek values for maturity date to prevent NaN or 0.000 values being returned.

  • Improved the error message returned when no dates are available in strategy_dt_list.

  • inspect.display_sizing_calculation method now works for all strategies, not just futures.

  • Changed rolling adjustments for RollingFutureStrategy and RollingFutureFXHedgedStrategy to bypass non-existing contracts.

  • Fixed error loading data history for KE COMDTY by overriding the default rolling future parameters.

  • Fixed strategy to catch error when first contract history is empty and start_date is not specified.

  • Set default values net_target_quantity and force_target_quantity as True in set_option_positions, while an error is raised if target_quantity cannot be achieved.

  • Fixed bug with CUSTOM_DATA_SOURCE_CONFIG for multiple data providers.

  • Created dynamically-generated new monthly schedules for rolling rules based on front_table.

  • plot_surface can now be displayed on matplotlib v3.8, successfully plotting a graph.

  • Adjusted timeline widget visuals for better viewing in dark mode.

  • Removed requirement for currency field in SingleBondStrategy.

  • Fixed memory leak after using object.clear_cache. After clearing the object cache, users should still be able to access the object.

  • Strike type checks in option groups are now case-insensitive.

v9.0#

Please see Guide to v9 for release notes and a full guide to version 9.0 of the SigTech framework.