Graal-Fraktalkanal-Strategie
Überblick
Die Graal Fractal Channel Strategy ist eine StockSharp-Portierung des MetaTrader 4-Expertenberaters „Graal-003“. Der Algorithmus beobachtet Fraktalmuster mit fünf Kerzen und bestätigt Ausbrüche mithilfe adaptiver Preiskanäle. Wenn ein gültiges bullisches oder bärisches Fraktal erscheint, wertet die Strategie mehrere Filter aus (Fraktaltunnel, Schlusspreishülle und optionale Unterdrückung des flachen Marktes), bevor sie in die Ausbruchsrichtung eintritt. Ein optionales Williams %R-Overlay repliziert die manuelle Exit-Logik des ursprünglichen Roboters, während Hedge-Stop-Orders inszeniert werden können, um den Gegentrendschutz des EA zu emulieren.
Datenfluss und Indikatoren
- Abonniert die konfigurierten
CandleType (standardmäßig stündliche Kerzen).
- Erstellt eine fortlaufende Warteschlange der letzten
ChannelPeriod-Kerzen, um einen Donchian-ähnlichen Schlusskurskanal zu berechnen, der für flache Filter und Orientierungsprüfungen verwendet wird.
- Erkennt Fraktalhochs und -tiefs mit fünf Balken direkt aus dem Kerzenstrom.
- Füttert den integrierten
WilliamsPercentRange-Indikator, um optionale Ausgangssignale zu überwachen.
Handelsablauf
- Fraktale Erkennung – die Strategie verfolgt fünf aufeinanderfolgende fertige Kerzen. Wenn das Hoch/Tief des mittleren Balkens im Vergleich zu seinen beiden Vorgängern und zwei Followern das Extrem ist, registriert es ein oberes oder unteres Fraktal und markiert ein ausstehendes kurzes oder langes Signal.
- Signalalterung – jede neue Kerze erhöht das fraktale Alter. Wenn
SignalAgeLimit Balken ohne Ausführung vergehen, verfällt das ausstehende Signal.
- Kanalauswertung – der Rolling-Close-Kanal stellt drei Filter zur Verfügung:
- Fraktaltunnel: Wenn
UseFractalChannel aktiviert ist, muss der Schlusskurs innerhalb eines Prozentsatzes des Abstands zwischen dem letzten Fraktalhoch und -tief (DepthPercent) bleiben.
- Hohe/Niedrige Ausrichtung: Bei
UseHighLowChannel darf der Abschluss nur einen begrenzten Teil des Umschlags durchdringen (OrientationPercent).
- Flat Blocking: Wenn
AllowFlatTrading deaktiviert ist, werden Trades ausgesetzt, solange die Kanalbreite unter FlatThresholdPips bleibt.
- Auftragsausführung – Sobald die Filter bestanden wurden, normalisiert die Strategie den gewünschten
OrderVolume anhand der Instrumentenbeschränkungen und sendet einen Marktauftrag in fraktaler Richtung.
- Hedge-Stopps – wenn
UseCounterOrders aktiv ist, platziert der Algorithmus die entgegengesetzte Stop-Order zum Fraktalpreis plus/minus OffsetPips und spiegelt damit die Gegentrend-Stufe von EA wider.
- Williams wird beendet – wenn
UseWilliamsExit aktiviert ist, schließt der letzte Williams %R-Wert Long-Positionen, wenn er über -WilliamsThreshold steigt, und Short-Positionen, wenn er unter -100 + WilliamsThreshold fällt.
Stop-Loss- und Take-Profit-Distanzen sind optional. Immer wenn StopLossPips oder TakeProfitPips positiv ist, wandelt die Strategie den Pip-Abstand in einen absoluten Preisversatz unter Verwendung der Tick-Größe des Instruments um (mit der 3/5-stelligen Anpassung von EA) und delegiert die Verwaltung der Schutzorder an StartProtection.
Parameter
| Parameter |
Standard |
Beschreibung |
OrderVolume |
0.1 |
Basis-Market-Order-Größe vor der Normalisierung gegenüber den Instrumentenlimits. |
StopLossPips |
500 |
Schutzstoppabstand in Pips. In Preis umgerechnet und über StartProtection angewendet. |
TakeProfitPips |
500 |
Nehmen Sie die Gewinnentfernung in Pips. In Preis umgerechnet und über StartProtection angewendet. |
OffsetPips |
5 |
Zusätzlicher Abstand, der bei der Inszenierung von Stop-Orders gegen den Trend verwendet wird. |
ChannelPeriod |
14 |
Anzahl der zuletzt für den Schlusskurskanal gespeicherten Kerzen. |
UseFractalChannel |
false |
Erfordert, dass der Preis vor Eintritten innerhalb des inneren fraktalen Korridors bleibt. |
DepthPercent |
25 |
Prozentsatz des fraktalen Bereichs, der den inneren Korridor definiert. |
UseHighLowChannel |
false |
Aktiviert den Close-Channel-Orientierungsfilter im Donchian-Stil. |
OrientationPercent |
20 |
Zulässiges Eindringen in den nahen Kanal, wenn UseHighLowChannel wahr ist. |
AllowFlatTrading |
true |
Ermöglicht den Handel auch dann, wenn der Markt entsprechend der Breite des engen Kanals flach ist. |
FlatThresholdPips |
20 |
Erforderliche Mindestkanalbreite (in Pips), wenn der Flat-Trading deaktiviert ist. |
UseWilliamsExit |
false |
Aktiviert Williams %R-basierte Exit-Regeln. |
WilliamsPeriod |
14 |
Rückblickzeitraum für den Indikator Williams %R. |
WilliamsThreshold |
30 |
Empfindlichkeitsschwelle (Prozentpunkte) für Williams %R Exits. |
UseCounterOrders |
false |
Platziert nach einem Markteintritt die entgegengesetzte Stop-Order. |
SinglePosition |
false |
Blockiert zusätzliche Eingaben in die gleiche Richtung, während eine Position offen ist. |
SignalAgeLimit |
3 |
Maximale Anzahl neuer Balken, während der ein Fraktalsignal gültig bleibt. |
CandleType |
H1 |
Für die Analyse verwendete Kerzendatenreihe (standardmäßig ein einstündiger Zeitrahmen). |
Nutzungshinweise
- Die Strategie erwartet Instrumente mit einem gültigen
PriceStep, MinVolume und VolumeStep, damit die Volumennormalisierung und die Pip-Umrechnung korrekt funktionieren.
- Gegentrend-Orders werden automatisch storniert, wenn die Position geschlossen wird, die Strategie stoppt oder die Funktion deaktiviert wird.
- Williams %R-Exits fungieren als Sicherheitsnetz und können Positionen schließen, selbst wenn das ursprüngliche Fraktalsignal noch aktiv ist.
- Der Algorithmus setzt den gesamten zwischengespeicherten Status (Fraktalpuffer, Williams-Verlauf, bereitgestellte Bestellungen) zurück, wenn
OnReseted ausgelöst wird.
- Die StockSharp-Implementierung verwendet
SubscribeCandles().Bind(...)-Abonnements auf hoher Ebene anstelle manueller Indikatorschleifen.
- Schutzstopps basieren auf
StartProtection, sodass keine direkte Stop-/Limit-Order-Buchhaltung erforderlich ist.
- Das Volumen wird vor dem Senden von Bestellungen anhand der Börsenlimits normalisiert und entspricht den StockSharp-Konventionen.
Präzise prüfbare Regeln: Ein Fünf-Kerzen-Fraktal erfordert strikte Vergleiche der mittleren Kerze mit je zwei Nachbarn. Ist eine Kerze gleichzeitig Hoch- und Tief-Fraktal, entsteht kein Richtungssignal. Der Kanal benötigt ChannelPeriod abgeschlossene Schlusskurse. Der Fraktalfilter fordert low+(high-low)*DepthPercent/100 <= close <= high-(high-low)*DepthPercent/100. Der Orientierungsfilter erlaubt Käufe nur im obersten OrientationPercent%-Bereich und Verkäufe im untersten Bereich. Nach mehr als SignalAgeLimit weiteren abgeschlossenen Kerzen verfällt das Signal. Eine Umkehr erfolgt mit einer einzigen Netto-Market-Order (Betrag der alten Position plus normalisiertes OrderVolume). Der Gegen-Stop wird erst nach tatsächlicher Ausführung und nur bei bekanntem Gegenfraktal platziert.
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using System.Linq;
using StockSharp.Algo;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.MatchingEngine;
using StockSharp.Messages;
/// <summary>
/// Trades confirmed five-bar fractal signals with optional channel, Williams %R
/// and counter-stop filters. The indicator midpoint is not an entry signal.
/// </summary>
public class GraalFractalChannelStrategy : Strategy
{
private readonly StrategyParam<decimal> _orderVolume;
private readonly StrategyParam<decimal> _stopLossPips;
private readonly StrategyParam<decimal> _takeProfitPips;
private readonly StrategyParam<decimal> _offsetPips;
private readonly StrategyParam<int> _channelPeriod;
private readonly StrategyParam<bool> _useFractalChannel;
private readonly StrategyParam<decimal> _depthPercent;
private readonly StrategyParam<bool> _useHighLowChannel;
private readonly StrategyParam<decimal> _orientationPercent;
private readonly StrategyParam<bool> _allowFlatTrading;
private readonly StrategyParam<decimal> _flatThresholdPips;
private readonly StrategyParam<bool> _useWilliamsExit;
private readonly StrategyParam<int> _williamsPeriod;
private readonly StrategyParam<decimal> _williamsThreshold;
private readonly StrategyParam<bool> _useCounterOrders;
private readonly StrategyParam<bool> _singlePosition;
private readonly StrategyParam<int> _signalAgeLimit;
private readonly StrategyParam<DataType> _candleType;
private readonly Queue<(decimal High, decimal Low)> _bars = new();
private readonly Queue<decimal> _closes = new();
private decimal? _upperFractal;
private decimal? _lowerFractal;
private Sides? _pendingSide;
private int _pendingAge;
private int _finishedBars;
private Order _entryOrder;
private Order _counterOrder;
private bool _closing;
public decimal OrderVolume { get => _orderVolume.Value; set => _orderVolume.Value = value; }
public decimal StopLossPips { get => _stopLossPips.Value; set => _stopLossPips.Value = value; }
public decimal TakeProfitPips { get => _takeProfitPips.Value; set => _takeProfitPips.Value = value; }
public decimal OffsetPips { get => _offsetPips.Value; set => _offsetPips.Value = value; }
public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
public bool UseFractalChannel { get => _useFractalChannel.Value; set => _useFractalChannel.Value = value; }
public decimal DepthPercent { get => _depthPercent.Value; set => _depthPercent.Value = value; }
public bool UseHighLowChannel { get => _useHighLowChannel.Value; set => _useHighLowChannel.Value = value; }
public decimal OrientationPercent { get => _orientationPercent.Value; set => _orientationPercent.Value = value; }
public bool AllowFlatTrading { get => _allowFlatTrading.Value; set => _allowFlatTrading.Value = value; }
public decimal FlatThresholdPips { get => _flatThresholdPips.Value; set => _flatThresholdPips.Value = value; }
public bool UseWilliamsExit { get => _useWilliamsExit.Value; set => _useWilliamsExit.Value = value; }
public int WilliamsPeriod { get => _williamsPeriod.Value; set => _williamsPeriod.Value = value; }
public decimal WilliamsThreshold { get => _williamsThreshold.Value; set => _williamsThreshold.Value = value; }
public bool UseCounterOrders { get => _useCounterOrders.Value; set => _useCounterOrders.Value = value; }
public bool SinglePosition { get => _singlePosition.Value; set => _singlePosition.Value = value; }
public int SignalAgeLimit { get => _signalAgeLimit.Value; set => _signalAgeLimit.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public GraalFractalChannelStrategy()
{
_orderVolume = Param(nameof(OrderVolume), 0.1m).SetGreaterThanZero().SetDisplay("Volume", "Entry volume", "Trading");
_stopLossPips = Param(nameof(StopLossPips), 500m).SetNotNegative().SetDisplay("Stop Loss", "Pip distance", "Protection");
_takeProfitPips = Param(nameof(TakeProfitPips), 500m).SetNotNegative().SetDisplay("Take Profit", "Pip distance", "Protection");
_offsetPips = Param(nameof(OffsetPips), 5m).SetNotNegative().SetDisplay("Offset", "Counter stop offset in pips", "Protection");
_channelPeriod = Param(nameof(ChannelPeriod), 14).SetGreaterThanZero().SetDisplay("Channel Period", "Completed closes", "Indicators");
_useFractalChannel = Param(nameof(UseFractalChannel), false);
_depthPercent = Param(nameof(DepthPercent), 25m);
_useHighLowChannel = Param(nameof(UseHighLowChannel), false);
_orientationPercent = Param(nameof(OrientationPercent), 20m);
_allowFlatTrading = Param(nameof(AllowFlatTrading), true);
_flatThresholdPips = Param(nameof(FlatThresholdPips), 20m);
_useWilliamsExit = Param(nameof(UseWilliamsExit), false);
_williamsPeriod = Param(nameof(WilliamsPeriod), 14).SetGreaterThanZero();
_williamsThreshold = Param(nameof(WilliamsThreshold), 30m);
_useCounterOrders = Param(nameof(UseCounterOrders), false);
_singlePosition = Param(nameof(SinglePosition), false);
_signalAgeLimit = Param(nameof(SignalAgeLimit), 3).SetNotNegative();
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame());
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_bars.Clear();
_closes.Clear();
_upperFractal = null;
_lowerFractal = null;
_pendingSide = null;
_pendingAge = 0;
_finishedBars = 0;
_entryOrder = null;
_counterOrder = null;
_closing = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_bars.Clear();
_closes.Clear();
_upperFractal = null;
_lowerFractal = null;
_pendingSide = null;
_pendingAge = 0;
_finishedBars = 0;
_entryOrder = null;
_counterOrder = null;
_closing = false;
Volume = OrderVolume;
var pip = PipSize();
StartProtection(
takeProfit: TakeProfitPips > 0m ? new Unit(TakeProfitPips * pip, UnitTypes.Absolute) : new Unit(),
stopLoss: StopLossPips > 0m ? new Unit(StopLossPips * pip, UnitTypes.Absolute) : new Unit(),
useMarketOrders: true, isLocalStop: true);
var williams = new WilliamsR { Length = WilliamsPeriod };
SubscribeCandles(CandleType).Bind(williams, ProcessCandle).Start();
}
private decimal PipSize()
{
var step = Security?.PriceStep ?? 0m;
if (step <= 0m)
throw new InvalidOperationException("Graal Fractal Channel requires a positive instrument price step.");
return Security?.Decimals is 3 or 5 ? step * 10m : step;
}
private decimal NormalizeVolume(decimal volume)
{
if (Security?.VolumeStep is decimal step && step > 0m)
volume = decimal.Floor(volume / step) * step;
if (Security?.MinVolume is decimal min && min > 0m)
volume = Math.Max(volume, min);
if (Security?.MaxVolume is decimal max && max > 0m)
volume = Math.Min(volume, max);
return volume;
}
private static bool Pending(Order order) => order is not null &&
order.State is not (OrderStates.Done or OrderStates.Failed);
private void CancelCounter()
{
if (_counterOrder is not null && _counterOrder.State == OrderStates.Active)
CancelOrder(_counterOrder);
_counterOrder = null;
}
private void ProcessCandle(ICandleMessage candle, decimal williams)
{
if (candle.State != CandleStates.Finished)
return;
_finishedBars++;
if (_pendingSide.HasValue && ++_pendingAge > SignalAgeLimit)
_pendingSide = null;
_bars.Enqueue((candle.HighPrice, candle.LowPrice));
if (_bars.Count > 5)
_bars.Dequeue();
_closes.Enqueue(candle.ClosePrice);
if (_closes.Count > ChannelPeriod)
_closes.Dequeue();
if (_bars.Count == 5)
{
var bars = _bars.ToArray();
var upper = bars.Where((_, i) => i != 2).All(b => bars[2].High > b.High);
var lower = bars.Where((_, i) => i != 2).All(b => bars[2].Low < b.Low);
if (upper) _upperFractal = bars[2].High;
if (lower) _lowerFractal = bars[2].Low;
// A bar can be both a high and a low fractal: it is not a directional signal.
if (upper != lower)
{
_pendingSide = upper ? Sides.Sell : Sides.Buy;
_pendingAge = 0;
}
else if (upper)
_pendingSide = null;
}
if (!UseCounterOrders || Position == 0m)
CancelCounter();
if (_closing)
{
if (Position != 0m)
return;
_closing = false;
}
if (UseWilliamsExit && _finishedBars >= WilliamsPeriod &&
(Position > 0m && williams > -WilliamsThreshold ||
Position < 0m && williams < -100m + WilliamsThreshold))
{
CancelCounter();
_pendingSide = null;
_closing = true;
ClosePosition();
return;
}
if (!_pendingSide.HasValue || Pending(_entryOrder) || !PassesFilters(candle.ClosePrice, _pendingSide.Value))
return;
var side = _pendingSide.Value;
if (SinglePosition && (side == Sides.Buy ? Position > 0m : Position < 0m))
return;
var volume = NormalizeVolume(OrderVolume);
if (volume <= 0m)
return;
// Reverse the existing net position in one order, rather than submitting
// two uncoordinated default-volume orders.
if (side == Sides.Buy && Position < 0m || side == Sides.Sell && Position > 0m)
volume += Math.Abs(Position);
CancelCounter();
_entryOrder = new Order
{
Security = Security,
Portfolio = Portfolio,
Type = OrderTypes.Market,
Side = side,
Volume = volume,
Comment = "Graal fractal entry",
};
RegisterOrder(_entryOrder);
_pendingSide = null;
}
private bool PassesFilters(decimal close, Sides side)
{
if (_closes.Count < ChannelPeriod)
return false;
var highest = _closes.Max();
var lowest = _closes.Min();
var width = highest - lowest;
if (!AllowFlatTrading && width < FlatThresholdPips * PipSize())
return false;
if (UseFractalChannel)
{
if (_upperFractal is not decimal high || _lowerFractal is not decimal low || high <= low)
return false;
var inner = (high - low) * DepthPercent / 100m;
if (close < low + inner || close > high - inner)
return false;
}
if (UseHighLowChannel)
{
if (width <= 0m)
return false;
var allowed = width * OrientationPercent / 100m;
if (side == Sides.Buy && close < highest - allowed ||
side == Sides.Sell && close > lowest + allowed)
return false;
}
return true;
}
protected override void OnOwnTradeReceived(MyTrade trade)
{
base.OnOwnTradeReceived(trade);
if (Position == 0m || !UseCounterOrders)
{
CancelCounter();
return;
}
if (trade?.Order != _entryOrder || trade.Trade is null)
return;
CancelCounter();
var side = Position > 0m ? Sides.Sell : Sides.Buy;
var level = side == Sides.Sell ? _lowerFractal - OffsetPips * PipSize()
: _upperFractal + OffsetPips * PipSize();
if (level is not decimal price || price <= 0m)
return;
_counterOrder = new Order
{
Security = Security,
Portfolio = Portfolio,
Type = OrderTypes.Conditional,
Side = side,
Volume = Math.Abs(Position),
Condition = new StopOrderCondition { ActivationPrice = Security.ShrinkPrice(price, ShrinkRules.Auto) },
Comment = "Graal counter stop",
};
RegisterOrder(_counterOrder);
}
}
import clr
import math
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
clr.AddReference("StockSharp.BusinessEntities")
clr.AddReference("StockSharp.MatchingEngine")
from System import TimeSpan, Decimal
from StockSharp.Messages import (
DataType, CandleStates, Sides, OrderStates, OrderTypes,
Unit, UnitTypes, ShrinkRules,
)
from StockSharp.Algo import TraderHelper
from StockSharp.Algo.Indicators import WilliamsR
from StockSharp.Algo.Strategies import Strategy
from StockSharp.BusinessEntities import Order
from StockSharp.MatchingEngine import StopOrderCondition
class graal_fractal_channel_strategy(Strategy):
"""Confirmed five-bar fractals, optional channel filters and Williams exits."""
def __init__(self):
super(graal_fractal_channel_strategy, self).__init__()
self._order_volume = self.Param("OrderVolume", 0.1).SetGreaterThanZero()
self._stop_loss_pips = self.Param("StopLossPips", 500.0).SetNotNegative()
self._take_profit_pips = self.Param("TakeProfitPips", 500.0).SetNotNegative()
self._offset_pips = self.Param("OffsetPips", 5.0).SetNotNegative()
self._channel_period = self.Param("ChannelPeriod", 14).SetGreaterThanZero()
self._use_fractal_channel = self.Param("UseFractalChannel", False)
self._depth_percent = self.Param("DepthPercent", 25.0)
self._use_high_low_channel = self.Param("UseHighLowChannel", False)
self._orientation_percent = self.Param("OrientationPercent", 20.0)
self._allow_flat_trading = self.Param("AllowFlatTrading", True)
self._flat_threshold_pips = self.Param("FlatThresholdPips", 20.0)
self._use_williams_exit = self.Param("UseWilliamsExit", False)
self._williams_period = self.Param("WilliamsPeriod", 14).SetGreaterThanZero()
self._williams_threshold = self.Param("WilliamsThreshold", 30.0)
self._use_counter_orders = self.Param("UseCounterOrders", False)
self._single_position = self.Param("SinglePosition", False)
self._signal_age_limit = self.Param("SignalAgeLimit", 3).SetNotNegative()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._clear_state()
@property
def order_volume(self): return self._order_volume.Value
@property
def channel_period(self): return self._channel_period.Value
@property
def williams_period(self): return self._williams_period.Value
@property
def candle_type(self): return self._candle_type.Value
def _clear_state(self):
self._bars = []
self._closes = []
self._upper_fractal = None
self._lower_fractal = None
self._pending_side = None
self._pending_age = 0
self._finished_bars = 0
self._entry_order = None
self._counter_order = None
self._closing = False
def GetWorkingSecurities(self):
return [(self.Security, self.candle_type)]
def OnReseted(self):
super(graal_fractal_channel_strategy, self).OnReseted()
self._clear_state()
def _pip_size(self):
step = float(self.Security.PriceStep or 0)
if step <= 0:
raise ValueError("Graal Fractal Channel requires a positive instrument price step")
return step * (10.0 if self.Security.Decimals in (3, 5) else 1.0)
def _normalized_volume(self, size):
size = float(size)
step = float(self.Security.VolumeStep or 0)
if step > 0:
size = math.floor(size / step) * step
minimum = float(self.Security.MinVolume or 0)
maximum = float(self.Security.MaxVolume or 0)
if minimum > 0:
size = max(size, minimum)
if maximum > 0:
size = min(size, maximum)
return max(size, 0.0)
@staticmethod
def _pending(order):
return order is not None and order.State not in (OrderStates.Done, OrderStates.Failed)
def _cancel_counter(self):
if self._counter_order is not None and self._counter_order.State == OrderStates.Active:
self.CancelOrder(self._counter_order)
self._counter_order = None
def OnStarted2(self, time):
super(graal_fractal_channel_strategy, self).OnStarted2(time)
self._clear_state()
self.Volume = Decimal(self.order_volume)
pip = self._pip_size()
take = float(self._take_profit_pips.Value)
stop = float(self._stop_loss_pips.Value)
self.StartProtection(
Unit(Decimal(take * pip), UnitTypes.Absolute) if take > 0 else Unit(),
Unit(Decimal(stop * pip), UnitTypes.Absolute) if stop > 0 else Unit(),
useMarketOrders=True, isLocalStop=True,
)
wpr = WilliamsR()
wpr.Length = self.williams_period
self.SubscribeCandles(self.candle_type).Bind(wpr, self._process_candle).Start()
def _passes_filters(self, close, side):
if len(self._closes) < self.channel_period:
return False
highest = max(self._closes)
lowest = min(self._closes)
width = highest - lowest
if not self._allow_flat_trading.Value and width < float(self._flat_threshold_pips.Value) * self._pip_size():
return False
if self._use_fractal_channel.Value:
high = self._upper_fractal
low = self._lower_fractal
if high is None or low is None or high <= low:
return False
inner = (high - low) * float(self._depth_percent.Value) / 100.0
if close < low + inner or close > high - inner:
return False
if self._use_high_low_channel.Value:
if width <= 0:
return False
allowed = width * float(self._orientation_percent.Value) / 100.0
if side == Sides.Buy and close < highest - allowed:
return False
if side == Sides.Sell and close > lowest + allowed:
return False
return True
def _process_candle(self, candle, wpr_value):
if candle.State != CandleStates.Finished:
return
self._finished_bars += 1
if self._pending_side is not None:
self._pending_age += 1
if self._pending_age > self._signal_age_limit.Value:
self._pending_side = None
self._bars.append((float(candle.HighPrice), float(candle.LowPrice)))
if len(self._bars) > 5:
self._bars.pop(0)
close = float(candle.ClosePrice)
self._closes.append(close)
if len(self._closes) > self.channel_period:
self._closes.pop(0)
if len(self._bars) == 5:
center = self._bars[2]
upper = all(center[0] > bar[0] for i, bar in enumerate(self._bars) if i != 2)
lower = all(center[1] < bar[1] for i, bar in enumerate(self._bars) if i != 2)
if upper:
self._upper_fractal = center[0]
if lower:
self._lower_fractal = center[1]
if upper != lower:
self._pending_side = Sides.Sell if upper else Sides.Buy
self._pending_age = 0
elif upper:
self._pending_side = None
if not self._use_counter_orders.Value or self.Position == 0:
self._cancel_counter()
if self._closing:
if self.Position != 0:
return
self._closing = False
if self._use_williams_exit.Value and self._finished_bars >= self.williams_period:
wpr = float(wpr_value)
threshold = float(self._williams_threshold.Value)
if (self.Position > 0 and wpr > -threshold or
self.Position < 0 and wpr < -100.0 + threshold):
self._cancel_counter()
self._pending_side = None
self._closing = True
self.ClosePosition()
return
if self._pending_side is None or self._pending(self._entry_order):
return
side = self._pending_side
if not self._passes_filters(close, side):
return
if self._single_position.Value and (
side == Sides.Buy and self.Position > 0 or
side == Sides.Sell and self.Position < 0):
return
volume = self._normalized_volume(self.order_volume)
if volume <= 0:
return
if side == Sides.Buy and self.Position < 0 or side == Sides.Sell and self.Position > 0:
volume += abs(float(self.Position))
self._cancel_counter()
order = Order()
order.Security = self.Security
order.Portfolio = self.Portfolio
order.Type = OrderTypes.Market
order.Side = side
order.Volume = Decimal(volume)
order.Comment = "Graal fractal entry"
self._entry_order = order
self.RegisterOrder(order)
self._pending_side = None
def OnOwnTradeReceived(self, trade):
super(graal_fractal_channel_strategy, self).OnOwnTradeReceived(trade)
if self.Position == 0 or not self._use_counter_orders.Value:
self._cancel_counter()
return
if trade is None or trade.Order != self._entry_order or trade.Trade is None:
return
self._cancel_counter()
side = Sides.Sell if self.Position > 0 else Sides.Buy
if side == Sides.Sell and self._lower_fractal is None:
return
if side == Sides.Buy and self._upper_fractal is None:
return
offset = float(self._offset_pips.Value) * self._pip_size()
price = (self._lower_fractal - offset if side == Sides.Sell
else self._upper_fractal + offset)
if price <= 0:
return
order = Order()
order.Security = self.Security
order.Portfolio = self.Portfolio
order.Type = OrderTypes.Conditional
order.Side = side
order.Volume = Decimal(abs(float(self.Position)))
condition = StopOrderCondition()
condition.ActivationPrice = TraderHelper.ShrinkPrice(self.Security, Decimal(price), ShrinkRules.Auto)
order.Condition = condition
order.Comment = "Graal counter stop"
self._counter_order = order
self.RegisterOrder(order)
def CreateClone(self):
return graal_fractal_channel_strategy()