import numpy as np
import pandas as pd
import datetime as dt
import matplotlib.pyplot as plt
from quantopian.research.experimental import history
from quantopian.research.experimental import continuous_future
"""
this function takes an argument df (of type DataFrame) and inserts a "cost of carry" column that contains
the cost of carry for a given futures contract. the rows of the DataFrame should be as follows:
- column 1: current date
- column 2: current futures contract's price
- column 3: current futures contract's maturity date
- column 4: spot price of the futures contract's underlying
"""
def calc_cost_of_carry(df):
# iterate over the rows of df and for each row, calculate the value of cost of carry
for row in df.itertuples():
# the formula used for cost of carry is c = ln(current price / spot price) / (maturity date - current date)
# where c is cost of carry
current_date = row[0]
current_price = row[1]
maturity_date = row[3].expiration_date
spot_price = row[4]
cost_of_carry = np.log(current_price / spot_price) / (maturity_date - current_date).days
# after calculating cost of carry, insert the value into a new column at the given row
df.loc[row[0], "cost of carry"] = cost_of_carry
Take advantage of the spread between a commodity's futures contracts with different delivery dates. One of these commodities is natural gas (or related fossil fuels). The price of natural gas is lower in summer months than it is in winter months. The price of futures contracts should have a direct relationship with their underlying. Therefore, ordering futures contracts in summer months and selling them in winter months should result in profit.
If you'd like to get started on your own futures research and algorithm development, check out the many tutorials available on Quantopian's site.
Identify futures contracts that have cyclical price behavior.
Natural gas has a predictable demand cylce. Using that information, the price should also have a noticeable trend that can be applied to a strategy. With respect to the calendar cycle mentioned above, a potential strategy is a calendar spread strategy that buys futures contracts with a winter expiry date in summer months and exits these positions in winter months when the relative price of these contracts is highest.
A raw analysis of the price trend of natural gas oil does not confirm the hypothesis. Rather, what is most visible is the overall bearish trend. This is likely because of the unexpected decline of crude oil prices.
Now, let's move into exploring natural gas contracts.
natural_gas_future = continuous_future("NG", offset=0, roll="calendar", adjustment=None)
active_natural_gas_future = history(
natural_gas_future,
fields=["contract", "price", "volume"],
frequency="daily",
start_date="2014-01-01",
end_date="2017-01-01"
)
Below, we plot the price of natural gas futures contracts over the past two years. Unfortunately, there is no observable pattern in the pricing of these contracts besides a general decline, which is likely related to the decline in the price of the underlying.
active_natural_gas_future.price.plot()
Hoever, volume has remained relatively consistent, with the exception of early 2014 when there was a significant spike in trading volume of these contracts.
active_natural_gas_future.volume.plot()
There aren't any apparent price or volume trends. So, perhaps calculating cost of carry will show some visible trend.
$$ F(t, T) = S(t) \times e^{c(T - t)} $$Because spot price data is not available yet, we'll use a natural gas ETF as a proxy of the spot price.
natural_gas_etf = symbols("FCG")
natural_gas_etf_price_data = get_pricing(symbols=natural_gas_etf, start_date="2014-01-01", end_date="2017-01-01", frequency="daily")
The price of the ETF follows a somewhat similar bearish trend. Still, the recovery seen in the price of the futures contracts is not as pronounced in the price of the ETF. This is something worth exploring.
natural_gas_etf_price_data.price.plot()
So, when plotting the price of the natural gas ETF against a normalized natural gas futures contract price, we can begin confirming the pattern alluded to above. The prices do indeed start moving closer to each other in 2016. Calculating the mean square error between these two values further confirms this. However, this still does not move us in the direction of confirming a signal.
(10 * active_natural_gas_future["price"]).plot()
natural_gas_etf_price_data["price"].plot()
The plot below shows the normalized prices of natural gas and an ETF tracking the natural gas industry. It seems that before January 2016, there was backwardation between the price of the futures contract compared to the price of the underlying.
After plotting the mean squared error of the normalized prices of natural gas and a natural gas ETF, the difference between the two prices begins deminishing after May 2015.
mse_price = (10 * active_natural_gas_future["price"] - natural_gas_etf_price_data["price"]) ** 2
mse_price.plot()
active_natural_gas_future['spot price'] = natural_gas_etf_price_data['price']
active_natural_gas_future.head()
calc_cost_of_carry(active_natural_gas_future)
active_natural_gas_future['cost of carry'].plot()
plt.title("Cost of Carry")
plt.ylabel("Value")
plt.xlabel("Time")
Using the above plot. One strategy that comes to mind would be to use the Optimize API's MaximizeAlpha ability to maximize the cost of carry plotted above multiplied by negative 1. Before doing this however, contracts with an expiration date within approximately 15 days of the current trading day would need to be exited.