IU Higher Timeframe MA Cross Strategy
IU Higher Timeframe MA Cross Strategy trades when a fast moving average computed on a user-selected timeframe crosses a slower moving average from possibly another timeframe. A long position opens on a bullish cross and a short position on a bearish cross. Stop loss is placed at the previous candle's extreme, and take profit uses a configurable risk-to-reward ratio.
Details
- Data: Candles from specified timeframes.
- Entry Criteria:
- Long: MA1 crosses above MA2.
- Short: MA1 crosses below MA2.
- Exit Criteria: Stop loss or take profit hit.
- Stops: Previous candle high/low with
RiskToRewardmultiplier. - Default Values:
Ma1CandleType= 60mMa1Length= 20Ma1Type= MovingAverageTypeEnum.ExponentialMa2CandleType= 60mMa2Length= 50Ma2Type= MovingAverageTypeEnum.ExponentialRiskToReward= 2
- Filters:
- Category: Trend
- Direction: Long & Short
- Indicators: Moving Average
- Complexity: Low
- Risk level: Medium
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()