FX Carry Strategy#

Understand this code: Read our step-by-step explanation of this notebook: guided walkthrough.

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.

Environment#

[ ]:
import sigtech.framework as sig

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

sns.set(rc = {'figure.figsize': (18, 6)})
env = sig.init(env_date=dtm.date(2024, 2, 22), log_level = 'ERROR');

Define the investment universe#

[ ]:
ASSETS = ['AUD', 'CAD', 'CHF', 'EUR', 'GBP', 'JPY', 'NOK', 'NZD', 'SEK']
[ ]:
START_DATE = dtm.date(2010, 1, 4)
[ ]:
fx_cross=env.fx_cross
def ccy_mapping(ccy):
    if fx_cross.is_flipped('USD', ccy):
        return f'USD{ccy} CURNCY'
    else:
        return f'{ccy}USD CURNCY'
[ ]:
CCY_MAPPING = {
    ccy: ccy_mapping(ccy) for ccy in ASSETS
}
[ ]:
spot_rates = {
    ccy: sig.obj.get(ticker).history()
    for ccy, ticker in CCY_MAPPING.items()
}
[ ]:
def create_rolling_fx_forwards(
    asset: str,
    start_date: dtm.date,
    fwd_tenor: str = "3M_IMM",
    base_ccy: str = "USD",
) -> str:
    '''Creates RollingFXForwardStrategy objects and returns the name of the object.'''

    rfxfs = sig.RollingFXForwardStrategy(
        # Long currency
        long_currency = asset,

        # Short currency
        currency = base_ccy,

        # Tenor of forward
        forward_tenor = fwd_tenor,

        # Start date of strategy
        start_date = start_date,

        # Name of strategy
        ticker = f'{asset} FWD'
    )

    return rfxfs.name
[ ]:
fwd_universe = {
    asset: create_rolling_fx_forwards(
        asset=asset,
        start_date=START_DATE,
    ) for asset in ASSETS
}
fwd_universe
[ ]:
fwd_histories = pd.concat({asset: sig.obj.get(fwd_universe[asset]).history() for asset in ASSETS}, axis=1)
fwd_histories.plot();

Create the strategy#

[ ]:
def generate_fwd_rates(spot_dates, under: str, over: str, fx_market, forward_tenor: str = '3M'):
    maturities = [fx_market.fx_forward_date(over, under, date, forward_tenor) for date in spot_dates]
    return fx_market.calculate_forward_rate_batch(over, under, spot_dates, maturities)
[ ]:
fx_market = sig.FXMarket.instance()
fwd_rates = {
    ccy: pd.Series(generate_fwd_rates(
        spot_rates[ccy].index,
        over=CCY_MAPPING[ccy][:3],
        under=CCY_MAPPING[ccy][3:6],
        fx_market=fx_market
    ), index=spot_rates[ccy].index)
    for ccy, ticker in CCY_MAPPING.items()
}
[ ]:
signals_carry = pd.DataFrame(
    {ccy: np.sign(fwd_rates[ccy]-spot_rates[ccy]) for ccy in ASSETS},
    columns=ASSETS
)
signals_carry.tail()
[ ]:
signal_obj = sig.signal_library.from_ts(signals_carry)

Construct the portfolio#

[ ]:
fx_carry_strat = sig.SignalStrategy(
    # Start date of strategy
    start_date=START_DATE,

    # Base currency for strategy
    currency='USD',

    # Reference to signal object
    signal_name=signal_obj.name,

    # Rebalance frequency of the strategy
    rebalance_frequency='3M',

    # Allocation function used between constituents
    # in strategy
    allocation_function=sig.signal_library.allocation.equally_weighted,

    # Mapping constituent in signals to the relevant
    # RollingFutureStrategy objects
    instrument_mapping=fwd_universe,

    # Name of strategy
    ticker=f'FX Carry'
)

Generate the performance report#

[ ]:
sig.PerformanceReport(fx_carry_strat.history()).report()