forked from QuantConnect/Lean
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathHistoryAndWarmupRegressionAlgorithm.cs
247 lines (211 loc) · 8.13 KB
/
HistoryAndWarmupRegressionAlgorithm.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
/*
* 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.Collections.Generic;
using QuantConnect.Data;
using QuantConnect.Indicators;
using QuantConnect.Orders;
using QuantConnect.Securities;
namespace QuantConnect.Algorithm.CSharp
{
/// <summary>
/// Regression test for history and warm up using the data available in open source.
/// </summary>
/// <meta name="tag" content="history and warm up" />
/// <meta name="tag" content="history" />
/// <meta name="tag" content="regression test" />
/// <meta name="tag" content="warm up" />
public class HistoryAndWarmupRegressionAlgorithm : QCAlgorithm
{
private const string SPY = "SPY";
private const string GOOG = "GOOG";
private const string IBM = "IBM";
private const string BAC = "BAC";
private const string GOOGL = "GOOGL";
private readonly Dictionary<Symbol, SymbolData> _sd = new Dictionary<Symbol, SymbolData>();
public override void Initialize()
{
SetStartDate(2013, 10, 08);
SetEndDate(2013, 10, 11);
SetCash(1000000);
AddSecurity(SecurityType.Equity, SPY, Resolution.Minute);
AddSecurity(SecurityType.Equity, IBM, Resolution.Minute);
AddSecurity(SecurityType.Equity, BAC, Resolution.Minute);
AddSecurity(SecurityType.Equity, GOOG, Resolution.Daily);
AddSecurity(SecurityType.Equity, GOOGL, Resolution.Daily);
foreach (var security in Securities)
{
_sd.Add(security.Key, new SymbolData(security.Key, this));
}
// we want to warm up our algorithm
SetWarmup(SymbolData.RequiredBarsWarmup);
}
public override void OnData(Slice data)
{
// we are only using warmup for indicator spooling, so wait for us to be warm then continue
if (IsWarmingUp) return;
foreach (var sd in _sd.Values)
{
var lastPriceTime = sd.Close.Current.Time;
// only make decisions when we have data on our requested resolution
if (lastPriceTime.RoundDown(sd.Security.Resolution.ToTimeSpan()) == lastPriceTime)
{
sd.Update();
}
}
}
public override void OnOrderEvent(OrderEvent fill)
{
SymbolData sd;
if (_sd.TryGetValue(fill.Symbol, out sd))
{
sd.OnOrderEvent(fill);
}
}
class SymbolData
{
public const int RequiredBarsWarmup = 40;
public const decimal PercentTolerance = 0.001m;
public const decimal PercentGlobalStopLoss = 0.01m;
private const int LotSize = 10;
public readonly Symbol Symbol;
public readonly Security Security;
public decimal Quantity
{
get { return Security.Holdings.Quantity; }
}
public readonly Identity Close;
public readonly AverageDirectionalIndex ADX;
public readonly ExponentialMovingAverage EMA;
public readonly MovingAverageConvergenceDivergence MACD;
private readonly QCAlgorithm _algorithm;
private OrderTicket _currentStopLoss;
public SymbolData(Symbol symbol, QCAlgorithm algorithm)
{
Symbol = symbol;
Security = algorithm.Securities[symbol];
Close = algorithm.Identity(symbol);
ADX = algorithm.ADX(symbol, 14);
EMA = algorithm.EMA(symbol, 14);
MACD = algorithm.MACD(symbol, 12, 26, 9);
// if we're receiving daily
_algorithm = algorithm;
}
public bool IsReady
{
get { return Close.IsReady && ADX.IsReady & EMA.IsReady && MACD.IsReady; }
}
public bool IsUptrend
{
get
{
const decimal tolerance = 1 + PercentTolerance;
return MACD.Signal > MACD*tolerance
&& EMA > Close*tolerance;
}
}
public bool IsDowntrend
{
get
{
const decimal tolerance = 1 - PercentTolerance;
return MACD.Signal < MACD*tolerance
&& EMA < Close*tolerance;
}
}
public void OnOrderEvent(OrderEvent fill)
{
if (fill.Status != OrderStatus.Filled)
{
return;
}
// if we just finished entering, place a stop loss as well
if (Security.Invested)
{
var stop = Security.Holdings.IsLong
? fill.FillPrice*(1 - PercentGlobalStopLoss)
: fill.FillPrice*(1 + PercentGlobalStopLoss);
_currentStopLoss = _algorithm.StopMarketOrder(Symbol, -Quantity, stop, "StopLoss at: " + stop);
}
// check for an exit, cancel the stop loss
else
{
if (_currentStopLoss != null && _currentStopLoss.Status.IsOpen())
{
// cancel our current stop loss
_currentStopLoss.Cancel("Exited position");
_currentStopLoss = null;
}
}
}
public void Update()
{
OrderTicket ticket;
TryEnter(out ticket);
TryExit(out ticket);
}
public bool TryEnter(out OrderTicket ticket)
{
ticket = null;
if (Security.Invested)
{
// can't enter if we're already in
return false;
}
int qty = 0;
decimal limit = 0m;
if (IsUptrend)
{
// 100 order lots
qty = LotSize;
limit = Security.Low;
}
else if (IsDowntrend)
{
limit = Security.High;
qty = -LotSize;
}
if (qty != 0)
{
ticket = _algorithm.LimitOrder(Symbol, qty, limit, "TryEnter at: " + limit);
}
return qty != 0;
}
public bool TryExit(out OrderTicket ticket)
{
const decimal exitTolerance = 1 + 2 * PercentTolerance;
ticket = null;
if (!Security.Invested)
{
// can't exit if we haven't entered
return false;
}
decimal limit = 0m;
if (Security.Holdings.IsLong && Close*exitTolerance < EMA)
{
limit = Security.High;
}
else if (Security.Holdings.IsShort && Close > EMA*exitTolerance)
{
limit = Security.Low;
}
if (limit != 0)
{
ticket = _algorithm.LimitOrder(Symbol, -Quantity, limit, "TryExit at: " + limit);
}
return -Quantity != 0;
}
}
}
}