GitHub で見る
iVIDyA シンプル戦略
概要
この戦略は、MetaTrader エキスパート 「iVIDyA Simple」 の上位レベルの StockSharp 移植です。 Chande Momentum Oscillator(CMO)を介して市場の勢いに適応するVariable Index Dynamic Average(VIDYA)を追跡することにより、単一のシンボルを取引します。最も最近完成したローソク足がシフトされた VIDYA ラインを横切るたびに、この戦略はブレイクアウトの方向に市場ポジションを開き、オプションで保護的なストップロス注文と利益確定注文を付けます。
取引ロジック
- ローソク足データは、構成されたタイムフレーム (
CandleType) から読み取られます。
- 期間
CmoPeriod の CMO はローソク足シリーズにバインドされています。その絶対値は、VIDYA の平滑化係数を動的にスケーリングします。元の MQL 実装と同様に、基本係数は 2 / (EmaPeriod + 1) に等しくなります。
- ローリング VIDYA 値は維持されます。完成したキャンドルごとにアルゴリズムは次のようになります。
- ローソク足(終値、始値、中央値など)から適用される価格(
AppliedPrice)を選択します。
- 適応平滑化係数を使用して VIDYA を更新します。
- MetaTrader の
MA shift オプションをエミュレートする履歴値を保存します。
- ローソク足はシフトされた VIDYA 値と比較されます (
MaShift バー戻ります)。
- ローソク足が VIDYA より下で開き、VIDYA より上で閉じる場合、買いシグナルが生成されます。
- ローソク足が VIDYA より上で開き、VIDYA より下で閉じる場合、売り シグナルが生成されます。
- 新しいポジションをオープンする前に、この戦略は、反転するために必要な全量を取引することにより、反対のエクスポージャーを平準化します。
- 各エントリの後、それぞれの距離が正の場合、
SetStopLoss と SetTakeProfit が呼び出されます。
これは、新しい足のみで注文をトリガーし、CMO と EMA 期間から計算された VIDYA を使用し、ポイントで表現されるオプションのストップを付加した元のエキスパート アドバイザーを反映しています。
パラメーター
| 名前 |
デフォルト |
説明 |
Volume |
1 |
注文に使用される基本取引量。ポジションを反転すると、既存のエクスポージャが自動的にネッティングされます。 |
StopLossPoints |
150 |
価格ステップのストップロス距離。無効にするには、0 に設定します。 |
TakeProfitPoints |
460 |
価格ステップでの利食い距離。無効にするには、0 に設定します。 |
CmoPeriod |
15 |
適応 VIDYA 重みを決定する Chande Momentum Oscillator の長さ。 |
EmaPeriod |
12 |
VIDYA 式の基本平滑化係数を定義する EMA の長さ。 |
MaShift |
1 |
MetaTrader インジケーターの ma_shift 入力と一致する、VIDYA ラインを前方にシフトするために使用される完了したローソク足の数。 |
AppliedPrice |
Close |
VIDYA 計算に渡される価格ソース (Close、Open、High、Low、Median、Typical、Weighted)。 |
CandleType |
TimeSpan.FromMinutes(5) |
すべての計算とシグナルに使用されるローソク足のタイプと時間枠。 |
追加メモ
- 保護命令は、組み込みの高レベルの API (
SetStopLoss/SetTakeProfit) によって管理されますが、元の MQL コードは凍結レベルに対して手動チェックを実行していました。
- この戦略は完了したローソク足のみをサブスクライブし、MetaTrader からの「新しいバー」実行制約を複製します。
- VIDYA 履歴は自動的にトリミングされるため、
MaShift が大きい場合でもメモリ フットプリントは小さく保たれます。
- プロジェクトの要件に合わせて、コード内のコメントはすべて英語で書かれています。
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()