自動駕駛交易面板策略
此策略將 MQL5 的「Trade panel with autopilot」示例移植到 StockSharp 框架。它在多個時間框中比較當前與前一根 K 線,計算多空百分比。當百分比超過 Open % 時開倉,跌破 Close % 時平倉。可選地,使用 10 分鐘 K 線的最新分形作為止損。
參數
- Autopilot – 是否啟用自動交易。
- Open % – 開倉所需的票數百分比。
- Close % – 平倉的閾值。
- Use Fixed Volume – 是否使用固定手數。
- Fixed Volume – 固定手數。
- Volume % – 當使用百分比時的資金比例。
- Use Stop Loss – 是否啟用分形止損。
邏輯
策略遍歷從 1 分鐘到 1 個月的各個時間框,根據開、高、低、收及其平均值給出買賣票數。買賣百分比控制訂單執行。若啟用止損,10 分鐘分形作為拖尾止損。
本示例僅供教學使用,並非投資建議。
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.
/// Aggregates candle comparison signals over a rolling window.
/// Buys when buy signal percentage exceeds threshold, sells on opposite.
/// </summary>
public class TradePanelWithAutopilotStrategy : 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 TradePanelWithAutopilotStrategy()
{
_openThreshold = Param(nameof(OpenThreshold), 70m)
.SetDisplay("Open %", "Threshold for new position", "General");
_closeThreshold = Param(nameof(CloseThreshold), 45m)
.SetDisplay("Close %", "Threshold for closing", "General");
_windowSize = Param(nameof(WindowSize), 10)
.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;
}
// Compare current vs previous candle across 7 metrics
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;
// Aggregate signals
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;
// Close positions
if (Position > 0 && buyPct < CloseThreshold)
SellMarket();
else if (Position < 0 && sellPct < CloseThreshold)
BuyMarket();
// Open new positions
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_with_autopilot_strategy(Strategy):
def __init__(self):
super(trade_panel_with_autopilot_strategy, self).__init__()
self._open_threshold = self.Param("OpenThreshold", 70.0) \
.SetDisplay("Open %", "Threshold for new position", "General")
self._close_threshold = self.Param("CloseThreshold", 45.0) \
.SetDisplay("Close %", "Threshold for closing", "General")
self._window_size = self.Param("WindowSize", 10) \
.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_with_autopilot_strategy, self).OnReseted()
self._prev_candle = None
self._signal_window = []
def OnStarted2(self, time):
super(trade_panel_with_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_with_autopilot_strategy()