Skip to content

Commit a2dc841

Browse files
committed
Return weekly contracts if no standard contracts exist
1 parent 7ea0f60 commit a2dc841

File tree

3 files changed

+203
-13
lines changed

3 files changed

+203
-13
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/*
2+
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3+
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
16+
using QuantConnect.Data;
17+
using QuantConnect.Data.Market;
18+
using QuantConnect.Interfaces;
19+
using QuantConnect.Securities.Option;
20+
using System.Collections.Generic;
21+
22+
namespace QuantConnect.Algorithm.CSharp
23+
{
24+
/// <summary>
25+
/// Verifies that weekly option contracts are included when no standard contracts are available.
26+
/// </summary>
27+
public class OptionChainIncludeWeeklysByDefaultRegressionAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
28+
{
29+
private Option _option;
30+
private Symbol _optionSymbol;
31+
private int _weeklyCount;
32+
private int _totalCount;
33+
34+
/// <summary>
35+
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
36+
/// </summary>
37+
public override void Initialize()
38+
{
39+
SetStartDate(2015, 12, 24);
40+
SetEndDate(2015, 12, 24);
41+
42+
_option = AddOption("GOOG");
43+
_optionSymbol = _option.Symbol;
44+
45+
_option.SetFilter((optionFilter) =>
46+
{
47+
return optionFilter.Strikes(-8, +8).Expiration(0, 0);
48+
});
49+
}
50+
51+
/// <summary>
52+
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
53+
/// </summary>
54+
/// <param name="data">Slice object keyed by symbol containing the stock data</param>
55+
public override void OnData(Slice slice)
56+
{
57+
OptionChain chain;
58+
if (slice.OptionChains.TryGetValue(_optionSymbol, out chain))
59+
{
60+
_totalCount += chain.Contracts.Count;
61+
foreach (var contract in chain.Contracts.Values)
62+
{
63+
if (!OptionSymbol.IsStandard(contract.Symbol))
64+
{
65+
_weeklyCount++;
66+
}
67+
}
68+
}
69+
}
70+
71+
public override void OnEndOfAlgorithm()
72+
{
73+
if (_weeklyCount == 0)
74+
{
75+
throw new RegressionTestException("No weekly contracts found");
76+
}
77+
if (_totalCount != _weeklyCount)
78+
{
79+
throw new RegressionTestException("When no standard option expirations are available, the option chain must fall back to weekly contracts only");
80+
}
81+
}
82+
83+
/// <summary>
84+
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
85+
/// </summary>
86+
public bool CanRunLocally { get; } = true;
87+
88+
/// <summary>
89+
/// This is used by the regression test system to indicate which languages this algorithm is written in.
90+
/// </summary>
91+
public List<Language> Languages { get; } = new() { Language.CSharp, Language.Python };
92+
93+
/// <summary>
94+
/// Data Points count of all timeslices of algorithm
95+
/// </summary>
96+
public long DataPoints => 22702;
97+
98+
/// <summary>
99+
/// Data Points count of the algorithm history
100+
/// </summary>
101+
public int AlgorithmHistoryDataPoints => 0;
102+
103+
/// <summary>
104+
/// Final status of the algorithm
105+
/// </summary>
106+
public AlgorithmStatus AlgorithmStatus => AlgorithmStatus.Completed;
107+
108+
/// <summary>
109+
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
110+
/// </summary>
111+
public Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
112+
{
113+
{"Total Orders", "0"},
114+
{"Average Win", "0%"},
115+
{"Average Loss", "0%"},
116+
{"Compounding Annual Return", "0%"},
117+
{"Drawdown", "0%"},
118+
{"Expectancy", "0"},
119+
{"Start Equity", "100000"},
120+
{"End Equity", "100000"},
121+
{"Net Profit", "0%"},
122+
{"Sharpe Ratio", "0"},
123+
{"Sortino Ratio", "0"},
124+
{"Probabilistic Sharpe Ratio", "0%"},
125+
{"Loss Rate", "0%"},
126+
{"Win Rate", "0%"},
127+
{"Profit-Loss Ratio", "0"},
128+
{"Alpha", "0"},
129+
{"Beta", "0"},
130+
{"Annual Standard Deviation", "0"},
131+
{"Annual Variance", "0"},
132+
{"Information Ratio", "0"},
133+
{"Tracking Error", "0"},
134+
{"Treynor Ratio", "0"},
135+
{"Total Fees", "$0.00"},
136+
{"Estimated Strategy Capacity", "$0"},
137+
{"Lowest Capacity Asset", ""},
138+
{"Portfolio Turnover", "0%"},
139+
{"Drawdown Recovery", "0"},
140+
{"OrderListHash", "d41d8cd98f00b204e9800998ecf8427e"}
141+
};
142+
}
143+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
2+
# Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS,
10+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
# See the License for the specific language governing permissions and
12+
# limitations under the License.
13+
14+
from AlgorithmImports import *
15+
16+
### <summary>
17+
### Verifies that weekly option contracts are included when no standard contracts are available.
18+
### </summary>
19+
class OptionChainIncludeWeeklysByDefaultRegressionAlgorithm(QCAlgorithm):
20+
21+
def initialize(self):
22+
self.set_start_date(2015, 12, 24)
23+
self.set_end_date(2015, 12, 24)
24+
25+
self.option = self.add_option("GOOG")
26+
self.option_symbol = self.option.Symbol
27+
28+
self.option.set_filter(lambda u: u.strikes(-8, 8).expiration(0, 0))
29+
30+
self.weekly_count = 0
31+
self.total_count = 0
32+
33+
def on_data(self, data):
34+
chain = data.option_chains.get(self.option_symbol)
35+
if chain:
36+
self.total_count += len(chain.contracts)
37+
for contract in chain.contracts.values():
38+
if not OptionSymbol.is_standard(contract.symbol):
39+
self.weekly_count += 1
40+
41+
def on_end_of_algorithm(self):
42+
if self.weekly_count == 0:
43+
raise RegressionTestException("No weekly contracts found")
44+
45+
if self.total_count != self.weekly_count:
46+
raise RegressionTestException("When no standard option expirations are available, the option chain must fall back to weekly contracts only")

Common/Securities/ContractSecurityFilterUniverse.cs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ namespace QuantConnect.Securities
2727
/// Used by OptionFilterUniverse and FutureFilterUniverse
2828
/// </summary>
2929
public abstract class ContractSecurityFilterUniverse<T, TData> : IDerivativeSecurityFilterUniverse<TData>
30-
where T: ContractSecurityFilterUniverse<T, TData>
31-
where TData: IChainUniverseData
30+
where T : ContractSecurityFilterUniverse<T, TData>
31+
where TData : IChainUniverseData
3232
{
3333
private bool _alreadyAppliedTypeFilters;
3434

@@ -107,6 +107,7 @@ internal IEnumerable<Symbol> AllSymbols
107107
/// </summary>
108108
protected ContractSecurityFilterUniverse()
109109
{
110+
Type = ContractExpirationType.Standard | ContractExpirationType.Weekly;
110111
}
111112

112113
/// <summary>
@@ -116,7 +117,7 @@ protected ContractSecurityFilterUniverse(IEnumerable<TData> allData, DateTime lo
116117
{
117118
Data = allData;
118119
LocalTime = localTime;
119-
Type = ContractExpirationType.Standard;
120+
Type = ContractExpirationType.Standard | ContractExpirationType.Weekly;
120121
}
121122

122123
/// <summary>
@@ -139,7 +140,7 @@ internal T ApplyTypesFilter()
139140
{
140141
if (_alreadyAppliedTypeFilters)
141142
{
142-
return (T) this;
143+
return (T)this;
143144
}
144145

145146
// memoization map for ApplyTypesFilter()
@@ -174,7 +175,7 @@ internal T ApplyTypesFilter()
174175
}).ToList();
175176

176177
_alreadyAppliedTypeFilters = true;
177-
return (T) this;
178+
return (T)this;
178179
}
179180

180181
/// <summary>
@@ -186,7 +187,7 @@ public virtual void Refresh(IEnumerable<TData> allData, DateTime localTime)
186187
{
187188
Data = allData;
188189
LocalTime = localTime;
189-
Type = ContractExpirationType.Standard;
190+
Type = ContractExpirationType.Standard | ContractExpirationType.Weekly;
190191
_alreadyAppliedTypeFilters = false;
191192
}
192193

@@ -241,11 +242,11 @@ public virtual T FrontMonth()
241242
{
242243
ApplyTypesFilter();
243244
var ordered = Data.OrderBy(x => x.ID.Date).ToList();
244-
if (ordered.Count == 0) return (T) this;
245+
if (ordered.Count == 0) return (T)this;
245246
var frontMonth = ordered.TakeWhile(x => ordered[0].ID.Date == x.ID.Date);
246247

247248
Data = frontMonth.ToList();
248-
return (T) this;
249+
return (T)this;
249250
}
250251

251252
/// <summary>
@@ -256,11 +257,11 @@ public virtual T BackMonths()
256257
{
257258
ApplyTypesFilter();
258259
var ordered = Data.OrderBy(x => x.ID.Date).ToList();
259-
if (ordered.Count == 0) return (T) this;
260+
if (ordered.Count == 0) return (T)this;
260261
var backMonths = ordered.SkipWhile(x => ordered[0].ID.Date == x.ID.Date);
261262

262263
Data = backMonths.ToList();
263-
return (T) this;
264+
return (T)this;
264265
}
265266

266267
/// <summary>
@@ -351,7 +352,7 @@ public T Contracts(PyObject contracts)
351352
public T Contracts(IEnumerable<Symbol> contracts)
352353
{
353354
AllSymbols = contracts.ToList();
354-
return (T) this;
355+
return (T)this;
355356
}
356357

357358
/// <summary>
@@ -376,7 +377,7 @@ public T Contracts(Func<IEnumerable<TData>, IEnumerable<Symbol>> contractSelecto
376377
{
377378
// force materialization using ToList
378379
AllSymbols = contractSelector(Data).ToList();
379-
return (T) this;
380+
return (T)this;
380381
}
381382

382383
/// <summary>
@@ -400,7 +401,7 @@ public T Contracts(Func<IEnumerable<TData>, IEnumerable<TData>> contractSelector
400401
[Obsolete("Deprecated as of 2023-12-13. Filters are always non-dynamic as of now, which means they will only bee applied daily.")]
401402
public T OnlyApplyFilterAtMarketOpen()
402403
{
403-
return (T) this;
404+
return (T)this;
404405
}
405406

406407
/// <summary>

0 commit comments

Comments
 (0)