Cyberia Trader Adaptive Strategy
Overview
The Cyberia Trader Adaptive Strategy is a C# port of the classic MetaTrader "CyberiaTrader" expert advisor. The strategy rebuilds the original probability driven core in StockSharp and augments it with optional technical filters. It continuously analyses price swings to measure the odds of reversals and then optionally confirms the signal with EMA, MACD, CCI, ADX or fractal filters before sending orders.
Probability engine
The heart of the strategy is the probability calculator inspired by the MQL version. It uses an adaptive sampling period
(ValuePeriod) and inspects historical bars at fixed steps to classify each bar as:
- Sell probability – bullish bar following a bearish bar (potential fading opportunity).
- Buy probability – bearish bar following a bullish bar.
- Undefined probability – all other bar configurations.
For each class the strategy accumulates average amplitude, hit-rate and success-rate statistics over ValuePeriod × HistoryMultiplier
samples. The adaptive search scans periods from 1 to MaxPeriod (default 23) and keeps the period that produces the highest
success-rate. These statistics are exposed internally as:
BuyPossibility,SellPossibility,UndefinedPossibility– current bar classification values.BuyPossibilityMid,SellPossibilityMid, ... – running averages used by the original decision tree.PossibilityQuality,PossibilitySuccessQuality– quality ratios used for diagnostics and auto period selection.
When insufficient history is available the strategy simply waits until the probability engine reports a valid sample set.
Indicator filters
The original EA allowed enabling or disabling additional indicator based modules. The port keeps the same idea:
- EMA filter – compares the slope of an EMA (
MaPeriod) between the last two finished candles. - MACD filter – checks the relation between MACD and its signal line (
MacdFast,MacdSlow,MacdSignal). - CCI filter – flags overbought/oversold regimes using
CciPeriodand ±100 thresholds. - ADX filter – inspects +DI and −DI components (
AdxPeriod) to prefer the dominant direction. - Fractal filter – detects the most recent swing using a configurable
FractalDepthwindow and blocks orders against it. - Reversal detector – toggles the direction flags when a probability spike exceeds
ReversalIndextimes its average.
Each module can be toggled via parameters and mirrors the behaviour of the original boolean extern inputs.
Trading logic
- Subscribe to the configured candle series (
CandleType). - Rebuild the probability statistics and optionally re-select the optimal sampling period on every finished candle.
- Apply the optional indicator filters and the Cyberia decision tree to enable or disable buy/sell directions.
- Execute trades when a buy or sell decision is active, respecting the global
BlockBuyandBlockSellswitches. - Optionally apply absolute stop-loss or take-profit protection if
StopLossPointsorTakeProfitPointsare specified. - Close positions early when the decision becomes
Unknownand the probability quality deteriorates.
Parameters
| Name | Description |
|---|---|
CandleType |
Candle series used for calculations. |
AutoSelectPeriod |
Enables the adaptive search over MaxPeriod to find the best sampling window. |
InitialPeriod |
Fallback probability period when auto selection is disabled. |
MaxPeriod |
Maximum period considered during the adaptive search (default 23 like the EA). |
HistoryMultiplier |
Number of samples per period used in the statistics (default 5). |
SpreadFilter |
Minimum move (in price units) required to treat a probability as "successful". |
EnableCyberiaLogic |
Toggles the original decision tree that compares probability averages. |
EnableMa, EnableMacd, EnableCci, EnableAdx, EnableFractals, EnableReversalDetector |
Enable individual filters. |
MaPeriod |
EMA length for the moving-average filter. |
MacdFast, MacdSlow, MacdSignal |
MACD configuration. |
CciPeriod |
Commodity Channel Index length. |
AdxPeriod |
Average Directional Index length. |
FractalDepth |
Odd number of candles analysed to detect the most recent fractal swing. |
ReversalIndex |
Multiplier used by the reversal detector. |
BlockBuy, BlockSell |
Hard switches that stop opening trades in the given direction. |
TakeProfitPoints, StopLossPoints |
Optional absolute take-profit and stop-loss distances. |
Notes
- The adaptive period search requires sufficient history:
ValuePeriod × HistoryMultiplier + ValuePeriodbars. - All comments were rewritten in English and the logic keeps to the high level StockSharp API with indicator bindings.
- The probability metrics are internal fields but exposed through logs or by extending the strategy if further diagnostics are needed.
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;
using StockSharp.Algo;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Adaptive port of the CyberiaTrader expert advisor that reconstructs its probability based decision tree.
/// Combines the original statistical core with optional indicator based filters.
/// </summary>
public class CyberiaTraderAdaptiveStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _autoSelectPeriod;
private readonly StrategyParam<int> _initialPeriod;
private readonly StrategyParam<int> _maxPeriod;
private readonly StrategyParam<int> _historyMultiplier;
private readonly StrategyParam<decimal> _spreadFilter;
private readonly StrategyParam<bool> _enableCyberiaLogic;
private readonly StrategyParam<bool> _enableMa;
private readonly StrategyParam<bool> _enableMacd;
private readonly StrategyParam<bool> _enableCci;
private readonly StrategyParam<bool> _enableAdx;
private readonly StrategyParam<bool> _enableFractals;
private readonly StrategyParam<bool> _enableReversalDetector;
private readonly StrategyParam<int> _maPeriod;
private readonly StrategyParam<int> _macdFast;
private readonly StrategyParam<int> _macdSlow;
private readonly StrategyParam<int> _macdSignal;
private readonly StrategyParam<int> _cciPeriod;
private readonly StrategyParam<int> _adxPeriod;
private readonly StrategyParam<int> _fractalDepth;
private readonly StrategyParam<decimal> _reversalIndex;
private readonly StrategyParam<bool> _blockBuy;
private readonly StrategyParam<bool> _blockSell;
private readonly StrategyParam<decimal> _takeProfitPoints;
private readonly StrategyParam<decimal> _stopLossPoints;
private ExponentialMovingAverage _ema = null!;
private MovingAverageConvergenceDivergenceSignal _macd = null!;
private CommodityChannelIndex _cci = null!;
private AverageDirectionalIndex _adx = null!;
private readonly List<CandleSnapshot> _history = new();
private int _currentValuePeriod;
private int _previousValuePeriod;
private int _currentValuesPeriodCount;
private decimal _lastSuitablePeriodQuality;
private decimal? _previousEmaValue;
private decimal? _lastEmaValue;
private decimal? _lastMacdValue;
private decimal? _lastMacdSignal;
private decimal? _lastCciValue;
private decimal? _lastPlusDi;
private decimal? _lastMinusDi;
private FractalDirections _fractalDirection = FractalDirections.None;
private bool _disableBuy;
private bool _disableSell;
private bool _blockBuyFlag;
private bool _blockSellFlag;
private DecisionTypes _currentDecision = DecisionTypes.Unknown;
private int _candlesSinceLastTrade;
private decimal _buyPossibility;
private decimal _sellPossibility;
private decimal _undefinedPossibility;
private decimal _decisionValue;
private decimal _previousDecisionValue;
private decimal _buyPossibilityMid;
private decimal _sellPossibilityMid;
private decimal _undefinedPossibilityMid;
private decimal _buySucPossibilityMid;
private decimal _sellSucPossibilityMid;
private decimal _undefinedSucPossibilityMid;
private decimal _buyPossibilityQuality;
private decimal _sellPossibilityQuality;
private decimal _undefinedPossibilityQuality;
private decimal _buySucPossibilityQuality;
private decimal _sellSucPossibilityQuality;
private decimal _undefinedSucPossibilityQuality;
private decimal _possibilityQuality;
private decimal _possibilitySuccessQuality;
/// <summary>
/// Creates a new instance of the adaptive CyberiaTrader strategy.
/// </summary>
public CyberiaTraderAdaptiveStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle series used for calculations", "General");
_autoSelectPeriod = Param(nameof(AutoSelectPeriod), true)
.SetDisplay("Auto Period", "Automatically scan for the best probability window", "General");
_initialPeriod = Param(nameof(InitialPeriod), 5)
.SetGreaterThanZero()
.SetDisplay("Initial Period", "Fallback period for probability sampling", "General");
_maxPeriod = Param(nameof(MaxPeriod), 23)
.SetGreaterThanZero()
.SetDisplay("Max Period", "Upper bound for adaptive period search", "General");
_historyMultiplier = Param(nameof(HistoryMultiplier), 5)
.SetGreaterThanZero()
.SetDisplay("History Multiplier", "Number of samples per period used for statistics", "General");
_spreadFilter = Param(nameof(SpreadFilter), 0m)
.SetDisplay("Spread Filter", "Minimum move treated as actionable", "General");
_enableCyberiaLogic = Param(nameof(EnableCyberiaLogic), true)
.SetDisplay("Enable Cyberia Logic", "Use original probability based decision rules", "Logic");
_enableMa = Param(nameof(EnableMa), false)
.SetDisplay("Enable EMA", "Use EMA slope filter", "Logic");
_enableMacd = Param(nameof(EnableMacd), false)
.SetDisplay("Enable MACD", "Use MACD trend filter", "Logic");
_enableCci = Param(nameof(EnableCci), false)
.SetDisplay("Enable CCI", "Use CCI overbought/oversold filter", "Logic");
_enableAdx = Param(nameof(EnableAdx), false)
.SetDisplay("Enable ADX", "Use ADX directional filter", "Logic");
_enableFractals = Param(nameof(EnableFractals), false)
.SetDisplay("Enable Fractals", "Block trades opposite to the latest fractal", "Logic");
_enableReversalDetector = Param(nameof(EnableReversalDetector), false)
.SetDisplay("Enable Reversal Detector", "Toggle direction when probabilities spike", "Logic");
_maPeriod = Param(nameof(MaPeriod), 23)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "Length of the EMA used by the filter", "Indicators");
_macdFast = Param(nameof(MacdFast), 12)
.SetGreaterThanZero()
.SetDisplay("MACD Fast", "Fast EMA length for MACD", "Indicators");
_macdSlow = Param(nameof(MacdSlow), 26)
.SetGreaterThanZero()
.SetDisplay("MACD Slow", "Slow EMA length for MACD", "Indicators");
_macdSignal = Param(nameof(MacdSignal), 9)
.SetGreaterThanZero()
.SetDisplay("MACD Signal", "Signal EMA length for MACD", "Indicators");
_cciPeriod = Param(nameof(CciPeriod), 13)
.SetGreaterThanZero()
.SetDisplay("CCI Period", "Commodity Channel Index length", "Indicators");
_adxPeriod = Param(nameof(AdxPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ADX Period", "Average Directional Index length", "Indicators");
_fractalDepth = Param(nameof(FractalDepth), 5)
.SetGreaterThanZero()
.SetDisplay("Fractal Depth", "Number of candles used to detect fractals", "Indicators");
_reversalIndex = Param(nameof(ReversalIndex), 3m)
.SetDisplay("Reversal Index", "Multiplier for spike based reversal detection", "Logic");
_blockBuy = Param(nameof(BlockBuy), false)
.SetDisplay("Block Buy", "Prevent buy orders regardless of signals", "Risk");
_blockSell = Param(nameof(BlockSell), false)
.SetDisplay("Block Sell", "Prevent sell orders regardless of signals", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 0m)
.SetDisplay("Take Profit", "Absolute take profit distance", "Risk");
_stopLossPoints = Param(nameof(StopLossPoints), 0m)
.SetDisplay("Stop Loss", "Absolute stop loss distance", "Risk");
Volume = 1;
}
/// <summary>
/// Candle type used by the strategy.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Enable adaptive period selection.
/// </summary>
public bool AutoSelectPeriod
{
get => _autoSelectPeriod.Value;
set => _autoSelectPeriod.Value = value;
}
/// <summary>
/// Fallback probability period when auto selection is disabled.
/// </summary>
public int InitialPeriod
{
get => _initialPeriod.Value;
set => _initialPeriod.Value = value;
}
/// <summary>
/// Maximum period evaluated during adaptive search.
/// </summary>
public int MaxPeriod
{
get => _maxPeriod.Value;
set => _maxPeriod.Value = value;
}
/// <summary>
/// Number of historical samples analysed per period.
/// </summary>
public int HistoryMultiplier
{
get => _historyMultiplier.Value;
set => _historyMultiplier.Value = value;
}
/// <summary>
/// Minimum move required to consider a probability successful.
/// </summary>
public decimal SpreadFilter
{
get => _spreadFilter.Value;
set => _spreadFilter.Value = value;
}
/// <summary>
/// Toggle the original Cyberia logic module.
/// </summary>
public bool EnableCyberiaLogic
{
get => _enableCyberiaLogic.Value;
set => _enableCyberiaLogic.Value = value;
}
/// <summary>
/// Toggle EMA filter.
/// </summary>
public bool EnableMa
{
get => _enableMa.Value;
set => _enableMa.Value = value;
}
/// <summary>
/// Toggle MACD filter.
/// </summary>
public bool EnableMacd
{
get => _enableMacd.Value;
set => _enableMacd.Value = value;
}
/// <summary>
/// Toggle CCI filter.
/// </summary>
public bool EnableCci
{
get => _enableCci.Value;
set => _enableCci.Value = value;
}
/// <summary>
/// Toggle ADX filter.
/// </summary>
public bool EnableAdx
{
get => _enableAdx.Value;
set => _enableAdx.Value = value;
}
/// <summary>
/// Toggle fractal filter.
/// </summary>
public bool EnableFractals
{
get => _enableFractals.Value;
set => _enableFractals.Value = value;
}
/// <summary>
/// Toggle probability spike based reversal detector.
/// </summary>
public bool EnableReversalDetector
{
get => _enableReversalDetector.Value;
set => _enableReversalDetector.Value = value;
}
/// <summary>
/// EMA period used in the moving average filter.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Fast MACD period.
/// </summary>
public int MacdFast
{
get => _macdFast.Value;
set => _macdFast.Value = value;
}
/// <summary>
/// Slow MACD period.
/// </summary>
public int MacdSlow
{
get => _macdSlow.Value;
set => _macdSlow.Value = value;
}
/// <summary>
/// MACD signal period.
/// </summary>
public int MacdSignal
{
get => _macdSignal.Value;
set => _macdSignal.Value = value;
}
/// <summary>
/// CCI period.
/// </summary>
public int CciPeriod
{
get => _cciPeriod.Value;
set => _cciPeriod.Value = value;
}
/// <summary>
/// ADX period.
/// </summary>
public int AdxPeriod
{
get => _adxPeriod.Value;
set => _adxPeriod.Value = value;
}
/// <summary>
/// Depth used to confirm fractal swings.
/// </summary>
public int FractalDepth
{
get => _fractalDepth.Value;
set => _fractalDepth.Value = value;
}
/// <summary>
/// Multiplier for reversal detection.
/// </summary>
public decimal ReversalIndex
{
get => _reversalIndex.Value;
set => _reversalIndex.Value = value;
}
/// <summary>
/// Hard block for buy orders.
/// </summary>
public bool BlockBuy
{
get => _blockBuy.Value;
set => _blockBuy.Value = value;
}
/// <summary>
/// Hard block for sell orders.
/// </summary>
public bool BlockSell
{
get => _blockSell.Value;
set => _blockSell.Value = value;
}
/// <summary>
/// Take profit distance in absolute points.
/// </summary>
public decimal TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Stop loss distance in absolute points.
/// </summary>
public decimal StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnReseted()
{
base.OnReseted();
_ema = default;
_macd = default;
_cci = default;
_adx = default;
_history.Clear();
_currentValuePeriod = 0;
_previousValuePeriod = 0;
_currentValuesPeriodCount = 0;
_lastSuitablePeriodQuality = 0;
_previousEmaValue = null;
_lastEmaValue = null;
_lastMacdValue = null;
_lastMacdSignal = null;
_lastCciValue = null;
_lastPlusDi = null;
_lastMinusDi = null;
_fractalDirection = default;
_disableBuy = false;
_disableSell = false;
_blockBuyFlag = false;
_blockSellFlag = false;
_currentDecision = default;
_candlesSinceLastTrade = 0;
_buyPossibility = 0;
_sellPossibility = 0;
_undefinedPossibility = 0;
_decisionValue = 0;
_previousDecisionValue = 0;
_buyPossibilityMid = 0;
_sellPossibilityMid = 0;
_undefinedPossibilityMid = 0;
_buySucPossibilityMid = 0;
_sellSucPossibilityMid = 0;
_undefinedSucPossibilityMid = 0;
_buyPossibilityQuality = 0;
_sellPossibilityQuality = 0;
_undefinedPossibilityQuality = 0;
_buySucPossibilityQuality = 0;
_sellSucPossibilityQuality = 0;
_undefinedSucPossibilityQuality = 0;
_possibilityQuality = 0;
_possibilitySuccessQuality = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
_currentValuePeriod = Math.Max(1, InitialPeriod);
_previousValuePeriod = _currentValuePeriod;
_currentValuesPeriodCount = Math.Max(1, _currentValuePeriod * HistoryMultiplier);
_ema = new EMA { Length = MaPeriod };
_macd = new MovingAverageConvergenceDivergenceSignal
{
Macd =
{
ShortMa = { Length = MacdFast },
LongMa = { Length = MacdSlow },
},
SignalMa = { Length = MacdSignal }
};
_cci = new CommodityChannelIndex { Length = CciPeriod };
_adx = new AverageDirectionalIndex { Length = AdxPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_ema, _macd, _cci, _adx, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ema);
DrawOwnTrades(area);
var indicatorArea = CreateChartArea();
if (indicatorArea != null)
{
DrawIndicator(indicatorArea, _macd);
DrawIndicator(indicatorArea, _cci);
DrawIndicator(indicatorArea, _adx);
}
}
Unit takeProfit = TakeProfitPoints > 0m ? new Unit(TakeProfitPoints, UnitTypes.Absolute) : null;
Unit stopLoss = StopLossPoints > 0m ? new Unit(StopLossPoints, UnitTypes.Absolute) : null;
if (takeProfit != null || stopLoss != null)
{
StartProtection(takeProfit, stopLoss);
}
base.OnStarted2(time);
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue emaValue, IIndicatorValue macdValue, IIndicatorValue cciValue, IIndicatorValue adxValue)
{
// Ignore updates for unfinished candles.
if (candle.State != CandleStates.Finished)
return;
// Ensure every indicator reports a final value before using it.
if (!emaValue.IsFinal || !macdValue.IsFinal || !cciValue.IsFinal || !adxValue.IsFinal)
return;
var ema = emaValue.ToDecimal();
var macdTyped = (MovingAverageConvergenceDivergenceSignalValue)macdValue;
var cci = cciValue.ToDecimal();
var adxTyped = (AverageDirectionalIndexValue)adxValue;
_previousEmaValue = _lastEmaValue;
_lastEmaValue = ema;
_lastMacdValue = macdTyped.Macd;
_lastMacdSignal = macdTyped.Signal;
_lastCciValue = cci;
_lastPlusDi = adxTyped.Dx.Plus;
_lastMinusDi = adxTyped.Dx.Minus;
// Store the latest bar snapshot for probability calculations.
AddCandle(candle);
UpdateFractalState();
_candlesSinceLastTrade++;
// Skip trading until the probability model is ready.
if (!UpdateAdaptivePeriod())
return;
CalculateDirection();
ExecuteTradingLogic();
}
private void CalculateDirection()
{
// Reset direction flags before applying the filter chain.
_disableBuy = false;
_disableSell = false;
_blockBuyFlag = BlockBuy;
_blockSellFlag = BlockSell;
if (EnableCyberiaLogic)
ApplyCyberiaLogic();
if (EnableMacd)
ApplyMacdFilter();
if (EnableMa)
ApplyMaFilter();
if (EnableCci)
ApplyCciFilter();
if (EnableAdx)
ApplyAdxFilter();
if (EnableFractals)
ApplyFractalFilter();
if (EnableReversalDetector)
ApplyReversalDetector();
}
private void ExecuteTradingLogic()
{
if (!IsFormedAndOnlineAndAllowTrading())
return;
// Enforce a minimum holding period to prevent rapid order churn.
var minHold = Math.Max(MaxPeriod, _currentValuePeriod) * HistoryMultiplier;
if (_candlesSinceLastTrade < minHold)
return;
// Combine internal logic and user defined blocks.
var allowBuy = !_disableBuy && !_blockBuyFlag;
var allowSell = !_disableSell && !_blockSellFlag;
if (_currentDecision == DecisionTypes.Buy && allowBuy && Position <= 0)
{
_candlesSinceLastTrade = 0;
if (Position < 0)
BuyMarket(Math.Abs(Position) + Volume);
else
BuyMarket(Volume);
}
else if (_currentDecision == DecisionTypes.Sell && allowSell && Position >= 0)
{
_candlesSinceLastTrade = 0;
if (Position > 0)
SellMarket(Position + Volume);
else
SellMarket(Volume);
}
else if (_currentDecision == DecisionTypes.Unknown)
{
if (_possibilityQuality < 0.5m)
{
_candlesSinceLastTrade = 0;
ClosePosition();
}
}
}
private void ClosePosition()
{
if (Position > 0)
{
SellMarket(Position);
}
else if (Position < 0)
{
BuyMarket(Math.Abs(Position));
}
}
private void ApplyCyberiaLogic()
{
var leftScore = _sellPossibilityMid * _sellPossibilityQuality;
var rightScore = _buyPossibilityMid * _buyPossibilityQuality;
var leftSuccess = _sellSucPossibilityMid * _sellSucPossibilityQuality;
var rightSuccess = _buySucPossibilityMid * _buySucPossibilityQuality;
if (_currentValuePeriod > _previousValuePeriod)
{
if (leftScore > rightScore)
{
_disableSell = false;
_disableBuy = true;
if (leftSuccess > rightSuccess)
_disableSell = true;
}
else if (leftScore < rightScore)
{
_disableSell = true;
_disableBuy = false;
if (leftSuccess < rightSuccess)
_disableBuy = true;
}
}
else if (_currentValuePeriod < _previousValuePeriod)
{
_disableSell = true;
_disableBuy = true;
}
if (leftScore == rightScore)
{
_disableSell = true;
_disableBuy = true;
}
if (_sellPossibilityMid > 0m && _sellSucPossibilityMid > 0m &&
_sellPossibility > _sellSucPossibilityMid * 2m)
{
_disableSell = true;
}
if (_buyPossibilityMid > 0m && _buySucPossibilityMid > 0m &&
_buyPossibility > _buySucPossibilityMid * 2m)
{
_disableBuy = true;
}
}
private void ApplyMacdFilter()
{
if (_lastMacdValue is not decimal macd || _lastMacdSignal is not decimal signal)
return;
if (macd > signal)
{
_disableSell = true;
}
else if (macd < signal)
{
_disableBuy = true;
}
}
private void ApplyMaFilter()
{
if (_previousEmaValue is not decimal prev || _lastEmaValue is not decimal current)
return;
if (current > prev)
{
_disableSell = true;
}
else if (current < prev)
{
_disableBuy = true;
}
}
private void ApplyCciFilter()
{
if (_lastCciValue is not decimal cci)
return;
if (cci < -100m)
{
_disableSell = true;
}
else if (cci > 100m)
{
_disableBuy = true;
}
}
private void ApplyAdxFilter()
{
if (_lastPlusDi is not decimal plus || _lastMinusDi is not decimal minus)
return;
if (plus > minus)
{
_disableSell = true;
}
else if (minus > plus)
{
_disableBuy = true;
}
}
private void ApplyFractalFilter()
{
if (_fractalDirection == FractalDirections.Up)
{
_blockBuyFlag = true;
_blockSellFlag = false;
}
else if (_fractalDirection == FractalDirections.Down)
{
_blockSellFlag = true;
_blockBuyFlag = false;
}
}
private void ApplyReversalDetector()
{
var trigger = false;
if (_buyPossibility != 0m && _buyPossibilityMid != 0m &&
_buyPossibility > _buyPossibilityMid * ReversalIndex)
{
trigger = true;
}
if (_sellPossibility != 0m && _sellPossibilityMid != 0m &&
_sellPossibility > _sellPossibilityMid * ReversalIndex)
{
trigger = true;
}
if (!trigger)
return;
_disableSell = !_disableSell;
_disableBuy = !_disableBuy;
}
private void AddCandle(ICandleMessage candle)
{
var snapshot = new CandleSnapshot(candle.OpenPrice, candle.HighPrice, candle.LowPrice, candle.ClosePrice);
_history.Add(snapshot);
var maxHistory = Math.Max(MaxPeriod, _currentValuePeriod) * (HistoryMultiplier + 2) + 2;
while (_history.Count > maxHistory)
{
_history.RemoveAt(0);
}
}
private void UpdateFractalState()
{
var depth = Math.Max(5, FractalDepth);
if (depth % 2 == 0)
depth += 1;
if (_history.Count < depth)
return;
var start = _history.Count - depth;
var middle = start + depth / 2;
var center = _history[middle];
var isUpper = true;
var isLower = true;
for (var i = start; i < start + depth; i++)
{
if (i == middle)
continue;
var sample = _history[i];
if (sample.High >= center.High)
isUpper = false;
if (sample.Low <= center.Low)
isLower = false;
}
if (isUpper)
{
_fractalDirection = FractalDirections.Up;
}
else if (isLower)
{
_fractalDirection = FractalDirections.Down;
}
}
private bool UpdateAdaptivePeriod()
{
// Evaluate possible sampling windows and keep the most reliable one.
var basePeriod = Math.Max(1, InitialPeriod);
var maxPeriod = AutoSelectPeriod ? Math.Max(1, MaxPeriod) : basePeriod;
PossibilityStats? bestStats = null;
var bestQuality = decimal.MinValue;
var selectedPeriod = basePeriod;
for (var period = 1; period <= maxPeriod; period++)
{
if (!AutoSelectPeriod && period != basePeriod)
continue;
var stats = CalculateStatistics(period);
if (!stats.IsValid)
continue;
if (!AutoSelectPeriod)
{
bestStats = stats;
bestQuality = stats.PossibilitySuccessQuality;
selectedPeriod = period;
break;
}
if (stats.PossibilitySuccessQuality > bestQuality)
{
bestQuality = stats.PossibilitySuccessQuality;
selectedPeriod = period;
bestStats = stats;
}
}
if (bestStats == null)
return false;
_previousValuePeriod = _currentValuePeriod;
_currentValuePeriod = selectedPeriod;
_currentValuesPeriodCount = Math.Max(1, _currentValuePeriod * HistoryMultiplier);
_lastSuitablePeriodQuality = bestQuality;
ApplyStatistics(bestStats.Value);
return true;
}
private PossibilityStats CalculateStatistics(int period)
{
// Compute averages and hit rates for the specified sampling period.
var modelingBars = Math.Max(1, period * HistoryMultiplier);
var required = period * (modelingBars + 1);
if (_history.Count < required)
return PossibilityStats.Invalid;
var spread = SpreadFilter;
decimal buySum = 0m;
decimal sellSum = 0m;
decimal undefinedSum = 0m;
decimal buySuccessSum = 0m;
decimal sellSuccessSum = 0m;
decimal undefinedSuccessSum = 0m;
var buyCount = 0;
var sellCount = 0;
var undefinedCount = 0;
var buySuccessCount = 0;
var sellSuccessCount = 0;
var undefinedSuccessCount = 0;
var buyQuality = 0m;
var sellQuality = 0m;
var undefinedQuality = 0m;
var buySuccessQuality = 0m;
var sellSuccessQuality = 0m;
var undefinedSuccessQuality = 0m;
DecisionTypes currentDecision = DecisionTypes.Unknown;
decimal currentBuy = 0m;
decimal currentSell = 0m;
decimal currentUndefined = 0m;
decimal currentDecisionValue = 0m;
decimal previousDecisionValue = 0m;
var shifts = Math.Min(modelingBars, (_history.Count / period) - 1);
for (var i = 0; i <= shifts; i++)
{
var result = CalculatePossibility(period, i);
if (i == 0)
{
currentDecision = result.Decision;
currentBuy = result.BuyPossibility;
currentSell = result.SellPossibility;
currentUndefined = result.UndefinedPossibility;
currentDecisionValue = result.DecisionValue;
previousDecisionValue = result.PreviousDecisionValue;
}
if (result.Decision == DecisionTypes.Buy)
buyQuality += 1m;
else if (result.Decision == DecisionTypes.Sell)
sellQuality += 1m;
else
undefinedQuality += 1m;
if (result.BuyPossibility > spread)
{
buySuccessQuality += 1m;
buySuccessSum += result.BuyPossibility;
buySuccessCount += 1;
}
if (result.SellPossibility > spread)
{
sellSuccessQuality += 1m;
sellSuccessSum += result.SellPossibility;
sellSuccessCount += 1;
}
if (result.UndefinedPossibility > spread)
{
undefinedSuccessQuality += 1m;
undefinedSuccessSum += result.UndefinedPossibility;
undefinedSuccessCount += 1;
}
buySum += result.BuyPossibility;
sellSum += result.SellPossibility;
undefinedSum += result.UndefinedPossibility;
buyCount += 1;
sellCount += 1;
undefinedCount += 1;
}
var totalQuality = buyQuality + sellQuality + undefinedQuality;
var totalSuccessQuality = buySuccessQuality + sellSuccessQuality + undefinedSuccessQuality;
var stats = new PossibilityStats
(
currentDecision,
currentBuy,
currentSell,
currentUndefined,
currentDecisionValue,
previousDecisionValue,
buyCount > 0 ? buySum / buyCount : 0m,
sellCount > 0 ? sellSum / sellCount : 0m,
undefinedCount > 0 ? undefinedSum / undefinedCount : 0m,
buySuccessCount > 0 ? buySuccessSum / buySuccessCount : 0m,
sellSuccessCount > 0 ? sellSuccessSum / sellSuccessCount : 0m,
undefinedSuccessCount > 0 ? undefinedSuccessSum / undefinedSuccessCount : 0m,
buyQuality,
sellQuality,
undefinedQuality,
buySuccessQuality,
sellSuccessQuality,
undefinedSuccessQuality,
totalQuality > 0m ? (sellQuality + buyQuality) / totalQuality : 0m,
totalSuccessQuality > 0m ? (sellSuccessQuality + buySuccessQuality) / totalSuccessQuality : 0m,
true
);
return stats;
}
private void ApplyStatistics(PossibilityStats stats)
{
_currentDecision = stats.Decision;
_buyPossibility = stats.BuyPossibility;
_sellPossibility = stats.SellPossibility;
_undefinedPossibility = stats.UndefinedPossibility;
_decisionValue = stats.DecisionValue;
_previousDecisionValue = stats.PreviousDecisionValue;
_buyPossibilityMid = stats.BuyPossibilityMid;
_sellPossibilityMid = stats.SellPossibilityMid;
_undefinedPossibilityMid = stats.UndefinedPossibilityMid;
_buySucPossibilityMid = stats.BuySucPossibilityMid;
_sellSucPossibilityMid = stats.SellSucPossibilityMid;
_undefinedSucPossibilityMid = stats.UndefinedSucPossibilityMid;
_buyPossibilityQuality = stats.BuyPossibilityQuality;
_sellPossibilityQuality = stats.SellPossibilityQuality;
_undefinedPossibilityQuality = stats.UndefinedPossibilityQuality;
_buySucPossibilityQuality = stats.BuySucPossibilityQuality;
_sellSucPossibilityQuality = stats.SellSucPossibilityQuality;
_undefinedSucPossibilityQuality = stats.UndefinedSucPossibilityQuality;
_possibilityQuality = stats.PossibilityQuality;
_possibilitySuccessQuality = stats.PossibilitySuccessQuality;
}
private PossibilityResult CalculatePossibility(int period, int shift)
{
var currentIndex = period * shift;
var previousIndex = period * (shift + 1);
var current = GetCandle(currentIndex);
var previous = GetCandle(previousIndex);
var decisionValue = current.Close - current.Open;
var previousDecisionValue = previous.Close - previous.Open;
decimal buyPossibility = 0m;
decimal sellPossibility = 0m;
decimal undefinedPossibility = 0m;
var decision = DecisionTypes.Unknown;
if (decisionValue > 0m)
{
if (previousDecisionValue < 0m)
{
decision = DecisionTypes.Sell;
sellPossibility = decisionValue;
}
else
{
decision = DecisionTypes.Unknown;
undefinedPossibility = decisionValue;
}
}
else if (decisionValue < 0m)
{
if (previousDecisionValue > 0m)
{
decision = DecisionTypes.Buy;
buyPossibility = -decisionValue;
}
else
{
decision = DecisionTypes.Unknown;
undefinedPossibility = -decisionValue;
}
}
return new PossibilityResult(decision, buyPossibility, sellPossibility, undefinedPossibility, decisionValue, previousDecisionValue);
}
private CandleSnapshot GetCandle(int shift)
{
var index = _history.Count - 1 - shift;
if (index < 0)
return default;
return _history[index];
}
private readonly record struct CandleSnapshot(decimal Open, decimal High, decimal Low, decimal Close);
private enum DecisionTypes
{
Sell,
Buy,
Unknown,
}
private enum FractalDirections
{
None,
Up,
Down,
}
private readonly record struct PossibilityResult(DecisionTypes Decision, decimal BuyPossibility, decimal SellPossibility, decimal UndefinedPossibility, decimal DecisionValue, decimal PreviousDecisionValue);
private readonly record struct PossibilityStats(
DecisionTypes Decision,
decimal BuyPossibility,
decimal SellPossibility,
decimal UndefinedPossibility,
decimal DecisionValue,
decimal PreviousDecisionValue,
decimal BuyPossibilityMid,
decimal SellPossibilityMid,
decimal UndefinedPossibilityMid,
decimal BuySucPossibilityMid,
decimal SellSucPossibilityMid,
decimal UndefinedSucPossibilityMid,
decimal BuyPossibilityQuality,
decimal SellPossibilityQuality,
decimal UndefinedPossibilityQuality,
decimal BuySucPossibilityQuality,
decimal SellSucPossibilityQuality,
decimal UndefinedSucPossibilityQuality,
decimal PossibilityQuality,
decimal PossibilitySuccessQuality,
bool HasValue)
{
public static PossibilityStats Invalid { get; } = new PossibilityStats(DecisionTypes.Unknown, 0m, 0m, 0m, 0m, 0m, 0m, 0m, 0m, 0m, 0m, 0m, 0m, 0m, 0m, 0m, 0m, 0m, 0m, 0m, false);
public bool IsValid => HasValue;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Indicators import (ExponentialMovingAverage,
MovingAverageConvergenceDivergenceSignal, CommodityChannelIndex,
AverageDirectionalIndex)
from StockSharp.Algo.Strategies import Strategy
class cyberia_trader_adaptive_strategy(Strategy):
"""
Adaptive port of CyberiaTrader EA. Combines probability based decision tree
with optional indicator filters (EMA, MACD, CCI, ADX, fractals).
"""
DECISION_SELL = 0
DECISION_BUY = 1
DECISION_UNKNOWN = 2
FRACTAL_NONE = 0
FRACTAL_UP = 1
FRACTAL_DOWN = 2
def __init__(self):
super(cyberia_trader_adaptive_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle series used for calculations", "General")
self._auto_select_period = self.Param("AutoSelectPeriod", True) \
.SetDisplay("Auto Period", "Automatically scan for the best probability window", "General")
self._initial_period = self.Param("InitialPeriod", 5) \
.SetDisplay("Initial Period", "Fallback period for probability sampling", "General")
self._max_period = self.Param("MaxPeriod", 23) \
.SetDisplay("Max Period", "Upper bound for adaptive period search", "General")
self._history_multiplier = self.Param("HistoryMultiplier", 5) \
.SetDisplay("History Multiplier", "Number of samples per period used for statistics", "General")
self._spread_filter = self.Param("SpreadFilter", 0.0) \
.SetDisplay("Spread Filter", "Minimum move treated as actionable", "General")
self._enable_cyberia_logic = self.Param("EnableCyberiaLogic", True) \
.SetDisplay("Enable Cyberia Logic", "Use original probability based decision rules", "Logic")
self._enable_ma = self.Param("EnableMa", False) \
.SetDisplay("Enable EMA", "Use EMA slope filter", "Logic")
self._enable_macd = self.Param("EnableMacd", False) \
.SetDisplay("Enable MACD", "Use MACD trend filter", "Logic")
self._enable_cci = self.Param("EnableCci", False) \
.SetDisplay("Enable CCI", "Use CCI overbought/oversold filter", "Logic")
self._enable_adx = self.Param("EnableAdx", False) \
.SetDisplay("Enable ADX", "Use ADX directional filter", "Logic")
self._enable_fractals = self.Param("EnableFractals", False) \
.SetDisplay("Enable Fractals", "Block trades opposite to the latest fractal", "Logic")
self._enable_reversal_detector = self.Param("EnableReversalDetector", False) \
.SetDisplay("Enable Reversal Detector", "Toggle direction when probabilities spike", "Logic")
self._ma_period = self.Param("MaPeriod", 23) \
.SetDisplay("EMA Period", "Length of the EMA used by the filter", "Indicators")
self._macd_fast = self.Param("MacdFast", 12) \
.SetDisplay("MACD Fast", "Fast EMA length for MACD", "Indicators")
self._macd_slow = self.Param("MacdSlow", 26) \
.SetDisplay("MACD Slow", "Slow EMA length for MACD", "Indicators")
self._macd_signal = self.Param("MacdSignal", 9) \
.SetDisplay("MACD Signal", "Signal EMA length for MACD", "Indicators")
self._cci_period = self.Param("CciPeriod", 13) \
.SetDisplay("CCI Period", "Commodity Channel Index length", "Indicators")
self._adx_period = self.Param("AdxPeriod", 14) \
.SetDisplay("ADX Period", "Average Directional Index length", "Indicators")
self._fractal_depth = self.Param("FractalDepth", 5) \
.SetDisplay("Fractal Depth", "Number of candles used to detect fractals", "Indicators")
self._reversal_index = self.Param("ReversalIndex", 3.0) \
.SetDisplay("Reversal Index", "Multiplier for spike based reversal detection", "Logic")
self._block_buy = self.Param("BlockBuy", False) \
.SetDisplay("Block Buy", "Prevent buy orders regardless of signals", "Risk")
self._block_sell = self.Param("BlockSell", False) \
.SetDisplay("Block Sell", "Prevent sell orders regardless of signals", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 0.0) \
.SetDisplay("Take Profit", "Absolute take profit distance", "Risk")
self._stop_loss_points = self.Param("StopLossPoints", 0.0) \
.SetDisplay("Stop Loss", "Absolute stop loss distance", "Risk")
self._history = []
self._current_value_period = 5
self._previous_value_period = 5
self._current_values_period_count = 25
self._previous_ema_value = None
self._last_ema_value = None
self._last_macd_value = None
self._last_macd_signal_val = None
self._last_cci_value = None
self._last_plus_di = None
self._last_minus_di = None
self._fractal_direction = self.FRACTAL_NONE
self._disable_buy = False
self._disable_sell = False
self._block_buy_flag = False
self._block_sell_flag = False
self._current_decision = self.DECISION_UNKNOWN
self._buy_possibility = 0.0
self._sell_possibility = 0.0
self._undefined_possibility = 0.0
self._buy_possibility_mid = 0.0
self._sell_possibility_mid = 0.0
self._undefined_possibility_mid = 0.0
self._buy_suc_possibility_mid = 0.0
self._sell_suc_possibility_mid = 0.0
self._undefined_suc_possibility_mid = 0.0
self._buy_possibility_quality = 0.0
self._sell_possibility_quality = 0.0
self._undefined_possibility_quality = 0.0
self._buy_suc_possibility_quality = 0.0
self._sell_suc_possibility_quality = 0.0
self._undefined_suc_possibility_quality = 0.0
self._possibility_quality = 0.0
self._possibility_success_quality = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(cyberia_trader_adaptive_strategy, self).OnStarted2(time)
self._current_value_period = max(1, self._initial_period.Value)
self._previous_value_period = self._current_value_period
self._current_values_period_count = max(1, self._current_value_period * self._history_multiplier.Value)
self._history = []
ema = ExponentialMovingAverage()
ema.Length = self._ma_period.Value
macd = MovingAverageConvergenceDivergenceSignal()
macd.Macd.ShortMa.Length = self._macd_fast.Value
macd.Macd.LongMa.Length = self._macd_slow.Value
macd.SignalMa.Length = self._macd_signal.Value
cci = CommodityChannelIndex()
cci.Length = self._cci_period.Value
adx = AverageDirectionalIndex()
adx.Length = self._adx_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(ema, macd, cci, adx, self.on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
tp = float(self._take_profit_points.Value)
sl = float(self._stop_loss_points.Value)
tp_unit = Unit(tp, UnitTypes.Absolute) if tp > 0 else None
sl_unit = Unit(sl, UnitTypes.Absolute) if sl > 0 else None
if tp_unit is not None or sl_unit is not None:
self.StartProtection(tp_unit, sl_unit)
def on_process(self, candle, ema_val, macd_val, cci_val, adx_val):
if candle.State != CandleStates.Finished:
return
if not ema_val.IsFinal or not macd_val.IsFinal or not cci_val.IsFinal or not adx_val.IsFinal:
return
ema_dec = float(ema_val)
cci_dec = float(cci_val)
macd_typed = macd_val
macd_v = float(macd_typed.Macd) if macd_typed.Macd is not None else None
macd_s = float(macd_typed.Signal) if macd_typed.Signal is not None else None
adx_typed = adx_val
plus_di = float(adx_typed.Dx.Plus) if adx_typed.Dx.Plus is not None else None
minus_di = float(adx_typed.Dx.Minus) if adx_typed.Dx.Minus is not None else None
self._previous_ema_value = self._last_ema_value
self._last_ema_value = ema_dec
self._last_macd_value = macd_v
self._last_macd_signal_val = macd_s
self._last_cci_value = cci_dec
self._last_plus_di = plus_di
self._last_minus_di = minus_di
self._add_candle(candle)
self._update_fractal_state()
if not self._update_adaptive_period():
return
self._calculate_direction()
self._execute_trading_logic()
def _calculate_direction(self):
self._disable_buy = False
self._disable_sell = False
self._block_buy_flag = self._block_buy.Value
self._block_sell_flag = self._block_sell.Value
if self._enable_cyberia_logic.Value:
self._apply_cyberia_logic()
if self._enable_macd.Value:
self._apply_macd_filter()
if self._enable_ma.Value:
self._apply_ma_filter()
if self._enable_cci.Value:
self._apply_cci_filter()
if self._enable_adx.Value:
self._apply_adx_filter()
if self._enable_fractals.Value:
self._apply_fractal_filter()
if self._enable_reversal_detector.Value:
self._apply_reversal_detector()
def _execute_trading_logic(self):
allow_buy = not self._disable_buy and not self._block_buy_flag
allow_sell = not self._disable_sell and not self._block_sell_flag
if self._current_decision == self.DECISION_BUY and allow_buy:
if self.Position < 0:
self.BuyMarket()
if self.Position <= 0:
self.BuyMarket()
elif self._current_decision == self.DECISION_SELL and allow_sell:
if self.Position > 0:
self.SellMarket()
if self.Position >= 0:
self.SellMarket()
elif self._current_decision == self.DECISION_UNKNOWN:
if self._possibility_quality < 0.5:
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
def _apply_cyberia_logic(self):
left_score = self._sell_possibility_mid * self._sell_possibility_quality
right_score = self._buy_possibility_mid * self._buy_possibility_quality
left_success = self._sell_suc_possibility_mid * self._sell_suc_possibility_quality
right_success = self._buy_suc_possibility_mid * self._buy_suc_possibility_quality
if self._current_value_period > self._previous_value_period:
if left_score > right_score:
self._disable_sell = False
self._disable_buy = True
if left_success > right_success:
self._disable_sell = True
elif left_score < right_score:
self._disable_sell = True
self._disable_buy = False
if left_success < right_success:
self._disable_buy = True
elif self._current_value_period < self._previous_value_period:
self._disable_sell = True
self._disable_buy = True
if left_score == right_score:
self._disable_sell = True
self._disable_buy = True
if (self._sell_possibility_mid > 0 and self._sell_suc_possibility_mid > 0 and
self._sell_possibility > self._sell_suc_possibility_mid * 2):
self._disable_sell = True
if (self._buy_possibility_mid > 0 and self._buy_suc_possibility_mid > 0 and
self._buy_possibility > self._buy_suc_possibility_mid * 2):
self._disable_buy = True
def _apply_macd_filter(self):
if self._last_macd_value is None or self._last_macd_signal_val is None:
return
if self._last_macd_value > self._last_macd_signal_val:
self._disable_sell = True
elif self._last_macd_value < self._last_macd_signal_val:
self._disable_buy = True
def _apply_ma_filter(self):
if self._previous_ema_value is None or self._last_ema_value is None:
return
if self._last_ema_value > self._previous_ema_value:
self._disable_sell = True
elif self._last_ema_value < self._previous_ema_value:
self._disable_buy = True
def _apply_cci_filter(self):
if self._last_cci_value is None:
return
if self._last_cci_value < -100:
self._disable_sell = True
elif self._last_cci_value > 100:
self._disable_buy = True
def _apply_adx_filter(self):
if self._last_plus_di is None or self._last_minus_di is None:
return
if self._last_plus_di > self._last_minus_di:
self._disable_sell = True
elif self._last_minus_di > self._last_plus_di:
self._disable_buy = True
def _apply_fractal_filter(self):
if self._fractal_direction == self.FRACTAL_UP:
self._block_buy_flag = True
self._block_sell_flag = False
elif self._fractal_direction == self.FRACTAL_DOWN:
self._block_sell_flag = True
self._block_buy_flag = False
def _apply_reversal_detector(self):
trigger = False
ri = float(self._reversal_index.Value)
if (self._buy_possibility != 0 and self._buy_possibility_mid != 0 and
self._buy_possibility > self._buy_possibility_mid * ri):
trigger = True
if (self._sell_possibility != 0 and self._sell_possibility_mid != 0 and
self._sell_possibility > self._sell_possibility_mid * ri):
trigger = True
if not trigger:
return
self._disable_sell = not self._disable_sell
self._disable_buy = not self._disable_buy
def _add_candle(self, candle):
snapshot = (float(candle.OpenPrice), float(candle.HighPrice),
float(candle.LowPrice), float(candle.ClosePrice))
self._history.append(snapshot)
max_hist = max(self._max_period.Value, self._current_value_period) * (self._history_multiplier.Value + 2) + 2
while len(self._history) > max_hist:
self._history.pop(0)
def _update_fractal_state(self):
depth = max(5, self._fractal_depth.Value)
if depth % 2 == 0:
depth += 1
if len(self._history) < depth:
return
start = len(self._history) - depth
middle = start + depth // 2
center = self._history[middle]
is_upper = True
is_lower = True
for i in range(start, start + depth):
if i == middle:
continue
sample = self._history[i]
if sample[1] >= center[1]:
is_upper = False
if sample[2] <= center[2]:
is_lower = False
if is_upper:
self._fractal_direction = self.FRACTAL_UP
elif is_lower:
self._fractal_direction = self.FRACTAL_DOWN
def _update_adaptive_period(self):
base_period = max(1, self._initial_period.Value)
max_period_val = max(1, self._max_period.Value) if self._auto_select_period.Value else base_period
best_stats = None
best_quality = -1e18
selected_period = base_period
for period in range(1, max_period_val + 1):
if not self._auto_select_period.Value and period != base_period:
continue
stats = self._calculate_statistics(period)
if stats is None:
continue
if not self._auto_select_period.Value:
best_stats = stats
best_quality = stats['poss_success_quality']
selected_period = period
break
if stats['poss_success_quality'] > best_quality:
best_quality = stats['poss_success_quality']
selected_period = period
best_stats = stats
if best_stats is None:
return False
self._previous_value_period = self._current_value_period
self._current_value_period = selected_period
self._current_values_period_count = max(1, self._current_value_period * self._history_multiplier.Value)
self._apply_statistics(best_stats)
return True
def _calculate_statistics(self, period):
modeling_bars = max(1, period * self._history_multiplier.Value)
required = period * (modeling_bars + 1)
if len(self._history) < required:
return None
spread = float(self._spread_filter.Value)
buy_sum = sell_sum = undef_sum = 0.0
buy_suc_sum = sell_suc_sum = undef_suc_sum = 0.0
buy_count = sell_count = undef_count = 0
buy_suc_count = sell_suc_count = undef_suc_count = 0
buy_q = sell_q = undef_q = 0.0
buy_suc_q = sell_suc_q = undef_suc_q = 0.0
current_decision = self.DECISION_UNKNOWN
current_buy = current_sell = current_undef = 0.0
current_dv = prev_dv = 0.0
shifts = min(modeling_bars, (len(self._history) // period) - 1)
for i in range(shifts + 1):
result = self._calculate_possibility(period, i)
if i == 0:
current_decision = result[0]
current_buy = result[1]
current_sell = result[2]
current_undef = result[3]
current_dv = result[4]
prev_dv = result[5]
if result[0] == self.DECISION_BUY:
buy_q += 1.0
elif result[0] == self.DECISION_SELL:
sell_q += 1.0
else:
undef_q += 1.0
if result[1] > spread:
buy_suc_q += 1.0
buy_suc_sum += result[1]
buy_suc_count += 1
if result[2] > spread:
sell_suc_q += 1.0
sell_suc_sum += result[2]
sell_suc_count += 1
if result[3] > spread:
undef_suc_q += 1.0
undef_suc_sum += result[3]
undef_suc_count += 1
buy_sum += result[1]
sell_sum += result[2]
undef_sum += result[3]
buy_count += 1
sell_count += 1
undef_count += 1
total_q = buy_q + sell_q + undef_q
total_suc_q = buy_suc_q + sell_suc_q + undef_suc_q
return {
'decision': current_decision,
'buy_poss': current_buy,
'sell_poss': current_sell,
'undef_poss': current_undef,
'dv': current_dv,
'prev_dv': prev_dv,
'buy_mid': buy_sum / buy_count if buy_count > 0 else 0.0,
'sell_mid': sell_sum / sell_count if sell_count > 0 else 0.0,
'undef_mid': undef_sum / undef_count if undef_count > 0 else 0.0,
'buy_suc_mid': buy_suc_sum / buy_suc_count if buy_suc_count > 0 else 0.0,
'sell_suc_mid': sell_suc_sum / sell_suc_count if sell_suc_count > 0 else 0.0,
'undef_suc_mid': undef_suc_sum / undef_suc_count if undef_suc_count > 0 else 0.0,
'buy_q': buy_q,
'sell_q': sell_q,
'undef_q': undef_q,
'buy_suc_q': buy_suc_q,
'sell_suc_q': sell_suc_q,
'undef_suc_q': undef_suc_q,
'poss_quality': (sell_q + buy_q) / total_q if total_q > 0 else 0.0,
'poss_success_quality': (sell_suc_q + buy_suc_q) / total_suc_q if total_suc_q > 0 else 0.0,
}
def _apply_statistics(self, stats):
self._current_decision = stats['decision']
self._buy_possibility = stats['buy_poss']
self._sell_possibility = stats['sell_poss']
self._undefined_possibility = stats['undef_poss']
self._buy_possibility_mid = stats['buy_mid']
self._sell_possibility_mid = stats['sell_mid']
self._undefined_possibility_mid = stats['undef_mid']
self._buy_suc_possibility_mid = stats['buy_suc_mid']
self._sell_suc_possibility_mid = stats['sell_suc_mid']
self._undefined_suc_possibility_mid = stats['undef_suc_mid']
self._buy_possibility_quality = stats['buy_q']
self._sell_possibility_quality = stats['sell_q']
self._undefined_possibility_quality = stats['undef_q']
self._buy_suc_possibility_quality = stats['buy_suc_q']
self._sell_suc_possibility_quality = stats['sell_suc_q']
self._undefined_suc_possibility_quality = stats['undef_suc_q']
self._possibility_quality = stats['poss_quality']
self._possibility_success_quality = stats['poss_success_quality']
def _calculate_possibility(self, period, shift):
current_index = period * shift
previous_index = period * (shift + 1)
current = self._get_candle(current_index)
previous = self._get_candle(previous_index)
dv = current[3] - current[0]
prev_dv = previous[3] - previous[0]
buy_p = sell_p = undef_p = 0.0
decision = self.DECISION_UNKNOWN
if dv > 0:
if prev_dv < 0:
decision = self.DECISION_SELL
sell_p = dv
else:
decision = self.DECISION_UNKNOWN
undef_p = dv
elif dv < 0:
if prev_dv > 0:
decision = self.DECISION_BUY
buy_p = -dv
else:
decision = self.DECISION_UNKNOWN
undef_p = -dv
return (decision, buy_p, sell_p, undef_p, dv, prev_dv)
def _get_candle(self, shift):
index = len(self._history) - 1 - shift
if index < 0:
return (0.0, 0.0, 0.0, 0.0)
return self._history[index]
def CreateClone(self):
return cyberia_trader_adaptive_strategy()