Ivan CCI Averaging Strategy
Port of the "Ivan" MetaTrader expert advisor that trades CCI extremes with averaging entries and a smoothed moving-average stop. The strategy monitors a long-term CCI(100) to raise global buy or sell regimes, optionally layers additional positions when a short-term CCI(13) retraces, and manages risk with break-even and trailing logic around a smoothed moving average. Position sizing mirrors the original percent-risk model and a profit-protection coefficient closes the book when equity multiplies.
Details
- Entry Criteria:
- Long global signal: CCI(100) rises above
GlobalSignalLevelwhile no buy regime is active. A long market order is sent with the initial stop at the smoothed MA value, provided the stop is at leastMinStopDistancebelow price. - Long averaging: If
UseAveragingis enabled and the global buy flag is set, any dip of CCI(13) below-GlobalSignalLeveladds another long using the same stop template. - Short global signal: CCI(100) falls below
-GlobalSignalLevelwhile no sell regime is active, triggering a short entry when the MA stop is at leastMinStopDistanceabove price. - Short averaging: With
UseAveragingenabled, a rally of CCI(13) aboveGlobalSignalLevelinside a sell regime adds to the short exposure.
- Long global signal: CCI(100) rises above
- Long/Short: Trades both directions and can pyramid positions inside the active bias.
- Exit Criteria:
- Crossing back inside
±ReverseLevelon CCI(100) cancels both regimes and forces flat exposure. - Portfolio equity exceeding
ProfitProtectionFactortimes the starting balance forces liquidation of all positions. - Reaching the tracked stop price (break-even or trailed MA) closes the position side.
- Crossing back inside
- Stops:
- Initial stop comes from a
StopLossMaPeriodsmoothed moving average (SMMA). - Break-even moves the stop to the entry price once price advances by
BreakEvenDistance(set to zero to disable). - Trailing tightens the stop only if the MA progresses by at least
TrailingStepbeyond the current stop.
- Initial stop comes from a
- Filters:
UseZeroBarreplicates the MT5 option to read either the freshly opened bar or the last closed bar for signal comparisons.MinStopDistanceprevents trades when the MA stop is too close to price.
- Position Sizing:
- Each new order risks
RiskPercentof the current portfolio value divided by the distance between price and the stop, withMinimumVolumeas a safety floor.
- Each new order risks
Parameters
- Use Averaging (bool, default: true) — Enable additional averaging orders during an active global regime.
- Stop MA Period (int, default: 36) — Period of the smoothed MA used to derive stop levels.
- Risk % (decimal, default: 10) — Percentage of account equity to risk on each new trade.
- Use Zero Bar (bool, default: true) — If true, uses the latest candle values; otherwise signals rely on the previous closed bar.
- Reverse Level (decimal, default: 100) — Absolute CCI threshold that cancels both regimes and closes all positions.
- Global Level (decimal, default: 100) — Absolute CCI threshold that activates a new global buy or sell signal.
- Min Stop Distance (decimal, default: 0.005) — Minimum price gap between entry and the MA stop (0.005 ≈ 50 pips on 5-digit FX pairs).
- Trailing Step (decimal, default: 0.001) — Minimum improvement required before the MA trailing stop is advanced.
- BreakEven Distance (decimal, default: 0.0005) — Price move needed to shift the stop to the entry price; set to 0 to disable break-even.
- Profit Protection (decimal, default: 1.5) — Equity multiple that triggers full liquidation to lock in gains.
- Minimum Volume (decimal, default: 1) — Fallback trade size when risk-based sizing yields small or zero volume.
- Candle Type (DataType) — Candle series used for indicators (default 15-minute time frame).
Notes
- Distances such as
MinStopDistance,TrailingStep, andBreakEvenDistanceare expressed in price units and should be adjusted to the instrument's tick size. - The strategy assumes synchronous fills from
BuyMarket/SellMarketorders; adjust execution settings if slippage or partial fills are expected. - Portfolio-based sizing requires a connected portfolio adapter; otherwise
MinimumVolumeis used for all orders.
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>
/// CCI based averaging strategy converted from the Ivan expert advisor.
/// </summary>
public class IvanCciAveragingStrategy : Strategy
{
private readonly StrategyParam<bool> _useAveraging;
private readonly StrategyParam<int> _stopLossMaPeriod;
private readonly StrategyParam<decimal> _riskPercent;
private readonly StrategyParam<bool> _useZeroBar;
private readonly StrategyParam<decimal> _reverseLevel;
private readonly StrategyParam<decimal> _globalSignalLevel;
private readonly StrategyParam<decimal> _minStopDistance;
private readonly StrategyParam<decimal> _trailingStep;
private readonly StrategyParam<decimal> _breakEvenDistance;
private readonly StrategyParam<decimal> _profitProtectionFactor;
private readonly StrategyParam<decimal> _minimumVolume;
private readonly StrategyParam<DataType> _candleType;
private CommodityChannelIndex _cci100 = null!;
private CommodityChannelIndex _cci13 = null!;
private SmoothedMovingAverage _stopMa = null!;
private decimal? _lastCci100;
private decimal? _prevCci100;
private decimal? _lastCci13;
private decimal? _prevCci13;
private bool _globalBuySignal;
private bool _globalSellSignal;
private bool _closeAll;
private decimal? _initialBalance;
private decimal _longEntryPrice;
private decimal _shortEntryPrice;
private decimal _longStop;
private decimal _shortStop;
private bool _longBreakEvenActivated;
private bool _shortBreakEvenActivated;
private bool _hasLongEntry;
private bool _hasShortEntry;
/// <summary>
/// Enables additional averaging entries when short CCI pulls back.
/// </summary>
public bool UseAveraging
{
get => _useAveraging.Value;
set => _useAveraging.Value = value;
}
/// <summary>
/// Period for the smoothed moving average used as stop reference.
/// </summary>
public int StopLossMaPeriod
{
get => _stopLossMaPeriod.Value;
set => _stopLossMaPeriod.Value = value;
}
/// <summary>
/// Portfolio risk percent used to size new entries.
/// </summary>
public decimal RiskPercent
{
get => _riskPercent.Value;
set => _riskPercent.Value = value;
}
/// <summary>
/// Use the latest candle (zero bar) instead of the previous closed bar for signals.
/// </summary>
public bool UseZeroBar
{
get => _useZeroBar.Value;
set => _useZeroBar.Value = value;
}
/// <summary>
/// Reverse level for the long term CCI that triggers full liquidation.
/// </summary>
public decimal ReverseLevel
{
get => _reverseLevel.Value;
set => _reverseLevel.Value = value;
}
/// <summary>
/// Threshold for the long term CCI global signal.
/// </summary>
public decimal GlobalSignalLevel
{
get => _globalSignalLevel.Value;
set => _globalSignalLevel.Value = value;
}
/// <summary>
/// Minimum distance between price and stop when entering a position.
/// </summary>
public decimal MinStopDistance
{
get => _minStopDistance.Value;
set => _minStopDistance.Value = value;
}
/// <summary>
/// Minimum improvement required before trailing the stop with the MA.
/// </summary>
public decimal TrailingStep
{
get => _trailingStep.Value;
set => _trailingStep.Value = value;
}
/// <summary>
/// Profit distance that moves the stop to break-even. Zero disables break-even.
/// </summary>
public decimal BreakEvenDistance
{
get => _breakEvenDistance.Value;
set => _breakEvenDistance.Value = value;
}
/// <summary>
/// Equity multiple that forces liquidation of all positions.
/// </summary>
public decimal ProfitProtectionFactor
{
get => _profitProtectionFactor.Value;
set => _profitProtectionFactor.Value = value;
}
/// <summary>
/// Minimum trading volume used when risk based sizing is not available.
/// </summary>
public decimal MinimumVolume
{
get => _minimumVolume.Value;
set => _minimumVolume.Value = value;
}
/// <summary>
/// Type of candles used by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="IvanCciAveragingStrategy"/> class.
/// </summary>
public IvanCciAveragingStrategy()
{
_useAveraging = Param(nameof(UseAveraging), false)
.SetDisplay("Use Averaging", "Allow additional averaging entries", "Signals");
_stopLossMaPeriod = Param(nameof(StopLossMaPeriod), 36)
.SetGreaterThanZero()
.SetDisplay("Stop MA Period", "Length of SMMA for stop placement", "Stops");
_riskPercent = Param(nameof(RiskPercent), 10m)
.SetGreaterThanZero()
.SetDisplay("Risk %", "Portfolio percent risked per trade", "Risk");
_useZeroBar = Param(nameof(UseZeroBar), false)
.SetDisplay("Use Zero Bar", "Use current bar values instead of previous", "Signals");
_reverseLevel = Param(nameof(ReverseLevel), 100m)
.SetGreaterThanZero()
.SetDisplay("Reverse Level", "CCI level that closes all trades", "Signals");
_globalSignalLevel = Param(nameof(GlobalSignalLevel), 100m)
.SetGreaterThanZero()
.SetDisplay("Global Level", "CCI level that creates global signal", "Signals");
_minStopDistance = Param(nameof(MinStopDistance), 0.005m)
.SetGreaterThanZero()
.SetDisplay("Min Stop Distance", "Minimum price gap between entry and stop", "Stops");
_trailingStep = Param(nameof(TrailingStep), 0.001m)
.SetGreaterThanZero()
.SetDisplay("Trailing Step", "Minimum MA progress before trailing", "Stops");
_breakEvenDistance = Param(nameof(BreakEvenDistance), 0.0005m)
.SetDisplay("BreakEven Distance", "Distance to move stop to entry", "Stops");
_profitProtectionFactor = Param(nameof(ProfitProtectionFactor), 1.5m)
.SetGreaterThanZero()
.SetDisplay("Profit Protection", "Equity multiple to flatten positions", "Risk");
_minimumVolume = Param(nameof(MinimumVolume), 1m)
.SetGreaterThanZero()
.SetDisplay("Minimum Volume", "Fallback trade volume", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles for calculations", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_cci100?.Reset();
_cci13?.Reset();
_stopMa?.Reset();
_cci100 = null!;
_cci13 = null!;
_stopMa = null!;
_lastCci100 = null;
_prevCci100 = null;
_lastCci13 = null;
_prevCci13 = null;
_globalBuySignal = false;
_globalSellSignal = false;
_closeAll = false;
_initialBalance = null;
_longEntryPrice = 0m;
_shortEntryPrice = 0m;
_longStop = 0m;
_shortStop = 0m;
_longBreakEvenActivated = false;
_shortBreakEvenActivated = false;
_hasLongEntry = false;
_hasShortEntry = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Initialize indicators used for signal and stop logic.
_cci100 = new CommodityChannelIndex { Length = 100 };
_cci13 = new CommodityChannelIndex { Length = 13 };
_stopMa = new SmoothedMovingAverage { Length = StopLossMaPeriod };
// Reset state variables for a new run.
_lastCci100 = null;
_prevCci100 = null;
_lastCci13 = null;
_prevCci13 = null;
_globalBuySignal = false;
_globalSellSignal = false;
_closeAll = false;
_hasLongEntry = false;
_hasShortEntry = false;
_longBreakEvenActivated = false;
_shortBreakEvenActivated = false;
_longStop = 0m;
_shortStop = 0m;
_initialBalance = Portfolio?.CurrentValue ?? 0m;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_cci100, _cci13, _stopMa, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal cci100Value, decimal cci13Value, decimal stopMaValue)
{
if (candle.State != CandleStates.Finished)
return;
// Update profit protection flag based on equity growth.
var equity = Portfolio?.CurrentValue ?? 0m;
if (ProfitProtectionFactor > 1m && _initialBalance.HasValue && _initialBalance.Value > 0m)
{
if (equity >= _initialBalance.Value * ProfitProtectionFactor)
_closeAll = true;
}
// Ensure indicators are ready before using their values.
if (!_cci100.IsFormed || !_cci13.IsFormed || !_stopMa.IsFormed)
{
UpdateHistory(cci100Value, cci13Value);
return;
}
decimal? currentCci;
decimal? previousCci;
decimal? shortCci;
// Recreate the zero/first bar selection logic from MQL.
if (UseZeroBar)
{
currentCci = cci100Value;
previousCci = _lastCci100;
shortCci = cci13Value;
}
else
{
currentCci = _lastCci100;
previousCci = _prevCci100;
shortCci = _lastCci13;
}
if (currentCci is null || previousCci is null || shortCci is null)
{
UpdateHistory(cci100Value, cci13Value);
return;
}
// Detect reverse conditions that require flattening the book.
if ((previousCci.Value > ReverseLevel && currentCci.Value < ReverseLevel) ||
(previousCci.Value < -ReverseLevel && currentCci.Value > -ReverseLevel))
{
_globalBuySignal = false;
_globalSellSignal = false;
_closeAll = true;
}
else if (!_closeAll)
{
// Generate global signals and optional averaging entries.
if (currentCci.Value > GlobalSignalLevel && !_globalBuySignal)
{
_globalBuySignal = true;
_globalSellSignal = false;
TryEnterLong(candle, stopMaValue);
}
else if (currentCci.Value < -GlobalSignalLevel && !_globalSellSignal)
{
_globalBuySignal = false;
_globalSellSignal = true;
TryEnterShort(candle, stopMaValue);
}
else if (UseAveraging)
{
if (_globalBuySignal && shortCci.Value < -GlobalSignalLevel)
TryEnterLong(candle, stopMaValue);
else if (_globalSellSignal && shortCci.Value > GlobalSignalLevel)
TryEnterShort(candle, stopMaValue);
}
}
ManagePositions(candle, stopMaValue);
if (_closeAll)
{
ClosePosition();
_closeAll = false;
}
UpdateHistory(cci100Value, cci13Value);
}
private void ManagePositions(ICandleMessage candle, decimal stopMaValue)
{
if (Position > 0 && _hasLongEntry)
{
// Move the long stop to break-even when profit reaches the target distance.
if (BreakEvenDistance > 0m && !_longBreakEvenActivated && candle.ClosePrice >= _longEntryPrice + BreakEvenDistance)
{
_longStop = _longEntryPrice;
_longBreakEvenActivated = true;
}
// Trail the stop with the smoothed moving average if it keeps rising.
if (stopMaValue < candle.ClosePrice)
{
if (stopMaValue - TrailingStep > _longStop)
_longStop = stopMaValue;
}
if (_longStop > 0m && candle.ClosePrice <= _longStop)
{
SellMarket();
_hasLongEntry = false;
_longBreakEvenActivated = false;
}
}
else if (Position < 0 && _hasShortEntry)
{
// Move the short stop to break-even when profit reaches the target distance.
if (BreakEvenDistance > 0m && !_shortBreakEvenActivated && candle.ClosePrice <= _shortEntryPrice - BreakEvenDistance)
{
_shortStop = _shortEntryPrice;
_shortBreakEvenActivated = true;
}
// Trail the stop with the smoothed moving average if it keeps falling.
if (stopMaValue > candle.ClosePrice)
{
if (_shortStop == 0m || stopMaValue + TrailingStep < _shortStop)
_shortStop = stopMaValue;
}
if (_shortStop > 0m && candle.ClosePrice >= _shortStop)
{
BuyMarket();
_hasShortEntry = false;
_shortBreakEvenActivated = false;
}
}
}
private void TryEnterLong(ICandleMessage candle, decimal stopMaValue)
{
if (stopMaValue >= candle.ClosePrice)
return;
var distance = candle.ClosePrice - stopMaValue;
if (distance < MinStopDistance)
return;
var volume = CalculateVolume(candle.ClosePrice, stopMaValue);
if (volume <= 0m)
return;
BuyMarket();
_longEntryPrice = candle.ClosePrice;
_longStop = stopMaValue;
_longBreakEvenActivated = false;
_hasLongEntry = true;
_hasShortEntry = false;
}
private void TryEnterShort(ICandleMessage candle, decimal stopMaValue)
{
if (stopMaValue <= candle.ClosePrice)
return;
var distance = stopMaValue - candle.ClosePrice;
if (distance < MinStopDistance)
return;
var volume = CalculateVolume(candle.ClosePrice, stopMaValue);
if (volume <= 0m)
return;
SellMarket();
_shortEntryPrice = candle.ClosePrice;
_shortStop = stopMaValue;
_shortBreakEvenActivated = false;
_hasShortEntry = true;
_hasLongEntry = false;
}
private decimal CalculateVolume(decimal entryPrice, decimal stopPrice)
{
var minimum = MinimumVolume > 0m ? MinimumVolume : 0m;
if (entryPrice <= 0m || stopPrice <= 0m)
return minimum;
var riskPerUnit = Math.Abs(entryPrice - stopPrice);
if (riskPerUnit <= 0m)
return minimum;
if (RiskPercent <= 0m)
return minimum;
var equity = Portfolio?.CurrentValue ?? 0m;
if (equity <= 0m)
return minimum;
var riskCapital = equity * RiskPercent / 100m;
if (riskCapital <= 0m)
return minimum;
var volume = riskCapital / riskPerUnit;
if (volume <= 0m)
return minimum;
return Math.Max(minimum, volume);
}
private void ClosePosition()
{
if (Position > 0)
{
SellMarket();
}
else if (Position < 0)
{
BuyMarket();
}
_hasLongEntry = false;
_hasShortEntry = false;
_longBreakEvenActivated = false;
_shortBreakEvenActivated = false;
_longStop = 0m;
_shortStop = 0m;
}
private void UpdateHistory(decimal cci100Value, decimal cci13Value)
{
_prevCci100 = _lastCci100;
_lastCci100 = cci100Value;
_prevCci13 = _lastCci13;
_lastCci13 = cci13Value;
}
}
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 CommodityChannelIndex, SmoothedMovingAverage
from StockSharp.Algo.Strategies import Strategy
class ivan_cci_averaging_strategy(Strategy):
def __init__(self):
super(ivan_cci_averaging_strategy, self).__init__()
self._use_averaging = self.Param("UseAveraging", False)
self._stop_loss_ma_period = self.Param("StopLossMaPeriod", 36)
self._risk_percent = self.Param("RiskPercent", 10.0)
self._use_zero_bar = self.Param("UseZeroBar", False)
self._reverse_level = self.Param("ReverseLevel", 100.0)
self._global_signal_level = self.Param("GlobalSignalLevel", 100.0)
self._min_stop_distance = self.Param("MinStopDistance", 0.005)
self._trailing_step = self.Param("TrailingStep", 0.001)
self._break_even_distance = self.Param("BreakEvenDistance", 0.0005)
self._profit_protection_factor = self.Param("ProfitProtectionFactor", 1.5)
self._minimum_volume = self.Param("MinimumVolume", 1.0)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._last_cci100 = None
self._prev_cci100 = None
self._last_cci13 = None
self._prev_cci13 = None
self._global_buy_signal = False
self._global_sell_signal = False
self._close_all = False
self._initial_balance = None
self._long_entry_price = 0.0
self._short_entry_price = 0.0
self._long_stop = 0.0
self._short_stop = 0.0
self._long_breakeven_activated = False
self._short_breakeven_activated = False
self._has_long_entry = False
self._has_short_entry = False
@property
def UseAveraging(self):
return self._use_averaging.Value
@UseAveraging.setter
def UseAveraging(self, value):
self._use_averaging.Value = value
@property
def StopLossMaPeriod(self):
return self._stop_loss_ma_period.Value
@StopLossMaPeriod.setter
def StopLossMaPeriod(self, value):
self._stop_loss_ma_period.Value = value
@property
def RiskPercent(self):
return self._risk_percent.Value
@RiskPercent.setter
def RiskPercent(self, value):
self._risk_percent.Value = value
@property
def UseZeroBar(self):
return self._use_zero_bar.Value
@UseZeroBar.setter
def UseZeroBar(self, value):
self._use_zero_bar.Value = value
@property
def ReverseLevel(self):
return self._reverse_level.Value
@ReverseLevel.setter
def ReverseLevel(self, value):
self._reverse_level.Value = value
@property
def GlobalSignalLevel(self):
return self._global_signal_level.Value
@GlobalSignalLevel.setter
def GlobalSignalLevel(self, value):
self._global_signal_level.Value = value
@property
def MinStopDistance(self):
return self._min_stop_distance.Value
@MinStopDistance.setter
def MinStopDistance(self, value):
self._min_stop_distance.Value = value
@property
def TrailingStep(self):
return self._trailing_step.Value
@TrailingStep.setter
def TrailingStep(self, value):
self._trailing_step.Value = value
@property
def BreakEvenDistance(self):
return self._break_even_distance.Value
@BreakEvenDistance.setter
def BreakEvenDistance(self, value):
self._break_even_distance.Value = value
@property
def ProfitProtectionFactor(self):
return self._profit_protection_factor.Value
@ProfitProtectionFactor.setter
def ProfitProtectionFactor(self, value):
self._profit_protection_factor.Value = value
@property
def MinimumVolume(self):
return self._minimum_volume.Value
@MinimumVolume.setter
def MinimumVolume(self, value):
self._minimum_volume.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(ivan_cci_averaging_strategy, self).OnStarted2(time)
self._cci100 = CommodityChannelIndex()
self._cci100.Length = 100
self._cci13 = CommodityChannelIndex()
self._cci13.Length = 13
self._stop_ma = SmoothedMovingAverage()
self._stop_ma.Length = self.StopLossMaPeriod
self._last_cci100 = None
self._prev_cci100 = None
self._last_cci13 = None
self._prev_cci13 = None
self._global_buy_signal = False
self._global_sell_signal = False
self._close_all = False
self._has_long_entry = False
self._has_short_entry = False
self._long_breakeven_activated = False
self._short_breakeven_activated = False
self._long_stop = 0.0
self._short_stop = 0.0
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._cci100, self._cci13, self._stop_ma, self.ProcessCandle).Start()
self.StartProtection(
Unit(2000.0, UnitTypes.Absolute),
Unit(1000.0, UnitTypes.Absolute))
def ProcessCandle(self, candle, cci100_value, cci13_value, stop_ma_value):
if candle.State != CandleStates.Finished:
return
cci100 = float(cci100_value)
cci13 = float(cci13_value)
stop_ma = float(stop_ma_value)
close = float(candle.ClosePrice)
if not self._cci100.IsFormed or not self._cci13.IsFormed or not self._stop_ma.IsFormed:
self._update_history(cci100, cci13)
return
if self.UseZeroBar:
current_cci = cci100
previous_cci = self._last_cci100
short_cci = cci13
else:
current_cci = self._last_cci100
previous_cci = self._prev_cci100
short_cci = self._last_cci13
if current_cci is None or previous_cci is None or short_cci is None:
self._update_history(cci100, cci13)
return
reverse_level = float(self.ReverseLevel)
global_level = float(self.GlobalSignalLevel)
if (previous_cci > reverse_level and current_cci < reverse_level) or \
(previous_cci < -reverse_level and current_cci > -reverse_level):
self._global_buy_signal = False
self._global_sell_signal = False
self._close_all = True
elif not self._close_all:
if current_cci > global_level and not self._global_buy_signal:
self._global_buy_signal = True
self._global_sell_signal = False
self._try_enter_long(candle, stop_ma)
elif current_cci < -global_level and not self._global_sell_signal:
self._global_buy_signal = False
self._global_sell_signal = True
self._try_enter_short(candle, stop_ma)
elif self.UseAveraging:
if self._global_buy_signal and short_cci < -global_level:
self._try_enter_long(candle, stop_ma)
elif self._global_sell_signal and short_cci > global_level:
self._try_enter_short(candle, stop_ma)
self._manage_positions(candle, stop_ma)
if self._close_all:
self._close_position()
self._close_all = False
self._update_history(cci100, cci13)
def _manage_positions(self, candle, stop_ma_value):
close = float(candle.ClosePrice)
if self.Position > 0 and self._has_long_entry:
be = float(self.BreakEvenDistance)
if be > 0.0 and not self._long_breakeven_activated and close >= self._long_entry_price + be:
self._long_stop = self._long_entry_price
self._long_breakeven_activated = True
trailing_step = float(self.TrailingStep)
if stop_ma_value < close:
if stop_ma_value - trailing_step > self._long_stop:
self._long_stop = stop_ma_value
if self._long_stop > 0.0 and close <= self._long_stop:
self.SellMarket()
self._has_long_entry = False
self._long_breakeven_activated = False
elif self.Position < 0 and self._has_short_entry:
be = float(self.BreakEvenDistance)
if be > 0.0 and not self._short_breakeven_activated and close <= self._short_entry_price - be:
self._short_stop = self._short_entry_price
self._short_breakeven_activated = True
trailing_step = float(self.TrailingStep)
if stop_ma_value > close:
if self._short_stop == 0.0 or stop_ma_value + trailing_step < self._short_stop:
self._short_stop = stop_ma_value
if self._short_stop > 0.0 and close >= self._short_stop:
self.BuyMarket()
self._has_short_entry = False
self._short_breakeven_activated = False
def _try_enter_long(self, candle, stop_ma_value):
close = float(candle.ClosePrice)
if stop_ma_value >= close:
return
distance = close - stop_ma_value
if distance < float(self.MinStopDistance):
return
self.BuyMarket()
self._long_entry_price = close
self._long_stop = stop_ma_value
self._long_breakeven_activated = False
self._has_long_entry = True
self._has_short_entry = False
def _try_enter_short(self, candle, stop_ma_value):
close = float(candle.ClosePrice)
if stop_ma_value <= close:
return
distance = stop_ma_value - close
if distance < float(self.MinStopDistance):
return
self.SellMarket()
self._short_entry_price = close
self._short_stop = stop_ma_value
self._short_breakeven_activated = False
self._has_short_entry = True
self._has_long_entry = False
def _close_position(self):
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
self._has_long_entry = False
self._has_short_entry = False
self._long_breakeven_activated = False
self._short_breakeven_activated = False
self._long_stop = 0.0
self._short_stop = 0.0
def _update_history(self, cci100, cci13):
self._prev_cci100 = self._last_cci100
self._last_cci100 = cci100
self._prev_cci13 = self._last_cci13
self._last_cci13 = cci13
def OnReseted(self):
super(ivan_cci_averaging_strategy, self).OnReseted()
self._last_cci100 = None
self._prev_cci100 = None
self._last_cci13 = None
self._prev_cci13 = None
self._global_buy_signal = False
self._global_sell_signal = False
self._close_all = False
self._initial_balance = None
self._long_entry_price = 0.0
self._short_entry_price = 0.0
self._long_stop = 0.0
self._short_stop = 0.0
self._long_breakeven_activated = False
self._short_breakeven_activated = False
self._has_long_entry = False
self._has_short_entry = False
def CreateClone(self):
return ivan_cci_averaging_strategy()