Estratégia do Painel de Operações em Piloto Automático
Visão geral
Esta estratégia reproduz a lógica central do expert MQL4 original "trade panel with autopilot". Ela agrega a direção do preço em múltiplos períodos e abre ou fecha uma única posição de acordo com o sentimento dominante do mercado.
A estratégia monitora as duas últimas velas em oito períodos diferentes (M1, M5, M15, M30, H1, H4, D1, W1). Para cada período, compara vários componentes de preço entre as duas velas mais recentes:
Open
High
Low
(High + Low) / 2
Close
(High + Low + Close) / 3
(High + Low + Close + Close) / 4
Cada comparação contribui para uma pontuação de compra ou venda. As pontuações de todos os períodos são somadas e convertidas em percentuais. Quando o percentual de compra ou venda cruza um limiar configurado, a estratégia entra em uma posição. A posição existente é fechada se o percentual oposto cair abaixo do limiar de fechamento.
Parâmetros
Autopilot — ativa ou desativa a negociação automática.
OpenThreshold — nível percentual necessário para abrir uma nova posição. Padrão: 85.
CloseThreshold — nível percentual para fechar uma posição existente. Padrão: 55.
LotFixed — volume fixo da ordem quando UseFixedLot está habilitado.
LotPercent — volume como percentual do valor da carteira quando UseFixedLot está desabilitado.
UseFixedLot — alterna entre volume fixo e percentual.
UseStopLoss — inicia a proteção da posição quando habilitado.
Lógica de negociação
Subscrever candles em todos os períodos configurados.
Calcular as pontuações de compra/venda para cada novo candle concluído.
Somar as pontuações por períodos e calcular os percentuais de compra/venda.
Se Autopilot estiver desabilitado, a estratégia apenas rastreia as pontuações.
Se não houver posição aberta e o percentual de compra exceder OpenThreshold, entrar em uma posição comprada. Se o percentual de venda exceder o limiar, entrar em uma posição vendida.
Se existir uma posição comprada e o percentual de compra cair abaixo de CloseThreshold, sair da posição. A mesma lógica se aplica para posições vendidas usando o percentual de venda.
Notas
A estratégia mantém no máximo uma posição aberta por vez.
O gerenciamento opcional de stop-loss é ativado via StartProtection() quando UseStopLoss é verdadeiro.
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()