Diese Strategie ist eine High-Level-StockSharp-Portierung des MetaTrader-Experten „iVIDyA Simple“. Es handelt ein einzelnes Symbol, indem es einen dynamischen variablen Indexdurchschnitt (VIDYA) verfolgt, der sich über den Chande Momentum Oscillator (CMO) an die Marktdynamik anpasst. Immer wenn die zuletzt beendete Kerze die verschobene VIDYA-Linie kreuzt, eröffnet die Strategie eine Marktposition in Richtung des Ausbruchs und fügt optional schützende Stop-Loss- und Take-Profit-Orders hinzu.
Handelslogik
Kerzendaten werden aus dem konfigurierten Zeitrahmen (CandleType) gelesen.
Der CMO mit der Periode CmoPeriod ist an die Kerzenserie gebunden. Sein absoluter Wert skaliert dynamisch den Glättungsfaktor von VIDYA. Der Basisfaktor entspricht 2 / (EmaPeriod + 1), genau wie bei der ursprünglichen MQL-Implementierung.
Ein rollierender VIDYA-Wert wird beibehalten. Bei jeder fertigen Kerze der Algorithmus:
Wählt den angewendeten Preis (AppliedPrice) aus der Kerze aus (Schlusskurs, Eröffnungskurs, Mediankurs usw.).
Aktualisiert VIDYA mit dem adaptiven Glättungskoeffizienten.
Speichert historische Werte, um die Option MA shift von MetaTrader zu emulieren.
Die Kerze wird mit dem verschobenen VIDYA-Wert (MaShift Balken zurück) verglichen:
Wenn die Kerze unterhalb von VIDYA öffnet und darüber schließt, wird ein Kaufsignal generiert.
Wenn die Kerze über VIDYA öffnet und darunter schließt, wird ein Verkaufssignal generiert.
Vor der Eröffnung einer neuen Position glättet die Strategie jegliches gegenteilige Risiko, indem sie das gesamte für die Umkehrung erforderliche Volumen handelt.
Nach jeder Eingabe werden SetStopLoss und SetTakeProfit aufgerufen, wenn die jeweiligen Abstände positiv sind.
Dies spiegelt den ursprünglichen Expertenberater wider, der Aufträge ausschließlich für neue Balken auslöste, einen VIDYA verwendete, der aus CMO und EMA-Zeiträumen berechnet wurde, und optionale Stopps in Punkten anfügte.
Parameter
Name
Standard
Beschreibung
Volume
1
Basishandelsvolumen, das für Aufträge verwendet wird. Bei der Umkehrung von Positionen wird das bestehende Exposure automatisch saldiert.
StopLossPoints
150
Stop-Loss-Distanz in Preisschritten. Zum Deaktivieren auf 0 setzen.
TakeProfitPoints
460
Take-Profit-Distanz in Preisschritten. Zum Deaktivieren auf 0 setzen.
CmoPeriod
15
Länge des Chande-Momentum-Oszillators, der das adaptive VIDYA-Gewicht bestimmt.
EmaPeriod
12
EMA Länge, die den Basisglättungskoeffizienten in der VIDYA-Formel definiert.
MaShift
1
Anzahl der abgeschlossenen Kerzen, die verwendet werden, um die VIDYA-Linie nach vorne zu verschieben, entsprechend der Eingabe ma_shift des Indikators MetaTrader.
AppliedPrice
Close
An die VIDYA-Berechnung übergebene Preisquelle (Close, Open, High, Low, Median, Typical, Weighted).
CandleType
TimeSpan.FromMinutes(5)
Kerzentyp und Zeitrahmen, die für alle Berechnungen und Signale verwendet werden.
Zusätzliche Hinweise
Schutzanordnungen werden über den integrierten High-Level-API (SetStopLoss/SetTakeProfit) verwaltet, während der ursprüngliche MQL-Code manuelle Prüfungen anhand der Einfrierstufen durchführte.
Die Strategie abonniert nur fertige Kerzen und repliziert die Ausführungsbeschränkung „neuer Balken“ von MetaTrader.
Der VIDYA-Verlauf wird automatisch gekürzt, sodass der Speicherbedarf auch dann gering bleibt, wenn MaShift groß ist.
Alle Kommentare im Code sind auf Englisch verfasst, um den Projektanforderungen zu entsprechen.
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Simplified from "iVIDyA Simple" MetaTrader expert.
/// Computes a Variable Index Dynamic Average using CMO and EMA smoothing.
/// Trades when price crosses above/below the VIDYA line.
/// </summary>
public class IvidyaSimpleStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _cmoPeriod;
private readonly StrategyParam<int> _emaPeriod;
private ChandeMomentumOscillator _cmo;
private decimal? _prevVidya;
private decimal? _prevClose;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int CmoPeriod
{
get => _cmoPeriod.Value;
set => _cmoPeriod.Value = value;
}
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
public IvidyaSimpleStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Primary candle series", "General");
_cmoPeriod = Param(nameof(CmoPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("CMO Period", "Chande Momentum Oscillator length", "Indicator");
_emaPeriod = Param(nameof(EmaPeriod), 30)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "Base EMA length used by VIDYA", "Indicator");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_cmo = new ChandeMomentumOscillator { Length = CmoPeriod };
_prevVidya = null;
_prevClose = null;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_cmo, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal cmoValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_cmo.IsFormed)
return;
var close = candle.ClosePrice;
// VIDYA = alpha * |CMO/100| * price + (1 - alpha * |CMO/100|) * prevVidya
var alpha = 2m / (EmaPeriod + 1m);
var momentumFactor = Math.Abs(cmoValue) / 100m;
var sf = alpha * momentumFactor;
var prevVidya = _prevVidya ?? close;
var currentVidya = sf * close + (1m - sf) * prevVidya;
if (_prevVidya is null || _prevClose is null)
{
_prevVidya = currentVidya;
_prevClose = close;
return;
}
// Price crosses above VIDYA -> buy
var crossUp = _prevClose <= _prevVidya && close > currentVidya;
// Price crosses below VIDYA -> sell
var crossDown = _prevClose >= _prevVidya && close < currentVidya;
var minDistance = close * 0.001m;
var volume = Volume;
if (volume <= 0)
volume = 1;
if (crossUp && Math.Abs(close - currentVidya) >= minDistance)
{
if (Position <= 0)
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
}
else if (crossDown && Math.Abs(close - currentVidya) >= minDistance)
{
if (Position >= 0)
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
}
_prevVidya = currentVidya;
_prevClose = close;
}
/// <inheritdoc />
protected override void OnReseted()
{
_cmo = null;
_prevVidya = null;
_prevClose = null;
base.OnReseted();
}
}
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 ChandeMomentumOscillator
from StockSharp.Algo.Strategies import Strategy
class ividya_simple_strategy(Strategy):
def __init__(self):
super(ividya_simple_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._cmo_period = self.Param("CmoPeriod", 20)
self._ema_period = self.Param("EmaPeriod", 30)
self._prev_vidya = 0.0
self._prev_close = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def CmoPeriod(self):
return self._cmo_period.Value
@CmoPeriod.setter
def CmoPeriod(self, value):
self._cmo_period.Value = value
@property
def EmaPeriod(self):
return self._ema_period.Value
@EmaPeriod.setter
def EmaPeriod(self, value):
self._ema_period.Value = value
def OnReseted(self):
super(ividya_simple_strategy, self).OnReseted()
self._prev_vidya = 0.0
self._prev_close = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(ividya_simple_strategy, self).OnStarted2(time)
self._prev_vidya = 0.0
self._prev_close = 0.0
self._has_prev = False
cmo = ChandeMomentumOscillator()
cmo.Length = self.CmoPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(cmo, self._process_candle).Start()
def _process_candle(self, candle, cmo_value):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
cmo_val = float(cmo_value)
# VIDYA = alpha * |CMO/100| * price + (1 - alpha * |CMO/100|) * prevVidya
alpha = 2.0 / (self.EmaPeriod + 1.0)
momentum_factor = abs(cmo_val) / 100.0
sf = alpha * momentum_factor
prev_vidya = self._prev_vidya if self._has_prev else close
current_vidya = sf * close + (1.0 - sf) * prev_vidya
if self._has_prev:
cross_up = self._prev_close <= self._prev_vidya and close > current_vidya
cross_down = self._prev_close >= self._prev_vidya and close < current_vidya
min_distance = close * 0.001
if cross_up and abs(close - current_vidya) >= min_distance and self.Position <= 0:
self.BuyMarket()
elif cross_down and abs(close - current_vidya) >= min_distance and self.Position >= 0:
self.SellMarket()
self._prev_vidya = current_vidya
self._prev_close = close
self._has_prev = True
def CreateClone(self):
return ividya_simple_strategy()