FX carry strategy#

Create a strategy that trades FX forward contracts. To make trading decisions, the FX carry strategy uses a signal based on forward and spot rate data for different currency pairs.

The code on this page uses FX data.

Please email sales@sigtech.com to upgrade your data entitlements if you can’t run the code on this page!

Initialize SigTech

Import Python libraries:

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

import sigtech.framework as sig

Initialize the SigTech framework environment:

env = sig.init()

Define FX carry strategy parameters#

Define start and end dates for the FX carry strategy:

START_DATE = dtm.date(2023, 1, 1)
END_DATE = dtm.date(2024, 1, 1)

Define currency assets#

The FX carry strategy is composed of multiple rolling FX forward strategies, each of which represents an underlying currency asset.

Define the currency assets:

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

SigTech handles currency conversions and related FX market conventions using an environment component, fx_cross. Retrieve the fx_cross environment object:

fx_cross = env.fx_cross
fx_cross

For some currencies, market quoting conventions reverse the order of the base and quote currencies. Define a helper function, ccy_mapping, that uses fx_cross to make sure currency pairs are specified correctly:

def ccy_mapping(ccy):
    if fx_cross.is_flipped("USD", ccy):
        return f"USD{ccy} CURNCY"
    else:
        return f"{ccy}USD CURNCY"

Use the helper function to map each asset currency to a currency pair:

CCY_MAPPING = {
    ccy: ccy_mapping(ccy) for ccy in ASSETS
}
CCY_MAPPING

Retrieve spot rates#

Retrieve spot rate data for each currency pair:

spot_rates = {
    ccy: sig.obj.get(ticker).history()
    for ccy, ticker in CCY_MAPPING.items()
}
[print(key, val.iloc[0]) for key, val in spot_rates.items()];

Create a rolling FX forward strategy for each asset#

Define a helper function, create_rfxf, that creates RollingFXForwardStrategy strategy objects:

def create_rfxf(asset, forward_tenor="3M_IMM"):
    strat = sig.RollingFXForwardStrategy(
        start_date=START_DATE,
        end_date=END_DATE,
        currency="USD",
        long_currency=asset,
        forward_tenor=forward_tenor,
    )
    return strat.name

Create a dictionary of RollingFxForwardStrategy objects:

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

Plot the performance of the rolling FX forward strategies over time:

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

Create a trading signal#

The FX carry strategy uses a trading signal to decide whether to go long or short in its component strategies.

This section creates the trading signal by examining FX forward and spot rates for each currency pair.

Generate FX forward rates#

The sig.FXMarket class calculates the FX forward curves that determine FX forward prices. Retrieve an instance of FXMarket:

fx_market = sig.FXMarket.instance()
fx_market

Define a helper function, generate_fwd_rates, to generate the forward FX rates for the same dates as the spot rates:

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)

Use the helper function to generate a forward rate for each spot rate:

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()
}
[print(key, val.iloc[0]) for key, val in fwd_rates.items()];

Create trading signal data#

When the forward rate is below the spot rate, the signal for that currency pair is negative (-1). This tells the FX carry strategy to go short for that currency pair. When the forward rate is above the spot rate, the signal for that currency pair is positive (1) and the FX carry strategy goes long.

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

Use a utility function from the SigTech signal library to create a signal object:

signal_obj = sig.signal_library.from_ts(signals_carry)

Create the FX carry strategy#

Next, create a strategy that trades component rolling FX forward strategies according to the signal data.

Learn more

You can change how a signal strategy allocates funds between different assets by specifying the strategy’s allocation function.

Signal strategy allocation functions

The code block below creates a strategy that rebalances every three months. The assets are equally weighted. To control trading decisions, the strategy uses the signal object you created in the previous section. The instrument_mapping parameter maps the strategy objects to their corresponding signals.

fx_carry_strat = sig.SignalStrategy(
    start_date=START_DATE,
    end_date=END_DATE,
    currency="USD",
    signal_name=signal_obj.name,
    rebalance_frequency="3M",
    allocation_function=sig.signal_library.allocation.equally_weighted,
    instrument_mapping=fwd_universe,
    ticker=f"FX Carry",
)
fx_carry_strat.history().tail()

Create a performance report#

Create a report with performance metrics, charts, and calendars to help analyze the strategy’s results.

sig.PerformanceReport(fx_carry_strat).report()