Strategie Color PEMA Envelopes Digit System
Das Color PEMA Envelopes Digit System reproduziert die Logik des MetaTrader-Experten
Exp_Color_PEMA_Envelopes_Digit_System.mq5. Die Strategie wertet die Farbcodes aus,
die vom Color PEMA Envelopes-Indikator erzeugt werden: Wenn eine Kerze außerhalb der
oberen oder unteren Bande schließt, malt der Indikator eine spezielle Farbe, und sobald
der Preis wieder in den Kanal eintritt, wird ein Trade in Richtung des Ausbruchs ausgelöst.
Funktionsweise
- Die Strategie baut eine achtstufige Polynomielle EMA (PEMA) mit fraktionalen Längen auf, genau wie im ursprünglichen Indikator. Das Ergebnis wird auf die konfigurierte Genauigkeit gerundet und um den optionalen Preisversatz verschoben.
- Obere und untere Hüllkurven werden durch Anwendung einer prozentualen Abweichung um den PEMA-Wert erstellt.
- Jede abgeschlossene Kerze erhält einen Farbcode je nach ihrer Beziehung zu den verschobenen Hüllkurven:
4/3: Schlusskurs über der oberen Bande (bullischer/bärischer Körper).1/0: Schlusskurs unter der unteren Bande (bullischer/bärischer Körper).2: Preis bleibt innerhalb der Hüllkurve.
- Die Strategie liest die Farbe, die auf der
SignalBar + 1-Kerze aufgetreten ist, und vergleicht sie mit der Farbe derSignalBar-Kerze. Dies ahmt dieCopyBuffer-Aufrufe des Expertenberaters nach. - Wenn die ältere Farbe einen Ausbruch über die obere Bande anzeigt und die neuere Farbe in den Kanal zurückkehrt, ist ein Long-Einstieg erlaubt (wenn aktiviert) und jede Short-Position wird geschlossen. Die gespiegelte Logik wird für Short-Einstiege und zum Schließen von Long-Positionen verwendet.
- Schutz-Stop-Loss- und Take-Profit-Orders werden über das Risikmodul von StockSharp verwaltet.
Parameter
CandleType– Zeitrahmen für Analyse und Handel.TradeVolume– Menge, die mit Market-Orders gesendet wird.EmaLength– Fraktionale Länge, die von jeder EMA-Schicht in der PEMA-Kette verwendet wird.AppliedPrices– Quellpreis (Schluss, Eröffnung, Median, gewichtet, Trendfolge, DeMark, etc.).DeviationPercent– Prozentualer Abstand für beide Hüllkurven um PEMA.Shift– Anzahl abgeschlossener Kerzen zur Verschiebung des Hüllkurvenvergleichs.PriceShift– Zusätzliche absolute Verschiebung für beide Hüllkurven.Digit– Zusätzliche Präzisionsstellen beim Runden der PEMA-Ausgabe.SignalBar– Wie viele geschlossene Kerzen zurück die aktuelle Farbe abgelesen wird (die ältere Farbe wird eine Barre weiter zurück genommen).AllowBuyOpen/AllowSellOpen– Neue Long/Short-Einstiege aktivieren oder deaktivieren.AllowBuyClose/AllowSellClose– Schließen von Long/Short-Positionen bei entgegengesetzten Signalen erlauben.StopLossPoints– Schutz-Stop-Abstand in Preispunkten (multipliziert mitPriceStep).TakeProfitPoints– Gewinnziel-Abstand in Preispunkten.
Standardwerte
CandleType = TimeSpan.FromHours(4).TimeFrame()TradeVolume = 1mEmaLength = 50.01mAppliedPrices = AppliedPrices.CloseDeviationPercent = 0.1mShift = 1PriceShift = 0mDigit = 2SignalBar = 1AllowBuyOpen = trueAllowSellOpen = trueAllowBuyClose = trueAllowSellClose = trueStopLossPoints = 1000mTakeProfitPoints = 2000m
Filter
- Kategorie: Ausbruch / Kanal-Wiedereintritt
- Richtung: Long/Short
- Indikatoren: Polynomielle EMA-Hüllkurven
- Stops: Ja (punktbasierter Stop-Loss und Take-Profit)
- Zeitrahmen: Swing (Standard 4H)
- Risikolevel: Moderat – handelt nur, wenn der Preis von einem Extrem zurückkehrt
- Saisonalität: Keine
- Neuronale Netze: Nein
- Divergenz: Nein
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()