A estratégia Zakryvator é um módulo de gerenciamento de risco que monitora a posição aberta atual e a fecha quando a perda não realizada excede um limiar predefinido. A perda permitida depende do volume da posição, replicando a lógica do script MQL original onde diferentes tamanhos de lote correspondem a diferentes drawdowns máximos.
Esta estratégia não gera entradas por si mesma. Espera-se que as posições sejam abertas manualmente ou por outra estratégia. Zakryvator simplesmente protege a conta saindo automaticamente de operações com prejuízo.
Detalhes
Critérios de entrada: Nenhum. A estratégia apenas gerencia posições existentes.
Critérios de saída: Fecha a posição atual quando a perda atinge o limiar configurado para seu volume.
Comprado/Vendido: Ambas as direções são suportadas.
Stops: Utiliza limites de perda monetária fixos que variam com o tamanho da posição.
Filtros: Sem filtros adicionais.
Parâmetros
Parâmetro
Descrição
Min001002
Perda máxima para posições com volume ≤ 0.02 lotes.
Min002005
Perda máxima para posições com volume entre 0.02 e 0.05 lotes.
Min00501
Perda máxima para posições com volume entre 0.05 e 0.10 lotes.
Min0103
Perda máxima para posições com volume entre 0.10 e 0.30 lotes.
Min0305
Perda máxima para posições com volume entre 0.30 e 0.50 lotes.
Min051
Perda máxima para posições com volume entre 0.50 e 1 lote.
MinFrom1
Perda máxima para posições com volume maior que 1 lote.
Comportamento
A estratégia subscreve ticks de operações para rastrear preços em tempo real.
Em cada tick calcula o PnL não realizado usando o preço atual e o preço médio de entrada.
Se a perda exceder o limiar correspondente ao volume da posição atual, a posição é fechada a mercado.
Isso torna o Zakryvator uma ferramenta simples, mas eficaz, para limitar drawdowns com base no tamanho da operação.
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>
/// Strategy that opens positions using SMA crossover and closes them
/// when unrealized loss exceeds a volume-based threshold ("Zakryvator" = position closer on loss).
/// </summary>
public class ZakryvatorStrategy : Strategy
{
private decimal _entryPrice;
private decimal _lastPrice;
private bool _prevShortAboveLong;
private readonly SimpleMovingAverage _smaShort = new() { Length = 50 };
private readonly SimpleMovingAverage _smaLong = new() { Length = 150 };
private readonly StrategyParam<int> _shortPeriod;
private readonly StrategyParam<int> _longPeriod;
private readonly StrategyParam<decimal> _lossThreshold;
/// <summary>Short SMA period.</summary>
public int ShortPeriod { get => _shortPeriod.Value; set => _shortPeriod.Value = value; }
/// <summary>Long SMA period.</summary>
public int LongPeriod { get => _longPeriod.Value; set => _longPeriod.Value = value; }
/// <summary>Maximum unrealized loss before closing position.</summary>
public decimal LossThreshold { get => _lossThreshold.Value; set => _lossThreshold.Value = value; }
/// <summary>Constructor.</summary>
public ZakryvatorStrategy()
{
_shortPeriod = Param(nameof(ShortPeriod), 50)
.SetDisplay("Short SMA", "Short SMA period for entry signal", "Entry");
_longPeriod = Param(nameof(LongPeriod), 150)
.SetDisplay("Long SMA", "Long SMA period for entry signal", "Entry");
_lossThreshold = Param(nameof(LossThreshold), 500m)
.SetDisplay("Loss Threshold", "Max unrealized loss before closing position", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, TimeSpan.FromMinutes(5).TimeFrame())];
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_smaShort.Length = ShortPeriod;
_smaLong.Length = LongPeriod;
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription
.Bind(_smaShort, _smaLong, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal shortSma, decimal longSma)
{
if (candle.State != CandleStates.Finished)
return;
if (!_smaShort.IsFormed || !_smaLong.IsFormed)
return;
_lastPrice = candle.ClosePrice;
var shortAboveLong = shortSma > longSma;
// Check loss threshold for open position
if (Position != 0 && _entryPrice != 0m)
{
var openPnL = Position * (_lastPrice - _entryPrice);
if (openPnL <= -LossThreshold)
{
// Close on loss
if (Position > 0)
SellMarket();
else
BuyMarket();
_entryPrice = 0m;
_prevShortAboveLong = shortAboveLong;
return;
}
}
// SMA crossover entry/exit logic
var crossUp = shortAboveLong && !_prevShortAboveLong;
var crossDown = !shortAboveLong && _prevShortAboveLong;
if (crossUp)
{
if (Position < 0)
{
BuyMarket();
_entryPrice = 0m;
}
if (Position == 0)
{
BuyMarket();
_entryPrice = _lastPrice;
}
}
else if (crossDown)
{
if (Position > 0)
{
SellMarket();
_entryPrice = 0m;
}
if (Position == 0)
{
SellMarket();
_entryPrice = _lastPrice;
}
}
_prevShortAboveLong = shortAboveLong;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0m;
_lastPrice = 0m;
_prevShortAboveLong = false;
_smaShort.Reset();
_smaLong.Reset();
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class zakryvator_strategy(Strategy):
def __init__(self):
super(zakryvator_strategy, self).__init__()
self._entry_price = 0.0
self._last_price = 0.0
self._prev_short_above_long = False
self._sma_short = SimpleMovingAverage()
self._sma_long = SimpleMovingAverage()
self._short_period = self.Param("ShortPeriod", 50) \
.SetDisplay("Short SMA", "Short SMA period for entry signal", "Entry")
self._long_period = self.Param("LongPeriod", 150) \
.SetDisplay("Long SMA", "Long SMA period for entry signal", "Entry")
self._loss_threshold = self.Param("LossThreshold", 500.0) \
.SetDisplay("Loss Threshold", "Max unrealized loss before closing position", "Risk")
@property
def ShortPeriod(self):
return self._short_period.Value
@property
def LongPeriod(self):
return self._long_period.Value
@property
def LossThreshold(self):
return self._loss_threshold.Value
def GetWorkingSecurities(self):
return [(self.Security, DataType.TimeFrame(TimeSpan.FromMinutes(5)))]
def OnStarted2(self, time):
super(zakryvator_strategy, self).OnStarted2(time)
self._sma_short.Length = self.ShortPeriod
self._sma_long.Length = self.LongPeriod
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._sma_short, self._sma_long, self.process_candle).Start()
def process_candle(self, candle, short_sma, long_sma):
if candle.State != CandleStates.Finished:
return
if not self._sma_short.IsFormed or not self._sma_long.IsFormed:
return
self._last_price = float(candle.ClosePrice)
short_above_long = float(short_sma) > float(long_sma)
# Check loss threshold for open position
if self.Position != 0 and self._entry_price != 0.0:
open_pnl = float(self.Position) * (self._last_price - self._entry_price)
if open_pnl <= -float(self.LossThreshold):
# Close on loss
if self.Position > 0:
self.SellMarket()
else:
self.BuyMarket()
self._entry_price = 0.0
self._prev_short_above_long = short_above_long
return
# SMA crossover entry/exit logic
cross_up = short_above_long and not self._prev_short_above_long
cross_down = not short_above_long and self._prev_short_above_long
if cross_up:
if self.Position < 0:
self.BuyMarket()
self._entry_price = 0.0
if self.Position == 0:
self.BuyMarket()
self._entry_price = self._last_price
elif cross_down:
if self.Position > 0:
self.SellMarket()
self._entry_price = 0.0
if self.Position == 0:
self.SellMarket()
self._entry_price = self._last_price
self._prev_short_above_long = short_above_long
def OnReseted(self):
super(zakryvator_strategy, self).OnReseted()
self._entry_price = 0.0
self._last_price = 0.0
self._prev_short_above_long = False
self._sma_short.Reset()
self._sma_long.Reset()
def CreateClone(self):
return zakryvator_strategy()