Tutorial: FX carry strategy

This tutorial shows new SigTech users how to construct a systematic investment strategy, where the strategy involves a number of different FX forward contracts, and where the investment decision is based on the difference between relevant short term interest rates.

Learn more: Example notebooks

Access: to run the related Jupyter notebook, the appropriate data entitlements need to be in place for your organisation. To request access to SigTech's FX data, email sales@Sigtech.com.

Workflow overview

When creating an investment strategy on the SigTech platform, the workflow follows these steps:

  1. Set up the environment

  2. Define the investment universe

  3. Create the strategy

  4. Construct the portfolio

  5. Generate the performance report

Set up the environment

Setting up your environment takes three steps:

  • Import the relevant internal and external libraries

  • Configure the environment parameters

  • Initialise the environment

import sigtech.framework as sig
from sigtech.framework.infra.analytics.fx.fx_market import FXMarket

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.config.init();

Define the investment universe

In this step, you will define the currencies that will form the universe of the investment strategy.

Note: the currencies are traded against the US Dollar for simplicity, but the platform also allows for cross currency pairs to be traded.

Learn more: Market Data

ASSETS = ['AUD', 'CAD', 'CHF', 'EUR', 'GBP', 'JPY', 'NOK', 'NZD', 'SEK']

Define the relevant start date to be used when creating the strategy:

START_DATE = dtm.date(2010, 1, 4)

Get the currency pairs based on the previously defined constant ASSETS:

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
}

Get the price time series for each currency pair below:

spot_rates = {
    ccy: sig.obj.get(ticker).history()
    for ccy, ticker in CCY_MAPPING.items()
}

This strategy is expressed using FX Forward contracts. When the investment universe contains forward contracts, it's important to handle the rolling of the contracts. As a given forward contract has a maturity date, it's crucial to systematically handle how the strategy trades in and out of forward contracts to maintain an exposure to the underlying asset.

The SigTech platform offers the RollingFXForwardStrategy class which allows easily handling of the rolling of forward contracts.

In this tutorial, nine RollingFXForwardStrategy objects are created. The following function allows you to easily create these RollingFXForwardStrategy objects by passing in a few parameters.

The RollingFXForwardStrategy objects are instantiated using a few different parameters, such as forward_tenor and long_currency.

Note: the RollingFXForwardStrategy objects assume a constant long exposure from start date to end date of the strategy.

def create_rolling_fx_forwards(asset, start_date,
                               fwd_tenor='3M_IMM', base_ccy='USD'):
    '''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

Using a dictionary comprehension, all of the previously defined currencies are passed into the following function, creating a RollingFXForwardStrategy object for each of the currencies against the US Dollar:

fwd_universe = {
    asset: create_rolling_fx_forwards(
        asset=asset,
        start_date=START_DATE,
    ) for asset in ASSETS
}
fwd_universe

The following code block shows how each of the RollingFXForwardStrategy objects are performing over time:

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

Create the strategy

The strategy will take a long position in the currency pairs where the spot FX rate is trading at a discount in relation to the forward FX rate, and a short position in currency pairs that are trading at a premium in relation to the spot FX rate.

In the following code block the forward FX rates are being generated for the time period of the strategy:

def generate_fwd_rates(spot_dates, under, over, fx_market, forward_tenor='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 = 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()
}

The following code block generates a -1 when the forward rate is below the spot rate, and a 1 when the forward rate is above the spot rate:

signals_carry = pd.DataFrame(
    {ccy: np.sign(fwd_rates[ccy] - spot_rates[ccy]) for ccy in ASSETS},
    columns=ASSETS
)
signals_carry.tail()

Construct the portfolio

To apply the strategy to the universe and combine all the assets into a portfolio, use the SignalStrategy class.

Before you can use the SignalStrategy class, you need to convert the pandas DataFrame of signals into a signal object:

signal_obj = sig.signal_library.from_ts(signals_carry)

You can now create a SignalStrategy object. There are numerous different parameters and alternatives that you can make use of, such as using different allocation functions between the assets in the strategy.

Learn more: Signal Strategy

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

Use the following code block to create a performance report that contains information on metrics, rolling performance charts and performance calendars.

There are a wide range of different performance views and reports available, depending on the specific use case.

sig.PerformanceReport(fx_carry_strat).report()

Next steps

Last updated

© 2023 SIG Technologies Limited