FuzzyLogic-Strategie
Übersicht
Die FuzzyLogic-Strategie repliziert den MT5-Expertenberater Fuzzy logic (barabashkakvn's Edition) mithilfe der High-Level-API von StockSharp. Das System misst Trendstärke und Momentum-Erschöpfung mit Bill-Williams-Oszillatoren und Momentum-Indikatoren, wandelt diese Werte in Fuzzy-Zugehörigkeitswerte um und aggregiert sie zu einem einzelnen Entscheidungswert zwischen 0 und 1.
Handelsaktionen werden ausgelöst, wenn der Fuzzy-Score kalibrierte Schwellenwerte überschreitet:
- Decision > 0.75 – Short-Position eröffnen (starke Erschöpfung / überkaufte Bedingungen).
- Decision < 0.25 – Long-Position eröffnen (starkes bullisches Umkehrsetup).
Positionen werden mit festen Take-Profit- und Stop-Loss-Abständen in Preisschritten verwaltet. Wenn ein Trailing-Stop-Abstand angegeben wird, wird der Schutz-Stop in einen Trailing-Stop umgewandelt.
Indikatorenstack
| Komponente | Zweck |
|---|---|
| Gator-Oszillator (aus Alligator-Linien aufgebaut) | Misst die Summe der Kiefer–Zähne- und Zähne–Lippen-Abstände, um Trendausweitung oder -kontraktion zu beurteilen. |
| Williams %R (14) | Erkennt überkaufte / überverkaufte Niveaus. |
| Acceleration/Deceleration Oscillator (AC) | Zählt aufeinanderfolgende Momentum-Verschiebungen zur Schätzung der Trendbeschleunigung. |
| DeMarker (14) | Bestätigt Erschöpfung durch Hoch/Tief-Vergleiche. Direkt innerhalb der Strategie implementiert. |
| RSI (14) | Verfolgt klassische Momentum-Schwingungen. |
Alligator-Linien werden mit geglätteten gleitenden Durchschnitten berechnet und genau wie im Original-Expertenberater vorwärts verschoben, um den Gator-Oszillator zu reproduzieren. AC-Werte werden aus dem Awesome Oscillator (5/34 SMA-Differenz) minus seinem 5-Perioden-Gleitenden Durchschnitt abgeleitet und liefern identische Werte wie MT5s iAC-Indikator.
Handelslogik
- Jeder Indikatorwert wird auf fünf Fuzzy-Zugehörigkeitsmengen abgebildet (sehr bärisch → sehr bullisch). Stückweise lineare Funktionen replizieren die ursprünglichen MT5-Arrays.
- Die fünf Zugehörigkeitsgruppen werden gewichtet (0.133, 0.133, 0.133, 0.268, 0.333) und in vier Zusammenfassungsbins aggregiert.
- Der Fuzzy-Entscheidungswert wird als
Σ summary[x] * (0.2 * (x + 1) - 0.1)berechnet und ergibt Werte im Bereich[0, 1]. - Signale werden einmal pro abgeschlossener Kerze ausgewertet. Die Strategie bleibt flach, sofern die Entscheidung die Einstiegsschwellen nicht überschreitet.
- Die Ordergröße basiert auf der Eigenschaft
Volume(Standard 1). Schutz-Stops werden überStartProtectionregistriert.
Risikomanagement
- StopLossPoints – absoluter Abstand (in Preisschritten) für den Schutz-Stop. Wird verwendet, wenn
TrailingStopPointsnull ist. - TrailingStopPoints – wenn > 0, wechselt der Stop-Loss-Abstand auf diesen Wert und der Trailing-Modus wird aktiviert.
- TakeProfitPoints – absoluter Abstand für das Gewinnziel.
Parameter
| Parameter | Beschreibung |
|---|---|
CandleType |
Zeitrahmen / Kerzentyp für Berechnungen. |
BuyThreshold |
Fuzzy-Score, unterhalb dessen ein Long-Einstieg geöffnet wird. Standard 0.25. |
SellThreshold |
Fuzzy-Score, oberhalb dessen ein Short-Einstieg geöffnet wird. Standard 0.75. |
StopLossPoints |
Stop-Loss-Abstand in Instrument-Preisschritten. Standard 60. |
TakeProfitPoints |
Take-Profit-Abstand in Preisschritten. Standard 20. |
TrailingStopPoints |
Trailing-Stop-Abstand in Preisschritten. Standard 0 (deaktiviert). |
WilliamsPeriod |
Lookback für Williams %R. Standard 14. |
RsiPeriod |
Lookback für RSI. Standard 14. |
DeMarkerPeriod |
Lookback für die eingebettete DeMarker-Berechnung. Standard 14. |
Implementierungshinweise
- Der DeMarker-Oszillator ist manuell implementiert, da StockSharp keine integrierte Version bereitstellt. Hoch- und Tief-Deltas werden in Warteschlangen gespeichert, um MT5-Summen zu reproduzieren.
- Der AC-Verlauf speichert die fünf zuletzt abgeschlossenen Werte, damit die Fuzzy-Logik aufeinanderfolgende Beschleunigungssequenzen prüfen kann, genau wie
iAC(..., shift)in MT5. - Alligator-Kiefer/Zähne/Lippen-Puffer führen dieselbe Vorwärtsverschiebung (8/5/3 Balken) ein, bevor die Gator-Histogrammwerte abgeleitet werden.
- Die Strategie öffnet nur dann eine neue Position, wenn
Position == 0, und respektiert damit das Einzelpositionsverhalten des ursprünglichen Expertenberaters.
Verwendungsschritte
- Hängen Sie die Strategie in Designer/Backtester an ein Portfolio und ein Wertpapier an.
- Konfigurieren Sie die gewünschte Kerzenserie über
CandleType. - Passen Sie bei Bedarf Schwellenwerte oder Stop-Abstände an.
- Starten Sie die Strategie; sie handelt automatisch, wenn der Fuzzy-Score die konfigurierten Niveaus überschreitet.
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>
/// Fuzzy logic strategy combining Bill Williams oscillators, RSI and DeMarker.
/// Opens short positions when the fuzzy score indicates exhaustion and
/// opens long positions during oversold momentum reversals.
/// </summary>
public class FuzzyLogicStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<decimal> _buyThreshold;
private readonly StrategyParam<decimal> _sellThreshold;
private readonly StrategyParam<decimal> _stopLossPoints;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<decimal> _trailingStopPoints;
private readonly StrategyParam<int> _williamsPeriod;
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _deMarkerPeriod;
private WilliamsR _williamsIndicator = null!;
private RelativeStrengthIndex _rsiIndicator = null!;
private readonly SmoothedMovingAverage _jaw = new() { Length = 13 };
private readonly SmoothedMovingAverage _teeth = new() { Length = 8 };
private readonly SmoothedMovingAverage _lips = new() { Length = 5 };
private readonly SimpleMovingAverage _aoFast = new() { Length = 5 };
private readonly SimpleMovingAverage _aoSlow = new() { Length = 34 };
private readonly SimpleMovingAverage _acAverage = new() { Length = 5 };
private readonly decimal?[] _jawBuffer = new decimal?[9];
private readonly decimal?[] _teethBuffer = new decimal?[6];
private readonly decimal?[] _lipsBuffer = new decimal?[4];
private int _jawCount;
private int _teethCount;
private int _lipsCount;
private readonly decimal[] _acHistory = new decimal[5];
private int _acCount;
private readonly Queue<decimal> _deMaxQueue = new();
private readonly Queue<decimal> _deMinQueue = new();
private decimal _deMaxSum;
private decimal _deMinSum;
private decimal? _previousHigh;
private decimal? _previousLow;
/// <summary>
/// Candle series type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Decision value that triggers long entries.
/// </summary>
public decimal BuyThreshold
{
get => _buyThreshold.Value;
set => _buyThreshold.Value = value;
}
/// <summary>
/// Decision value that triggers short entries.
/// </summary>
public decimal SellThreshold
{
get => _sellThreshold.Value;
set => _sellThreshold.Value = value;
}
/// <summary>
/// Initial stop-loss distance in price steps.
/// </summary>
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Trailing stop distance in price steps.
/// </summary>
public decimal TrailingStopPoints
{
get => _trailingStopPoints.Value;
set => _trailingStopPoints.Value = value;
}
/// <summary>
/// Williams %R lookback.
/// </summary>
public int WilliamsPeriod
{
get => _williamsPeriod.Value;
set => _williamsPeriod.Value = value;
}
/// <summary>
/// RSI lookback.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// DeMarker oscillator lookback.
/// </summary>
public int DeMarkerPeriod
{
get => _deMarkerPeriod.Value;
set => _deMarkerPeriod.Value = value;
}
/// <summary>
/// Initialize <see cref="FuzzyLogicStrategy"/>.
/// </summary>
public FuzzyLogicStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to analyze", "General");
_buyThreshold = Param(nameof(BuyThreshold), 0.15m)
.SetDisplay("Buy Threshold", "Decision level for long entries", "Trading")
.SetRange(0.1m, 0.5m)
;
_sellThreshold = Param(nameof(SellThreshold), 0.85m)
.SetDisplay("Sell Threshold", "Decision level for short entries", "Trading")
.SetRange(0.5m, 0.9m)
;
_stopLossPoints = Param(nameof(StopLossPoints), 60m)
.SetDisplay("Stop Loss (points)", "Protective stop distance in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 40m)
.SetDisplay("Take Profit (points)", "Target distance in price steps", "Risk");
_trailingStopPoints = Param(nameof(TrailingStopPoints), 0m)
.SetDisplay("Trailing Stop (points)", "Trailing stop distance in price steps", "Risk");
_williamsPeriod = Param(nameof(WilliamsPeriod), 14)
.SetDisplay("Williams %R Period", "Lookback for Williams %R", "Indicators")
;
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetDisplay("RSI Period", "Lookback for RSI", "Indicators")
;
_deMarkerPeriod = Param(nameof(DeMarkerPeriod), 14)
.SetDisplay("DeMarker Period", "Lookback for DeMarker", "Indicators")
;
Volume = 1;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
Array.Clear(_jawBuffer);
Array.Clear(_teethBuffer);
Array.Clear(_lipsBuffer);
_jawCount = 0;
_teethCount = 0;
_lipsCount = 0;
Array.Clear(_acHistory);
_acCount = 0;
_deMaxQueue.Clear();
_deMinQueue.Clear();
_deMaxSum = 0m;
_deMinSum = 0m;
_previousHigh = null;
_previousLow = null;
_jaw.Reset();
_teeth.Reset();
_lips.Reset();
_aoFast.Reset();
_aoSlow.Reset();
_acAverage.Reset();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_williamsIndicator = new WilliamsR { Length = WilliamsPeriod };
_rsiIndicator = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_williamsIndicator, _rsiIndicator, ProcessCandle)
.Start();
var step = Security?.PriceStep ?? 1m;
var stopDistance = TrailingStopPoints > 0m ? TrailingStopPoints : StopLossPoints;
var slUnit = stopDistance > 0m ? new Unit(stopDistance * step, UnitTypes.Absolute) : new Unit();
var tpUnit = TakeProfitPoints > 0m ? new Unit(TakeProfitPoints * step, UnitTypes.Absolute) : new Unit();
StartProtection(slUnit, tpUnit);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _williamsIndicator);
DrawIndicator(area, _rsiIndicator);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue wprValue, IIndicatorValue rsiValue)
{
// Work only with finished candles to avoid partial data.
if (candle.State != CandleStates.Finished)
return;
var hl2 = (candle.HighPrice + candle.LowPrice) / 2m;
var hl2Input = new DecimalIndicatorValue(_jaw, hl2, candle.OpenTime) { IsFinal = true };
var jawValue = _jaw.Process(hl2Input);
var teethValue = _teeth.Process(new DecimalIndicatorValue(_teeth, hl2, candle.OpenTime) { IsFinal = true });
var lipsValue = _lips.Process(new DecimalIndicatorValue(_lips, hl2, candle.OpenTime) { IsFinal = true });
var aoFastValue = _aoFast.Process(new DecimalIndicatorValue(_aoFast, hl2, candle.OpenTime) { IsFinal = true });
var aoSlowValue = _aoSlow.Process(new DecimalIndicatorValue(_aoSlow, hl2, candle.OpenTime) { IsFinal = true });
if (!jawValue.IsFinal || !teethValue.IsFinal || !lipsValue.IsFinal || !aoFastValue.IsFinal || !aoSlowValue.IsFinal)
{
UpdateDeMarker(candle);
return;
}
var jawShifted = UpdateShiftBuffer(_jawBuffer, ref _jawCount, 8, jawValue.GetValue<decimal>());
var teethShifted = UpdateShiftBuffer(_teethBuffer, ref _teethCount, 5, teethValue.GetValue<decimal>());
var lipsShifted = UpdateShiftBuffer(_lipsBuffer, ref _lipsCount, 3, lipsValue.GetValue<decimal>());
if (jawShifted is null || teethShifted is null || lipsShifted is null)
{
UpdateDeMarker(candle);
return;
}
var ao = aoFastValue.GetValue<decimal>() - aoSlowValue.GetValue<decimal>();
var acAverageValue = _acAverage.Process(new DecimalIndicatorValue(_acAverage, ao, candle.OpenTime) { IsFinal = true });
if (!acAverageValue.IsFinal)
{
UpdateDeMarker(candle);
return;
}
var ac = ao - acAverageValue.GetValue<decimal>();
var deMarker = UpdateDeMarker(candle);
if (deMarker is null)
{
UpdateAcHistory(ac);
return;
}
if (!wprValue.IsFinal || !rsiValue.IsFinal)
{
UpdateAcHistory(ac);
return;
}
if (_acCount < _acHistory.Length)
{
UpdateAcHistory(ac);
return;
}
var sumGator = Math.Abs(jawShifted.Value - teethShifted.Value) + Math.Abs(teethShifted.Value - lipsShifted.Value);
var wpr = wprValue.ToDecimal();
var rsi = rsiValue.ToDecimal();
var decision = CalculateDecision(sumGator, wpr, deMarker.Value, rsi);
if (Position == 0)
{
if (decision > SellThreshold)
{
SellMarket();
}
else if (decision < BuyThreshold)
{
BuyMarket();
}
}
UpdateAcHistory(ac);
}
private decimal? UpdateShiftBuffer(decimal?[] buffer, ref int filled, int shift, decimal value)
{
for (var i = 0; i < shift; i++)
buffer[i] = buffer[i + 1];
buffer[shift] = value;
if (filled >= shift)
return buffer[0];
filled++;
return null;
}
private decimal? UpdateDeMarker(ICandleMessage candle)
{
// Store previous extremes to compute DeMarker increments.
if (_previousHigh is null || _previousLow is null)
{
_previousHigh = candle.HighPrice;
_previousLow = candle.LowPrice;
return null;
}
var deMax = Math.Max(candle.HighPrice - _previousHigh.Value, 0m);
var deMin = Math.Max(_previousLow.Value - candle.LowPrice, 0m);
_previousHigh = candle.HighPrice;
_previousLow = candle.LowPrice;
if (_deMaxQueue.Count == DeMarkerPeriod)
{
_deMaxSum -= _deMaxQueue.Dequeue();
_deMinSum -= _deMinQueue.Dequeue();
}
_deMaxQueue.Enqueue(deMax);
_deMinQueue.Enqueue(deMin);
_deMaxSum += deMax;
_deMinSum += deMin;
if (_deMaxQueue.Count < DeMarkerPeriod)
return null;
var denominator = _deMaxSum + _deMinSum;
return denominator == 0m ? 0m : _deMaxSum / denominator;
}
private void UpdateAcHistory(decimal ac)
{
for (var i = _acHistory.Length - 1; i > 0; i--)
_acHistory[i] = _acHistory[i - 1];
_acHistory[0] = ac;
if (_acCount < _acHistory.Length)
_acCount++;
}
private decimal CalculateDecision(decimal sumGator, decimal wpr, decimal deMarker, decimal rsi)
{
var rang = new decimal[5, 5];
var summary = new decimal[5];
var gatorLevels = new[] { 0.010m, 0.020m, 0.030m, 0.040m, 0.040m, 0.030m, 0.020m, 0.010m };
var wprLevels = new[] { -95m, -90m, -80m, -75m, -25m, -20m, -10m, -5m };
var acLevels = new[] { 5m, 4m, 3m, 2m, 2m, 3m, 4m, 5m };
var deMarkerLevels = new[] { 0.15m, 0.20m, 0.25m, 0.30m, 0.70m, 0.75m, 0.80m, 0.85m };
var rsiLevels = new[] { 25m, 30m, 35m, 40m, 60m, 65m, 70m, 75m };
var weights = new[] { 0.133m, 0.133m, 0.133m, 0.268m, 0.333m };
// 1) Gator oscillator membership.
if (sumGator < gatorLevels[0])
{
rang[0, 0] = 0.5m;
rang[0, 4] = 0.5m;
}
if (sumGator >= gatorLevels[0] && sumGator < gatorLevels[1])
{
var part = (sumGator - gatorLevels[0]) / (gatorLevels[1] - gatorLevels[0]);
rang[0, 0] = (1m - part) / 2m;
rang[0, 1] = (1m - rang[0, 0] * 2m) / 2m;
rang[0, 4] = rang[0, 0];
rang[0, 3] = rang[0, 1];
}
if (sumGator >= gatorLevels[1] && sumGator < gatorLevels[2])
{
rang[0, 1] = 0.5m;
rang[0, 3] = 0.5m;
}
if (sumGator >= gatorLevels[2] && sumGator < gatorLevels[3])
{
var part = (sumGator - gatorLevels[2]) / (gatorLevels[3] - gatorLevels[2]);
rang[0, 1] = (1m - part) / 2m;
rang[0, 2] = 1m - rang[0, 1] * 2m;
rang[0, 3] = rang[0, 1];
}
if (sumGator >= gatorLevels[3])
rang[0, 2] = 1m;
// 2) Williams %R membership.
if (wpr < wprLevels[0])
rang[1, 0] = 1m;
if (wpr >= wprLevels[0] && wpr < wprLevels[1])
{
var part = (wpr - wprLevels[0]) / (wprLevels[1] - wprLevels[0]);
rang[1, 0] = 1m - part;
rang[1, 1] = 1m - rang[1, 0];
}
if (wpr >= wprLevels[1] && wpr < wprLevels[2])
rang[1, 1] = 1m;
if (wpr >= wprLevels[2] && wpr < wprLevels[3])
{
var part = (wpr - wprLevels[2]) / (wprLevels[3] - wprLevels[2]);
rang[1, 1] = 1m - part;
rang[1, 2] = 1m - rang[1, 1];
}
if (wpr >= wprLevels[3] && wpr < wprLevels[4])
rang[1, 2] = 1m;
if (wpr >= wprLevels[4] && wpr < wprLevels[5])
{
var part = (wpr - wprLevels[4]) / (wprLevels[5] - wprLevels[4]);
rang[1, 2] = 1m - part;
rang[1, 3] = 1m - rang[1, 2];
}
if (wpr >= wprLevels[5] && wpr < wprLevels[6])
rang[1, 3] = 1m;
if (wpr >= wprLevels[6] && wpr < wprLevels[7])
{
var part = (wpr - wprLevels[6]) / (wprLevels[7] - wprLevels[6]);
rang[1, 3] = 1m - part;
rang[1, 4] = 1m - rang[1, 3];
}
if (wpr >= wprLevels[7])
rang[1, 4] = 1m;
// 3) Acceleration/Deceleration oscillator sequences.
var tempAcBuy = 0m;
if (_acHistory[0] < _acHistory[1] && _acHistory[0] < 0m && _acHistory[1] < 0m)
tempAcBuy = 2m;
if (_acHistory[0] < _acHistory[1] && _acHistory[1] < _acHistory[2] &&
_acHistory[0] < 0m && _acHistory[1] < 0m && _acHistory[2] < 0m)
tempAcBuy = 3m;
if (_acHistory[0] < _acHistory[1] && _acHistory[1] < _acHistory[2] &&
_acHistory[2] < _acHistory[3] && _acHistory[0] < 0m && _acHistory[1] < 0m &&
_acHistory[2] < 0m && _acHistory[3] < 0m)
tempAcBuy = 4m;
if (_acHistory[0] < _acHistory[1] && _acHistory[1] < _acHistory[2] &&
_acHistory[2] < _acHistory[3] && _acHistory[3] < _acHistory[4] &&
_acHistory[0] < 0m && _acHistory[1] < 0m && _acHistory[2] < 0m &&
_acHistory[3] < 0m && _acHistory[4] < 0m)
tempAcBuy = 5m;
var tempAcSell = 0m;
if (_acHistory[0] > _acHistory[1] && _acHistory[0] > 0m && _acHistory[1] > 0m)
tempAcSell = 2m;
if (_acHistory[0] > _acHistory[1] && _acHistory[1] > _acHistory[2] &&
_acHistory[0] > 0m && _acHistory[1] > 0m && _acHistory[2] > 0m)
tempAcSell = 3m;
if (_acHistory[0] > _acHistory[1] && _acHistory[1] > _acHistory[2] &&
_acHistory[2] > _acHistory[3] && _acHistory[0] > 0m && _acHistory[1] > 0m &&
_acHistory[2] > 0m && _acHistory[3] > 0m)
tempAcSell = 4m;
if (_acHistory[0] > _acHistory[1] && _acHistory[1] > _acHistory[2] &&
_acHistory[2] > _acHistory[3] && _acHistory[3] > _acHistory[4] &&
_acHistory[0] > 0m && _acHistory[1] > 0m && _acHistory[2] > 0m &&
_acHistory[3] > 0m && _acHistory[4] > 0m)
tempAcSell = 5m;
if (tempAcBuy == acLevels[0] || tempAcBuy == acLevels[1])
rang[2, 0] = 1m;
if (tempAcBuy == acLevels[2] || tempAcBuy == acLevels[3])
rang[2, 1] = 1m;
if (tempAcSell == acLevels[4] || tempAcSell == acLevels[5])
rang[2, 3] = 1m;
if (tempAcSell == acLevels[6] || tempAcSell == acLevels[7])
rang[2, 4] = 1m;
if (rang[2, 0] == 0m && rang[2, 1] == 0m && rang[2, 3] == 0m && rang[2, 4] == 0m)
rang[2, 2] = 1m;
// 4) DeMarker membership.
if (deMarker < deMarkerLevels[0])
rang[3, 0] = 1m;
if (deMarker >= deMarkerLevels[0] && deMarker < deMarkerLevels[1])
{
var part = (deMarker - deMarkerLevels[0]) / (deMarkerLevels[1] - deMarkerLevels[0]);
rang[3, 0] = 1m - part;
rang[3, 1] = 1m - rang[3, 0];
}
if (deMarker >= deMarkerLevels[1] && deMarker < deMarkerLevels[2])
rang[3, 1] = 1m;
if (deMarker >= deMarkerLevels[2] && deMarker < deMarkerLevels[3])
{
var part = (deMarker - deMarkerLevels[2]) / (deMarkerLevels[3] - deMarkerLevels[2]);
rang[3, 1] = 1m - part;
rang[3, 2] = 1m - rang[3, 1];
}
if (deMarker >= deMarkerLevels[3] && deMarker < deMarkerLevels[4])
rang[3, 2] = 1m;
if (deMarker >= deMarkerLevels[4] && deMarker < deMarkerLevels[5])
{
var part = (deMarker - deMarkerLevels[4]) / (deMarkerLevels[5] - deMarkerLevels[4]);
rang[3, 2] = 1m - part;
rang[3, 3] = 1m - rang[3, 2];
}
if (deMarker >= deMarkerLevels[5] && deMarker < deMarkerLevels[6])
rang[3, 3] = 1m;
if (deMarker >= deMarkerLevels[6] && deMarker < deMarkerLevels[7])
{
var part = (deMarker - deMarkerLevels[6]) / (deMarkerLevels[7] - deMarkerLevels[6]);
rang[3, 3] = 1m - part;
rang[3, 4] = 1m - rang[3, 3];
}
if (deMarker >= deMarkerLevels[7])
rang[3, 4] = 1m;
// 5) RSI membership.
if (rsi < rsiLevels[0])
rang[4, 0] = 1m;
if (rsi >= rsiLevels[0] && rsi < rsiLevels[1])
{
var part = (rsi - rsiLevels[0]) / (rsiLevels[1] - rsiLevels[0]);
rang[4, 0] = 1m - part;
rang[4, 1] = 1m - rang[4, 0];
}
if (rsi >= rsiLevels[1] && rsi < rsiLevels[2])
rang[4, 1] = 1m;
if (rsi >= rsiLevels[2] && rsi < rsiLevels[3])
{
var part = (rsi - rsiLevels[2]) / (rsiLevels[3] - rsiLevels[2]);
rang[4, 1] = 1m - part;
rang[4, 2] = 1m - rang[4, 1];
}
if (rsi >= rsiLevels[3] && rsi < rsiLevels[4])
rang[4, 2] = 1m;
if (rsi >= rsiLevels[4] && rsi < rsiLevels[5])
{
var part = (rsi - rsiLevels[4]) / (rsiLevels[5] - rsiLevels[4]);
rang[4, 2] = 1m - part;
rang[4, 3] = 1m - rang[4, 2];
}
if (rsi >= rsiLevels[5] && rsi < rsiLevels[6])
rang[4, 3] = 1m;
if (rsi >= rsiLevels[6] && rsi < rsiLevels[7])
{
var part = (rsi - rsiLevels[6]) / (rsiLevels[7] - rsiLevels[6]);
rang[4, 3] = 1m - part;
rang[4, 4] = 1m - rang[4, 3];
}
if (rsi >= rsiLevels[7])
rang[4, 4] = 1m;
for (var x = 0; x < 4; x++)
{
for (var y = 0; y < 4; y++)
summary[x] += rang[y, x] * weights[x];
}
var decision = 0m;
for (var x = 0; x < 4; x++)
decision += summary[x] * (0.2m * (x + 1) - 0.1m);
return decision;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.BusinessEntities")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math, Decimal
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import WilliamsR, RelativeStrengthIndex, SmoothedMovingAverage, SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class fuzzy_logic_strategy(Strategy):
def __init__(self):
super(fuzzy_logic_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15)))
self._buy_threshold = self.Param("BuyThreshold", 0.15)
self._sell_threshold = self.Param("SellThreshold", 0.85)
self._stop_loss_points = self.Param("StopLossPoints", 60.0)
self._take_profit_points = self.Param("TakeProfitPoints", 40.0)
self._trailing_stop_points = self.Param("TrailingStopPoints", 0.0)
self._williams_period = self.Param("WilliamsPeriod", 14)
self._rsi_period = self.Param("RsiPeriod", 14)
self._demarker_period = self.Param("DeMarkerPeriod", 14)
self._jaw_buffer = [None] * 9
self._teeth_buffer = [None] * 6
self._lips_buffer = [None] * 4
self._jaw_count = 0
self._teeth_count = 0
self._lips_count = 0
self._ac_history = [0.0] * 5
self._ac_count = 0
self._de_max_queue = []
self._de_min_queue = []
self._de_max_sum = 0.0
self._de_min_sum = 0.0
self._previous_high = None
self._previous_low = None
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def BuyThreshold(self):
return self._buy_threshold.Value
@BuyThreshold.setter
def BuyThreshold(self, value):
self._buy_threshold.Value = value
@property
def SellThreshold(self):
return self._sell_threshold.Value
@SellThreshold.setter
def SellThreshold(self, value):
self._sell_threshold.Value = value
@property
def StopLossPoints(self):
return self._stop_loss_points.Value
@StopLossPoints.setter
def StopLossPoints(self, value):
self._stop_loss_points.Value = value
@property
def TakeProfitPoints(self):
return self._take_profit_points.Value
@TakeProfitPoints.setter
def TakeProfitPoints(self, value):
self._take_profit_points.Value = value
@property
def TrailingStopPoints(self):
return self._trailing_stop_points.Value
@TrailingStopPoints.setter
def TrailingStopPoints(self, value):
self._trailing_stop_points.Value = value
@property
def WilliamsPeriod(self):
return self._williams_period.Value
@WilliamsPeriod.setter
def WilliamsPeriod(self, value):
self._williams_period.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def DeMarkerPeriod(self):
return self._demarker_period.Value
@DeMarkerPeriod.setter
def DeMarkerPeriod(self, value):
self._demarker_period.Value = value
def OnStarted2(self, time):
super(fuzzy_logic_strategy, self).OnStarted2(time)
self._jaw_buffer = [None] * 9
self._teeth_buffer = [None] * 6
self._lips_buffer = [None] * 4
self._jaw_count = 0
self._teeth_count = 0
self._lips_count = 0
self._ac_history = [0.0] * 5
self._ac_count = 0
self._de_max_queue = []
self._de_min_queue = []
self._de_max_sum = 0.0
self._de_min_sum = 0.0
self._previous_high = None
self._previous_low = None
self._jaw = SmoothedMovingAverage()
self._jaw.Length = 13
self._teeth = SmoothedMovingAverage()
self._teeth.Length = 8
self._lips = SmoothedMovingAverage()
self._lips.Length = 5
self._ao_fast = SimpleMovingAverage()
self._ao_fast.Length = 5
self._ao_slow = SimpleMovingAverage()
self._ao_slow.Length = 34
self._ac_average = SimpleMovingAverage()
self._ac_average.Length = 5
self._williams = WilliamsR()
self._williams.Length = self.WilliamsPeriod
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(self._williams, self._rsi, self.ProcessCandle).Start()
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if step <= 0.0:
step = 1.0
stop_distance = float(self.TrailingStopPoints) if float(self.TrailingStopPoints) > 0.0 else float(self.StopLossPoints)
sl_unit = Unit(stop_distance * step, UnitTypes.Absolute) if stop_distance > 0.0 else Unit()
tp_unit = Unit(float(self.TakeProfitPoints) * step, UnitTypes.Absolute) if float(self.TakeProfitPoints) > 0.0 else Unit()
self.StartProtection(sl_unit, tp_unit)
def ProcessCandle(self, candle, wpr_value, rsi_value):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
hl2 = (high + low) / 2.0
jaw_result = process_float(self._jaw, Decimal(hl2), candle.OpenTime, True)
teeth_result = process_float(self._teeth, Decimal(hl2), candle.OpenTime, True)
lips_result = process_float(self._lips, Decimal(hl2), candle.OpenTime, True)
ao_fast_result = process_float(self._ao_fast, Decimal(hl2), candle.OpenTime, True)
ao_slow_result = process_float(self._ao_slow, Decimal(hl2), candle.OpenTime, True)
if not jaw_result.IsFinal or not teeth_result.IsFinal or not lips_result.IsFinal or not ao_fast_result.IsFinal or not ao_slow_result.IsFinal:
self._update_demarker(candle)
return
jaw_shifted = self._update_shift_buffer_jaw(float(jaw_result))
teeth_shifted = self._update_shift_buffer_teeth(float(teeth_result))
lips_shifted = self._update_shift_buffer_lips(float(lips_result))
if jaw_shifted is None or teeth_shifted is None or lips_shifted is None:
self._update_demarker(candle)
return
ao = float(ao_fast_result) - float(ao_slow_result)
ac_avg_result = process_float(self._ac_average, Decimal(ao), candle.OpenTime, True)
if not ac_avg_result.IsFinal:
self._update_demarker(candle)
return
ac = ao - float(ac_avg_result)
demarker = self._update_demarker(candle)
if demarker is None:
self._update_ac_history(ac)
return
if not wpr_value.IsFinal or not rsi_value.IsFinal:
self._update_ac_history(ac)
return
if self._ac_count < 5:
self._update_ac_history(ac)
return
sum_gator = abs(jaw_shifted - teeth_shifted) + abs(teeth_shifted - lips_shifted)
wpr = float(wpr_value)
rsi = float(rsi_value)
decision = self._calculate_decision(sum_gator, wpr, demarker, rsi)
if self.Position == 0:
if decision > float(self.SellThreshold):
self.SellMarket()
elif decision < float(self.BuyThreshold):
self.BuyMarket()
self._update_ac_history(ac)
def _update_shift_buffer_jaw(self, value):
shift = 8
for i in range(shift):
self._jaw_buffer[i] = self._jaw_buffer[i + 1]
self._jaw_buffer[shift] = value
if self._jaw_count >= shift:
return self._jaw_buffer[0]
self._jaw_count += 1
return None
def _update_shift_buffer_teeth(self, value):
shift = 5
for i in range(shift):
self._teeth_buffer[i] = self._teeth_buffer[i + 1]
self._teeth_buffer[shift] = value
if self._teeth_count >= shift:
return self._teeth_buffer[0]
self._teeth_count += 1
return None
def _update_shift_buffer_lips(self, value):
shift = 3
for i in range(shift):
self._lips_buffer[i] = self._lips_buffer[i + 1]
self._lips_buffer[shift] = value
if self._lips_count >= shift:
return self._lips_buffer[0]
self._lips_count += 1
return None
def _update_demarker(self, candle):
high = float(candle.HighPrice)
low = float(candle.LowPrice)
if self._previous_high is None or self._previous_low is None:
self._previous_high = high
self._previous_low = low
return None
de_max = max(high - self._previous_high, 0.0)
de_min = max(self._previous_low - low, 0.0)
self._previous_high = high
self._previous_low = low
period = int(self.DeMarkerPeriod)
if len(self._de_max_queue) == period:
self._de_max_sum -= self._de_max_queue.pop(0)
self._de_min_sum -= self._de_min_queue.pop(0)
self._de_max_queue.append(de_max)
self._de_min_queue.append(de_min)
self._de_max_sum += de_max
self._de_min_sum += de_min
if len(self._de_max_queue) < period:
return None
denominator = self._de_max_sum + self._de_min_sum
if denominator == 0.0:
return 0.0
return self._de_max_sum / denominator
def _update_ac_history(self, ac):
for i in range(len(self._ac_history) - 1, 0, -1):
self._ac_history[i] = self._ac_history[i - 1]
self._ac_history[0] = ac
if self._ac_count < len(self._ac_history):
self._ac_count += 1
def _calculate_decision(self, sum_gator, wpr, demarker, rsi):
rang = [[0.0] * 5 for _ in range(5)]
summary = [0.0] * 5
gator_levels = [0.010, 0.020, 0.030, 0.040, 0.040, 0.030, 0.020, 0.010]
wpr_levels = [-95.0, -90.0, -80.0, -75.0, -25.0, -20.0, -10.0, -5.0]
ac_levels = [5.0, 4.0, 3.0, 2.0, 2.0, 3.0, 4.0, 5.0]
demarker_levels = [0.15, 0.20, 0.25, 0.30, 0.70, 0.75, 0.80, 0.85]
rsi_levels = [25.0, 30.0, 35.0, 40.0, 60.0, 65.0, 70.0, 75.0]
weights = [0.133, 0.133, 0.133, 0.268, 0.333]
# 1) Gator oscillator membership
if sum_gator < gator_levels[0]:
rang[0][0] = 0.5
rang[0][4] = 0.5
if sum_gator >= gator_levels[0] and sum_gator < gator_levels[1]:
part = (sum_gator - gator_levels[0]) / (gator_levels[1] - gator_levels[0])
rang[0][0] = (1.0 - part) / 2.0
rang[0][1] = (1.0 - rang[0][0] * 2.0) / 2.0
rang[0][4] = rang[0][0]
rang[0][3] = rang[0][1]
if sum_gator >= gator_levels[1] and sum_gator < gator_levels[2]:
rang[0][1] = 0.5
rang[0][3] = 0.5
if sum_gator >= gator_levels[2] and sum_gator < gator_levels[3]:
part = (sum_gator - gator_levels[2]) / (gator_levels[3] - gator_levels[2])
rang[0][1] = (1.0 - part) / 2.0
rang[0][2] = 1.0 - rang[0][1] * 2.0
rang[0][3] = rang[0][1]
if sum_gator >= gator_levels[3]:
rang[0][2] = 1.0
# 2) Williams %R membership
if wpr < wpr_levels[0]:
rang[1][0] = 1.0
if wpr >= wpr_levels[0] and wpr < wpr_levels[1]:
part = (wpr - wpr_levels[0]) / (wpr_levels[1] - wpr_levels[0])
rang[1][0] = 1.0 - part
rang[1][1] = 1.0 - rang[1][0]
if wpr >= wpr_levels[1] and wpr < wpr_levels[2]:
rang[1][1] = 1.0
if wpr >= wpr_levels[2] and wpr < wpr_levels[3]:
part = (wpr - wpr_levels[2]) / (wpr_levels[3] - wpr_levels[2])
rang[1][1] = 1.0 - part
rang[1][2] = 1.0 - rang[1][1]
if wpr >= wpr_levels[3] and wpr < wpr_levels[4]:
rang[1][2] = 1.0
if wpr >= wpr_levels[4] and wpr < wpr_levels[5]:
part = (wpr - wpr_levels[4]) / (wpr_levels[5] - wpr_levels[4])
rang[1][2] = 1.0 - part
rang[1][3] = 1.0 - rang[1][2]
if wpr >= wpr_levels[5] and wpr < wpr_levels[6]:
rang[1][3] = 1.0
if wpr >= wpr_levels[6] and wpr < wpr_levels[7]:
part = (wpr - wpr_levels[6]) / (wpr_levels[7] - wpr_levels[6])
rang[1][3] = 1.0 - part
rang[1][4] = 1.0 - rang[1][3]
if wpr >= wpr_levels[7]:
rang[1][4] = 1.0
# 3) Acceleration/Deceleration oscillator sequences
h = self._ac_history
temp_ac_buy = 0.0
if h[0] < h[1] and h[0] < 0.0 and h[1] < 0.0:
temp_ac_buy = 2.0
if h[0] < h[1] and h[1] < h[2] and h[0] < 0.0 and h[1] < 0.0 and h[2] < 0.0:
temp_ac_buy = 3.0
if h[0] < h[1] and h[1] < h[2] and h[2] < h[3] and h[0] < 0.0 and h[1] < 0.0 and h[2] < 0.0 and h[3] < 0.0:
temp_ac_buy = 4.0
if h[0] < h[1] and h[1] < h[2] and h[2] < h[3] and h[3] < h[4] and h[0] < 0.0 and h[1] < 0.0 and h[2] < 0.0 and h[3] < 0.0 and h[4] < 0.0:
temp_ac_buy = 5.0
temp_ac_sell = 0.0
if h[0] > h[1] and h[0] > 0.0 and h[1] > 0.0:
temp_ac_sell = 2.0
if h[0] > h[1] and h[1] > h[2] and h[0] > 0.0 and h[1] > 0.0 and h[2] > 0.0:
temp_ac_sell = 3.0
if h[0] > h[1] and h[1] > h[2] and h[2] > h[3] and h[0] > 0.0 and h[1] > 0.0 and h[2] > 0.0 and h[3] > 0.0:
temp_ac_sell = 4.0
if h[0] > h[1] and h[1] > h[2] and h[2] > h[3] and h[3] > h[4] and h[0] > 0.0 and h[1] > 0.0 and h[2] > 0.0 and h[3] > 0.0 and h[4] > 0.0:
temp_ac_sell = 5.0
if temp_ac_buy == ac_levels[0] or temp_ac_buy == ac_levels[1]:
rang[2][0] = 1.0
if temp_ac_buy == ac_levels[2] or temp_ac_buy == ac_levels[3]:
rang[2][1] = 1.0
if temp_ac_sell == ac_levels[4] or temp_ac_sell == ac_levels[5]:
rang[2][3] = 1.0
if temp_ac_sell == ac_levels[6] or temp_ac_sell == ac_levels[7]:
rang[2][4] = 1.0
if rang[2][0] == 0.0 and rang[2][1] == 0.0 and rang[2][3] == 0.0 and rang[2][4] == 0.0:
rang[2][2] = 1.0
# 4) DeMarker membership
if demarker < demarker_levels[0]:
rang[3][0] = 1.0
if demarker >= demarker_levels[0] and demarker < demarker_levels[1]:
part = (demarker - demarker_levels[0]) / (demarker_levels[1] - demarker_levels[0])
rang[3][0] = 1.0 - part
rang[3][1] = 1.0 - rang[3][0]
if demarker >= demarker_levels[1] and demarker < demarker_levels[2]:
rang[3][1] = 1.0
if demarker >= demarker_levels[2] and demarker < demarker_levels[3]:
part = (demarker - demarker_levels[2]) / (demarker_levels[3] - demarker_levels[2])
rang[3][1] = 1.0 - part
rang[3][2] = 1.0 - rang[3][1]
if demarker >= demarker_levels[3] and demarker < demarker_levels[4]:
rang[3][2] = 1.0
if demarker >= demarker_levels[4] and demarker < demarker_levels[5]:
part = (demarker - demarker_levels[4]) / (demarker_levels[5] - demarker_levels[4])
rang[3][2] = 1.0 - part
rang[3][3] = 1.0 - rang[3][2]
if demarker >= demarker_levels[5] and demarker < demarker_levels[6]:
rang[3][3] = 1.0
if demarker >= demarker_levels[6] and demarker < demarker_levels[7]:
part = (demarker - demarker_levels[6]) / (demarker_levels[7] - demarker_levels[6])
rang[3][3] = 1.0 - part
rang[3][4] = 1.0 - rang[3][3]
if demarker >= demarker_levels[7]:
rang[3][4] = 1.0
# 5) RSI membership
if rsi < rsi_levels[0]:
rang[4][0] = 1.0
if rsi >= rsi_levels[0] and rsi < rsi_levels[1]:
part = (rsi - rsi_levels[0]) / (rsi_levels[1] - rsi_levels[0])
rang[4][0] = 1.0 - part
rang[4][1] = 1.0 - rang[4][0]
if rsi >= rsi_levels[1] and rsi < rsi_levels[2]:
rang[4][1] = 1.0
if rsi >= rsi_levels[2] and rsi < rsi_levels[3]:
part = (rsi - rsi_levels[2]) / (rsi_levels[3] - rsi_levels[2])
rang[4][1] = 1.0 - part
rang[4][2] = 1.0 - rang[4][1]
if rsi >= rsi_levels[3] and rsi < rsi_levels[4]:
rang[4][2] = 1.0
if rsi >= rsi_levels[4] and rsi < rsi_levels[5]:
part = (rsi - rsi_levels[4]) / (rsi_levels[5] - rsi_levels[4])
rang[4][2] = 1.0 - part
rang[4][3] = 1.0 - rang[4][2]
if rsi >= rsi_levels[5] and rsi < rsi_levels[6]:
rang[4][3] = 1.0
if rsi >= rsi_levels[6] and rsi < rsi_levels[7]:
part = (rsi - rsi_levels[6]) / (rsi_levels[7] - rsi_levels[6])
rang[4][3] = 1.0 - part
rang[4][4] = 1.0 - rang[4][3]
if rsi >= rsi_levels[7]:
rang[4][4] = 1.0
for x in range(4):
for y in range(4):
summary[x] += rang[y][x] * weights[x]
decision = 0.0
for x in range(4):
decision += summary[x] * (0.2 * (x + 1) - 0.1)
return decision
def OnReseted(self):
super(fuzzy_logic_strategy, self).OnReseted()
self._jaw_buffer = [None] * 9
self._teeth_buffer = [None] * 6
self._lips_buffer = [None] * 4
self._jaw_count = 0
self._teeth_count = 0
self._lips_count = 0
self._ac_history = [0.0] * 5
self._ac_count = 0
self._de_max_queue = []
self._de_min_queue = []
self._de_max_sum = 0.0
self._de_min_sum = 0.0
self._previous_high = None
self._previous_low = None
def CreateClone(self):
return fuzzy_logic_strategy()