/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
namespace QuantConnect.Securities.Future
{
///
/// Helpers for getting the futures contracts that are trading on a given date.
/// This is a substitute for the BacktestingFutureChainProvider, but
/// does not outright replace it because of missing entries. This will resolve
/// the listed contracts without having any data in place. We follow the listing rules
/// set forth by the exchange to get the s that are listed at a given date.
///
public static class FuturesListings
{
private static readonly Symbol _zb = Symbol.Create("ZB", SecurityType.Future, Market.CBOT);
private static readonly Symbol _zc = Symbol.Create("ZC", SecurityType.Future, Market.CBOT);
private static readonly Symbol _zs = Symbol.Create("ZS", SecurityType.Future, Market.CBOT);
private static readonly Symbol _zt = Symbol.Create("ZT", SecurityType.Future, Market.CBOT);
private static readonly Symbol _zw = Symbol.Create("ZW", SecurityType.Future, Market.CBOT);
private static Dictionary>> _futuresListingRules = new Dictionary>>
{
{ "ZB", t => QuarterlyContracts(_zb, t, 3) },
{ "ZC", t => MonthlyContractListings(
_zc,
t,
12,
new FuturesListingCycles(new[] { 3, 5, 9 }, 9),
new FuturesListingCycles(new[] { 7, 12 }, 8)) },
{ "ZN", t => QuarterlyContracts(_zt, t, 3) },
{ "ZS", t => MonthlyContractListings(
_zs,
t,
11,
new FuturesListingCycles(new[] { 1, 3, 5, 8, 9 }, 15),
new FuturesListingCycles(new[] { 7, 11 }, 8)) },
{ "ZT", t => QuarterlyContracts(_zt, t, 3) },
{ "ZW", t => MonthlyContractListings(
_zw,
t,
7,
new FuturesListingCycles(new[] { 3, 5, 7, 9, 12 }, 15)) }
};
///
/// Gets the listed futures contracts on a given date
///
/// Ticker of the future contract
/// Contracts to look up that are listed at that time
/// The currently trading contracts on the exchange
public static List ListedContracts(string futureTicker, DateTime time)
{
if (!_futuresListingRules.ContainsKey(futureTicker))
{
// No entries found. This differs from entries being returned as an empty array, where
// that would mean that no listings were found.
return null;
}
return _futuresListingRules[futureTicker](time);
}
///
/// Gets contracts following a quarterly listing procedure, with a limit of
/// how many contracts are listed at once.
///
/// Canonical Futures Symbol
/// Contracts to look up that are listed at that time
/// Number of Symbols we get back/are listed at a given time
/// Symbols that are listed at the given time
private static List QuarterlyContracts(Symbol canonicalFuture, DateTime time, int limit)
{
var contractMonth = new DateTime(time.Year, time.Month, 1);
var futureExpiry = DateTime.MinValue;
var expiryFunc = FuturesExpiryFunctions.FuturesExpiryFunction(canonicalFuture);
// Skip any contracts that have already expired.
while (futureExpiry < time)
{
futureExpiry = FuturesExpiryFunctions.FuturesExpiryFunction(canonicalFuture)(contractMonth);
contractMonth = contractMonth.AddMonths(1);
}
// Negate the last incrementation from the while loop to get the actual contract month of the future.
var firstFutureContractMonth = contractMonth.AddMonths(-1);
var quarterlyContracts = new List();
// Gets the next closest month from the current month in multiples of 3
var quarterlyContractMonth = (int)Math.Ceiling((double)firstFutureContractMonth.Month / 3) * 3;
for (var i = 0; i < limit; i++)
{
// We're past the expiration frontier due to the while loop above, which means
// that any contracts from here on out will be greater than the current time.
var currentContractMonth = firstFutureContractMonth.AddMonths(-firstFutureContractMonth.Month + quarterlyContractMonth);
var currentFutureExpiry = expiryFunc(currentContractMonth);
quarterlyContracts.Add(Symbol.CreateFuture(canonicalFuture.ID.Symbol, canonicalFuture.ID.Market, currentFutureExpiry));
quarterlyContractMonth += 3;
}
return quarterlyContracts;
}
///
/// Gets Futures contracts that follow a limited cyclical pattern
///
/// Canonical Futures Symbol
/// Contracts to look up that are listed at that time
/// Contract month that results in new listings after this contract's expiry
///
/// Cycles that define the number of contracts and the months the contracts are listed on, including
/// the limit of how many contracts will be listed.
///
/// Symbols that are listed at the given time
private static List MonthlyContractListings(
Symbol canonicalFuture,
DateTime time,
int contractMonthForNewListings,
params FuturesListingCycles[] futureListingCycles)
{
var listings = new List();
var expiryFunc = FuturesExpiryFunctions.FuturesExpiryFunction(canonicalFuture);
var yearDelta = 0;
var contractMonthForNewListingCycle = new DateTime(time.Year, contractMonthForNewListings, 1);
var contractMonthForNewListingCycleExpiry = expiryFunc(contractMonthForNewListingCycle);
if (time <= contractMonthForNewListingCycleExpiry)
{
// Go back a year if we haven't yet crossed this year's contract renewal expiration date.
contractMonthForNewListingCycleExpiry = expiryFunc(contractMonthForNewListingCycle.AddYears(-1));
yearDelta = -1;
}
foreach (var listingCycle in futureListingCycles)
{
var year = yearDelta;
var count = 0;
var initialListings = true;
while (count != listingCycle.Limit)
{
var monthStartIndex = 0;
if (initialListings)
{
// For the initial listing, we want to start counting at some month that might not be the first
// index of the collection. The index is discovered here and used as the starting point for listed contracts.
monthStartIndex = listingCycle.Cycle.Length - listingCycle.Cycle.Count(c => c > contractMonthForNewListingCycleExpiry.Month);
initialListings = false;
}
for (var m = monthStartIndex; m < listingCycle.Cycle.Length; m++)
{
// Add the future's expiration to the listings
var currentContractMonth = new DateTime(time.Year + year, listingCycle.Cycle[m], 1);
var currentFutureExpiry = expiryFunc(currentContractMonth);
if (currentFutureExpiry >= time)
{
listings.Add(Symbol.CreateFuture(canonicalFuture.ID.Symbol, canonicalFuture.ID.Market, currentFutureExpiry));
}
if (++count == listingCycle.Limit)
{
break;
}
}
year++;
}
}
return listings;
}
///
/// Listing Cycles, i.e. the months and number of contracts that are renewed whenever
/// the specified renewal expiration contract expires.
///
///
/// Example:
///
/// (from: https://www.cmegroup.com/trading/agricultural/grain-and-oilseed/wheat_contract_specifications.html)
/// "15 monthly contracts of Mar, May, Jul, Sep, Dec listed annually following the termination of trading in the July contract of the current year."
///
/// This would equate to a cycle of [3, 5, 7, 9, 12], a limit of 15, and the contract month == 7.
///
private class FuturesListingCycles
{
///
/// Monthly cycles that the futures listings rule follows
///
public int[] Cycle { get; }
///
/// Max number of contracts returned by this rule
///
public int Limit { get; }
///
/// Creates a listing cycle rule
///
/// New contract listing cycles
/// Max number of contracts to return in this rule
public FuturesListingCycles(int[] cycle, int limit)
{
Cycle = cycle;
Limit = limit;
}
}
}
}