GitHub で見る
HTH Traderヘッジ戦略
概要
この戦略はMetaTraderエキスパートアドバイザー「HTH Trader」の直接変換版です。4本脚のフォレックスバスケットを取引し、EURUSDとUSDCHF、GBPUSD、AUDUSDのミラーバスケット間の日次平均回帰を捉えようとします。StockSharpポートは元のリスク管理とタイミングルールを維持しながら、マルチ証券取引に高レベルAPIを使用します。
主な特徴:
- 端末時間の00:05から00:12の間に1日1回ヘッジバスケットをオープンします。
- EURUSDの過去2日分の日次終値を使用してバスケットの方向を決定します。
- 4つのインストゥルメントを同時に管理:EURUSD(主要証券)、USDCHF、GBPUSD、AUDUSD。
- オープン利益をpipsで追跡し、バスケット全体の利益・損失ターゲットをサポートします。
- バスケットのドローダウンが閾値を超えたときに利益が出ている脚に追加する緊急倍増機能を含みます。
- 端末時間23:00またはバスケットが設定した利益/損失制限に達したときにすべての取引をクローズします。
データ要件
- イントラデイロウソク足: 4つのシンボルすべてが
IntradayCandleTypeで設定された時間軸(デフォルト5分)のイントラデイロウソク足を配信する必要があります。これらのロウソク足は最新の価格とセッション時計を提供します。
- 日次ロウソク足: 戦略が最後の2つの完成した日次終値を監視できるように、各シンボルは日次ロウソク足を提供する必要があります。
トレードロジック
- 完成した各イントラデイロウソク足の終わりに、戦略は現在のオープン利益を確認します:
AllowEmergencyTradingが有効で、総オープン利益≤-EmergencyLossPipsの場合、戦略は現在利益が出ているすべての脚を倍増し、その日のそれ以上の緊急取引を無効にします。
UseProfitTargetが有効で、総オープン利益≥ProfitTargetPipsの場合、バスケットは即座にクローズされます。
UseLossLimitが有効で、総オープン利益≤-LossLimitPipsの場合、バスケットは即座にクローズされます。
- 時計が23:00に達すると、利益に関わらずバスケットはクローズされます。
- ポジションが開いておらず、時計が00:05–00:12ウィンドウ内にある場合、戦略は主要シンボル(デフォルトEURUSD)の最後の2つの完成した日次終値を確認します:
- 日次の変化率が正の場合、戦略はオープンします:ロングEURUSD、ロングUSDCHF、ショートGBPUSD、ロングAUDUSD。
- 変化が負の場合、オープンします:ショートEURUSD、ショートUSDCHF、ロングGBPUSD、ショートAUDUSD。
- 変化がゼロか日次終値が欠落している場合、戦略はその日の取引をスキップします。
- すべてのポジションは
ClosePositionを通じて成行注文でクローズされます。
パラメーター
| 名前 |
説明 |
デフォルト |
TradeEnabled |
注文の配置を有効または無効にします。 |
true |
ShowProfitInfo |
ポジションがオープンの間、各更新時にバスケット利益をpipsで記録します。 |
true |
UseProfitTarget |
ProfitTargetPipsに達したときの自動クローズを有効にします。 |
false |
UseLossLimit |
LossLimitPipsに達したときの自動クローズを有効にします。 |
false |
AllowEmergencyTrading |
緊急倍増機能を許可します。 |
true |
EmergencyLossPips |
緊急倍増をトリガーするバスケットドローダウン(pips単位)。 |
60 |
ProfitTargetPips |
UseProfitTargetが有効なときにクローズをトリガーするバスケット利益(pips単位)。 |
80 |
LossLimitPips |
UseLossLimitが有効なときにクローズをトリガーするバスケット損失(pips単位)。 |
40 |
TradingVolume |
各脚の注文ボリューム。 |
0.01 |
Symbol2 |
2番目の証券(デフォルトUSDCHF)。 |
null |
Symbol3 |
3番目の証券(デフォルトGBPUSD)。 |
null |
Symbol4 |
4番目の証券(デフォルトAUDUSD)。 |
null |
IntradayCandleType |
スケジューリングと価格更新に使用するイントラデイ時間軸。 |
5分ロウソク足 |
使用上の注記
- 開始前に主要証券(
Strategy.Security)をEURUSD(または希望するリード通貨ペア)に割り当て、Symbol2、Symbol3、Symbol4を相関するインストゥルメントにマッピングします。
- 各証券に有効な
PriceStepがあることを確認してください。そうでないとpipsでの利益計算ができず、緊急ロジックは非アクティブのままになります。
- 緊急倍増機能は現在利益が出ている脚にのみ追加します。損失が出ている脚はドローダウンを増幅させないよう手つかずのままにされます。
- 実装は成行注文が最新のロウソク足終値近くで執行されると仮定します。正確な計算のために、タイムリーなイントラデイロウソク足を配信するデータフィードに戦略を接続してください。
- ロジックは1分(または選択した時間軸)ごとの単一バーで駆動されるため、元のMQLのティックバイティック動作は執行タイミングでわずかに異なる場合がありますが、取引のシーケンスと条件は参照エキスパートアドバイザーと一致します。
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>
/// Hedge strategy simplified from HTH Trader. Trades based on daily close deviation.
/// </summary>
public class HthTraderStrategy : Strategy
{
private readonly StrategyParam<bool> _tradeEnabled;
private readonly StrategyParam<bool> _useProfitTarget;
private readonly StrategyParam<bool> _useLossLimit;
private readonly StrategyParam<int> _profitTargetPips;
private readonly StrategyParam<int> _lossLimitPips;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevClose1;
private decimal _prevClose2;
private decimal _entryPrice;
private decimal _priceStep;
/// <summary>
/// Enable automated trading.
/// </summary>
public bool TradeEnabled
{
get => _tradeEnabled.Value;
set => _tradeEnabled.Value = value;
}
/// <summary>
/// Enable closing by reaching the profit target.
/// </summary>
public bool UseProfitTarget
{
get => _useProfitTarget.Value;
set => _useProfitTarget.Value = value;
}
/// <summary>
/// Enable closing by reaching the loss limit.
/// </summary>
public bool UseLossLimit
{
get => _useLossLimit.Value;
set => _useLossLimit.Value = value;
}
/// <summary>
/// Profit target in pips.
/// </summary>
public int ProfitTargetPips
{
get => _profitTargetPips.Value;
set => _profitTargetPips.Value = value;
}
/// <summary>
/// Loss limit in pips.
/// </summary>
public int LossLimitPips
{
get => _lossLimitPips.Value;
set => _lossLimitPips.Value = value;
}
/// <summary>
/// Candle type for monitoring.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes parameters.
/// </summary>
public HthTraderStrategy()
{
_tradeEnabled = Param(nameof(TradeEnabled), true)
.SetDisplay("Trade Enabled", "Allow the strategy to submit orders", "General");
_useProfitTarget = Param(nameof(UseProfitTarget), true)
.SetDisplay("Use Profit Target", "Close when profit target is reached", "Risk");
_useLossLimit = Param(nameof(UseLossLimit), true)
.SetDisplay("Use Loss Limit", "Close when loss limit is reached", "Risk");
_profitTargetPips = Param(nameof(ProfitTargetPips), 80)
.SetDisplay("Profit Target (pips)", "Profit target in pips", "Risk");
_lossLimitPips = Param(nameof(LossLimitPips), 40)
.SetDisplay("Loss Limit (pips)", "Loss limit in pips", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for monitoring", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevClose1 = 0m;
_prevClose2 = 0m;
_entryPrice = 0m;
_priceStep = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_priceStep = Security?.PriceStep ?? 0.0001m;
if (_priceStep <= 0m)
_priceStep = 0.0001m;
SubscribeCandles(CandleType)
.Bind(ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
if (!TradeEnabled)
return;
// Check exit conditions
if (Position != 0 && _entryPrice > 0m)
{
var priceDiff = Position > 0
? candle.ClosePrice - _entryPrice
: _entryPrice - candle.ClosePrice;
var pipsDiff = priceDiff / _priceStep;
if (UseProfitTarget && pipsDiff >= ProfitTargetPips)
{
if (Position > 0) SellMarket();
else BuyMarket();
_entryPrice = 0m;
return;
}
if (UseLossLimit && pipsDiff <= -LossLimitPips)
{
if (Position > 0) SellMarket();
else BuyMarket();
_entryPrice = 0m;
return;
}
}
// Entry logic based on daily close deviation
if (Position == 0 && _prevClose1 > 0m && _prevClose2 > 0m)
{
var deviation = (100m * _prevClose1 / _prevClose2) - 100m;
if (deviation > 0.1m)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
}
else if (deviation < -0.1m)
{
SellMarket();
_entryPrice = candle.ClosePrice;
}
}
_prevClose2 = _prevClose1;
_prevClose1 = candle.ClosePrice;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class hth_trader_strategy(Strategy):
def __init__(self):
super(hth_trader_strategy, self).__init__()
self._trade_enabled = self.Param("TradeEnabled", True)
self._use_profit_target = self.Param("UseProfitTarget", True)
self._use_loss_limit = self.Param("UseLossLimit", True)
self._profit_target_pips = self.Param("ProfitTargetPips", 80)
self._loss_limit_pips = self.Param("LossLimitPips", 40)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._prev_close1 = 0.0
self._prev_close2 = 0.0
self._entry_price = 0.0
self._price_step = 0.0
@property
def TradeEnabled(self):
return self._trade_enabled.Value
@TradeEnabled.setter
def TradeEnabled(self, value):
self._trade_enabled.Value = value
@property
def UseProfitTarget(self):
return self._use_profit_target.Value
@UseProfitTarget.setter
def UseProfitTarget(self, value):
self._use_profit_target.Value = value
@property
def UseLossLimit(self):
return self._use_loss_limit.Value
@UseLossLimit.setter
def UseLossLimit(self, value):
self._use_loss_limit.Value = value
@property
def ProfitTargetPips(self):
return self._profit_target_pips.Value
@ProfitTargetPips.setter
def ProfitTargetPips(self, value):
self._profit_target_pips.Value = value
@property
def LossLimitPips(self):
return self._loss_limit_pips.Value
@LossLimitPips.setter
def LossLimitPips(self, value):
self._loss_limit_pips.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(hth_trader_strategy, self).OnStarted2(time)
self._price_step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 0.0001
if self._price_step <= 0.0:
self._price_step = 0.0001
self._prev_close1 = 0.0
self._prev_close2 = 0.0
self._entry_price = 0.0
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self.ProcessCandle).Start()
def ProcessCandle(self, candle):
if candle.State != CandleStates.Finished:
return
if not self.TradeEnabled:
return
close = float(candle.ClosePrice)
# Check exit conditions
if self.Position != 0 and self._entry_price > 0.0:
if self.Position > 0:
price_diff = close - self._entry_price
else:
price_diff = self._entry_price - close
pips_diff = price_diff / self._price_step
if self.UseProfitTarget and pips_diff >= int(self.ProfitTargetPips):
if self.Position > 0:
self.SellMarket()
else:
self.BuyMarket()
self._entry_price = 0.0
return
if self.UseLossLimit and pips_diff <= -int(self.LossLimitPips):
if self.Position > 0:
self.SellMarket()
else:
self.BuyMarket()
self._entry_price = 0.0
return
# Entry logic
if self.Position == 0 and self._prev_close1 > 0.0 and self._prev_close2 > 0.0:
deviation = (100.0 * self._prev_close1 / self._prev_close2) - 100.0
if deviation > 0.1:
self.BuyMarket()
self._entry_price = close
elif deviation < -0.1:
self.SellMarket()
self._entry_price = close
self._prev_close2 = self._prev_close1
self._prev_close1 = close
def OnReseted(self):
super(hth_trader_strategy, self).OnReseted()
self._prev_close1 = 0.0
self._prev_close2 = 0.0
self._entry_price = 0.0
self._price_step = 0.0
def CreateClone(self):
return hth_trader_strategy()