Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: adjust option expiry reference date #8322

Merged
merged 5 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading