Futures data is now available in Quantopian research for 72 US futures going back to the start of 2002 (where applicable). Data includes daily and minutely open, high, low, close, and volume (OHLCV) data for 24 hours x 5 days a week and is collected from electronic trade data.
The list of US futures currently available on Quantopian can be found at the end of this notebook.
Let's start by looking at the data for a particular contract for the Crude Oil future. Similar to equities, a reference to a futures contract is obtained via the symbols
function. Futures contracts are denoted by a base symbol + a code for month/year of delivery. CLF16
is a contract for crude oil (CL
) with delivery in January (F
) 2016 (16
). -- Here is a reference for a mapping of month codes.
clf16 = symbols('CLF16')
clf16
Here is a brief explanation of some of the properies of the Future
object:
root_symbol
: The root symbol of the underlying. For example, 'CL' for crude oil.start_date
: The first date that the contract exists on Quantopian. Note that the price of a contract might be a NaN
near the start_date
as it may not be traded actively until closer to delivery.auto_close_date
: The auto_close_date
is 2 days prior to the earlier of the end_date
(last traded date) and the notice_date
. See this explanation of last traded date and notice date. In backtesting, positions in contracts will be automatically be closed out on their auto_close_date
.tick_size
: The price of a future can only change in increments of its tick_size
. For example, CL
changes in increments of \$0.01.multiplier
: The number of units per contract. A contract for CL
is for 1000 barrels of oil.history
¶Right now, futures OHLCV data is available in research via a new history
function in the experimental
library. It's possible that this API might change in the near future depending on the feedback that we get from you, the community. The history
function is very similar to get_pricing
.
To start interacting with pricing and volume data for futures, let's import history
:
from quantopian.research.experimental import history
In short, history
can be called like this:
history(assets, fields, frequency, start_date, end_date)
To see the full doc string for history
, uncomment and run the next cell.
#print history.__doc__
Let's get the close price and volume of our crude oil contract for the 2 months leading up to the delivery date (2015-12-21
). Note that all pricing data for futures are unit prices. For a CL
contract, the price is the price per barrel, and a contract is for 1000 barrels (the multiplier
). In backtesting, the value of a portfolio will change by an amount equal to the price change * multiplier
.
clf16_data = history(
clf16,
fields=['price', 'volume'],
frequency='daily',
start_date='2015-10-21',
end_date='2015-12-21'
)
clf16_data.head()
Now let's plot each field.
clf16_data.price.plot();
clf16_data.volume.plot();
The rise and subsequent drop in trading volume prior to the delivery date of a contract is typical behavior for futures. Let's see what the volume looks like for a series of consecutive contracts.
cl_contracts = symbols(['CLF16', 'CLG16', 'CLH16', 'CLJ16', 'CLK16', 'CLM16'])
cl_consecutive_contract_volume = history(
cl_contracts,
fields='volume',
frequency='daily',
start_date='2015-10-21',
end_date='2016-06-01'
)
cl_consecutive_contract_volume.plot();
Trading activity jumps from one contract to the next. Transitions happen just prior to the delivery date of each contract.
This phenomenon can make it difficult to work with futures. Having to explicitly reference a series of transient contracts when trading or simulating futures can be a hassle.
In order to trade consecutive contracts for the same underlying future, we can use what's called a "Continuous Future".
ContinuousFutures
¶Continuous futures are abstractions over the 'underlying' commodities/assets/indexes of futures. For example, if we wanted to trade crude oil, we could create a reference to CL
, instead of a series of CL
contracts. Continuous futures essentially maintain a reference to a 'current' contract deemed to be the active contract for the particular underlying.
N.B. Continuous futures are not tradable assets. They maintain a reference to the current active contract related to a given underlying.
To create a continuous future on Quantopian, we must import the continuous_future
function from the experimental
library. Similar to history
, the continuous_future
function is in the quantopian.research.experimental
.
Let's import it and take a look at the documentation.
from quantopian.research.experimental import continuous_future
print continuous_future.__doc__
So there are 4 arguments that we need to consider.
root_symbol
: The root symbol of the underlying. For example, 'CL' for crude oil.offset
: The distance from the primary contract. 0 = primary, 1 = secondary, etc. We'll get into this more later.roll
: How to determine the 'current' contract of the continuous future. Current options are 'volume'
and 'calendar'
. The 'volume' approach chooses the current active contract based on trading volume. The 'calendar' approach chooses the current active contract based simply on the auto_close_date
s of each contract.**adjustment
: How to adjust historical prices from earlier contracts. We'll get into this more later. Options are 'mul'
, 'add'
, or 'None'
.cl = continuous_future('CL', offset=0, roll='volume', adjustment='mul')
cl
To determine the active contract of a continuous future, we can ask for the 'contract'
field of a ContinuousFuture
object from history
.
cl_active = history(
cl,
fields='contract',
frequency='daily',
start_date='2015-11-10',
end_date='2015-11-25'
)
cl_active.tail(10)
To get a better sense of what this is doing, let's get the volume history of our CL continuous future and plot it with the individual contract volumes that we plotted earlier.
cl_continuous_volume = history(
cl,
fields='volume',
frequency='daily',
start_date='2015-10-21',
end_date='2016-06-01'
)
import pandas as pd
cl_volume_history = pd.concat([cl_consecutive_contract_volume, cl_continuous_volume], axis=1)
cl_volume_history.plot(style={cl: 'k--'});
From this plot, we see that the volume for the CL ContinuousFuture
is essentially the skyline of the individual contract volumes. Note that there are some points where this looks a little off, most notably in the transition from CLK16
to CLM16
between April and May. This is because the rolls are currently computed daily, using only the previous day's volume to avoid lookahead bias.
Now let's take a look at pricing.
# Pricing data for our consecutive contracts from earlier.
cl_consecutive_contract_pricing = history(
cl_contracts,
fields='price',
frequency='daily',
start_date='2015-10-21',
end_date='2016-06-01'
)
# Pricing data for our `ContinuousFuture`.
cl_continuous_future_pricing = history(
cl,
fields='price',
frequency='daily',
start_date='2015-10-21',
end_date='2016-06-01'
)
Plotting the pricing data for consecutive contracts over a 7 month span is a little bit tricky because at the start, we have prices for all 7 contracts, which makes it a bit hard to visualize:
cl_consecutive_contract_pricing.plot();
To better visualize this, let's look at the historical price of the active contract of our CL ContinuousFuture
.
cl_continuous_future_pricing.plot();
This represents the price of the underlying commodity, CL, on the most actively traded contract. Much easier to look at.
You might notice that the price at the start of this plot exceeds \$60, but when we plotted the individual contracts, it barely made it above \$50. This is because the historical price is getting adjusted for jumps between contracts.
The best way to explain this is to plot the prices history of the unadjusted continuous future.
cl_unadjusted = continuous_future('CL', offset=0, roll='volume', adjustment=None)
# Pricing data for our `ContinuousFuture`.
cl_cf_pricing_unadjusted = history(
cl_unadjusted,
fields='price',
frequency='daily',
start_date='2015-10-21',
end_date='2016-06-01'
)
cl_cf_pricing_unadjusted.plot();
It's still a bit tricky to tell what's going on. Let's see if we can make it a little more obvious by plotting the active contracts separately.
cl_unadjusted_history = history(
cl_unadjusted,
fields=['contract', 'price'],
frequency='daily',
start_date='2015-10-21',
end_date='2016-06-01'
)
cl_unadjusted_history = cl_unadjusted_history.pivot(index=cl_unadjusted_history.index, columns='contract')
cl_unadjusted_history.plot();
The increase in price when jumping from one contract is not considered to be an increase in value in the future. Instead, it is associated with the carrying cost of the underlying commodity. This will be covered more in the Lecture Series (futures lectures in progress).
Let's see what the unadjusted ContinuousFuture
is doing.
pd.concat(
[cl_unadjusted_history, cl_cf_pricing_unadjusted],
axis=1
).plot(
style={cl_unadjusted: 'k--'}
);
And now the adjusted ContinuousFuture
.
pd.concat(
[cl_unadjusted_history, cl_continuous_future_pricing],
axis=1
).plot(
style={cl: 'k--'}
);
In this example, our ContinuousFuture
is using the 'mul'
adjustment technique. This essentially computes the adjustment as the ratio of new contract price / old contract price
whenever the active contract rolls to a new contract.
The 'add'
technique computes the adjustment as the difference new contract price - old contract price
.
The offset
of a continuous future determines whether the active contract is the 'primary', the 'secondary', 'tertiary', etc. contract. So far in this notebook, we've been looking at the primary contracts, which are the contracts trading with the earliest delivery. The secondary contract is the one with the subsequent delivery date, tertiary is the next, etc.
For example, if we want to look at the term structure of a particular future, we can get a chain of contracts like this:
cl_cfs = []
for i in range(5):
cl_cfs.append(continuous_future('CL', offset=i, roll='volume', adjustment='mul'))
pricing = history(
cl_cfs,
fields=['price', 'contract'],
frequency='daily',
start_date='2015-05-05',
end_date='2015-05-05'
)
Let's convert this Panel into a DataFrame and change our index to the auto_close_date
of the active contracts.
term_structure = pricing[:, '2015-05-05', :]
term_structure.set_index(pd.Index([c.auto_close_date for c in term_structure.contract]), inplace=True)
term_structure.plot(marker='o');
This is a normal futures curve.
This is a pre-release of futures in research. We're looking for feedback on the API presented in this notebook. If you have suggestions, difficulty using certain parts of the API, or any questions, please let us know.
We are working on adding futures to the backtester. The API for futures in backtesting will look very similar to the API presented in this notebook. We're planning to officially launch futures in backtesting at QuantCon.
These are the 72 US futures that are currently available on Quantopian.
Symbol | Future |
---|---|
BD | Big Dow |
BO | Soybean Oil |
CM | Corn E-Mini |
CN | Corn |
DJ | DJIA Futures |
ET | Ethanol |
FF | 30-Day Federal Funds |
FI | 5-Year Deliverable Interest Rate Swap Futures |
FS | 5-Year Interest Rate Swap Futures |
FV | 5-Year T-Note |
MB | Municipal Bonds |
MS | Soybeans E-Mini |
MW | Wheat E-Mini |
OA | Oats |
RR | Rough Rice |
SM | Soybean Meal |
SY | Soybeans |
TN | 10-Year Deliverable Interest Rate Swap Futures |
TS | 10-Year Interest Rate Swap Futures |
TU | 2-Year T-Note |
TY | 10-Year T-Note |
UB | Ultra Tbond |
US | 30-Year T-Bond |
WC | Wheat |
YM | Dow Jones E-mini |
VX | VIX Futures |
AD | Australian Dollar |
AI | Bloomberg Commodity Index Futures |
BP | British Pound |
CD | Canadian Dollar |
EC | Euro FX |
ED | Eurodollar |
EE | Euro FX E-mini |
ES | S&P 500 E-Mini |
EU | E-micro EUR/USD Futures |
FC | Feeder Cattle |
JE | Japanese Yen E-mini |
JY | Japanese Yen |
LB | Lumber |
LC | Live Cattle |
LH | Lean Hogs |
MD | S&P 400 MidCap Futures |
ME | Mexican Peso |
MI | S&P 400 MidCap E-Mini |
ND | NASDAQ 100 Futures |
NK | Nikkei 225 Futures |
NQ | NASDAQ 100 E-Mini |
NZ | New Zealand Dollar |
SF | Swiss Franc |
SP | S&P 500 Futures |
TB | TBills |
GC | Gold |
HG | Copper High Grade |
SV | Silver |
CL | Light Sweet Crude Oil |
HO | NY Harbor ULSD Futures |
HU | Unleaded Gasoline |
NG | Natural Gas |
PA | Palladium |
PL | Platinum |
PB | Pork Bellies |
QG | Natural Gas E-mini |
QM | Crude Oil E-Mini |
XB | RBOB Gasoline Futures |
EI | MSCI Emerging Markets Mini |
EL | Eurodollar NYSE LIFFE |
MG | MSCI EAFE Mini |
XG | Gold mini-sized |
YS | Silver mini-sized |
RM | Russell 1000 Mini |
SB | Sugar #11 |
ER | Russell 2000 Mini |
# All of the available futures categorized in a dictionary for convenience.
available_futures = {
# Agricultural Commodities
'ag_commodities': {
'soybean': continuous_future('SY', offset=0, roll='volume', adjustment='mul'),
'soybean_emini': continuous_future('MS', offset=0, roll='volume', adjustment='mul'),
'soybean_oil': continuous_future('BO', offset=0, roll='volume', adjustment='mul'),
'soybean_meal': continuous_future('SM', offset=0, roll='volume', adjustment='mul'),
'wheat': continuous_future('WC', offset=0, roll='volume', adjustment='mul'),
'wheat_emini': continuous_future('MW', offset=0, roll='volume', adjustment='mul'),
'corn': continuous_future('CN', offset=0, roll='volume', adjustment='mul'),
'corn_emini': continuous_future('CM', offset=0, roll='volume', adjustment='mul'),
'oats': continuous_future('OA', offset=0, roll='volume', adjustment='mul'),
'rough_rice': continuous_future('RR', offset=0, roll='volume', adjustment='mul'),
'sugar': continuous_future('SB', offset=0, roll='volume', adjustment='mul'),
'ethanol': continuous_future('ET', offset=0, roll='volume', adjustment='mul'),
'feeder_cattle': continuous_future('FC', offset=0, roll='volume', adjustment='mul'),
'live_cattle': continuous_future('LC', offset=0, roll='volume', adjustment='mul'),
'lean_hogs': continuous_future('LH', offset=0, roll='volume', adjustment='mul'),
'pork_bellies': continuous_future('PB', offset=0, roll='volume', adjustment='mul'),
'lumber': continuous_future('LB', offset=0, roll='volume', adjustment='mul'),
},
# Non-Agricultural Commodities
'non_ag_commodities': {
'crude_oil': continuous_future('CL', offset=0, roll='volume', adjustment='mul'),
'crude_oil_emini': continuous_future('QM', offset=0, roll='volume', adjustment='mul'),
'natural_gas': continuous_future('NG', offset=0, roll='volume', adjustment='mul'),
'natural_gas_emini': continuous_future('QG', offset=0, roll='volume', adjustment='mul'),
'unleaded_gasoline': continuous_future('HU', offset=0, roll='volume', adjustment='mul'),
'rbob_gasoline': continuous_future('XB', offset=0, roll='volume', adjustment='mul'),
'heating_oil': continuous_future('HO', offset=0, roll='volume', adjustment='mul'),
'gold': continuous_future('GC', offset=0, roll='volume', adjustment='mul'),
'gold_mini': continuous_future('XG', offset=0, roll='volume', adjustment='mul'),
'silver': continuous_future('SV', offset=0, roll='volume', adjustment='mul'),
'silver_mini': continuous_future('YS', offset=0, roll='volume', adjustment='mul'),
'copper': continuous_future('HG', offset=0, roll='volume', adjustment='mul'),
'palladium': continuous_future('PA', offset=0, roll='volume', adjustment='mul'),
'platinum': continuous_future('PL', offset=0, roll='volume', adjustment='mul'),
},
# Currencies
'currencies': {
'jpy': continuous_future('JY', offset=0, roll='volume', adjustment='mul'),
'jpy_emini': continuous_future('JE', offset=0, roll='volume', adjustment='mul'),
'cad': continuous_future('CD', offset=0, roll='volume', adjustment='mul'),
'mxn': continuous_future('ME', offset=0, roll='volume', adjustment='mul'),
'aud': continuous_future('AD', offset=0, roll='volume', adjustment='mul'),
'nzd': continuous_future('NZ', offset=0, roll='volume', adjustment='mul'),
'gbp': continuous_future('BP', offset=0, roll='volume', adjustment='mul'),
'chf': continuous_future('SF', offset=0, roll='volume', adjustment='mul'),
'eur_emicro': continuous_future('EU', offset=0, roll='volume', adjustment='mul'),
'euro_fx': continuous_future('EC', offset=0, roll='volume', adjustment='mul'),
'euro_fx_emini': continuous_future('EE', offset=0, roll='volume', adjustment='mul'),
'eurodollar_nyse_liffe': continuous_future('EL', offset=0, roll='volume', adjustment='mul'),
},
# Equities
'equities': {
'sp500': continuous_future('SP', offset=0, roll='volume', adjustment='mul'),
'sp500_emini': continuous_future('ES', offset=0, roll='volume', adjustment='mul'),
'sp400_midcap': continuous_future('MD', offset=0, roll='volume', adjustment='mul'),
'sp400_midcap_emini': continuous_future('MI', offset=0, roll='volume', adjustment='mul'),
'nasdaq100': continuous_future('ND', offset=0, roll='volume', adjustment='mul'),
'nasdaq100_emini': continuous_future('NQ', offset=0, roll='volume', adjustment='mul'),
'russell1000_emini': continuous_future('RM', offset=0, roll='volume', adjustment='mul'),
'russell2000_emini': continuous_future('ER', offset=0, roll='volume', adjustment='mul'),
'nikkei225': continuous_future('NK', offset=0, roll='volume', adjustment='mul'),
'MSCI_emerging_markets_mini': continuous_future('EI', offset=0, roll='volume', adjustment='mul'),
'MSCI_EAFE_mini': continuous_future('MG', offset=0, roll='volume', adjustment='mul'),
'dow_jones': continuous_future('DJ', offset=0, roll='volume', adjustment='mul'),
'dow_jones_emini': continuous_future('YM', offset=0, roll='volume', adjustment='mul'),
'big_dow': continuous_future('BD', offset=0, roll='volume', adjustment='mul'),
'vix': continuous_future('VX', offset=0, roll='volume', adjustment='mul'),
'bloomberg_commodity_index': continuous_future('AI', offset=0, roll='volume', adjustment='mul'),
},
# Rates
'rates': {
'us_2y': continuous_future('TU', offset=0, roll='volume', adjustment='mul'),
'us_5y': continuous_future('FV', offset=0, roll='volume', adjustment='mul'),
'us_10y': continuous_future('TY', offset=0, roll='volume', adjustment='mul'),
'us_30y': continuous_future('US', offset=0, roll='volume', adjustment='mul'),
'deliverable_interest_rate_swap_5y': continuous_future('FI', offset=0, roll='volume', adjustment='mul'),
'deliverable_interest_rate_swap_10y': continuous_future('TN', offset=0, roll='volume', adjustment='mul'),
'interest_rate_swap_5y': continuous_future('FS', offset=0, roll='volume', adjustment='mul'),
'interest_rate_swap_10y': continuous_future('TS', offset=0, roll='volume', adjustment='mul'),
'municipal_bonds': continuous_future('MB', offset=0, roll='volume', adjustment='mul'),
'utra_tbond': continuous_future('UB', offset=0, roll='volume', adjustment='mul'),
'tbills': continuous_future('TB', offset=0, roll='volume', adjustment='mul'),
'fed30day': continuous_future('FF', offset=0, roll='volume', adjustment='mul'),
'eur': continuous_future('ED', offset=0, roll='volume', adjustment='mul'),
},
}