namespace StockSharp.Samples.Strategies;
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;
/// <summary>
/// Conversion of the MetaTrader 4 expert advisor "Starter" (2005 release).
/// Combines a Laguerre RSI proxy, EMA slope confirmation and a CCI filter.
/// Implements adaptive lot sizing inspired by the original LotsOptimized routine.
/// </summary>
public class Starter2005Strategy : Strategy
{
private readonly StrategyParam<decimal> _baseVolume;
private readonly StrategyParam<decimal> _maximumRisk;
private readonly StrategyParam<decimal> _riskDivider;
private readonly StrategyParam<decimal> _decreaseFactor;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<decimal> _cciThreshold;
private readonly StrategyParam<decimal> _laguerreGamma;
private readonly StrategyParam<decimal> _laguerreEntryTolerance;
private readonly StrategyParam<decimal> _laguerreExitHigh;
private readonly StrategyParam<decimal> _laguerreExitLow;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _ema = null!;
private CommodityChannelIndex _cci = null!;
private decimal? _previousMa;
private decimal _lagL0;
private decimal _lagL1;
private decimal _lagL2;
private decimal _lagL3;
private bool _laguerreFormed;
private decimal? _entryPrice;
private decimal _entryVolume;
private Sides? _entrySide;
private int _consecutiveLosses;
/// <summary>
/// Initializes a new instance of the <see cref="Starter2005Strategy"/> class.
/// </summary>
public Starter2005Strategy()
{
_baseVolume = Param(nameof(BaseVolume), 1.2m)
.SetGreaterThanZero()
.SetDisplay("Base Volume", "Initial lot size used when risk-based sizing is unavailable", "Risk Management")
.SetOptimize(0.1m, 5m, 0.1m);
_maximumRisk = Param(nameof(MaximumRisk), 0.036m)
.SetNotNegative()
.SetDisplay("Maximum Risk", "Fraction of account equity considered for sizing", "Risk Management")
.SetOptimize(0m, 0.1m, 0.005m);
_riskDivider = Param(nameof(RiskDivider), 500m)
.SetGreaterThanZero()
.SetDisplay("Risk Divider", "Divisor applied to risk capital (mimics the original /500 rule)", "Risk Management")
.SetOptimize(100m, 1000m, 50m);
_decreaseFactor = Param(nameof(DecreaseFactor), 2m)
.SetGreaterThanZero()
.SetDisplay("Decrease Factor", "Lot reduction factor after consecutive losses", "Risk Management")
.SetOptimize(1m, 5m, 0.5m);
_maPeriod = Param(nameof(MaPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "Length of the exponential moving average applied to median price", "Indicators")
.SetOptimize(3, 30, 1);
_cciPeriod = Param(nameof(CciPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "Commodity Channel Index lookback length", "Indicators")
.SetOptimize(5, 40, 1);
_cciThreshold = Param(nameof(CciThreshold), 5m)
.SetNotNegative()
.SetDisplay("CCI Threshold", "Absolute CCI level required for signals", "Indicators")
.SetOptimize(1m, 50m, 1m);
_laguerreGamma = Param(nameof(LaguerreGamma), 0.66m)
.SetRange(0.1m, 0.9m)
.SetDisplay("Laguerre Gamma", "Smoothing factor of the Laguerre RSI filter", "Indicators")
.SetOptimize(0.3m, 0.9m, 0.05m);
_laguerreEntryTolerance = Param(nameof(LaguerreEntryTolerance), 0.02m)
.SetRange(0m, 0.3m)
.SetDisplay("Laguerre Entry Tolerance", "Closeness to 0/1 required to mimic the original equality checks", "Signals")
.SetOptimize(0.005m, 0.1m, 0.005m);
_laguerreExitHigh = Param(nameof(LaguerreExitHigh), 0.9m)
.SetRange(0.5m, 1m)
.SetDisplay("Laguerre Exit High", "Upper exit level for long positions", "Signals")
.SetOptimize(0.6m, 1m, 0.05m);
_laguerreExitLow = Param(nameof(LaguerreExitLow), 0.1m)
.SetRange(0m, 0.5m)
.SetDisplay("Laguerre Exit Low", "Lower exit level for short positions", "Signals")
.SetOptimize(0m, 0.4m, 0.05m);
_takeProfitPoints = Param(nameof(TakeProfitPoints), 10m)
.SetNotNegative()
.SetDisplay("Take Profit (points)", "Distance in price points before profit is locked", "Risk Management")
.SetOptimize(0m, 50m, 5m);
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Primary timeframe processed by the strategy", "General");
}
/// <summary>
/// Base lot size used when the risk model produces a smaller value.
/// </summary>
public decimal BaseVolume
{
get => _baseVolume.Value;
set => _baseVolume.Value = value;
}
/// <summary>
/// Fraction of the portfolio considered for risk-based sizing.
/// </summary>
public decimal MaximumRisk
{
get => _maximumRisk.Value;
set => _maximumRisk.Value = value;
}
/// <summary>
/// Divider applied to the risk capital (mirrors the /500 rule).
/// </summary>
public decimal RiskDivider
{
get => _riskDivider.Value;
set => _riskDivider.Value = value;
}
/// <summary>
/// Lot reduction factor after consecutive losses.
/// </summary>
public decimal DecreaseFactor
{
get => _decreaseFactor.Value;
set => _decreaseFactor.Value = value;
}
/// <summary>
/// EMA length applied to median price.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// CCI lookback period.
/// </summary>
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
/// <summary>
/// Absolute CCI level required for entry.
/// </summary>
public decimal CciThreshold
{
get => _cciThreshold.Value;
set => _cciThreshold.Value = value;
}
/// <summary>
/// Laguerre smoothing factor (gamma).
/// </summary>
public decimal LaguerreGamma
{
get => _laguerreGamma.Value;
set => _laguerreGamma.Value = value;
}
/// <summary>
/// Tolerance applied when checking Laguerre against 0 or 1.
/// </summary>
public decimal LaguerreEntryTolerance
{
get => _laguerreEntryTolerance.Value;
set => _laguerreEntryTolerance.Value = value;
}
/// <summary>
/// Laguerre exit threshold for long positions.
/// </summary>
public decimal LaguerreExitHigh
{
get => _laguerreExitHigh.Value;
set => _laguerreExitHigh.Value = value;
}
/// <summary>
/// Laguerre exit threshold for short positions.
/// </summary>
public decimal LaguerreExitLow
{
get => _laguerreExitLow.Value;
set => _laguerreExitLow.Value = value;
}
/// <summary>
/// Profit distance expressed in price points.
/// </summary>
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Candle type processed by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ema = null!;
_cci = null!;
_previousMa = null;
_lagL0 = _lagL1 = _lagL2 = _lagL3 = 0m;
_laguerreFormed = false;
_entryPrice = null;
_entryVolume = 0m;
_entrySide = null;
_consecutiveLosses = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ema = new EMA { Length = MaPeriod };
_cci = new CommodityChannelIndex { Length = CciPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_ema, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ema);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal ma)
{
if (candle.State != CandleStates.Finished)
return;
// Process CCI manually
_cci.Process(candle);
if (!_ema.IsFormed || !_cci.IsFormed)
{
_previousMa = ma;
return;
}
var cci = _cci.GetCurrentValue<decimal>();
var laguerre = CalculateLaguerre(candle.ClosePrice);
if (!_laguerreFormed)
{
_previousMa = ma;
return;
}
var previousMa = _previousMa;
_previousMa = ma;
if (!previousMa.HasValue)
return;
var maRising = ma > previousMa.Value;
var maFalling = ma < previousMa.Value;
var entryTolerance = LaguerreEntryTolerance;
var takeProfitDistance = GetTakeProfitDistance();
var price = GetDecisionPrice(candle);
if (Position == 0m && !HasActiveOrders())
{
if (maRising && laguerre <= entryTolerance && cci < -CciThreshold)
{
var volume = CalculateOrderVolume(price);
if (volume > 0m)
{
BuyMarket(volume);
_entrySide = Sides.Buy;
_entryPrice = price;
_entryVolume = volume;
LogInfo($"Opening long. Laguerre={laguerre:F4}, CCI={cci:F2}, EMA rising.");
}
}
else if (maFalling && laguerre >= 1m - entryTolerance && cci > CciThreshold)
{
var volume = CalculateOrderVolume(price);
if (volume > 0m)
{
SellMarket(volume);
_entrySide = Sides.Sell;
_entryPrice = price;
_entryVolume = volume;
LogInfo($"Opening short. Laguerre={laguerre:F4}, CCI={cci:F2}, EMA falling.");
}
}
}
if (_entrySide == Sides.Buy && Position > 0m && _entryPrice.HasValue)
{
var gain = price - _entryPrice.Value;
if ((LaguerreExitHigh > 0m && laguerre >= LaguerreExitHigh) || (takeProfitDistance > 0m && gain >= takeProfitDistance))
{
var volume = Math.Abs(Position);
if (volume <= 0m)
volume = _entryVolume;
if (volume > 0m && !HasActiveOrders())
{
SellMarket(volume);
RegisterTradeResult(gain);
ResetPositionState();
LogInfo($"Closing long. Laguerre={laguerre:F4}, gain={gain:F5}.");
}
}
}
else if (_entrySide == Sides.Sell && Position < 0m && _entryPrice.HasValue)
{
var gain = _entryPrice.Value - price;
if ((LaguerreExitLow > 0m && laguerre <= LaguerreExitLow) || (takeProfitDistance > 0m && gain >= takeProfitDistance))
{
var volume = Math.Abs(Position);
if (volume <= 0m)
volume = _entryVolume;
if (volume > 0m && !HasActiveOrders())
{
BuyMarket(volume);
RegisterTradeResult(gain);
ResetPositionState();
LogInfo($"Closing short. Laguerre={laguerre:F4}, gain={gain:F5}.");
}
}
}
else if (Position == 0m && !HasActiveOrders())
{
ResetPositionState();
}
}
private decimal CalculateLaguerre(decimal price)
{
var gamma = LaguerreGamma;
var l0Prev = _lagL0;
var l1Prev = _lagL1;
var l2Prev = _lagL2;
var l3Prev = _lagL3;
_lagL0 = (1m - gamma) * price + gamma * l0Prev;
_lagL1 = -gamma * _lagL0 + l0Prev + gamma * l1Prev;
_lagL2 = -gamma * _lagL1 + l1Prev + gamma * l2Prev;
_lagL3 = -gamma * _lagL2 + l2Prev + gamma * l3Prev;
decimal cu = 0m;
decimal cd = 0m;
if (_lagL0 >= _lagL1)
cu = _lagL0 - _lagL1;
else
cd = _lagL1 - _lagL0;
if (_lagL1 >= _lagL2)
cu += _lagL1 - _lagL2;
else
cd += _lagL2 - _lagL1;
if (_lagL2 >= _lagL3)
cu += _lagL2 - _lagL3;
else
cd += _lagL3 - _lagL2;
var denominator = cu + cd;
var result = denominator == 0m ? 0m : cu / denominator;
_laguerreFormed = true;
return result;
}
private decimal CalculateOrderVolume(decimal price)
{
var volume = BaseVolume;
if (MaximumRisk > 0m && RiskDivider > 0m)
{
var portfolio = Portfolio;
var equity = portfolio?.CurrentValue ?? portfolio?.BeginValue ?? 0m;
if (equity > 0m && price > 0m)
{
var riskVolume = equity * MaximumRisk / RiskDivider;
riskVolume /= price;
if (riskVolume > volume)
volume = riskVolume;
}
}
if (DecreaseFactor > 0m && _consecutiveLosses > 1)
{
var reduction = volume * _consecutiveLosses / DecreaseFactor;
volume -= reduction;
}
return NormalizeVolume(volume);
}
private decimal NormalizeVolume(decimal volume)
{
var security = Security;
if (security != null)
{
var step = security.VolumeStep ?? 0m;
if (step <= 0m)
step = 1m;
var minVolume = security.MinVolume ?? step;
var maxVolume = security.MaxVolume;
var steps = decimal.Floor(volume / step);
if (steps < 1m)
steps = 1m;
volume = steps * step;
if (volume < minVolume)
volume = minVolume;
if (maxVolume is decimal max && max > 0m && volume > max)
volume = max;
}
if (volume <= 0m)
volume = 1m;
return volume;
}
private decimal GetTakeProfitDistance()
{
if (TakeProfitPoints <= 0m)
return 0m;
var point = Security?.PriceStep ?? 0m;
if (point <= 0m)
{
var decimals = Security?.Decimals ?? 4;
point = 1m;
for (var i = 0; i < decimals; i++)
point /= 10m;
}
return TakeProfitPoints * point;
}
private decimal GetDecisionPrice(ICandleMessage candle)
{
if (candle.ClosePrice > 0m)
return candle.ClosePrice;
return candle.OpenPrice;
}
private bool HasActiveOrders()
{
foreach (var order in Orders)
{
if (order.State == OrderStates.Active)
return true;
}
return false;
}
private void RegisterTradeResult(decimal gain)
{
if (gain > 0m)
{
if (_consecutiveLosses > 0)
LogInfo($"Profit resets loss streak of {_consecutiveLosses} trades.");
_consecutiveLosses = 0;
}
else if (gain < 0m)
{
_consecutiveLosses++;
LogInfo($"Loss streak increased to {_consecutiveLosses}.");
}
}
private void ResetPositionState()
{
_entryPrice = null;
_entryVolume = 0m;
_entrySide = null;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.BusinessEntities")
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, Sides
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Algo.Indicators import ExponentialMovingAverage, CommodityChannelIndex, CandleIndicatorValue
class starter2005_strategy(Strategy):
def __init__(self):
super(starter2005_strategy, self).__init__()
self._base_volume = self.Param("BaseVolume", 1.2) \
.SetDisplay("Base Volume", "Initial lot size used when risk-based sizing is unavailable", "Risk Management")
self._maximum_risk = self.Param("MaximumRisk", 0.036) \
.SetDisplay("Maximum Risk", "Fraction of account equity considered for sizing", "Risk Management")
self._risk_divider = self.Param("RiskDivider", 500.0) \
.SetDisplay("Risk Divider", "Divisor applied to risk capital", "Risk Management")
self._decrease_factor = self.Param("DecreaseFactor", 2.0) \
.SetDisplay("Decrease Factor", "Lot reduction factor after consecutive losses", "Risk Management")
self._ma_period = self.Param("MaPeriod", 5) \
.SetDisplay("EMA Period", "Length of the exponential moving average", "Indicators")
self._cci_period = self.Param("CciPeriod", 14) \
.SetDisplay("CCI Period", "Commodity Channel Index lookback length", "Indicators")
self._cci_threshold = self.Param("CciThreshold", 5.0) \
.SetDisplay("CCI Threshold", "Absolute CCI level required for signals", "Indicators")
self._laguerre_gamma = self.Param("LaguerreGamma", 0.66) \
.SetDisplay("Laguerre Gamma", "Smoothing factor of the Laguerre RSI filter", "Indicators")
self._laguerre_entry_tolerance = self.Param("LaguerreEntryTolerance", 0.02) \
.SetDisplay("Laguerre Entry Tolerance", "Closeness to 0/1 required for entry", "Signals")
self._laguerre_exit_high = self.Param("LaguerreExitHigh", 0.9) \
.SetDisplay("Laguerre Exit High", "Upper exit level for long positions", "Signals")
self._laguerre_exit_low = self.Param("LaguerreExitLow", 0.1) \
.SetDisplay("Laguerre Exit Low", "Lower exit level for short positions", "Signals")
self._take_profit_points = self.Param("TakeProfitPoints", 10.0) \
.SetDisplay("Take Profit (points)", "Distance in price points before profit is locked", "Risk Management")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Primary timeframe processed by the strategy", "General")
self._ema = None
self._cci = None
self._previous_ma = None
self._lag_l0 = 0.0
self._lag_l1 = 0.0
self._lag_l2 = 0.0
self._lag_l3 = 0.0
self._laguerre_formed = False
self._entry_price = None
self._entry_volume = 0.0
self._entry_side = None
self._consecutive_losses = 0
@property
def BaseVolume(self):
return self._base_volume.Value
@property
def MaximumRisk(self):
return self._maximum_risk.Value
@property
def RiskDivider(self):
return self._risk_divider.Value
@property
def DecreaseFactor(self):
return self._decrease_factor.Value
@property
def MaPeriod(self):
return self._ma_period.Value
@property
def CciPeriod(self):
return self._cci_period.Value
@property
def CciThreshold(self):
return self._cci_threshold.Value
@property
def LaguerreGamma(self):
return self._laguerre_gamma.Value
@property
def LaguerreEntryTolerance(self):
return self._laguerre_entry_tolerance.Value
@property
def LaguerreExitHigh(self):
return self._laguerre_exit_high.Value
@property
def LaguerreExitLow(self):
return self._laguerre_exit_low.Value
@property
def TakeProfitPoints(self):
return self._take_profit_points.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(starter2005_strategy, self).OnStarted2(time)
self._ema = ExponentialMovingAverage()
self._ema.Length = self.MaPeriod
self._cci = CommodityChannelIndex()
self._cci.Length = self.CciPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._ema, self.ProcessCandle).Start()
def ProcessCandle(self, candle, ma_value):
if candle.State != CandleStates.Finished:
return
ma = float(ma_value)
cci_result = self._cci.Process(CandleIndicatorValue(self._cci, candle))
cci = float(cci_result) if cci_result is not None else 0.0
if not self._ema.IsFormed or not self._cci.IsFormed:
self._previous_ma = ma
return
laguerre = self._calculate_laguerre(float(candle.ClosePrice))
if not self._laguerre_formed:
self._previous_ma = ma
return
previous_ma = self._previous_ma
self._previous_ma = ma
if previous_ma is None:
return
ma_rising = ma > previous_ma
ma_falling = ma < previous_ma
entry_tolerance = float(self.LaguerreEntryTolerance)
tp_distance = self._get_take_profit_distance()
price = self._get_decision_price(candle)
if self.Position == 0:
if ma_rising and laguerre <= entry_tolerance and cci < -float(self.CciThreshold):
volume = self._calculate_order_volume(price)
if volume > 0:
self.BuyMarket(volume)
self._entry_side = Sides.Buy
self._entry_price = price
self._entry_volume = volume
elif ma_falling and laguerre >= 1.0 - entry_tolerance and cci > float(self.CciThreshold):
volume = self._calculate_order_volume(price)
if volume > 0:
self.SellMarket(volume)
self._entry_side = Sides.Sell
self._entry_price = price
self._entry_volume = volume
if self._entry_side == Sides.Buy and self.Position > 0 and self._entry_price is not None:
gain = price - self._entry_price
exit_high = float(self.LaguerreExitHigh)
if (exit_high > 0 and laguerre >= exit_high) or (tp_distance > 0 and gain >= tp_distance):
volume = abs(self.Position)
if volume <= 0:
volume = self._entry_volume
if volume > 0:
self.SellMarket(volume)
self._register_trade_result(gain)
self._reset_position_state()
elif self._entry_side == Sides.Sell and self.Position < 0 and self._entry_price is not None:
gain = self._entry_price - price
exit_low = float(self.LaguerreExitLow)
if (exit_low > 0 and laguerre <= exit_low) or (tp_distance > 0 and gain >= tp_distance):
volume = abs(self.Position)
if volume <= 0:
volume = self._entry_volume
if volume > 0:
self.BuyMarket(volume)
self._register_trade_result(gain)
self._reset_position_state()
elif self.Position == 0:
self._reset_position_state()
def _calculate_laguerre(self, price):
gamma = float(self.LaguerreGamma)
l0_prev = self._lag_l0
l1_prev = self._lag_l1
l2_prev = self._lag_l2
l3_prev = self._lag_l3
self._lag_l0 = (1.0 - gamma) * price + gamma * l0_prev
self._lag_l1 = -gamma * self._lag_l0 + l0_prev + gamma * l1_prev
self._lag_l2 = -gamma * self._lag_l1 + l1_prev + gamma * l2_prev
self._lag_l3 = -gamma * self._lag_l2 + l2_prev + gamma * l3_prev
cu = 0.0
cd = 0.0
if self._lag_l0 >= self._lag_l1:
cu = self._lag_l0 - self._lag_l1
else:
cd = self._lag_l1 - self._lag_l0
if self._lag_l1 >= self._lag_l2:
cu += self._lag_l1 - self._lag_l2
else:
cd += self._lag_l2 - self._lag_l1
if self._lag_l2 >= self._lag_l3:
cu += self._lag_l2 - self._lag_l3
else:
cd += self._lag_l3 - self._lag_l2
denominator = cu + cd
result = 0.0 if denominator == 0 else cu / denominator
self._laguerre_formed = True
return result
def _calculate_order_volume(self, price):
volume = float(self.BaseVolume)
max_risk = float(self.MaximumRisk)
risk_divider = float(self.RiskDivider)
if max_risk > 0 and risk_divider > 0:
equity = 0.0
if self.Portfolio is not None:
cv = self.Portfolio.CurrentValue
if cv is not None and float(cv) > 0:
equity = float(cv)
elif self.Portfolio.BeginValue is not None:
equity = float(self.Portfolio.BeginValue)
if equity > 0 and price > 0:
risk_volume = equity * max_risk / risk_divider
risk_volume = risk_volume / price
if risk_volume > volume:
volume = risk_volume
decrease_factor = float(self.DecreaseFactor)
if decrease_factor > 0 and self._consecutive_losses > 1:
reduction = volume * self._consecutive_losses / decrease_factor
volume -= reduction
return self._normalize_volume(volume)
def _normalize_volume(self, volume):
if self.Security is not None:
vs = self.Security.VolumeStep
step = float(vs) if vs is not None and float(vs) > 0 else 1.0
min_vol_sec = self.Security.MinVolume
min_vol = float(min_vol_sec) if min_vol_sec is not None else step
max_vol_sec = self.Security.MaxVolume
import math
steps = math.floor(volume / step)
if steps < 1:
steps = 1
volume = steps * step
if volume < min_vol:
volume = min_vol
if max_vol_sec is not None and float(max_vol_sec) > 0 and volume > float(max_vol_sec):
volume = float(max_vol_sec)
if volume <= 0:
volume = 1.0
return volume
def _get_take_profit_distance(self):
tp_pts = float(self.TakeProfitPoints)
if tp_pts <= 0:
return 0.0
point = 0.0
if self.Security is not None and self.Security.PriceStep is not None:
point = float(self.Security.PriceStep)
if point <= 0:
decimals = 4
if self.Security is not None and self.Security.Decimals is not None:
decimals = int(self.Security.Decimals)
point = 1.0
for _ in range(decimals):
point /= 10.0
return tp_pts * point
def _get_decision_price(self, candle):
cp = float(candle.ClosePrice)
if cp > 0:
return cp
return float(candle.OpenPrice)
def _register_trade_result(self, gain):
if gain > 0:
self._consecutive_losses = 0
elif gain < 0:
self._consecutive_losses += 1
def _reset_position_state(self):
self._entry_price = None
self._entry_volume = 0.0
self._entry_side = None
def OnReseted(self):
super(starter2005_strategy, self).OnReseted()
self._ema = None
self._cci = None
self._previous_ma = None
self._lag_l0 = 0.0
self._lag_l1 = 0.0
self._lag_l2 = 0.0
self._lag_l3 = 0.0
self._laguerre_formed = False
self._entry_price = None
self._entry_volume = 0.0
self._entry_side = None
self._consecutive_losses = 0
def CreateClone(self):
return starter2005_strategy()