Futures#

A futures contract is a commitment to buy or sell an asset on the contract’s expiry date for a fixed price.

Investors use futures contracts to invest in an asset without ever buying or selling the asset itself. After buying a futures contract for some asset, an investor holds the contract until some point in time closer to its expiry date. Then the investor “rolls” the contract, trading it for another contract for the same asset, with a later expiry date.

Learn how to work with futures contracts on the SigTech platform.

Environment#

Import Python libraries and initialize the SigTech framework environment:

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

import sigtech.framework as sig

env = sig.init()

Learn more: Setting up the environment

Futures contract groups#

On the SigTech platform, a futures contract group (FCG) represents contracts issued by an exchange for the same asset, with sequential expiry dates.

Futures contract groups are instances of the FuturesContractGroup class. This class contains common information for futures that belong to the same futures strip (i.e. the trading of multiple futures contracts with different expiration dates, traded as a single transaction).

To retrieve a futures contract group on the SigTech platform, pass its object name to the sig.obj.get utility function:

# Zinc futures on London Metal Exchange
lx_fcg = sig.obj.get("LX COMDTY FUTURES GROUP")

# US Treasury bond futures
ty_fcg = sig.obj.get("TY COMDTY FUTURES GROUP")

# Frozen orange juice
jo_fcg = sig.obj.get("JO COMDTY FUTURES GROUP")

For most framework objects on the SigTech platform, you can use the data_dict instance method to retrieve a collection of descriptive static data about the object.

Print the static data fields available for a futures contract group:

jo_fcg.data_dict().keys()

Define a function to print selected static data for a framework object:

def get_info(sig_obj, dd_keys):
    if isinstance(sig_obj, str):
        sig_obj = sig.obj.get(sig_obj)
    info = f"{sig_obj.name}\n"
    dd = sig_obj.data_dict()
    for key in dd_keys:
        _key = f"{key}:".ljust(25)
        info += f"{_key} {dd[key]}\n"
    info += "\n"
    return info

Print select fields for some futures contract groups:

fcg_fields = [
    "asset_description", "contract_code", "contract_sector",
    "exchange_code", "item_product_type",
]
print(get_info(jo_fcg, fcg_fields))
print(get_info(ty_fcg, fcg_fields))
print(get_info(lx_fcg, fcg_fields))

List futures contracts in an FCG#

Each FCG has a set of futures contracts with sequential expiry dates. The object names of futures contracts in an FCG are made up of:

  • The FCG contract code

  • A year code: two numerical characters representing the contract year

  • A month code: an alphabetical character representing the contract month

Retrieve the object names of all futures contract instruments for an FCG:

jo_fcg.query_instrument_names()[:10]

View month codes for futures contracts#

To see all the futures month codes, use one of the following FCG class attributes:

  • sig.FuturesContractGroup.CONTRACT_MONTH_CODES_DICT

  • sig.FuturesContractGroup.CONTRACT_MONTH_CODES_LIST

print(sig.FuturesContractGroup.CONTRACT_MONTH_CODES_DICT)
print(sig.FuturesContractGroup.CONTRACT_MONTH_CODES_LIST)

Get contract months for an FCG#

For some futures products, only a subset of months per year have a futures contract.

Get an FCG’s contract months for a given year:

print(jo_fcg.month_codes_for_year(2020))
print(jo_fcg.month_codes_for_year(20))
print(jo_fcg.month_codes_for_year("2000"))
print(jo_fcg.month_codes_for_year("20"))

Futures contract instruments#

Futures contract instruments are instances of subclasses of the Future class, such as BondFuture or CommodityFuture.

A futures contract instrument’s object name comprises:

  • The FCG contract code

  • A month code: an alphabetical character representing the contract month

  • A year code: two numerical charaters representing the contract year

For example:

contract = sig.obj.get("JOF23 COMDTY")
print(contract.name, contract.contract_month, contract.contract_year)

contract = sig.obj.get("KCH09 COMDTY")
print(contract.name, contract.contract_month, contract.contract_year)

contract = sig.obj.get("LXQ09 COMDTY")
print(contract.name, contract.contract_month, contract.contract_year)

contract = sig.obj.get("CON18 COMDTY")
print(contract.name, contract.contract_month, contract.contract_year)

Define a function to retrieve a futures contract instrument for a given contract code, month, and year:

def get_contract(contract_code, month, year, contract_sector="COMDTY"):
    result = None
    year_code = str(year)[-2:]
    month_code = sig.FuturesContractGroup.CONTRACT_MONTH_CODES_LIST[int(month)-1]
    try:
        fcg = sig.obj.get(f"{contract_code} {contract_sector} FUTURES GROUP")
    except:
        pass
    else:
        if month_code in fcg.month_codes_for_year(year_code):
            result = sig.obj.get(f"{contract_code}{month_code}{year_code} {contract_sector}")
    return result

Retrieve some futures contract instruments:

contract = get_contract("JO", 1, 23)
print(contract.name, contract.contract_month, contract.contract_year)

contract = get_contract("KC", "03", "02")
print(contract.name, contract.contract_month, contract.contract_year)

contract = get_contract("LX", "12", 2012)
print(contract.name, contract.contract_month, contract.contract_year)

contract = get_contract("CO", 8, 12)
print(contract.name, contract.contract_month, contract.contract_year)

Alternatively, retrieve futures contract instruments from a futures contract group object.

Get the first futures contract for an FCG:

jo_first = sig.obj.get(jo_fcg.first_contract)
jo_first

Get the active futures contract for an FCG:

jo_active = jo_fcg.active_contract()
jo_active

From a futures contract instrument, get the next contract in the sequence:

jo_next = jo_active.next_contract()
jo_next.name

Retrieve time series data for a futures product#

View the available historical data fields for a futures contract instrument:

jo_active.activity_fields, jo_active.history_fields

Retrieve and plot historical performance data for a futures contract:

jo_active.history().plot()

Rolling future strategies#

Use the RollingFutureStrategy class to create futures investment strategies. You can choose to roll your futures strategy over multiple days by setting the rolling_rule parameter. Our page on roll schedules has a list of available rolling rules.

def create_rfs(contract_code, contract_sector="COMDTY"):
    return sig.RollingFutureStrategy(
        start_date=START_DATE,
        end_date=END_DATE,
        currency="USD",
        contract_code=contract_code,
        contract_sector=contract_sector,
        rolling_rule="F_0",
    )

Define some variables for the rolling future strategies:

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

index = pd.date_range(START_DATE, END_DATE, freq="D")
columns = ["LX", "KC", "JO"]

Create and compare some rolling future strategies:

df = pd.DataFrame(columns=columns, data={
    code: create_rfs(code).history()
    for code in columns
}, index=index).dropna()
df

Learn more: Rolling future strategies

Back-adjusted futures data#

You can use back-adjusted futures data to avoid triggering spurious trading signals during the roll.

Use the following parameters with an RFPriceIndex object to specify back adjustment options:

  • back_adjusted: Boolean; default is False. When True, the strategy uses back-adjusted data.

  • back_adjustment_type: String; default is "diff".

    • When set to "diff", the back adjustment is calculated by difference.

    • When set to "ratio", the back adjustment is calculated by ratio.

Define a function to create a back-adjusted price series:

def get_ba_series(contract_code, ba=False, ba_type=None, contract_sector="COMDTY"):
    return sig.RFPriceIndex(
        start_date=START_DATE,
        end_date=END_DATE,
        currency="USD",
        contract_code=contract_code,
        contract_sector=contract_sector,
        front_offset="-4,-1",
        back_adjusted=ba,
        back_adjustment_type=ba_type,
    )

Compare some back-adjusted data:

columns = ["None", "Diff", "Ratio"]
data = {
    "None": get_ba_series("CO", False).history(),
    "Diff": get_ba_series("CO", True).history(),
    "Ratio": get_ba_series("CO", True, "ratio").history(),
}
ba_df = pd.DataFrame(columns=columns, data=data, index=index).dropna()
ba_df