蒙特卡罗模拟 - 随机行走策略
该示例策略使用历史对数收益进行蒙特卡罗模拟,生成未来价格路径。它不执行交易,只用于展示如何产生随机行走并估计未来可能的最高和最低价位。
细节
- 入场条件:无,策略不交易。
- 多空方向:无。
- 出场条件:不适用。
- 止损:无。
- 默认值:
NumberOfBarsToPredict= 50。NumberOfSimulations= 500。DataLength= 2000。KeepPastMinMaxLevels= false。
- 过滤条件:无。
- 复杂度:中等。
- 时间框架:可配置。
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Monte Carlo simulation random walk strategy.
/// Uses MC simulation to estimate expected price range and trades based on mean forecast.
/// </summary>
public class MonteCarloSimulationRandomWalkStrategy : Strategy
{
private readonly StrategyParam<int> _forecastBars;
private readonly StrategyParam<int> _simulations;
private readonly StrategyParam<int> _dataLength;
private readonly StrategyParam<decimal> _minForecastEdgePercent;
private readonly StrategyParam<int> _signalCooldownBars;
private readonly StrategyParam<DataType> _candleType;
private readonly List<decimal> _returns = new();
private decimal? _prevClose;
private int _barsFromSignal;
public int ForecastBars { get => _forecastBars.Value; set => _forecastBars.Value = value; }
public int Simulations { get => _simulations.Value; set => _simulations.Value = value; }
public int DataLength { get => _dataLength.Value; set => _dataLength.Value = value; }
public decimal MinForecastEdgePercent { get => _minForecastEdgePercent.Value; set => _minForecastEdgePercent.Value = value; }
public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MonteCarloSimulationRandomWalkStrategy()
{
_forecastBars = Param(nameof(ForecastBars), 10).SetGreaterThanZero();
_simulations = Param(nameof(Simulations), 100).SetGreaterThanZero();
_dataLength = Param(nameof(DataLength), 100).SetGreaterThanZero();
_minForecastEdgePercent = Param(nameof(MinForecastEdgePercent), 0.5m).SetGreaterThanZero();
_signalCooldownBars = Param(nameof(SignalCooldownBars), 12).SetGreaterThanZero();
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_returns.Clear();
_prevClose = null;
_barsFromSignal = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
_returns.Clear();
_prevClose = null;
_barsFromSignal = SignalCooldownBars;
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevClose is decimal prevClose && prevClose > 0)
{
var ret = (decimal)Math.Log((double)(candle.ClosePrice / prevClose));
_returns.Add(ret);
if (_returns.Count > DataLength)
_returns.RemoveAt(0);
}
_prevClose = candle.ClosePrice;
if (_returns.Count < 20)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
_barsFromSignal++;
var history = _returns.ToArray();
var avg = history.Average();
var variance = history.Select(r => (r - avg) * (r - avg)).Average();
var drift = avg - variance / 2m;
var random = new Random(unchecked((int)candle.OpenTime.Ticks));
double sum = 0;
for (var sim = 0; sim < Simulations; sim++)
{
var price = (double)candle.ClosePrice;
for (var step = 0; step < ForecastBars; step++)
{
var idx = random.Next(history.Length);
price *= Math.Exp((double)(history[idx] + drift));
}
sum += price;
}
var meanForecast = (decimal)(sum / Simulations);
var current = candle.ClosePrice;
var edgePercent = (meanForecast - current) / current * 100m;
if (_barsFromSignal >= SignalCooldownBars && edgePercent >= MinForecastEdgePercent && Position <= 0)
{
BuyMarket();
_barsFromSignal = 0;
}
else if (_barsFromSignal >= SignalCooldownBars && edgePercent <= -MinForecastEdgePercent && Position >= 0)
{
SellMarket();
_barsFromSignal = 0;
}
}
}
import clr
import math
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class monte_carlo_simulation_random_walk_strategy(Strategy):
def __init__(self):
super(monte_carlo_simulation_random_walk_strategy, self).__init__()
self._forecast_bars = self.Param("ForecastBars", 10) \
.SetGreaterThanZero()
self._simulations = self.Param("Simulations", 100) \
.SetGreaterThanZero()
self._data_length = self.Param("DataLength", 100) \
.SetGreaterThanZero()
self._min_forecast_edge_percent = self.Param("MinForecastEdgePercent", 0.5) \
.SetGreaterThanZero()
self._signal_cooldown_bars = self.Param("SignalCooldownBars", 12) \
.SetGreaterThanZero()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15)))
self._returns = []
self._prev_close = 0.0
self._bars_from_signal = 0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(monte_carlo_simulation_random_walk_strategy, self).OnReseted()
self._returns = []
self._prev_close = 0.0
self._bars_from_signal = 0
def OnStarted2(self, time):
super(monte_carlo_simulation_random_walk_strategy, self).OnStarted2(time)
self._returns = []
self._prev_close = 0.0
self._bars_from_signal = self._signal_cooldown_bars.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.OnProcess).Start()
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
if self._prev_close > 0.0:
ret = math.log(close / self._prev_close)
self._returns.append(ret)
dl = self._data_length.Value
if len(self._returns) > dl:
self._returns.pop(0)
self._prev_close = close
if len(self._returns) < 20:
return
self._bars_from_signal += 1
history = self._returns[:]
n = len(history)
avg = sum(history) / n
variance = sum((r - avg) ** 2 for r in history) / n
drift = avg - variance / 2.0
seed = int(candle.OpenTime.Ticks) & 0x7FFFFFFF
import random as rnd_mod
rng = rnd_mod.Random(seed)
total = 0.0
sims = self._simulations.Value
fb = self._forecast_bars.Value
for sim in range(sims):
price = close
for step in range(fb):
idx = rng.randint(0, n - 1)
price *= math.exp(history[idx] + drift)
total += price
mean_forecast = total / sims
edge_percent = (mean_forecast - close) / close * 100.0
min_edge = float(self._min_forecast_edge_percent.Value)
cd = self._signal_cooldown_bars.Value
if self._bars_from_signal >= cd and edge_percent >= min_edge and self.Position <= 0:
self.BuyMarket()
self._bars_from_signal = 0
elif self._bars_from_signal >= cd and edge_percent <= -min_edge and self.Position >= 0:
self.SellMarket()
self._bars_from_signal = 0
def CreateClone(self):
return monte_carlo_simulation_random_walk_strategy()