Ver en GitHub

Market Predictor Strategy

Overview

The Market Predictor strategy is a high-level adaptation of the original MetaTrader MarketPredictor expert advisor. The logic focuses on continuously re-estimating the expected price movement by combining a Monte Carlo forecast with adaptive statistical parameters gathered from recent candles. The strategy subscribes to candles of the selected timeframe and processes only finished bars to avoid premature signals.

Core Concepts

  • Adaptive mean estimation: The strategy maintains a dynamic mean price (mu) updated from a simple moving average. This mirrors the parameter optimization step from the original expert advisor.
  • Volatility-driven amplitude: The ATR of the same candle series controls the amplitude coefficient (alpha), keeping the prediction responsive to volatility spikes.
  • Monte Carlo projection: For each completed candle the strategy runs a configurable number of random simulations to estimate the expected price (P_t1). The forecast equals the average of the simulated prices.
  • Directional decision: Market orders are sent when the forecast deviates from the latest close by more than the sigma threshold. The position direction is flipped only after the previous exposure is fully closed.

Trading Rules

  1. Wait for the candle to finish and confirm that all indicators are formed.
  2. Update mu with the SMA value and alpha with the ATR-based amplitude.
  3. Perform Monte Carlo simulations around the latest close price.
  4. If the average simulated price is above Close + sigma, enter a long position with a market order when no position is open.
  5. If the average simulated price is below Close - sigma, enter a short position with a market order when no position is open.
  6. Hold the position until the opposite signal is produced.

Parameters

  • InitialAlpha – Default amplitude used before the ATR becomes available.
  • InitialBeta – Placeholder coefficient kept for compatibility with the original Expert Advisor (not used directly in the calculations).
  • InitialGamma – Placeholder damping constant preserved for documentation consistency (not used directly).
  • Kappa – Sensitivity parameter for the underlying sigmoid component concept. It is stored for reference and future extensions.
  • InitialMu – Default mean price until the moving average is formed.
  • Sigma – Required deviation between the predicted price and the latest close to trigger market entries.
  • MonteCarloSimulations – Number of simulations used to estimate the next price.
  • CandleType – Timeframe of the candle series.

Notes

  • The high-level StockSharp API handles candle subscriptions, indicator binding, and market order execution.
  • Comments in the source code explain each step of the process for easier maintenance.
  • The Python port is intentionally omitted as requested.
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();
		}
	}
}