Lean  $LEAN_TAG$
RandomDataGeneratorSettings.cs
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 
17 using System;
18 using System.Collections.Generic;
19 using System.Globalization;
20 using System.Linq;
21 using System.Threading;
22 using QuantConnect.Logging;
23 using QuantConnect.Util;
24 
26 {
28  {
29  private static int MarketCode = 100;
30  private static readonly string[] DateFormats = { DateFormat.EightCharacter, DateFormat.YearMonth, "yyyy-MM-dd" };
31 
32  public DateTime Start { get; init; }
33  public DateTime End { get; init; }
34  public SecurityType SecurityType { get; init; } = SecurityType.Equity;
35  public DataDensity DataDensity { get; init; } = DataDensity.Dense;
36  public Resolution Resolution { get; init; } = Resolution.Minute;
37  public string Market { get; init; }
38  public bool IncludeCoarse { get; init; } = true;
39  public int SymbolCount { get; set; }
40  public double QuoteTradeRatio { get; init; } = 1;
41  public int RandomSeed { get; init; }
42  public bool RandomSeedSet { get; init; }
43  public double HasIpoPercentage { get; init; }
44  public double HasRenamePercentage { get; init; }
45  public double HasSplitsPercentage { get; init; }
46  public double MonthSplitPercentage { get; init; }
47  public double HasDividendsPercentage { get; init; }
48  public double DividendEveryQuarterPercentage { get; init; }
49  public string OptionPriceEngineName { get; init; }
50  public int ChainSymbolCount { get; init; } = 1;
51  public Resolution VolatilityModelResolution{ get; init; } = Resolution.Daily;
52  public List<string> Tickers { get; init; }
53  public static RandomDataGeneratorSettings FromCommandLineArguments(
54  string startDateString,
55  string endDateString,
56  string symbolCountString,
57  string market,
58  string securityTypeString,
59  string resolutionString,
60  string dataDensityString,
61  string includeCoarseString,
62  string quoteTradeRatioString,
63  string randomSeedString,
64  string hasIpoPercentageString,
65  string hasRenamePercentageString,
66  string hasSplitsPercentageString,
67  string hasDividendsPercentageString,
68  string dividendEveryQuarterPercentageString,
69  string optionPriceEngineName,
70  string volatilityModelResolutionString,
71  string chainSymbolCountString,
72  List<string> tickers,
73  double monthSplitPercentage = 5.0
74  )
75  {
76  var randomSeedSet = true;
77 
78  int randomSeed;
79  int symbolCount;
80  int chainSymbolCount;
81  bool includeCoarse;
82  Resolution resolution;
83  double quoteTradeRatio;
84  DataDensity dataDensity;
85  SecurityType securityType;
86  DateTime startDate, endDate;
87  double hasIpoPercentage;
88  double hasRenamePercentage;
89  double hasSplitsPercentage;
90  double hasDividendsPercentage;
91  double dividendEveryQuarterPercentage;
92  Resolution volatilityModelResolution;
93 
94  var failed = false;
95  // --start
96  if (!DateTime.TryParseExact(startDateString, DateFormats, null, DateTimeStyles.None, out startDate))
97  {
98  failed = true;
99  Log.Error($"RandomDataGeneratorSettings(): Required parameter --from-date was incorrectly formatted. Please specify in yyyyMMdd format. Value provided: '{startDateString}'");
100  }
101 
102  // --end
103  if (!DateTime.TryParseExact(endDateString, DateFormats, null, DateTimeStyles.None, out endDate))
104  {
105  failed = true;
106  Log.Error($"RandomDataGeneratorSettings(): Required parameter --to-date was incorrectly formatted. Please specify in yyyyMMdd format. Value provided: '{endDateString}'");
107  }
108 
109  // --tickers
110  if (!tickers.IsNullOrEmpty())
111  {
112  symbolCount = tickers.Count;
113  Log.Trace("RandomDataGeneratorSettings(): Ignoring symbol count will use provided tickers");
114  }
115  // --symbol-count
116  else if (!int.TryParse(symbolCountString, out symbolCount) || symbolCount <= 0)
117  {
118  failed = true;
119  Log.Error($"RandomDataGeneratorSettings(): Required parameter --symbol-count was incorrectly formatted. Please specify a valid integer greater than zero. Value provided: '{symbolCountString}'");
120  }
121 
122  // --chain-symbol-count
123  if (!int.TryParse(chainSymbolCountString, out chainSymbolCount) || chainSymbolCount <= 0)
124  {
125  chainSymbolCount = 10;
126  Log.Trace($"RandomDataGeneratorSettings(): Using default value of '{chainSymbolCount}' for --chain-symbol-count");
127  }
128 
129  // --resolution
130  if (string.IsNullOrEmpty(resolutionString))
131  {
132  resolution = Resolution.Minute;
133  Log.Trace($"RandomDataGeneratorSettings(): Using default value of '{resolution}' for --resolution");
134  }
135  else if (!Enum.TryParse(resolutionString, true, out resolution))
136  {
137  var validValues = string.Join(", ", Enum.GetValues(typeof(Resolution)).Cast<Resolution>());
138  failed = true;
139  Log.Error($"RandomDataGeneratorSettings(): Optional parameter --resolution was incorrectly formatted. Default is Minute. Please specify a valid Resolution. Value provided: '{resolutionString}' Valid values: {validValues}");
140  }
141 
142  // --standard deviation volatility period span
143  if (string.IsNullOrEmpty(volatilityModelResolutionString))
144  {
145  volatilityModelResolution = Resolution.Daily;
146  Log.Trace($"RandomDataGeneratorSettings():Using default value of '{resolution}' for --resolution");
147  }
148  else if (!Enum.TryParse(volatilityModelResolutionString, true, out volatilityModelResolution))
149  {
150  var validValues = string.Join(", ", Enum.GetValues(typeof(Resolution)).Cast<Resolution>());
151  failed = true;
152  Log.Error($"RandomDataGeneratorSettings(): Optional parameter --volatility-model-resolution was incorrectly formatted. Default is Daily. Please specify a valid Resolution. Value provided: '{volatilityModelResolutionString}' Valid values: {validValues}");
153  }
154 
155  // --security-type
156  if (string.IsNullOrEmpty(securityTypeString))
157  {
158  securityType = SecurityType.Equity;
159  Log.Trace($"RandomDataGeneratorSettings(): Using default value of '{securityType}' for --security-type");
160  }
161  else if (!Enum.TryParse(securityTypeString, true, out securityType))
162  {
163  var validValues = string.Join(", ", Enum.GetValues(typeof(SecurityType)).Cast<SecurityType>());
164  failed = true;
165  Log.Error($"RandomDataGeneratorSettings(): Optional parameter --security-type is invalid. Default is Equity. Please specify a valid SecurityType. Value provided: '{securityTypeString}' Valid values: {validValues}");
166  }
167 
168  if (securityType == SecurityType.Option && resolution != Resolution.Minute)
169  {
170  failed = true;
171  Log.Error($"RandomDataGeneratorSettings(): When using --security-type=Option you must specify --resolution=Minute");
172  }
173 
174  // --market
175  if (string.IsNullOrEmpty(market))
176  {
177  market = DefaultBrokerageModel.DefaultMarketMap[securityType];
178  Log.Trace($"RandomDataGeneratorSettings(): Using default value of '{market}' for --market and --security-type={securityType}");
179  }
180  else if (QuantConnect.Market.Encode(market) == null)
181  {
182  // be sure to add a reference to the unknown market, otherwise we won't be able to decode it coming out
183  QuantConnect.Market.Add(market, Interlocked.Increment(ref MarketCode));
184  Log.Trace($"RandomDataGeneratorSettings(): Please verify that the specified market value is correct: '{market}' This value is not known has been added to the market value map. If this is an error, stop the application immediately using Ctrl+C");
185  }
186 
187  // --include-coarse
188  if (string.IsNullOrEmpty(includeCoarseString))
189  {
190  includeCoarse = securityType == SecurityType.Equity;
191  if (securityType != SecurityType.Equity)
192  {
193  Log.Trace($"RandomDataGeneratorSettings(): Using default value of '{includeCoarse}' for --security-type={securityType}");
194  }
195  }
196  else if (!bool.TryParse(includeCoarseString, out includeCoarse))
197  {
198  failed = true;
199  Log.Error($"RandomDataGeneratorSettings(): Optional parameter --include-coarse was incorrectly formatted. Please specify a valid boolean. Value provided: '{includeCoarseString}'. Valid values: 'true' or 'false'");
200  }
201  else if (includeCoarse && securityType != SecurityType.Equity)
202  {
203  Log.Trace("RandomDataGeneratorSettings(): Optional parameter --include-coarse will be ignored because it only applies to --security-type=Equity");
204  }
205 
206  // --data-density
207  if (string.IsNullOrEmpty(dataDensityString))
208  {
209  dataDensity = DataDensity.Dense;
210  Log.Trace($"RandomDataGeneratorSettings(): Using default value of '{dataDensity}' for --data-density");
211  }
212  else if (!Enum.TryParse(dataDensityString, true, out dataDensity))
213  {
214  var validValues = string.Join(", ", Enum.GetValues(typeof(DataDensity))).Cast<DataDensity>();
215  failed = true;
216  Log.Error($"RandomDataGeneratorSettings(): Optional parameter --data-density was incorrectly formatted. Please specify a valid DataDensity. Value provided: '{dataDensityString}'. Valid values: {validValues}");
217  }
218 
219  // --quote-trade-ratio
220  if (string.IsNullOrEmpty(quoteTradeRatioString))
221  {
222  quoteTradeRatio = 1;
223  }
224  else if (!double.TryParse(quoteTradeRatioString, out quoteTradeRatio))
225  {
226  failed = true;
227  Log.Error($"RandomDataGeneratorSettings(): Optional parameter --quote-trade-ratio was incorrectly formatted. Please specify a valid double greater than or equal to zero. Value provided: '{quoteTradeRatioString}'");
228  }
229 
230  // --random-seed
231  if (string.IsNullOrEmpty(randomSeedString))
232  {
233  randomSeed = 0;
234  randomSeedSet = false;
235  }
236  else if (!int.TryParse(randomSeedString, out randomSeed))
237  {
238  failed = true;
239  Log.Error($"RandomDataGeneratorSettings(): Optional parameter --random-seed was incorrectly formatted. Please specify a valid integer");
240  }
241 
242  // --ipo-percentage
243  if (string.IsNullOrEmpty(hasIpoPercentageString))
244  {
245  hasIpoPercentage = 5.0;
246  }
247  else if (!double.TryParse(hasIpoPercentageString, out hasIpoPercentage))
248  {
249  failed = true;
250  Log.Error($"RandomDataGeneratorSettings(): Optional parameter --ipo-percentage was incorrectly formatted. Please specify a valid double greater than or equal to zero. Value provided: '{hasIpoPercentageString}'");
251  }
252 
253  // --rename-percentage
254  if (string.IsNullOrEmpty(hasRenamePercentageString))
255  {
256  hasRenamePercentage = 30.0;
257  }
258  else if (!double.TryParse(hasRenamePercentageString, out hasRenamePercentage))
259  {
260  failed = true;
261  Log.Error($"RandomDataGeneratorSettings(): Optional parameter --rename-percentage was incorrectly formatted. Please specify a valid double greater than or equal to zero. Value provided: '{hasRenamePercentageString}'");
262  }
263 
264  // --splits-percentage
265  if (string.IsNullOrEmpty(hasSplitsPercentageString))
266  {
267  hasSplitsPercentage = 15.0;
268  }
269  else if (!double.TryParse(hasSplitsPercentageString, out hasSplitsPercentage))
270  {
271  failed = true;
272  Log.Error($"RandomDataGeneratorSettings(): Optional parameter --splits-percentage was incorrectly formatted. Please specify a valid double greater than or equal to zero. Value provided: '{hasSplitsPercentageString}'");
273  }
274 
275  // --dividends-percentage
276  if (string.IsNullOrEmpty(hasDividendsPercentageString))
277  {
278  hasDividendsPercentage = 60.0;
279  }
280  else if (!double.TryParse(hasDividendsPercentageString, out hasDividendsPercentage))
281  {
282  failed = true;
283  Log.Error($"RandomDataGeneratorSettings(): Optional parameter --dividends-percentage was incorrectly formatted. Please specify a valid double greater than or equal to zero. Value provided: '{hasDividendsPercentageString}'");
284  }
285 
286  // --dividend-every-quarter-percentage
287  if (string.IsNullOrEmpty(dividendEveryQuarterPercentageString))
288  {
289  dividendEveryQuarterPercentage = 30.0;
290  }
291  else if (!double.TryParse(dividendEveryQuarterPercentageString, out dividendEveryQuarterPercentage))
292  {
293  failed = true;
294  Log.Error($"RandomDataGeneratorSettings(): Optional parameter --dividend-ever-quarter-percentage was incorrectly formatted. Please specify a valid double greater than or equal to zero. Value provided: '{dividendEveryQuarterPercentageString}'");
295  }
296 
297  if (failed)
298  {
299  Log.Error("RandomDataGeneratorSettings(): Please address the errors and run the application again.");
300  Environment.Exit(-1);
301  }
302 
303  return new RandomDataGeneratorSettings
304  {
305  End = endDate,
306  Start = startDate,
307 
308  Market = market,
309  SymbolCount = symbolCount,
310  SecurityType = securityType,
311  QuoteTradeRatio = quoteTradeRatio,
312  ChainSymbolCount = chainSymbolCount,
313 
314  Resolution = resolution,
315 
316  DataDensity = dataDensity,
317  IncludeCoarse = includeCoarse,
318  RandomSeed = randomSeed,
319  RandomSeedSet = randomSeedSet,
320 
321  HasIpoPercentage = hasIpoPercentage,
322  HasRenamePercentage = hasRenamePercentage,
323  HasSplitsPercentage = hasSplitsPercentage,
324  MonthSplitPercentage = monthSplitPercentage,
325  HasDividendsPercentage = hasDividendsPercentage,
326  DividendEveryQuarterPercentage = dividendEveryQuarterPercentage,
327  OptionPriceEngineName = optionPriceEngineName,
328  VolatilityModelResolution = volatilityModelResolution,
329  Tickers = tickers
330  };
331  }
332  }
333 }