Estrategia Exp Martin V2
La estrategia Exp Martin V2 implementa un enfoque martingala exponencial. Siempre mantiene una sola posición abierta y después de cada operación decide la siguiente dirección y volumen en función del beneficio de la última transacción.
La estrategia comienza con un tipo de orden predefinido (compra o venta) y un volumen inicial. Se aplica un take-profit y un stop-loss fijos a cada posición. Cuando una operación se cierra con beneficio, se abre una nueva posición del mismo tipo con el volumen inicial. Si la operación termina con pérdida, la dirección se invierte y el volumen se multiplica por un factor especificado. La multiplicación continúa después de cada pérdida hasta que se alcanza un número máximo de multiplicaciones; entonces el volumen se restablece al valor inicial.
Esto crea una secuencia escalada de operaciones opuestas que busca recuperar las pérdidas anteriores una vez que ocurre un movimiento rentable.
Detalles
- Lógica de entrada:
- Abrir la posición inicial según Start Type (0 - compra, 1 - venta) con el Start Volume.
- Después de una operación rentable, repetir la misma dirección con el volumen inicial.
- Después de una operación perdedora, invertir la dirección y multiplicar el volumen por Factor hasta alcanzar las multiplicaciones de Limit.
- Largo/Corto: Ambos, dependiendo de la secuencia actual.
- Lógica de salida:
- Las posiciones se cierran cuando el precio alcanza los niveles configurados de Take Profit o Stop Loss.
- Stops: Stop-loss y take-profit fijos en puntos.
- Filtros: Ninguno.
- Gestión de posición: Solo una posición abierta a la vez.
Utilice esta estrategia para experimentar con la gestión de dinero martingala en StockSharp sin indicadores adicionales.
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Exponential martingale strategy.
/// </summary>
public class ExpMartinV2Strategy : Strategy
{
private readonly StrategyParam<decimal> _startVolume;
private readonly StrategyParam<decimal> _factor;
private readonly StrategyParam<int> _limit;
private readonly StrategyParam<int> _stopLoss;
private readonly StrategyParam<int> _takeProfit;
private readonly StrategyParam<int> _startType;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cycleCooldownBars;
private decimal _currentVolume;
private decimal _maxVolume;
private bool _needOpenPosition = true;
private int _direction;
private decimal _entryPrice;
private decimal _longTake;
private decimal _longStop;
private decimal _shortTake;
private decimal _shortStop;
private int _cooldownRemaining;
/// <summary>
/// Initial order volume.
/// </summary>
public decimal StartVolume { get => _startVolume.Value; set => _startVolume.Value = value; }
/// <summary>
/// Volume multiplier after a loss.
/// </summary>
public decimal Factor { get => _factor.Value; set => _factor.Value = value; }
/// <summary>
/// Maximum number of volume multiplications.
/// </summary>
public int Limit { get => _limit.Value; set => _limit.Value = value; }
/// <summary>
/// Stop loss in price steps.
/// </summary>
public int StopLoss { get => _stopLoss.Value; set => _stopLoss.Value = value; }
/// <summary>
/// Take profit in price steps.
/// </summary>
public int TakeProfit { get => _takeProfit.Value; set => _takeProfit.Value = value; }
/// <summary>
/// Starting order type: 0 - buy, 1 - sell.
/// </summary>
public int StartType { get => _startType.Value; set => _startType.Value = value; }
/// <summary>
/// Candle type used for processing.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// Closed candles to wait before starting the next martingale cycle.
/// </summary>
public int CycleCooldownBars { get => _cycleCooldownBars.Value; set => _cycleCooldownBars.Value = value; }
public ExpMartinV2Strategy()
{
_startVolume = Param(nameof(StartVolume), 1m)
.SetGreaterThanZero()
.SetDisplay("Start Volume", "Initial order volume", "General");
_factor = Param(nameof(Factor), 2m)
.SetGreaterThanZero()
.SetDisplay("Factor", "Volume multiplier", "General");
_limit = Param(nameof(Limit), 5)
.SetGreaterThanZero()
.SetDisplay("Limit", "Max multiplication count", "General");
_stopLoss = Param(nameof(StopLoss), 100)
.SetGreaterThanZero()
.SetDisplay("Stop Loss", "Loss limit in points", "Risk");
_takeProfit = Param(nameof(TakeProfit), 100)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Profit target in points", "Risk");
_startType = Param(nameof(StartType), 0)
.SetDisplay("Start Type", "0-Buy, 1-Sell", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
_cycleCooldownBars = Param(nameof(CycleCooldownBars), 2)
.SetNotNegative()
.SetDisplay("Cycle Cooldown Bars", "Closed candles to wait before the next entry cycle", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_currentVolume = 0m;
_maxVolume = 0m;
_needOpenPosition = true;
_direction = 0;
_entryPrice = 0m;
_longTake = 0m;
_longStop = 0m;
_shortTake = 0m;
_shortStop = 0m;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
_currentVolume = StartVolume;
_direction = StartType == 0 ? 1 : -1;
_maxVolume = StartVolume;
for (var i = 0; i < Limit; i++)
_maxVolume = RoundVolume(_maxVolume * Factor);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (_cooldownRemaining > 0)
_cooldownRemaining--;
var step = Security.PriceStep ?? 1m;
if (Position > 0)
{
if (candle.HighPrice >= _longTake)
{
SellMarket(Position);
PrepareNext(true);
}
else if (candle.LowPrice <= _longStop)
{
SellMarket(Position);
PrepareNext(false);
}
}
else if (Position < 0)
{
if (candle.LowPrice <= _shortTake)
{
BuyMarket(-Position);
PrepareNext(true);
}
else if (candle.HighPrice >= _shortStop)
{
BuyMarket(-Position);
PrepareNext(false);
}
}
if (_needOpenPosition && Position == 0 && _cooldownRemaining == 0)
{
_entryPrice = candle.ClosePrice;
if (_direction == 1)
{
BuyMarket(_currentVolume);
_longTake = _entryPrice + TakeProfit * step;
_longStop = _entryPrice - StopLoss * step;
}
else
{
SellMarket(_currentVolume);
_shortTake = _entryPrice - TakeProfit * step;
_shortStop = _entryPrice + StopLoss * step;
}
_needOpenPosition = false;
}
}
private void PrepareNext(bool wasProfit)
{
if (wasProfit)
{
_currentVolume = StartVolume;
}
else
{
_direction = -_direction;
_currentVolume = RoundVolume(_currentVolume * Factor);
if (_currentVolume > _maxVolume)
_currentVolume = StartVolume;
}
_needOpenPosition = true;
_cooldownRemaining = CycleCooldownBars;
}
private decimal RoundVolume(decimal volume)
{
var step = Security.VolumeStep ?? 1m;
return Math.Ceiling(volume / step) * step;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
import math
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class exp_martin_v2_strategy(Strategy):
"""
Exponential martingale strategy.
Opens positions and doubles volume on loss, resets on profit.
"""
def __init__(self):
super(exp_martin_v2_strategy, self).__init__()
self._start_volume = self.Param("StartVolume", 1.0) \
.SetDisplay("Start Volume", "Initial order volume", "General")
self._factor = self.Param("Factor", 2.0) \
.SetDisplay("Factor", "Volume multiplier", "General")
self._limit = self.Param("Limit", 5) \
.SetDisplay("Limit", "Max multiplication count", "General")
self._stop_loss = self.Param("StopLoss", 100) \
.SetDisplay("Stop Loss", "Loss limit in points", "Risk")
self._take_profit = self.Param("TakeProfit", 100) \
.SetDisplay("Take Profit", "Profit target in points", "Risk")
self._start_type = self.Param("StartType", 0) \
.SetDisplay("Start Type", "0-Buy, 1-Sell", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._cooldown_bars = self.Param("CycleCooldownBars", 2) \
.SetDisplay("Cycle Cooldown Bars", "Bars to wait before next cycle", "General")
self._current_volume = 0.0
self._max_volume = 0.0
self._need_open = True
self._direction = 0
self._entry_price = 0.0
self._long_take = 0.0
self._long_stop = 0.0
self._short_take = 0.0
self._short_stop = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(exp_martin_v2_strategy, self).OnReseted()
self._current_volume = 0.0
self._max_volume = 0.0
self._need_open = True
self._direction = 0
self._entry_price = 0.0
self._long_take = 0.0
self._long_stop = 0.0
self._short_take = 0.0
self._short_stop = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(exp_martin_v2_strategy, self).OnStarted2(time)
sv = float(self._start_volume.Value)
self._current_volume = sv
self._direction = 1 if self._start_type.Value == 0 else -1
self._max_volume = sv
f = float(self._factor.Value)
for i in range(self._limit.Value):
self._max_volume = self._round_volume(self._max_volume * f)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 1.0
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
if self.Position > 0:
if high >= self._long_take:
self.SellMarket(self.Position)
self._prepare_next(True)
elif low <= self._long_stop:
self.SellMarket(self.Position)
self._prepare_next(False)
elif self.Position < 0:
if low <= self._short_take:
self.BuyMarket(-self.Position)
self._prepare_next(True)
elif high >= self._short_stop:
self.BuyMarket(-self.Position)
self._prepare_next(False)
if self._need_open and self.Position == 0 and self._cooldown_remaining == 0:
self._entry_price = close
tp = self._take_profit.Value
sl = self._stop_loss.Value
if self._direction == 1:
self.BuyMarket(self._current_volume)
self._long_take = self._entry_price + tp * step
self._long_stop = self._entry_price - sl * step
else:
self.SellMarket(self._current_volume)
self._short_take = self._entry_price - tp * step
self._short_stop = self._entry_price + sl * step
self._need_open = False
def _prepare_next(self, was_profit):
if was_profit:
self._current_volume = float(self._start_volume.Value)
else:
self._direction = -self._direction
self._current_volume = self._round_volume(self._current_volume * float(self._factor.Value))
if self._current_volume > self._max_volume:
self._current_volume = float(self._start_volume.Value)
self._need_open = True
self._cooldown_remaining = self._cooldown_bars.Value
def _round_volume(self, volume):
step = 1.0
if self.Security is not None and self.Security.VolumeStep is not None:
step = float(self.Security.VolumeStep)
if step <= 0:
step = 1.0
return math.ceil(volume / step) * step
def CreateClone(self):
return exp_martin_v2_strategy()