Skip to content

Commit f4cf7b6

Browse files
authoredFeb 8, 2023
Fix SPXW option strategies (QuantConnect#6939)
- Fix SPXW option strategies. Adding regression algorithm
1 parent d427970 commit f4cf7b6

5 files changed

+188
-2
lines changed
 

‎Algorithm.CSharp/BasicTemplateSPXWeeklyIndexOptionsAlgorithm.cs

-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
using QuantConnect.Interfaces;
2222
using QuantConnect.Data.Market;
2323
using System.Collections.Generic;
24-
using QuantConnect.Data.UniverseSelection;
2524

2625
namespace QuantConnect.Algorithm.CSharp
2726
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
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+
17+
using System.Linq;
18+
using QuantConnect.Data;
19+
using QuantConnect.Orders;
20+
using QuantConnect.Interfaces;
21+
using QuantConnect.Data.Market;
22+
using System.Collections.Generic;
23+
using QuantConnect.Securities.Option;
24+
using System;
25+
26+
namespace QuantConnect.Algorithm.CSharp
27+
{
28+
/// <summary>
29+
/// This example demonstrates how to add and trade SPX index weekly option strategy
30+
/// </summary>
31+
/// <meta name="tag" content="using data" />
32+
/// <meta name="tag" content="options" />
33+
/// <meta name="tag" content="indexes" />
34+
public class BasicTemplateSPXWeeklyIndexOptionsStrategyAlgorithm : QCAlgorithm, IRegressionAlgorithmDefinition
35+
{
36+
private Symbol _spxOption;
37+
38+
/// <summary>
39+
/// Initialize your algorithm and add desired assets.
40+
/// </summary>
41+
public override void Initialize()
42+
{
43+
SetStartDate(2021, 1, 4);
44+
SetEndDate(2021, 1, 10);
45+
SetCash(1000000);
46+
47+
var spx = AddIndex("SPX").Symbol;
48+
49+
// weekly option SPX contracts
50+
var spxw = AddIndexOption(spx, "SPXW");
51+
spxw.SetFilter(u => u.Strikes(0, 1)
52+
// single week ahead since there are many SPXW contracts and we want to preserve performance
53+
.Expiration(0, 7)
54+
.IncludeWeeklys());
55+
56+
_spxOption = spxw.Symbol;
57+
}
58+
59+
public override void OnData(Slice slice)
60+
{
61+
if (Portfolio.Invested)
62+
{
63+
return;
64+
}
65+
66+
OptionChain chain;
67+
if (slice.OptionChains.TryGetValue(_spxOption, out chain))
68+
{
69+
// we find the first expiration group of call options and order them in ascending strike
70+
var contracts = chain
71+
.Where(x => x.Right == OptionRight.Call)
72+
.OrderBy(x => x.Expiry)
73+
.GroupBy(x => x.Expiry)
74+
.First()
75+
.OrderBy(x => x.Strike)
76+
.ToList();
77+
78+
if (contracts.Count > 1)
79+
{
80+
var smallerStrike = contracts[0];
81+
var higherStrike = contracts[1];
82+
83+
// if found, buy until it expires
84+
var optionStrategy = OptionStrategies.BearCallSpread(_spxOption, smallerStrike.Strike, higherStrike.Strike, smallerStrike.Expiry);
85+
Buy(optionStrategy, 1);
86+
}
87+
}
88+
}
89+
90+
public override void OnOrderEvent(OrderEvent orderEvent)
91+
{
92+
Debug(orderEvent.ToString());
93+
if (orderEvent.Symbol.ID.Symbol != "SPXW")
94+
{
95+
throw new Exception("Unexpected order event symbol!");
96+
}
97+
}
98+
99+
/// <summary>
100+
/// This is used by the regression test system to indicate if the open source Lean repository has the required data to run this algorithm.
101+
/// </summary>
102+
public virtual bool CanRunLocally { get; } = true;
103+
104+
/// <summary>
105+
/// This is used by the regression test system to indicate which languages this algorithm is written in.
106+
/// </summary>
107+
public virtual Language[] Languages { get; } = { Language.CSharp };
108+
109+
/// <summary>
110+
/// Data Points count of all timeslices of algorithm
111+
/// </summary>
112+
public virtual long DataPoints => 35611;
113+
114+
/// <summary>
115+
/// Data Points count of the algorithm history
116+
/// </summary>
117+
public virtual int AlgorithmHistoryDataPoints => 0;
118+
119+
/// <summary>
120+
/// This is used by the regression test system to indicate what the expected statistics are from running the algorithm
121+
/// </summary>
122+
public virtual Dictionary<string, string> ExpectedStatistics => new Dictionary<string, string>
123+
{
124+
{"Total Trades", "4"},
125+
{"Average Win", "0.46%"},
126+
{"Average Loss", "0.00%"},
127+
{"Compounding Annual Return", "42.201%"},
128+
{"Drawdown", "0.100%"},
129+
{"Expectancy", "115.281"},
130+
{"Net Profit", "0.452%"},
131+
{"Sharpe Ratio", "7.967"},
132+
{"Probabilistic Sharpe Ratio", "95.977%"},
133+
{"Loss Rate", "50%"},
134+
{"Win Rate", "50%"},
135+
{"Profit-Loss Ratio", "231.56"},
136+
{"Alpha", "0.029"},
137+
{"Beta", "-0.003"},
138+
{"Annual Standard Deviation", "0.001"},
139+
{"Annual Variance", "0"},
140+
{"Information Ratio", "-102.62"},
141+
{"Tracking Error", "0.07"},
142+
{"Treynor Ratio", "-2.462"},
143+
{"Total Fees", "$0.00"},
144+
{"Estimated Strategy Capacity", "$4100000.00"},
145+
{"Lowest Capacity Asset", "SPXW XKX6S2GM9PGU|SPX 31"},
146+
{"Fitness Score", "0.001"},
147+
{"Kelly Criterion Estimate", "0"},
148+
{"Kelly Criterion Probability Value", "0"},
149+
{"Sortino Ratio", "79228162514264337593543950335"},
150+
{"Return Over Maximum Drawdown", "79228162514264337593543950335"},
151+
{"Portfolio Turnover", "0.001"},
152+
{"Total Insights Generated", "0"},
153+
{"Total Insights Closed", "0"},
154+
{"Total Insights Analysis Completed", "0"},
155+
{"Long Insight Count", "0"},
156+
{"Short Insight Count", "0"},
157+
{"Long/Short Ratio", "100%"},
158+
{"Estimated Monthly Alpha Value", "$0"},
159+
{"Total Accumulated Estimated Alpha Value", "$0"},
160+
{"Mean Population Estimated Insight Value", "$0"},
161+
{"Mean Population Direction", "0%"},
162+
{"Mean Population Magnitude", "0%"},
163+
{"Rolling Averaged Population Direction", "0%"},
164+
{"Rolling Averaged Population Magnitude", "0%"},
165+
{"OrderListHash", "4b25b40cc766201845f66b3b613d1444"}
166+
};
167+
}
168+
}

‎Algorithm/QCAlgorithm.Trading.cs

+5-1
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,10 @@ public List<OrderTicket> ComboLimitOrder(List<Leg> legs, int quantity, decimal l
714714

715715
private IEnumerable<OrderTicket> GenerateOptionStrategyOrders(OptionStrategy strategy, int strategyQuantity, bool asynchronous, string tag, IOrderProperties orderProperties)
716716
{
717+
// if the option strategy canonical is set let's use it to make sure we target the right option, for example SPXW for SPX underlying,
718+
// it could be null if the user created the option strategy manually and just set the underlying, in which case we use the default option target by using 'null'
719+
var targetOption = strategy.CanonicalOption != null ? strategy.CanonicalOption.Canonical.ID.Symbol : null;
720+
717721
// setting up the tag text for all orders of one strategy
718722
tag ??= $"{strategy.Name} ({strategyQuantity.ToStringInvariant()})";
719723

@@ -726,7 +730,7 @@ private IEnumerable<OrderTicket> GenerateOptionStrategyOrders(OptionStrategy str
726730
// search for both american/european style -- much better than looping through all securities
727731
foreach (var optionStyle in new[] { OptionStyle.American, OptionStyle.European })
728732
{
729-
var option = QuantConnect.Symbol.CreateOption(strategy.Underlying, strategy.Underlying.ID.Market, optionStyle, optionLeg.Right, optionLeg.Strike, optionLeg.Expiration);
733+
var option = QuantConnect.Symbol.CreateOption(strategy.Underlying, targetOption, strategy.Underlying.ID.Market, optionStyle, optionLeg.Right, optionLeg.Strike, optionLeg.Expiration);
730734
if (Securities.ContainsKey(option))
731735
{
732736
// we found it, we add it a break/stop searching

‎Common/Securities/Option/OptionStrategies.cs

+10
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ DateTime expiration
6666
{
6767
Name = OptionStrategyDefinitions.BearCallSpread.Name,
6868
Underlying = canonicalOption.Underlying,
69+
CanonicalOption = canonicalOption,
6970
OptionLegs = new List<OptionStrategy.OptionLegData>
7071
{
7172
new OptionStrategy.OptionLegData
@@ -117,6 +118,7 @@ DateTime expiration
117118
{
118119
Name = OptionStrategyDefinitions.BearPutSpread.Name,
119120
Underlying = canonicalOption.Underlying,
121+
CanonicalOption = canonicalOption,
120122
OptionLegs = new List<OptionStrategy.OptionLegData>
121123
{
122124
new OptionStrategy.OptionLegData
@@ -169,6 +171,7 @@ DateTime expiration
169171
{
170172
Name = OptionStrategyDefinitions.BullCallSpread.Name,
171173
Underlying = canonicalOption.Underlying,
174+
CanonicalOption = canonicalOption,
172175
OptionLegs = new List<OptionStrategy.OptionLegData>
173176
{
174177
new OptionStrategy.OptionLegData
@@ -220,6 +223,7 @@ DateTime expiration
220223
{
221224
Name = OptionStrategyDefinitions.BullPutSpread.Name,
222225
Underlying = canonicalOption.Underlying,
226+
CanonicalOption = canonicalOption,
223227
OptionLegs = new List<OptionStrategy.OptionLegData>
224228
{
225229
new OptionStrategy.OptionLegData
@@ -260,6 +264,7 @@ public static OptionStrategy Straddle(Symbol canonicalOption, decimal strike, Da
260264
{
261265
Name = OptionStrategyDefinitions.Straddle.Name,
262266
Underlying = canonicalOption.Underlying,
267+
CanonicalOption = canonicalOption,
263268
OptionLegs = new List<OptionStrategy.OptionLegData>
264269
{
265270
new OptionStrategy.OptionLegData
@@ -313,6 +318,7 @@ DateTime expiration
313318
{
314319
Name = OptionStrategyDefinitions.Strangle.Name,
315320
Underlying = canonicalOption.Underlying,
321+
CanonicalOption = canonicalOption,
316322
OptionLegs = new List<OptionStrategy.OptionLegData>
317323
{
318324
new OptionStrategy.OptionLegData
@@ -369,6 +375,7 @@ DateTime expiration
369375
{
370376
Name = OptionStrategyDefinitions.ButterflyCall.Name,
371377
Underlying = canonicalOption.Underlying,
378+
CanonicalOption = canonicalOption,
372379
OptionLegs = new List<OptionStrategy.OptionLegData>
373380
{
374381
new OptionStrategy.OptionLegData
@@ -428,6 +435,7 @@ DateTime expiration
428435
{
429436
Name = OptionStrategyDefinitions.ButterflyPut.Name,
430437
Underlying = canonicalOption.Underlying,
438+
CanonicalOption = canonicalOption,
431439
OptionLegs = new List<OptionStrategy.OptionLegData>
432440
{
433441
new OptionStrategy.OptionLegData
@@ -487,6 +495,7 @@ DateTime expiration2
487495
{
488496
Name = OptionStrategyDefinitions.CallCalendarSpread.Name,
489497
Underlying = canonicalOption.Underlying,
498+
CanonicalOption = canonicalOption,
490499
OptionLegs = new List<OptionStrategy.OptionLegData>
491500
{
492501
new OptionStrategy.OptionLegData
@@ -539,6 +548,7 @@ DateTime expiration2
539548
{
540549
Name = OptionStrategyDefinitions.PutCalendarSpread.Name,
541550
Underlying = canonicalOption.Underlying,
551+
CanonicalOption = canonicalOption,
542552
OptionLegs = new List<OptionStrategy.OptionLegData>
543553
{
544554
new OptionStrategy.OptionLegData

‎Common/Securities/Option/OptionStrategy.cs

+5
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ public class OptionStrategy
2929
/// </summary>
3030
public string Name { get; set; }
3131

32+
/// <summary>
33+
/// The canonical Option symbol of the strategy
34+
/// </summary>
35+
public Symbol CanonicalOption { get; set; }
36+
3237
/// <summary>
3338
/// Underlying symbol of the strategy
3439
/// </summary>

0 commit comments

Comments
 (0)
Please sign in to comment.