Color PEMA Envelopes Digit System
Color PEMA Envelopes Digit System — это перенос на StockSharp эксперта
Exp_Color_PEMA_Envelopes_Digit_System.mq5. Стратегия анализирует цветовые коды
индикатора Color PEMA Envelopes: когда свеча закрывается выше/ниже канала,
индикатор меняет цвет. После возврата цены внутрь канала открывается сделка
в сторону первоначального пробоя.
Как это работает
- Рассчитывается восьмиступенчатый Polynomial EMA (PEMA) с дробной длиной, как в оригинальном индикаторе. Значение округляется с учётом параметра
Digitи смещается наPriceShift. - Вокруг PEMA строятся верхняя и нижняя оболочки через процентное отклонение
DeviationPercent. - Каждая закрытая свеча получает цветовой код в зависимости от положения относительно смещённых оболочек:
4/3: закрытие выше верхней границы (бычье/медвежье тело).1/0: закрытие ниже нижней границы (бычье/медвежье тело).2: цена находится внутри канала.
- Стратегия читает цвет
SignalBar + 1свечи (аналог вызоваCopyBufferв MQL5) и сравнивает его с цветомSignalBarсвечи. - Если более старая свеча показала пробой вверх, а следующая вернулась в канал, разрешается вход в лонг (если включено) и закрывается шорт. Для коротких позиций применяется зеркальная логика.
- Управление стоп-лоссом и тейк-профитом выполняется через модуль защиты StockSharp.
Параметры
CandleType– используемый таймфрейм.TradeVolume– объём рыночных ордеров.EmaLength– дробная длина каждого уровня EMA в PEMA.AppliedPrices– источник цены (close, open, median, weighted, trend-follow, DeMark и т.д.).DeviationPercent– процентное отклонение для построения оболочек.Shift– количество свечей для смещения сравнения с каналом.PriceShift– дополнительное абсолютное смещение PEMA.Digit– количество добавочных знаков при округлении PEMA.SignalBar– на сколько свечей назад смотреть текущий цвет (цветSignalBar + 1используется как «предыдущий»).AllowBuyOpen/AllowSellOpen– разрешить открытие длинных/коротких позиций.AllowBuyClose/AllowSellClose– разрешить закрытие длинных/коротких позиций по встречному сигналу.StopLossPoints– расстояние стоп-лосса в пунктах (умножается наPriceStep).TakeProfitPoints– расстояние тейк-профита в пунктах.
Значения по умолчанию
CandleType = TimeSpan.FromHours(4).TimeFrame()TradeVolume = 1mEmaLength = 50.01mAppliedPrices = AppliedPrices.CloseDeviationPercent = 0.1mShift = 1PriceShift = 0mDigit = 2SignalBar = 1AllowBuyOpen = trueAllowSellOpen = trueAllowBuyClose = trueAllowSellClose = trueStopLossPoints = 1000mTakeProfitPoints = 2000m
Фильтры
- Категория: Пробой / Возврат в канал
- Направление: Лонг и Шорт
- Индикаторы: Полиномиальные EMA-оболочки
- Стопы: Да (стоп и тейк в пунктах)
- Таймфрейм: Свинг (по умолчанию 4H)
- Риск: Средний — сделки возникают только после возврата цены из экстремума
- Сезонность: Нет
- Нейросети: Нет
- Дивергенции: Нет
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>
/// Strategy based on the Color PEMA Envelopes Digit indicator.
/// A long position is opened when price breaks above the upper envelope and then returns inside it,
/// while a short position is opened on a mirror setup around the lower envelope.
/// Previous color codes from the original indicator are used to detect these transitions.
/// </summary>
public class ColorPemaEnvelopesDigitSystemStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _emaLength;
private readonly StrategyParam<AppliedPrices> _appliedPrice;
private readonly StrategyParam<decimal> _deviationPercent;
private readonly StrategyParam<int> _shift;
private readonly StrategyParam<decimal> _priceShift;
private readonly StrategyParam<int> _digit;
private readonly StrategyParam<int> _signalBar;
private readonly StrategyParam<bool> _allowBuyOpen;
private readonly StrategyParam<bool> _allowSellOpen;
private readonly StrategyParam<bool> _allowBuyClose;
private readonly StrategyParam<bool> _allowSellClose;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<decimal> _tradeVolume;
private readonly PemaIndicator _pema = new();
private readonly List<decimal> _upperHistory = new();
private readonly List<decimal> _lowerHistory = new();
private readonly List<int> _colorHistory = new();
/// <summary>
/// Initializes a new instance of <see cref="ColorPemaEnvelopesDigitSystemStrategy"/>.
/// </summary>
public ColorPemaEnvelopesDigitSystemStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe used for calculations", "General");
_tradeVolume = Param(nameof(TradeVolume), 1m)
.SetGreaterThanZero()
.SetDisplay("Trade Volume", "Order volume used for entries", "Trading");
_emaLength = Param(nameof(EmaLength), 50.01m)
.SetGreaterThanZero()
.SetDisplay("PEMA Length", "Length of each EMA stage in PEMA", "Indicator");
_appliedPrice = Param(nameof(AppliedPrice), AppliedPrices.Close)
.SetDisplay("Applied Price", "Price source passed to PEMA", "Indicator");
_deviationPercent = Param(nameof(DeviationPercent), 0.1m)
.SetGreaterThanZero()
.SetDisplay("Envelope Deviation", "Percentage width of envelopes", "Indicator");
_shift = Param(nameof(Shift), 1)
.SetRange(0, 10)
.SetDisplay("Shift", "Bars used to offset envelope comparison", "Indicator");
_priceShift = Param(nameof(PriceShift), 0m)
.SetDisplay("Price Shift", "Additional absolute shift applied to envelopes", "Indicator");
_digit = Param(nameof(Digit), 2)
.SetRange(0, 8)
.SetDisplay("Rounding Digits", "Extra precision digits for rounding", "Indicator");
_signalBar = Param(nameof(SignalBar), 1)
.SetRange(1, 10)
.SetDisplay("Signal Bar", "How many completed bars back to check colors", "Logic");
_allowBuyOpen = Param(nameof(AllowBuyOpen), true)
.SetDisplay("Allow Buy Open", "Enable new long entries", "Logic");
_allowSellOpen = Param(nameof(AllowSellOpen), true)
.SetDisplay("Allow Sell Open", "Enable new short entries", "Logic");
_allowBuyClose = Param(nameof(AllowBuyClose), true)
.SetDisplay("Allow Buy Close", "Allow closing long positions on opposite signal", "Logic");
_allowSellClose = Param(nameof(AllowSellClose), true)
.SetDisplay("Allow Sell Close", "Allow closing short positions on opposite signal", "Logic");
_stopLossPoints = Param(nameof(StopLossPoints), 10m)
.SetRange(0m, 100000m)
.SetDisplay("Stop Loss Points", "Distance for protective stop", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 20m)
.SetRange(0m, 100000m)
.SetDisplay("Take Profit Points", "Distance for profit target", "Risk");
}
/// <summary>
/// Candle type for calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Order volume used for entries.
/// </summary>
public decimal TradeVolume
{
get => _tradeVolume.Value;
set => _tradeVolume.Value = value;
}
/// <summary>
/// Length of each EMA layer inside PEMA.
/// </summary>
public decimal EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
/// <summary>
/// Price source passed to PEMA.
/// </summary>
public AppliedPrices AppliedPrice
{
get => _appliedPrice.Value;
set => _appliedPrice.Value = value;
}
/// <summary>
/// Percentage width of the envelopes around PEMA.
/// </summary>
public decimal DeviationPercent
{
get => _deviationPercent.Value;
set => _deviationPercent.Value = value;
}
/// <summary>
/// Bars used to offset envelope comparison.
/// </summary>
public int Shift
{
get => _shift.Value;
set => _shift.Value = value;
}
/// <summary>
/// Additional absolute shift applied to envelopes.
/// </summary>
public decimal PriceShift
{
get => _priceShift.Value;
set => _priceShift.Value = value;
}
/// <summary>
/// Extra precision digits for rounding PEMA.
/// </summary>
public int Digit
{
get => _digit.Value;
set => _digit.Value = value;
}
/// <summary>
/// Completed bars back to inspect for signals.
/// </summary>
public int SignalBar
{
get => _signalBar.Value;
set => _signalBar.Value = value;
}
/// <summary>
/// Enable opening of long positions.
/// </summary>
public bool AllowBuyOpen
{
get => _allowBuyOpen.Value;
set => _allowBuyOpen.Value = value;
}
/// <summary>
/// Enable opening of short positions.
/// </summary>
public bool AllowSellOpen
{
get => _allowSellOpen.Value;
set => _allowSellOpen.Value = value;
}
/// <summary>
/// Allow closing of long positions on opposite signal.
/// </summary>
public bool AllowBuyClose
{
get => _allowBuyClose.Value;
set => _allowBuyClose.Value = value;
}
/// <summary>
/// Allow closing of short positions on opposite signal.
/// </summary>
public bool AllowSellClose
{
get => _allowSellClose.Value;
set => _allowSellClose.Value = value;
}
/// <summary>
/// Distance to the protective stop in price points.
/// </summary>
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Distance to the profit target in price points.
/// </summary>
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_pema.Reset();
_upperHistory.Clear();
_lowerHistory.Clear();
_colorHistory.Clear();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_pema.Length = EmaLength;
_pema.Digit = Digit;
_pema.PriceStep = Security?.PriceStep ?? 1m;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _pema);
DrawOwnTrades(area);
}
var step = Security?.PriceStep ?? 1m;
StartProtection(
takeProfit: new Unit(TakeProfitPoints * step, UnitTypes.Absolute),
stopLoss: new Unit(StopLossPoints * step, UnitTypes.Absolute));
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
{
return;
}
var price = GetAppliedPrice(candle);
// Calculate PEMA base value for the current candle.
var pemaValue = _pema.Process(new DecimalIndicatorValue(_pema, price, candle.OpenTime));
if (!pemaValue.IsFinal)
{
return;
}
var pema = pemaValue.GetValue<decimal>();
var upperCurrent = (1m + DeviationPercent / 100m) * pema + PriceShift;
var lowerCurrent = (1m - DeviationPercent / 100m) * pema + PriceShift;
var shift = Math.Max(0, Shift);
decimal? upperForColor;
decimal? lowerForColor;
if (shift == 0)
{
upperForColor = upperCurrent;
lowerForColor = lowerCurrent;
}
else
{
upperForColor = _upperHistory.Count >= shift ? _upperHistory[0] : (decimal?)null;
lowerForColor = _lowerHistory.Count >= shift ? _lowerHistory[0] : (decimal?)null;
}
// Determine the color code based on envelope breakouts.
var currentColor = CalculateColor(candle, upperForColor, lowerForColor);
if (!_pema.IsFormed)
{
UpdateHistories(currentColor, upperCurrent, lowerCurrent, shift);
return;
}
var hasRecentColor = TryGetColor(SignalBar, out var recentColor);
var hasOlderColor = TryGetColor(SignalBar + 1, out var olderColor);
var buyOpenSignal = false;
var sellOpenSignal = false;
var buyCloseSignal = false;
var sellCloseSignal = false;
// Evaluate signals using stored color history to reproduce the MQL logic.
if (hasOlderColor)
{
if (olderColor > 2)
{
if (AllowBuyOpen && hasRecentColor && recentColor < 3)
buyOpenSignal = true;
if (AllowSellClose)
sellCloseSignal = true;
}
else if (olderColor < 2)
{
if (AllowSellOpen && hasRecentColor && recentColor > 1)
sellOpenSignal = true;
if (AllowBuyClose)
buyCloseSignal = true;
}
}
// Close positions according to signal permissions.
if (buyCloseSignal && Position > 0)
SellMarket();
if (sellCloseSignal && Position < 0)
BuyMarket();
// Open new trades after handling position closures.
if (buyOpenSignal && Position <= 0)
{
BuyMarket();
}
else if (sellOpenSignal && Position >= 0)
{
SellMarket();
}
UpdateHistories(currentColor, upperCurrent, lowerCurrent, shift);
}
private decimal GetAppliedPrice(ICandleMessage candle)
{
var open = candle.OpenPrice;
var high = candle.HighPrice;
var low = candle.LowPrice;
var close = candle.ClosePrice;
return AppliedPrice switch
{
AppliedPrices.Close => close,
AppliedPrices.Open => open,
AppliedPrices.High => high,
AppliedPrices.Low => low,
AppliedPrices.Median => (high + low) / 2m,
AppliedPrices.Typical => (close + high + low) / 3m,
AppliedPrices.Weighted => (2m * close + high + low) / 4m,
AppliedPrices.Simple => (open + close) / 2m,
AppliedPrices.Quarter => (open + close + high + low) / 4m,
AppliedPrices.TrendFollow0 => close > open ? high : close < open ? low : close,
AppliedPrices.TrendFollow1 => close > open ? (high + close) / 2m : close < open ? (low + close) / 2m : close,
AppliedPrices.Demark => CalculateDemarkPrice(open, high, low, close),
_ => close,
};
}
private static decimal CalculateDemarkPrice(decimal open, decimal high, decimal low, decimal close)
{
var res = high + low + close;
if (close < open)
res = (res + low) / 2m;
else if (close > open)
res = (res + high) / 2m;
else
res = (res + close) / 2m;
return ((res - low) + (res - high)) / 2m;
}
private static int CalculateColor(ICandleMessage candle, decimal? upper, decimal? lower)
{
const int defaultColor = 2;
var color = defaultColor;
if (upper is decimal up)
{
if (candle.ClosePrice > up)
color = candle.OpenPrice <= candle.ClosePrice ? 4 : 3;
}
if (lower is decimal down)
{
if (candle.ClosePrice < down)
color = candle.OpenPrice > candle.ClosePrice ? 0 : 1;
}
return color;
}
private bool TryGetColor(int barsAgo, out int color)
{
if (barsAgo <= 0 || _colorHistory.Count < barsAgo)
{
color = default;
return false;
}
color = _colorHistory[^barsAgo];
return true;
}
private void UpdateHistories(int currentColor, decimal upperCurrent, decimal lowerCurrent, int shift)
{
_colorHistory.Add(currentColor);
var maxColors = Math.Max(3, Math.Max(shift, SignalBar) + 3);
while (_colorHistory.Count > maxColors)
{
try { _colorHistory.RemoveAt(0); } catch { break; }
}
if (shift > 0)
{
_upperHistory.Add(upperCurrent);
while (_upperHistory.Count > shift)
{
try { _upperHistory.RemoveAt(0); } catch { break; }
}
_lowerHistory.Add(lowerCurrent);
while (_lowerHistory.Count > shift)
{
try { _lowerHistory.RemoveAt(0); } catch { break; }
}
}
}
/// <summary>
/// Price source options for PEMA calculation.
/// </summary>
public enum AppliedPrices
{
Close = 1,
Open,
High,
Low,
Median,
Typical,
Weighted,
Simple,
Quarter,
TrendFollow0,
TrendFollow1,
Demark
}
private class PemaIndicator : BaseIndicator
{
public decimal Length { get; set; } = 50.01m;
public int Digit { get; set; } = 2;
public decimal PriceStep { get; set; } = 1m;
private readonly decimal[] _emaValues = new decimal[8];
private bool _hasHistory;
private int _count;
protected override IIndicatorValue OnProcess(IIndicatorValue input)
{
var price = input.GetValue<decimal>();
var length = Length <= 0m ? 1m : Length;
var alpha = 2m / (length + 1m);
var oneMinusAlpha = 1m - alpha;
var current = price;
for (var i = 0; i < _emaValues.Length; i++)
{
var prev = _hasHistory ? _emaValues[i] : current;
var ema = alpha * current + oneMinusAlpha * prev;
_emaValues[i] = ema;
current = ema;
}
_hasHistory = true;
_count++;
var pema = 8m * _emaValues[0]
- 28m * _emaValues[1]
+ 56m * _emaValues[2]
- 70m * _emaValues[3]
+ 56m * _emaValues[4]
- 28m * _emaValues[5]
+ 8m * _emaValues[6]
- _emaValues[7];
var digits = Math.Max(0, Digit);
var step = PriceStep > 0m ? PriceStep : 1m;
var factor = step * (decimal)Math.Pow(10, digits);
if (factor > 0m)
pema = Math.Round(pema / factor, MidpointRounding.AwayFromZero) * factor;
IsFormed = _count > 8;
return new DecimalIndicatorValue(this, pema, input.Time) { IsFinal = true };
}
public override void Reset()
{
base.Reset();
Array.Clear(_emaValues, 0, _emaValues.Length);
_hasHistory = false;
_count = 0;
}
}
}
import clr
import math
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 color_pema_envelopes_digit_system_strategy(Strategy):
"""PEMA Envelopes with color-coded breakout signals and SL/TP protection."""
def __init__(self):
super(color_pema_envelopes_digit_system_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe used for calculations", "General")
self._ema_length = self.Param("EmaLength", 50.01) \
.SetGreaterThanZero() \
.SetDisplay("PEMA Length", "Length of each EMA stage in PEMA", "Indicator")
self._deviation_pct = self.Param("DeviationPercent", 0.1) \
.SetGreaterThanZero() \
.SetDisplay("Envelope Deviation", "Percentage width of envelopes", "Indicator")
self._shift = self.Param("Shift", 1) \
.SetDisplay("Shift", "Bars used to offset envelope comparison", "Indicator")
self._price_shift = self.Param("PriceShift", 0.0) \
.SetDisplay("Price Shift", "Additional absolute shift applied to envelopes", "Indicator")
self._digit = self.Param("Digit", 2) \
.SetDisplay("Rounding Digits", "Extra precision digits for rounding", "Indicator")
self._signal_bar = self.Param("SignalBar", 1) \
.SetDisplay("Signal Bar", "How many completed bars back to check colors", "Logic")
self._allow_buy_open = self.Param("AllowBuyOpen", True) \
.SetDisplay("Allow Buy Open", "Enable new long entries", "Logic")
self._allow_sell_open = self.Param("AllowSellOpen", True) \
.SetDisplay("Allow Sell Open", "Enable new short entries", "Logic")
self._allow_buy_close = self.Param("AllowBuyClose", True) \
.SetDisplay("Allow Buy Close", "Allow closing long positions on opposite signal", "Logic")
self._allow_sell_close = self.Param("AllowSellClose", True) \
.SetDisplay("Allow Sell Close", "Allow closing short positions on opposite signal", "Logic")
self._stop_loss_points = self.Param("StopLossPoints", 10.0) \
.SetDisplay("Stop Loss Points", "Distance for protective stop", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 20.0) \
.SetDisplay("Take Profit Points", "Distance for profit target", "Risk")
# PEMA state
self._ema_values = [0.0] * 8
self._has_history = False
self._pema_count = 0
# History buffers
self._upper_history = []
self._lower_history = []
self._color_history = []
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaLength(self):
return self._ema_length.Value
@property
def DeviationPercent(self):
return self._deviation_pct.Value
@property
def Shift(self):
return self._shift.Value
@property
def PriceShift(self):
return self._price_shift.Value
@property
def Digit(self):
return self._digit.Value
@property
def SignalBar(self):
return self._signal_bar.Value
@property
def AllowBuyOpen(self):
return self._allow_buy_open.Value
@property
def AllowSellOpen(self):
return self._allow_sell_open.Value
@property
def AllowBuyClose(self):
return self._allow_buy_close.Value
@property
def AllowSellClose(self):
return self._allow_sell_close.Value
@property
def StopLossPoints(self):
return self._stop_loss_points.Value
@property
def TakeProfitPoints(self):
return self._take_profit_points.Value
def OnStarted2(self, time):
super(color_pema_envelopes_digit_system_strategy, self).OnStarted2(time)
self._ema_values = [0.0] * 8
self._has_history = False
self._pema_count = 0
self._upper_history = []
self._lower_history = []
self._color_history = []
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
sec = self.Security
step = float(sec.PriceStep) if sec is not None and sec.PriceStep is not None and float(sec.PriceStep) > 0 else 1.0
sl = float(self.StopLossPoints) * step
tp = float(self.TakeProfitPoints) * step
if sl > 0 or tp > 0:
self.StartProtection(
takeProfit=Unit(tp, UnitTypes.Absolute) if tp > 0 else None,
stopLoss=Unit(sl, UnitTypes.Absolute) if sl > 0 else None
)
def _calc_pema(self, price):
length = float(self.EmaLength)
if length <= 0:
length = 1.0
alpha = 2.0 / (length + 1.0)
one_minus = 1.0 - alpha
current = price
for i in range(8):
prev = self._ema_values[i] if self._has_history else current
ema = alpha * current + one_minus * prev
self._ema_values[i] = ema
current = ema
self._has_history = True
self._pema_count += 1
pema = (8.0 * self._ema_values[0]
- 28.0 * self._ema_values[1]
+ 56.0 * self._ema_values[2]
- 70.0 * self._ema_values[3]
+ 56.0 * self._ema_values[4]
- 28.0 * self._ema_values[5]
+ 8.0 * self._ema_values[6]
- self._ema_values[7])
digits = max(0, int(self.Digit))
sec = self.Security
step = float(sec.PriceStep) if sec is not None and sec.PriceStep is not None and float(sec.PriceStep) > 0 else 1.0
factor = step * (10.0 ** digits)
if factor > 0:
pema = round(pema / factor) * factor
is_formed = self._pema_count > 8
return pema, is_formed
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
price = float(candle.ClosePrice)
pema, is_formed = self._calc_pema(price)
dev = float(self.DeviationPercent)
ps = float(self.PriceShift)
upper_current = (1.0 + dev / 100.0) * pema + ps
lower_current = (1.0 - dev / 100.0) * pema + ps
shift = max(0, int(self.Shift))
if shift == 0:
upper_for_color = upper_current
lower_for_color = lower_current
else:
upper_for_color = self._upper_history[0] if len(self._upper_history) >= shift else None
lower_for_color = self._lower_history[0] if len(self._lower_history) >= shift else None
current_color = self._calc_color(candle, upper_for_color, lower_for_color)
if not is_formed:
self._update_histories(current_color, upper_current, lower_current, shift)
return
sig_bar = int(self.SignalBar)
has_recent, recent_color = self._try_get_color(sig_bar)
has_older, older_color = self._try_get_color(sig_bar + 1)
buy_open_signal = False
sell_open_signal = False
buy_close_signal = False
sell_close_signal = False
if has_older:
if older_color > 2:
if self.AllowBuyOpen and has_recent and recent_color < 3:
buy_open_signal = True
if self.AllowSellClose:
sell_close_signal = True
elif older_color < 2:
if self.AllowSellOpen and has_recent and recent_color > 1:
sell_open_signal = True
if self.AllowBuyClose:
buy_close_signal = True
if buy_close_signal and self.Position > 0:
self.SellMarket()
if sell_close_signal and self.Position < 0:
self.BuyMarket()
if buy_open_signal and self.Position <= 0:
self.BuyMarket()
elif sell_open_signal and self.Position >= 0:
self.SellMarket()
self._update_histories(current_color, upper_current, lower_current, shift)
def _calc_color(self, candle, upper, lower):
color = 2
close = float(candle.ClosePrice)
o = float(candle.OpenPrice)
if upper is not None:
if close > upper:
color = 4 if o <= close else 3
if lower is not None:
if close < lower:
color = 0 if o > close else 1
return color
def _try_get_color(self, bars_ago):
if bars_ago <= 0 or len(self._color_history) < bars_ago:
return False, 0
return True, self._color_history[-bars_ago]
def _update_histories(self, current_color, upper_current, lower_current, shift):
self._color_history.append(current_color)
max_colors = max(3, max(shift, int(self.SignalBar)) + 3)
while len(self._color_history) > max_colors:
self._color_history.pop(0)
if shift > 0:
self._upper_history.append(upper_current)
while len(self._upper_history) > shift:
self._upper_history.pop(0)
self._lower_history.append(lower_current)
while len(self._lower_history) > shift:
self._lower_history.pop(0)
def OnReseted(self):
super(color_pema_envelopes_digit_system_strategy, self).OnReseted()
self._ema_values = [0.0] * 8
self._has_history = False
self._pema_count = 0
self._upper_history = []
self._lower_history = []
self._color_history = []
def CreateClone(self):
return color_pema_envelopes_digit_system_strategy()