A Estratégia BB Swing é um port fiel do consultor especialista MetaTrader "BB SWING". Opera pullbacks das Bandas de Bollinger alinhados com a tendência predominante definida por duas médias móveis lineares ponderadas (LWMAs). Um filtro de Momentum de período superior e um MACD muito lento ajudam a confirmar a força da reversão antes de qualquer posição ser aberta.
Lógica de trading
Trabalhar apenas com velas concluídas do período de tempo CandleType.
Rastrear as últimas quatro velas concluídas para inspecionar extremos recentes e corpos de velas.
Aguardar que a LWMA rápida permaneça acima (para longos) ou abaixo (para curtos) da LWMA lenta.
Verificar que um dos últimos três mínimos toca a banda inferior de Bollinger (configuração longa) ou um dos máximos toca a banda superior (configuração curta).
Requerer que a vela anterior tenha um corpo mais forte que sua predecessora, sinalizando Momentum se afastando da banda.
Confirmar a força da tendência com o Momentum calculado em MomentumCandleType. A estratégia mede a distância absoluta entre a leitura de Momentum e 100; a distância deve exceder os limiares de compra/venda configurados em qualquer um dos últimos três valores de Momentum.
Validar a direção de longo prazo com um MACD calculado no período de tempo MacdCandleType. Entradas longas são permitidas enquanto a linha principal do MACD permanece acima da linha de sinal; curtos requerem a relação oposta.
Quando todas as condições se alinham, entrar em uma posição de mercado usando o passo de volume martingale atual.
Dimensionamento de posição e escalonamento
InitialVolume define o volume da primeira entrada.
Cada add-on adicional multiplica o volume base por LotExponent (volume = InitialVolume * LotExponent^n).
MaxTrades limita o número de add-ons sequenciais para que o tamanho total da posição nunca exceda InitialVolume * MaxTrades.
Regras de saída e proteção
Valores fixos de StopLoss e TakeProfit expressos em passos de preço.
Lógica opcional de ponto de equilíbrio (EnableBreakEven) que move o stop para BreakEvenOffset quando o preço avança BreakEvenTrigger passos.
Trailing stop clássico (EnableTrailingStop) que segue o preço extremo por TrailingStop passos.
Ferramentas de gestão de capital:
UseMoneyTakeProfit fecha posições quando o lucro não realizado em moeda de conta atinge MoneyTakeProfit.
UsePercentTakeProfit fecha posições quando o lucro equivale a PercentTakeProfit por cento do capital inicial.
UseMoneyTrailing ativa um trail de lucro: quando o lucro excede MoneyTrailTarget, um recuo de MoneyTrailStop aciona uma saída.
UseEquityStop monitora o drawdown de capital relativo ao pico de capital registrado durante a sessão. Um drawdown maior que EquityRiskPercent fecha todas as posições.
CloseOnMacdCross opcional sai sempre que a linha principal do MACD cruzar a linha de sinal contra a direção da posição atual.
Todas as ações de proteção dependem de ordens de mercado (BuyMarket / SellMarket) para neutralizar toda a posição.
Parâmetros
Nome
Descrição
InitialVolume
Volume base de negociação usado para a primeira entrada.
LotExponent
Multiplicador aplicado ao volume de cada entrada adicional ao escalonar.
MaxTrades
Número máximo de add-ons sequenciais permitidos a qualquer momento.
TakeProfit
Take profit expresso em passos de preço.
StopLoss
Stop loss expresso em passos de preço.
FastMaPeriod
Período da LWMA rápida calculada em preços típicos.
SlowMaPeriod
Período da LWMA lenta calculada em preços típicos.
MomentumLength
Número de barras usadas no cálculo do Momentum.
MomentumBuyThreshold
Distância mínima de 100 para que o Momentum do período superior valide operações longas.
MomentumSellThreshold
Distância mínima de 100 para que o Momentum do período superior valide operações curtas.
EnableBreakEven
Habilita o movimento de stop para ponto de equilíbrio.
BreakEvenTrigger
Passos de preço necessários para acionar o movimento de ponto de equilíbrio.
BreakEvenOffset
Offset aplicado ao stop após ativação do ponto de equilíbrio.
EnableTrailingStop
Habilita o trailing stop clássico em passos de preço.
TrailingStop
Tamanho do trailing stop expresso em passos.
UseMoneyTakeProfit
Habilita tomada de lucro fixa em moeda de conta.
MoneyTakeProfit
Lucro em moeda que fecha a posição quando UseMoneyTakeProfit está ativo.
UsePercentTakeProfit
Habilita tomada de lucro baseada em percentual de capital.
PercentTakeProfit
Percentual do capital inicial que aciona uma saída quando UsePercentTakeProfit está ativo.
UseMoneyTrailing
Habilita trailing baseado em capital após atingir um lucro alvo.
MoneyTrailTarget
Nível de lucro que ativa a lógica de trailing monetário.
MoneyTrailStop
Recuo máximo permitido em moeda após ativação.
UseEquityStop
Habilita o fechamento de posições quando o drawdown flutuante excede um limiar.
EquityRiskPercent
Drawdown máximo de capital permitido em percentual.
CloseOnMacdCross
Habilita filtragem de saída baseada em MACD.
CandleType
Período de tempo primário usado para cálculos de sinais.
MomentumCandleType
Período superior usado para o filtro de Momentum.
MacdCandleType
Período muito lento usado pelo filtro de saída MACD.
Notas
A estratégia processa apenas velas concluídas; não reage dentro de uma barra.
Todos os cálculos de stop e alvo usam o passo de preço do instrumento reportado pelo exchange conectado. Certifique-se de que PriceStep esteja corretamente configurado para controle preciso de risco.
Proteções monetárias e baseadas em capital dependem das estatísticas de portfólio de estratégia disponíveis no StockSharp. Ao executar no modo tester, certifique-se de que o feed do portfólio esteja habilitado.
Ao contrário do especialista MQL original, esta implementação em C# mantém uma única posição agregada por direção. O escalonamento aumenta a posição agregada em vez de abrir múltiplos tickets discretos.
As Bandas de Bollinger usam comprimento fixo de 20 e largura de 2 desvios padrão em preços típicos, correspondendo ao código original.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class BbSwingStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public BbSwingStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class bb_swing_strategy(Strategy):
def __init__(self):
super(bb_swing_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(bb_swing_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(bb_swing_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return bb_swing_strategy()