Стратегия Ivan CCI Averaging
Порт советника MetaTrader «Ivan», торгующего экстремумы индикатора CCI с усредняющими входами и стопом по сглаженной скользящей средней. Стратегия отслеживает долгосрочный CCI(100) для включения глобального бычьего или медвежьего режима, при необходимости наращивает позицию при откатах CCI(13) и управляет риском через перенос стопа в безубыток и трейлинг вокруг сглаженной средней. Размер позиции повторяет модель риска в процентах от капитала, а коэффициент защиты прибыли закрывает все позиции при кратном росте эквити.
Детали
- Условия входа:
- Глобальный лонг: CCI(100) поднимается выше
GlobalSignalLevel, пока режим покупок не активен. Открывается рыночный лонг со стопом на значении сглаженной средней при условии, что стоп минимум наMinStopDistanceниже цены. - Усреднение лонга: при активном флаге покупок и включённом
UseAveragingлюбое падение CCI(13) ниже-GlobalSignalLevelдобавляет ещё один лонг по тому же шаблону стопа. - Глобальный шорт: CCI(100) опускается ниже
-GlobalSignalLevel, пока режим продаж не активен, что открывает шорт при условии, что стоп от MA минимум наMinStopDistanceвыше цены. - Усреднение шорта: при включённом
UseAveragingрост CCI(13) вышеGlobalSignalLevelв режиме продаж добавляет к шортовой позиции.
- Глобальный лонг: CCI(100) поднимается выше
- Направление: работает в обе стороны и может пирамидить позиции внутри активного режима.
- Условия выхода:
- Возврат CCI(100) внутрь диапазона
±ReverseLevelотменяет оба режима и принудительно закрывает позиции. - Рост эквити портфеля выше
ProfitProtectionFactorот стартового баланса закрывает все сделки и фиксирует результат. - Достижение отслеживаемого стопа (безубыток или трейлинг по MA) закрывает соответствующее плечо.
- Возврат CCI(100) внутрь диапазона
- Стопы:
- Базовый стоп берётся из сглаженной скользящей средней со сроком
StopLossMaPeriod. - Перенос в безубыток двигает стоп на цену входа после прохождения
BreakEvenDistance(0 отключает функцию). - Трейлинг подтягивает стоп только если MA продвинулась минимум на
TrailingStepза текущий уровень.
- Базовый стоп берётся из сглаженной скользящей средней со сроком
- Фильтры:
UseZeroBarповторяет опцию MT5 — использовать значения только что открывшейся свечи или последней закрытой.MinStopDistanceблокирует сделки, когда стоп от MA слишком близок к цене.
- Размер позиции:
- Каждый новый ордер рискует
RiskPercentтекущего портфеля, делённым на расстояние до стопа;MinimumVolumeзадаёт нижний предел объёма.
- Каждый новый ордер рискует
Параметры
- Use Averaging (bool, по умолчанию: true) — разрешить дополнительные усредняющие входы в активном режиме.
- Stop MA Period (int, по умолчанию: 36) — период сглаженной MA, от которой берётся стоп.
- Risk % (decimal, по умолчанию: 10) — доля капитала, которую стратегия рискует на каждом новом входе.
- Use Zero Bar (bool, по умолчанию: true) — использовать нулевую свечу; при
falseрасчёт ведётся по последней закрытой. - Reverse Level (decimal, по умолчанию: 100) — модуль порога CCI, при котором режимы сбрасываются и позиции закрываются.
- Global Level (decimal, по умолчанию: 100) — модуль порога CCI, при достижении которого формируется глобальный сигнал.
- Min Stop Distance (decimal, по умолчанию: 0.005) — минимальный зазор между ценой и стопом (0.005 ≈ 50 пунктов для 5-значных валютных пар).
- Trailing Step (decimal, по умолчанию: 0.001) — минимальное продвижение MA, необходимое для подтяжки трейлинг-стопа.
- BreakEven Distance (decimal, по умолчанию: 0.0005) — ход цены, после которого стоп переносится в точку входа; 0 отключает перенос.
- Profit Protection (decimal, по умолчанию: 1.5) — коэффициент роста эквити, при котором все позиции закрываются.
- Minimum Volume (decimal, по умолчанию: 1) — минимальный объём сделки при недостаточном рассчитанном объёме.
- Candle Type (DataType) — тип используемых свечей (по умолчанию таймфрейм 15 минут).
Примечания
- Расстояния
MinStopDistance,TrailingStepиBreakEvenDistanceзадаются в ценовых единицах и требуют адаптации под шаг цены инструмента. - Предполагается мгновенное исполнение
BuyMarket/SellMarket; при ожидании проскальзываний или частичных исполнений скорректируйте настройки исполнения. - Расчёт объёма по портфелю требует подключённого портфельного адаптера; иначе будет использоваться значение
MinimumVolume.
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()