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>
/// Contrarian strategy based on the 2pb Ideal Moving Average filters.
/// Opens trades on crossings between the single and triple smoothed filters
/// and re-enters the trend when price advances by a configured number of ticks.
/// </summary>
public class TwoPbIdealMaReOpenStrategy : Strategy
{
private readonly StrategyParam<decimal> _positionVolume;
private readonly StrategyParam<int> _stopLossTicks;
private readonly StrategyParam<int> _takeProfitTicks;
private readonly StrategyParam<int> _priceStepTicks;
private readonly StrategyParam<int> _maxReEntries;
private readonly StrategyParam<bool> _enableBuyEntries;
private readonly StrategyParam<bool> _enableSellEntries;
private readonly StrategyParam<bool> _enableBuyExits;
private readonly StrategyParam<bool> _enableSellExits;
private readonly StrategyParam<int> _signalBarShift;
private readonly StrategyParam<int> _period1;
private readonly StrategyParam<int> _period2;
private readonly StrategyParam<int> _periodX1;
private readonly StrategyParam<int> _periodX2;
private readonly StrategyParam<int> _periodY1;
private readonly StrategyParam<int> _periodY2;
private readonly StrategyParam<int> _periodZ1;
private readonly StrategyParam<int> _periodZ2;
private readonly StrategyParam<DataType> _candleType;
private IdealMovingAverage _fastMa = null!;
private TripleIdealMovingAverage _slowMa = null!;
private readonly List<decimal> _fastHistory = new();
private readonly List<decimal> _slowHistory = new();
private decimal _lastBuyPrice;
private decimal _lastSellPrice;
private int _buyReEntries;
private int _sellReEntries;
/// <summary>
/// Initializes a new instance of <see cref="TwoPbIdealMaReOpenStrategy"/>.
/// </summary>
public TwoPbIdealMaReOpenStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe used for calculations", "General");
_positionVolume = Param(nameof(PositionVolume), 0.1m)
.SetGreaterThanZero()
.SetDisplay("Position Volume", "Base order volume", "Risk");
_stopLossTicks = Param(nameof(StopLossTicks), 1000)
.SetNotNegative()
.SetDisplay("Stop Loss (ticks)", "Protective stop distance in ticks", "Risk");
_takeProfitTicks = Param(nameof(TakeProfitTicks), 2000)
.SetNotNegative()
.SetDisplay("Take Profit (ticks)", "Protective profit distance in ticks", "Risk");
_priceStepTicks = Param(nameof(PriceStepTicks), 300)
.SetNotNegative()
.SetDisplay("Re-entry Step (ticks)", "Price advance required for re-entry", "Entries");
_maxReEntries = Param(nameof(MaxReEntries), 10)
.SetNotNegative()
.SetDisplay("Max Re-entries", "Maximum number of add-on trades", "Entries");
_enableBuyEntries = Param(nameof(EnableBuyEntries), true)
.SetDisplay("Enable Long Entries", "Allow opening long positions", "Entries");
_enableSellEntries = Param(nameof(EnableSellEntries), true)
.SetDisplay("Enable Short Entries", "Allow opening short positions", "Entries");
_enableBuyExits = Param(nameof(EnableBuyExits), true)
.SetDisplay("Close Long Positions", "Close longs on opposite signal", "Risk");
_enableSellExits = Param(nameof(EnableSellExits), true)
.SetDisplay("Close Short Positions", "Close shorts on opposite signal", "Risk");
_signalBarShift = Param(nameof(SignalBarShift), 1)
.SetNotNegative()
.SetDisplay("Signal Shift", "Bars back used for crossover detection", "Logic");
_period1 = Param(nameof(Period1), 10)
.SetGreaterThanZero()
.SetDisplay("Fast Filter Period 1", "First weight of single filter", "Indicators");
_period2 = Param(nameof(Period2), 10)
.SetGreaterThanZero()
.SetDisplay("Fast Filter Period 2", "Second weight of single filter", "Indicators");
_periodX1 = Param(nameof(PeriodX1), 10)
.SetGreaterThanZero()
.SetDisplay("Stage X Period 1", "First weight of stage X", "Indicators");
_periodX2 = Param(nameof(PeriodX2), 10)
.SetGreaterThanZero()
.SetDisplay("Stage X Period 2", "Second weight of stage X", "Indicators");
_periodY1 = Param(nameof(PeriodY1), 10)
.SetGreaterThanZero()
.SetDisplay("Stage Y Period 1", "First weight of stage Y", "Indicators");
_periodY2 = Param(nameof(PeriodY2), 10)
.SetGreaterThanZero()
.SetDisplay("Stage Y Period 2", "Second weight of stage Y", "Indicators");
_periodZ1 = Param(nameof(PeriodZ1), 10)
.SetGreaterThanZero()
.SetDisplay("Stage Z Period 1", "First weight of stage Z", "Indicators");
_periodZ2 = Param(nameof(PeriodZ2), 10)
.SetGreaterThanZero()
.SetDisplay("Stage Z Period 2", "Second weight of stage Z", "Indicators");
}
/// <summary>
/// Candle type used by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Base order size used for entries and re-entries.
/// </summary>
public decimal PositionVolume
{
get => _positionVolume.Value;
set => _positionVolume.Value = value;
}
/// <summary>
/// Stop loss distance expressed in ticks.
/// </summary>
public int StopLossTicks
{
get => _stopLossTicks.Value;
set => _stopLossTicks.Value = value;
}
/// <summary>
/// Take profit distance expressed in ticks.
/// </summary>
public int TakeProfitTicks
{
get => _takeProfitTicks.Value;
set => _takeProfitTicks.Value = value;
}
/// <summary>
/// Number of price ticks required between re-entry orders.
/// </summary>
public int PriceStepTicks
{
get => _priceStepTicks.Value;
set => _priceStepTicks.Value = value;
}
/// <summary>
/// Maximum number of additional entries allowed per direction.
/// </summary>
public int MaxReEntries
{
get => _maxReEntries.Value;
set => _maxReEntries.Value = value;
}
/// <summary>
/// Controls whether the strategy can open new long positions.
/// </summary>
public bool EnableBuyEntries
{
get => _enableBuyEntries.Value;
set => _enableBuyEntries.Value = value;
}
/// <summary>
/// Controls whether the strategy can open new short positions.
/// </summary>
public bool EnableSellEntries
{
get => _enableSellEntries.Value;
set => _enableSellEntries.Value = value;
}
/// <summary>
/// Controls whether long trades are closed when the signal reverses.
/// </summary>
public bool EnableBuyExits
{
get => _enableBuyExits.Value;
set => _enableBuyExits.Value = value;
}
/// <summary>
/// Controls whether short trades are closed when the signal reverses.
/// </summary>
public bool EnableSellExits
{
get => _enableSellExits.Value;
set => _enableSellExits.Value = value;
}
/// <summary>
/// Number of bars back that participates in crossover detection.
/// </summary>
public int SignalBarShift
{
get => _signalBarShift.Value;
set => _signalBarShift.Value = value;
}
/// <summary>
/// First weight for the single stage ideal moving average.
/// </summary>
public int Period1
{
get => _period1.Value;
set => _period1.Value = value;
}
/// <summary>
/// Second weight for the single stage ideal moving average.
/// </summary>
public int Period2
{
get => _period2.Value;
set => _period2.Value = value;
}
/// <summary>
/// First weight for the X stage of the triple filter.
/// </summary>
public int PeriodX1
{
get => _periodX1.Value;
set => _periodX1.Value = value;
}
/// <summary>
/// Second weight for the X stage of the triple filter.
/// </summary>
public int PeriodX2
{
get => _periodX2.Value;
set => _periodX2.Value = value;
}
/// <summary>
/// First weight for the Y stage of the triple filter.
/// </summary>
public int PeriodY1
{
get => _periodY1.Value;
set => _periodY1.Value = value;
}
/// <summary>
/// Second weight for the Y stage of the triple filter.
/// </summary>
public int PeriodY2
{
get => _periodY2.Value;
set => _periodY2.Value = value;
}
/// <summary>
/// First weight for the Z stage of the triple filter.
/// </summary>
public int PeriodZ1
{
get => _periodZ1.Value;
set => _periodZ1.Value = value;
}
/// <summary>
/// Second weight for the Z stage of the triple filter.
/// </summary>
public int PeriodZ2
{
get => _periodZ2.Value;
set => _periodZ2.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fastHistory.Clear();
_slowHistory.Clear();
_lastBuyPrice = 0m;
_lastSellPrice = 0m;
_buyReEntries = 0;
_sellReEntries = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fastMa = new IdealMovingAverage
{
Period1 = Period1,
Period2 = Period2
};
_slowMa = new TripleIdealMovingAverage
{
PeriodX1 = PeriodX1,
PeriodX2 = PeriodX2,
PeriodY1 = PeriodY1,
PeriodY2 = PeriodY2,
PeriodZ1 = PeriodZ1,
PeriodZ2 = PeriodZ2
};
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_fastMa, _slowMa, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _fastMa);
DrawIndicator(area, _slowMa);
DrawOwnTrades(area);
}
StartProtection(
new Unit(2000m, UnitTypes.Absolute),
new Unit(1000m, UnitTypes.Absolute));
if (PositionVolume > 0m)
Volume = PositionVolume;
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
_fastHistory.Add(fast);
_slowHistory.Add(slow);
TrimHistory(_fastHistory);
TrimHistory(_slowHistory);
var shift = SignalBarShift;
if (_fastHistory.Count < shift + 2 || _slowHistory.Count < shift + 2)
return;
var currentIndex = _fastHistory.Count - 1 - shift;
var previousIndex = currentIndex - 1;
if (previousIndex < 0)
return;
var fastCurrent = _fastHistory[currentIndex];
var fastPrevious = _fastHistory[previousIndex];
var slowCurrent = _slowHistory[currentIndex];
var slowPrevious = _slowHistory[previousIndex];
var bearishCross = fastPrevious > slowPrevious && fastCurrent < slowCurrent;
var bullishCross = fastPrevious < slowPrevious && fastCurrent > slowCurrent;
if (EnableBuyExits && bullishCross && Position > 0)
{
SellMarket();
ResetLongState();
}
if (EnableSellExits && bearishCross && Position < 0)
{
BuyMarket();
ResetShortState();
}
var step = Security?.PriceStep ?? 1m;
var reEntryDistance = PriceStepTicks * step;
if (PriceStepTicks > 0 && reEntryDistance > 0m)
{
if (Position > 0 && _buyReEntries < MaxReEntries)
{
var advance = candle.ClosePrice - _lastBuyPrice;
if (advance >= reEntryDistance)
{
BuyMarket();
_buyReEntries++;
_lastBuyPrice = candle.ClosePrice;
}
}
else if (Position < 0 && _sellReEntries < MaxReEntries)
{
var advance = _lastSellPrice - candle.ClosePrice;
if (advance >= reEntryDistance)
{
SellMarket();
_sellReEntries++;
_lastSellPrice = candle.ClosePrice;
}
}
}
if (bearishCross && EnableBuyEntries && Position == 0)
{
BuyMarket(PositionVolume);
_lastBuyPrice = candle.ClosePrice;
_buyReEntries = 0;
_sellReEntries = 0;
}
else if (bullishCross && EnableSellEntries && Position == 0)
{
SellMarket();
_lastSellPrice = candle.ClosePrice;
_sellReEntries = 0;
_buyReEntries = 0;
}
}
private void TrimHistory(List<decimal> history)
{
var maxCount = Math.Max(SignalBarShift + 2, 3);
while (history.Count > maxCount)
history.RemoveAt(0);
}
private void ResetLongState()
{
_lastBuyPrice = 0m;
_buyReEntries = 0;
}
private void ResetShortState()
{
_lastSellPrice = 0m;
_sellReEntries = 0;
}
private sealed class IdealMovingAverage : BaseIndicator
{
public int Period1 { get; set; } = 10;
public int Period2 { get; set; } = 10;
private bool _initialized;
private decimal _previousPrice;
private decimal _previousValue;
protected override IIndicatorValue OnProcess(IIndicatorValue input)
{
var price = input.ToDecimal();
if (!_initialized)
{
_initialized = true;
_previousPrice = price;
_previousValue = price;
IsFormed = false;
return new DecimalIndicatorValue(this, price, input.Time);
}
var weight1 = 1m / Math.Max(Period1, 1);
var weight2 = 1m / Math.Max(Period2, 1);
var diff = price - _previousPrice;
var diffSquaredMinusOne = diff * diff - 1m;
var denominator = 1m + weight2 * diffSquaredMinusOne;
decimal result;
if (denominator == 0m)
{
result = price;
}
else
{
var numerator = weight1 * (price - _previousValue) + _previousValue + weight2 * _previousValue * diffSquaredMinusOne;
result = numerator / denominator;
}
_previousPrice = price;
_previousValue = result;
IsFormed = true;
return new DecimalIndicatorValue(this, result, input.Time);
}
public override void Reset()
{
base.Reset();
_initialized = false;
_previousPrice = 0m;
_previousValue = 0m;
}
}
private sealed class TripleIdealMovingAverage : BaseIndicator
{
public int PeriodX1 { get; set; } = 10;
public int PeriodX2 { get; set; } = 10;
public int PeriodY1 { get; set; } = 10;
public int PeriodY2 { get; set; } = 10;
public int PeriodZ1 { get; set; } = 10;
public int PeriodZ2 { get; set; } = 10;
private bool _initialized;
private decimal _previousPrice;
private decimal _previousX;
private decimal _previousY;
private decimal _previousZ;
protected override IIndicatorValue OnProcess(IIndicatorValue input)
{
var price = input.ToDecimal();
if (!_initialized)
{
_initialized = true;
_previousPrice = price;
_previousX = price;
_previousY = price;
_previousZ = price;
IsFormed = false;
return new DecimalIndicatorValue(this, price, input.Time);
}
var weightX1 = 1m / Math.Max(PeriodX1, 1);
var weightX2 = 1m / Math.Max(PeriodX2, 1);
var weightY1 = 1m / Math.Max(PeriodY1, 1);
var weightY2 = 1m / Math.Max(PeriodY2, 1);
var weightZ1 = 1m / Math.Max(PeriodZ1, 1);
var weightZ2 = 1m / Math.Max(PeriodZ2, 1);
var x = Calculate(weightX1, weightX2, _previousPrice, price, _previousX);
var y = Calculate(weightY1, weightY2, _previousX, x, _previousY);
var z = Calculate(weightZ1, weightZ2, _previousY, y, _previousZ);
_previousPrice = price;
_previousX = x;
_previousY = y;
_previousZ = z;
IsFormed = true;
return new DecimalIndicatorValue(this, z, input.Time);
}
public override void Reset()
{
base.Reset();
_initialized = false;
_previousPrice = 0m;
_previousX = 0m;
_previousY = 0m;
_previousZ = 0m;
}
private static decimal Calculate(decimal weight1, decimal weight2, decimal previousInput, decimal currentInput, decimal previousValue)
{
var diff = currentInput - previousInput;
var diffSquaredMinusOne = diff * diff - 1m;
var denominator = 1m + weight2 * diffSquaredMinusOne;
if (denominator == 0m)
return currentInput;
var numerator = weight1 * (currentInput - previousValue) + previousValue + weight2 * previousValue * diffSquaredMinusOne;
return numerator / denominator;
}
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class two_pb_ideal_ma_re_open_strategy(Strategy):
def __init__(self):
super(two_pb_ideal_ma_re_open_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._position_volume = self.Param("PositionVolume", 0.1)
self._stop_loss_ticks = self.Param("StopLossTicks", 1000)
self._take_profit_ticks = self.Param("TakeProfitTicks", 2000)
self._price_step_ticks = self.Param("PriceStepTicks", 300)
self._max_re_entries = self.Param("MaxReEntries", 10)
self._enable_buy_entries = self.Param("EnableBuyEntries", True)
self._enable_sell_entries = self.Param("EnableSellEntries", True)
self._enable_buy_exits = self.Param("EnableBuyExits", True)
self._enable_sell_exits = self.Param("EnableSellExits", True)
self._signal_bar_shift = self.Param("SignalBarShift", 1)
self._period1 = self.Param("Period1", 10)
self._period2 = self.Param("Period2", 10)
self._period_x1 = self.Param("PeriodX1", 10)
self._period_x2 = self.Param("PeriodX2", 10)
self._period_y1 = self.Param("PeriodY1", 10)
self._period_y2 = self.Param("PeriodY2", 10)
self._period_z1 = self.Param("PeriodZ1", 10)
self._period_z2 = self.Param("PeriodZ2", 10)
self._fast_history = []
self._slow_history = []
self._last_buy_price = 0.0
self._last_sell_price = 0.0
self._buy_re_entries = 0
self._sell_re_entries = 0
self._fast_initialized = False
self._fast_prev_price = 0.0
self._fast_prev_value = 0.0
self._slow_initialized = False
self._slow_prev_price = 0.0
self._slow_prev_x = 0.0
self._slow_prev_y = 0.0
self._slow_prev_z = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def PositionVolume(self):
return self._position_volume.Value
@PositionVolume.setter
def PositionVolume(self, value):
self._position_volume.Value = value
@property
def StopLossTicks(self):
return self._stop_loss_ticks.Value
@StopLossTicks.setter
def StopLossTicks(self, value):
self._stop_loss_ticks.Value = value
@property
def TakeProfitTicks(self):
return self._take_profit_ticks.Value
@TakeProfitTicks.setter
def TakeProfitTicks(self, value):
self._take_profit_ticks.Value = value
@property
def PriceStepTicks(self):
return self._price_step_ticks.Value
@PriceStepTicks.setter
def PriceStepTicks(self, value):
self._price_step_ticks.Value = value
@property
def MaxReEntries(self):
return self._max_re_entries.Value
@MaxReEntries.setter
def MaxReEntries(self, value):
self._max_re_entries.Value = value
@property
def EnableBuyEntries(self):
return self._enable_buy_entries.Value
@EnableBuyEntries.setter
def EnableBuyEntries(self, value):
self._enable_buy_entries.Value = value
@property
def EnableSellEntries(self):
return self._enable_sell_entries.Value
@EnableSellEntries.setter
def EnableSellEntries(self, value):
self._enable_sell_entries.Value = value
@property
def EnableBuyExits(self):
return self._enable_buy_exits.Value
@EnableBuyExits.setter
def EnableBuyExits(self, value):
self._enable_buy_exits.Value = value
@property
def EnableSellExits(self):
return self._enable_sell_exits.Value
@EnableSellExits.setter
def EnableSellExits(self, value):
self._enable_sell_exits.Value = value
@property
def SignalBarShift(self):
return self._signal_bar_shift.Value
@SignalBarShift.setter
def SignalBarShift(self, value):
self._signal_bar_shift.Value = value
@property
def Period1(self):
return self._period1.Value
@Period1.setter
def Period1(self, value):
self._period1.Value = value
@property
def Period2(self):
return self._period2.Value
@Period2.setter
def Period2(self, value):
self._period2.Value = value
@property
def PeriodX1(self):
return self._period_x1.Value
@PeriodX1.setter
def PeriodX1(self, value):
self._period_x1.Value = value
@property
def PeriodX2(self):
return self._period_x2.Value
@PeriodX2.setter
def PeriodX2(self, value):
self._period_x2.Value = value
@property
def PeriodY1(self):
return self._period_y1.Value
@PeriodY1.setter
def PeriodY1(self, value):
self._period_y1.Value = value
@property
def PeriodY2(self):
return self._period_y2.Value
@PeriodY2.setter
def PeriodY2(self, value):
self._period_y2.Value = value
@property
def PeriodZ1(self):
return self._period_z1.Value
@PeriodZ1.setter
def PeriodZ1(self, value):
self._period_z1.Value = value
@property
def PeriodZ2(self):
return self._period_z2.Value
@PeriodZ2.setter
def PeriodZ2(self, value):
self._period_z2.Value = value
def _calc_ideal(self, w1, w2, prev_input, curr_input, prev_value):
diff = curr_input - prev_input
dsm1 = diff * diff - 1.0
denom = 1.0 + w2 * dsm1
if denom == 0.0:
return curr_input
num = w1 * (curr_input - prev_value) + prev_value + w2 * prev_value * dsm1
return num / denom
def _process_fast(self, price):
if not self._fast_initialized:
self._fast_initialized = True
self._fast_prev_price = price
self._fast_prev_value = price
return price
w1 = 1.0 / max(int(self.Period1), 1)
w2 = 1.0 / max(int(self.Period2), 1)
result = self._calc_ideal(w1, w2, self._fast_prev_price, price, self._fast_prev_value)
self._fast_prev_price = price
self._fast_prev_value = result
return result
def _process_slow(self, price):
if not self._slow_initialized:
self._slow_initialized = True
self._slow_prev_price = price
self._slow_prev_x = price
self._slow_prev_y = price
self._slow_prev_z = price
return price
wx1 = 1.0 / max(int(self.PeriodX1), 1)
wx2 = 1.0 / max(int(self.PeriodX2), 1)
wy1 = 1.0 / max(int(self.PeriodY1), 1)
wy2 = 1.0 / max(int(self.PeriodY2), 1)
wz1 = 1.0 / max(int(self.PeriodZ1), 1)
wz2 = 1.0 / max(int(self.PeriodZ2), 1)
x = self._calc_ideal(wx1, wx2, self._slow_prev_price, price, self._slow_prev_x)
y = self._calc_ideal(wy1, wy2, self._slow_prev_x, x, self._slow_prev_y)
z = self._calc_ideal(wz1, wz2, self._slow_prev_y, y, self._slow_prev_z)
self._slow_prev_price = price
self._slow_prev_x = x
self._slow_prev_y = y
self._slow_prev_z = z
return z
def OnStarted2(self, time):
super(two_pb_ideal_ma_re_open_strategy, self).OnStarted2(time)
self._fast_history = []
self._slow_history = []
self._last_buy_price = 0.0
self._last_sell_price = 0.0
self._buy_re_entries = 0
self._sell_re_entries = 0
self._fast_initialized = False
self._slow_initialized = False
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
self.StartProtection(
Unit(2000.0, UnitTypes.Absolute),
Unit(1000.0, UnitTypes.Absolute))
if float(self.PositionVolume) > 0.0:
self.Volume = self.PositionVolume
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
price = float(candle.ClosePrice)
fast = self._process_fast(price)
slow = self._process_slow(price)
self._fast_history.append(fast)
self._slow_history.append(slow)
shift = int(self.SignalBarShift)
max_count = max(shift + 2, 3)
while len(self._fast_history) > max_count:
self._fast_history.pop(0)
while len(self._slow_history) > max_count:
self._slow_history.pop(0)
if len(self._fast_history) < shift + 2 or len(self._slow_history) < shift + 2:
return
ci = len(self._fast_history) - 1 - shift
pi = ci - 1
if pi < 0:
return
fast_curr = self._fast_history[ci]
fast_prev = self._fast_history[pi]
slow_curr = self._slow_history[ci]
slow_prev = self._slow_history[pi]
bearish_cross = fast_prev > slow_prev and fast_curr < slow_curr
bullish_cross = fast_prev < slow_prev and fast_curr > slow_curr
if self.EnableBuyExits and bullish_cross and self.Position > 0:
self.SellMarket()
self._last_buy_price = 0.0
self._buy_re_entries = 0
if self.EnableSellExits and bearish_cross and self.Position < 0:
self.BuyMarket()
self._last_sell_price = 0.0
self._sell_re_entries = 0
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
re_entry_dist = float(self.PriceStepTicks) * step
if int(self.PriceStepTicks) > 0 and re_entry_dist > 0.0:
if self.Position > 0 and self._buy_re_entries < int(self.MaxReEntries):
advance = price - self._last_buy_price
if advance >= re_entry_dist:
self.BuyMarket()
self._buy_re_entries += 1
self._last_buy_price = price
elif self.Position < 0 and self._sell_re_entries < int(self.MaxReEntries):
advance = self._last_sell_price - price
if advance >= re_entry_dist:
self.SellMarket()
self._sell_re_entries += 1
self._last_sell_price = price
if bearish_cross and self.EnableBuyEntries and self.Position == 0:
self.BuyMarket()
self._last_buy_price = price
self._buy_re_entries = 0
self._sell_re_entries = 0
elif bullish_cross and self.EnableSellEntries and self.Position == 0:
self.SellMarket()
self._last_sell_price = price
self._sell_re_entries = 0
self._buy_re_entries = 0
def OnReseted(self):
super(two_pb_ideal_ma_re_open_strategy, self).OnReseted()
self._fast_history = []
self._slow_history = []
self._last_buy_price = 0.0
self._last_sell_price = 0.0
self._buy_re_entries = 0
self._sell_re_entries = 0
def CreateClone(self):
return two_pb_ideal_ma_re_open_strategy()