Estrategia FuzzyLogic
Descripción general
La estrategia FuzzyLogic replica el asesor experto de MT5 Fuzzy logic (edición de barabashkakvn) utilizando el API de alto nivel de StockSharp. El sistema mide la fuerza de la tendencia y el agotamiento del momentum con osciladores de Bill Williams e indicadores de momentum, convierte esas lecturas en grados de pertenencia difusa y los agrega en una puntuación de decisión única entre 0 y 1.
Las acciones de trading se activan cuando la puntuación difusa cruza umbrales calibrados:
- Decision > 0.75 – abrir una posición corta (agotamiento fuerte / condiciones de sobrecompra).
- Decision < 0.25 – abrir una posición larga (configuración de reversión alcista fuerte).
Las posiciones se gestionan con distancias fijas de toma de ganancias y stop-loss expresadas en pasos de precio. Cuando se suministra una distancia de trailing stop, el stop protector se convierte en uno de seguimiento.
Conjunto de indicadores
| Componente | Propósito |
|---|---|
| Oscilador Gator (construido a partir de líneas Alligator) | Mide la suma de los diferenciales entre mandíbula–dientes y dientes–labios para evaluar la expansión o contracción de la tendencia. |
| Williams %R (14) | Detecta niveles de sobrecompra / sobreventa. |
| Acceleration/Deceleration Oscillator (AC) | Cuenta cambios consecutivos de momentum para estimar la aceleración de la tendencia. |
| DeMarker (14) | Confirma el agotamiento mediante comparaciones de máximos/mínimos. Implementado directamente dentro de la estrategia. |
| RSI (14) | Rastrea los oscilaciones clásicas de momentum. |
Las líneas Alligator se calculan con medias móviles suavizadas y se desplazan hacia adelante exactamente como en el asesor experto original para reproducir el oscilador Gator. Los valores de AC se derivan del Awesome Oscillator (diferencia SMA 5/34) menos su media móvil de 5 períodos, proporcionando lecturas idénticas al indicador iAC de MT5.
Lógica de trading
- El valor de cada indicador se mapea a cinco conjuntos de pertenencia difusa (muy bajista → muy alcista). Las funciones lineales a tramos replican los arrays originales de MT5.
- Los cinco grupos de pertenencia se ponderan (0.133, 0.133, 0.133, 0.268, 0.333) y se agregan en cuatro contenedores de resumen.
- La puntuación de decisión difusa se calcula como
Σ summary[x] * (0.2 * (x + 1) - 0.1), produciendo valores en el rango[0, 1]. - Las señales se evalúan una vez por vela cerrada. La estrategia permanece sin posición a menos que la decisión supere los umbrales de entrada.
- El tamaño de la orden depende de la propiedad
Volume(predeterminado 1). Los stops protectores se registran a través deStartProtection.
Gestión de riesgo
- StopLossPoints – distancia absoluta (en pasos de precio) para el stop protector. Se usa cuando
TrailingStopPointses cero. - TrailingStopPoints – si > 0, la distancia del stop-loss cambia a este valor y se activa el modo de seguimiento.
- TakeProfitPoints – distancia absoluta para el objetivo de ganancia.
Parámetros
| Parámetro | Descripción |
|---|---|
CandleType |
Marco temporal / tipo de vela utilizado para los cálculos. |
BuyThreshold |
Puntuación difusa por debajo de la cual se abre una entrada larga. Predeterminado 0.25. |
SellThreshold |
Puntuación difusa por encima de la cual se abre una entrada corta. Predeterminado 0.75. |
StopLossPoints |
Distancia del stop-loss en pasos de precio del instrumento. Predeterminado 60. |
TakeProfitPoints |
Distancia de toma de ganancias en pasos de precio. Predeterminado 20. |
TrailingStopPoints |
Distancia de trailing stop en pasos de precio. Predeterminado 0 (desactivado). |
WilliamsPeriod |
Período de lookback para Williams %R. Predeterminado 14. |
RsiPeriod |
Período de lookback para RSI. Predeterminado 14. |
DeMarkerPeriod |
Período de lookback para el cálculo integrado de DeMarker. Predeterminado 14. |
Notas de implementación
- El oscilador DeMarker se implementa manualmente porque StockSharp no expone una versión integrada. Los deltas de máximos y mínimos se almacenan en cola para reproducir las sumas de MT5.
- El historial de AC almacena los cinco valores completados más recientes para que la lógica difusa pueda verificar rachas de aceleración consecutivas al igual que
iAC(..., shift)en MT5. - Los búferes de mandíbula/dientes/labios del Alligator introducen el mismo desplazamiento hacia adelante (8/5/3 barras) antes de derivar los valores del histograma Gator.
- La estrategia solo abre una nueva posición cuando
Position == 0, respetando el comportamiento de posición única del asesor experto original.
Pasos de uso
- Adjunte la estrategia a una cartera y un valor en Designer/Backtester.
- Configure la serie de velas deseada a través de
CandleType. - Ajuste los umbrales o las distancias de stop si es necesario.
- Inicie la estrategia; operará automáticamente cuando la puntuación difusa cruce los niveles configurados.
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()