A Estratégia Expotest é uma conversão direta para StockSharp do assessor especialista original Expotest.mq5. Ela opera um único instrumento usando o indicador Parabolic SAR e uma regra simples de gerenciamento monetário inspirada no martingale. A estratégia abre apenas uma posição de cada vez e depende de níveis predefinidos de stop-loss e take-profit para as saídas.
Lógica de trading
Indicador: Parabolic SAR calculado na série de velas selecionada. Tanto o fator de aceleração (SarStep) quanto a aceleração máxima (SarMaximum) são configuráveis.
Condições de entrada: Quando nenhuma posição está aberta, a estratégia verifica a última vela fechada.
Se o valor do Parabolic SAR estiver abaixo ou igual ao preço de fechamento, uma posição comprada é iniciada.
Se o valor do Parabolic SAR estiver acima ou igual ao preço de fechamento, uma posição vendida é iniciada.
Condições de saída: Os níveis de stop-loss e take-profit são colocados a uma distância fixa do preço de entrada, medida em passos de preço. Durante cada nova vela, a estratégia monitora se o intervalo da vela toca qualquer nível e fecha a posição de acordo. O tipo de saída (lucro ou perda) é lembrado para futuras decisões de dimensionamento de posição.
Dimensionamento de posição
Volume base: Definido pelo parâmetro FixedVolume quando for maior que zero. Caso contrário, a estratégia estima o tamanho a partir dos valores RiskPercent e StopLossPoints usando o capital atual do portfólio. Se nenhum método retornar um tamanho válido, o Strategy.Volume padrão é usado.
Passo martingale: Após uma operação perdedora, o próximo tamanho de posição é dobrado em comparação com o volume da posição perdedora. Uma saída lucrativa redefine o multiplicador e a próxima ordem usa o volume base novamente.
Parâmetros configuráveis
CandleType – Tipo de dados para agregação de velas (período ou outro formato de vela).
SarStep – Fator de aceleração inicial para o Parabolic SAR.
SarMaximum – Fator de aceleração máximo para o Parabolic SAR.
StopLossPoints – Distância do stop-loss desde a entrada expressa em passos de preço.
TakeProfitPoints – Distância do take-profit desde a entrada expressa em passos de preço.
RiskPercent – Porcentagem do capital do portfólio a arriscar por operação quando o dimensionamento dinâmico está habilitado.
FixedVolume – Volume de ordem explícito. Definir como 0 para habilitar o dimensionamento baseado em risco.
Notas adicionais
A estratégia processa apenas velas terminadas para se manter próxima da implementação MQL original baseada em ticks enquanto permanece compatível com as subscrições do StockSharp.
Os níveis de proteção são rastreados internamente em vez de ordens stop/limit separadas, o que mantém a lógica transparente e fácil de backtestear.
A implementação Python é intencionalmente omitida conforme solicitado.
namespace StockSharp.Samples.Strategies;
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// Parabolic SAR based strategy converted from the Expotest MQL expert advisor.
/// Enters long when SAR is below price, short when SAR is above price.
/// </summary>
public class ExpotestStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _sarStep;
private readonly StrategyParam<decimal> _sarMaximum;
private bool _prevSarBelow;
private bool _initialized;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public decimal SarStep
{
get => _sarStep.Value;
set => _sarStep.Value = value;
}
public decimal SarMaximum
{
get => _sarMaximum.Value;
set => _sarMaximum.Value = value;
}
public ExpotestStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle type for signal generation", "General");
_sarStep = Param(nameof(SarStep), 0.02m)
.SetDisplay("SAR Step", "Acceleration factor for Parabolic SAR", "Indicators");
_sarMaximum = Param(nameof(SarMaximum), 0.2m)
.SetDisplay("SAR Maximum", "Maximum acceleration factor for Parabolic SAR", "Indicators");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevSarBelow = false;
_initialized = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevSarBelow = false;
_initialized = false;
var sar = new ParabolicSar
{
Acceleration = SarStep,
AccelerationMax = SarMaximum
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sar, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sar);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, decimal sarValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var sarBelow = sarValue < candle.ClosePrice;
if (!_initialized)
{
_prevSarBelow = sarBelow;
_initialized = true;
return;
}
// SAR flipped from above to below price => Buy signal
if (sarBelow && !_prevSarBelow && Position <= 0)
{
BuyMarket();
}
// SAR flipped from below to above price => Sell signal
else if (!sarBelow && _prevSarBelow && Position >= 0)
{
SellMarket();
}
_prevSarBelow = sarBelow;
}
}
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 ParabolicSar
from StockSharp.Algo.Strategies import Strategy
class expotest_strategy(Strategy):
def __init__(self):
super(expotest_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candle type for signal generation", "General")
self._sar_step = self.Param("SarStep", 0.02) \
.SetDisplay("SAR Step", "Acceleration factor for Parabolic SAR", "Indicators")
self._sar_maximum = self.Param("SarMaximum", 0.2) \
.SetDisplay("SAR Maximum", "Maximum acceleration factor for Parabolic SAR", "Indicators")
self._prev_sar_below = False
self._initialized = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def SarStep(self):
return self._sar_step.Value
@property
def SarMaximum(self):
return self._sar_maximum.Value
def OnReseted(self):
super(expotest_strategy, self).OnReseted()
self._prev_sar_below = False
self._initialized = False
def OnStarted2(self, time):
super(expotest_strategy, self).OnStarted2(time)
self._prev_sar_below = False
self._initialized = False
sar = ParabolicSar()
sar.Acceleration = self.SarStep
sar.AccelerationMax = self.SarMaximum
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(sar, self._on_process) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sar)
self.DrawOwnTrades(area)
def _on_process(self, candle, sar_value):
if candle.State != CandleStates.Finished:
return
sv = float(sar_value)
sar_below = sv < float(candle.ClosePrice)
if not self._initialized:
self._prev_sar_below = sar_below
self._initialized = True
return
if sar_below and not self._prev_sar_below and self.Position <= 0:
self.BuyMarket()
elif not sar_below and self._prev_sar_below and self.Position >= 0:
self.SellMarket()
self._prev_sar_below = sar_below
def CreateClone(self):
return expotest_strategy()