あらゆるポジションをヘッジする戦略
概要
あらゆるポジションをヘッジする戦略は、元のMQL5エキスパート*Hedge any positions (barabashkakvn's edition)*の直接変換です。StockSharpバージョンは中心的なアイデアをそのまま保持します:戦略によって作成された各オープンレッグを監視し、レッグが定義された数のpipsを失うと、すぐに増幅されたロットサイズで反対ポジションを開きます。実装はStockSharpの高レベルAPIに依存しているため、ヘッジ注文は成行注文を通じて配置され、ポジション追跡はカスタム注文ルーティングコードなしに内部で処理されます。
戦略は開始時にオプションで最初のトレードを配置できます。その後は単に不利な価格変動に反応し、ヘッジングトレードのはしごを構築し、各レッグをヘッジ済みとしてマークして同じポジションが複数の反対エントリーを引き起こせないようにします。
ヘッジングワークフロー
- ローソク足フィード – 設定可能な
CandleTypeが戦略を駆動します。完了したローソク足のみが処理されます。
- 損失計算 – 各ローソク足のクローズ時に、戦略はクローズ価格が計算されたpipサイズを掛けた
LosingPips以上だけオープンレッグに不利に動いたかどうかを確認します。
- ヘッジ実行 – 損失レッグが見つかった場合、反対方向に成行注文が送られます。注文ボリュームは元のレッグボリュームを
LotCoefficientで掛けたものに等しく、銘柄ボリュームステップに丸められ、許可された最小/最大ボリュームに制限されます。
- 状態更新 – 反対の注文が送られると、元のレッグはヘッジ済みとしてフラグが立てられ、新しく開かれたトレードは新鮮なレッグとして保存されます。そのレッグ自体も後で価格が再び反転した場合にヘッジされる可能性があります。
パラメーター
| パラメーター |
説明 |
デフォルト |
CandleType |
価格変動を評価してヘッジを引き起こすために使用される時間軸。 |
1分ローソク足 |
LosingPips |
ヘッジが開かれる前に価格がレッグに不利に動く必要があるpips数。 |
5 |
LotCoefficient |
ヘッジ注文を送信する際に元のボリュームに適用される乗数。 |
2.0 |
AutoPlaceInitialTrade |
有効にすると、戦略は開始時に最初のトレードを自動的に送ります。 |
無効 |
InitialVolume |
オプションの最初のトレードで使用される注文サイズ。銘柄ボリュームステップに丸められます。 |
0.10 |
InitialDirection |
オプションの最初のトレードに使用されるサイド(買いまたは売り)。 |
買い |
注: Strategy.Volumeプロパティを戦略が使用する基本注文サイズに設定します。上記のパラメーターはヘッジング固有の動作のみを制御します。
使用ガイドライン
- 戦略を開始する前に
Security、Portfolio、希望する基本Volumeを割り当てます。
- 選択した銘柄のボラティリティとリスク許容度を反映するよう
LosingPipsとLotCoefficientを調整します。
- StockSharpバージョンが最初のポジションを自動的に作成するようにしたい場合は
AutoPlaceInitialTradeを有効にします;そうでない場合は手動で最初のレッグを開くか、別のコンポーネントが行います。
- StockSharpの高レベルAPIはネットポジションで動作するため、内部レッグリストはヘッジ構造をエミュレートするために使用されます。ネッティングアカウントで実行する場合はアカウントエクスポージャーを監視します。
- 実行レポートを確認します:各ヘッジは成行注文(
BuyMarketまたはSellMarket)で配置されます。
元のエキスパートとの違い
- マージン検証、スリッページチェック、詳細な結果ログは削除されました;StockSharpはすでに戦略イベントを通じて実行の問題を報告します。
- 変換はtick-by-tickデータの代わりに完了したローソク足を使用します。より速い反応時間が必要な場合は十分に小さな時間軸を選択します。
- ロット丸めは
Security.VolumeStep、Security.MinVolume、Security.MaxVolumeに基づき、銘柄のトレードルールに準拠します。
- アラート、通知、MQLバージョンのテスター専用のランダム初期トレードは意図的に省略されました。オプションの自動エントリーパラメーターがその動作を置き換えます。
推奨される機能拡張
- 最初のポジションをいつ作成するかを定義する個別のエントリー戦略でヘッジングモジュールを組み合わせます。
- 無制限のヘッジングチェーンを防ぐために、エクイティベースのシャットダウンルールまたは最大深度制限を追加します。
- マージン要件が許容可能な限度内に収まるようにポートフォリオレベルの監視を統合します。
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;
/// <summary>
/// Hedge Any Positions strategy using ATR-based mean reversion.
/// Enters long when price drops below EMA by ATR threshold, enters short on rally above.
/// Uses stop-loss and take-profit for risk management.
/// </summary>
public class HedgeAnyPositionsStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _ema;
private AverageTrueRange _atr;
private decimal _entryPrice;
private int _cooldown;
/// <summary>
/// EMA period.
/// </summary>
public int EmaPeriod
{
get => _emaPeriod.Value;
set => _emaPeriod.Value = value;
}
/// <summary>
/// ATR period.
/// </summary>
public int AtrPeriod
{
get => _atrPeriod.Value;
set => _atrPeriod.Value = value;
}
/// <summary>
/// Multiplier applied to ATR for entry threshold.
/// </summary>
public decimal AtrMultiplier
{
get => _atrMultiplier.Value;
set => _atrMultiplier.Value = value;
}
/// <summary>
/// Stop-loss distance in price steps.
/// </summary>
public int StopLossPoints
{
get => _stopLossPoints.Value;
set => _stopLossPoints.Value = value;
}
/// <summary>
/// Take-profit distance in price steps.
/// </summary>
public int TakeProfitPoints
{
get => _takeProfitPoints.Value;
set => _takeProfitPoints.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="HedgeAnyPositionsStrategy"/> class.
/// </summary>
public HedgeAnyPositionsStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 100)
.SetGreaterThanZero()
.SetDisplay("EMA Period", "EMA period for mean price", "Indicator");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR calculation period", "Indicator");
_atrMultiplier = Param(nameof(AtrMultiplier), 2m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "Multiplier for entry distance", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200)
.SetNotNegative()
.SetDisplay("Stop Loss", "Stop-loss distance in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 300)
.SetNotNegative()
.SetDisplay("Take Profit", "Take-profit distance in price steps", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ema = null;
_atr = null;
_entryPrice = 0;
_cooldown = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ema = new ExponentialMovingAverage { Length = EmaPeriod };
_atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_ema, _atr, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal emaValue, decimal atrValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_ema.IsFormed || !_atr.IsFormed)
return;
if (_cooldown > 0)
{
_cooldown--;
return;
}
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
var threshold = atrValue * AtrMultiplier;
// Check SL/TP
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
return;
}
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step)
{
SellMarket();
_entryPrice = 0;
_cooldown = 80;
return;
}
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
return;
}
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step)
{
BuyMarket();
_entryPrice = 0;
_cooldown = 80;
return;
}
}
// Mean reversion: buy when price drops below EMA by threshold
if (close < emaValue - threshold && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = close;
_cooldown = 80;
}
// Mean reversion: sell when price rallies above EMA by threshold
else if (close > emaValue + threshold && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = close;
_cooldown = 80;
}
}
}
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, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class hedge_any_positions_strategy(Strategy):
"""
Hedge Any Positions: ATR-based mean reversion.
Enters long when price drops below EMA by ATR*multiplier.
Enters short when price rallies above EMA by ATR*multiplier.
Uses SL/TP for risk management with cooldown.
"""
def __init__(self):
super(hedge_any_positions_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 100) \
.SetDisplay("EMA Period", "EMA period for mean price", "Indicator")
self._atr_period = self.Param("AtrPeriod", 14) \
.SetDisplay("ATR Period", "ATR calculation period", "Indicator")
self._atr_multiplier = self.Param("AtrMultiplier", 2.0) \
.SetDisplay("ATR Multiplier", "Multiplier for entry distance", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200) \
.SetDisplay("Stop Loss", "Stop-loss distance in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 300) \
.SetDisplay("Take Profit", "Take-profit distance in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._entry_price = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(hedge_any_positions_strategy, self).OnReseted()
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(hedge_any_positions_strategy, self).OnStarted2(time)
ema = ExponentialMovingAverage()
ema.Length = self._ema_period.Value
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(ema, atr, self._process_candle).Start()
def _process_candle(self, candle, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
if self._cooldown > 0:
self._cooldown -= 1
return
close = float(candle.ClosePrice)
ema = float(ema_val)
atr = float(atr_val)
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 1.0
threshold = atr * self._atr_multiplier.Value
if self.Position > 0 and self._entry_price > 0:
sl_pts = self._stop_loss_points.Value
tp_pts = self._take_profit_points.Value
if sl_pts > 0 and close <= self._entry_price - sl_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
return
if tp_pts > 0 and close >= self._entry_price + tp_pts * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 80
return
elif self.Position < 0 and self._entry_price > 0:
sl_pts = self._stop_loss_points.Value
tp_pts = self._take_profit_points.Value
if sl_pts > 0 and close >= self._entry_price + sl_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
return
if tp_pts > 0 and close <= self._entry_price - tp_pts * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 80
return
if close < ema - threshold and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 80
elif close > ema + threshold and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 80
def CreateClone(self):
return hedge_any_positions_strategy()