Стратегия Range EA
Стратегия торгует отклонения цены от скользящей средней на фиксированном расстоянии. Открывает длинные или короткие позиции, когда цена смещается на заданную величину. Поддерживает трейлинг-стоп, модуль усреднения, разворот и ограничение по времени торговли.
Детали
- Условия входа:
- Long: цена закрытия выше скользящей средней +
Range - Short: цена закрытия ниже скользящей средней -
Range
- Long: цена закрытия выше скользящей средней +
- Направление: Оба
- Условия выхода:
- Достижение
TakeProfitилиStopLoss - Срабатывание трейлинг-стопа, если включён
- Опциональный разворот после движения на
Turn
- Достижение
- Стопы: фиксированное значение
- Значения по умолчанию:
MaLength= 21Range= 250mTakeProfit= 500mStopLoss= 250mUseTrailingStop= trueTrailingStop= 250mUseTurn= trueTurn= 250mLotMultiplicator= 1.65mTurnTakeProfit= 500mUseStepDown= falseStepDown= 150mUseTradeTime= falseOpenTradeTime= 08:00:00CloseTradeTime= 21:30:00OrderVolume= 0.1mCandleType= TimeSpan.FromMinutes(5).TimeFrame()
- Фильтры:
- Категория: Range
- Направление: Оба
- Индикаторы: MA
- Стопы: Да
- Сложность: Средняя
- Таймфрейм: Внутридневной
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Нет
- Уровень риска: Средний
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>
/// Range breakout strategy with optional step-down averaging and turn reversal.
/// </summary>
public class RangeEaStrategy : Strategy
{
private readonly StrategyParam<int> _maLength;
private readonly StrategyParam<decimal> _range;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<bool> _useTrailingStop;
private readonly StrategyParam<decimal> _trailingStop;
private readonly StrategyParam<bool> _useTurn;
private readonly StrategyParam<decimal> _turn;
private readonly StrategyParam<decimal> _lotMultiplicator;
private readonly StrategyParam<decimal> _turnTakeProfit;
private readonly StrategyParam<bool> _useStepDown;
private readonly StrategyParam<decimal> _stepDown;
private readonly StrategyParam<bool> _useTradeTime;
private readonly StrategyParam<TimeSpan> _openTradeTime;
private readonly StrategyParam<TimeSpan> _closeTradeTime;
private readonly StrategyParam<decimal> _orderVolume;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private decimal _stopPrice;
private decimal _takeProfitPrice;
private decimal _nextStepPrice;
private decimal _turnPrice;
public int MaLength
{
get => _maLength.Value;
set => _maLength.Value = value;
}
public decimal Range
{
get => _range.Value;
set => _range.Value = value;
}
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
public bool UseTrailingStop
{
get => _useTrailingStop.Value;
set => _useTrailingStop.Value = value;
}
public decimal TrailingStop
{
get => _trailingStop.Value;
set => _trailingStop.Value = value;
}
public bool UseTurn
{
get => _useTurn.Value;
set => _useTurn.Value = value;
}
public decimal Turn
{
get => _turn.Value;
set => _turn.Value = value;
}
public decimal LotMultiplicator
{
get => _lotMultiplicator.Value;
set => _lotMultiplicator.Value = value;
}
public decimal TurnTakeProfit
{
get => _turnTakeProfit.Value;
set => _turnTakeProfit.Value = value;
}
public bool UseStepDown
{
get => _useStepDown.Value;
set => _useStepDown.Value = value;
}
public decimal StepDown
{
get => _stepDown.Value;
set => _stepDown.Value = value;
}
public bool UseTradeTime
{
get => _useTradeTime.Value;
set => _useTradeTime.Value = value;
}
public TimeSpan OpenTradeTime
{
get => _openTradeTime.Value;
set => _openTradeTime.Value = value;
}
public TimeSpan CloseTradeTime
{
get => _closeTradeTime.Value;
set => _closeTradeTime.Value = value;
}
public decimal OrderVolume
{
get => _orderVolume.Value;
set => _orderVolume.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public RangeEaStrategy()
{
_maLength = Param(nameof(MaLength), 21)
.SetDisplay("MA Length", "Moving average period", "Parameters")
.SetOptimize(10, 50, 5);
_range = Param(nameof(Range), 2500m)
.SetDisplay("Range", "Price range from MA", "Parameters")
.SetOptimize(50m, 500m, 50m);
_takeProfit = Param(nameof(TakeProfit), 3000m)
.SetDisplay("Take Profit", "Fixed take profit", "Parameters");
_stopLoss = Param(nameof(StopLoss), 1500m)
.SetDisplay("Stop Loss", "Fixed stop loss", "Parameters");
_useTrailingStop = Param(nameof(UseTrailingStop), false)
.SetDisplay("Use Trailing", "Enable trailing stop", "Parameters");
_trailingStop = Param(nameof(TrailingStop), 250m)
.SetDisplay("Trailing", "Trailing stop distance", "Parameters");
_useTurn = Param(nameof(UseTurn), false)
.SetDisplay("Use Turn", "Enable reversal module", "Parameters");
_turn = Param(nameof(Turn), 250m)
.SetDisplay("Turn", "Reversal distance", "Parameters");
_lotMultiplicator = Param(nameof(LotMultiplicator), 1.65m)
.SetDisplay("Lot Mult", "Volume multiplier for reversal", "Parameters");
_turnTakeProfit = Param(nameof(TurnTakeProfit), 500m)
.SetDisplay("Turn TP", "Take profit after reversal", "Parameters");
_useStepDown = Param(nameof(UseStepDown), false)
.SetDisplay("Use StepDown", "Enable averaging module", "Parameters");
_stepDown = Param(nameof(StepDown), 150m)
.SetDisplay("Step Down", "Averaging step", "Parameters");
_useTradeTime = Param(nameof(UseTradeTime), false)
.SetDisplay("Use Trade Time", "Limit trading hours", "Parameters");
_openTradeTime = Param(nameof(OpenTradeTime), TimeSpan.Parse("08:00:00"))
.SetDisplay("Open Time", "Trading start time", "Parameters");
_closeTradeTime = Param(nameof(CloseTradeTime), TimeSpan.Parse("21:30:00"))
.SetDisplay("Close Time", "Trading end time", "Parameters");
_orderVolume = Param(nameof(OrderVolume), 0.1m)
.SetDisplay("Volume", "Order volume", "Parameters");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "Parameters");
}
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_stopPrice = 0;
_takeProfitPrice = 0;
_nextStepPrice = 0;
_turnPrice = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = default;
_stopPrice = default;
_takeProfitPrice = default;
_nextStepPrice = default;
_turnPrice = default;
var ma = new SimpleMovingAverage
{
Length = MaLength
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ma, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal maValue)
{
if (candle.State != CandleStates.Finished)
return;
var time = candle.OpenTime.TimeOfDay;
if (UseTradeTime)
{
if (time < OpenTradeTime || time > CloseTradeTime)
{
if (Position != 0)
ClosePosition();
return;
}
}
var price = candle.ClosePrice;
// Manage existing long position
if (Position > 0)
{
if (price >= _takeProfitPrice || price <= _stopPrice)
{
ClosePosition();
return;
}
if (UseTrailingStop && price - _entryPrice >= TrailingStop)
_stopPrice = Math.Max(_stopPrice, price - TrailingStop);
if (UseTurn && price <= _turnPrice)
{
ClosePosition();
SellMarket(OrderVolume * LotMultiplicator);
_entryPrice = price;
_stopPrice = price + StopLoss;
_takeProfitPrice = price - TurnTakeProfit;
if (UseStepDown)
_nextStepPrice = _entryPrice + StepDown;
if (UseTurn)
_turnPrice = _entryPrice + Turn;
return;
}
if (UseStepDown && price <= _nextStepPrice)
{
BuyMarket(OrderVolume);
_nextStepPrice -= StepDown;
}
}
// Manage existing short position
else if (Position < 0)
{
if (price <= _takeProfitPrice || price >= _stopPrice)
{
ClosePosition();
return;
}
if (UseTrailingStop && _entryPrice - price >= TrailingStop)
_stopPrice = Math.Min(_stopPrice, price + TrailingStop);
if (UseTurn && price >= _turnPrice)
{
ClosePosition();
BuyMarket(OrderVolume * LotMultiplicator);
_entryPrice = price;
_stopPrice = price - StopLoss;
_takeProfitPrice = price + TurnTakeProfit;
if (UseStepDown)
_nextStepPrice = _entryPrice - StepDown;
if (UseTurn)
_turnPrice = _entryPrice - Turn;
return;
}
if (UseStepDown && price >= _nextStepPrice)
{
SellMarket(OrderVolume);
_nextStepPrice += StepDown;
}
}
else
{
// Entry conditions when flat
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (price >= maValue + Range)
{
BuyMarket(OrderVolume);
_entryPrice = price;
_stopPrice = price - StopLoss;
_takeProfitPrice = price + TakeProfit;
if (UseStepDown)
_nextStepPrice = _entryPrice - StepDown;
if (UseTurn)
_turnPrice = _entryPrice - Turn;
}
else if (price <= maValue - Range)
{
SellMarket(OrderVolume);
_entryPrice = price;
_stopPrice = price + StopLoss;
_takeProfitPrice = price - TakeProfit;
if (UseStepDown)
_nextStepPrice = _entryPrice + StepDown;
if (UseTurn)
_turnPrice = _entryPrice + Turn;
}
}
}
private void ClosePosition()
{
if (Position > 0)
SellMarket(Position);
else if (Position < 0)
BuyMarket(-Position);
}
}
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
from StockSharp.Algo.Indicators import SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class range_ea_strategy(Strategy):
def __init__(self):
super(range_ea_strategy, self).__init__()
self._ma_length = self.Param("MaLength", 21) \
.SetDisplay("MA Length", "Moving average period", "Parameters")
self._range = self.Param("Range", 2500.0) \
.SetDisplay("Range", "Price range from MA", "Parameters")
self._take_profit = self.Param("TakeProfit", 3000.0) \
.SetDisplay("Take Profit", "Fixed take profit", "Parameters")
self._stop_loss = self.Param("StopLoss", 1500.0) \
.SetDisplay("Stop Loss", "Fixed stop loss", "Parameters")
self._use_trailing_stop = self.Param("UseTrailingStop", False) \
.SetDisplay("Use Trailing", "Enable trailing stop", "Parameters")
self._trailing_stop = self.Param("TrailingStop", 250.0) \
.SetDisplay("Trailing", "Trailing stop distance", "Parameters")
self._use_turn = self.Param("UseTurn", False) \
.SetDisplay("Use Turn", "Enable reversal module", "Parameters")
self._turn = self.Param("Turn", 250.0) \
.SetDisplay("Turn", "Reversal distance", "Parameters")
self._lot_multiplicator = self.Param("LotMultiplicator", 1.65) \
.SetDisplay("Lot Mult", "Volume multiplier for reversal", "Parameters")
self._turn_take_profit = self.Param("TurnTakeProfit", 500.0) \
.SetDisplay("Turn TP", "Take profit after reversal", "Parameters")
self._use_step_down = self.Param("UseStepDown", False) \
.SetDisplay("Use StepDown", "Enable averaging module", "Parameters")
self._step_down = self.Param("StepDown", 150.0) \
.SetDisplay("Step Down", "Averaging step", "Parameters")
self._use_trade_time = self.Param("UseTradeTime", False) \
.SetDisplay("Use Trade Time", "Limit trading hours", "Parameters")
self._open_trade_time = self.Param("OpenTradeTime", TimeSpan.Parse("08:00:00")) \
.SetDisplay("Open Time", "Trading start time", "Parameters")
self._close_trade_time = self.Param("CloseTradeTime", TimeSpan.Parse("21:30:00")) \
.SetDisplay("Close Time", "Trading end time", "Parameters")
self._order_volume = self.Param("OrderVolume", 0.1) \
.SetDisplay("Volume", "Order volume", "Parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "Parameters")
self._entry_price = 0.0
self._stop_price = 0.0
self._take_profit_price = 0.0
self._next_step_price = 0.0
self._turn_price = 0.0
@property
def MaLength(self):
return self._ma_length.Value
@MaLength.setter
def MaLength(self, value):
self._ma_length.Value = value
@property
def Range(self):
return self._range.Value
@Range.setter
def Range(self, value):
self._range.Value = value
@property
def TakeProfit(self):
return self._take_profit.Value
@TakeProfit.setter
def TakeProfit(self, value):
self._take_profit.Value = value
@property
def StopLoss(self):
return self._stop_loss.Value
@StopLoss.setter
def StopLoss(self, value):
self._stop_loss.Value = value
@property
def UseTrailingStop(self):
return self._use_trailing_stop.Value
@UseTrailingStop.setter
def UseTrailingStop(self, value):
self._use_trailing_stop.Value = value
@property
def TrailingStop(self):
return self._trailing_stop.Value
@TrailingStop.setter
def TrailingStop(self, value):
self._trailing_stop.Value = value
@property
def UseTurn(self):
return self._use_turn.Value
@UseTurn.setter
def UseTurn(self, value):
self._use_turn.Value = value
@property
def Turn(self):
return self._turn.Value
@Turn.setter
def Turn(self, value):
self._turn.Value = value
@property
def LotMultiplicator(self):
return self._lot_multiplicator.Value
@LotMultiplicator.setter
def LotMultiplicator(self, value):
self._lot_multiplicator.Value = value
@property
def TurnTakeProfit(self):
return self._turn_take_profit.Value
@TurnTakeProfit.setter
def TurnTakeProfit(self, value):
self._turn_take_profit.Value = value
@property
def UseStepDown(self):
return self._use_step_down.Value
@UseStepDown.setter
def UseStepDown(self, value):
self._use_step_down.Value = value
@property
def StepDown(self):
return self._step_down.Value
@StepDown.setter
def StepDown(self, value):
self._step_down.Value = value
@property
def UseTradeTime(self):
return self._use_trade_time.Value
@UseTradeTime.setter
def UseTradeTime(self, value):
self._use_trade_time.Value = value
@property
def OpenTradeTime(self):
return self._open_trade_time.Value
@OpenTradeTime.setter
def OpenTradeTime(self, value):
self._open_trade_time.Value = value
@property
def CloseTradeTime(self):
return self._close_trade_time.Value
@CloseTradeTime.setter
def CloseTradeTime(self, value):
self._close_trade_time.Value = value
@property
def OrderVolume(self):
return self._order_volume.Value
@OrderVolume.setter
def OrderVolume(self, value):
self._order_volume.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def _close_position(self):
pos = self.Position
if pos > 0:
self.SellMarket(pos)
elif pos < 0:
self.BuyMarket(-pos)
def OnStarted2(self, time):
super(range_ea_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._stop_price = 0.0
self._take_profit_price = 0.0
self._next_step_price = 0.0
self._turn_price = 0.0
ma = SimpleMovingAverage()
ma.Length = self.MaLength
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(ma, self.ProcessCandle) \
.Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def ProcessCandle(self, candle, ma_value):
if candle.State != CandleStates.Finished:
return
time_of_day = candle.OpenTime.TimeOfDay
if self.UseTradeTime:
if time_of_day < self.OpenTradeTime or time_of_day > self.CloseTradeTime:
if self.Position != 0:
self._close_position()
return
price = float(candle.ClosePrice)
ma_val = float(ma_value)
pos = self.Position
if pos > 0:
if price >= self._take_profit_price or price <= self._stop_price:
self._close_position()
return
if self.UseTrailingStop and price - self._entry_price >= float(self.TrailingStop):
self._stop_price = max(self._stop_price, price - float(self.TrailingStop))
if self.UseTurn and price <= self._turn_price:
self._close_position()
self.SellMarket(float(self.OrderVolume) * float(self.LotMultiplicator))
self._entry_price = price
self._stop_price = price + float(self.StopLoss)
self._take_profit_price = price - float(self.TurnTakeProfit)
if self.UseStepDown:
self._next_step_price = self._entry_price + float(self.StepDown)
if self.UseTurn:
self._turn_price = self._entry_price + float(self.Turn)
return
if self.UseStepDown and price <= self._next_step_price:
self.BuyMarket(self.OrderVolume)
self._next_step_price -= float(self.StepDown)
elif pos < 0:
if price <= self._take_profit_price or price >= self._stop_price:
self._close_position()
return
if self.UseTrailingStop and self._entry_price - price >= float(self.TrailingStop):
self._stop_price = min(self._stop_price, price + float(self.TrailingStop))
if self.UseTurn and price >= self._turn_price:
self._close_position()
self.BuyMarket(float(self.OrderVolume) * float(self.LotMultiplicator))
self._entry_price = price
self._stop_price = price - float(self.StopLoss)
self._take_profit_price = price + float(self.TurnTakeProfit)
if self.UseStepDown:
self._next_step_price = self._entry_price - float(self.StepDown)
if self.UseTurn:
self._turn_price = self._entry_price - float(self.Turn)
return
if self.UseStepDown and price >= self._next_step_price:
self.SellMarket(self.OrderVolume)
self._next_step_price += float(self.StepDown)
else:
if price >= ma_val + float(self.Range):
self.BuyMarket(self.OrderVolume)
self._entry_price = price
self._stop_price = price - float(self.StopLoss)
self._take_profit_price = price + float(self.TakeProfit)
if self.UseStepDown:
self._next_step_price = self._entry_price - float(self.StepDown)
if self.UseTurn:
self._turn_price = self._entry_price - float(self.Turn)
elif price <= ma_val - float(self.Range):
self.SellMarket(self.OrderVolume)
self._entry_price = price
self._stop_price = price + float(self.StopLoss)
self._take_profit_price = price - float(self.TakeProfit)
if self.UseStepDown:
self._next_step_price = self._entry_price + float(self.StepDown)
if self.UseTurn:
self._turn_price = self._entry_price + float(self.Turn)
def OnReseted(self):
super(range_ea_strategy, self).OnReseted()
self._entry_price = 0.0
self._stop_price = 0.0
self._take_profit_price = 0.0
self._next_step_price = 0.0
self._turn_price = 0.0
def CreateClone(self):
return range_ea_strategy()