Framework v8 Latest#

v8.28.0#

Added support for sig.VarianceSwap in sig.DynamicMultiOptionsStrategy and sig.DynamicOptionsStrategy classes.#

Code example:

import datetime as dtm
import sigtech.framework as sig

import pandas as pd
import numpy as np

sig.init()

def basket_creation_method(strategy, dt, positions, **additional_parameters):
    signal = additional_parameters['signal']
    option_group = additional_parameters['option_group']
    size_date = dt.date()
    d = pd.Timestamp(dt.date())
    if d in signal.index:
        vs = sig.VarianceSwap(
            currency='USD',
            start_date=size_date,
            maturity_date=dtm.date(2023, 1, 4),
            option_group=option_group.name,
            use_option_replication=True
        )
        return {vs: 100}
    else:
        return {}

index = sig.obj.get("SPX INDEX")
option_group = sig.obj.get('SPX INDEX OTC OPTION GROUP')

signal = np.sign(index.history().pct_change()).dropna().loc["2022-01-01":"2022-03-05"]
signal = signal[signal.shift() !=  signal]

bc_params = {"signal": signal,
             'option_group': option_group,
}

strat = sig.DynamicOptionsStrategy(
    currency='USD',
    start_date=dtm.date(2022,1,1),
    group_name=option_group.name,
    end_date=dtm.date(2022, 3, 5),
    rolling_frequencies=['1BD'],
    basket_creation_method=basket_creation_method,
    basket_creation_kwargs=bc_params
)

hist = strat.history()
hist.plot()

Added swap_rate_shift_details and risk_ladder methods to sig.OISSwap class#

Added swap_rate_shift_details and risk_ladder methods to sig.OISSwap:

  • swap_rate_shift_details allows swap rate shifts for single tenor and parallel

  • risk_ladder allows forward and zero rate shifts for single tenor and parallel

Users can now carry out rate shifts and evaluate the effect on net present value (NPV) and PV01 yield curves.

Code example:

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

# create ois swap
ois_swap = sig.OISSwap(
    currency="USD",
    tenor="5Y",
    trade_date=dtm.date(2022, 4, 6),
    start_date=dtm.date(2022, 4, 8),
)

# shift 5Y tenor of discounting curve by 10 bps
ois_swap.swap_rate_shift_details(
    d=dtm.date(2022, 4, 8),
    shift_bps=10,
    tenor='5Y',
    notional=10_000_000.0
)

# shift forward and zero rates for all tenors individually as well as
# simultaneously (parallel) for discounting curve by
# 10 bps and evaluate change in NPV
ois_swap.risk_ladder(
    d=dtm.date(2022, 4, 8),
    shift_bps=10,
    field='NPV',
    notional=10_000_000.0
)

# shift forward and zero rates for all tenors individually as well as
# simultaneously (parallel) for discounting curve by 10 bps and
# evaluate change in PV01
ois_swap.risk_ladder(
    d=dtm.date(2022, 4, 8),
    shift_bps=10,
    field='PV01',
    notional=10_000_000.0
)

Added period lengths and replacement options to add_bootstrap_timeseries_modifier method#

Added periods and replace parameters to add_bootstrap_timeseries_modifier method. This allows users to simulate circular block bootstrapping with time series data.

When backtesting strategies, users can now utilize variable period lengths for returns and differences, as well as choose whether to bootstrap with or without replacement.

Code example:

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

import datetime as dtm
sig.init()

sim_adapter = SimAdapter()
sim_adapter.push_to_data_service()
sim_adapter.add_bootstrap_timeseries_modifier(
    "SPX INDEX",
    change_type="RETURN",
    start_date=dtm.date(2024, 4, 15),
    end_date=dtm.date(2024, 4, 19),
    periods=1,
    replace=True
)

Added new method to sim_adapter class to allow spot price shock simulation#

Added add_overwrite_timeseries_modifier method to sim_adapter to override the following change_type:

  • ‘PRICE’ - value of time series

  • ‘RETURN’ - percentage change of time series

  • ‘DIFF’ - value change of time series

Users can now simulate spot price shocks to see how this affects an option’s net asset value.

Code example:

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

# override value of effective federal funds rate
sim_adapter = SimAdapter()
sim_adapter.push_to_data_service()
sim_adapter.add_overwrite_timeseries_modifier(
    'FEDL01 INDEX',
    change_type='PRICE',
    overwrites={
        dtm.date(2000, 7, 3): 6.5,
        dtm.date(2005, 1, 3): 2.0,
        dtm.date(2023, 3, 3): 4.3
    },
)

# override return of EURO STOXX 50
sim_adapter = SimAdapter()
sim_adapter.push_to_data_service()
sim_adapter.add_overwrite_timeseries_modifier(
    'SX5E INDEX',
    change_type='RETURN',
    overwrites={
        dtm.date(2006, 6, 12): -0.05, # -5%
        dtm.date(2018, 6, 12): 0.08,  # +8%
    },
)

# override value change of GBPUSD CURNCY on 04/01/2007
sim_adapter = SimAdapter()
sim_adapter.push_to_data_service()
sim_adapter.add_overwrite_timeseries_modifier(
    'GBPUSD CURNCY',
    change_type='DIFF',
    fields=['MidPrice'],
    data_points=[DataPoint('LONDON_1600')],
    overwrites={
        dtm.date(2007, 1, 4): 0.2,
    },
)

# override value of S&P 5pp index to 1100 on 04/09/2010
# and propagate change to forward and volatility surface
# i.e. shift volatility curve horizontally
sim_adapter = SimAdapter()
sim_adapter.push_to_data_service()
sim_adapter.add_overwrite_timeseries_modifier(
    "SPX INDEX",
    change_type="PRICE",
    overwrite_fwd = True,
    shift_vol = True,
    overwrites={
        dtm.date(2010, 4, 9): 1100.0,
    },
)

# newly created options will use the modified time series
# of S&P 500
option_group = sig.obj.get('SPX INDEX OTC OPTION GROUP')
option = option_group.get_option(
    option_type='Call',
    strike=1100,
    start_date=dtm.date(2010, 4, 6),
    maturity=dtm.date(2011, 4, 6)
)
option.history()

Added option to return swap_details and swap_rate_shift_details dataframes for sig.OISSwap and sig.InterestRateSwap classes#

Added option to return swap_details and swap_rate_shift_details as a DataFrame instead of a dictionary.

  • To get swap_details as a DataFrame, users will need to set the ‘as_df’ parameter to ‘TRUE’

  • swap_rate_shift_details will always return a DataFrame

Code example:

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

# create interest rate swap
irs = sig.InterestRateSwap(
    currency='USD',
    trade_date=dtm.date(2022, 4, 6),
    start_date=dtm.date(2022, 4, 8),
    tenor='5Y',
)

# get swap details as DataFrame
irs.swap_details(
    d=dtm.date(2023, 4, 12),
    notional=10_000_000.0,
    as_df=True
)

# get swap rate shift details as DataFrame
irs.swap_rate_shift_details(
    d=dtm.date(2023, 4, 12),
    shift_bps=10,
    curves='forecasting',
    tenor='5Y',
    notional=10_000_000.0
)

Added new non-rolling options strategy classes#

Added the following non-rolling options strategies: OptionsStrategy, StraddleOptionStrategy, StrangleOptionStrategy, SpreadOptionsStrategy and ButterflyOptionsStrategy.

Users can utilize these options structures when plotting dynamic options strategies.

Code example:

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

# create option strategy
s = sig.OptionsStrategy(
    start_date=dtm.date(2018, 1, 4),
    end_date=dtm.date(2018, 2, 4),
    currency='USD',
    group_name='SPX INDEX OTC OPTION GROUP',
    maturity='3M',
    option_type='Call',
    strike='SPOT',
    target_quantity=1,
)
s.history().plot()

# create 50 delta call straddle option strategy
s = sig.StraddleOptionStrategy(
    start_date=dtm.date(2018, 1, 4),
    end_date=dtm.date(2018, 2, 4),
    currency='USD',
    group_name='SPX INDEX OTC OPTION GROUP',
    maturity='3M',
    option_type='Call',
    strike_type='Delta',
    strike=0.5,
    target_quantity=1,
)
s.history().plot()

# create 20 delta strangle option strategy
s = sig.StrangleOptionStrategy(
    start_date=dtm.date(2018, 1, 4),
    end_date=dtm.date(2018, 2, 4),
    currency='USD',
    group_name='SPX INDEX OTC OPTION GROUP',
    maturity='3M',
    strike_type='Delta',
    call_strike=0.2,
    put_strike=-0.2,
    target_quantity=1,
)
s.history().plot()

# create 20/30 delta bull call spread strategy
s = sig.SpreadOptionsStrategy(
    start_date=dtm.date(2018, 1, 4),
    end_date=dtm.date(2018, 2, 4),
    currency='USD',
    group_name='SPX INDEX OTC OPTION GROUP',
    maturity='3M',
    option_type='Call',
    spread_direction='Bull',
    strike_type='Delta',
    strike_1='0.2',
    strike_2='0.3',
    target_quantity=1,
)
s.history().plot()

# create 10/20/30 delta long call butterfly strategy
s = sig.ButterflyOptionsStrategy(
    start_date=dtm.date(2018, 1, 4),
    end_date=dtm.date(2018, 2, 4),
    currency='USD',
    group_name='SPX INDEX OTC OPTION GROUP',
    maturity='3M',
    option_type='Call',
    butterfly_direction='long',
    strike_type='Delta',
    strike_1='0.1',
    strike_2='0.2',
    strike_3='0.3',
    target_quantity=1,
)
s.history().plot()

Added attributes to sig.RFPriceIndex class#

Added front_offset_from_expiry and month_end_offset attributes to sig.RFPriceIndex class. Users can now roll contracts at days prior to expiry.

Code example:

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

es_rfp = sig.RFPriceIndex(
    currency='USD',
    start_date=dtm.date(2023, 4, 4),
    end_date=dtm.date(2024, 4, 4),
    contract_code='ES',
    contract_sector='INDEX',
    rolling_rule='front',
    front_offset_from_expiry=True,
    front_offset='-6,-6',
    initial_cash=1,
)

New display_sizing_calculation method to print the data points and sources used to calculate a strategy#

Added data_point and source to display_sizing_calculation method. Users can now use display_sizing_calculation to print the data points and sources used to calculate a trading strategy.

Added parameter display_sizing_data=True to trade_timings_df, which gets the sizing calculation to output a dataframe and check if the trade_size calculation is correct.

Code example:

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

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

rfs = sig.RollingFutureStrategy(
    start_date = dtm.date(2019,12,20),
    contract_code = 'CL',
    contract_sector = 'COMDTY',
    rolling_rule = 'F_0',
    monthly_roll_days = '1:1',
    total_return = False,
    initial_cash=1000000,
)
rfs_s = rfs.clone_object({'direction': 'short', 'monthly_roll_days': '3:3'})

bs = sig.BasketStrategy(
    start_date = dtm.date(2019,12,29),
    constituent_names = [rfs.name, rfs_s.name],
    # weights = [2.3, 2.3],
    weights = [0.5, 0.5],
    rebalance_frequency = '5BDOM',
    total_return = False,
    instant_sizing=True,
    initial_cash=10000000,
    unit_type='WEIGHT',
)

bs.inspect.display_sizing_calculation(end_dt= dtm.datetime(2020,8,6,20,0))

bs.inspect.trade_timings_df(display_sizing_data=True)

Added cost variable to bottom_trades_df#

Added cost variable to bottom_trades_df. Users can now get the cost per trade in their strategy in order to carry out a cost analysis.

Code example:

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

b = sig.RollingFutureStrategy(
    start_date=dt.date(2017, 1, 10),
    contract_code='ES',
    contract_sector='INDEX',
    monthly_roll_days='1,1',
    total_return=False
    )
cost = b.inspect.bottom_trades_df()['t_cost']

Added parameter back_adjustment_type to classes sig.RollingFutureStrategy and sig.RFPriceIndex#

Added back_adjustment_type parameter to sig.RFPriceIndex and sig.RollingFutureStrategy.

With back_adjustment_type, you can now choose between the difference method or the ratio method to calculate the back adjustment.

Code examples:

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

# Get frozen orange juice commodity futures group
oj = sig.obj.get('JO COMDTY FUTURES GROUP')
# All params (except back adjustment) will be the same in each example
params = {
    'start_date': dtm.date(2023, 1, 1),
    'end_date': dtm.date(2024, 1, 1),
    'currency': 'USD',
    'contract_code': oj.contract_code,
    'contract_sector': oj.contract_sector,
    'rolling_rule': 'f_0',

}
# Create a time slice that covers a roll period to observe adjustments
_slc = slice('2023-05-25','2023-06-05')

# Example rolling future strategies
print('*'*80+'\n', 'JO RFS.price_series:')
oj_rfs = sig.RollingFutureStrategy(**params,
    back_adjusted=False,
    back_adjustment_type=None,
)
oj_rfs_back_adj_ratio = sig.RollingFutureStrategy(**params,
    back_adjusted=True,
    back_adjustment_type='ratio',
)
print('Default (no back-adjustment)')
print(oj_rfs.price_series()[_slc])
print('Back-adjusted using ratio method')
print(oj_rfs_back_adj_ratio.price_series()[_slc])

# Example rolling future price indices
print('*'*80+'\n', 'JO RFPI:')
oj_pi = sig.RFPriceIndex(**params,
    back_adjusted=False,
    back_adjustment_type=None,
)
oj_pi_back_adj = sig.RFPriceIndex(**params,
    back_adjusted=True,
    back_adjustment_type=None,
)
print('Default (no back-adjustment)')
print(oj_pi.history()[_slc])
print('Back-adjusted using default (diff) method')
print(oj_pi_back_adj.history()[_slc])