пятница, 10 октября 2025 г.

25.10.10, Generators

25.10.10, Generators

Исправленная версия AdvancedPriceGenerator

csharp
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 (на всякий случай)

csharp
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;
    }
}

Улучшенная функция анализа

csharp
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;
    }
}

Основные исправления:

  1. Защита от деления на ноль - проверка положительных цен

  2. Обработка исключений - try-catch вокруг опасных операций

  3. Валидация данных - проверка на NaN и Infinity

  4. Корректное вычисление волатильности - через логарифмические возвраты

  5. Запасные значения - на случай ошибок в вычислениях

Теперь продвинутая модель должна работать корректно! Запустите снова и посмотрите на результаты. 🚀

Комментариев нет:

Отправить комментарий