Fuzzy Logic 策略
概览
Fuzzy Logic 策略将 MT5 专家顾问 Fuzzy logic (barabashkakvn 版) 迁移到 StockSharp 的高级 API。系统结合比尔·威廉姆斯的多项指标与经典动量指标,将观测值转换为模糊隶属度,并在 0 到 1 之间计算单一决策分数。
当模糊得分越过以下阈值时触发交易:
- Decision > 0.75 —— 视为强烈的做空机会(动能衰竭 / 超买)。
- Decision < 0.25 —— 视为强烈的做多机会(动能回升 / 超卖)。
策略使用以价格步长表示的固定止盈、止损距离;若设置了跟踪止损距离,则会自动启用跟踪模式。
指标组合
| 指标 | 作用 |
|---|---|
| Gator 振荡器(基于 Alligator 线) | 通过颚线、齿线、唇线之间的距离衡量趋势扩张或收敛。 |
| Williams %R (14) | 识别超买与超卖区域。 |
| Acceleration/Deceleration Oscillator | 统计连续的动量变化次数,评估趋势加速度。 |
| DeMarker (14) | 比较高低点以确认衰竭信号,在策略中手工实现。 |
| RSI (14) | 经典动量震荡指标。 |
Alligator 线采用平滑均线并向前偏移 8/5/3 根柱,与原版 EA 完全一致,从而得到相同的 Gator 值。AC 指标通过 Awesome Oscillator(5/34 SMA 之差)减去其 5 周期均线获得,结果与 MT5 的 iAC 保持一致。
交易逻辑
- 每个指标的数值被映射到五个模糊集合(从强烈看空到强烈看多),分段线性函数沿用了 MT5 中的原始阈值。
- 五个集合分别乘以 0.133、0.133、0.133、0.268、0.333 的权重,并汇总为四个中间得分。
- 最终决策得分通过
Σ summary[x] * (0.2 * (x + 1) - 0.1)计算,范围保持在[0, 1]。 - 仅在蜡烛收盘时评估信号,若账户当前无持仓并且得分突破阈值则开仓。
- 下单数量由
Volume属性决定(默认 1),防守单通过StartProtection自动注册。
风险控制
- StopLossPoints —— 止损距离(价格步长单位),当
TrailingStopPoints为 0 时使用该值。 - TrailingStopPoints —— 跟踪止损距离,大于 0 时启用跟踪功能并以此作为初始止损距离。
- TakeProfitPoints —— 止盈距离(价格步长单位)。
参数
| 参数 | 说明 |
|---|---|
CandleType |
计算所用的蜡烛类型/周期。 |
BuyThreshold |
模糊得分低于该值时建立多头,默认 0.25。 |
SellThreshold |
模糊得分高于该值时建立空头,默认 0.75。 |
StopLossPoints |
止损距离(价格步长)。默认 60。 |
TakeProfitPoints |
止盈距离(价格步长)。默认 20。 |
TrailingStopPoints |
跟踪止损距离(价格步长)。默认 0(关闭)。 |
WilliamsPeriod |
Williams %R 的计算周期。默认 14。 |
RsiPeriod |
RSI 的计算周期。默认 14。 |
DeMarkerPeriod |
内置 DeMarker 的周期。默认 14。 |
实现要点
- 由于 StockSharp 未提供 DeMarker 指标,实现中直接累计高点差与低点差以再现 MT5 算法。
- 保存最近五个 AC 数值,以便模糊逻辑检测连续的加速序列,与
iAC(..., shift)的行为保持一致。 - 通过内部缓冲区还原 Alligator 的 8/5/3 根前移,使得 Gator 振荡器与原策略等效。
- 仅在
Position == 0时允许开新仓,遵循原专家顾问一次仅持有一笔仓位的规则。
使用步骤
- 在 Designer 或 Backtester 中将策略绑定到目标投资组合与证券。
- 通过
CandleType选择分析用的蜡烛序列。 - 如有需要,调整阈值或止盈止损距离。
- 启动策略,当模糊得分突破设定阈值时,系统会自动发送订单。
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()