Files
quantconnect--lean/Tests/Common/SymbolRepresentationTests.cs
Gerardo Salazar 0f0a2bc9a8 Fixes weeklies parsing, causing certain futures to be inaccessible in QCAlgorithm (#4936)
* Fixes weeklies parsing, causing certain futures to be inaccessible in Algorithm

  The FuturesExpiryFunction expects the contract month of the Future,
  not the expiration. As a result, the contract gets filtered as a
  weekly contract, rather than as a standard due to the discrepancy
  between the expiry dates when the contract month differs from the
  expiry date's month.

  A very important fact to note is that futures can and do expire prior
  to the contract month. BZ,(brent crude financial futures) expire two
  months prior to the contract month, CL one month prior, etc.

  There has been an addition that contains a "reverse" futures expiry function
  lookup table. We use this to lookup the contract month to re-calculate
  the Future expiry.

  This PR also fixes dairy and adds extra expiry dates. Dairy can have
  an expiry *after* the contract month, so a new path was added to the
  SymbolRepresentation to ensure that these contracts are loaded
  correctly.

* Address review: Adds tests and fixes bug in SymbolRepresentation

  * Updates SID comment on `Date` property to reflect fact that we use
    future expiry for its value

  * Fixes bug in SymbolRepresentation where expiration day would always
    be 01 when parsing a contract with an expiration after the contract
    month

  * Fixes bug in SybmolRepresentation where expiration year would be
    four digits long when parsing a contract with an expiration after
    the contract month

  * Fixes some bad dairy expiry dates

  * Adds tests for SymbolRepresentation and the futures filtering for
    standard contracts

  * Renames method used to extract delta between contract month and
    expiry date

* Removes GH comment and restores Futures contract month expiry param
2020-11-20 11:58:49 -03:00

185 lines
9.9 KiB
C#

/*
* 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 NUnit.Framework;
using QuantConnect.Securities;
namespace QuantConnect.Tests.Common
{
[TestFixture]
public class SymbolRepresentationTests
{
[Test]
public void OptionSymbolAliasMatchesOSI()
{
const string expected = @"MSFT 060318C00047500";
var result = SymbolRepresentation.GenerateOptionTickerOSI("MSFT", OptionRight.Call, 47.50m, new DateTime(2006, 03, 18));
Assert.AreEqual(expected, result);
}
[Test]
public void OptionSymbolAliasAddsPaddingSpaceForSixOrMoreCharacterSymbols()
{
const string expected = @"ABCDEF 060318C00047500";
var symbol = SymbolRepresentation.GenerateOptionTickerOSI("ABCDEF", OptionRight.Call, 47.50m, new DateTime(2006, 03, 18));
Assert.AreEqual(expected, symbol);
}
[Test]
public void ParseOptionIQFeedTicker()
{
// ticker contains two digits year of expiration
var result = SymbolRepresentation.ParseOptionTickerIQFeed("MSFT1615D30");
Assert.AreEqual(result.Underlying, "MSFT");
Assert.AreEqual(result.OptionRight, OptionRight.Call);
Assert.AreEqual(result.OptionStrike, 30m);
Assert.AreEqual(result.ExpirationDate, new DateTime(2016, 4, 15));
}
[Test]
public void ParseFuturesTickers()
{
// ticker contains two digits year of expiration, no day expiration
var result = SymbolRepresentation.ParseFutureTicker("EX20");
Assert.AreEqual(result.Underlying, "E");
Assert.AreEqual(result.ExpirationDay, 1);
Assert.AreEqual(result.ExpirationYearShort, 20);
Assert.AreEqual(result.ExpirationMonth, 11); // November
// ticker contains one digit year of expiration, no day expiration
result = SymbolRepresentation.ParseFutureTicker("ABCZ1");
Assert.AreEqual(result.Underlying, "ABC");
Assert.AreEqual(result.ExpirationDay, 1);
Assert.AreEqual(result.ExpirationYearShort, 1);
Assert.AreEqual(result.ExpirationMonth, 12); // December
// ticker contains two digits year of expiration, with day expiration
result = SymbolRepresentation.ParseFutureTicker("ED01X20");
Assert.AreEqual(result.Underlying, "ED");
Assert.AreEqual(result.ExpirationDay, 1);
Assert.AreEqual(result.ExpirationYearShort, 20);
Assert.AreEqual(result.ExpirationMonth, 11); // November
// ticker contains one digit year of expiration, with day expiration
result = SymbolRepresentation.ParseFutureTicker("ABC11Z1");
Assert.AreEqual(result.Underlying, "ABC");
Assert.AreEqual(result.ExpirationDay, 11);
Assert.AreEqual(result.ExpirationYearShort, 1);
Assert.AreEqual(result.ExpirationMonth, 12); // December
}
[Test]
public void GenerateFuturesTickers()
{
const string ticker = @"ED";
var result = SymbolRepresentation.GenerateFutureTicker(ticker, new DateTime(2016, 12, 12));
// ticker contains two digits year of expiration
Assert.AreEqual(result, "ED12Z16");
// ticker contains one digit year of expiration
result = SymbolRepresentation.GenerateFutureTicker(ticker, new DateTime(2016, 12, 12), false);
Assert.AreEqual(result, "ED12Z6");
}
[Test]
public void GenerateFuturesTickersBackAndForth()
{
const string expected = @"ED01Z16";
var result = SymbolRepresentation.ParseFutureTicker(expected);
var ticker = SymbolRepresentation.GenerateFutureTicker(result.Underlying, new DateTime(2000 + result.ExpirationYearShort, result.ExpirationMonth, result.ExpirationDay));
Assert.AreEqual(expected, ticker);
}
[Test]
public void ParseInvalidFuturesTickers()
{
var result = SymbolRepresentation.ParseFutureTicker("invalid");
Assert.AreEqual(result, null);
}
[TestCase(Futures.Energies.ArgusLLSvsWTIArgusTradeMonth, 2017, 1, 29, "AE529G7", false)] // Previous month
[TestCase(Futures.Energies.ArgusPropaneSaudiAramco, 2017, 1, 29, "A9N29G7", false)] // Previous month
[TestCase(Futures.Energies.BrentCrude, 2017, 1, 29, "B29H7", false)] // Second prior month
[TestCase(Futures.Energies.BrentLastDayFinancial, 2017, 1, 29, "BZ29H7", false)] // Second prior month
[TestCase(Futures.Energies.CrudeOilWTI, 2017, 11, 20, "CL20Z17", true)] // Prior month
[TestCase(Futures.Energies.Gasoline, 2017, 11, 20, "RB20Z17", true)] // Prior month
[TestCase(Futures.Energies.HeatingOil, 2017, 11, 20, "HO20Z17", true)] // Prior month
[TestCase(Futures.Energies.MarsArgusVsWTITradeMonth, 2017, 11, 20, "AYV20Z17", true)] // Prior month
[TestCase(Futures.Energies.NaturalGas, 2017, 11, 20, "NG20Z17", true)] // Prior month
[TestCase(Futures.Energies.NaturalGasHenryHubLastDayFinancial, 2017, 11, 20, "HH20Z17", true)] // Prior month
[TestCase(Futures.Energies.NaturalGasHenryHubPenultimateFinancial, 2017, 11, 20, "HP20Z17", true)] // Prior month
[TestCase(Futures.Energies.WTIHoustonArgusVsWTITradeMonth, 2017, 11, 20, "HTT20Z17", true)] // Prior month
[TestCase(Futures.Energies.WTIHoustonCrudeOil, 2017, 11, 20, "HCL20Z17", true)] // Prior month
[TestCase(Futures.Softs.Sugar11, 2017, 11, 20, "SB20Z17", true)] // Prior month
[TestCase(Futures.Softs.Sugar11CME, 2017, 11, 20, "YO20Z17", true)] // Prior month
public void GenerateFutureTickerExpiringInPreviousMonth(string underlying, int year, int month, int day, string ticker, bool doubleDigitsYear)
{
// CL Dec17 expires in Nov17
var result = SymbolRepresentation.GenerateFutureTicker(underlying, new DateTime(year, month, day), doubleDigitsYear);
Assert.AreEqual(ticker, result);
}
[TestCase(Futures.Energies.ArgusLLSvsWTIArgusTradeMonth, 2016, 12, 29, "AE529F7", false)] // Previous month
[TestCase(Futures.Energies.ArgusPropaneSaudiAramco, 2016, 12, 29, "A9N29F7", false)] // Previous month
[TestCase(Futures.Energies.BrentCrude, 2016, 11, 29, "B29F7", false)] // Second prior month
[TestCase(Futures.Energies.BrentCrude, 2016, 12, 29, "B29G7", false)] // Second prior month
[TestCase(Futures.Energies.BrentLastDayFinancial, 2016, 11, 29, "BZ29F7", false)] // Second prior month
[TestCase(Futures.Energies.BrentLastDayFinancial, 2016, 12, 29, "BZ29G7", false)] // Second prior month
[TestCase(Futures.Energies.CrudeOilWTI, 2016, 12, 20, "CL20F17", true)] // Prior month
[TestCase(Futures.Energies.Gasoline, 2016, 12, 20, "RB20F17", true)] // Prior month
[TestCase(Futures.Energies.HeatingOil, 2016, 12, 20, "HO20F17", true)] // Prior month
[TestCase(Futures.Energies.MarsArgusVsWTITradeMonth, 2016, 12, 20, "AYV20F17", true)] // Prior month
[TestCase(Futures.Energies.NaturalGas, 2016, 12, 20, "NG20F17", true)] // Prior month
[TestCase(Futures.Energies.NaturalGasHenryHubLastDayFinancial, 2016, 12, 20, "HH20F17", true)] // Prior month
[TestCase(Futures.Energies.NaturalGasHenryHubPenultimateFinancial, 2016, 12, 20, "HP20F17", true)] // Prior month
[TestCase(Futures.Energies.WTIHoustonArgusVsWTITradeMonth, 2016, 12, 20, "HTT20F17", true)] // Prior month
[TestCase(Futures.Energies.WTIHoustonCrudeOil, 2016, 12, 20, "HCL20F17", true)] // Prior month
[TestCase(Futures.Softs.Sugar11, 2016, 12, 20, "SB20F17", true)] // Prior month
[TestCase(Futures.Softs.Sugar11CME, 2016, 12, 20, "YO20F17", true)] // Prior month
public void GenerateFutureTickerExpiringInPreviousMonthOverYearBoundary(string underlying, int year, int month, int day, string ticker, bool doubleDigitsYear)
{
// CL Dec17 expires in Nov17
var result = SymbolRepresentation.GenerateFutureTicker(underlying, new DateTime(year, month, day), doubleDigitsYear);
Assert.AreEqual(ticker, result);
}
[TestCase("ABC", 2017, 12, 20, "ABC20Z17", true)] // Generic contract (i.e. expires current month
public void GenerateFutureTickerExpiringInCurrentMonth(string underlying, int year, int month, int day, string ticker, bool doubleDigitsYear)
{
// CL Dec17 expires in Nov17
var result = SymbolRepresentation.GenerateFutureTicker(underlying, new DateTime(year, month, day), doubleDigitsYear);
Assert.AreEqual(ticker, result);
}
[TestCase("DC", 2023, 1, 4, "DC04Z22", true)] // Contract month is 2022-12, expires on 2023-01-04. Same situation with the rest of the test cases.
[TestCase("DY", 2022, 10, 4, "DY04U22", true)]
[TestCase("GDK", 2022, 11, 1, "GDK01V22", true)]
public void GenerateFutureTickerExpiringInNextMonth(string ticker, int year, int month, int day, string expectedValue, bool doubleDigitsYear)
{
var result = SymbolRepresentation.GenerateFutureTicker(ticker, new DateTime(year, month, day), doubleDigitsYear);
Assert.AreEqual(expectedValue, result);
}
}
}