Skip to content

Commit

Permalink
Fix: adjust option expiry reference date (#8322)
Browse files Browse the repository at this point in the history
* Fix: adjust option expiry reference date

* Add universe files

* Update data and other minor changes

* Minor changes

* Add regression algorithm summary
  • Loading branch information
jhonabreul committed Sep 13, 2024
1 parent fa9ff39 commit bf4b08e
Show file tree
Hide file tree
Showing 15 changed files with 525 additions and 35 deletions.
147 changes: 147 additions & 0 deletions Algorithm.CSharp/ZeroDTEOptionsRegressionAlgorithm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/*
* 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;
using QuantConnect.Interfaces;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Securities;

namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Algorithm asserting that options are selected every day and that selection for 0DTE contracts works as expected,
/// always including the contracts that expire the same date the option chain belongs to.
/// </summary>
public class ZeroDTEOptionsRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
{
private List<DateTime> _selectionDays;
private int _currentSelectionDayIndex;

private int _previouslyAddedContracts;

public override void Initialize()
{
SetStartDate(2024, 01, 01);
SetEndDate(2024, 01, 10);
SetCash(100000);

var equity = AddEquity("SPY");
var option = AddOption(equity.Symbol);

option.SetFilter(u => u.IncludeWeeklys().Expiration(0, 0));

// use the underlying equity as the benchmark
SetBenchmark(equity.Symbol);

_selectionDays = new List<DateTime>()
{
new DateTime(2024, 01, 01), // Sunday midnight, already Monday 1st, it's a holiday. Selection happens for Tuesday here
new DateTime(2024, 01, 03), // Wednesday, midnight
new DateTime(2024, 01, 04),
new DateTime(2024, 01, 05),
new DateTime(2024, 01, 06), // Friday midnight, selection happens for Monday here
new DateTime(2024, 01, 09), // Monday midnight, already Tuesday, selection happens for Tuesday here
new DateTime(2024, 01, 10),
};
}

public override void OnSecuritiesChanged(SecurityChanges changes)
{
// We expect selection every trading day
if (Time.Date != _selectionDays[_currentSelectionDayIndex++])
{
throw new RegressionTestException($"Unexpected date. Expected {_selectionDays[_currentSelectionDayIndex]} but was {Time.Date}");
}

var addedOptions = changes.AddedSecurities.Where(x => x.Symbol.SecurityType == SecurityType.Option && !x.Symbol.IsCanonical()).ToList();

if (addedOptions.Count == 0)
{
throw new RegressionTestException("No options were added");
}

var removedOptions = changes.RemovedSecurities.Where(x => x.Symbol.SecurityType == SecurityType.Option && !x.Symbol.IsCanonical()).ToList();

// Since we are selecting only 0DTE contracts, they must be deselected that same day
if (removedOptions.Count != _previouslyAddedContracts)
{
throw new RegressionTestException($"Unexpected number of removed contracts. Expected {_previouslyAddedContracts} but was {removedOptions.Count}");
}
_previouslyAddedContracts = addedOptions.Count;
}

/// <summary>
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
/// </summary>
public bool CanRunLocally { get; } = true;

/// <summary>
/// This is used by the regression test system to indicate which languages this algorithm is written in.
/// </summary>
public List<Language> Languages { get; } = new() { Language.CSharp };

/// <summary>
/// Data Points count of all timeslices of algorithm
/// </summary>
public long DataPoints => 227;

/// <summary>
/// Data Points count of the algorithm history
/// </summary>
public int AlgorithmHistoryDataPoints => 0;

/// <summary>
/// Final status of the algorithm
/// </summary>
public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;

/// <summary>
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
/// </summary>
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
{
{"Total Orders", "0"},
{"Average Win", "0%"},
{"Average Loss", "0%"},
{"Compounding Annual Return", "0%"},
{"Drawdown", "0%"},
{"Expectancy", "0"},
{"Start Equity", "100000"},
{"End Equity", "100000"},
{"Net Profit", "0%"},
{"Sharpe Ratio", "0"},
{"Sortino Ratio", "0"},
{"Probabilistic Sharpe Ratio", "0%"},
{"Loss Rate", "0%"},
{"Win Rate", "0%"},
{"Profit-Loss Ratio", "0"},
{"Alpha", "0"},
{"Beta", "0"},
{"Annual Standard Deviation", "0"},
{"Annual Variance", "0"},
{"Information Ratio", "0"},
{"Tracking Error", "0"},
{"Treynor Ratio", "0"},
{"Total Fees", "$0.00"},
{"Estimated Strategy Capacity", "$0"},
{"Lowest Capacity Asset", ""},
{"Portfolio Turnover", "0%"},
{"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"}
};
}
}
20 changes: 16 additions & 4 deletions Common/Securities/ContractSecurityFilterUniverse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,16 @@ public T BackMonth()
return BackMonths().FrontMonth();
}

/// <summary>
/// Adjust the reference date used for expiration filtering. By default it just returns the same date.
/// </summary>
/// <param name="referenceDate">The reference date to be adjusted</param>
/// <returns>The adjusted date</returns>
protected virtual DateTime AdjustExpirationReferenceDate(DateTime referenceDate)
{
return referenceDate;
}

/// <summary>
/// Applies filter selecting options contracts based on a range of expiration dates relative to the current day
/// </summary>
Expand All @@ -294,19 +304,21 @@ public virtual T Expiration(TimeSpan minExpiry, TimeSpan maxExpiry)
{
if (LocalTime == default)
{
return (T) this;
return (T)this;
}

if (maxExpiry > Time.MaxTimeSpan) maxExpiry = Time.MaxTimeSpan;

var minExpiryToDate = LocalTime.Date + minExpiry;
var maxExpiryToDate = LocalTime.Date + maxExpiry;
var referenceDate = AdjustExpirationReferenceDate(LocalTime.Date);

var minExpiryToDate = referenceDate + minExpiry;
var maxExpiryToDate = referenceDate + maxExpiry;

Data = Data
.Where(symbol => symbol.ID.Date.Date >= minExpiryToDate && symbol.ID.Date.Date <= maxExpiryToDate)
.ToList();

return (T) this;
return (T)this;
}

/// <summary>
Expand Down
24 changes: 23 additions & 1 deletion Common/Securities/Option/OptionFilterUniverse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ namespace QuantConnect.Securities
/// </summary>
public class OptionFilterUniverse : ContractSecurityFilterUniverse<OptionFilterUniverse, OptionUniverse>
{
private Option.Option _option;

// Fields used in relative strikes filter
private List<decimal> _uniqueStrikes;
private bool _refreshUniqueStrikes;
Expand Down Expand Up @@ -59,16 +61,18 @@ public BaseData Underlying
/// <param name="option">The canonical option chain security</param>
public OptionFilterUniverse(Option.Option option)
{
_option = option;
_underlyingScaleFactor = option.SymbolProperties.StrikeMultiplier;
}

/// <summary>
/// Constructs OptionFilterUniverse
/// </summary>
/// <remarks>Used for testing only</remarks>
public OptionFilterUniverse(IEnumerable<OptionUniverse> allData, BaseData underlying, decimal underlyingScaleFactor = 1)
public OptionFilterUniverse(Option.Option option, IEnumerable<OptionUniverse> allData, BaseData underlying, decimal underlyingScaleFactor = 1)
: base(allData, underlying.EndTime)
{
_option = option;
UnderlyingInternal = underlying;
_refreshUniqueStrikes = true;
_underlyingScaleFactor = underlyingScaleFactor;
Expand Down Expand Up @@ -128,6 +132,24 @@ protected override OptionUniverse CreateDataInstance(Symbol symbol)
};
}

/// <summary>
/// Adjusts the date to the next trading day if the current date is not a trading day, so that expiration filter is properly applied.
/// e.g. Selection for Mondays happen on Friday midnight (Saturday start), so if the minimum time to expiration is, say 0,
/// contracts expiring on Monday would be filtered out if the date is not properly adjusted to the next trading day (Monday).
/// </summary>
/// <param name="referenceDate">The date to be adjusted</param>
/// <returns>The adjusted date</returns>
protected override DateTime AdjustExpirationReferenceDate(DateTime referenceDate)
{
// Check whether the reference time is a tradable date:
if (!_option.Exchange.Hours.IsDateOpen(referenceDate))
{
referenceDate = _option.Exchange.Hours.GetNextTradingDay(referenceDate);
}

return referenceDate;
}

/// <summary>
/// Applies filter selecting options contracts based on a range of strikes in relative terms
/// </summary>
Expand Down
24 changes: 24 additions & 0 deletions Data/option/usa/universes/spy/20231228.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#symbol_id,symbol_value,open,high,low,close,volume,open_interest,implied_volatility,delta,gamma,vega,theta,rho
SPY R735QTJ8XC9X,SPY,476.8100,477.5300,476.2600,476.6900,59498271,,,,,,,
SPY YEPD29TW8ETI|SPY R735QTJ8XC9X,SPY 231229C00270000,206.7850,208.0850,205.9000,206.6100,46,47,0.0000001,1,0,0,0,0
SPY YEW96XE2ROXY|SPY R735QTJ8XC9X,SPY 240105C00270000,207.1400,208.3450,206.1650,206.9000,46,46,1.2716023,1,0,0,0,0
SPY YEPD2AYWP9WM|SPY R735QTJ8XC9X,SPY 231229C00275000,201.7150,203.0850,200.6200,201.5850,0,,0.0000001,1,0,0,0,0
SPY YEW96YJ38K12|SPY R735QTJ8XC9X,SPY 240105C00275000,202.1500,203.3400,201.1150,201.9350,0,,1.2324590,1,0,0,0,0
SPY YEPD29U26QVA|SPY R735QTJ8XC9X,SPY 231229C00280000,196.7300,198.0350,195.8500,196.6150,10,6,0.0000001,1,0,0,0,0
SPY YEW96XE8Q0ZQ|SPY R735QTJ8XC9X,SPY 240105C00280000,197.1600,198.3500,196.1250,196.9400,0,,1.1940285,1,0,0,0,0
SPY YEPD2B0K8MEE|SPY R735QTJ8XC9X,SPY 231229C00285000,191.7200,193.0950,190.6500,191.6350,33,37,0.0000001,1,0,0,0,0
SPY YEW96YKQRWIU|SPY R735QTJ8XC9X,SPY 240105C00285000,192.1700,193.3700,190.9850,191.9350,20,20,1.1562856,1,0,0,0,0
SPY YEPD29U852X2|SPY R735QTJ8XC9X,SPY 231229C00290000,186.7900,188.1000,185.9150,186.6550,0,,0.0000001,1,0,0,0,0
SPY YEW96XEEOD1I|SPY R735QTJ8XC9X,SPY 240105C00290000,187.1750,188.3800,186.0250,186.9400,0,,1.1192060,1,0,0,0,0
SPY YEPD2B27RYW6|SPY R735QTJ8XC9X,SPY 231229C00295000,181.7900,183.1050,180.9200,181.6750,4,4,0.0000001,1,0,0,0,0
SPY YEW96YMEB90M|SPY R735QTJ8XC9X,SPY 240105C00295000,182.1850,183.3800,181.1150,181.9650,0,,1.0827669,1,0,0,0,0
SPY YEPD29PXEISM|SPY R735QTJ8XC9X,SPY 231229C00300000,176.7200,178.1050,175.8600,176.6550,0,4,0.0000001,1,0,0,0,0
SPY YEW96XA3XSX2|SPY R735QTJ8XC9X,SPY 240105C00300000,177.1950,178.3900,176.2150,176.9750,0,,1.0469466,1,0,0,0,0
SPY YEPD2B3VBBDY|SPY R735QTJ8XC9X,SPY 231229C00305000,171.7250,173.1150,170.9200,171.6600,0,,0.0000001,1,0,0,0,0
SPY YEW96YO1ULIE|SPY R735QTJ8XC9X,SPY 240105C00305000,172.2050,173.4000,171.0100,171.9450,0,,1.0117244,1,0,0,0,0
SPY YFA1G9SEX5RA|SPY R735QTJ8XC9X,SPY 240119C00305000,172.9300,174.2500,171.6450,172.6200,0,6,0.5837597,1,0,0,0,0
SPY YEPD29UK1R0M|SPY R735QTJ8XC9X,SPY 231229C00310000,166.7600,168.1150,165.6950,166.6300,0,,0.0000001,1,0,0,0,0
SPY YEW96XEQL152|SPY R735QTJ8XC9X,SPY 240105C00310000,167.2150,168.4100,166.1500,166.9950,0,,0.9770808,1,0,0,0,0
SPY YEPD2B5IUNVQ|SPY R735QTJ8XC9X,SPY 231229C00315000,161.8000,163.1200,160.9200,161.6450,0,,0.0000001,1,0,0,0,0
SPY YEW96YPPDY06|SPY R735QTJ8XC9X,SPY 240105C00315000,162.2200,163.4100,161.1500,162.0200,0,,0.9429972,1,0,0,0,0
SPY YEPD29UQ032E|SPY R735QTJ8XC9X,SPY 231229C00320000,156.7250,158.1250,155.9200,156.6400,0,,0.0000001,1,0,0,0,0
16 changes: 16 additions & 0 deletions Data/option/usa/universes/spy/20231229.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#symbol_id,symbol_value,open,high,low,close,volume,open_interest,implied_volatility,delta,gamma,vega,theta,rho
SPY R735QTJ8XC9X,SPY,476.4700,477.0200,473.3150,475.3100,96706712,,,,,,,
SPY YETAUP0L4STI|SPY R735QTJ8XC9X,SPY 240102C00402000,74.5000,75.7850,71.5100,73.5200,1,,0.6225652,0.9990582,0.0001866,0.0015352,-0.0097524,-0.0007456
SPY YEUAASDR7JZA|SPY R735QTJ8XC9X,SPY 240103C00402000,74.6900,75.8650,71.6300,73.6350,8,,0.5390879,0.9992449,0.0001939,0.0014585,-0.0055175,-0.0006899
SPY YETAUP0R34VA|SPY R735QTJ8XC9X,SPY 240102C00403000,73.5000,74.7850,70.5400,72.5400,0,,0.6140000,0.9990404,0.0001911,0.0015516,-0.0107077,-0.0007402
SPY YEUAASDX5W12|SPY R735QTJ8XC9X,SPY 240103C00403000,73.5900,74.8650,70.6350,72.6250,0,,0.5500093,0.9986411,0.0002501,0.0021631,-0.0108552,-0.0010937
SPY YETAUP0X1GX2|SPY R735QTJ8XC9X,SPY 240102C00404000,72.5000,73.7800,69.5200,71.5350,0,1,0.6052588,0.9990271,0.0001953,0.0015642,-0.0113485,-0.0007372
SPY YEUAASE3482U|SPY R735QTJ8XC9X,SPY 240103C00404000,72.6850,73.8650,69.6550,71.6300,8,,0.5241112,0.9992114,0.0002023,0.0015033,-0.0064616,-0.0006820
SPY YETAUP12ZSYU|SPY R735QTJ8XC9X,SPY 240102C00405000,71.5000,72.7850,68.5550,70.5400,8,,0.5961598,0.9990230,0.0001989,0.0015696,-0.0113716,-0.0007268
SPY YEUAASE92K4M|SPY R735QTJ8XC9X,SPY 240103C00405000,71.6000,72.8700,68.5850,70.6450,10,,0.5166274,0.9991950,0.0002078,0.0015250,-0.0069180,-0.0006887
SPY YETAUP18Y50M|SPY R735QTJ8XC9X,SPY 240102C00406000,70.5000,71.7800,67.5500,69.5450,5,,0.5877552,0.9990020,0.0002047,0.0015896,-0.0125101,-0.0007239
SPY YEUAASEF0W6E|SPY R735QTJ8XC9X,SPY 240103C00406000,70.6900,71.8700,67.6250,69.6500,6,,0.5088669,0.9991881,0.0002122,0.0015362,-0.0070462,-0.0006788
SPY YETAUP1EWH2E|SPY R735QTJ8XC9X,SPY 240102C00407000,69.5000,70.7850,66.5500,68.5500,14,,0.5793580,0.9989810,0.0002107,0.0016087,-0.0129474,-0.0007251
SPY YEUAASEKZ886|SPY R735QTJ8XC9X,SPY 240103C00407000,69.6900,70.8700,66.6050,68.6450,12,1,0.5016151,0.9991663,0.0002189,0.0015658,-0.0073815,-0.0006749
SPY YETAUP1KUT46|SPY R735QTJ8XC9X,SPY 240102C00408000,68.5000,69.7850,65.5250,67.5550,8,,0.5708631,0.9989624,0.0002165,0.0016252,-0.0128765,-0.0007338
SPY YEUAASEQXK9Y|SPY R735QTJ8XC9X,SPY 240103C00408000,68.6900,69.8700,65.6000,67.6350,0,,0.4943122,0.9991460,0.0002256,0.0015929,-0.0073707,-0.0007211
20 changes: 20 additions & 0 deletions Data/option/usa/universes/spy/20240102.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#symbol_id,symbol_value,open,high,low,close,volume,open_interest,implied_volatility,delta,gamma,vega,theta,rho
SPY R735QTJ8XC9X,SPY,472.2100,473.6700,470.4900,472.6500,96336531,,,,,,,
SPY YEUAASDR7JZA|SPY R735QTJ8XC9X,SPY 240103C00402000,70.2350,71.9700,68.2050,70.6750,1,6,0.0000001,1,0,0,0,0
SPY YEV9QVQXAB52|SPY R735QTJ8XC9X,SPY 240104C00402000,70.4300,72.2050,68.4050,70.9600,0,1,1.0444474,0.9987106,0.0001826,0.0011904,-0.0503821,-0.0004696
SPY YEUAASDX5W12|SPY R735QTJ8XC9X,SPY 240103C00403000,69.2350,70.9750,67.2100,69.6850,0,,0.0000001,1,0,0,0,0
SPY YEV9QVR38N6U|SPY R735QTJ8XC9X,SPY 240104C00403000,69.4300,71.2100,67.4050,69.9200,0,,1.0296539,0.9986927,0.0001877,0.0011951,-0.0546676,-0.0004655
SPY YEUAASE3482U|SPY R735QTJ8XC9X,SPY 240103C00404000,68.2350,69.9250,66.2100,68.6950,0,5,0.0000001,1,0,0,0,0
SPY YEV9QVR96Z8M|SPY R735QTJ8XC9X,SPY 240104C00404000,68.4300,70.1750,66.4100,68.9200,0,,1.0150155,0.9986728,0.0001932,0.0011999,-0.0575827,-0.0004614
SPY YEUAASE92K4M|SPY R735QTJ8XC9X,SPY 240103C00405000,67.2350,68.9700,65.2200,67.6950,0,6,0.0000001,1,0,0,0,0
SPY YEV9QVRF5BAE|SPY R735QTJ8XC9X,SPY 240104C00405000,67.4400,69.2100,65.4100,67.8950,0,,1.0001185,0.9986563,0.0001984,0.0012042,-0.0569533,-0.0004559
SPY YEUAASEF0W6E|SPY R735QTJ8XC9X,SPY 240103C00406000,66.2350,67.9800,64.2200,66.6900,0,4,0.0000001,1,0,0,0,0
SPY YEV9QVRL3NC6|SPY R735QTJ8XC9X,SPY 240104C00406000,66.4400,68.2150,64.4100,66.9300,0,,0.9852584,0.9986394,0.0002038,0.0012083,-0.0563180,-0.0004550
SPY YEUAASEKZ886|SPY R735QTJ8XC9X,SPY 240103C00407000,65.2350,66.9750,63.2150,65.6800,0,8,0.0000001,1,0,0,0,0
SPY YEV9QVRR1ZDY|SPY R735QTJ8XC9X,SPY 240104C00407000,65.4400,67.2100,63.5150,65.9250,0,,0.9704350,0.9986219,0.0002095,0.0012122,-0.0556880,-0.0004510
SPY YEUAASEQXK9Y|SPY R735QTJ8XC9X,SPY 240103C00408000,64.2400,65.8150,62.2150,64.6900,0,,0.0000001,1,0,0,0,0
SPY YEV9QVRX0BFQ|SPY R735QTJ8XC9X,SPY 240104C00408000,64.4400,66.2100,62.4100,64.9500,0,1,0.9556481,0.9986041,0.0002154,0.0012163,-0.0550450,-0.0004467
SPY YEUAASEWVWBQ|SPY R735QTJ8XC9X,SPY 240103C00409000,63.2400,64.9700,61.2150,63.6950,0,,0.0000001,1,0,0,0,0
SPY YEV9QVS2YNHI|SPY R735QTJ8XC9X,SPY 240104C00409000,63.4400,65.2150,61.4100,63.9600,0,,0.9408974,0.9985857,0.0002215,0.0012205,-0.0543964,-0.0004403
SPY YEUAAQQ1YVBA|SPY R735QTJ8XC9X,SPY 240103C00410000,62.2400,63.9750,60.2150,62.6900,1,2,0.0000001,1,0,0,0,0
SPY YEV9QU381MH2|SPY R735QTJ8XC9X,SPY 240104C00410000,62.4400,64.2100,60.4150,62.9650,0,,0.9261830,0.9985668,0.0002279,0.0012248,-0.0537200,-0.0004386
Loading

0 comments on commit bf4b08e

Please sign in to comment.