Killer Sell 2.0 é um consultor especialista MetaTrader 4 somente-vendido que cronometra entradas após leituras estendidas de sobrecompra e assegura lucros quando o momentum oscila para território de sobrevenda. Este port reescreve a lógica original sobre a API de estratégia de alto nível do StockSharp. Todo o processamento de indicadores é orientado por eventos através de SubscribeCandles().BindEx(...), e as regras de gerenciamento de dinheiro estão encapsuladas dentro da classe de estratégia.
Lógica de trading
A lógica convertida segue a cadeia de sinais original enquanto usa o modelo de posição líquida do StockSharp. Cada vela completada do período configurado executa os seguintes passos:
Preparação de dados. A estratégia atualiza um MACD (12/120/9), Williams %R (período 350 para ambos os filtros) e dois osciladores Estocásticos (10/1/3 para entrada, 90/7/1 para saídas). Os valores dos indicadores são consumidos apenas quando a nova barra está concluída e as entradas estão completamente formadas.
Filtro de entrada. Um setup vendido é válido quando todas as condições abaixo são atendidas:
Williams %R sobe acima de −10, sinalizando um mercado sobrecomprado.
A linha principal do MACD é maior que 0.0014.
O %K do Estocástico de entrada cruza abaixo do nível de entrada configurável (padrão 90). A detecção de cruzamento é realizada em leituras consecutivas de %K.
Colocação de ordem. Quando os filtros se alinham, a estratégia envia uma venda de mercado usando o tamanho de lote martingale atual. As ordens herdam um take-profit definido N pips adiante (padrão 100 pips) via StartProtection.
Gerenciamento de saída. Enquanto existe exposição vendida, a estratégia calcula a média aritmética do lucro em pips dos tickets abertos. Dependendo do momentum:
Se o lucro médio estiver abaixo de 10 pips e Williams %R cair abaixo de −80, todas as vendidas são fechadas imediatamente.
Se o lucro médio estiver acima de 15 pips e o %K do Estocástico de saída cair abaixo de 12, a posição é fechada para assegurar o ganho.
Gerenciamento de dinheiro
Killer Sell 2.0 usa uma escada martingale semelhante ao EA original. A implementação StockSharp mantém uma lista interna de lotes vendidos abertos para imitar os cálculos por ticket do MetaTrader:
A primeira operação usa InitialVolume (padrão 0.05 lotes).
Após um ciclo lucrativo ou break-even, o volume é redefinido para o tamanho de lote inicial.
Após um ciclo perdedor, a próxima ordem é multiplicada por MartingaleMultiplier (padrão ×1.2). Um limite de segurança MaxVolume previne crescimento descontrolado.
O auxiliar também rastreia PnL realizado em preenchimentos para decidir se o ciclo anterior foi lucrativo.
Parâmetros
Parâmetro
Descrição
CandleType
Período principal que alimenta cada indicador.
EntryWprPeriod / ExitWprPeriod
Comprimentos de Williams %R para confirmações de entrada e saída.
Limite de sobrevenda verificado antes de assegurar lucros.
EntryWprThreshold / ExitWprThreshold
Limiares de Williams %R para entradas/saídas.
LossExitPips / ProfitExitPips
Limites de lucro médio (em pips) controlando saídas defensivas e de alvo.
TakeProfitPips
Take-profit protetor atribuído a cada ordem de venda.
InitialVolume
Volume do primeiro passo martingale.
MartingaleMultiplier
Fator aplicado após perdas.
MaxVolume
Limite absoluto aplicado ao próximo tamanho de lote.
Notas de conversão
MetaTrader mantém tickets individuais; StockSharp trabalha com uma posição líquida. A estratégia portanto armazena cada vendido preenchido (volume + preço) para reproduzir cálculos de lucro médio e para avaliar resets martingale.
O bloco "martingale" do MT4 expunha muitos modos adicionais (fixo, risco percentual, 1326, Fibonacci, etc.). A configuração original usava o ramo martingale simples; apenas esse comportamento é replicado aqui.
O stop-loss de emergência estava desabilitado no projeto fonte. O port espelha essa configuração anexando apenas um take-profit e gerenciando outras saídas internamente.
Dicas de uso
Anexe a estratégia a um portfólio e ativo, depois configure o mesmo período usado nos backtests do MT4 (os padrões assumem H1).
Certifique-se de que os dados de mercado entregam velas completadas; os indicadores dependem de eventos CandleStates.Finished.
Revise a alavancagem da conta e os tamanhos de lote permitidos. O limite martingale padrão (5 lotes) deve ser ajustado aos requisitos do seu corretor.
Faça backtests extensivamente — estratégias martingale amplificam o risco quando os mercados têm tendência forte contra o viés vendido.
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 KillerSell20Strategy : 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 KillerSell20Strategy()
{
_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 killer_sell20_strategy(Strategy):
def __init__(self):
super(killer_sell20_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(killer_sell20_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(killer_sell20_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 killer_sell20_strategy()