在 GitHub 上查看

TradePad 示例策略

概述

TradePad 示例策略 移植自 MetaTrader 的 "TradePad" 样例。原始 EA 会在图表上绘制一个符号矩阵,并根据随机指标 (Stochastic) 的读数改变按钮颜色。本移植版本保留了多品种监控和趋势判定的核心逻辑,但不再尝试重现 MT5 中的图形界面。 策略会为每个配置的交易品种订阅蜡烛数据,计算随机指标,并把结果划分为 UptrendDowntrendFlat 三种状态。 当状态发生变化时,会输出一条日志消息,对应原版中按钮颜色的切换。

策略不会自动下单,主要定位为人工交易者的监控工具,用于在多个市场之间快速识别动量变化。

工作流程

  1. 品种解析SymbolList 参数接受以逗号分隔的代码列表。如果为空,则使用运行环境中分配给策略的主合约。
  2. 蜡烛订阅:所有品种使用 CandleType 指定的同一时间框架。
  3. 指标计算:为每个品种创建独立的 StochasticOscillator 指标,在蜡烛收盘时得到 %K 值。
  4. 趋势判定:读数高于 UpperLevel 视为 Uptrend,低于 LowerLevel 视为 Downtrend,其余情况视为 Flat。最近的 %K 值保存在 LatestKValues 中。
  5. 刷新节流TimerPeriodSeconds 参数模拟原始 TradePad 的定时器逻辑,每个品种在该间隔内最多记录一次状态变更,避免 高频蜡烛导致日志泛滥。

参数

参数 说明
SymbolList 要监控的交易品种,逗号分隔;留空时使用策略的主合约。
TimerPeriodSeconds 每个品种之间隔多少秒才允许再次记录状态,用于节流。
StochasticLength 随机指标 %K 的基准周期长度。
StochasticKPeriod %K 线的平滑周期。
StochasticDPeriod %D 线的平滑周期(保留以方便优化,策略目前只读取 %K)。
UpperLevel 判定为上涨趋势的阈值。
LowerLevel 判定为下跌趋势的阈值。
CandleType 用于计算指标的蜡烛时间框架。

使用提示

  • 请确保连接器能够提供参数中列出的所有品种。不存在的代码会在日志中提示并被跳过。
  • TrendStates 属性公开了最新的趋势分类,便于在 Designer 中绑定自定义可视化组件。
  • 可以在 Designer 或 Runner 中结合自定义界面,将 AddInfoLog 消息或公开字典映射到面板、小工具等。
  • 策略不发送任何订单,可在真实行情连接上安心用于监控。

原版 MQL 与 StockSharp 版本的差异

MQL5 功能 StockSharp 中的实现
图形按钮面板 以日志与公开字典的形式提供,方便在 Designer 中自建界面。
BUY/SELL 按钮 未实现,策略保持纯监控模式。
拖动图表逻辑 与 StockSharp 不兼容,已省略。
趋势颜色刷新 通过 TimerPeriodSeconds 控制的状态更新代替。

扩展建议

  • 在 Designer 中读取 TrendStates,利用自定义控件恢复彩色矩阵效果。
  • 当某个品种从 Flat 转为 UptrendDowntrend 时触发提醒、推送或声音提示。
  • 若需自动交易,可基于当前分类叠加下单逻辑或风险控制模块。
namespace StockSharp.Samples.Strategies;

using System;

using StockSharp.Algo;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;

/// <summary>
/// TradePad Sample strategy.
/// Classifies market state using Stochastic oscillator and trades on state transitions.
/// Buys when stochastic crosses up from oversold, sells when it crosses down from overbought.
/// </summary>
public class TradePadSampleStrategy : Strategy
{
	private readonly StrategyParam<DataType> _candleType;
	private readonly StrategyParam<int> _stochasticKPeriod;
	private readonly StrategyParam<int> _stochasticDPeriod;
	private readonly StrategyParam<decimal> _upperLevel;
	private readonly StrategyParam<decimal> _lowerLevel;

	private decimal _prevK;
	private bool _hasPrev;

	public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
	public int StochasticKPeriod { get => _stochasticKPeriod.Value; set => _stochasticKPeriod.Value = value; }
	public int StochasticDPeriod { get => _stochasticDPeriod.Value; set => _stochasticDPeriod.Value = value; }
	public decimal UpperLevel { get => _upperLevel.Value; set => _upperLevel.Value = value; }
	public decimal LowerLevel { get => _lowerLevel.Value; set => _lowerLevel.Value = value; }

	public TradePadSampleStrategy()
	{
		_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
			.SetDisplay("Candle Type", "Timeframe", "General");

		_stochasticKPeriod = Param(nameof(StochasticKPeriod), 10)
			.SetDisplay("Stochastic %K", "%K period", "Indicators");

		_stochasticDPeriod = Param(nameof(StochasticDPeriod), 3)
			.SetDisplay("Stochastic %D", "%D period", "Indicators");

		_upperLevel = Param(nameof(UpperLevel), 75m)
			.SetDisplay("Upper Threshold", "Overbought level", "Signals");

		_lowerLevel = Param(nameof(LowerLevel), 25m)
			.SetDisplay("Lower Threshold", "Oversold level", "Signals");
	}

	/// <inheritdoc />
	public override System.Collections.Generic.IEnumerable<(StockSharp.BusinessEntities.Security sec, DataType dt)> GetWorkingSecurities()
		=> [(Security, CandleType)];

	/// <inheritdoc />
	protected override void OnReseted()
	{
		base.OnReseted();
		_prevK = 0;
		_hasPrev = false;
	}

	/// <inheritdoc />
	protected override void OnStarted2(DateTime time)
	{
		base.OnStarted2(time);

		_hasPrev = false;

		var stochastic = new StochasticOscillator();
		stochastic.K.Length = StochasticKPeriod;
		stochastic.D.Length = StochasticDPeriod;

		var subscription = SubscribeCandles(CandleType);

		subscription
			.BindEx(stochastic, ProcessCandle)
			.Start();

		var area = CreateChartArea();
		if (area != null)
		{
			DrawCandles(area, subscription);
			DrawOwnTrades(area);
		}
	}

	private void ProcessCandle(ICandleMessage candle, IIndicatorValue stochVal)
	{
		if (candle.State != CandleStates.Finished)
			return;

		if (!stochVal.IsFinal || !stochVal.IsFormed)
			return;

		var stoch = (StochasticOscillatorValue)stochVal;
		if (stoch.K is not decimal kValue)
			return;

		if (!_hasPrev)
		{
			_prevK = kValue;
			_hasPrev = true;
			return;
		}

		// Buy: stochastic crosses up through lower level (leaving oversold)
		var crossUp = _prevK <= LowerLevel && kValue > LowerLevel;
		// Sell: stochastic crosses down through upper level (leaving overbought)
		var crossDown = _prevK >= UpperLevel && kValue < UpperLevel;

		if (crossUp && Position <= 0)
		{
			BuyMarket();
		}
		else if (crossDown && Position >= 0)
		{
			SellMarket();
		}

		_prevK = kValue;
	}
}