Diese StockSharp-Strategie portiert den MetaTrader-Experten Exp_SpearmanRankCorrelation_Histogram. Sie abonniert eine konfigurierbare Kerzenserie, berechnet das Spearman-Rang-Korrelations-Histogramm für jede abgeschlossene Kerze und reagiert wenn sich der farbcodierte Zustand ändert. Je nach Handelsmodus kann der Algorithmus entgegengesetzte Positionen schließen, in einen neuen Trade umkehren oder auf Extremwerte warten bevor er handelt.
Indikator-Pipeline
Ein RankCorrelationIndex-Indikator (Spearman-Rang-Korrelation skaliert auf ±100) wird mit Kerzenschlusskursen gespeist. Das Lookback-Fenster ist durch MaxRange begrenzt und beträgt standardmäßig 14 Kerzen.
Die rohe Korrelation wird auf das Intervall [-1, 1] normiert. Wenn InvertCorrelation aktiviert ist, wird das Vorzeichen umgekehrt um das MQL-direction-Flag zu emulieren.
Der normierte Wert wird mit HighLevel und LowLevel verglichen um einen Farbzustand zuzuweisen:
4 – stark bullische Zone (value > HighLevel).
3 – moderat bullische Zone (0 < value ≤ HighLevel).
2 – neutral (value == 0).
1 – moderat bearische Zone (LowLevel ≤ value < 0).
0 – stark bearische Zone (value < LowLevel).
Die neuesten Farben werden in einem serienartigen Puffer gespeichert, sodass Index 0 die zuletzt geschlossene Kerze darstellt, Index 1 die vorherige, und so weiter.
Handels-Workflow
Signale werden nur auf abgeschlossenen Kerzen ausgewertet (CandleStates.Finished).
Der Parameter SignalBar definiert welche abgeschlossene Kerze inspiziert wird (Standard eine Kerze zurück). Die Strategie betrachtet auch die unmittelbar ältere Kerze und repliziert damit die Doppelpuffer-Suche des Expert Advisors.
Order-Schalter (AllowBuyEntries, AllowSellEntries, AllowBuyExits, AllowSellExits) entscheiden ob Long-/Short-Positionen geöffnet oder geschlossen werden dürfen.
Handelsmodi reproduzieren den MetaTrader-Schalter:
Modus 1 – entgegengesetzte Position schließen sobald die ältere Farbe bullisch/bearisch ist (> 2 oder < 2). Falls erlaubt in die neue Richtung öffnen wenn die neuere Farbe die bullische (< 3) oder bearische (> 1) Zone verlässt.
Modus 2 – nur auf Extremfarben reagieren. Bullisches Extrem (4) ermöglicht der Strategie Shorts zu schließen und optional Longs zu öffnen wenn die neuere Kerze unter 4 fällt. Bearisches Extrem (0) schließt Longs und kann Shorts öffnen wenn die neuere Kerze über 0 steigt.
Modus 3 – eine strengere Version von Modus 2: Shorts werden sofort auf 4 geschlossen, Longs auf 0, und neue Trades sind unter denselben Bedingungen wie Modus 2 erlaubt.
CancelActiveOrders() wird vor dem Senden neuer Marktorders ausgeführt um veraltete Anfragen zu vermeiden.
Positionsumkehrungen verwenden das konfigurierte Volume plus die absolute aktuelle Position damit der Trade vollständig auf die entgegengesetzte Seite wechselt.
Optionale StopLossPoints und TakeProfitPoints (Preiseinheiten) ermöglichen StartProtection-basiertes Risikomanagement; bei 0 werden keine Schutzorders erzeugt.
Parameter
Parameter
Beschreibung
CandleType
Zeitrahmen für Indikator und Handelsentscheidungen.
RangeLength
Nominaler Spearman-Lookback-Zeitraum (begrenzt durch MaxRange).
MaxRange
Obergrenze für die effektive Lookback-Länge; fällt auf 10 wenn auf 0 gesetzt.
HighLevel, LowLevel
Schwellenwerte die bullische und bearische Histogramm-Zonen trennen.
SignalBar
Anzahl geschlossener Kerzen die vor der Histogramm-Analyse übersprungen werden.
InvertCorrelation
Kehrt das Histogrammvorzeichen um um das MQL-direction=false-Verhalten zu entsprechen.
Wählt Modus 1-, Modus 2- oder Modus 3-Logik aus dem Original-Experten.
StopLossPoints, TakeProfitPoints
Optionale Schutzabstände in absoluten Preiseinheiten für StartProtection.
Volume (eingebaut)
Basis-Ordergröße beim Öffnen oder Umkehren von Positionen.
Unterschiede zum MetaTrader-Experten
Geldverwaltungseingaben (MM, MMMode) und Slippage (Deviation_) werden nicht repliziert; die Positionsgrößenbestimmung basiert auf der Standard-Volume-Eigenschaft und der Broker-Konfiguration.
Die MQL-Hilfsfunktionen aus TradeAlgorithms.mqh werden durch direkte BuyMarket/SellMarket-Aufrufe nach dem Stornieren ausstehender Orders ersetzt.
Der CalculatedBars-Performance-Hinweis ist in StockSharp unnötig und wurde weggelassen.
Das direction-Flag wird durch InvertCorrelation dargestellt, das einfach das Histogrammvorzeichen spiegelt.
Stop-Loss- und Take-Profit-Abstände (StopLoss_, TakeProfit_) werden als absolute Preisoffsets beim Aktivieren von StartProtection interpretiert; keine automatische Punkt-zu-Preis-Konvertierung wird durchgeführt.
Signalzeiten werden beim Kerzenschluss verarbeitet; es gibt keine verzögerte Planung bis zur nächsten Kerzeneröffnung.
Diese Anpassungen folgen den StockSharp High-Level-Strategie-Richtlinien während die ursprüngliche Signallogik erhalten bleibt.
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class ExpSpearmanRankCorrelationHistogramStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public ExpSpearmanRankCorrelationHistogramStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 14).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class exp_spearman_rank_correlation_histogram_strategy(Strategy):
def __init__(self):
super(exp_spearman_rank_correlation_histogram_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 14) \
.SetDisplay("Fast Period", "Fast MA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50) \
.SetDisplay("Slow Period", "Slow MA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400) \
.SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def stop_loss_points(self):
return self._stop_loss_points.Value
@property
def take_profit_points(self):
return self._take_profit_points.Value
def OnReseted(self):
super(exp_spearman_rank_correlation_histogram_strategy, self).OnReseted()
self._fast = None
self._slow = None
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(exp_spearman_rank_correlation_histogram_strategy, self).OnStarted2(time)
self._fast = ExponentialMovingAverage()
self._fast.Length = self.fast_period
self._slow = ExponentialMovingAverage()
self._slow.Length = self.slow_period
subscription = self.SubscribeCandles(DataType.TimeFrame(TimeSpan.FromMinutes(5)))
subscription.Bind(self._fast, self._slow, self._process_candle)
subscription.Start()
def _process_candle(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast_value)
slow_val = float(slow_value)
if not self._fast.IsFormed or not self._slow.IsFormed:
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast_val
self._prev_slow = slow_val
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close <= self._entry_price - self.stop_loss_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close >= self._entry_price + self.take_profit_points * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
elif self.Position < 0 and self._entry_price > 0:
if self.stop_loss_points > 0 and close >= self._entry_price + self.stop_loss_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self.take_profit_points > 0 and close <= self._entry_price - self.take_profit_points * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
return
if self._prev_fast <= self._prev_slow and fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return exp_spearman_rank_correlation_histogram_strategy()