25.10.09, Stat, Test, Norm, Model, Volatility, DeepSeek,
Отлично понимаю вашу проблему! Вы описываете нестационарный процесс с локальной автокорреляцией, а обычное нормальное распределение дает стационарный белый шум. Давайте создадим генератор, который имитирует реальные котировки.
1. Модель с дрейфом и локальной волатильностью
csharp
public class RealisticStockPriceGenerator { private readonly NormalDistributionGenerator _normalGenerator; private readonly Random _random; private double _currentPrice; private double _currentDrift; private double _currentVolatility; private readonly double _meanReversionSpeed; private readonly double _volatilityMeanReversion; public RealisticStockPriceGenerator(double initialPrice, int? seed = null) { _normalGenerator = new NormalDistributionGenerator(seed: seed); _random = seed.HasValue ? new Random(seed.Value) : new Random(); _currentPrice = initialPrice; _currentDrift = 0; _currentVolatility = 0.02; // 2% волатильность _meanReversionSpeed = 0.05; // Скорость возврата к среднему _volatilityMeanReversion = 0.1; // Скорость изменения волатильности } public double NextPrice() { // 1. Обновляем дрейф (медленно меняется) _currentDrift = _currentDrift * 0.95 + _normalGenerator.Next() * 0.0005; // 2. Обновляем волатильность (кластеризация волатильности) _currentVolatility = Math.Max(0.001, _currentVolatility * 0.9 + Math.Abs(_normalGenerator.Next()) * 0.01); // 3. Генерируем возврат с учетом текущего дрейфа и волатильности double returns = _currentDrift + _currentVolatility * _normalGenerator.Next(); // 4. Применяем к цене (геометрическое броуновское движение) _currentPrice *= Math.Exp(returns); return _currentPrice; } public double[] GeneratePriceSeries(int count) { var prices = new double[count]; for (int i = 0; i < count; i++) { prices[i] = NextPrice(); } return prices; } }
2. Модель с каналами и поддержкой/сопротивлением
csharp
public class ChannelBasedPriceGenerator { private readonly NormalDistributionGenerator _normalGenerator; private double _currentPrice; private double _channelCenter; private double _channelWidth; private double _trend; private int _timeInChannel; public ChannelBasedPriceGenerator(double initialPrice, int? seed = null) { _normalGenerator = new NormalDistributionGenerator(seed: seed); _currentPrice = initialPrice; _channelCenter = initialPrice; _channelWidth = initialPrice * 0.05; // 5% ширина канала _trend = 0; _timeInChannel = 0; } public double NextPrice() { _timeInChannel++; // Раз в 100-200 шагов меняем канал if (_timeInChannel > 100 + _normalGenerator.Next() * 100) { CreateNewChannel(); } // Локальные колебания внутри канала double localMove = _normalGenerator.Next() * _channelWidth * 0.1; // Притяжение к центру канала (сила поддержки/сопротивления) double distanceToCenter = _channelCenter - _currentPrice; double meanReversionForce = distanceToCenter * 0.1; // Трендовая составляющая double trendMove = _trend; double newPrice = _currentPrice + localMove + meanReversionForce + trendMove; // Отскок от границ канала if (Math.Abs(newPrice - _channelCenter) > _channelWidth) { localMove *= -0.5; // Отскок newPrice = _currentPrice + localMove + meanReversionForce + trendMove; } _currentPrice = newPrice; return _currentPrice; } private void CreateNewChannel() { // Новый центр канала - случайное смещение ±10% double shift = (_normalGenerator.Next() * 0.2 - 0.1) * _channelCenter; _channelCenter += shift; // Новая ширина канала 3-8% _channelWidth = _channelCenter * (0.03 + _normalGenerator.Next() * 0.05); // Новый тренд (очень медленный) _trend = _normalGenerator.Next() * _channelCenter * 0.0001; _timeInChannel = 0; } public (double[] prices, double[] centers, double[] widths) GenerateSeriesWithChannels(int count) { var prices = new double[count]; var centers = new double[count]; var widths = new double[count]; for (int i = 0; i < count; i++) { prices[i] = NextPrice(); centers[i] = _channelCenter; widths[i] = _channelWidth; } return (prices, centers, widths); } }
3. Продвинутая модель с памятью и кластеризацией
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.Exp(returns); _priceHistory.Add(_currentPrice); // Поддерживаем размер истории if (_priceHistory.Count > _memorySize) _priceHistory.RemoveAt(0); return _currentPrice; } private void UpdateLocalStatistics() { if (_priceHistory.Count < 10) return; // Локальное среднее (скользящее среднее) _localMean = _priceHistory.Average(); // Локальная волатильность (на основе недавних данных) var recentPrices = _priceHistory.TakeLast(20).ToArray(); double mean = recentPrices.Average(); double variance = recentPrices.Average(p => Math.Pow(p - mean, 2)); _localVolatility = Math.Sqrt(variance) / mean; // Ограничиваем волатильность _localVolatility = Math.Max(0.005, Math.Min(0.15, _localVolatility)); } private double GenerateCorrelatedReturns() { // Автокорреляция - текущее значение зависит от предыдущих double correlatedNoise = 0; double totalWeight = 0; for (int i = 1; i <= Math.Min(5, _priceHistory.Count - 1); i++) { double weight = Math.Exp(-i * 0.5); // Экспоненциально убывающие веса double pastReturn = Math.Log(_priceHistory[^i] / _priceHistory[^(i + 1)]); correlatedNoise += pastReturn * weight; totalWeight += weight; } correlatedNoise /= totalWeight; // Случайная компонента + память double randomComponent = _normalGenerator.Next() * _localVolatility; double memoryComponent = correlatedNoise * 0.3; // Очень медленный дрейф к локальному среднему double meanReversion = (_localMean - _currentPrice) / _currentPrice * 0.01; 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); } }
4. Генератор с режимами рынка
csharp
public class RegimeSwitchingPriceGenerator { public enum MarketRegime { Trending, // Трендовый режим MeanReverting, // Флэтовый режим Volatile, // Волатильный режим Calm // Спокойный режим } private readonly NormalDistributionGenerator _normalGenerator; private double _currentPrice; private MarketRegime _currentRegime; private int _regimeDuration; private int _timeInRegime; public RegimeSwitchingPriceGenerator(double initialPrice, int? seed = null) { _normalGenerator = new NormalDistributionGenerator(seed: seed); _currentPrice = initialPrice; _currentRegime = MarketRegime.Calm; _regimeDuration = 200 + (int)(_normalGenerator.Next() * 300); _timeInRegime = 0; } public (double price, MarketRegime regime) NextPrice() { _timeInRegime++; // Смена режима if (_timeInRegime > _regimeDuration) { SwitchRegime(); } double returns = GenerateReturnsByRegime(); _currentPrice *= Math.Exp(returns); return (_currentPrice, _currentRegime); } private void SwitchRegime() { var regimes = Enum.GetValues<MarketRegime>(); _currentRegime = regimes[_normalGenerator.Next() > 0 ? 2 : 0]; // Упрощенная логика _regimeDuration = 100 + (int)(Math.Abs(_normalGenerator.Next()) * 400); _timeInRegime = 0; } private double GenerateReturnsByRegime() { return _currentRegime switch { MarketRegime.Trending => _normalGenerator.Next() * 0.01 + 0.001, // Вверх + шум MarketRegime.MeanReverting => _normalGenerator.Next() * 0.005, // Маленькие колебания MarketRegime.Volatile => _normalGenerator.Next() * 0.03, // Большие колебания MarketRegime.Calm => _normalGenerator.Next() * 0.002, // Очень маленькие _ => _normalGenerator.Next() * 0.01 }; } public (double[] prices, MarketRegime[] regimes) GenerateSeriesWithRegimes(int count) { var prices = new double[count]; var regimes = new MarketRegime[count]; for (int i = 0; i < count; i++) { (prices[i], regimes[i]) = NextPrice(); } return (prices, regimes); } }
5. Пример использования и визуализация
csharp
class Program { static void Main() { // Тестируем разные генераторы Console.WriteLine("ГЕНЕРАЦИЯ РЕАЛИСТИЧНЫХ БИРЖЕВЫХ ДАННЫХ"); Console.WriteLine("=====================================\n"); // 1. Базовая модель var basicGenerator = new RealisticStockPriceGenerator(100, seed: 42); var basicPrices = basicGenerator.GeneratePriceSeries(500); // 2. Модель с каналами var channelGenerator = new ChannelBasedPriceGenerator(100, seed: 42); var (channelPrices, centers, widths) = channelGenerator.GenerateSeriesWithChannels(500); // 3. Продвинутая модель var advancedGenerator = new AdvancedPriceGenerator(100, seed: 42); var (advancedPrices, localMeans, localVolatilities) = advancedGenerator.GenerateSeriesWithStats(500); // 4. Модель с режимами var regimeGenerator = new RegimeSwitchingPriceGenerator(100, seed: 42); var (regimePrices, regimes) = regimeGenerator.GenerateSeriesWithRegimes(500); // Анализ результатов AnalyzeSeries("Базовая модель", basicPrices); AnalyzeSeries("Модель с каналами", channelPrices); AnalyzeSeries("Продвинутая модель", advancedPrices); AnalyzeSeries("Модель с режимами", regimePrices); // Визуализация (псевдографика) VisualizePrices(advancedPrices, "Продвинутая модель"); } static void AnalyzeSeries(string name, double[] prices) { 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(); } static double CalculateReturnsVolatility(double[] prices) { var returns = new double[prices.Length - 1]; for (int i = 1; i < prices.Length; i++) { returns[i - 1] = Math.Log(prices[i] / prices[i - 1]); } double mean = returns.Average(); double variance = returns.Average(r => Math.Pow(r - mean, 2)); return Math.Sqrt(variance); } static double CalculateAutocorrelation(double[] prices, int lag) { if (lag >= prices.Length - 1) return 0; var returns = new double[prices.Length - 1]; for (int i = 1; i < prices.Length; i++) { returns[i - 1] = Math.Log(prices[i] / prices[i - 1]); } double mean = returns.Average(); double variance = returns.Average(r => Math.Pow(r - mean, 2)); if (variance == 0) return 0; double covariance = 0; for (int i = 0; i < returns.Length - lag; i++) { covariance += (returns[i] - mean) * (returns[i + lag] - mean); } covariance /= (returns.Length - lag); return covariance / variance; } static double EstimateHurstExponent(double[] prices) { // Упрощенная оценка экспоненты Хёрста var returns = prices.Select((p, i) => i > 0 ? Math.Log(p / prices[i - 1]) : 0).Skip(1).ToArray(); return 0.6 + CalculateAutocorrelation(prices, 1) * 0.4; // Эвристика } static void VisualizePrices(double[] prices, string title) { Console.WriteLine($"{title}:"); Console.WriteLine(new string('-', 60)); double min = prices.Min(); double max = prices.Max(); int height = 20; for (int y = height - 1; y >= 0; y--) { double priceLevel = min + (max - min) * y / height; Console.Write($"{priceLevel,8:F1} | "); for (int x = 0; x < Math.Min(50, prices.Length); x++) { if (Math.Abs(prices[x] - priceLevel) < (max - min) / height * 0.5) Console.Write("●"); else Console.Write(" "); } Console.WriteLine(); } Console.WriteLine(new string('-', 60)); } }
Ключевые особенности:
Локальность - цены находятся в определенных диапазонах
Память - текущее значение зависит от предыдущих
Кластеризация волатильности - периоды спокойствия и активности
Режимы рынка - трендовые и флэтовые периоды
Поддержка/сопротивление - отскоки от границ каналов
Теперь генерация будет похожа на реальные котировки! 📈
Комментариев нет:
Отправить комментарий