Market Predictor 策略
概述
Market Predictor 策略是对 MetaTrader 平台 MarketPredictor 智能交易顾问的 StockSharp 高级 API 版本。策略通过蒙特卡洛预测与基于最新蜡烛的自适应统计参数相结合,持续评估下一步价格走势,只处理已经收盘的蜡烛。
核心思想
- 自适应均值 (
mu): 使用简单移动平均线动态更新期望值,对应原始 EA 中的参数优化步骤。 - 波动性驱动的幅度: ATR 控制幅度系数 (
alpha),让策略能够捕捉波动率的增加。 - 蒙特卡洛预测: 每根蜡烛结束时运行可配置数量的随机模拟,计算未来价格 (
P_t1) 的平均值。 - 方向判定: 当预测价格与最新收盘价的差异超过
sigma阈值时,以市价单开仓;只有在没有持仓时才允许入场。
交易规则
- 等待蜡烛收盘并确认所有指标形成。
- 通过 SMA 更新
mu,通过 ATR 更新alpha。 - 围绕最新收盘价执行蒙特卡洛模拟。
- 如果平均模拟价格高于
Close + sigma,在空仓时买入开多。 - 如果平均模拟价格低于
Close - sigma,在空仓时卖出开空。 - 持仓直至出现反向信号。
参数
- InitialAlpha – ATR 尚未形成时使用的默认幅度。
- InitialBeta – 保留的系数,与原始 EA 保持一致(当前未直接参与计算)。
- InitialGamma – 保留的阻尼系数(当前未直接使用)。
- Kappa – Sigmoid 概念的敏感度参数,作为未来扩展的占位符。
- InitialMu – SMA 尚未形成时的默认平均值。
- Sigma – 触发信号所需的预测价格偏离阈值。
- MonteCarloSimulations – 预测下一价格所使用的模拟次数。
- CandleType – 使用的蜡烛类型(时间框架)。
备注
- 策略依赖 StockSharp 的高级 API 实现蜡烛订阅、指标绑定及市价委托。
- 源码中的注释全部为英文,便于国际化协同。
- 根据需求,未创建 Python 版本及其目录。
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy that adapts the MarketPredictor expert advisor logic to StockSharp.
/// It recalculates statistical parameters from finished candles and
/// runs a Monte Carlo forecast to determine directional bias.
/// </summary>
public class MarketPredictorStrategy : Strategy
{
private readonly StrategyParam<decimal> _initialAlpha;
private readonly StrategyParam<decimal> _initialBeta;
private readonly StrategyParam<decimal> _initialGamma;
private readonly StrategyParam<decimal> _kappa;
private readonly StrategyParam<decimal> _initialMu;
private readonly StrategyParam<decimal> _sigma;
private readonly StrategyParam<int> _monteCarloSimulations;
private readonly StrategyParam<DataType> _candleType;
private decimal _alpha;
private decimal _mu;
/// <summary>
/// Default amplitude used before ATR values become available.
/// </summary>
public decimal InitialAlpha
{
get => _initialAlpha.Value;
set => _initialAlpha.Value = value;
}
/// <summary>
/// Placeholder coefficient retained from the original expert advisor.
/// </summary>
public decimal InitialBeta
{
get => _initialBeta.Value;
set => _initialBeta.Value = value;
}
/// <summary>
/// Placeholder damping constant retained for documentation purposes.
/// </summary>
public decimal InitialGamma
{
get => _initialGamma.Value;
set => _initialGamma.Value = value;
}
/// <summary>
/// Sensitivity parameter associated with the sigmoid concept.
/// </summary>
public decimal Kappa
{
get => _kappa.Value;
set => _kappa.Value = value;
}
/// <summary>
/// Default mean price used until the moving average is formed.
/// </summary>
public decimal InitialMu
{
get => _initialMu.Value;
set => _initialMu.Value = value;
}
/// <summary>
/// Required deviation between forecast and latest close to trigger entries.
/// </summary>
public decimal Sigma
{
get => _sigma.Value;
set => _sigma.Value = value;
}
/// <summary>
/// Number of Monte Carlo simulations used to forecast the next price.
/// </summary>
public int MonteCarloSimulations
{
get => _monteCarloSimulations.Value;
set => _monteCarloSimulations.Value = value;
}
/// <summary>
/// Candle type used for the strategy calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes parameters with defaults matching the original EA configuration.
/// </summary>
public MarketPredictorStrategy()
{
_initialAlpha = Param(nameof(InitialAlpha), 0.1m)
.SetDisplay("Initial Alpha", "Default amplitude before ATR is formed", "Prediction")
.SetOptimize(0.05m, 0.5m, 0.05m);
_initialBeta = Param(nameof(InitialBeta), 0.1m)
.SetDisplay("Initial Beta", "Fractal weight placeholder", "Prediction")
;
_initialGamma = Param(nameof(InitialGamma), 0.1m)
.SetDisplay("Initial Gamma", "Fractal damping placeholder", "Prediction")
;
_kappa = Param(nameof(Kappa), 1.0m)
.SetDisplay("Kappa", "Sigmoid sensitivity placeholder", "Prediction")
;
_initialMu = Param(nameof(InitialMu), 1.0m)
.SetDisplay("Initial Mu", "Fallback mean price", "Prediction")
.SetOptimize(0.5m, 2.0m, 0.25m);
_sigma = Param(nameof(Sigma), 10.0m)
.SetGreaterThanZero()
.SetDisplay("Sigma", "Deviation threshold for trades", "Trading")
.SetOptimize(1.0m, 30.0m, 1.0m);
_monteCarloSimulations = Param(nameof(MonteCarloSimulations), 1000)
.SetGreaterThanZero()
.SetDisplay("Monte Carlo Simulations", "Number of simulations per candle", "Prediction")
.SetOptimize(100, 2000, 100);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for candle subscription", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_alpha = InitialAlpha;
_mu = InitialMu;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_alpha = InitialAlpha;
_mu = InitialMu;
var sma = new SimpleMovingAverage { Length = 14 };
var atr = new AverageTrueRange { Length = 14 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, atr, (candle, smaValue, atrValue) => ProcessCandle(candle, sma, atr, smaValue, atrValue))
.Start();
}
private void ProcessCandle(ICandleMessage candle, SimpleMovingAverage sma, AverageTrueRange atr, decimal smaValue, decimal atrValue)
{
// Process only finished candles to avoid premature trading decisions.
if (candle.State != CandleStates.Finished)
return;
// Confirm that the strategy is allowed to trade and all prerequisites are met.
// Update adaptive mean when the moving average is formed.
if (sma.IsFormed)
{
_mu = smaValue;
}
else
{
_mu = InitialMu;
}
// Adjust amplitude based on volatility when ATR values are reliable.
if (atr.IsFormed && atrValue > 0m)
{
_alpha = atrValue * 0.1m;
}
else
{
_alpha = InitialAlpha;
}
var currentPrice = candle.ClosePrice;
ExecuteTrade(currentPrice);
}
private void ExecuteTrade(decimal currentPrice)
{
var deviation = _alpha > 0 ? Sigma * _alpha : Sigma;
// Mean-reversion: buy when significantly below mean, sell when significantly above
if (currentPrice < _mu - deviation && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (currentPrice > _mu + deviation && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
// Exit when price returns to mean
else if (Position > 0 && currentPrice >= _mu)
{
SellMarket();
}
else if (Position < 0 && currentPrice <= _mu)
{
BuyMarket();
}
}
}
import clr
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.Indicators import SimpleMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class market_predictor_strategy(Strategy):
def __init__(self):
super(market_predictor_strategy, self).__init__()
self._initial_alpha = self.Param("InitialAlpha", 0.1) \
.SetDisplay("Initial Alpha", "Default amplitude before ATR is formed", "Prediction")
self._initial_beta = self.Param("InitialBeta", 0.1) \
.SetDisplay("Initial Beta", "Fractal weight placeholder coefficient", "Prediction")
self._initial_gamma = self.Param("InitialGamma", 0.1) \
.SetDisplay("Initial Gamma", "Fractal damping constant", "Prediction")
self._kappa = self.Param("Kappa", 1.0) \
.SetDisplay("Kappa", "Sigmoid sensitivity parameter", "Prediction")
self._initial_mu = self.Param("InitialMu", 1.0) \
.SetDisplay("Initial Mu", "Fallback mean price", "Prediction")
self._sigma = self.Param("Sigma", 10.0) \
.SetGreaterThanZero() \
.SetDisplay("Sigma", "Deviation threshold for trades", "Trading")
self._monte_carlo_sims = self.Param("MonteCarloSimulations", 1000) \
.SetGreaterThanZero() \
.SetDisplay("Monte Carlo Simulations", "Number of simulations per candle", "Prediction")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for candle subscription", "General")
self._alpha = 0.1
self._mu = 1.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(market_predictor_strategy, self).OnReseted()
self._alpha = float(self._initial_alpha.Value)
self._mu = float(self._initial_mu.Value)
def OnStarted2(self, time):
super(market_predictor_strategy, self).OnStarted2(time)
self._alpha = float(self._initial_alpha.Value)
self._mu = float(self._initial_mu.Value)
self._sma = SimpleMovingAverage()
self._sma.Length = 14
self._atr = AverageTrueRange()
self._atr.Length = 14
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._sma, self._atr, self.process_candle).Start()
def process_candle(self, candle, sma_value, atr_value):
if candle.State != CandleStates.Finished:
return
sma_val = float(sma_value)
atr_val = float(atr_value)
if self._sma.IsFormed:
self._mu = sma_val
else:
self._mu = float(self._initial_mu.Value)
if self._atr.IsFormed and atr_val > 0:
self._alpha = atr_val * 0.1
else:
self._alpha = float(self._initial_alpha.Value)
current_price = float(candle.ClosePrice)
sigma = float(self._sigma.Value)
deviation = sigma * self._alpha if self._alpha > 0 else sigma
if current_price < self._mu - deviation and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif current_price > self._mu + deviation and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
elif self.Position > 0 and current_price >= self._mu:
self.SellMarket()
elif self.Position < 0 and current_price <= self._mu:
self.BuyMarket()
def CreateClone(self):
return market_predictor_strategy()