QuantRocket logo


Moonshot Native Spread Strategy

To run the earlier Moonshot strategy on a native calendar spread requires modified code because we are now trading a single instrument instead of multiple instruments. The modified code is provided in calspread_native.py.

Code highlights

Target weights

Moonshot expects target weights to be defined as a percentage of capital: for example, a target weight of 0.10 tells Moonshot to buy a number of futures contracts equal to 10% of capital, based on the contract's price and multiplier. This presents a problem for native combos, as the combo price is often a small number (specifically, the difference in price of the two legs), which can result in Moonshot calculating a large number of contracts that should be ordered.

The recommended solution is to specify the exact number of spread contracts to order, rather than relying on percentage weights. To accomplish this, we first set the percentage weights extremely high in signals_to_target_weights:

def signals_to_target_weights(self, signals, prices):
    weights = signals * 1000
    return weights

Then, we reduce the weights to the exact desired quantities (in this example 1 contract) in limit_position_sizes:

def limit_position_sizes(self, prices):
    Limit the position sizes to 1 spread contract.

    (Note that limit_position_sizes only cares about absolute values so no need
    to worry about signs.)
    bids = prices.loc["BidPriceClose"]
    ones = pd.DataFrame(1, index=bids.index, columns=bids.columns)
    max_quantities_for_longs = max_quantities_for_shorts = ones
    return max_quantities_for_longs, max_quantities_for_shorts


To support live/paper trading, the native spread strategy defines an order_stubs_to_orders method which routes orders to NYMEX, ensuring the combo orders are executed as native orders (see the usage guide to learn more):

def order_stubs_to_orders(self, orders, prices):
    orders["Exchange"] = "NYMEX"
    orders["OrderType"] = "MKT"
    orders["Tif"] = "DAY"
    return orders

Install strategy file

Install the strategy by moving it to the /codeload/moonshot directory:

Example Backtest

After collecting an adequate amount of real-time data (at least enough to cover BBAND_LOOKBACK_WINDOW), it is possible to run a backtest of the modified strategy. To generate trading activity for the backtest, we use the params argument to reset some parameters on-the-fly to lower their thresholds:

Then we see if there were any trades:

Account allocation

To trade the strategy, we must allocate calspread-native-cl to one or more accounts. Open quantrocket.moonshot.allocations.yml, edit the account number to match your live or paper IB account, and edit the capital allocation percentage as desired.

If you don't already have a quantrocket.moonshot.allocations.yml in the /codeload directory (i.e. top level of the Jupyter file browser), you can execute the following command to copy it over. Otherwise, append the new allocation to your existing file.

Generate Moonshot orders

Next we can run Moonshot's trade command to generate example orders.

First, we check the backtest results for a time when a signal was generated:

For testing purposes, edit the strategy file so that the strategy parameters match those used to produce the backtest results (BBAND_WINDOW = 10 and BBAND_STD = 1 were the parameters we set on-the-fly in this example).

Then we can use the --review-date parameter to tell Moonshot to generate orders as if it were one of the above example times. Moonshot returns a CSV of orders, which we format for the terminal with csvlook:

Next Up

Part 6: Scheduling