IU Higher Timeframe MA Cross Strategy
Стратегия IU Higher Timeframe MA Cross открывает сделки при пересечении быстрой скользящей средней, рассчитанной на выбранном таймфрейме, и медленной скользящей средней другого таймфрейма. Лонг открывается при пересечении вверх, шорт — при пересечении вниз. Стоп-лосс устанавливается на экстремуме предыдущей свечи, тейк-профит рассчитывается по заданному соотношению риск/прибыль.
Подробности
- Данные: свечи указанных таймфреймов.
- Условия входа:
- Лонг: MA1 пересекает MA2 снизу вверх.
- Шорт: MA1 пересекает MA2 сверху вниз.
- Условия выхода: достижение стоп-лосса или тейк-профита.
- Стопы: экстремум предыдущей свечи с множителем
RiskToReward. - Параметры по умолчанию:
Ma1CandleType= 60mMa1Length= 20Ma1Type= MovingAverageTypeEnum.ExponentialMa2CandleType= 60mMa2Length= 50Ma2Type= MovingAverageTypeEnum.ExponentialRiskToReward= 2
- Фильтры:
- Категория: тренд
- Направление: лонг и шорт
- Индикаторы: Moving Average
- Сложность: низкая
- Уровень риска: средний
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;
using StockSharp.Algo;
namespace StockSharp.Samples.Strategies;
public class IUHigherTimeframeMACrossStrategy : Strategy
{
private readonly StrategyParam<decimal> _riskToReward;
private readonly StrategyParam<DataType> _ma1CandleType;
private readonly StrategyParam<int> _ma1Length;
private readonly StrategyParam<MovingAverageTypes> _ma1Type;
private readonly StrategyParam<DataType> _ma2CandleType;
private readonly StrategyParam<int> _ma2Length;
private readonly StrategyParam<MovingAverageTypes> _ma2Type;
private decimal? _ma1;
private decimal? _ma2;
private decimal? _prevMa1;
private decimal? _prevMa2;
private decimal? _entryPrice;
private decimal? _stopPrice;
private decimal? _takePrice;
private decimal? _prevLow;
private decimal? _prevHigh;
private ICandleMessage _lastMa1Candle;
private DecimalLengthIndicator _ma1Indicator;
private DecimalLengthIndicator _ma2Indicator;
public IUHigherTimeframeMACrossStrategy()
{
_riskToReward = Param(nameof(RiskToReward), 2m)
.SetGreaterThanZero()
.SetDisplay("RTR", "Risk to reward ratio", "Protection")
.SetOptimize(1m, 5m, 0.5m);
_ma1CandleType = Param(nameof(Ma1CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("MA1 Timeframe", "Timeframe for first MA", "Moving Averages");
_ma1Length = Param(nameof(Ma1Length), 20)
.SetGreaterThanZero()
.SetDisplay("MA1 Length", "Period for first MA", "Moving Averages")
.SetOptimize(5, 100, 5);
_ma1Type = Param(nameof(Ma1Type), MovingAverageTypes.Exponential)
.SetDisplay("MA1 Type", "Type of first MA", "Moving Averages");
_ma2CandleType = Param(nameof(Ma2CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("MA2 Timeframe", "Timeframe for second MA", "Moving Averages");
_ma2Length = Param(nameof(Ma2Length), 50)
.SetGreaterThanZero()
.SetDisplay("MA2 Length", "Period for second MA", "Moving Averages")
.SetOptimize(10, 200, 5);
_ma2Type = Param(nameof(Ma2Type), MovingAverageTypes.Exponential)
.SetDisplay("MA2 Type", "Type of second MA", "Moving Averages");
}
public decimal RiskToReward { get => _riskToReward.Value; set => _riskToReward.Value = value; }
public DataType Ma1CandleType { get => _ma1CandleType.Value; set => _ma1CandleType.Value = value; }
public int Ma1Length { get => _ma1Length.Value; set => _ma1Length.Value = value; }
public MovingAverageTypes Ma1Type { get => _ma1Type.Value; set => _ma1Type.Value = value; }
public DataType Ma2CandleType { get => _ma2CandleType.Value; set => _ma2CandleType.Value = value; }
public int Ma2Length { get => _ma2Length.Value; set => _ma2Length.Value = value; }
public MovingAverageTypes Ma2Type { get => _ma2Type.Value; set => _ma2Type.Value = value; }
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, Ma1CandleType), (Security, Ma2CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ma1 = null;
_ma2 = null;
_prevMa1 = null;
_prevMa2 = null;
_entryPrice = null;
_stopPrice = null;
_takePrice = null;
_prevLow = null;
_prevHigh = null;
_lastMa1Candle = null;
_ma1Indicator = null;
_ma2Indicator = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ma1Indicator = CreateMa(Ma1Type, Ma1Length);
_ma2Indicator = CreateMa(Ma2Type, Ma2Length);
var ma1Sub = SubscribeCandles(Ma1CandleType);
ma1Sub.Bind(_ma1Indicator, ProcessMa1).Start();
var ma2Sub = SubscribeCandles(Ma2CandleType);
ma2Sub.Bind(_ma2Indicator, ProcessMa2).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, ma1Sub);
DrawIndicator(area, _ma1Indicator);
DrawIndicator(area, _ma2Indicator);
DrawOwnTrades(area);
}
}
private void ProcessMa1(ICandleMessage candle, decimal value)
{
if (candle.State != CandleStates.Finished)
return;
if (_lastMa1Candle != null)
{
_prevLow = _lastMa1Candle.LowPrice;
_prevHigh = _lastMa1Candle.HighPrice;
}
_lastMa1Candle = candle;
_prevMa1 = _ma1;
_ma1 = value;
if (Position > 0)
{
if (_stopPrice.HasValue && candle.LowPrice <= _stopPrice)
CloseLong();
else if (_takePrice.HasValue && candle.HighPrice >= _takePrice)
CloseLong();
}
else if (Position < 0)
{
if (_stopPrice.HasValue && candle.HighPrice >= _stopPrice)
CloseShort();
else if (_takePrice.HasValue && candle.LowPrice <= _takePrice)
CloseShort();
}
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (_prevMa1.HasValue && _prevMa2.HasValue && _ma1.HasValue && _ma2.HasValue && Position == 0)
{
var crossUp = _prevMa1 < _prevMa2 && _ma1 > _ma2;
var crossDown = _prevMa1 > _prevMa2 && _ma1 < _ma2;
if (crossUp)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
var sl = _prevLow ?? candle.LowPrice;
_stopPrice = sl;
_takePrice = (_entryPrice - sl) * RiskToReward + _entryPrice;
}
else if (crossDown)
{
SellMarket();
_entryPrice = candle.ClosePrice;
var sl = _prevHigh ?? candle.HighPrice;
_stopPrice = sl;
_takePrice = _entryPrice - (sl - _entryPrice) * RiskToReward;
}
}
}
private void ProcessMa2(ICandleMessage candle, decimal value)
{
if (candle.State != CandleStates.Finished)
return;
_prevMa2 = _ma2;
_ma2 = value;
}
private void CloseLong()
{
SellMarket(Position);
ResetProtection();
}
private void CloseShort()
{
BuyMarket(Math.Abs(Position));
ResetProtection();
}
private void ResetProtection()
{
_entryPrice = null;
_stopPrice = null;
_takePrice = null;
}
private static DecimalLengthIndicator CreateMa(MovingAverageTypes type, int length)
{
return type switch
{
MovingAverageTypes.Simple => new SMA { Length = length },
MovingAverageTypes.Exponential => new EMA { Length = length },
MovingAverageTypes.Smoothed => new SmoothedMovingAverage { Length = length },
MovingAverageTypes.Weighted => new WeightedMovingAverage { Length = length },
MovingAverageTypes.VolumeWeighted => new VolumeWeightedMovingAverage { Length = length },
_ => new SMA { Length = length },
};
}
public enum MovingAverageTypes
{
Simple,
Exponential,
Smoothed,
Weighted,
VolumeWeighted
}
}
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 as SMA, ExponentialMovingAverage as EMA, SmoothedMovingAverage, WeightedMovingAverage, VolumeWeightedMovingAverage
from StockSharp.Algo.Strategies import Strategy
class iu_higher_timeframe_ma_cross_strategy(Strategy):
def __init__(self):
super(iu_higher_timeframe_ma_cross_strategy, self).__init__()
self._risk_to_reward = self.Param("RiskToReward", 2.0) \
.SetDisplay("RTR", "Risk to reward ratio", "Protection")
self._ma1_candle_type = self.Param("Ma1CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60))) \
.SetDisplay("MA1 Timeframe", "Timeframe for first MA", "Moving Averages")
self._ma1_length = self.Param("Ma1Length", 20) \
.SetDisplay("MA1 Length", "Period for first MA", "Moving Averages")
self._ma1_type = self.Param("Ma1Type", 1) \
.SetDisplay("MA1 Type", "Type of first MA (0=SMA,1=EMA,2=Smoothed,3=Weighted,4=VWMA)", "Moving Averages")
self._ma2_candle_type = self.Param("Ma2CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60))) \
.SetDisplay("MA2 Timeframe", "Timeframe for second MA", "Moving Averages")
self._ma2_length = self.Param("Ma2Length", 50) \
.SetDisplay("MA2 Length", "Period for second MA", "Moving Averages")
self._ma2_type = self.Param("Ma2Type", 1) \
.SetDisplay("MA2 Type", "Type of second MA (0=SMA,1=EMA,2=Smoothed,3=Weighted,4=VWMA)", "Moving Averages")
self._ma1 = None
self._ma2 = None
self._prev_ma1 = None
self._prev_ma2 = None
self._entry_price = None
self._stop_price = None
self._take_price = None
self._prev_low = None
self._prev_high = None
self._last_ma1_candle = None
@property
def risk_to_reward(self):
return self._risk_to_reward.Value
@property
def ma1_candle_type(self):
return self._ma1_candle_type.Value
@property
def ma1_length(self):
return self._ma1_length.Value
@property
def ma2_candle_type(self):
return self._ma2_candle_type.Value
@property
def ma2_length(self):
return self._ma2_length.Value
def _create_ma(self, ma_type, length):
t = int(ma_type)
if t == 0:
ma = SMA()
elif t == 1:
ma = EMA()
elif t == 2:
ma = SmoothedMovingAverage()
elif t == 3:
ma = WeightedMovingAverage()
elif t == 4:
ma = VolumeWeightedMovingAverage()
else:
ma = SMA()
ma.Length = length
return ma
def OnReseted(self):
super(iu_higher_timeframe_ma_cross_strategy, self).OnReseted()
self._ma1 = None
self._ma2 = None
self._prev_ma1 = None
self._prev_ma2 = None
self._entry_price = None
self._stop_price = None
self._take_price = None
self._prev_low = None
self._prev_high = None
self._last_ma1_candle = None
def OnStarted2(self, time):
super(iu_higher_timeframe_ma_cross_strategy, self).OnStarted2(time)
ma1_ind = self._create_ma(self._ma1_type.Value, self.ma1_length)
ma2_ind = self._create_ma(self._ma2_type.Value, self.ma2_length)
ma1_sub = self.SubscribeCandles(self.ma1_candle_type)
ma1_sub.Bind(ma1_ind, self._process_ma1).Start()
ma2_sub = self.SubscribeCandles(self.ma2_candle_type)
ma2_sub.Bind(ma2_ind, self._process_ma2).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, ma1_sub)
self.DrawIndicator(area, ma1_ind)
self.DrawIndicator(area, ma2_ind)
self.DrawOwnTrades(area)
def _process_ma1(self, candle, value):
if candle.State != CandleStates.Finished:
return
if self._last_ma1_candle is not None:
self._prev_low = float(self._last_ma1_candle.LowPrice)
self._prev_high = float(self._last_ma1_candle.HighPrice)
self._last_ma1_candle = candle
self._prev_ma1 = self._ma1
self._ma1 = float(value)
pos = self.Position
if pos > 0:
if self._stop_price is not None and float(candle.LowPrice) <= self._stop_price:
self.SellMarket(pos)
self._reset_protection()
elif self._take_price is not None and float(candle.HighPrice) >= self._take_price:
self.SellMarket(pos)
self._reset_protection()
elif pos < 0:
if self._stop_price is not None and float(candle.HighPrice) >= self._stop_price:
self.BuyMarket(abs(pos))
self._reset_protection()
elif self._take_price is not None and float(candle.LowPrice) <= self._take_price:
self.BuyMarket(abs(pos))
self._reset_protection()
if self._prev_ma1 is not None and self._prev_ma2 is not None and self._ma1 is not None and self._ma2 is not None and self.Position == 0:
cross_up = self._prev_ma1 < self._prev_ma2 and self._ma1 > self._ma2
cross_down = self._prev_ma1 > self._prev_ma2 and self._ma1 < self._ma2
if cross_up:
self.BuyMarket()
self._entry_price = float(candle.ClosePrice)
sl = self._prev_low if self._prev_low is not None else float(candle.LowPrice)
self._stop_price = sl
self._take_price = (self._entry_price - sl) * float(self.risk_to_reward) + self._entry_price
elif cross_down:
self.SellMarket()
self._entry_price = float(candle.ClosePrice)
sl = self._prev_high if self._prev_high is not None else float(candle.HighPrice)
self._stop_price = sl
self._take_price = self._entry_price - (sl - self._entry_price) * float(self.risk_to_reward)
def _process_ma2(self, candle, value):
if candle.State != CandleStates.Finished:
return
self._prev_ma2 = self._ma2
self._ma2 = float(value)
def _reset_protection(self):
self._entry_price = None
self._stop_price = None
self._take_price = None
def CreateClone(self):
return iu_higher_timeframe_ma_cross_strategy()