Estrategia Knux Martingale
Estrategia de martingala que aumenta el volumen de operación después de una posición perdedora. El método filtra las entradas por el Average Directional Index (ADX) para operar solo en mercados con tendencia. Las velas alcistas abren posiciones largas, las velas bajistas abren posiciones cortas.
Detalles
- Criterios de entrada:
- ADX > 25
- Largo:
Close > Open - Corto:
Close < Open
- Largo/Corto: Ambos
- Criterios de salida: Stop loss o take profit
- Stops: Sí
- Valores predeterminados:
AdxPeriod= 14LotsMultiplier= 1.5mStopLoss= 150mTakeProfit= 50mCandleType= TimeSpan.FromMinutes(5).TimeFrame()
- Filtros:
- Categoría: Seguimiento de tendencia, Martingale
- Dirección: Ambos
- Indicadores: AverageDirectionalIndex
- Stops: Absoluto
- Complejidad: Intermedio
- Marco temporal: Intradía
- Estacionalidad: No
- Redes neuronales: No
- Divergencia: No
- Nivel de riesgo: Alto
using System;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Martingale strategy increasing volume after losses and filtered by ADX.
/// </summary>
public class KnuxMartingaleStrategy : Strategy
{
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<decimal> _lotsMultiplier;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _trendThreshold;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<DataType> _candleType;
private decimal _currentVolume;
private decimal _prevSma;
private bool _hasPrevSma;
private int _barsSinceExit;
/// <summary>
/// ADX period used for trend strength.
/// </summary>
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
/// <summary>
/// Volume multiplier applied after a losing trade.
/// </summary>
public decimal LotsMultiplier
{
get => _lotsMultiplier.Value;
set => _lotsMultiplier.Value = value;
}
/// <summary>
/// Stop loss in absolute price units.
/// </summary>
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take profit in absolute price units.
/// </summary>
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Minimum normalized distance between price and trend average.
/// </summary>
public decimal TrendThreshold
{
get => _trendThreshold.Value;
set => _trendThreshold.Value = value;
}
/// <summary>
/// Cooldown after a flat position before a new entry.
/// </summary>
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the strategy.
/// </summary>
public KnuxMartingaleStrategy()
{
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetDisplay("ADX Period", "Period for ADX filter", "Indicators");
_lotsMultiplier = Param(nameof(LotsMultiplier), 1.5m)
.SetDisplay("Lots Multiplier", "Multiplier for losing trades", "Risk");
_stopLoss = Param(nameof(StopLoss), 150m)
.SetDisplay("Stop Loss", "Absolute stop loss in price units", "Risk");
_takeProfit = Param(nameof(TakeProfit), 300m)
.SetDisplay("Take Profit", "Absolute take profit in price units", "Risk");
_trendThreshold = Param(nameof(TrendThreshold), 0.008m)
.SetDisplay("Trend Threshold", "Minimum distance from trend average", "Indicators");
_cooldownBars = Param(nameof(CooldownBars), 3)
.SetDisplay("Cooldown Bars", "Bars to wait after a completed position", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Time frame for strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_currentVolume = Volume;
_prevSma = 0m;
_hasPrevSma = false;
_barsSinceExit = CooldownBars;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_currentVolume = Volume;
var sma = new SimpleMovingAverage { Length = AdxPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(sma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, sma);
DrawOwnTrades(area);
}
StartProtection(
takeProfit: new Unit(TakeProfit, UnitTypes.Absolute),
stopLoss: new Unit(StopLoss, UnitTypes.Absolute));
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_barsSinceExit < CooldownBars)
_barsSinceExit++;
if (!_hasPrevSma)
{
_prevSma = smaValue;
_hasPrevSma = true;
return;
}
var distance = smaValue == 0m ? 0m : Math.Abs(candle.ClosePrice - smaValue) / smaValue;
var isTrendUp = candle.ClosePrice > smaValue && smaValue > _prevSma;
var isTrendDown = candle.ClosePrice < smaValue && smaValue < _prevSma;
if (distance < TrendThreshold)
{
_prevSma = smaValue;
return;
}
if (_barsSinceExit < CooldownBars && Position == 0)
{
_prevSma = smaValue;
return;
}
var volume = Math.Max(Volume, _currentVolume);
if (isTrendUp && candle.ClosePrice > candle.OpenPrice && Position <= 0)
{
BuyMarket(volume);
}
else if (isTrendDown && candle.ClosePrice < candle.OpenPrice && Position >= 0)
{
SellMarket(volume);
}
_prevSma = smaValue;
}
/// <inheritdoc />
protected override void OnOwnTradeReceived(MyTrade myTrade)
{
base.OnOwnTradeReceived(myTrade);
if (Position != 0)
return;
if (myTrade.PnL < 0)
{
_currentVolume *= LotsMultiplier;
}
else
{
_currentVolume = Volume;
}
_barsSinceExit = 0;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, Unit, UnitTypes, CandleStates
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class knux_martingale_strategy(Strategy):
def __init__(self):
super(knux_martingale_strategy, self).__init__()
self._adx_period = self.Param("AdxPeriod", 14) \
.SetDisplay("ADX Period", "Period for ADX filter", "Indicators")
self._lots_multiplier = self.Param("LotsMultiplier", 1.5) \
.SetDisplay("Lots Multiplier", "Multiplier for losing trades", "Risk")
self._stop_loss = self.Param("StopLoss", 150.0) \
.SetDisplay("Stop Loss", "Absolute stop loss in price units", "Risk")
self._take_profit = self.Param("TakeProfit", 300.0) \
.SetDisplay("Take Profit", "Absolute take profit in price units", "Risk")
self._trend_threshold = self.Param("TrendThreshold", 0.008) \
.SetDisplay("Trend Threshold", "Minimum distance from trend average", "Indicators")
self._cooldown_bars = self.Param("CooldownBars", 3) \
.SetDisplay("Cooldown Bars", "Bars to wait after a completed position", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Time frame for strategy", "General")
self._current_volume = 0.0
self._prev_sma = 0.0
self._has_prev_sma = False
self._bars_since_exit = 0
@property
def AdxPeriod(self):
return self._adx_period.Value
@AdxPeriod.setter
def AdxPeriod(self, value):
self._adx_period.Value = value
@property
def LotsMultiplier(self):
return self._lots_multiplier.Value
@LotsMultiplier.setter
def LotsMultiplier(self, value):
self._lots_multiplier.Value = value
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def TrendThreshold(self):
return self._trend_threshold.Value
@TrendThreshold.setter
def TrendThreshold(self, value):
self._trend_threshold.Value = value
@property
def CooldownBars(self):
return self._cooldown_bars.Value
@CooldownBars.setter
def CooldownBars(self, value):
self._cooldown_bars.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(knux_martingale_strategy, self).OnStarted2(time)
self._current_volume = float(self.Volume)
sma = SimpleMovingAverage()
sma.Length = self.AdxPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(sma, self.ProcessCandle) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, sma)
self.DrawOwnTrades(area)
self.StartProtection(
takeProfit=Unit(self.TakeProfit, UnitTypes.Absolute),
stopLoss=Unit(self.StopLoss, UnitTypes.Absolute))
def ProcessCandle(self, candle, sma_value):
if candle.State != CandleStates.Finished:
return
sma_val = float(sma_value)
if self._bars_since_exit < self.CooldownBars:
self._bars_since_exit += 1
if not self._has_prev_sma:
self._prev_sma = sma_val
self._has_prev_sma = True
return
close = float(candle.ClosePrice)
distance = abs(close - sma_val) / sma_val if sma_val != 0.0 else 0.0
is_trend_up = close > sma_val and sma_val > self._prev_sma
is_trend_down = close < sma_val and sma_val < self._prev_sma
if distance < float(self.TrendThreshold):
self._prev_sma = sma_val
return
if self._bars_since_exit < self.CooldownBars and self.Position == 0:
self._prev_sma = sma_val
return
volume = max(float(self.Volume), self._current_volume)
pos = self.Position
if is_trend_up and float(candle.ClosePrice) > float(candle.OpenPrice) and pos <= 0:
self.BuyMarket(volume)
elif is_trend_down and float(candle.ClosePrice) < float(candle.OpenPrice) and pos >= 0:
self.SellMarket(volume)
self._prev_sma = sma_val
def OnOwnTradeReceived(self, my_trade):
super(knux_martingale_strategy, self).OnOwnTradeReceived(my_trade)
if self.Position != 0:
return
if my_trade.PnL < 0:
self._current_volume *= float(self.LotsMultiplier)
else:
self._current_volume = float(self.Volume)
self._bars_since_exit = 0
def OnReseted(self):
super(knux_martingale_strategy, self).OnReseted()
self._current_volume = float(self.Volume)
self._prev_sma = 0.0
self._has_prev_sma = False
self._bars_since_exit = self.CooldownBars
def CreateClone(self):
return knux_martingale_strategy()