GitHub で見る
Hans123Trader Rangeブレイクアウト戦略
概要
Hans123Trader ストラテジー は、StockSharp の高レベル API を使用して、MetaTrader エキスパート アドバイザー「Hans123Trader v1」を再作成します。システムは、直近の 5 分間の取引レンジのブレイクに基づいて、ストップ注文を 1 日 2 回発行します。価格ステップが小数点以下のピップに対応する外国為替スタイルのシンボルに合わせて調整されています。未決注文は取引日ごとに更新され、カレンダーが切り替わるとオープンポジションは強制的にクローズされます。
コアワークフロー
- レンジトラッキング – 5分足ローソク足のローリング80バーウィンドウは、
HighestおよびLowestインジケーターを介して維持されます。直近の高値と安値がブレイクアウトレベルを定義します。
- セッションのスケジュール – 2 つの独立した取引ウィンドウは、
EndSession1 と EndSession2 によって制御されます。クロックが設定された時間 (分 00) に達すると、ストラテジーは新しい保留中のストップ注文を計算します。
- 注文の配置 – 買いストップは検出された高値より
5 ポイント上に送信され、売りストップは検出された安値より 5 ポイント下に送信されます。注文は、MetaTrader の有効期限 23:59 に倣って新しい日が始まるとすぐに削除されます。
- ポジション管理 – エントリー後、戦略は要求された最初のストップロス、オプションのテイクプロフィット、およびトレーリングストップを適用します。保護レベルはポイントで表され、商品の
PriceStep を使用して価格に変換されます。
- 毎日の衛生管理 – 新しい取引日の開始時にポジションがオープンのままの場合、そのポジションは市場でクローズされます。前日の保留中の注文はすべて、新しい注文が準備される前にキャンセルされます。
取引ルール
- エントリーシグナル
- 1 日に 2 回のブレークアウト試行: 1 回目は
EndSession1、もう 1 回目は EndSession2 (時間はブローカー/サーバー時間)。
- 買いストップ価格 =
HighestHigh + 5 points。売りストップ価格 = LowestLow − 5 points。
- どちらのオーダーも現在の
Volume パラメータ (デフォルトは 1) を使用します。
- 出来高が正でない場合、注文はスキップされます。
- 終了ロジック
- 最初のストップロス = エントリー価格 ±
InitialStopLoss ポイント (ロングの場合は下、ショートの場合は上)。
- テイクプロフィット = エントリー価格 ±
TakeProfit ポイント (上はロング、下はショート)。
- トレーリングストップは、終値が少なくとも
TrailingStop ポイント以上利益に近づくたびに、保護レベルを強化します。
- 翌日まで残ったポジションは市場で直ちに決済されます。
- 注文のメンテナンス
- 保留中のストップ注文は、暦日ごとの初めにキャンセルされます。
- 逆指値注文がトリガーされる(またはキャンセル/失敗される)と、内部参照は自動的にクリアされます。
パラメーター
| 名前 |
説明 |
BeginSession1 / BeginSession2 |
UI の互換性 (開始時間のヒント) のために保存されています。現在の実装は終了時間トリガーに依存しています。 |
EndSession1 / EndSession2 |
新しい逆指値注文が準備される時間帯 (0 ~ 23)。分は正確にゼロでなければなりません。 |
TrailingStop |
後続距離 (ポイント単位)。 0 は末尾を無効にします。 |
TakeProfit |
テイクプロフィット距離(ポイント単位)。 0 はテイクプロフィットを無効にします。 |
InitialStopLoss |
ポイント単位の初期ストップロス距離。 0 は、トレーリングがアクティブにならない限り、保護ストップなしで取引を終了します。 |
CandleType |
80 バー範囲に使用されるローソク足シリーズ (デフォルトは TimeSpan.FromMinutes(5))。 |
Volume |
Strategy から継承された戦略ベース ボリューム。 |
変換メモ
- MetaTrader ヘルパー関数
OrderSendExtended とグローバル変数ロックは必要ありません。 StockSharp は同時実行性を内部で管理します。
- マジックナンバーは明示的な順序参照 (
_session* フィールド) に置き換えられます。注文ライフサイクル イベントは、注文が終了するとこれらの参照をクリアします。
- 23:59 に期限切れになる保留中の注文は、新しい日が始まるときにキャンセルすることでエミュレートされます。
- トレーリング ストップ ロジックは、MetaTrader の買値/売値の代わりにローソク足の終値を使用します。
- すべてのポイントベースの距離には
Security.PriceStep が乗算されます。 PriceStep が設定されていない場合、生のポイント値は絶対価格距離として扱われます。
使用のヒント
- ポイントから価格への変換と出来高の四捨五入が正確になるように、適切に設定された
PriceStep、StepPrice、および VolumeStep を商品に割り当てます。
- 5 分間の履歴データが利用可能であることを確認します。ブレイクアウトレベルは、最新の 80 のローソク足に依存します。
EndSession1/EndSession2 を調整して、希望する市場セッション(ロンドン前やニューヨーク休憩前など)に合わせます。
- ライブ デプロイの前に、デザイナーまたはランナーを使用して、選択した機器に合わせて
InitialStopLoss、TakeProfit、および TrailingStop を最適化します。
- 複数の戦略が同じポートフォリオを共有する場合は、戦略を StockSharp のリスク管理と組み合わせます。
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>
/// Hans123 breakout strategy. Builds a range from recent highest/lowest prices
/// and enters on breakout above range high or below range low.
/// </summary>
public class Hans123TraderRangeBreakoutStrategy : Strategy
{
private readonly StrategyParam<int> _rangeLength;
private readonly StrategyParam<DataType> _candleType;
private decimal _entryPrice;
private decimal _prevHighest;
private decimal _prevLowest;
public Hans123TraderRangeBreakoutStrategy()
{
_rangeLength = Param(nameof(RangeLength), 20)
.SetDisplay("Range Length", "Number of candles used to compute the breakout range.", "Breakout");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Candle series for range detection.", "General");
}
public int RangeLength
{
get => _rangeLength.Value;
set => _rangeLength.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_prevHighest = 0;
_prevLowest = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_prevHighest = 0;
_prevLowest = 0;
var highest = new Highest { Length = RangeLength };
var lowest = new Lowest { Length = RangeLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(highest, lowest, ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent)
);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, highest);
DrawIndicator(area, lowest);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal highestValue, decimal lowestValue)
{
if (candle.State != CandleStates.Finished)
return;
if (highestValue <= 0 || lowestValue <= 0)
{
_prevHighest = highestValue;
_prevLowest = lowestValue;
return;
}
// Entry on breakout using previous levels
if (Position == 0 && _prevHighest > 0 && _prevLowest > 0)
{
if (candle.ClosePrice > _prevHighest)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
}
else if (candle.ClosePrice < _prevLowest)
{
SellMarket();
_entryPrice = candle.ClosePrice;
}
}
_prevHighest = highestValue;
_prevLowest = lowestValue;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
from StockSharp.Algo.Indicators import Highest, Lowest
class hans123_trader_range_breakout_strategy(Strategy):
def __init__(self):
super(hans123_trader_range_breakout_strategy, self).__init__()
self._range_length = self.Param("RangeLength", 20) \
.SetDisplay("Range Length", "Number of candles used to compute the breakout range", "Breakout")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Candle series for range detection", "General")
self._entry_price = 0.0
self._stop_price = 0.0
self._prev_highest = 0.0
self._prev_lowest = 0.0
@property
def RangeLength(self):
return self._range_length.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(hans123_trader_range_breakout_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._stop_price = 0.0
self._prev_highest = 0.0
self._prev_lowest = 0.0
self._highest = Highest()
self._highest.Length = self.RangeLength
self._lowest = Lowest()
self._lowest.Length = self.RangeLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._highest, self._lowest, self.ProcessCandle).Start()
self.StartProtection(
Unit(2, UnitTypes.Percent),
Unit(1, UnitTypes.Percent)
)
def ProcessCandle(self, candle, highest_value, lowest_value):
if candle.State != CandleStates.Finished:
return
hv = float(highest_value)
lv = float(lowest_value)
if hv <= 0 or lv <= 0:
self._prev_highest = hv
self._prev_lowest = lv
return
# Entry on breakout using previous levels
if self.Position == 0 and self._prev_highest > 0 and self._prev_lowest > 0:
close = float(candle.ClosePrice)
if close > self._prev_highest:
self.BuyMarket()
self._entry_price = close
elif close < self._prev_lowest:
self.SellMarket()
self._entry_price = close
self._prev_highest = hv
self._prev_lowest = lv
def OnReseted(self):
super(hans123_trader_range_breakout_strategy, self).OnReseted()
self._entry_price = 0.0
self._stop_price = 0.0
self._prev_highest = 0.0
self._prev_lowest = 0.0
def CreateClone(self):
return hans123_trader_range_breakout_strategy()