Framework v8

v8.27.0

New class: RollingSpreadOptionsStrategy

Create options trading strategies based on spread direction (Bull or Bear) and option type (Call or Put).

Example:

import datetime as dtm
import sigtech.framework as sig

sig.init()

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

# Create a Bull Call Spread options trading strategy
spread = sig.RollingSpreadOptionsStrategy(
    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',
    option_type='Call',
    spread_direction='Bull',
    strike_type='SPOT',
    strike_1='ATM',
    strike_2='ATM+3%',
    target_type='Vega',
    target_quantity=10.0,
)

New class: RollingButterflyOptionsStrategy

Create butterfly spread options trading strategies based on butterfly direction ('long' or 'short') and option type (Call or Put).

Example:

import datetime as dtm
import sigtech.framework as sig

sig.init()

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

# Create a long butterfly spread with calls
butterfly = 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',
    option_type='Call',
    butterfly_direction='long',
    strike_type='SPOT',
    strike_1='ATM',
    strike_2='ATM+3%',
    strike_3='ATM+5%',
    target_type='Vega',
    target_quantity=10.0,
)

New feature: Target the net of a target quantity

Set net_target_quantity to True to target net of target quantity in options trading strategies.

You can force target quantity by adjusting the ratio of option legs.

Example:

import datetime as dtm
import sigtech.framework as sig

sig.init()

# Create strangle on SPX INDEX with call strike of 4500 and put strike 5500.
# Scale option legs to get net target delta of 1.

group = sig.obj.get('SPX INDEX OTC OPTION GROUP')
strangle = sig.Strangle(
    start_date=dtm.date(2023, 1, 4),
    end_date=dtm.date(2024, 1, 4),
    currency=group.underlying_obj.currency,
    group_name=group.name,
    rolling_frequencies=['1M'],
    maturity='3M',
    strike_type='Price',
    put_strike=4500,
    call_strike=5500,
    target_type='Delta',
    target_quantity=1.0,
    net_target_quantity=True,
    total_return=False
)
strangle.history().plot()

This change applies to strategies created with the following classes:

  • DynamicMultiOptionsStrategy

  • DynamicMultiSwaptionsStrategy

  • DynamicOptionsStrategy

  • DynamicSwaptionsStrategy

  • RollingButterflyOptionsStrategy

  • RollingOptionStrategy

  • RollingSpreadOptionsStrategy

  • RollingStraddleOptionStrategy

  • RollingStrangleOptionStrategy

  • RollingVarianceSwapStrategy

  • Any custom class inheriting from RollingOptionsStrategyBase

Improved transaction cost handling for IR and OIS OTC swaps

Transaction costs for IR and OIS OTC swaps can now be handled separately from the valuation price of the swap. This is in line with market conventions for modeling transaction costs.

To use this functionality, set INCLUDE_OTC_T_COSTS_IN_CASH to True.

Example:

import datetime as dtm
import sigtech.framework as sig

sig.env()[sig.config.IGNORE_T_COSTS] = False
sig.env()[sig.config.INCLUDE_OTC_T_COSTS_IN_CASH] = True

rolling_swap= sig.RollingSwapStrategy(
        tenor="10Y",
        currency='USD',
        swap_currency='USD',
        rolling_frequency_months=6,
        forward_start_months=6,
        start_date=dtm.date(2023, 12, 5),
        initial_cash=0,
        notional_target = 2000000,
    )
rolling_swap.build()

rolling_swap.plot.portfolio_table(dts='ACTION_PTS')

New method for OISSwap class: swap_details

Get information about a swap for a specified reference date.

Example:

import sigtech.framework as sig
import datetime as dtm

sig.init()

ois_swap = sig.OISSwap(
    currency="USD",
    tenor="5Y",
    start_date=dtm.date(2020, 11, 9),
    trade_date=dtm.date(2020, 11, 9),
)

ois_swap.swap_details(d=dtm.date(2020, 11, 9), notional=10_000_000.0)

New method for discounting and projection curves: yield_curve

This change adds the yield_curve method to display forward and zero rates for a given discounting or projection curve.

Example:

import datetime as dtm
import sigtech.framework as sig

from sigtech.framework.instruments.ir_otc import IRSwapMarket

sig.init()

# Get a discounting curve
discounting_curve = sig.obj.get(IRSwapMarket.discounting_curve_name('USD'))

# Get the yield curve for the discounting curve
yield_curve = discounting_curve.yield_curve(d=dtm.date(2024, 3, 13))

Pass a list of holiday calendars when creating a dynamic strategy

Add non-trading days to a dynamic strategy by passing the extra_holidays parameter on creation.

The extra_holidays parameter can be a string or a list of strings. It must contain the object names of the holiday calendars you want the strategy to use.

Example:

fx_option_group = sig.obj.get('EURUSD OTC OPTION GROUP')

trade_dictionary = {dtm.date(2010, 4, 7): {
        fx_option_group.get_option('Call', 1.3, dtm.date(2010, 4, 6), dtm.date(2011, 4, 6)) : 1,
        fx_option_group.get_option('Put', 1.3, dtm.date(2010, 4, 6), dtm.date(2011, 4, 6)) : -1
    }
}

strat = sig.DynamicStrategy(
    currency=fx_option_group.over,
    start_date=dtm.date(2010, 1, 6),
    end_date=dtm.date(2012, 1, 20),
    trade_dictionary=trade_dictionary,
    #extra_holidays=['CMX(T) CALENDAR','SINGEX_JP(T) CALENDAR'],
    extra_holidays='CMX(T) CALENDAR,SINGEX_JP(T) CALENDAR',
)

strat.history()

v8.26

Swaps: New attributes for interest rate swaps

Added the following attributes to InterestRateSwap instruments:

  • fixed_day_count: Fixed leg day count convention.

  • float_day_count: Floating leg day count convention.

  • fixing_lag: Delay in days between the fixing date and effective date of an interest rate.

  • index: Swap fixing index.

Example:

import datetime as dtm
import sigtech.framework as sig

env = sig.init()

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

print(irs.fixed_day_count)  # 30/360
print(irs.float_day_count)  # ACT/360
print(irs.fixing_lag)  # 2
print(irs.index)  # US0003M INDEX

Various strategies: New parameter to close all positions at end date

Added trade_out_end parameter to various strategies. When trade_out_end is set to True, the strategy closes all positions upon end date.

The trade_out_end parameter is supported for strategies based on the following classes:

  • BasketStrategy

  • RollingAssetSwap

  • RollingButterflyBonds

  • RollingButterflySwaps

  • RollingCurveSteepenerBonds

  • RollingCurveSteepenerSwaps

  • RollingFutureStrategy

  • RollingStructureBasket

  • SignalStrategy

Example: Close out all trades at the end of a rolling future strategy

import datetime as dtm
import numpy as np
import pandas as pd
import sigtech.framework as sig

env = sig.init()

rfs = sig.RollingFutureStrategy(
    currency="USD",
    start_date=dtm.date(2022, 6, 14),
    end_date=dtm.date(2023, 9, 29),
    contract_code="ES",
    contract_sector="INDEX",
    trade_out_end=True,
)
rfs.history()

Swaps: Added methods for swap rate shift

Examples 1, 2, and 3 in this section use the following setup code:

import datetime as dtm
import numpy as np
import pandas as pd
import sigtech.framework as sig

env = sig.init()

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

Example 1: Shift 5Y tenor of forecasting and discounting curve by 10 bps.

# Shift 5Y tenor of forecasting and discounting curve by 10 bps
irs.swap_rate_shift_details(
    d=dtm.date(2022, 4, 8),
    shift_bps=10,
    curves='both',
    tenor='5Y',
    notional=10_000_000.0
)

Example 2:

  • Shift forward and zero rates for all tenors individually by 10bps.

  • Perform same shift simultaneously (in parallel) forecasting and discounting curve.

  • Evaluate change in NPV.

irs.risk_ladder(
    d=dtm.date(2022, 4, 8),
    shift_bps=10,
    curves='both',
    field='NPV',
    notional=10_000_000.0
)

Example 3:

  • Shift forward and zero rates for all tenors individually by 10bps.

  • Perform same shift simultaneously (in parallel) forecasting and discounting curve.

  • Evaluate change in PV01.

irs.risk_ladder(
    d=dtm.date(2022, 4, 8),
    shift_bps=10,
    curves='discounting',
    field='PV01',
    notional=10_000_000.0
)

Options: New strike type 'Premium'

Options can now be retrieved from option groups using 'Premium' as the strike type. For example:

import datetime as dtm
import numpy as np
import pandas as pd
import sigtech.framework as sig

env = sig.init()

start_date = dtm.date(2018, 1, 4)
maturity = dtm.date(2018, 6, 26)
option_type = 'Call'

# Get call option on SPX INDEX with a premium of 100
option_group = sig.obj.get('SPX INDEX OTC OPTION GROUP')
option = option_group.get_option(
    start_date=start_date,
    option_type=option_type,
    strike=100.0,
    strike_type='Premium',
    maturity=maturity
)

Analytics: Added functionality to get explicit information on daily trades for execution

You can now get more explicit information on which trades to execute each day. To use the new functionality, call Strategy.inspect.evaluate_trades with the following new parameters:

  • summary: Show summarized output with only 'CURRENT', 'TARGET', and 'TRADE'. Defaults to False.

  • dt_orders_only: Show only the trades on dt.date; exclude subsequent days. Defaults to False.

  • rounded_units: Round the trade quantities. Default depends on the type of instrument.

Example:

import datetime as dtm
import numpy as np
import pandas as pd
import sigtech.framework as sig

env = sig.init()

strat = sig.default_strategy_objects.rolling_swaps.usd_2y()
strat.inspect.evaluate_trades(
    dt=dtm.datetime(2010, 6, 16),
    rounded_units=True,
    summary=True,
    dt_orders_only=False,
)

v8.25

All objects

This release improves the formatting of the data frame returned by the data_df method. All framework objects, including strategies, instruments, and instrument groups, have this functionality.

This feature is disabled by default. To enable the improved formatting, pass pretty_print=True to data_df:

import datetime as dtm
import sigtech.framework as sig

env = sig.init()

irs = sig.InterestRateSwap(
    currency='USD',
    trade_date=dtm.date(2022, 4, 6),
    start_date=dtm.date(2022, 4, 8),
    tenor='5Y'
)
pretty_df = irs.data_df(pretty_print=True)
pretty_df

Added new property alias to all instruments. Added position information to the strategy portfolio table.

Strategies: Option to simplify plot portfolio tables

To simplify plot portfolio tables when composing a strategy from other strategies, use flatten_strategy_orders. When set to True, only the bottom level trades for each order are shown.

Methods affected:

  • Strategy.plot.portfolio_table

  • Strategy.plot.portfolio_tree

Example:

import datetime as dtm
import numpy as np
import pandas as pd
import sigtech.framework as sig
from sigtech.framework.default_strategy_objects import rolling_futures

env = sig.init()

tickers = sig.get_single_stock_strategy(exchange_tickers=['AAPL','TSLA'])
signal_1_df = pd.DataFrame({ticker.name: np.sign(ticker.history().pct_change()) for ticker in tickers}).dropna()

# Building 2 strategies to be traded
strategy_stock = sig.SignalStrategy(
        currency='USD',
        start_date=dtm.date(2022, 1, 4),
        end_date=dtm.date(2022, 12, 30),
        signal_name=sig.signal_library.from_ts(signal_1_df).name,
        # instrument_mapping = long_instrument_mapping,
        # use_signal_for_rebalance_dates = True,
        rebalance_frequency='EOM',
        allocation_function=sig.signal_library.allocation.normalize_weights,
        ticker = 'Long Stock'
    )
es_rfs = rolling_futures.es_index_front()
gc_rfs = rolling_futures.gc_comdty_rici()
signal_2 = pd.DataFrame({
    es_rfs.name: es_rfs.history(),
    gc_rfs.name: gc_rfs.history(),
}).loc['2022']
signal_2 = 1 + 0 * signal_2
strategy_index = sig.SignalStrategy(
    currency='USD',
    start_date=dtm.date(2022, 1, 4),
    end_date=dtm.date(2022, 12, 30),
    rebalance_frequency='EOM',
    signal_name=sig.signal_library.from_ts(signal_2).name,
    ticker = 'Long Index'
)

# Building a basket of those 2 strategies
strat = sig.BasketStrategy(
    start_date=dtm.date(2022, 2, 1),
    end_date = dtm.date(2022, 12, 30),
    constituent_names=[strategy_stock.name, strategy_index.name],
    weights=[0.6, 0.4],
    rebalance_frequency='EOM',
    # total_return=False,
)

# Display portfolio_table
strat.plot.portfolio_table('ACTION_PTS', flatten_strategy_orders=True)

Strategy analytics: Improvements to help validate trade size

Added new parameter display_sizing_data to trade_timings_df. To use it, call Strategy.inspect.trade_timings_df with display_sizing_data=True.

For example:

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

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)

New bonds strategy classes

Added new curve steepener bonds and butterfly bonds strategy classes:

import sigtech.framework as sig

help(sig.ButterflyBonds)
help(sig.CurveSteepenerBonds)
help(sig.RollingButterflyBonds)
help(sig.RollingButterflySwaps)
help(sig.RollingCurveSteepenerBonds)
help(sig.RollingCurveSteepenerSwaps)

v8.24

Futures: New syntax for front_offset, month_end_offset, monthly_roll_days

For improved consistency, interval boundaries are now included in the interval.

For example, to roll a third of the contract each of the 3 days from 4th to 2nd day before contract expiry:

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

rfs = sig.RollingFutureStrategy(
        start_date=dtm.datetime(2010,1,4),
        currency=group.currency,
        contract_code=group.contract_code,
        contract_sector=group.contract_sector,
        month_end_offset='-5,-3',
        rolling_rule = 'prev_month',
    )
rfs.rolling_table

Strategies: Add option to remove string formatting in portfolio_table

In Strategy.plot.portfolio_table, you can now pass str_format=False to prevent string formatting in the resulting data frame.

v8.23

Futures: Added custom_rolling_table to RollingFutureStrategy

Strategies: Added stop_trigger allocation function and stop_trigger parameters

Fixes

  • TRBC operators in EquityUniverseFilter now perform partial matching

  • Single-letter futures codes no longer need padding with an extra space

v8.22

  • CustomView can now access PerformanceReport properties.

    from sigtech.framework.analytics.performance.performance_report \
    import PerformanceReport, View, CustomView
    
    help(CustomView)
    help(PerformanceReport)
    help(View)
  • Added new class MarketCapWeightedIndex.

    sig.instruments.indices.MarketCapWeightedIndex?
  • Added new class DynamicStrategy.

    sig.DynamicStrategy?
  • Bond futures: Added ffill input to bond futures strategy method spot_dv01.

    Bond futures strategy method spot_dv01 now has an additional argument, ffill, with default set to True.

    This parameter behaves similarly to pandas.DataFrame.ffill.

    When ffill is set to True and DV01 is requested for a DatetimeIndex d, then the DV01 series is reindexed using d and any NaN value is filled by propagating the last valid observation to the next valid one.

v8.21.1

Enhanced print_dependencies method

The print_dependencies method has been significantly improved to offer users more control and clarity when printing dependency trees. Here's a breakdown of its features:

  • root_dependency: allows users to specify the starting dependency of the tree.

  • resolve_future_dependencies: ensures that future dependencies are resolved before printing. If set to False, the method will not print anything, ensuring that users only see resolved dependencies in their output.

  • fields: Users can now select additional fields to extract from the dependencies object. While the default fields include product_type, currency, frequency, and data_source, this enhancement allows users to include other relevant fields from the dependency object. To determine which fields are available for an object, users can utilize the data_dict method.

New position_value_series and get_cash_valuation methods

position_value_series returns the valuation of position holdings (excluding cash) in the strategy's currency. Users can specify various options for the datetime points (dts) to include in the series enabling granular analysis of your strategy's performance at critical moments, such as when orders are executed or when top-level holdings change.

get_cash_valuation returns the value of cash positions in a strategy at a specified time.

Enhanced RollingSwapStrategy with swap_rate and swap_rate_series methods

You can now view the swap rate of a RollingSwapStrategy at any given datetime (using swap_rate) or the historical swap rates across the lifetime of a strategy using swap_rate_series.

import sigtech.framework as sig
import datetime as dtm

sig.init()

s = sig.RollingSwapStrategy(
    tenor="10Y",
    currency="USD",
    swap_currency="USD",
    rolling_frequency_months=6,
    forward_start_months=6,
    start_date=dtm.date(2010, 1, 4)
)

sr = s.swap_rate(dtm.datetime(2014,4,4))
print(sr)

s.swap_rate_series()

Enhanced InterestRateSwap with data_df and fair_rate methods and InterestRateSwapGroup with data_df and fair_rate_df

Interest Rate Swap

  • Deprecated the static method fair_swap_rate.

  • Introduced new methods: fair_rate and data_df.

    • data_df: returns a dataframe containing all the data available for this interest rate swap. Typically, this includes the LastPrice, Rate and PV01.

    • fair_rate: returns the fair swap rate for an interest rate swap for a particular date. Note that unlike fair_swap_rate, env and currency are not required arguments.

import sigtech.framework as sig
import datetime as dtm

sig.init()

ir_swap = sig.InterestRateSwap(
    currency='EUR',
    trade_date=dtm.date(2017, 4, 14),
    start_date=dtm.date(2017, 1, 5),
    tenor=dtm.date(2022, 7, 5),
    fixed_rate=0.05,
)

ir_swap.data_df()

ir_swap.fair_rate()

Interest Rate Swap Group

  • Deprecated the static method fair_swap_rate.

  • Introduced two new methods: fair_rate_df (static) and data_df (non-static).

    • data_df: returns a dataframe containing all the data available for this interest rate swap group.

    • fair_rate_df: returns a dataframe containing all the fair rate data for the interest rate swap group.

import sigtech.framework as sig
import datetime as dtm

sig.init()

# Using fair_rate_df (static)
df1 = sig.InterestRateSwapGroup.fair_rate_df(
    tenor='5Y',
    start_date=dtm.date(2016, 12, 20),
    end_date=dtm.date(2017, 1, 3),
    fwd_tenor='3Y',
    currency='EUR'
)

# Using data_df
g = sig.obj.get('EUR INTEREST RATE SWAP GROUP')
df2 = g.data_df(
    tenor='5Y',
    start_date=dtm.date(2016, 12, 20),
    end_date=dtm.date(2017, 1, 3),
    fwd_tenor='3Y',
)
# Further data manipulations are possible using data_df

g.data_df('5Y', dtm.date(2016, 12, 20))
g.data_df('5Y', start_date=dtm.date(2016, 12, 20), end_date=dtm.date(2016, 12, 23))

Fixes

  • Replaced hard-coded OBJECT_CLASS_REGISTER with automated logic to fetch framework classes and corresponding modules; exceptions may apply.

  • The start date of rolling future strategies will be computed using the first contract in the roll table to ensure accuracy.

  • All relevant greeks are now available when using the portfolio_greeks method.

  • Fixed a data concatenation issue in the intraday history retrieval for fx_otc.

  • Updated the timeline to the latest version (6.18.7) for live foreign exchange orders.

  • Enhanced the rolling logic to fetch open interest data only when it's available.

  • Implemented additional checks on callability features within session_data methods.

  • Expanded the historical data available for the Chinese Yuan (CNY) cash index.

  • Optimized the application of price factors on intraday price data columns.

  • Improved error notifications for incorrect valuation time inputs, making them more user-friendly.

  • Introduced a new calculation method for dv01, specifically for bonds quoted by yield.

  • Added further callability checks within the trading manager get_exchange_open_close_dt method.

  • destroy_env procedure added to final step in FunctionJob and StrategyJob decorators.

v8.20.0

New custom_hedge_exposure parameter for FXForwardHedgingStrategy and additional maturity options in roll_offset

The custom_hedge_exposure parameter allows you to customize the hedge exposure for each currency in your FXForwardHedgingStrategy, enabling you to hedge specific portions of the underlying strategy's exposure based on your unique requirements. By passing a dictionary of currency exposures as {"CCY": exposure}, you can now specify the desired hedge percentages for individual currencies. For example, you can hedge 50% of the GBP exposure and 20% of the JPY exposure by passing {"GBP":0.5, "JPY":0.2}. This new feature empowers users to tailor their hedging approach to better align with their risk management strategies and investment goals.

Additionally, this update introduces further maturity options for the roll_offset parameter in the FXForwardHedgingStrategy. The roll_offset parameter allows you to specify the tenor of the FX forwards used for hedging.

New size_from_first_roll_trade parameter for RollingFutureStrategy

With the size_from_first_roll_trade parameter, you now have the option to adjust how roll trades are sized in the strategy. By default, this feature is activated, meaning that all roll trades will be sized based on the first trade of the roll. This ensures a consistent sizing approach across roll trades.

However, with the size_from_first_roll_trade parameter, you have the flexibility to customize trade sizes individually for each roll trade. When set to True, each roll trade will be sized based on the first trade of the roll. On the other hand, if set to False, you can size each trade separately, allowing for more fine-grained control over the trade sizes.

SchedulePeriodic now has business day of the month frequency

By entering an integer appended by BDOM you can specify a specific business day of the month as for your schedule.

For example 16BDOM represents the 16th business day of the month.

Fixes

  • Implemented a workaround to ensure accurate CVaR (Conditional Value at Risk) calculation for small datasets and a new trigger has been introduced to generate comprehensive summary reports for single transactions.

  • Added support for AUDUSD cross-currency swap fx_datapoint.

  • The framework now performs a thorough check of initial rolls and automatically adjusts the first contract forward, if necessary.

  • Added commodity futures groups to F_0_table

  • Enhanced the data parsing mechanism to ensure correct string ID extraction when the data source is available.

  • Added informative error messages for early trading scenarios within SignalStrategy.

  • The computation of spot DV01 series now utilizes valuation points.

  • Fixed an issue that caused potential field availability discrepancies during the reindexing process in get_series. The system now ensures that all relevant fields are available and correctly aligned.

  • The default value of closing_only in the bottom_trades_pnl_df function has been updated.

v8.19.0

More statistics have been added to the performance report

New trade related statistics have been added to the performance report. These can be found within the trade_statistics table method. The new statistics added are as follows:

  • Trades (number),

  • Win (number),

  • Loss (number),

  • Win/loss ratio,

  • Win probability

  • Kelly Criterion

See the code example below for an example of how to run this new table.

Also available is a new inspect wrapper view called bottom_trade_pnl_df.

Code Example:

start_date = dtm.date(2020, 1, 10)
end_date = dtm.date(2021, 1, 10)
etf_ids = [
    '1001692.SINGLE_STOCK.TRADABLE',
]
etfs = sig.get_single_stock_strategy(etf_ids, {'start_date': start_date, 'end_date': end_date})
rs = {e.underlyer_object.exchange_ticker: e for e in etfs}
spy = rs['SPY']
signal_ts = np.sign(spy.history()-spy.history().rolling(20).mean()).fillna(1)

strat = sig.SignalStrategy(
    currency='USD',
    signal_name=sig.signal.library.from_ts(signal_ts.to_frame(spy.name)).name,
    start_date=start_date,
    end_date=end_date,
)
sig.PerformanceReport(strat, views='ALL').report()

To run the new trade_statistics method: strat.analytics.trades_statistics() New inspect wrapper view: strat.inspect.bottom_trades_pnl_df(closing_only=False)

Upgrade QuantLib to 1.30

The QuantLib dependency has been updated from v1.28 to v1.30. For more information on this release, see the QuantLib release notes.

Add risk reversal method to vol surfaces

Before this release, the risk_reversal method was available only for FX vol surfaces. (this method returns the history of Call Vol minus Put Vol for a given vol surface).

With this release, the risk_reversal method has been extended and is now available for equity and commodity vol surfaces.

In addition, two optional parameters, start_date and end_date have been added, allowing you to specify a date range for the returned historical date. This is useful if you want to get a small date range as it makes the calculations much quicker.

Code Example

# Commodity vol surface
vs1 = sig.obj.get('GC COMDTY VOLSURFACE')
vs1.risk_reversal(tenor='1M',
delta=25,
start_date=dtm.date(2020, 1, 1),
end_date=dtm.date(2020, 2, 10))
# Equity vol surface
vs2 = sig.obj.get('SPX INDEX VOLSURFACE')
vs2.risk_reversal('1M', 25) # No range specified

Fixes

  • Any override of the rolling parameters is now correctly applied in the RollingFutureStrategy constructor.

  • The dependencies for the FX objects used in a basket strategy or signal strategy are now correctly updated according to their expected evaluation start date and end date.

  • Strategies no longer fail when basktesting times are approaching midnight UTC.

  • When using CapADVOrderTransform the volume parameter is now case-insensitive.

  • Before this release, the optimized_allocations and instrument_mapping parameters within the SignalStrategy were not compatible. With this release, instrument_mapping can be a parameter of the optimization function.

  • With this release, the data_df method no longer restructures the internal history data.

v8.18.0

This release combines versions 8.18.0 and 8.17.0.

Breaking change to swap tickers

With this release, a # character has been added to swap tickers to distinguish if only valuing pre-start. If you have hard-coded the name of a swap, this could break your analysis notebook. For example, the following function obj.get('USD LIBIMM S3M 0.0254578 2010-06-15 U10X5Y IRS') needs to be changed to obj.get('USD LIBIMM S3M 0.0254578 2010-06-15 U10X5Y # IRS') to continue working.

Two new child-classes (StopLossStrategy and TakeProfitStrategy) have been added to the StopStrategy building block

With this release, two child classes have been added to the StopStrategy building block, allowing you to create stop loss orders and take profit events. Below are code examples for StopLossStrategy and TakeProfitStrategy.

import datetime as dtm
import sigtech.framework as sig
env = sig.init()
future = env.object.get('ESZ19 INDEX')

stop_loss = sig.StopLossStrategy(
    currency='USD',
    trade_date=dtm.date(2019,9,1),
    instrument_name=future.name,
    trigger_type='FIXED_PCT_PROFIT',
    trigger_level=0.01,
)

take_profit = sig.TakeProfitStrategy(
    currency='USD',
    trade_date=dtm.date(2019,9,1),
    instrument_name=future.name,
    trigger_type='FIXED_PCT_PROFIT',
    trigger_level=0.01,
)

New threshold function added to achieve dynamic rebalancing in the SignalStrategy

In previous releases, the threshold function returned a boolean value that was passed into the SignalStrategy, serving as an "on/off switch" that controlled if the strategy rebalanced or not.

In this release, the threshold output can either be a boolean value or a dictionary of strategy weighting, giving you greater control of the threshold you wish to apply.

A built-in threshold function called signal_threshold_rebalance_dict has been introduced. This requires an additional keyword argument in the form of a dictionary which contains float values that represent:

  • A threshold for a change in the weighting.

  • How much of the strategy to rebalance (as a percentage).

For example, to achieve the following dynamic rebalancing of:

  • 10% deviated from the target position, rebalance 100%,

  • 30% deviated from the target position, rebalance 70%,

  • 50% deviated from the target position, rebalance 50%.

Pass the following dictionary:

rebalance_dict = {0.1: 1.0, 0.3: 0.7, 0.5: 0.5, 1.0: 0.0}

To use this new threshold function, import it from the signal_rebalancing library.

Below is a code example of this new threshold function used within a signal strategy.

from sigtech.framework.signal.signal_rebalancing import signal_threshold_rebalance_dict

rebalance_dict = {0.1: 1.0, 0.3: 0.7, 0.5: 0.5, 1.0: 0.0}

strategy = sig.SignalStrategy(
    currency='USD',
    start_date=start_date,
    signal_name=signal_name,
    rebalance_frequency='EOM',
    allocation_function=sig.signal_library.allocation.equally_weighted,
    threshold_function=signal_threshold_rebalance_dict,
    threshold_kwargs={'rebalance_dict': rebalance_dict},
)

Added get_yield_curve and data_df methods to the BondYieldCurve objects

With this release, two new methods have been added that return yield curve data from a BondYieldCurve object.

  • get_yield_curve(date, tenor) creates the yield curve for a reference date up to a specified tenor. By default, this tenor is 30Y.

  • data_df() returns a DataFrame containing all history dates for bonds that have a otr_tenor (2Y, 3Y, 5Y, 7Y, 10Y, 30Y). As this function returns a lot of data, it may affect your runtimes.

Below is a code example of how to call these new methods:

US_bonds = sig.obj.get("US GOVT BOND GROUP")
curve = US_bonds.get_bond_yield_curve()
date = dtm.date(2023, 3, 31)
tenor = '10Y'

yield_curve = curve.get_yield_curve(date=date, tenor=tenor)

yield_df = curve.data_df()

New parameter min_periods added to rolling statistics methods

With this release a new parameter min_periods has been added to the following performance metrics summary methods:

  • summary_rolling_series

  • summary_rolling_ccy_series

min_periods is the minimum number of observations in the period window that require a value.

If this parameter is not set, the default value of 252 is used instead - this is the same value as the previous period parameter, where 252 is the number of business days in a year.

description property now available for CommodityIndex objects

In previous releases, the description property was missing from the CommodityIndex sub-asset class, and was only available for EquityIndex objects.

With this release, the property has been moved to the parent class Index so all sub-classes can inherit this property. Therefore, the description property can be called for all types of Index objects.

Below is a code example of how to call this property.

import sigtech.framework as sig
sig.init()

equity_index = sig.obj.get('SPX INDEX')
equity_index.description

cmdty_index = sig.obj.get('SPGSINTR INDEX')
cmdty_index.description

More statistics have been added to the Performance Report

The following statistics have been added to the report function found in the PerformanceReport class:

  • Calmar Ratio

  • VaR - 99% and 95% (both 1day)

  • cVaR - 99% and 95% (both 1day)

To view these new parameters, run the following code:

import sigtech.framework as sig
sig.init()
from sigtech.framework.default_strategy_objects.rolling_futures import bo_comdty_f_0
data = bo_comdty_f_0().history()
sig.PerformanceReport(data).report()

Fixes

  • The object_service docstring has been updated.

  • Before this release, there were errors when creating an equity index option after a vol surface was assessed the with data_df() method. With this release, the DataLoadError issues have been resolved.

  • The deprecated exchange_open property has been removed from the info() method and replaced with exchange_open_on_day.

  • Certain methods now cannot be accessed while a strategy is being built.

  • The price series index has been converted to a datetime index in the RollingFutureStrategy building block.

  • When plotting a volatility surface, adjustments have to be made to improve the ticks on the tenor axis.

  • Decimal values have replaced commas in prices. For example, 1000,00 is now displayed as 1000.00.

  • Missing credibility fields have been added to available_fields method when using DataCredibilityAdapter.

v8.17.0

This release was merged with 8.18. Please refer to the release notes for version v8.18.0 to see what was included in version 8.17.0.

v8.16.0

Enhanced sig.object.get can now query all cross pairs

With this release, you can use sig.obj.get() to query FX spot rates for any currency pair, even those which are not available in our data browser. Previously, sig.obj.get() you could only query FX spot rates for currency pairs in our data browser.

A couple of examples are shown below:

import sigtech.framework as sig
sig.init()

usdeur = sig.obj.get('USDEUR CURNCY')
usdeur

seknok = sig.obj.get('SEKNOK CURNCY')
seknok

Store and load environment configs from a file

You can save your Sigtech environment in your JupyterLab instance. This saves any variables or attributes that you have configured in your environment. JSON serialisable attributes are saved in a .JSON format and non-JSON serialisable attributes are saved in .PKL. Once an environment is saved, you can easily reload it in a new notebook.

See the code example below for an example:

import sigtech.framework as sig

# Saves environment config as 'env.json' and 'env.pkl'
env = sig.init()
env.save_config('env')

# Destroys the environment instance
sig.de_init()

# Loads environment config from 'env.json' and 'env.pkl'
loaded_env = sig.init(load_env=True, env_file='env')

Added minimalist attributes view to BuildingBlockBrowser widget

A new Minimalist View option is available on the BuildingBlockBrowser widget. Selecting this option reduces the list of the class attributes shown in the BuildingBlockBrowser by only showing you the mandatory attributes. Not selecting/deselecting this option will result in all the attributes available for a class is visible.

New data_df method added and history_df method deprecated

A new method data_df() has been added to all framework instruments. The data_df() method replaces the history_df() method as it returns improved data. This new method returns a pandas DataFrame containing all data available for this object.

The data_df() method takes the following optional arguments:

  • data_point: Choose the data point from which to return the history of the object.

  • multi_index: Set to True to index rows uniquely by a multi-index (if applicable). Default is False.

  • drop_nan_cols: Set to True to drop all NaN columns. Default is False.

data_df() is an improvement over the previous history_df method as it can perform clean-up operations as the data is queried. Such clean-up operations include column rearrangement and renaming, reset of indices and forward filling of values when placeholders are set.

See the code example below for how to use data_df:

import sigtech.framework as sig

sig.init()

# Single stock
sig.obj.get('1000045.SINGLE_STOCK.TRADABLE').data_df(drop_nan_cols=True)

# Credit index curve
sig.obj.get('CDX.NA CURVE').data_df(multi_index=True)

# Vol surface
sig.obj.get('EURUSD VOLSURFACE').data_df()

Lists of holidays and instruments can be passed into SchedulePeriod method

With this release, you can now pass a single or a mixed list of holiday calendar names and instruments objects into the holidays parameter of the SchedulePeriod method. This generates a calendar that accommodates all the holidays in the passed list. If a list of instruments has been passed, then the calendar will accommodate the holidays for each of the instruments in the list. This is particularly useful if the list consists of different indices.

See the code example below for how to create a weekly schedule:

instrument_list = ['ESH23 INDEX', 'COH22 COMDTY', '1000045.SINGLE_STOCK.TRADABLE']
basket = [sig.obj.get(ins) for ins in instrument_list]

weekly_schedule = sig.SchedulePeriodic(
    start_date=dtm.date(2020, 2, 1),
    end_date=dtm.date(2020, 3, 28),
    holidays=basket,
    frequency='1W-WED',
    offset='1BD',
    offset_backwards=True,
    bdc=sig.calendar.BDC_MODIFIED_FOLLOWING)

print(weekly_schedule.get_holidays())
print(weekly_schedule.get_holidays().all_data_dates())

New back_adjusted feature and equivalent get_rf_price_index method added to RollingFutureStrategy

A back_adjusted parameter has been added to the price_series method (belonging to the RollingFutureStrategy class). This parameter calculates the back-adjusted prices for underlying futures.

Furthermore, a new method get_rf_price_index has been added to RollingFutureStrategy, it returns the corresponding rolling future price index object. Likewise, an additional method get_rolling_future_strategy has been added to the RFPriceIndex class, it returns the corresponding rolling future strategy.

See the code example below for how to pass the back_adjusted parameter:

rfs = sig.RollingFutureStrategy(
            currency='USD',
            start_date=dtm.date(2020, 1, 1),
            end_date=dtm.date(2022, 1, 1),
            contract_code='NG',
            contract_sector='COMDTY',
            rolling_rule='front',
            front_offset='-2:-1'
        )
rfs.price_series(back_adjusted=True)

The following example queries the rolling future price index from a back-adjusted rolling future strategy.

rfs_pi = rfs.get_rf_price_index(back_adjusted=True)

The following example gets a rolling futures strategy from the back-adjusted price index.

rfpi_strat = rfpi.get_rolling_future_strategy()

New parameters added to SignalStrategy that allow for order execution delays

You can now pass an execution_delay argument to a signal strategy.

This allows you to delay an order execution by a set period. In addition, you can use the bump_execution_delay parameter to ensure the order execution falls within trading hours. Set bump_execution_delay to False to ignore orders that would be executed out of trading hours. This means the order will not be executed if the delay causes it to execute outside of trading hours. Set bump_execution_delay to True (default) to ensure the order will be executed within trading hours by moving the execution time forward to the next available data point (that falls within trading hours).

For example:


import pytz
import datetime as dtm
import pandas as pd
import numpy as np
import sigtech.framework as sig
env = sig.init()

rfs = sig.default_strategy_objects.rolling_futures.es_index_front()
rfs.history();

start_date = dtm.date(2023,3,1)
signal = pd.Series({d: np.random.uniform(0,1) for d in pd.date_range(start_date, env.asofdate)}).to_frame(rfs.name)

rfs = sig.default_strategy_objects.rolling_futures.es_index_front()
rfs.history();

# Use bump_execution_delay=False to skip trades that would execute outside trading hours based on the delay provided via execution_delay
strategy = sig.SignalStrategy(
    currency='USD',
    signal_name=sig.signal.library.from_ts(signal).name,
    start_date=start_date,
    execution_delay = dtm.timedelta(days=1),
    bump_execution_delay = False,
)

strategy.plot.portfolio_table('TOP_ORDER_PTS', end_dt=dtm.date(2023,3,6))

# Use bump_execution_delay=True to bump trades that would execute outside trading hours to the EOD execution on the next day based on the delay provided via execution_delay
strategy = sig.SignalStrategy(
    currency='USD',
    signal_name=sig.signal.library.from_ts(signal).name,
    start_date=start_date,
    execution_delay = dtm.timedelta(days=1),
    bump_execution_delay = True,
)

strategy.plot.portfolio_table('TOP_ORDER_PTS', end_dt=dtm.date(2023,3,6))

Fixes

  • The copy code issues in BuildingBlockBrowser have been resolved.

  • Overridden attributes in the BuildingBlockBrowser have been updated.

  • The data_date and env_date in the intraday notebook have been updated so that they no longer return errors.

  • Bugs involving abe_encryption_key and stream_access endpoints have been resolved.

  • Timeline retrieval is now prevented before a strategy is run.

  • OTC instruments can now trade before the instrument's start date.

  • Use holidays to check the exchange open/close times and skip trades that occur outside of trading hours.

  • FX forwards now include the current date in the history time series.

  • The timestamp behaviour of the FX convert_at_d method has been corrected.

  • An attribute filter has been added to the DataBrowser widget.

  • The intraday argument can now be used in stoploss allocation functions.

  • The trading_decision_time argument has been removed following its deprecation.

  • Interactive optimizers now clear correctly.

v8.16.1

Fixes

  • Grouped strategy orders now execute at EOD.

  • Conversion errors on FX objects causing FXFix datapoint retrieval issues have been fixed.

  • The behavior of the back_adjusted parameter in the price_series function of the RollingFutureStrategy building block has been fixed.

v8.15.0

New curve CollateralizedOISCurve added

A new discount curve called CollateralizedOISCurve has been added to the framework. This new curve implements discounting on-the-fly using a collateral OIS discount curve and FX forwards. The curve applies the discounts based on collateral posted in a different currency (USD by default). The future cash flows are converted to the collateral currency using FX forwards and discounted using the collateral OIS curve.

The following code creates a curve that discounts JPY cash flows assuming that USD collateral is used for the instrument.

CollateralizedOISCurve(currency='JPY',
    curve_name='JPY.C',
    group_name="OIS ICE CURVE GROUP",
    interpolation_type='CS',
    collateral_currency='USD')

Date range parameters added to pnl_explain()

Two new start_date and end_date parameters have been added to the portfolio_greeks and pnl_explain functions (these functions belong to the rolling options/swaption strategies and also the DeltaHedgingStrategy).

Pass the start and end dates as a datetime. For example, start_date=datetime.date(2020,1,1).

This new feature has been introduced to increase the computation speeds. If a small date range is entered then the greeks and pnl are calculated for just this small date range, making the calculations faster.

New input data_points added to portfolio_greeks function

A new optional parameter data_points has been added to the portfolio_greeks function. When using this parameter in the rolling options strategies and the DeltaHedgingStrategy, the reported greek times will be overridden with the times specified by the data points list passed to the data_points parameter.

An example of this is shown below:

# creates the strategy
ro=sig.RollingOption(
    currency='USD',
    start_date=dtm.date(2019,1,4),
    group_name='EURUSD OTC OPTION GROUP',
    maturity='3M',
    rolling_frequencies=['1M'],
    option_type='Call',
    strike_type='SPOT',
    strike='ATM+3%',
    target_type='SpotNotionalAsProportionOfNAV',
    target_quantity=-10,
    end_date=dtm.date(2019,3,4)
)
# calls the function with the new data points
ro.portfolio_greeks(start_date=dtm.datetime(2019,1,8,17),
    end_date=dtm.date(2019,1,10),
    data_points=['LONDON_1600','LONDON_2000'])

Transaction costs updates

The interface for the defined transaction costs has been improved. These improvements include:

  • When entering parameters into a model, you must now enter them as a dictionary of parameter values. Previously, this was supported for only some transaction cost models but with this release, it is now available for all models.

  • The docstrings for each model have been updated and now also provide an example of how these transaction costs work.

  • Previously, some instruments (such as bonds and single stocks) used cost models defined on the objects instead of being defined via configured models. This made it harder to get additional information on the cost models. With this release, these cost models are now included.

Limits removed when plotting portfolio_table

Previously, when calling plot.portfolio_table with more than 100 rows the table would error.

With this release, if there are more than 100 rows the table is still plotted but a warning message is provided telling you that only the last 100 rows of the portfolio_table have been plotted.

If you want to view a DataFrame version of the portfolio_table, pass the argument as_df=True. In previous releases, the DataFrame would also error if there were more than 100 nows. With this release, the DataFrame returns every row available, even if there are more than 100 rows.

New parameter basket_creation_kwargs added to DynamicOptionsStrategy

You can now pass additional keyword arguments for the basket creation strategy when creating a DynamicOptionsStrategy. Pass the optional keyword arguments as a dictionary. An example of how to pass these arguments is shown in the code below:

# pass the additional keyword arguments in a dictionary.
bc_params = {'signal':signal,
             'option_group':spx_option_name,
             'strike':'SPOT',
             'maturity':'2M'}

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

Added ability aggregate trades in bottom_trades_df

A new parameter net_trades has been added to the inspect.bottom_trades_df method. If set to True, you can now see the aggregated orders for trades happening at the same time for the same instrument.

An example of this is shown below:

from sigtech.framework.default_strategy_objects import rolling_futures

rfs = rolling_futures.es_index_front()
rfs1 = rfs.clone_object({'fixed_contracts': 1, 'ticker': 'C1'})
rfs2 = rfs.clone_object({'fixed_contracts': 2, 'ticker': 'C2'})