A Estratégia SwingTrader é uma versão StockSharp do MetaTrader 4 consultor especialista SwingTrader.mq4. O EA original procura
Bollinger Reversões de banda: quando o preço salta da banda externa e a próxima barra cruza a linha média, o consultor abre uma
posição e começa a construir uma grade média no estilo martingale. A estratégia traduzida reproduz o mesmo comportamento de alto nível
usando velas StockSharp, Bollinger bandas de StockSharp.Algo.Indicators e os auxiliares de pedido da estrutura (BuyMarket,
SellMarket). A escala de volume, a largura da grade e as regras de liquidação refletem o código MT4, respeitando a troca
limites fornecidos pelos metadados Security.
Lógica de negociação
Assine o período configurado (CandleType) e alimente um indicador de bandas Bollinger com comprimento BollingerPeriod e um
multiplicador de desvio padrão fixo de 2.
Trabalhe apenas com velas prontas; o retorno de chamada do indicador ignora barras parcialmente formadas para replicar o MT4 IsNewCandle()
guarda.
Acompanhe se a vela anterior tocou a banda superior ou inferior. O par booleano _upTouch / _downTouch segue o
lógica de alternância original que mantém apenas um lado ativo até que a banda oposta seja tocada.
Quando nenhuma cesta estiver aberta:
abra uma posição longa se a última barra completa cruzar acima da faixa do meio depois de tocar anteriormente na faixa inferior;
abra uma posição curta se a barra cruzou abaixo da banda do meio depois de tocar a banda superior.
O volume de primeira ordem é igual a InitialVolume (após arredondamento de câmbio) e a largura inicial da grade é igual à última distância
entre as bandas Bollinger superior e inferior.
Quando existir uma cesta, observe o movimento adverso de uma largura de banda completa desde o primeiro enchimento:
para posições compradas, se a mínima da vela estiver pelo menos uma largura de banda abaixo do preço âncora, compre outra fatia cujo tamanho seja multiplicado
por Multiplier a cada novo nível;
para vendas, se a máxima da vela estiver uma largura de banda acima do preço âncora, venda uma fatia adicional usando o mesmo
lógica multiplicadora.
Continue agregando novos pedidos até que a meta de lucro ou perda máxima tolerada seja atingida.
Gestão de dinheiro e saídas
O auxiliar CalculateUnrealizedProfit reproduz o cálculo do PnL flutuante MT4 convertendo diferenças de preço em preço
etapas (Security.PriceStep) e valor da etapa (Security.StepPrice).
A proxy de capital investido usa a fórmula original Lots * Price / TickSize * TickValue / 30, onde Lots se torna a soma
dos volumes da grade e os parâmetros de tick são provenientes de Security.
Feche a cesta inteira quando o lucro flutuante exceder TakeProfitFactor * invested capital.
Forçar uma liquidação de emergência quando a perda flutuante atingir 10 * TakeProfitFactor * invested capital (mesmo índice do
Código MT4).
Todas as saídas são executadas com ordens de mercado na direção oposta; uma vez plana, o estado da grade é redefinido e novos toques devem ser
detectado antes que outra entrada possa ser acionada.
Parâmetros
Nome
Tipo
Padrão
Descrição
TakeProfitFactor
decimal
0.05
Multiplicador aplicado ao capital investido para definir a meta de lucro.
Multiplier
decimal
1.5
Multiplicador de volume para cada pedido médio adicional.
BollingerPeriod
int
20
Número de velas usadas pelo indicador Bollinger Bandas.
InitialVolume
decimal
1
Volume base da primeira negociação em uma nova cesta (arredondado para os limites do local).
CandleType
DataType
Período de 15 minutos
Prazo usado para geração de sinal.
Diferenças do original EA
StockSharp trabalha com posições líquidas; a estratégia mantém listas explícitas de entradas de grade para emular a ordem baseada em tickets do MT4
manuseio.
Os filtros de volume do Exchange (Security.MinVolume, Security.VolumeStep, Security.MaxVolume) são aplicados automaticamente
de chamar manualmente CheckVolumeValue.
Os sinais são avaliados em velas fechadas; os gatilhos intrabarra da versão MT4 são aproximados usando máximos e mínimos de velas
para decisões de média.
As ordens são sempre enviadas como instruções de mercado, enquanto o MT4 usou OrderSend com parâmetros de compra/venda explícitos.
Notas de uso
Fornece metadados realistas para o instrumento negociado: PriceStep, StepPrice, MinVolume, VolumeStep e MaxVolume devem
ser preenchido para os cálculos de lucro, perda e volume para corresponder ao comportamento do MT4.
Como a grade média é dimensionada geometricamente, teste a configuração em dados históricos e considere a margem do corretor
requisitos antes de executá-lo ao vivo.
A largura da grade é igual à largura de banda atual Bollinger; alterar BollingerPeriod afeta diretamente o tempo de entrada e a grade
espaçamento. Valide a sensibilidade durante a otimização.
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Simplified from "SwingTrader" MetaTrader expert.
/// Uses Bollinger Band touches to detect swing direction, then enters on middle-band cross.
/// </summary>
public class SwingTraderStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _bollingerPeriod;
private readonly StrategyParam<decimal> _bollingerWidth;
private BollingerBands _bollinger;
private bool _upTouch;
private bool _downTouch;
private decimal? _prevClose;
private decimal? _prevMiddle;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int BollingerPeriod
{
get => _bollingerPeriod.Value;
set => _bollingerPeriod.Value = value;
}
public decimal BollingerWidth
{
get => _bollingerWidth.Value;
set => _bollingerWidth.Value = value;
}
public SwingTraderStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for signals", "General");
_bollingerPeriod = Param(nameof(BollingerPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("BB Period", "Bollinger Bands period", "Indicators");
_bollingerWidth = Param(nameof(BollingerWidth), 2m)
.SetGreaterThanZero()
.SetDisplay("BB Width", "Bollinger Bands deviation", "Indicators");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_bollinger = new BollingerBands { Length = BollingerPeriod, Width = BollingerWidth };
_upTouch = false;
_downTouch = false;
_prevClose = null;
_prevMiddle = null;
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_bollinger, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _bollinger);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue bbValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!bbValue.IsFinal)
return;
if (bbValue is not BollingerBandsValue bbVal)
return;
if (bbVal.UpBand is not decimal upper || bbVal.LowBand is not decimal lower || bbVal.MovingAverage is not decimal middle)
return;
if (!_bollinger.IsFormed)
{
_prevClose = candle.ClosePrice;
_prevMiddle = middle;
return;
}
var close = candle.ClosePrice;
// Track Bollinger touches
if (candle.HighPrice > upper)
{
_upTouch = true;
_downTouch = false;
}
if (candle.LowPrice < lower)
{
_downTouch = true;
_upTouch = false;
}
if (_prevClose is null || _prevMiddle is null)
{
_prevClose = close;
_prevMiddle = middle;
return;
}
var volume = Volume;
if (volume <= 0)
volume = 1;
// Buy: had a lower band touch, now price crosses above middle
var buySignal = _downTouch && _prevClose.Value < _prevMiddle.Value && close > middle;
// Sell: had an upper band touch, now price crosses below middle
var sellSignal = _upTouch && _prevClose.Value > _prevMiddle.Value && close < middle;
if (buySignal)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
if (Position <= 0)
BuyMarket(volume);
_downTouch = false;
}
else if (sellSignal)
{
if (Position > 0)
SellMarket(Position);
if (Position >= 0)
SellMarket(volume);
_upTouch = false;
}
_prevClose = close;
_prevMiddle = middle;
}
/// <inheritdoc />
protected override void OnReseted()
{
_bollinger = null;
_upTouch = false;
_downTouch = false;
_prevClose = null;
_prevMiddle = null;
base.OnReseted();
}
}
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 BollingerBands
from StockSharp.Algo.Strategies import Strategy
class swing_trader_strategy(Strategy):
"""Bollinger Band touch swing: buy on lower touch + middle cross up, sell on upper touch + middle cross down."""
def __init__(self):
super(swing_trader_strategy, self).__init__()
self._bb_period = self.Param("BollingerPeriod", 20).SetGreaterThanZero().SetDisplay("BB Period", "Bollinger Bands period", "Indicators")
self._bb_width = self.Param("BollingerWidth", 2.0).SetGreaterThanZero().SetDisplay("BB Width", "Bollinger Bands deviation", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Timeframe for signals", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(swing_trader_strategy, self).OnReseted()
self._up_touch = False
self._down_touch = False
self._prev_close = 0
self._prev_middle = 0
def OnStarted2(self, time):
super(swing_trader_strategy, self).OnStarted2(time)
self._up_touch = False
self._down_touch = False
self._prev_close = 0
self._prev_middle = 0
self._bb = BollingerBands()
self._bb.Length = self._bb_period.Value
self._bb.Width = self._bb_width.Value
sub = self.SubscribeCandles(self.CandleType)
sub.BindEx(self._bb, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, self._bb)
self.DrawOwnTrades(area)
def OnProcess(self, candle, bb_val):
if candle.State != CandleStates.Finished:
return
if not self._bb.IsFormed:
return
upper = float(bb_val.UpBand)
lower = float(bb_val.LowBand)
middle = float(bb_val.MovingAverage)
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
# Track Bollinger touches
if high > upper:
self._up_touch = True
self._down_touch = False
if low < lower:
self._down_touch = True
self._up_touch = False
if self._prev_close == 0 or self._prev_middle == 0:
self._prev_close = close
self._prev_middle = middle
return
# Buy: had lower band touch, now price crosses above middle
buy_signal = self._down_touch and self._prev_close < self._prev_middle and close > middle
# Sell: had upper band touch, now price crosses below middle
sell_signal = self._up_touch and self._prev_close > self._prev_middle and close < middle
if buy_signal:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
self._down_touch = False
elif sell_signal:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
self._up_touch = False
self._prev_close = close
self._prev_middle = middle
def CreateClone(self):
return swing_trader_strategy()