在 GitHub 上查看
Trading Panel 批量策略
概述
TradingPanelBatchStrategy 是 MetaTrader 4 专家顾问 EA_TradingPanel 的 StockSharp 版本。原脚本提供了一个手动面板,交易者先设置批量下单的数量、每笔手数以及止损/止盈距离,然后点击 BUY 或 SELL 按钮。移植到 StockSharp 后,操作员只需在参数中指定 Direction,策略就会在下一根完整蜡烛关闭时按照设定方向批量发送市价单,并立即把方向重置为 None。
逻辑保持轻量,方便与外部信号或人工干预组合使用。所有订单都会继承可选的止损和止盈,距离以点(pip)表示,完全对应原 MQL 面板的风险控制。
工作流程
- 策略启动时根据
Security.PriceStep 计算 pip 大小。对于 1/3/5 位报价的外汇品种,会额外乘以 10,与 MetaTrader 将 point 转换为 pip 的方式一致。
- 若止损或止盈距离大于零,则调用
StartProtection,并使用市价单管理离场。
- 订阅
CandleType 参数指定的蜡烛序列。每当有蜡烛收盘,就检查当前的 Direction。
- 如果指定了方向且允许交易,策略会按照
NumberOfOrders 的数量、OrderVolume 的手数连续发送市价单。
- 批量下单完成后写入日志,并把
Direction 自动恢复为 None,等待下一次人工触发。
通过这种设计,策略在两次执行之间保持无状态。操作者可以在需要时把 Direction 改成 Buy 或 Sell,下一根完成的蜡烛就会触发新的订单批次,避免在未完成的数据上行动。
参数
| 名称 |
类型 |
默认值 |
说明 |
NumberOfOrders |
int |
1 |
下一批要发送的市价单数量。 |
OrderVolume |
decimal |
0.01 |
每一张市价单的交易量。 |
StopLossPips |
decimal |
2 |
以 pip 表示的止损距离,会根据品种元数据转换为绝对价格。设为 0 表示不使用。 |
TakeProfitPips |
decimal |
10 |
以 pip 表示的止盈距离。设为 0 表示不使用。 |
Direction |
TradeDirection |
None |
下一次执行的方向,发送完毕后会自动重置为 None。 |
CandleType |
DataType |
TimeFrameCandle(1m) |
用于触发执行的蜡烛类型。 |
说明
- 运行前需要为
Security 配置正确的 PriceStep(以及 Decimals),否则 pip 转换将退化为常数 1。
StartProtection 使用市价单离场,以模拟原面板在触发止损/止盈时的处理方式。
- 由于执行发生在完整蜡烛上,操作者可以在蜡烛收盘前更新
Direction,从而把外部分析或手动判断与批量下单同步。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Trading Panel Batch strategy: DEMA crossover.
/// Buys when fast DEMA crosses above slow DEMA, sells on cross below.
/// </summary>
public class TradingPanelBatchStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public TradingPanelBatchStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_fastPeriod = Param(nameof(FastPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("Fast DEMA", "Fast DEMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("Slow DEMA", "Slow DEMA period", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0;
_prevSlow = 0;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevFast = 0;
_prevSlow = 0;
_hasPrev = false;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (_hasPrev)
{
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
BuyMarket();
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
SellMarket();
}
else
{
if (fastValue > slowValue && Position <= 0)
BuyMarket();
else if (fastValue < slowValue && Position >= 0)
SellMarket();
}
_prevFast = fastValue;
_prevSlow = slowValue;
_hasPrev = true;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class trading_panel_batch_strategy(Strategy):
def __init__(self):
super(trading_panel_batch_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._fast_period = self.Param("FastPeriod", 10)
self._slow_period = self.Param("SlowPeriod", 30)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def FastPeriod(self):
return self._fast_period.Value
@FastPeriod.setter
def FastPeriod(self, value):
self._fast_period.Value = value
@property
def SlowPeriod(self):
return self._slow_period.Value
@SlowPeriod.setter
def SlowPeriod(self, value):
self._slow_period.Value = value
def OnReseted(self):
super(trading_panel_batch_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(trading_panel_batch_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
fast = ExponentialMovingAverage()
fast.Length = self.FastPeriod
slow = ExponentialMovingAverage()
slow.Length = self.SlowPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(fast, slow, self._process_candle).Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if self._has_prev:
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
self.BuyMarket()
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
self.SellMarket()
else:
if fast_val > slow_val and self.Position <= 0:
self.BuyMarket()
elif fast_val < slow_val and self.Position >= 0:
self.SellMarket()
self._prev_fast = fast_val
self._prev_slow = slow_val
self._has_prev = True
def CreateClone(self):
return trading_panel_batch_strategy()