29 private readonly Random _random;
32 private const decimal _maximumPriceAllowed = 1000000m;
40 :
this(
new Random(seed))
52 :
this(
new Random(seed), marketHoursDatabase, symbolPropertiesDatabase)
58 _marketHoursDatabase = marketHoursDatabase;
59 _symbolPropertiesDatabase = symbolPropertiesDatabase;
62 public bool NextBool(
double percentOddsForTrue)
64 return _random.NextDouble() <= percentOddsForTrue / 100;
67 public virtual DateTime
NextDate(DateTime minDateTime, DateTime maxDateTime, DayOfWeek? dayOfWeek)
69 if (maxDateTime < minDateTime)
71 throw new ArgumentException(
72 "The maximum date time must be less than or equal to the minimum date time specified"
77 var rangeInDays = (int)maxDateTime.Subtract(minDateTime).TotalDays;
78 var daysOffsetFromMin = _random.Next(0, rangeInDays);
79 var dateTime = minDateTime.AddDays(daysOffsetFromMin);
81 var currentDayOfWeek = dateTime.DayOfWeek;
82 if (!dayOfWeek.HasValue || currentDayOfWeek == dayOfWeek.Value)
88 var nextDayOfWeek = Enumerable.Range(0, 7)
89 .Select(i => dateTime.AddDays(i))
90 .First(dt => dt.DayOfWeek == dayOfWeek.Value);
92 var previousDayOfWeek = Enumerable.Range(0, 7)
93 .Select(i => dateTime.AddDays(-i))
94 .First(dt => dt.DayOfWeek == dayOfWeek.Value);
97 if (IsWithinRange(nextDayOfWeek, minDateTime, maxDateTime) &&
98 IsWithinRange(previousDayOfWeek, minDateTime, maxDateTime)
101 return _random.Next(0, 1) == 0
106 if (IsWithinRange(nextDayOfWeek, minDateTime, maxDateTime))
108 return nextDayOfWeek;
111 if (IsWithinRange(previousDayOfWeek, minDateTime, maxDateTime))
113 return previousDayOfWeek;
116 throw new ArgumentException(
"The provided min and max dates do not have the requested day of week between them");
119 public double NextDouble() => _random.NextDouble();
121 public int NextInt(
int minValue,
int maxValue) => _random.Next(minValue, maxValue);
123 public int NextInt(
int maxValue) => _random.Next(maxValue);
138 public virtual decimal
NextPrice(
SecurityType securityType,
string market, decimal referencePrice, decimal maximumPercentDeviation)
140 if (referencePrice <= 0)
142 if (securityType ==
SecurityType.Option && referencePrice == 0)
146 throw new ArgumentException(
"The provided reference price must be a positive number.");
149 if (maximumPercentDeviation <= 0)
151 throw new ArgumentException(
"The provided maximum percent deviation must be a positive number");
155 maximumPercentDeviation /= 100m;
157 var symbolProperties = _symbolPropertiesDatabase.GetSymbolProperties(market,
null, securityType,
"USD");
158 var minimumPriceVariation = symbolProperties.MinimumPriceVariation;
162 var increaseProbabilityFactor = 0.5;
168 var deviation = referencePrice * maximumPercentDeviation * (decimal)(
NextDouble() - increaseProbabilityFactor);
169 deviation = Math.Sign(deviation) * Math.Max(Math.Abs(deviation), minimumPriceVariation);
170 price = referencePrice + deviation;
171 price = RoundPrice(price, minimumPriceVariation);
173 if (price < 20 * minimumPriceVariation)
178 increaseProbabilityFactor = Math.Max(increaseProbabilityFactor - 0.05, 0);
181 if (price > (_maximumPriceAllowed / 10m))
185 increaseProbabilityFactor = increaseProbabilityFactor + 0.05;
188 if (price > _maximumPriceAllowed)
194 }
while (!IsPriceValid(securityType, price) && ++attempts < 10);
196 if (!IsPriceValid(securityType, price))
199 price = referencePrice;
205 private static decimal RoundPrice(decimal price, decimal minimumPriceVariation)
207 if (minimumPriceVariation == 0)
return minimumPriceVariation;
208 return Math.Round(price / minimumPriceVariation) * minimumPriceVariation;
211 private bool IsWithinRange(DateTime value, DateTime min, DateTime max)
213 return value >= min && value <= max;
216 private static bool IsPriceValid(
SecurityType securityType, decimal price)
218 switch (securityType)
226 return price > 0 && price < _maximumPriceAllowed;