Стратегия Max Profit Min Loss Options
Эта стратегия сочетает быстрые и медленные скользящие средние с RSI, MACD и фильтром объёма. Покупает при совпадении тренда и импульса и использует стоп-лосс и трейлинг-профит для выхода.
Детали
- Условия входа:
- Лонг: бычий тренд, MACD пересекает сигнальную линию вверх, RSI выше уровня перепроданности и растёт, объём выше среднего.
- Шорт: медвежий тренд, MACD пересекает сигнальную линию вниз, RSI ниже уровня перекупленности и падает, объём выше среднего.
- Выход: противоположный сигнал или стоп-лосс/трейлинг-профит.
- Стопы: процентный стоп-лосс и трейлинг-профит.
- Значения по умолчанию:
- Быстрая MA = 9
- Медленная MA = 21
- RSI = 14
- SMA объёма = 20
- Стоп-лосс = 1%
- Трейлинг-профит = 4%
- Индикаторы: MA, RSI, MACD, SMA объёма
- Таймфрейм: 5-минутные свечи по умолчанию
using System;
using System.Linq;
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 MaxProfitMinLossOptionsStrategy : Strategy
{
private readonly StrategyParam<int> _fastLength;
private readonly StrategyParam<int> _slowLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<decimal> _stopLossPerc;
private readonly StrategyParam<decimal> _trailProfitPerc;
private readonly StrategyParam<decimal> _minTrendPercent;
private readonly StrategyParam<int> _signalCooldownBars;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _maFast;
private ExponentialMovingAverage _maSlow;
private RelativeStrengthIndex _rsi;
private decimal _entryPrice;
private decimal _highestPrice;
private decimal _lowestPrice;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
private int _barsFromSignal;
public int FastLength { get => _fastLength.Value; set => _fastLength.Value = value; }
public int SlowLength { get => _slowLength.Value; set => _slowLength.Value = value; }
public int RsiLength { get => _rsiLength.Value; set => _rsiLength.Value = value; }
public decimal StopLossPerc { get => _stopLossPerc.Value; set => _stopLossPerc.Value = value; }
public decimal TrailProfitPerc { get => _trailProfitPerc.Value; set => _trailProfitPerc.Value = value; }
public decimal MinTrendPercent { get => _minTrendPercent.Value; set => _minTrendPercent.Value = value; }
public int SignalCooldownBars { get => _signalCooldownBars.Value; set => _signalCooldownBars.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public MaxProfitMinLossOptionsStrategy()
{
_fastLength = Param(nameof(FastLength), 12)
.SetGreaterThanZero()
.SetDisplay("Fast Length", "Fast EMA period", "General");
_slowLength = Param(nameof(SlowLength), 48)
.SetGreaterThanZero()
.SetDisplay("Slow Length", "Slow EMA period", "General");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "General");
_stopLossPerc = Param(nameof(StopLossPerc), 1.5m)
.SetGreaterThanZero()
.SetDisplay("Stop Loss %", "Hard stop loss percent", "General");
_trailProfitPerc = Param(nameof(TrailProfitPerc), 5m)
.SetGreaterThanZero()
.SetDisplay("Trail Profit %", "Trailing exit percent", "General");
_minTrendPercent = Param(nameof(MinTrendPercent), 0.20m)
.SetGreaterThanZero()
.SetDisplay("Min Trend %", "Minimum EMA spread in percent", "General");
_signalCooldownBars = Param(nameof(SignalCooldownBars), 10)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown Bars", "Minimum bars between entries", "General");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Candles timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_maFast = null;
_maSlow = null;
_rsi = null;
_entryPrice = 0m;
_highestPrice = 0m;
_lowestPrice = 0m;
_prevFast = 0m;
_prevSlow = 0m;
_hasPrev = false;
_barsFromSignal = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
_maFast = new ExponentialMovingAverage { Length = FastLength };
_maSlow = new ExponentialMovingAverage { Length = SlowLength };
_rsi = new RelativeStrengthIndex { Length = RsiLength };
_entryPrice = 0;
_highestPrice = 0;
_lowestPrice = decimal.MaxValue;
_prevFast = 0m;
_prevSlow = 0m;
_hasPrev = false;
_barsFromSignal = SignalCooldownBars;
var subscription = SubscribeCandles(CandleType);
subscription.Bind(_maFast, _maSlow, _rsi, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal maFast, decimal maSlow, decimal rsi)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (!_maFast.IsFormed || !_maSlow.IsFormed || !_rsi.IsFormed)
return;
var crossedUp = _hasPrev && _prevFast <= _prevSlow && maFast > maSlow;
var crossedDown = _hasPrev && _prevFast >= _prevSlow && maFast < maSlow;
_hasPrev = true;
_prevFast = maFast;
_prevSlow = maSlow;
var close = candle.ClosePrice;
if (close <= 0m)
return;
var trendPercent = Math.Abs(maFast - maSlow) / close * 100m;
_barsFromSignal++;
if (_barsFromSignal >= SignalCooldownBars && crossedUp && rsi >= 55m && trendPercent >= MinTrendPercent && Position <= 0)
{
BuyMarket();
_entryPrice = close;
_highestPrice = close;
_barsFromSignal = 0;
}
else if (_barsFromSignal >= SignalCooldownBars && crossedDown && rsi <= 45m && trendPercent >= MinTrendPercent && Position >= 0)
{
SellMarket();
_entryPrice = close;
_lowestPrice = close;
_barsFromSignal = 0;
}
if (Position > 0)
{
_highestPrice = Math.Max(_highestPrice, close);
var stop = _entryPrice * (1m - StopLossPerc / 100m);
var trail = _highestPrice * (1m - TrailProfitPerc / 100m);
var exit = Math.Max(stop, trail);
if (close <= exit)
SellMarket();
}
else if (Position < 0)
{
_lowestPrice = Math.Min(_lowestPrice, close);
var stop = _entryPrice * (1m + StopLossPerc / 100m);
var trail = _lowestPrice * (1m + TrailProfitPerc / 100m);
var exit = Math.Min(stop, trail);
if (close >= exit)
BuyMarket();
}
}
}
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, RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class max_profit_min_loss_options_strategy(Strategy):
def __init__(self):
super(max_profit_min_loss_options_strategy, self).__init__()
self._fast_length = self.Param("FastLength", 12) \
.SetGreaterThanZero() \
.SetDisplay("Fast Length", "Fast EMA period", "General")
self._slow_length = self.Param("SlowLength", 48) \
.SetGreaterThanZero() \
.SetDisplay("Slow Length", "Slow EMA period", "General")
self._rsi_length = self.Param("RsiLength", 14) \
.SetGreaterThanZero() \
.SetDisplay("RSI Length", "RSI period", "General")
self._stop_loss_perc = self.Param("StopLossPerc", 1.5) \
.SetGreaterThanZero() \
.SetDisplay("Stop Loss %", "Hard stop loss percent", "General")
self._trail_profit_perc = self.Param("TrailProfitPerc", 5.0) \
.SetGreaterThanZero() \
.SetDisplay("Trail Profit %", "Trailing exit percent", "General")
self._min_trend_percent = self.Param("MinTrendPercent", 0.20) \
.SetGreaterThanZero() \
.SetDisplay("Min Trend %", "Minimum EMA spread in percent", "General")
self._signal_cooldown_bars = self.Param("SignalCooldownBars", 10) \
.SetGreaterThanZero() \
.SetDisplay("Signal Cooldown Bars", "Minimum bars between entries", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Candles timeframe", "General")
self._entry_price = 0.0
self._highest_price = 0.0
self._lowest_price = 999999999.0
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
self._bars_from_signal = 0
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(max_profit_min_loss_options_strategy, self).OnReseted()
self._entry_price = 0.0
self._highest_price = 0.0
self._lowest_price = 999999999.0
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
self._bars_from_signal = 0
def OnStarted2(self, time):
super(max_profit_min_loss_options_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._highest_price = 0.0
self._lowest_price = 999999999.0
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
self._bars_from_signal = self._signal_cooldown_bars.Value
self._ma_fast = ExponentialMovingAverage()
self._ma_fast.Length = self._fast_length.Value
self._ma_slow = ExponentialMovingAverage()
self._ma_slow.Length = self._slow_length.Value
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self._rsi_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._ma_fast, self._ma_slow, self._rsi, self.OnProcess).Start()
def OnProcess(self, candle, ma_fast, ma_slow, rsi):
if candle.State != CandleStates.Finished:
return
if not self._ma_fast.IsFormed or not self._ma_slow.IsFormed or not self._rsi.IsFormed:
return
fv = float(ma_fast)
sv = float(ma_slow)
rv = float(rsi)
crossed_up = self._has_prev and self._prev_fast <= self._prev_slow and fv > sv
crossed_down = self._has_prev and self._prev_fast >= self._prev_slow and fv < sv
self._has_prev = True
self._prev_fast = fv
self._prev_slow = sv
close = float(candle.ClosePrice)
if close <= 0.0:
return
trend_percent = abs(fv - sv) / close * 100.0
min_trend = float(self._min_trend_percent.Value)
self._bars_from_signal += 1
cd = self._signal_cooldown_bars.Value
if self._bars_from_signal >= cd and crossed_up and rv >= 55.0 and trend_percent >= min_trend and self.Position <= 0:
self.BuyMarket()
self._entry_price = close
self._highest_price = close
self._bars_from_signal = 0
elif self._bars_from_signal >= cd and crossed_down and rv <= 45.0 and trend_percent >= min_trend and self.Position >= 0:
self.SellMarket()
self._entry_price = close
self._lowest_price = close
self._bars_from_signal = 0
sl_pct = float(self._stop_loss_perc.Value) / 100.0
tp_pct = float(self._trail_profit_perc.Value) / 100.0
if self.Position > 0:
if close > self._highest_price:
self._highest_price = close
stop = self._entry_price * (1.0 - sl_pct)
trail = self._highest_price * (1.0 - tp_pct)
exit_p = max(stop, trail)
if close <= exit_p:
self.SellMarket()
elif self.Position < 0:
if close < self._lowest_price:
self._lowest_price = close
stop = self._entry_price * (1.0 + sl_pct)
trail = self._lowest_price * (1.0 + tp_pct)
exit_p = min(stop, trail)
if close >= exit_p:
self.BuyMarket()
def CreateClone(self):
return max_profit_min_loss_options_strategy()