Стратегия Universum 3.0
Стратегия на основе осциллятора DeMarker, открывающая позиции на закрытии каждой свечи и увеличивающая объём по принципу мартингейла.
Подробности
- Условия входа:
- Лонг:
DeMarker > 0.5 - Шорт:
DeMarker < 0.5
- Лонг:
- Направление: Лонг и шорт
- Условия выхода:
- Закрытие по тейк-профиту или стоп-лоссу
- Стопы: Абсолютные пункты через
TakeProfitPointsиStopLossPoints - Значения по умолчанию:
DemarkerPeriod= 10TakeProfitPoints= 50mStopLossPoints= 50mInitialVolume= 1mLossesLimit= 100CandleType= TimeSpan.FromMinutes(1).TimeFrame()
- Фильтры:
- Категория: Следование тренду
- Направление: Лонг и шорт
- Индикаторы: DeMarker
- Стопы: Да
- Сложность: Низкая
- Таймфрейм: Краткосрочный
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Высокий
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>
/// Universum 3.0 strategy based on DeMarker indicator with martingale volume.
/// </summary>
public class Universum30Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _demarkerPeriod;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<decimal> _initialVolume;
private readonly StrategyParam<int> _lossesLimit;
private decimal _currentVolume;
private int _losses;
private decimal _lastPnL;
private decimal _previousDeMarker;
private bool _hasPreviousDeMarker;
/// <summary>
/// Candle type for strategy calculation.
/// </summary>
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
/// <summary>
/// DeMarker indicator period.
/// </summary>
public int DemarkerPeriod { get => _demarkerPeriod.Value; set => _demarkerPeriod.Value = value; }
/// <summary>
/// Take profit distance in price points.
/// </summary>
public decimal TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
/// <summary>
/// Stop loss distance in price points.
/// </summary>
public decimal StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
/// <summary>
/// Starting order volume.
/// </summary>
public decimal InitialVolume { get => _initialVolume.Value; set => _initialVolume.Value = value; }
/// <summary>
/// Maximum allowed consecutive losses.
/// </summary>
public int LossesLimit { get => _lossesLimit.Value; set => _lossesLimit.Value = value; }
/// <summary>
/// Initializes a new instance of <see cref="Universum30Strategy"/>.
/// </summary>
public Universum30Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Time frame for analysis", "General");
_demarkerPeriod = Param(nameof(DemarkerPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("DeMarker Period", "Length of DeMarker indicator", "Indicators");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 50m)
.SetGreaterThanZero()
.SetDisplay("Take Profit", "Target profit in absolute points", "Risk");
_stopLossPoints = Param(nameof(StopLossPoints), 50m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss", "Loss limit in absolute points", "Risk");
_initialVolume = Param(nameof(InitialVolume), 1m)
.SetGreaterThanZero()
.SetDisplay("Initial Volume", "Base order volume", "Trading");
_lossesLimit = Param(nameof(LossesLimit), 100)
.SetGreaterThanZero()
.SetDisplay("Losses Limit", "Max consecutive losses before stop", "Trading");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_currentVolume = 0m;
_losses = 0;
_lastPnL = 0m;
_previousDeMarker = 0m;
_hasPreviousDeMarker = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_currentVolume = InitialVolume;
_losses = 0;
_lastPnL = 0m;
_previousDeMarker = 0m;
_hasPreviousDeMarker = false;
StartProtection(
takeProfit: new Unit(TakeProfitPoints, UnitTypes.Absolute),
stopLoss: new Unit(StopLossPoints, UnitTypes.Absolute));
var demarker = new DeMarker { Length = DemarkerPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(demarker, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, demarker);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal demarkerValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var buySignal = _hasPreviousDeMarker && _previousDeMarker <= 0.3m && demarkerValue > 0.3m;
var sellSignal = _hasPreviousDeMarker && _previousDeMarker >= 0.7m && demarkerValue < 0.7m;
if (buySignal && Position <= 0)
BuyMarket(_currentVolume + Math.Abs(Position));
else if (sellSignal && Position >= 0)
SellMarket(_currentVolume + Math.Abs(Position));
_previousDeMarker = demarkerValue;
_hasPreviousDeMarker = true;
}
/// <inheritdoc />
protected override void OnPositionReceived(Position position)
{
base.OnPositionReceived(position);
if (Position != 0)
return;
var tradePnL = PnL - _lastPnL;
_lastPnL = PnL;
if (tradePnL > 0)
{
_currentVolume = InitialVolume;
_losses = 0;
}
else if (tradePnL < 0)
{
_currentVolume *= 2;
_losses++;
if (_losses >= LossesLimit)
Stop();
}
}
}
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, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import DeMarker
from StockSharp.Algo.Strategies import Strategy
class universum_30_strategy(Strategy):
def __init__(self):
super(universum_30_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30)))
self._demarker_period = self.Param("DemarkerPeriod", 10)
self._take_profit_points = self.Param("TakeProfitPoints", 50.0)
self._stop_loss_points = self.Param("StopLossPoints", 50.0)
self._initial_volume = self.Param("InitialVolume", 1.0)
self._losses_limit = self.Param("LossesLimit", 100)
self._current_volume = 0.0
self._losses = 0
self._last_pnl = 0.0
self._prev_demarker = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def DemarkerPeriod(self):
return self._demarker_period.Value
@DemarkerPeriod.setter
def DemarkerPeriod(self, value):
self._demarker_period.Value = value
@property
def TakeProfitPoints(self):
return self._take_profit_points.Value
@TakeProfitPoints.setter
def TakeProfitPoints(self, value):
self._take_profit_points.Value = value
@property
def StopLossPoints(self):
return self._stop_loss_points.Value
@StopLossPoints.setter
def StopLossPoints(self, value):
self._stop_loss_points.Value = value
@property
def InitialVolume(self):
return self._initial_volume.Value
@InitialVolume.setter
def InitialVolume(self, value):
self._initial_volume.Value = value
@property
def LossesLimit(self):
return self._losses_limit.Value
@LossesLimit.setter
def LossesLimit(self, value):
self._losses_limit.Value = value
def OnStarted2(self, time):
super(universum_30_strategy, self).OnStarted2(time)
self._current_volume = float(self.InitialVolume)
self._losses = 0
self._last_pnl = 0.0
self._prev_demarker = 0.0
self._has_prev = False
self.StartProtection(
Unit(float(self.TakeProfitPoints), UnitTypes.Absolute),
Unit(float(self.StopLossPoints), UnitTypes.Absolute))
demarker = DeMarker()
demarker.Length = self.DemarkerPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(demarker, self.ProcessCandle).Start()
def ProcessCandle(self, candle, demarker_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
dv = float(demarker_value)
buy_signal = self._has_prev and self._prev_demarker <= 0.3 and dv > 0.3
sell_signal = self._has_prev and self._prev_demarker >= 0.7 and dv < 0.7
pos = float(self.Position)
if buy_signal and pos <= 0:
volume = self._current_volume + abs(pos)
self.BuyMarket(volume)
elif sell_signal and pos >= 0:
volume = self._current_volume + abs(pos)
self.SellMarket(volume)
self._prev_demarker = dv
self._has_prev = True
def OnPositionReceived(self, position):
super(universum_30_strategy, self).OnPositionReceived(position)
if float(self.Position) != 0:
return
trade_pnl = float(self.PnL) - self._last_pnl
self._last_pnl = float(self.PnL)
if trade_pnl > 0:
self._current_volume = float(self.InitialVolume)
self._losses = 0
elif trade_pnl < 0:
self._current_volume *= 2
self._losses += 1
if self._losses >= int(self.LossesLimit):
self.Stop()
def OnReseted(self):
super(universum_30_strategy, self).OnReseted()
self._current_volume = 0.0
self._losses = 0
self._last_pnl = 0.0
self._prev_demarker = 0.0
self._has_prev = False
def CreateClone(self):
return universum_30_strategy()