Conversão do consultor especialista MetaTrader 5 "Média Móvel com Quadros". O sistema original avalia a relação entre os preços de abertura/fechamento de cada vela e uma média móvel simples deslocada (SMA) enquanto exibe vários "quadros" de otimização nos gráficos. Esta porta StockSharp concentra-se na lógica de negociação: ela reage apenas uma vez por barra concluída, abre uma única posição de compensação e reflete as regras de gerenciamento de dinheiro do código-fonte.
Lógica de negociação
Fonte de dados – a estratégia assina o período configurado (CandleType) e processa apenas velas finalizadas, o que reproduz a restrição MetaTrader if(rt[1].tick_volume>1) return;.
Indicador – uma média móvel simples com período MovingPeriod. A saída do indicador é deslocada para frente em MovingShift velas concluídas, mantendo um buffer de valores anteriores.
Aquecimento – a negociação é suspensa até que pelo menos 100 velas concluídas sejam coletadas, correspondendo à guarda Bars(_Symbol,_Period)>100 original.
Condições de entrada
Vá comprado quando a vela abrir abaixo do SMA deslocado e fechar acima dele.
Opere short quando a vela abrir acima do SMA deslocado e fechar abaixo dele.
O motor impõe uma posição única: a exposição oposta é achatada antes de entrar na nova direção.
Condições de saída – uma posição longa existente é fechada quando o preço de abertura está acima e o preço de fechamento está abaixo do deslocado SMA; os shorts são fechados no cruzamento oposto. Novas negociações não são abertas na mesma barra após uma saída, assim como o especialista original.
Dimensionamento de posição e risco
MaximumRisk – determina o volume bruto do pedido como Portfolio.CurrentValue * MaximumRisk / price quando os dados do portfólio estão disponíveis. Se o feed do corretor não fornecer informações de patrimônio, a estratégia volta para a propriedade manual Volume.
DecreaseFactor – após mais de uma negociação consecutiva perdida, o tamanho da próxima posição é reduzido em volume * losses / DecreaseFactor, imitando a lógica de redução de lote de MetaTrader. Qualquer negociação lucrativa zera o contador.
Alinhamento de volume – o tamanho calculado é normalizado para o VolumeStep do instrumento, fixado entre MinVolume e MaxVolume e arredondado para duas casas decimais quando a exchange não publica uma etapa.
Notas adicionais
A visualização de "frames" MetaTrader não é portada porque StockSharp já fornece painéis de otimização avançados. A lógica de negociação, o tempo do sinal e o comportamento de dimensionamento permanecem fiéis à fonte.
Todos os valores do indicador são consumidos diretamente do retorno de chamada Bind; nenhuma chamada manual GetValue é usada.
O rastreamento de perdas consecutivas é implementado dentro de OnOwnTradeReceived, permitindo que a estratégia reaja corretamente aos preenchimentos parciais e ao comportamento da compensação.
Parâmetros
Parâmetro
Padrão
Descrição
MaximumRisk
0.02
Fração do patrimônio do portfólio arriscado em cada entrada.
DecreaseFactor
3
Divisor usado para diminuir o tamanho da posição após duas ou mais perdas consecutivas.
MovingPeriod
12
Comprimento da média móvel simples aplicada aos preços de fechamento.
MovingShift
6
Número de velas concluídas usadas para compensar o SMA avanço no tempo.
CandleType
1h time frame
Série de velas primárias processadas pela estratégia.
Dicas de uso
Anexe a estratégia a um título e portfólio no StockSharp Designer ou código.
Ajuste o tipo de vela para corresponder ao período do gráfico MetaTrader desejado.
Ajuste MaximumRisk e DecreaseFactor para corresponder ao tamanho da sua conta e à tolerância ao risco desejada.
Execute backtests para validar se os sinais de cruzamento estão alinhados com os resultados MetaTrader originais.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Moving Average With Frames: SMA crossover with candle body confirmation.
/// Buys when close crosses above shifted SMA, sells when crosses below.
/// </summary>
public class MovingAverageWithFramesStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _smaLength;
private readonly StrategyParam<int> _atrLength;
private decimal _entryPrice;
private decimal _prevClose;
public MovingAverageWithFramesStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(2).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_smaLength = Param(nameof(SmaLength), 12)
.SetDisplay("SMA Length", "SMA period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int SmaLength
{
get => _smaLength.Value;
set => _smaLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_prevClose = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var sma = new SimpleMovingAverage { Length = SmaLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal smaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
if (atrVal <= 0)
return;
var close = candle.ClosePrice;
if (_prevClose == 0)
{
_prevClose = close;
return;
}
if (Position > 0)
{
if (close < smaVal && _prevClose >= smaVal)
{
SellMarket();
_entryPrice = 0;
}
else if (close <= _entryPrice - atrVal * 2m)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close > smaVal && _prevClose <= smaVal)
{
BuyMarket();
_entryPrice = 0;
}
else if (close >= _entryPrice + atrVal * 2m)
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (close > smaVal && _prevClose <= smaVal)
{
_entryPrice = close;
BuyMarket();
}
else if (close < smaVal && _prevClose >= smaVal)
{
_entryPrice = close;
SellMarket();
}
}
_prevClose = close;
}
}
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
from StockSharp.Algo.Indicators import SimpleMovingAverage, AverageTrueRange
class moving_average_with_frames_strategy(Strategy):
def __init__(self):
super(moving_average_with_frames_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(2))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._sma_length = self.Param("SmaLength", 12) \
.SetDisplay("SMA Length", "SMA period.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._entry_price = 0.0
self._prev_close = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def SmaLength(self):
return self._sma_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(moving_average_with_frames_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._prev_close = 0.0
self._sma = SimpleMovingAverage()
self._sma.Length = self.SmaLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._sma, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, sma_val, atr_val):
if candle.State != CandleStates.Finished:
return
sv = float(sma_val)
av = float(atr_val)
if av <= 0:
return
close = float(candle.ClosePrice)
if self._prev_close == 0:
self._prev_close = close
return
if self.Position > 0:
if close < sv and self._prev_close >= sv:
self.SellMarket()
self._entry_price = 0.0
elif close <= self._entry_price - av * 2.0:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close > sv and self._prev_close <= sv:
self.BuyMarket()
self._entry_price = 0.0
elif close >= self._entry_price + av * 2.0:
self.BuyMarket()
self._entry_price = 0.0
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_close = close
return
if self.Position == 0:
if close > sv and self._prev_close <= sv:
self._entry_price = close
self.BuyMarket()
elif close < sv and self._prev_close >= sv:
self._entry_price = close
self.SellMarket()
self._prev_close = close
def OnReseted(self):
super(moving_average_with_frames_strategy, self).OnReseted()
self._entry_price = 0.0
self._prev_close = 0.0
def CreateClone(self):
return moving_average_with_frames_strategy()