25.10.10, Generators
Исправленная версия AdvancedPriceGenerator
public class AdvancedPriceGenerator { private readonly NormalDistributionGenerator _normalGenerator; private readonly List<double> _priceHistory; private readonly int _memorySize; private double _currentPrice; private double _localMean; private double _localVolatility; public AdvancedPriceGenerator(double initialPrice, int memorySize = 50, int? seed = null) { _normalGenerator = new NormalDistributionGenerator(seed: seed); _priceHistory = new List<double> { initialPrice }; _memorySize = memorySize; _currentPrice = initialPrice; _localMean = initialPrice; _localVolatility = initialPrice * 0.02; } public double NextPrice() { // Обновляем локальную статистику ДО генерации нового значения UpdateLocalStatistics(); // Генерируем возврат, зависящий от предыдущих значений double returns = GenerateCorrelatedReturns(); // Применяем к цене (защита от отрицательных цен) _currentPrice = Math.Max(0.01, _currentPrice * Math.Exp(returns)); _priceHistory.Add(_currentPrice); // Поддерживаем размер истории if (_priceHistory.Count > _memorySize) _priceHistory.RemoveAt(0); return _currentPrice; } private void UpdateLocalStatistics() { if (_priceHistory.Count < 5) { // На начальном этапе используем разумные значения по умолчанию _localMean = _priceHistory.Average(); _localVolatility = 0.02; return; } try { // Локальное среднее (скользящее среднее) _localMean = _priceHistory.Average(); // Локальная волатильность (на основе недавних данных) var recentPrices = _priceHistory.TakeLast(Math.Min(20, _priceHistory.Count)).ToArray(); // Вычисляем логарифмические возвраты для волатильности var returns = new List<double>(); for (int i = 1; i < recentPrices.Length; i++) { if (recentPrices[i-1] > 0 && recentPrices[i] > 0) { double ret = Math.Log(recentPrices[i] / recentPrices[i-1]); returns.Add(ret); } } if (returns.Count > 1) { double meanReturn = returns.Average(); double variance = returns.Sum(r => Math.Pow(r - meanReturn, 2)) / (returns.Count - 1); _localVolatility = Math.Sqrt(variance); } else { _localVolatility = 0.02; // значение по умолчанию } // Ограничиваем волатильность разумными пределами _localVolatility = Math.Max(0.001, Math.Min(0.5, _localVolatility)); } catch (Exception ex) { // Запасные значения в случае ошибки Console.WriteLine($"Ошибка в UpdateLocalStatistics: {ex.Message}"); _localMean = _priceHistory.Average(); _localVolatility = 0.02; } } private double GenerateCorrelatedReturns() { // Если история слишком короткая, используем простой случайный возврат if (_priceHistory.Count < 3) { return _normalGenerator.Next() * _localVolatility; } double correlatedNoise = 0; double totalWeight = 0; try { for (int i = 1; i <= Math.Min(5, _priceHistory.Count - 1); i++) { int idx1 = _priceHistory.Count - i - 1; int idx2 = _priceHistory.Count - i; if (idx1 >= 0 && idx2 >= 0 && _priceHistory[idx1] > 0 && _priceHistory[idx2] > 0) { double weight = Math.Exp(-i * 0.5); // Экспоненциально убывающие веса double pastReturn = Math.Log(_priceHistory[idx2] / _priceHistory[idx1]); correlatedNoise += pastReturn * weight; totalWeight += weight; } } } catch (Exception ex) { Console.WriteLine($"Ошибка в вычислении корреляции: {ex.Message}"); correlatedNoise = 0; totalWeight = 1; } if (totalWeight > 0) correlatedNoise /= totalWeight; // Случайная компонента + память double randomComponent = _normalGenerator.Next() * _localVolatility; double memoryComponent = correlatedNoise * 0.3; // Очень медленный дрейф к локальному среднему (если цена положительная) double meanReversion = 0; if (_currentPrice > 0) { meanReversion = (_localMean - _currentPrice) / _currentPrice * 0.005; // уменьшил коэффициент } return memoryComponent + randomComponent + meanReversion; } public (double[] prices, double[] localMean, double[] localVolatility) GenerateSeriesWithStats(int count) { var prices = new double[count]; var means = new double[count]; var volatilities = new double[count]; for (int i = 0; i < count; i++) { prices[i] = NextPrice(); means[i] = _localMean; volatilities[i] = _localVolatility; } return (prices, means, volatilities); } }
Также исправим NormalDistributionGenerator (на всякий случай)
public class NormalDistributionGenerator { private readonly Random _random; private readonly double _mean; private readonly double _stdDev; private bool _hasSpareValue; private double _spareValue; public NormalDistributionGenerator(double mean = 0, double stdDev = 1, int? seed = null) { _mean = mean; _stdDev = stdDev; _random = seed.HasValue ? new Random(seed.Value) : new Random(); _hasSpareValue = false; _spareValue = 0; } public double Next() { if (_hasSpareValue) { _hasSpareValue = false; return _spareValue * _stdDev + _mean; } double u, v, s; do { u = _random.NextDouble() * 2 - 1; v = _random.NextDouble() * 2 - 1; s = u * u + v * v; } while (s >= 1 || s == 0); s = Math.Sqrt(-2.0 * Math.Log(s) / s); _spareValue = v * s; _hasSpareValue = true; double result = u * s * _stdDev + _mean; // Защита от экстремальных значений if (double.IsInfinity(result) || double.IsNaN(result)) return _mean; return result; } public double[] GenerateSample(int size) { var sample = new double[size]; for (int i = 0; i < size; i++) { sample[i] = Next(); } return sample; } }
Улучшенная функция анализа
static void AnalyzeSeries(string name, double[] prices) { try { // Проверяем на валидность данных if (prices == null || prices.Length == 0 || prices.Any(p => double.IsNaN(p) || double.IsInfinity(p))) { Console.WriteLine($"{name}: НЕВАЛИДНЫЕ ДАННЫЕ"); return; } double returnsVolatility = CalculateReturnsVolatility(prices); double autocorrelation = CalculateAutocorrelation(prices, lag: 1); double hurstExponent = EstimateHurstExponent(prices); Console.WriteLine($"{name}:"); Console.WriteLine($" Волатильность: {returnsVolatility:P3}"); Console.WriteLine($" Автокорреляция: {autocorrelation:F4}"); Console.WriteLine($" Экспонента Хёрста: {hurstExponent:F4}"); Console.WriteLine($" Диапазон: {prices.Min():F2} - {prices.Max():F2}"); Console.WriteLine($" Последние цены: {string.Join(", ", prices.TakeLast(3).Select(p => p.ToString("F2")))}"); Console.WriteLine(); } catch (Exception ex) { Console.WriteLine($"{name}: ОШИБКА АНАЛИЗА - {ex.Message}"); } } static double CalculateReturnsVolatility(double[] prices) { try { var returns = new List<double>(); for (int i = 1; i < prices.Length; i++) { if (prices[i-1] > 0 && prices[i] > 0) { double ret = Math.Log(prices[i] / prices[i-1]); if (!double.IsInfinity(ret) && !double.IsNaN(ret)) returns.Add(ret); } } if (returns.Count == 0) return 0; double mean = returns.Average(); double variance = returns.Sum(r => Math.Pow(r - mean, 2)) / (returns.Count - 1); return Math.Sqrt(variance); } catch { return 0; } }
Основные исправления:
Защита от деления на ноль - проверка положительных цен
Обработка исключений - try-catch вокруг опасных операций
Валидация данных - проверка на NaN и Infinity
Корректное вычисление волатильности - через логарифмические возвраты
Запасные значения - на случай ошибок в вычислениях
Теперь продвинутая модель должна работать корректно! Запустите снова и посмотрите на результаты. 🚀
Комментариев нет:
Отправить комментарий