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>
/// Crossing of two iMA strategy with optional third filter and trailing protection.
/// </summary>
public class CrossingOfTwoIMaV2Strategy : Strategy
{
private readonly StrategyParam<int> _firstPeriod;
private readonly StrategyParam<int> _firstShift;
private readonly StrategyParam<MaMethods> _firstMethod;
private readonly StrategyParam<AppliedPriceTypes> _firstPrice;
private readonly StrategyParam<int> _secondPeriod;
private readonly StrategyParam<int> _secondShift;
private readonly StrategyParam<MaMethods> _secondMethod;
private readonly StrategyParam<AppliedPriceTypes> _secondPrice;
private readonly StrategyParam<bool> _useFilter;
private readonly StrategyParam<int> _thirdPeriod;
private readonly StrategyParam<int> _thirdShift;
private readonly StrategyParam<MaMethods> _thirdMethod;
private readonly StrategyParam<AppliedPriceTypes> _thirdPrice;
private readonly StrategyParam<bool> _useRiskPercent;
private readonly StrategyParam<decimal> _fixedVolume;
private readonly StrategyParam<decimal> _riskPercent;
private readonly StrategyParam<decimal> _pipValue;
private readonly StrategyParam<int> _stopLossPips;
private readonly StrategyParam<int> _takeProfitPips;
private readonly StrategyParam<int> _trailingStopPips;
private readonly StrategyParam<int> _trailingStepPips;
private readonly StrategyParam<DataType> _candleType;
private IIndicator _firstMa;
private IIndicator _secondMa;
private IIndicator _thirdMa;
private decimal?[] _firstSeries = Array.Empty<decimal?>();
private decimal?[] _secondSeries = Array.Empty<decimal?>();
private decimal?[] _thirdSeries = Array.Empty<decimal?>();
private decimal? _longStopPrice;
private decimal? _shortStopPrice;
private decimal? _longTakeProfit;
private decimal? _shortTakeProfit;
private decimal? _longTrail;
private decimal? _shortTrail;
private decimal? _bestLongPrice;
private decimal? _bestShortPrice;
private int _barsSinceLastEntry;
/// <summary>
/// Period of the first moving average.
/// </summary>
public int FirstPeriod
{
get => _firstPeriod.Value;
set => _firstPeriod.Value = value;
}
/// <summary>
/// Shift of the first moving average in bars.
/// </summary>
public int FirstShift
{
get => _firstShift.Value;
set => _firstShift.Value = value;
}
/// <summary>
/// Smoothing method for the first moving average.
/// </summary>
public MaMethods FirstMethod
{
get => _firstMethod.Value;
set => _firstMethod.Value = value;
}
/// <summary>
/// Price source for the first moving average.
/// </summary>
public AppliedPriceTypes FirstAppliedPrice
{
get => _firstPrice.Value;
set => _firstPrice.Value = value;
}
/// <summary>
/// Period of the second moving average.
/// </summary>
public int SecondPeriod
{
get => _secondPeriod.Value;
set => _secondPeriod.Value = value;
}
/// <summary>
/// Shift of the second moving average in bars.
/// </summary>
public int SecondShift
{
get => _secondShift.Value;
set => _secondShift.Value = value;
}
/// <summary>
/// Smoothing method for the second moving average.
/// </summary>
public MaMethods SecondMethod
{
get => _secondMethod.Value;
set => _secondMethod.Value = value;
}
/// <summary>
/// Price source for the second moving average.
/// </summary>
public AppliedPriceTypes SecondAppliedPrice
{
get => _secondPrice.Value;
set => _secondPrice.Value = value;
}
/// <summary>
/// Enable the third moving average filter.
/// </summary>
public bool UseFilter
{
get => _useFilter.Value;
set => _useFilter.Value = value;
}
/// <summary>
/// Period of the third moving average filter.
/// </summary>
public int ThirdPeriod
{
get => _thirdPeriod.Value;
set => _thirdPeriod.Value = value;
}
/// <summary>
/// Shift of the third moving average filter in bars.
/// </summary>
public int ThirdShift
{
get => _thirdShift.Value;
set => _thirdShift.Value = value;
}
/// <summary>
/// Smoothing method for the third moving average filter.
/// </summary>
public MaMethods ThirdMethod
{
get => _thirdMethod.Value;
set => _thirdMethod.Value = value;
}
/// <summary>
/// Price source for the third moving average filter.
/// </summary>
public AppliedPriceTypes ThirdAppliedPrice
{
get => _thirdPrice.Value;
set => _thirdPrice.Value = value;
}
/// <summary>
/// Use risk percentage position sizing instead of fixed volume.
/// </summary>
public bool UseRiskPercent
{
get => _useRiskPercent.Value;
set => _useRiskPercent.Value = value;
}
/// <summary>
/// Fixed volume when risk percentage sizing is disabled.
/// </summary>
public decimal FixedVolume
{
get => _fixedVolume.Value;
set => _fixedVolume.Value = value;
}
/// <summary>
/// Percentage of equity risked per trade when risk sizing is enabled.
/// </summary>
public decimal RiskPercent
{
get => _riskPercent.Value;
set => _riskPercent.Value = value;
}
/// <summary>
/// Monetary value of one pip for a single lot.
/// </summary>
public decimal PipValue
{
get => _pipValue.Value;
set => _pipValue.Value = value;
}
/// <summary>
/// Stop loss size in pips.
/// </summary>
public int StopLossPips
{
get => _stopLossPips.Value;
set => _stopLossPips.Value = value;
}
/// <summary>
/// Take profit size in pips.
/// </summary>
public int TakeProfitPips
{
get => _takeProfitPips.Value;
set => _takeProfitPips.Value = value;
}
/// <summary>
/// Trailing stop size in pips.
/// </summary>
public int TrailingStopPips
{
get => _trailingStopPips.Value;
set => _trailingStopPips.Value = value;
}
/// <summary>
/// Minimum trailing adjustment step in pips.
/// </summary>
public int TrailingStepPips
{
get => _trailingStepPips.Value;
set => _trailingStepPips.Value = value;
}
/// <summary>
/// Candle type used to drive the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initialize <see cref="CrossingOfTwoIMaV2Strategy"/>.
/// </summary>
public CrossingOfTwoIMaV2Strategy()
{
_firstPeriod = Param(nameof(FirstPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("First MA Period", "Period of the first moving average", "First Moving Average")
;
_firstShift = Param(nameof(FirstShift), 0)
.SetNotNegative()
.SetDisplay("First MA Shift", "Shift (in bars) applied to the first moving average", "First Moving Average")
;
_firstMethod = Param(nameof(FirstMethod), MaMethods.Simple)
.SetDisplay("First MA Method", "Smoothing method for the first moving average", "First Moving Average")
;
_firstPrice = Param(nameof(FirstAppliedPrice), AppliedPriceTypes.Close)
.SetDisplay("First MA Price", "Price source for the first moving average", "First Moving Average");
_secondPeriod = Param(nameof(SecondPeriod), 8)
.SetGreaterThanZero()
.SetDisplay("Second MA Period", "Period of the second moving average", "Second Moving Average")
;
_secondShift = Param(nameof(SecondShift), 0)
.SetNotNegative()
.SetDisplay("Second MA Shift", "Shift (in bars) applied to the second moving average", "Second Moving Average")
;
_secondMethod = Param(nameof(SecondMethod), MaMethods.Simple)
.SetDisplay("Second MA Method", "Smoothing method for the second moving average", "Second Moving Average")
;
_secondPrice = Param(nameof(SecondAppliedPrice), AppliedPriceTypes.Close)
.SetDisplay("Second MA Price", "Price source for the second moving average", "Second Moving Average");
_useFilter = Param(nameof(UseFilter), false)
.SetDisplay("Enable Filter", "Use the third moving average as a directional filter", "Filter");
_thirdPeriod = Param(nameof(ThirdPeriod), 13)
.SetGreaterThanZero()
.SetDisplay("Third MA Period", "Period of the third moving average filter", "Filter")
;
_thirdShift = Param(nameof(ThirdShift), 0)
.SetNotNegative()
.SetDisplay("Third MA Shift", "Shift (in bars) applied to the third moving average filter", "Filter")
;
_thirdMethod = Param(nameof(ThirdMethod), MaMethods.Simple)
.SetDisplay("Third MA Method", "Smoothing method for the third moving average filter", "Filter")
;
_thirdPrice = Param(nameof(ThirdAppliedPrice), AppliedPriceTypes.Close)
.SetDisplay("Third MA Price", "Price source for the third moving average filter", "Filter");
_useRiskPercent = Param(nameof(UseRiskPercent), true)
.SetDisplay("Risk Based Sizing", "Use percentage risk position sizing", "Risk");
_fixedVolume = Param(nameof(FixedVolume), 0.1m)
.SetGreaterThanZero()
.SetDisplay("Fixed Volume", "Trade volume when fixed sizing is enabled", "Risk");
_riskPercent = Param(nameof(RiskPercent), 5m)
.SetGreaterThanZero()
.SetDisplay("Risk Percent", "Percentage of equity risked per trade", "Risk")
;
_pipValue = Param(nameof(PipValue), 1m)
.SetGreaterThanZero()
.SetDisplay("Pip Value", "Monetary value of one pip for a single lot", "Risk");
_stopLossPips = Param(nameof(StopLossPips), 50)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop loss distance in pips", "Protection");
_takeProfitPips = Param(nameof(TakeProfitPips), 50)
.SetNotNegative()
.SetDisplay("Take Profit", "Take profit distance in pips", "Protection");
_trailingStopPips = Param(nameof(TrailingStopPips), 10)
.SetNotNegative()
.SetDisplay("Trailing Stop", "Trailing stop distance in pips", "Protection");
_trailingStepPips = Param(nameof(TrailingStepPips), 4)
.SetNotNegative()
.SetDisplay("Trailing Step", "Minimum trailing stop adjustment in pips", "Protection");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Candle type used for analysis", "General");
_barsSinceLastEntry = int.MaxValue;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_firstMa = null;
_secondMa = null;
_thirdMa = null;
_firstSeries = Array.Empty<decimal?>();
_secondSeries = Array.Empty<decimal?>();
_thirdSeries = Array.Empty<decimal?>();
ResetTradeState();
_barsSinceLastEntry = int.MaxValue;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_firstMa = CreateMovingAverage(FirstMethod, FirstPeriod);
_secondMa = CreateMovingAverage(SecondMethod, SecondPeriod);
_thirdMa = UseFilter ? CreateMovingAverage(ThirdMethod, ThirdPeriod) : null;
_firstSeries = new decimal?[FirstShift + 3];
_secondSeries = new decimal?[SecondShift + 3];
_thirdSeries = UseFilter ? new decimal?[ThirdShift + 1] : Array.Empty<decimal?>();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
// Ignore unfinished candles to work on closed bars only.
if (candle.State != CandleStates.Finished)
return;
if (_barsSinceLastEntry < int.MaxValue)
_barsSinceLastEntry++;
// Manage open positions and exit if protections trigger.
if (UpdateRiskManagement(candle))
return;
var firstInput = GetAppliedPrice(candle, FirstAppliedPrice);
var secondInput = GetAppliedPrice(candle, SecondAppliedPrice);
var thirdInput = UseFilter ? GetAppliedPrice(candle, ThirdAppliedPrice) : (decimal?)null;
// Update indicator series with the latest values.
var firstValue = _firstMa?.Process(firstInput, candle.OpenTime, true);
ShiftSeries(_firstSeries, firstValue?.IsFinal == true ? firstValue.ToDecimal() : (decimal?)null);
var secondValue = _secondMa?.Process(secondInput, candle.OpenTime, true);
ShiftSeries(_secondSeries, secondValue?.IsFinal == true ? secondValue.ToDecimal() : (decimal?)null);
if (UseFilter && _thirdMa != null && _thirdSeries.Length > 0 && thirdInput.HasValue)
{
var thirdValue = _thirdMa.Process(new DecimalIndicatorValue(_thirdMa, thirdInput.Value, candle.OpenTime));
ShiftSeries(_thirdSeries, thirdValue.IsFinal ? thirdValue.ToDecimal() : (decimal?)null);
}
// Ensure we have enough data for crossover evaluation.
if (!HasSeriesValue(_firstSeries, FirstShift, 2) || !HasSeriesValue(_secondSeries, SecondShift, 2))
return;
var first0 = GetSeriesValue(_firstSeries, FirstShift, 0)!.Value;
var first1 = GetSeriesValue(_firstSeries, FirstShift, 1)!.Value;
var second0 = GetSeriesValue(_secondSeries, SecondShift, 0)!.Value;
var second1 = GetSeriesValue(_secondSeries, SecondShift, 1)!.Value;
var buySignal = first0 > second0 && first1 < second1;
var sellSignal = first0 < second0 && first1 > second1;
if (!buySignal && !sellSignal && HasSeriesValue(_firstSeries, FirstShift, 2) && HasSeriesValue(_secondSeries, SecondShift, 2))
{
var first2 = GetSeriesValue(_firstSeries, FirstShift, 2)!.Value;
var second2 = GetSeriesValue(_secondSeries, SecondShift, 2)!.Value;
if (first0 > second0 && first2 < second2 && _barsSinceLastEntry > 2)
{
buySignal = true;
}
else if (first0 < second0 && first2 > second2 && _barsSinceLastEntry > 2)
{
sellSignal = true;
}
}
if (UseFilter)
{
if (_thirdSeries.Length > 0 && HasSeriesValue(_thirdSeries, ThirdShift, 0))
{
var filterValue = GetSeriesValue(_thirdSeries, ThirdShift, 0)!.Value;
if (buySignal && filterValue >= first0)
buySignal = false;
if (sellSignal && filterValue <= first0)
sellSignal = false;
}
else if (buySignal || sellSignal)
{
return;
}
}
if (buySignal && Position <= 0)
{
EnterLong(candle);
}
else if (sellSignal && Position >= 0)
{
EnterShort(candle);
}
}
private void EnterLong(ICandleMessage candle)
{
var volume = GetEntryVolume();
if (volume <= 0m)
return;
if (Position < 0)
{
BuyMarket(Math.Abs(Position));
}
BuyMarket(volume);
SetLongProtection(candle.ClosePrice);
_barsSinceLastEntry = 0;
}
private void EnterShort(ICandleMessage candle)
{
var volume = GetEntryVolume();
if (volume <= 0m)
return;
if (Position > 0)
{
SellMarket(Math.Abs(Position));
}
SellMarket(volume);
SetShortProtection(candle.ClosePrice);
_barsSinceLastEntry = 0;
}
private decimal GetEntryVolume()
{
if (!UseRiskPercent)
return FixedVolume;
var equity = Portfolio?.CurrentValue ?? Portfolio?.BeginValue ?? 0m;
if (equity <= 0m)
return FixedVolume;
var riskAmount = equity * RiskPercent / 100m;
var riskPips = StopLossPips > 0 ? StopLossPips : TrailingStopPips;
if (riskPips <= 0)
return FixedVolume;
if (PipValue <= 0m)
return FixedVolume;
var volume = riskAmount / (riskPips * PipValue);
return volume > 0m ? volume : FixedVolume;
}
private bool UpdateRiskManagement(ICandleMessage candle)
{
var point = GetPointValue();
var trailingStep = TrailingStepPips > 0 ? TrailingStepPips * point : 0m;
if (Position > 0)
{
_bestLongPrice = _bestLongPrice.HasValue ? Math.Max(_bestLongPrice.Value, candle.HighPrice) : candle.HighPrice;
if (TrailingStopPips > 0 && _bestLongPrice.HasValue)
{
var desiredStop = _bestLongPrice.Value - TrailingStopPips * point;
if (!_longTrail.HasValue || desiredStop - _longTrail.Value >= trailingStep)
_longTrail = desiredStop;
}
var exitStop = CombineLongStops(_longStopPrice, _longTrail);
if (exitStop.HasValue && candle.LowPrice <= exitStop.Value)
{
SellMarket(Math.Abs(Position));
ResetTradeState();
return true;
}
if (_longTakeProfit.HasValue && candle.HighPrice >= _longTakeProfit.Value)
{
SellMarket(Math.Abs(Position));
ResetTradeState();
return true;
}
}
else if (Position < 0)
{
_bestShortPrice = _bestShortPrice.HasValue ? Math.Min(_bestShortPrice.Value, candle.LowPrice) : candle.LowPrice;
if (TrailingStopPips > 0 && _bestShortPrice.HasValue)
{
var desiredStop = _bestShortPrice.Value + TrailingStopPips * point;
if (!_shortTrail.HasValue || _shortTrail.Value - desiredStop >= trailingStep)
_shortTrail = desiredStop;
}
var exitStop = CombineShortStops(_shortStopPrice, _shortTrail);
if (exitStop.HasValue && candle.HighPrice >= exitStop.Value)
{
BuyMarket(Math.Abs(Position));
ResetTradeState();
return true;
}
if (_shortTakeProfit.HasValue && candle.LowPrice <= _shortTakeProfit.Value)
{
BuyMarket(Math.Abs(Position));
ResetTradeState();
return true;
}
}
else
{
ResetTradeState();
}
return false;
}
private void SetLongProtection(decimal entryPrice)
{
var point = GetPointValue();
_longStopPrice = StopLossPips > 0 ? entryPrice - StopLossPips * point : null;
_longTakeProfit = TakeProfitPips > 0 ? entryPrice + TakeProfitPips * point : null;
_longTrail = TrailingStopPips > 0 ? entryPrice - TrailingStopPips * point : null;
_bestLongPrice = entryPrice;
_shortStopPrice = null;
_shortTakeProfit = null;
_shortTrail = null;
_bestShortPrice = null;
}
private void SetShortProtection(decimal entryPrice)
{
var point = GetPointValue();
_shortStopPrice = StopLossPips > 0 ? entryPrice + StopLossPips * point : null;
_shortTakeProfit = TakeProfitPips > 0 ? entryPrice - TakeProfitPips * point : null;
_shortTrail = TrailingStopPips > 0 ? entryPrice + TrailingStopPips * point : null;
_bestShortPrice = entryPrice;
_longStopPrice = null;
_longTakeProfit = null;
_longTrail = null;
_bestLongPrice = null;
}
private void ResetTradeState()
{
_longStopPrice = null;
_shortStopPrice = null;
_longTakeProfit = null;
_shortTakeProfit = null;
_longTrail = null;
_shortTrail = null;
_bestLongPrice = null;
_bestShortPrice = null;
}
private decimal GetPointValue()
{
var point = Security?.PriceStep ?? 1m;
return point > 0m ? point : 1m;
}
private static void ShiftSeries(decimal?[] series, decimal? value)
{
if (series.Length == 0)
return;
for (var i = series.Length - 1; i > 0; i--)
{
series[i] = series[i - 1];
}
series[0] = value;
}
private static bool HasSeriesValue(decimal?[] series, int shift, int depth)
{
var index = shift + depth;
return index < series.Length && series[index].HasValue;
}
private static decimal? GetSeriesValue(decimal?[] series, int shift, int depth)
{
var index = shift + depth;
return index < series.Length ? series[index] : null;
}
private static decimal? CombineLongStops(decimal? stopLoss, decimal? trailing)
{
if (stopLoss.HasValue && trailing.HasValue)
return Math.Max(stopLoss.Value, trailing.Value);
return stopLoss ?? trailing;
}
private static decimal? CombineShortStops(decimal? stopLoss, decimal? trailing)
{
if (stopLoss.HasValue && trailing.HasValue)
return Math.Min(stopLoss.Value, trailing.Value);
return stopLoss ?? trailing;
}
private static decimal GetAppliedPrice(ICandleMessage candle, AppliedPriceTypes priceType)
{
return priceType switch
{
AppliedPriceTypes.Open => candle.OpenPrice,
AppliedPriceTypes.High => candle.HighPrice,
AppliedPriceTypes.Low => candle.LowPrice,
AppliedPriceTypes.Median => (candle.HighPrice + candle.LowPrice) / 2m,
AppliedPriceTypes.Typical => (candle.HighPrice + candle.LowPrice + candle.ClosePrice) / 3m,
AppliedPriceTypes.Weighted => (candle.HighPrice + candle.LowPrice + candle.ClosePrice + candle.ClosePrice) / 4m,
_ => candle.ClosePrice
};
}
private static IIndicator CreateMovingAverage(MaMethods method, int period)
{
return method switch
{
MaMethods.Simple => new SimpleMovingAverage { Length = period },
MaMethods.Exponential => new ExponentialMovingAverage { Length = period },
MaMethods.Smoothed => new SmoothedMovingAverage { Length = period },
MaMethods.Weighted => new WeightedMovingAverage { Length = period },
_ => new SimpleMovingAverage { Length = period }
};
}
/// <summary>
/// Moving average smoothing methods supported by the strategy.
/// </summary>
public enum MaMethods
{
Simple,
Exponential,
Smoothed,
Weighted
}
/// <summary>
/// Price sources supported for indicator calculations.
/// </summary>
public enum AppliedPriceTypes
{
Close,
Open,
High,
Low,
Median,
Typical,
Weighted
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class crossing_of_two_i_ma_v2_strategy(Strategy):
"""
Crossing of two MA strategy with optional third MA filter and trailing stop.
Uses SMA crossover with protection-based stop loss and take profit.
"""
def __init__(self):
super(crossing_of_two_i_ma_v2_strategy, self).__init__()
self._first_period = self.Param("FirstPeriod", 5) \
.SetDisplay("First MA Period", "Period of the first moving average", "First MA")
self._second_period = self.Param("SecondPeriod", 8) \
.SetDisplay("Second MA Period", "Period of the second moving average", "Second MA")
self._use_filter = self.Param("UseFilter", False) \
.SetDisplay("Enable Filter", "Use the third moving average as directional filter", "Filter")
self._third_period = self.Param("ThirdPeriod", 13) \
.SetDisplay("Third MA Period", "Period of the third moving average filter", "Filter")
self._stop_loss_pips = self.Param("StopLossPips", 50) \
.SetDisplay("Stop Loss", "Stop loss distance in pips", "Protection")
self._take_profit_pips = self.Param("TakeProfitPips", 50) \
.SetDisplay("Take Profit", "Take profit distance in pips", "Protection")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Candle type used for analysis", "General")
self._prev_first = 0.0
self._prev_second = 0.0
self._entry_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(crossing_of_two_i_ma_v2_strategy, self).OnReseted()
self._prev_first = 0.0
self._prev_second = 0.0
self._entry_price = 0.0
def OnStarted2(self, time):
super(crossing_of_two_i_ma_v2_strategy, self).OnStarted2(time)
first_ma = SimpleMovingAverage()
first_ma.Length = self._first_period.Value
second_ma = SimpleMovingAverage()
second_ma.Length = self._second_period.Value
if self._use_filter.Value:
third_ma = SimpleMovingAverage()
third_ma.Length = self._third_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(first_ma, second_ma, third_ma, self.on_process_filtered).Start()
else:
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(first_ma, second_ma, self.on_process).Start()
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 1.0
sl_pips = self._stop_loss_pips.Value
tp_pips = self._take_profit_pips.Value
if sl_pips > 0 or tp_pips > 0:
tp_unit = Unit(float(tp_pips * step), UnitTypes.Absolute) if tp_pips > 0 else None
sl_unit = Unit(float(sl_pips * step), UnitTypes.Absolute) if sl_pips > 0 else None
if tp_unit is not None or sl_unit is not None:
self.StartProtection(tp_unit, sl_unit)
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def on_process(self, candle, first_val, second_val):
if candle.State != CandleStates.Finished:
return
first_val = float(first_val)
second_val = float(second_val)
if self._prev_first == 0.0 or self._prev_second == 0.0:
self._prev_first = first_val
self._prev_second = second_val
return
buy_signal = first_val > second_val and self._prev_first < self._prev_second
sell_signal = first_val < second_val and self._prev_first > self._prev_second
if buy_signal and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif sell_signal and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_first = first_val
self._prev_second = second_val
def on_process_filtered(self, candle, first_val, second_val, third_val):
if candle.State != CandleStates.Finished:
return
first_val = float(first_val)
second_val = float(second_val)
third_val = float(third_val)
if self._prev_first == 0.0 or self._prev_second == 0.0:
self._prev_first = first_val
self._prev_second = second_val
return
buy_signal = first_val > second_val and self._prev_first < self._prev_second
sell_signal = first_val < second_val and self._prev_first > self._prev_second
if buy_signal and third_val < first_val:
buy_signal = False
if sell_signal and third_val > first_val:
sell_signal = False
if buy_signal and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif sell_signal and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_first = first_val
self._prev_second = second_val
def CreateClone(self):
return crossing_of_two_i_ma_v2_strategy()