FX Option Strangle Strategies using Dynamic Options#

New user? The best way to learn SigTech is to follow the steps in our user guide.

Restricted data: You will only have access to the data used in this notebook if your organisation has specifically purchased it. To check your current data access, view Data. If you would like to access more data, please contact sales@sigtech.com.

Changes will not be saved: Edits made to SigTech’s example notebooks like this will only persist for the duration of your session. When you restart the research environment, this notebook will have been restored to its original state, and any changes you have made will have been lost. To make permanent changes, copy and paste the content to a new notebook in one of your workspaces.

Introduction#

The purpose of this notebook is to create a systematic strategy involving multiple straddle strategies using the DynamicOptionsStrategy building block. This notebook contains three examples:

  • 1M-3M strangle with a delta strike signal

  • Delta Hedged strangle

  • Date specific strangle roll

Environment#

This section imports the relevant internal and external libraries, and sets up the platform environment.

[ ]:
import datetime as dtm
import seaborn as sns
import pandas as pd
import numpy as np

import sigtech.framework as sig
sns.set(rc={'figure.figsize':(18, 6)})

env = sig.init(env_date=dtm.date(2024, 2, 22))

Variables#

[ ]:
ccy = "USD"
fx_group = "EURUSD OTC OPTION GROUP"
start_date = dtm.date(2020, 1, 1)

Delta Signal#

Signal defining where to place the near and far strangles.

[ ]:
delta_signal = pd.DataFrame(
    np.random.uniform(0.1, 0.5, size=(50, 2)),
    index=pd.bdate_range(start_date - pd.Timedelta(days=365), periods=50, freq="M"),
    columns=["1M_delta", "3M_delta"]
)

delta_signal.head()

Baseline strangles with 1M and 3M delta strikes#

[ ]:
def strangle_creation_method(strategy,dt, positions) -> dict:
    """
    Creates the strangles needed by looking at the delta signal.
    """
    size_date = strategy.size_date_from_decision_dt(dt)
    # Look up near and far delta strikes from signal
    signal_column = "1M_delta" if strategy.maturity == "1M" else "3M_delta"
    latest_delta = delta_signal.loc[:size_date, signal_column][-1]

    # Generate the strangle
    call = strategy.group.get_option(
        option_type="Call",
        strike=latest_delta,
        strike_type="Delta",
        start_date=size_date,
        maturity=strategy.maturity,
    )

    put = strategy.group.get_option(
        option_type="Put",
        strike=-latest_delta,
        strike_type="Delta",
        start_date=size_date,
        maturity=strategy.maturity,
    )

    # Return weights
    return {
        call: 1,
        put: 1,
    }

Create a DynamicOptionsStrategy of strangles based on the method defined above

[ ]:
near_strangle = sig.DynamicOptionsStrategy(
    maturity="1M",
    basket_creation_method=strangle_creation_method,
    currency=ccy,
    group_name=fx_group,
    start_date=start_date,
    rolling_frequencies=["1M"],
    close_out_at_roll=True,
    target_quantity=1,
    target_type="SpotNotional",
    initial_cash=0,
    total_return=False,
    include_trading_costs=False,
)

far_strangle = sig.DynamicOptionsStrategy(
    maturity="3M",
    basket_creation_method=strangle_creation_method,
    currency=ccy,
    group_name=fx_group,
    start_date=start_date,
    rolling_frequencies=["1M"],
    close_out_at_roll=True,
    target_quantity=1,
    target_type="SpotNotional",
    initial_cash=0,
    total_return=False,
    include_trading_costs=False,
)

near_strangle.build()
far_strangle.build()
[ ]:
near_strangle.history().plot.line(legend=True, label="1M strangle")
far_strangle.history().plot.line(legend=True, label="3M strangle")

Analysing historical strikes#

[ ]:
near_strangle.plot.portfolio_table(near_strangle.action_datetimes()[0:10])

Delta hedged baseline strangles#

[ ]:
dh_near_strangle = sig.DeltaHedgingStrategy(
    currency=near_strangle.currency,
    start_date=near_strangle.start_date,
    option_strategy_name=near_strangle.name,
    include_option_strategy=True,
    hedging_instrument_type="FXSPOT",
    hedging_frequency="1BD",
    initial_cash=0,
    total_return=False,
    include_trading_costs=False,
)

dh_far_strangle = sig.DeltaHedgingStrategy(
    currency=far_strangle.currency,
    start_date=far_strangle.start_date,
    option_strategy_name=far_strangle.name,
    include_option_strategy=True,
    hedging_instrument_type="FXSPOT",
    hedging_frequency="1BD",
    initial_cash=0,
    total_return=False,
    include_trading_costs=False,
)
[ ]:
dh_near_strangle.history().plot.line(legend=True, label="Delta-hedged 1M strangle")
dh_far_strangle.history().plot.line(legend=True, label="Delta-hedged 3M strangle");

Date specific roll#

Rolling strangles based on signal dates

[ ]:
def strangle_creation_method_date_dependent(strategy, dt, positions) -> dict:
    """
    Creates the strangles needed by looking at the delta signal.
    """
    size_date = strategy.size_date_from_decision_dt(dt)

    # Date roll logic
    if dt.date() not in delta_signal.index.date:
        return {}

    # Look up existing positions
    strategy.close_out_options(dt)

    # Look up near and far delta strikes from signal
    signal_column = "1M_delta" if strategy.maturity == "1M" else "3M_delta"

    # Get latest delta signal
    latest_delta = delta_signal.loc[:size_date, signal_column][-1]

    # Strangle
    call = strategy.group.get_option(
        option_type="Call",
        strike=latest_delta,
        strike_type="Delta",
        start_date=size_date,
        maturity=strategy.maturity,
    )

    put = strategy.group.get_option(
        option_type="Put",
        strike=-latest_delta,
        strike_type="Delta",
        start_date=size_date,
        maturity=strategy.maturity,
    )

    # Return weights
    return {
        call: 1,
        put: 1,
    }
[ ]:
near_strangle_date_dependent = sig.DynamicOptionsStrategy(
    maturity="1M",
    basket_creation_method=strangle_creation_method_date_dependent,
    currency=ccy,
    group_name=fx_group,
    start_date=start_date,
    rolling_frequencies=["1BD"],  # check delta daily
    close_out_at_roll=False,  # don't close out at roll anymore automatically
    target_quantity=1,
    target_type="SpotNotional",
    initial_cash=0,
    total_return=False,
    include_trading_costs=False,
)

far_strangle_date_dependent = sig.DynamicOptionsStrategy(
    maturity="3M",
    basket_creation_method=strangle_creation_method_date_dependent,
    currency=ccy,
    group_name=fx_group,
    start_date=start_date,
    rolling_frequencies=["1BD"],  # check delta daily
    close_out_at_roll=False,  # don't close out at roll anymore automatically
    target_quantity=1,
    target_type="SpotNotional",
    initial_cash=0,
    total_return=False,
    include_trading_costs=False,
)

near_strangle_date_dependent.build()
far_strangle_date_dependent.build()
[ ]:
near_strangle_date_dependent.history().plot.line(legend=True, label="1M strangle")
far_strangle_date_dependent.history().plot.line(legend=True, label="3M strangle");