GitHub で見る
Ten Pips EURUSD 戦略
概要
Ten Pips EURUSD 戦略は、オリジナルの MetaTrader Expert Advisor のロジックを再現するブレイクアウトシステムです。最近完了したローソク足を監視し、そのレンジの上下にストップ注文を配置します。注文は pips 単位でサイズ決定され、現在の銘柄のティックサイズに調整され、オプションでトレーリングストップで管理されます。この実装は StockSharp の高レベルローソク足サブスクリプションと注文板更新を組み合わせて、ブローカー中立を保ちながら MQL バージョンに近い動作を維持します。
戦略ロジック
- 選択したローソク足タイプをサブスクライブし、新しいバーがアクティブになるまで待ちます。
- そのバーが終了したとき、前のローソク足の高値と安値を取得します。オリジナル EA はそれらを 1 バーに制限しているため、この時点で保留注文がキャンセルされます。
- 新しいバーの最初のティックで以下を確認します:
- 現在の始値が前のローソク足のレンジ内にある(ギャップフィルタリング)。
- 現在の価格が両方の極値から少なくとも 3 pip 単位離れている(ブローカーのストップレベルのプロキシ)。
- ベスト bid/ask を使って現在のスプレッドを計算します。Level 1 データがない場合、戦略は pip サイズにフォールバックします。
- 2 つの保留ストップ注文を配置します:
- Buy Stop:
前の高値 + 2 × スプレッド で発動し、エントリー価格の StopLossPips 下にストップロス、トレーリングが無効の場合は 前の高値 + 2 × スプレッド + TakeProfitPips にテイクプロフィット。
- Sell Stop:
前の安値 − スプレッド で発動し、対称的な決済レベル。
- ローソク足が完了するか、両注文が約定/キャンセルされるとすぐに、次のバーのプロセスが繰り返されます。
ポジション管理
- ポジションがオープンの間、戦略は各注文板更新でベスト bid/ask を監視します。
- トレーリングが無効の場合、価格が固定ストップまたはテイクプロフィットレベルに触れるとポジションが決済されます。
- トレーリングが有効の場合:
- ロングトレードでは、価格が
TrailingStopPips 進んだ後にトレーリングストップが発動します。ストップは bid − TrailingStopPips に設定され、価格が少なくとも TrailingStepPips 改善するたびに移動します。
- ショートトレードでは、ask 価格を使ってロング側のロジックを反映します。
- 手動決済はすべての保護レベルをリセットし、EA のストラドル動作を再現するために、ローソク足が終了するまで未決済の反対側のストップ注文を維持します。
パラメーター
| 名前 |
デフォルト |
説明 |
Volume |
0.01 |
ロット単位の注文ボリューム(非 FX シンボルの場合は契約単位)。 |
StopLossPips |
50 |
エントリーと保護ストップ間の距離(pips 単位)。 |
TakeProfitPips |
150 |
pips 単位のテイクプロフィット距離。トレーリングが無効の場合のみ使用。 |
UseTrailing |
false |
トレーリングストップロジックを有効にします。 |
TrailingStopPips |
50 |
トレーリングストップの初期距離(pips 単位)。 |
TrailingStepPips |
25 |
アクティブなトレーリングストップを移動するために必要な最小利益(pips 単位)。 |
CandleType |
15 分足 |
ブレイクアウトレベルの検出に使用するローソク足シリーズ。 |
注意事項と推奨
- pip サイズは
Security.PriceStep から自動的に算出され、MQL の桁数調整をエミュレートするため、戦略は 3 桁・5 桁の FX シンボルに対応します。
- すべての距離は注文を配置する前に価格単位に再計算されるため、ティックサイズが定義されている限り、非 FX 資産とも互換性があります。
- 最小ストップレベルのフォールバック(3 pip 単位)は、ブローカーがストップレベルを報告しない場合の元の EA の動作を模倣します。
- 保留注文は各ローソク足の終わりに期限切れになるため、入力ローソク足ストリームにギャップなく、希望の時間軸で戦略を実行する必要があります。
- リスク管理が重要です。実際の資金で取引する前に、現実的なスプレッドと手数料モデルでテストすることを検討してください。
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>
/// Breakout strategy: enters long when price breaks above previous candle high,
/// enters short when price breaks below previous candle low.
/// Uses ATR-based stop loss and take profit.
/// </summary>
public class TenPipsEurusdStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLossMult;
private readonly StrategyParam<decimal> _takeProfitMult;
private readonly StrategyParam<decimal> _trailingMult;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevHigh;
private decimal _prevLow;
private decimal _entryPrice;
private decimal? _stopPrice;
private decimal? _takePrice;
private bool _hasPrev;
public decimal StopLossMult { get => _stopLossMult.Value; set => _stopLossMult.Value = value; }
public decimal TakeProfitMult { get => _takeProfitMult.Value; set => _takeProfitMult.Value = value; }
public decimal TrailingMult { get => _trailingMult.Value; set => _trailingMult.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public TenPipsEurusdStrategy()
{
_stopLossMult = Param(nameof(StopLossMult), 1.0m)
.SetGreaterThanZero()
.SetDisplay("SL Mult", "Stop loss ATR multiplier", "Risk");
_takeProfitMult = Param(nameof(TakeProfitMult), 2.0m)
.SetGreaterThanZero()
.SetDisplay("TP Mult", "Take profit ATR multiplier", "Risk");
_trailingMult = Param(nameof(TrailingMult), 0.8m)
.SetGreaterThanZero()
.SetDisplay("Trail Mult", "Trailing stop ATR multiplier", "Risk");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Period", "ATR calculation length", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
protected override void OnReseted()
{
base.OnReseted();
_prevHigh = 0;
_prevLow = 0;
_entryPrice = 0;
_stopPrice = null;
_takePrice = null;
_hasPrev = false;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal atr)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_hasPrev = true;
return;
}
var close = candle.ClosePrice;
// Manage existing position
if (Position > 0)
{
// Trail
var trail = close - TrailingMult * atr;
if (_stopPrice == null || trail > _stopPrice)
_stopPrice = trail;
if (close <= _stopPrice || (_takePrice != null && close >= _takePrice))
{
SellMarket(Math.Abs(Position));
_stopPrice = null;
_takePrice = null;
_entryPrice = 0;
}
}
else if (Position < 0)
{
var trail = close + TrailingMult * atr;
if (_stopPrice == null || trail < _stopPrice)
_stopPrice = trail;
if (close >= _stopPrice || (_takePrice != null && close <= _takePrice))
{
BuyMarket(Math.Abs(Position));
_stopPrice = null;
_takePrice = null;
_entryPrice = 0;
}
}
// Entry on breakout
if (_hasPrev && Position == 0)
{
if (close > _prevHigh + atr * 0.5m)
{
BuyMarket();
_entryPrice = close;
_stopPrice = close - StopLossMult * atr;
_takePrice = close + TakeProfitMult * atr;
}
else if (close < _prevLow - atr * 0.5m)
{
SellMarket();
_entryPrice = close;
_stopPrice = close + StopLossMult * atr;
_takePrice = close - TakeProfitMult * atr;
}
}
_prevHigh = candle.HighPrice;
_prevLow = candle.LowPrice;
_hasPrev = true;
}
}
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
from StockSharp.Algo.Indicators import AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class ten_pips_eurusd_strategy(Strategy):
def __init__(self):
super(ten_pips_eurusd_strategy, self).__init__()
self._sl_mult = self.Param("StopLossMult", 1.0)
self._tp_mult = self.Param("TakeProfitMult", 2.0)
self._trail_mult = self.Param("TrailingMult", 0.8)
self._atr_period = self.Param("AtrPeriod", 14)
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1)))
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(ten_pips_eurusd_strategy, self).OnReseted()
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
self._has_prev = False
def OnStarted2(self, time):
super(ten_pips_eurusd_strategy, self).OnStarted2(time)
self._prev_high = 0.0
self._prev_low = 0.0
self._entry_price = 0.0
self._stop_price = None
self._take_price = None
self._has_prev = False
atr = AverageTrueRange()
atr.Length = self._atr_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(atr, self.OnProcess).Start()
def OnProcess(self, candle, atr_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._has_prev = True
return
atr_v = float(atr_val)
close = float(candle.ClosePrice)
pos = float(self.Position)
if pos > 0:
trail = close - float(self._trail_mult.Value) * atr_v
if self._stop_price is None or trail > self._stop_price:
self._stop_price = trail
if close <= self._stop_price or (self._take_price is not None and close >= self._take_price):
self.SellMarket(abs(pos))
self._stop_price = None
self._take_price = None
self._entry_price = 0.0
elif pos < 0:
trail = close + float(self._trail_mult.Value) * atr_v
if self._stop_price is None or trail < self._stop_price:
self._stop_price = trail
if close >= self._stop_price or (self._take_price is not None and close <= self._take_price):
self.BuyMarket(abs(pos))
self._stop_price = None
self._take_price = None
self._entry_price = 0.0
pos = float(self.Position)
if self._has_prev and pos == 0:
if close > self._prev_high + atr_v * 0.5:
self.BuyMarket()
self._entry_price = close
self._stop_price = close - float(self._sl_mult.Value) * atr_v
self._take_price = close + float(self._tp_mult.Value) * atr_v
elif close < self._prev_low - atr_v * 0.5:
self.SellMarket()
self._entry_price = close
self._stop_price = close + float(self._sl_mult.Value) * atr_v
self._take_price = close - float(self._tp_mult.Value) * atr_v
self._prev_high = float(candle.HighPrice)
self._prev_low = float(candle.LowPrice)
self._has_prev = True
def CreateClone(self):
return ten_pips_eurusd_strategy()