Trade Panel Autopilot 策略
概述
该策略复现原始 MQL4 “trade panel with autopilot” 专家的核心逻辑。它在多个时间框中统计价格方向,根据市场的主要趋势自动开仓或平仓。
策略在八个时间框(M1、M5、M15、M30、H1、H4、D1、W1)上跟踪最近两根K线,并比较以下价格组成部分:
- 开盘价
- 最高价
- 最低价
- (High + Low) / 2
- 收盘价
- (High + Low + Close) / 3
- (High + Low + Close + Close) / 4
每次比较都会增加买入或卖出计数。所有时间框的计数合并后转换为百分比。当买入或卖出百分比超过设定阈值时,策略开仓;当相反方向的百分比下降到平仓阈值以下时,策略平仓。
参数
Autopilot— 是否启用自动交易。OpenThreshold— 开仓所需的百分比阈值,默认 85。CloseThreshold— 平仓所需的百分比阈值,默认 55。LotFixed— 当UseFixedLot启用时使用的固定下单量。LotPercent— 当UseFixedLot关闭时按账户权益百分比计算的下单量。UseFixedLot— 在固定下单量与按百分比计算之间切换。UseStopLoss— 启用后启动仓位保护。
交易逻辑
- 订阅所有时间框的K线数据。
- 对每根完成的K线计算买入和卖出计数。
- 汇总所有时间框的计数并计算百分比。
- 如果
Autopilot关闭,策略仅监控结果。 - 当没有持仓且买入百分比超过
OpenThreshold时开多单;当卖出百分比超过阈值时开空单。 - 持有多单时若买入百分比下降到
CloseThreshold以下则平仓;持有空单时使用卖出百分比判断是否平仓。
说明
- 策略任意时刻只保持一个仓位。
- 当
UseStopLoss为真时调用StartProtection()以启用止损管理。
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Trade panel autopilot strategy v2.
/// Aggregates 7 price comparison metrics over rolling window.
/// Buys when buy percentage exceeds open threshold, sells on opposite.
/// </summary>
public class TradePanelAutopilotStrategy : Strategy
{
private readonly StrategyParam<decimal> _openThreshold;
private readonly StrategyParam<decimal> _closeThreshold;
private readonly StrategyParam<int> _windowSize;
private readonly StrategyParam<DataType> _candleType;
private readonly Queue<(int buy, int sell)> _signalWindow = new();
private ICandleMessage _prevCandle;
public decimal OpenThreshold { get => _openThreshold.Value; set => _openThreshold.Value = value; }
public decimal CloseThreshold { get => _closeThreshold.Value; set => _closeThreshold.Value = value; }
public int WindowSize { get => _windowSize.Value; set => _windowSize.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public TradePanelAutopilotStrategy()
{
_openThreshold = Param(nameof(OpenThreshold), 65m)
.SetDisplay("Open %", "Threshold for new position", "General");
_closeThreshold = Param(nameof(CloseThreshold), 40m)
.SetDisplay("Close %", "Threshold for closing", "General");
_windowSize = Param(nameof(WindowSize), 8)
.SetGreaterThanZero()
.SetDisplay("Window Size", "Number of candles for signal aggregation", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevCandle = null;
_signalWindow.Clear();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevCandle = null;
_signalWindow.Clear();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevCandle == null)
{
_prevCandle = candle;
return;
}
int buy = 0, sell = 0;
if (candle.OpenPrice > _prevCandle.OpenPrice) buy++; else sell++;
if (candle.HighPrice > _prevCandle.HighPrice) buy++; else sell++;
if (candle.LowPrice > _prevCandle.LowPrice) buy++; else sell++;
if (candle.ClosePrice > _prevCandle.ClosePrice) buy++; else sell++;
var hlCurr = (candle.HighPrice + candle.LowPrice) / 2m;
var hlPrev = (_prevCandle.HighPrice + _prevCandle.LowPrice) / 2m;
if (hlCurr > hlPrev) buy++; else sell++;
var hlcCurr = (candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 3m;
var hlcPrev = (_prevCandle.HighPrice + _prevCandle.LowPrice + _prevCandle.ClosePrice) / 3m;
if (hlcCurr > hlcPrev) buy++; else sell++;
var hlccCurr = (candle.HighPrice + candle.LowPrice + 2m * candle.ClosePrice) / 4m;
var hlccPrev = (_prevCandle.HighPrice + _prevCandle.LowPrice + 2m * _prevCandle.ClosePrice) / 4m;
if (hlccCurr > hlccPrev) buy++; else sell++;
_signalWindow.Enqueue((buy, sell));
while (_signalWindow.Count > WindowSize)
_signalWindow.Dequeue();
_prevCandle = candle;
if (_signalWindow.Count < WindowSize)
return;
int totalBuy = 0, totalSell = 0;
foreach (var (b, s) in _signalWindow)
{
totalBuy += b;
totalSell += s;
}
var total = totalBuy + totalSell;
if (total == 0) return;
var buyPct = (decimal)totalBuy / total * 100m;
var sellPct = (decimal)totalSell / total * 100m;
if (Position > 0 && buyPct < CloseThreshold)
SellMarket();
else if (Position < 0 && sellPct < CloseThreshold)
BuyMarket();
if (Position == 0)
{
if (buyPct >= OpenThreshold)
BuyMarket();
else if (sellPct >= OpenThreshold)
SellMarket();
}
}
}
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.Strategies import Strategy
class trade_panel_autopilot_strategy(Strategy):
def __init__(self):
super(trade_panel_autopilot_strategy, self).__init__()
self._open_threshold = self.Param("OpenThreshold", 65.0) \
.SetDisplay("Open %", "Threshold for new position", "General")
self._close_threshold = self.Param("CloseThreshold", 40.0) \
.SetDisplay("Close %", "Threshold for closing", "General")
self._window_size = self.Param("WindowSize", 8) \
.SetDisplay("Window Size", "Number of candles for signal aggregation", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._signal_window = []
self._prev_candle = None
@property
def open_threshold(self):
return self._open_threshold.Value
@property
def close_threshold(self):
return self._close_threshold.Value
@property
def window_size(self):
return self._window_size.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(trade_panel_autopilot_strategy, self).OnReseted()
self._prev_candle = None
self._signal_window = []
def OnStarted2(self, time):
super(trade_panel_autopilot_strategy, self).OnStarted2(time)
self._prev_candle = None
self._signal_window = []
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if self._prev_candle is None:
self._prev_candle = candle
return
buy = 0
sell = 0
if float(candle.OpenPrice) > float(self._prev_candle.OpenPrice):
buy += 1
else:
sell += 1
if float(candle.HighPrice) > float(self._prev_candle.HighPrice):
buy += 1
else:
sell += 1
if float(candle.LowPrice) > float(self._prev_candle.LowPrice):
buy += 1
else:
sell += 1
if float(candle.ClosePrice) > float(self._prev_candle.ClosePrice):
buy += 1
else:
sell += 1
hl_curr = (float(candle.HighPrice) + float(candle.LowPrice)) / 2.0
hl_prev = (float(self._prev_candle.HighPrice) + float(self._prev_candle.LowPrice)) / 2.0
if hl_curr > hl_prev:
buy += 1
else:
sell += 1
hlc_curr = (float(candle.HighPrice) + float(candle.LowPrice) + float(candle.ClosePrice)) / 3.0
hlc_prev = (float(self._prev_candle.HighPrice) + float(self._prev_candle.LowPrice) + float(self._prev_candle.ClosePrice)) / 3.0
if hlc_curr > hlc_prev:
buy += 1
else:
sell += 1
hlcc_curr = (float(candle.HighPrice) + float(candle.LowPrice) + 2.0 * float(candle.ClosePrice)) / 4.0
hlcc_prev = (float(self._prev_candle.HighPrice) + float(self._prev_candle.LowPrice) + 2.0 * float(self._prev_candle.ClosePrice)) / 4.0
if hlcc_curr > hlcc_prev:
buy += 1
else:
sell += 1
self._signal_window.append((buy, sell))
ws = int(self.window_size)
while len(self._signal_window) > ws:
self._signal_window.pop(0)
self._prev_candle = candle
if len(self._signal_window) < ws:
return
total_buy = 0
total_sell = 0
for b, s in self._signal_window:
total_buy += b
total_sell += s
total = total_buy + total_sell
if total == 0:
return
buy_pct = float(total_buy) / float(total) * 100.0
sell_pct = float(total_sell) / float(total) * 100.0
close_threshold = float(self.close_threshold)
open_threshold = float(self.open_threshold)
if self.Position > 0 and buy_pct < close_threshold:
self.SellMarket()
elif self.Position < 0 and sell_pct < close_threshold:
self.BuyMarket()
if self.Position == 0:
if buy_pct >= open_threshold:
self.BuyMarket()
elif sell_pct >= open_threshold:
self.SellMarket()
def CreateClone(self):
return trade_panel_autopilot_strategy()