Vortex オシレーター・システム戦略
概要
VortexオシレーターシステムはMetaTrader 5のエキスパートアドバイザーの直接移植で、Vortexオシレーターを使用して正の方向性動きと負の方向性動きの間の急激な転換を捉えます。オシレーターは選択されたローソク足シリーズで計算されたVortexポジティブライン(VI+)とVortexネガティブライン(VI-)の差として構築されます。深い負の値はVI-がVI+を支配していることを示し、強い正の値はVI+のリードを示します。戦略はそれらの極値を潜在的な変曲点ゾーンとして解釈し、オシレーター駆動の決済に支えられた平均回帰スタイルのエントリーで反応します。
戦略の仕組み
- ローソク足は設定された時間軸を使用して構築され、組み込みの
VortexIndicatorに供給されます。 - インジケーターが形成されると、完成したローソク足ごとにオシレーター値が
VI+ - VI-として導出されます。 - オシレーターはユーザー定義のしきい値と比較されます:
- 買いしきい値を下回ると、ロングセットアップが検出されます。
- 売りしきい値を上回ると、ショートセットアップが検出されます。
- オプションフィルターにより、ロングシグナルを買いしきい値と専用のストップロスレベルの間のゾーンに制限できます(ショートシグナルの場合は逆)。
- 新しいセットアップが現れると、戦略は反対のポジションを閉じて設定されたボリュームでシグナル方向に取引を開始します。
- オープンポジションは継続的に監視されます。オシレーターが設定されたストップロスまたはテイクプロフィットの境界に達すると、ポジションは即座に閉じられます。
このシーケンスは元のMetaTraderロジックを再現します:取引は完成したバーでのみ評価され、両方向は相互排他的で、オシレーターベースの保護ルールが決済を管理します。
エントリー条件
- ロングエントリー
- オシレーターが買いしきい値以下の場合に発動します。
- ロングストップロスオプションが有効の場合、オシレーターはロングストップロスレベルより上にも留まる必要があります。
- ロング取引を開始する前に、アクティブなショートポジションが閉じられます。
- ショートエントリー
- オシレーターが売りしきい値以上の場合に発動します。
- ショートストップロスオプションが有効の場合、オシレーターはショートストップロスレベルより下にも留まる必要があります。
- ショート取引を開始する前に、アクティブなロングポジションが閉じられます。
- オシレーター値が買いと売りのしきい値の間にある場合、すべてのセットアップがキャンセルされ、ポジション変更は発生しません。
エグジット条件
- ロングポジション
- オシレーターがロングストップロスレベルを下回るかまたは等しくなると即座に閉じます(有効の場合)。
- オシレーターがロングテイクプロフィットレベルに達するかまたは上回ると即座に閉じます(有効の場合)。
- ショートポジション
- オシレーターがショートストップロスレベルを上回るかまたは等しくなると即座に閉じます(有効の場合)。
- オシレーターがショートテイクプロフィットレベルに達するかまたは下回ると即座に閉じます(有効の場合)。
決済チェックはすべてのローソク足クローズ後に実行され、MT5監視ループの忠実な再現を保証します。
パラメーター
- Vortex Length – Vortexインジケーターのルックバック期間(デフォルト14)。
- Candle Type – インジケーターに供給されるローソク足を構築するための時間軸。
- Use Buy Stop Loss – ロング取引のオシレーターベースのストップロスフィルターと決済を有効にします。
- Use Buy Take Profit – ロング取引のオシレーターベースのテイクプロフィット決済を有効にします。
- Use Sell Stop Loss – ショート取引のオシレーターベースのストップロスフィルターと決済を有効にします。
- Use Sell Take Profit – ショート取引のオシレーターベースのテイクプロフィット決済を有効にします。
- Buy Threshold – ロングエントリーを認定するオシレーター値(デフォルト -0.75)。
- Buy Stop Loss Level – ロングストップロスオプションが有効のときにロングポジションを閉じるオシレーター値(デフォルト -1.00)。
- Buy Take Profit Level – ロングテイクプロフィットオプションが有効のときにロングポジションを閉じるオシレーター値(デフォルト 0.00)。
- Sell Threshold – ショートエントリーを認定するオシレーター値(デフォルト 0.75)。
- Sell Stop Loss Level – ショートストップロスオプションが有効のときにショートポジションを閉じるオシレーター値(デフォルト 1.00)。
- Sell Take Profit Level – ショートテイクプロフィットオプションが有効のときにショートポジションを閉じるオシレーター値(デフォルト 0.00)。
- Volume – 新しいポジションに使用される取引サイズ(デフォルト 0.1、元のエキスパートアドバイザーに合わせて)。
実装上の注意
- 同じバー内でのシグナル重複を避けるため、処理は完成したローソク足のみで行われます。
- オシレーターしきい値はパラメーターメタデータに提供されている範囲のおかげで最適化できます。
- 戦略は反対側を閉じて新しいエクスポージャーを確立するのに十分な大きさの市場注文を送信することで自動的にポジションを反転させます。
- ストップロスとテイクプロフィット機能は独立して動作します;一方を有効にしても他方は必要ありません。
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>
/// Vortex oscillator trading system ported from the MetaTrader implementation.
/// Opens long positions when the oscillator drops below a configured level and
/// shorts when the oscillator rises above the upper threshold.
/// Optional stop-loss and take-profit rules monitor the oscillator value to exit positions.
/// </summary>
public class VortexOscillatorSystemStrategy : Strategy
{
private readonly StrategyParam<int> _length;
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<bool> _useBuyStopLoss;
private readonly StrategyParam<bool> _useBuyTakeProfit;
private readonly StrategyParam<bool> _useSellStopLoss;
private readonly StrategyParam<bool> _useSellTakeProfit;
private readonly StrategyParam<decimal> _buyThreshold;
private readonly StrategyParam<decimal> _buyStopLossLevel;
private readonly StrategyParam<decimal> _buyTakeProfitLevel;
private readonly StrategyParam<decimal> _sellThreshold;
private readonly StrategyParam<decimal> _sellStopLossLevel;
private readonly StrategyParam<decimal> _sellTakeProfitLevel;
private VortexIndicator _vortexIndicator = null!;
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public VortexOscillatorSystemStrategy()
{
_length = Param(nameof(Length), 14)
.SetGreaterThanZero()
.SetDisplay("Vortex Length", "Period used for the Vortex indicator.", "General")
.SetOptimize(7, 28, 7);
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe used to build candles for calculations.", "General");
_useBuyStopLoss = Param(nameof(UseBuyStopLoss), false)
.SetDisplay("Use Buy Stop Loss", "Enable oscillator-based stop loss for long positions.", "Risk Management");
_useBuyTakeProfit = Param(nameof(UseBuyTakeProfit), false)
.SetDisplay("Use Buy Take Profit", "Enable oscillator-based take profit for long positions.", "Risk Management");
_useSellStopLoss = Param(nameof(UseSellStopLoss), false)
.SetDisplay("Use Sell Stop Loss", "Enable oscillator-based stop loss for short positions.", "Risk Management");
_useSellTakeProfit = Param(nameof(UseSellTakeProfit), false)
.SetDisplay("Use Sell Take Profit", "Enable oscillator-based take profit for short positions.", "Risk Management");
_buyThreshold = Param(nameof(BuyThreshold), -0.1m)
.SetDisplay("Buy Threshold", "Oscillator value that triggers a long setup.", "Signals")
.SetOptimize(-1.5m, -0.25m, 0.25m);
_buyStopLossLevel = Param(nameof(BuyStopLossLevel), -1m)
.SetDisplay("Buy Stop Loss Level", "Oscillator value that closes long trades when stop loss is enabled.", "Signals");
_buyTakeProfitLevel = Param(nameof(BuyTakeProfitLevel), 0m)
.SetDisplay("Buy Take Profit Level", "Oscillator value that closes long trades when take profit is enabled.", "Signals");
_sellThreshold = Param(nameof(SellThreshold), 0.1m)
.SetDisplay("Sell Threshold", "Oscillator value that triggers a short setup.", "Signals")
.SetOptimize(0.25m, 1.5m, 0.25m);
_sellStopLossLevel = Param(nameof(SellStopLossLevel), 1m)
.SetDisplay("Sell Stop Loss Level", "Oscillator value that closes short trades when stop loss is enabled.", "Signals");
_sellTakeProfitLevel = Param(nameof(SellTakeProfitLevel), 0m)
.SetDisplay("Sell Take Profit Level", "Oscillator value that closes short trades when take profit is enabled.", "Signals");
Volume = 0.1m;
}
/// <summary>
/// Vortex indicator lookback length.
/// </summary>
public int Length
{
get => _length.Value;
set => _length.Value = value;
}
/// <summary>
/// Candle type used for indicator calculations.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Enable oscillator-based stop loss for long positions.
/// </summary>
public bool UseBuyStopLoss
{
get => _useBuyStopLoss.Value;
set => _useBuyStopLoss.Value = value;
}
/// <summary>
/// Enable oscillator-based take profit for long positions.
/// </summary>
public bool UseBuyTakeProfit
{
get => _useBuyTakeProfit.Value;
set => _useBuyTakeProfit.Value = value;
}
/// <summary>
/// Enable oscillator-based stop loss for short positions.
/// </summary>
public bool UseSellStopLoss
{
get => _useSellStopLoss.Value;
set => _useSellStopLoss.Value = value;
}
/// <summary>
/// Enable oscillator-based take profit for short positions.
/// </summary>
public bool UseSellTakeProfit
{
get => _useSellTakeProfit.Value;
set => _useSellTakeProfit.Value = value;
}
/// <summary>
/// Oscillator level used to open long trades.
/// </summary>
public decimal BuyThreshold
{
get => _buyThreshold.Value;
set => _buyThreshold.Value = value;
}
/// <summary>
/// Oscillator level that closes long trades when stop loss is enabled.
/// </summary>
public decimal BuyStopLossLevel
{
get => _buyStopLossLevel.Value;
set => _buyStopLossLevel.Value = value;
}
/// <summary>
/// Oscillator level that closes long trades when take profit is enabled.
/// </summary>
public decimal BuyTakeProfitLevel
{
get => _buyTakeProfitLevel.Value;
set => _buyTakeProfitLevel.Value = value;
}
/// <summary>
/// Oscillator level used to open short trades.
/// </summary>
public decimal SellThreshold
{
get => _sellThreshold.Value;
set => _sellThreshold.Value = value;
}
/// <summary>
/// Oscillator level that closes short trades when stop loss is enabled.
/// </summary>
public decimal SellStopLossLevel
{
get => _sellStopLossLevel.Value;
set => _sellStopLossLevel.Value = value;
}
/// <summary>
/// Oscillator level that closes short trades when take profit is enabled.
/// </summary>
public decimal SellTakeProfitLevel
{
get => _sellTakeProfitLevel.Value;
set => _sellTakeProfitLevel.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_vortexIndicator = new VortexIndicator
{
Length = Length,
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_vortexIndicator, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue vortexValue)
{
// Process only finished candles to mirror bar-close logic from the original script.
if (candle.State != CandleStates.Finished)
return;
if (!_vortexIndicator.IsFormed)
return;
var vortexTyped = (VortexIndicatorValue)vortexValue;
var viPlus = vortexTyped.PlusVi ?? 0m;
var viMinus = vortexTyped.MinusVi ?? 0m;
// Vortex oscillator equals the difference between VI+ and VI- lines.
var oscillator = viPlus - viMinus;
var longSetupExists = false;
var shortSetupExists = false;
// Long setups are considered when the oscillator falls below the buy threshold.
if (UseBuyStopLoss)
{
if (oscillator <= BuyThreshold && oscillator > BuyStopLossLevel)
{
longSetupExists = true;
shortSetupExists = false;
}
}
else if (oscillator <= BuyThreshold)
{
longSetupExists = true;
shortSetupExists = false;
}
// Short setups require the oscillator to rise above the sell threshold.
if (UseSellStopLoss)
{
if (oscillator >= SellThreshold && oscillator < SellStopLossLevel)
{
shortSetupExists = true;
longSetupExists = false;
}
}
else if (oscillator >= SellThreshold)
{
shortSetupExists = true;
longSetupExists = false;
}
// Neutral zone cancels both long and short intentions.
if (oscillator >= BuyThreshold && oscillator <= SellThreshold)
{
longSetupExists = false;
shortSetupExists = false;
}
var currentPosition = Position;
if (longSetupExists && currentPosition <= 0)
{
// Close existing shorts and open a long position when a valid long setup appears.
BuyMarket();
}
else if (shortSetupExists && currentPosition >= 0)
{
// Close existing longs and open a short position when a valid short setup appears.
SellMarket();
}
currentPosition = Position;
if (currentPosition > 0)
{
// Manage long positions with oscillator-based stops and targets.
if (UseBuyStopLoss && oscillator <= BuyStopLossLevel)
{
SellMarket();
return;
}
if (UseBuyTakeProfit && oscillator >= BuyTakeProfitLevel)
{
SellMarket();
return;
}
}
else if (currentPosition < 0)
{
// Manage short positions with oscillator-based stops and targets.
if (UseSellStopLoss && oscillator >= SellStopLossLevel)
{
BuyMarket();
return;
}
if (UseSellTakeProfit && oscillator <= SellTakeProfitLevel)
{
BuyMarket();
}
}
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import VortexIndicator, VortexIndicatorValue
class vortex_oscillator_system_strategy(Strategy):
"""Vortex oscillator system: trades when VI+-VI- crosses configurable thresholds."""
def __init__(self):
super(vortex_oscillator_system_strategy, self).__init__()
self._length = self.Param("Length", 14) \
.SetGreaterThanZero() \
.SetDisplay("Vortex Length", "Period used for the Vortex indicator", "General")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe used to build candles", "General")
self._use_buy_stop_loss = self.Param("UseBuyStopLoss", False) \
.SetDisplay("Use Buy Stop Loss", "Enable oscillator-based stop loss for longs", "Risk")
self._use_buy_take_profit = self.Param("UseBuyTakeProfit", False) \
.SetDisplay("Use Buy Take Profit", "Enable oscillator-based take profit for longs", "Risk")
self._use_sell_stop_loss = self.Param("UseSellStopLoss", False) \
.SetDisplay("Use Sell Stop Loss", "Enable oscillator-based stop loss for shorts", "Risk")
self._use_sell_take_profit = self.Param("UseSellTakeProfit", False) \
.SetDisplay("Use Sell Take Profit", "Enable oscillator-based take profit for shorts", "Risk")
self._buy_threshold = self.Param("BuyThreshold", -0.1) \
.SetDisplay("Buy Threshold", "Oscillator value that triggers a long setup", "Signals")
self._buy_stop_loss_level = self.Param("BuyStopLossLevel", -1.0) \
.SetDisplay("Buy Stop Loss Level", "Oscillator value that closes long trades", "Signals")
self._buy_take_profit_level = self.Param("BuyTakeProfitLevel", 0.0) \
.SetDisplay("Buy Take Profit Level", "Oscillator value that closes long trades", "Signals")
self._sell_threshold = self.Param("SellThreshold", 0.1) \
.SetDisplay("Sell Threshold", "Oscillator value that triggers a short setup", "Signals")
self._sell_stop_loss_level = self.Param("SellStopLossLevel", 1.0) \
.SetDisplay("Sell Stop Loss Level", "Oscillator value that closes short trades", "Signals")
self._sell_take_profit_level = self.Param("SellTakeProfitLevel", 0.0) \
.SetDisplay("Sell Take Profit Level", "Oscillator value that closes short trades", "Signals")
@property
def Length(self):
return int(self._length.Value)
@property
def CandleType(self):
return self._candle_type.Value
@property
def UseBuyStopLoss(self):
return self._use_buy_stop_loss.Value
@property
def UseBuyTakeProfit(self):
return self._use_buy_take_profit.Value
@property
def UseSellStopLoss(self):
return self._use_sell_stop_loss.Value
@property
def UseSellTakeProfit(self):
return self._use_sell_take_profit.Value
@property
def BuyThreshold(self):
return float(self._buy_threshold.Value)
@property
def BuyStopLossLevel(self):
return float(self._buy_stop_loss_level.Value)
@property
def BuyTakeProfitLevel(self):
return float(self._buy_take_profit_level.Value)
@property
def SellThreshold(self):
return float(self._sell_threshold.Value)
@property
def SellStopLossLevel(self):
return float(self._sell_stop_loss_level.Value)
@property
def SellTakeProfitLevel(self):
return float(self._sell_take_profit_level.Value)
def OnStarted2(self, time):
super(vortex_oscillator_system_strategy, self).OnStarted2(time)
self._vortex = VortexIndicator()
self._vortex.Length = self.Length
subscription = self.SubscribeCandles(self.CandleType)
subscription.BindEx(self._vortex, self.process_candle).Start()
def process_candle(self, candle, vortex_value):
if candle.State != CandleStates.Finished:
return
if not self._vortex.IsFormed:
return
vi_plus = float(vortex_value.PlusVi) if vortex_value.PlusVi is not None else 0.0
vi_minus = float(vortex_value.MinusVi) if vortex_value.MinusVi is not None else 0.0
oscillator = vi_plus - vi_minus
long_setup = False
short_setup = False
if self.UseBuyStopLoss:
if oscillator <= self.BuyThreshold and oscillator > self.BuyStopLossLevel:
long_setup = True
short_setup = False
elif oscillator <= self.BuyThreshold:
long_setup = True
short_setup = False
if self.UseSellStopLoss:
if oscillator >= self.SellThreshold and oscillator < self.SellStopLossLevel:
short_setup = True
long_setup = False
elif oscillator >= self.SellThreshold:
short_setup = True
long_setup = False
if oscillator >= self.BuyThreshold and oscillator <= self.SellThreshold:
long_setup = False
short_setup = False
if long_setup and self.Position <= 0:
self.BuyMarket()
elif short_setup and self.Position >= 0:
self.SellMarket()
if self.Position > 0:
if self.UseBuyStopLoss and oscillator <= self.BuyStopLossLevel:
self.SellMarket()
return
if self.UseBuyTakeProfit and oscillator >= self.BuyTakeProfitLevel:
self.SellMarket()
return
elif self.Position < 0:
if self.UseSellStopLoss and oscillator >= self.SellStopLossLevel:
self.BuyMarket()
return
if self.UseSellTakeProfit and oscillator <= self.SellTakeProfitLevel:
self.BuyMarket()
def OnReseted(self):
super(vortex_oscillator_system_strategy, self).OnReseted()
def CreateClone(self):
return vortex_oscillator_system_strategy()