マルチロットスキャルパー戦略
概要
Multi Lot Scalper Strategy は、古典的な MetaTrader エキスパート アドバイザー「Multi Lot Scalper」から変換されたマーチンゲール スタイルの平均化システムです。元のアルゴリズムは主要なFXペア向けに設計されており、MACD ヒストグラムの傾きに基づいて、市場が強気局面に入っているか弱気局面に入っているかを判断していました。方向が特定されると、この戦略は成行注文のはしごを開き、逆方向に動くたびに量を徐々に増やします。 StockSharp ポートは、元のエントリー ロジック、資金管理ルール、保護メカニズムを維持しながら、高レベルのキャンドル サブスクリプション API を活用します。
この戦略は、スプレッドが狭く、ピップの定義が安定している流動的な商品で最も効果的です。デフォルトでは、15 分のローソク足をサブスクライブしますが、商品と互換性のある他の時間枠は、CandleType パラメーターを通じて指定できます。
取引ロジック
- シグナル検出 – MACD インジケーター (
MacdFastLength、MacdSlowLength、MacdSignalLength) は、完成したすべてのキャンドルで評価されます。 MACD メインラインが前の値と比較して上昇すると、戦略はロングの機会を探しますが、そうでない場合はショートの準備をします。 ReverseSignals パラメータは、逆張りエントリーを好むユーザー向けにこの解釈を反転します。
- 最初のエントリー – 日付/時刻フィルター (
StartYear、StartMonth、EndYear、EndMonth、EndHour、EndMinute) が取引を許可している限り、有効なシグナルの直後に新しいシーケンスの最初のポジションがオープンされます。 MetaTrader の実装を反映して、成行注文が使用されます。
- ピラミッド – 価格が最新の約定に対して少なくとも
EntryDistancePips だけ変動した場合にのみ、後続の注文がトリガーされます。追加の取引ごとに、基本ボリュームが 2 倍または 1.5 倍(MaxTrades が 12 を超える場合)、EA のマーチンゲール サイズが再現されます。
- ストップとターゲット –
InitialStopPips と TakeProfitPips はバスケット全体の価格レベルに変換されます。トレーリングストップは、有利な動きが EntryDistancePips + TrailingStopPips を超えた後に有効になり、市場が加速するにつれて出口が狭まります。
- アカウント保護 – バスケットがその容量 (
MaxTrades - OrdersToProtect) に近づき、変動利益が SecureProfit に達すると、UseAccountProtection が有効になっている場合、戦略は最新の取引を終了し、新規エントリーを一時的にブロックします。
資金管理
元のエキスパートアドバイザーはオプションで、口座残高の関数として基本ロットサイズを再計算しました。 StockSharp ポートは、UseMoneyManagement、RiskPercent、および IsStandardAccount パラメータを通じてこの動作を維持します。この機能が有効な場合、基本ロット (LotSize) は無視され、代わりにポートフォリオ値から導出され、MQL コードと同様にミニ口座または標準口座向けにスケーリングされます。
パラメーター
| パラメータ |
説明 |
デフォルト |
TakeProfitPips |
各エントリーに適用されるテイクプロフィットディスタンス(pips 単位で表現)。 |
40 |
LotSize |
資金管理が無効な場合に使用される基本ロット サイズ。 |
0.1 |
InitialStopPips |
初期のストップロス距離 (pips)。 |
0 |
TrailingStopPips |
しきい値を超えた後にアクティブになるトレーリング ストップの距離。 |
20 |
MaxTrades |
同時に許可されるマーチンゲール エントリの最大数。 |
10 |
EntryDistancePips |
新しい注文を追加する前の逆方向の動きを最小限に抑えます。 |
15 |
SecureProfit |
アカウント保護をトリガーするには変動利益 (通貨) が必要です。 |
10 |
UseAccountProtection |
安全な利益のしきい値に達したときに最後の取引を終了できるようにします。 |
true |
OrdersToProtect |
確実な利益ルールの影響を受ける最終取引の数。 |
3 |
ReverseSignals |
MACD の解釈を反転します (強気はショート、弱気はロングになります)。 |
false |
UseMoneyManagement |
口座残高ベースのロット計算が可能になります。 |
false |
RiskPercent |
資金管理がアクティブな場合に使用されるリスクの割合。 |
12 |
IsStandardAccount |
ミニロット スケーリングの代わりに標準ロット スケーリングを使用します。 |
false |
EurUsdPipValue |
EURUSD の PIP 値の上書き。 |
10 |
GbpUsdPipValue |
GBPUSD の PIP 値のオーバーライド。 |
10 |
UsdChfPipValue |
USDCHF の Pip 値のオーバーライド。 |
10 |
UsdJpyPipValue |
USDJPY の Pip 値の上書き。 |
9.715 |
DefaultPipValue |
他のインストゥルメントに使用されるフォールバック pip 値。 |
5 |
StartYear |
新しいポジションが募集される最初の暦年。 |
2005 |
StartMonth |
最初の 1 か月間は新規エントリーが許可されます。 |
1 |
EndYear |
取引を開始した最後の暦年。 |
2006 |
EndMonth |
取引を開始する最後の暦月。 |
12 |
EndHour |
新しいエントリがブロックされるまでの時間 (24 時間)。 |
22 |
EndMinute |
毎日のカットオフ時間の分部分。 |
30 |
CandleType |
信号生成に使用されるキャンドル タイプ (デフォルトは 15 分)。 |
15-minute time frame |
MacdFastLength |
MACD インジケーターの高速な EMA 長さ。 |
14 |
MacdSlowLength |
MACD インジケーターの EMA の長さが遅いです。 |
26 |
MacdSignalLength |
MACD インジケーターの EMA の長さを信号します。 |
9 |
使用ガイドライン
- 機器の pip ステップが pip 値の設定と一致していることを確認してください。 CFD、金属、または暗号資産に戦略を適用する場合は、pip 値パラメーターを更新します。
- マーチンゲール法では、露出が急速に増加する可能性があります。より大きなバスケットを試す前に、控えめな
MaxTrades、EntryDistancePips、および TrailingStopPips の値から始めてください。
- 取引される商品の MACD 設定とローソク足間隔を最適化します。通常、チャートが遅いと平均化ステップの数が減りますが、チャートが速いとアクティビティが増加します。
- アカウント保護ルールは、突然の反転が起こりやすい市場では特に重要です。確保された利益が頻繁に達成されない場合は、
SecureProfit を減らすか、TrailingStopPips を引き締めることを検討してください。
- 取引ウィンドウ フィルターを使用すると、選択した日中時間の後に戦略を無効にすることができます。これは、ニュースリリースやセッション後半のボラティリティを回避するのに役立ちます。
変換メモ
- StockSharp バージョンは、手動ティック処理の代わりに高レベルのローソク足サブスクリプション API (
SubscribeCandles().BindEx(...)) を使用し、インジケーター管理の透明性を保ちます。
- トレーリングストップは、各子注文を個別に変更するのではなく、バスケットの集計ストップレベルを管理することによって内部的に処理され、ポートフォリオを認識した環境で意図された動作を反映します。
- EA による位置サイジングのための
AccountBalance の使用は、Portfolio.CurrentValue プロパティにマッピングされ、MetaTrader と StockSharp の実装間の同等性が維持されます。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Multi Lot Scalper: MACD slope scalping with EMA filter and ATR stops.
/// </summary>
public class MultiLotScalperStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _fastEmaLength;
private readonly StrategyParam<int> _slowEmaLength;
private readonly StrategyParam<int> _emaFilterLength;
private readonly StrategyParam<int> _atrLength;
private decimal _prevMacd;
private decimal _entryPrice;
public MultiLotScalperStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_fastEmaLength = Param(nameof(FastEmaLength), 12)
.SetDisplay("Fast EMA", "Fast EMA period.", "Indicators");
_slowEmaLength = Param(nameof(SlowEmaLength), 26)
.SetDisplay("Slow EMA", "Slow EMA period.", "Indicators");
_emaFilterLength = Param(nameof(EmaFilterLength), 50)
.SetDisplay("EMA Filter", "Trend filter.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int FastEmaLength
{
get => _fastEmaLength.Value;
set => _fastEmaLength.Value = value;
}
public int SlowEmaLength
{
get => _slowEmaLength.Value;
set => _slowEmaLength.Value = value;
}
public int EmaFilterLength
{
get => _emaFilterLength.Value;
set => _emaFilterLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMacd = 0;
_entryPrice = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevMacd = 0;
_entryPrice = 0;
var fastEma = new ExponentialMovingAverage { Length = FastEmaLength };
var slowEma = new ExponentialMovingAverage { Length = SlowEmaLength };
var emaFilter = new ExponentialMovingAverage { Length = EmaFilterLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fastEma, slowEma, emaFilter, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, emaFilter);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fastVal, decimal slowVal, decimal emaVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
var macd = fastVal - slowVal;
if (_prevMacd == 0 || atrVal <= 0)
{
_prevMacd = macd;
return;
}
var close = candle.ClosePrice;
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 2m || close <= _entryPrice - atrVal * 1.5m || macd < _prevMacd && macd < 0)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 2m || close >= _entryPrice + atrVal * 1.5m || macd > _prevMacd && macd > 0)
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (macd > 0 && _prevMacd <= 0 && close > emaVal)
{
_entryPrice = close;
BuyMarket();
}
else if (macd < 0 && _prevMacd >= 0 && close < emaVal)
{
_entryPrice = close;
SellMarket();
}
}
_prevMacd = macd;
}
}
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 multi_lot_scalper_strategy(Strategy):
"""
Multi Lot Scalper: MACD slope scalping with EMA filter and ATR stops.
"""
def __init__(self):
super(multi_lot_scalper_strategy, self).__init__()
self._fast_ema = self.Param("FastEmaLength", 12).SetDisplay("Fast EMA", "Fast EMA", "Indicators")
self._slow_ema = self.Param("SlowEmaLength", 26).SetDisplay("Slow EMA", "Slow EMA", "Indicators")
self._ema_filter = self.Param("EmaFilterLength", 50).SetDisplay("EMA Filter", "Trend filter", "Indicators")
self._atr_length = self.Param("AtrLength", 14).SetDisplay("ATR", "ATR period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candles", "General")
self._prev_macd = 0.0
self._entry_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(multi_lot_scalper_strategy, self).OnReseted()
self._prev_macd = 0.0
self._entry_price = 0.0
def OnStarted2(self, time):
super(multi_lot_scalper_strategy, self).OnStarted2(time)
fast = ExponentialMovingAverage()
fast.Length = self._fast_ema.Value
slow = ExponentialMovingAverage()
slow.Length = self._slow_ema.Value
ema_filter = ExponentialMovingAverage()
ema_filter.Length = self._ema_filter.Value
atr = AverageTrueRange()
atr.Length = self._atr_length.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, ema_filter, atr, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema_filter)
self.DrawOwnTrades(area)
def _process_candle(self, candle, fast_val, slow_val, ema_val, atr_val):
if candle.State != CandleStates.Finished:
return
fast = float(fast_val)
slow = float(slow_val)
ema = float(ema_val)
atr = float(atr_val)
macd = fast - slow
close = float(candle.ClosePrice)
if self._prev_macd == 0 or atr <= 0:
self._prev_macd = macd
return
if self.Position > 0:
if close >= self._entry_price + atr * 2.0 or close <= self._entry_price - atr * 1.5 or (macd < self._prev_macd and macd < 0):
self.SellMarket()
self._entry_price = 0
elif self.Position < 0:
if close <= self._entry_price - atr * 2.0 or close >= self._entry_price + atr * 1.5 or (macd > self._prev_macd and macd > 0):
self.BuyMarket()
self._entry_price = 0
if self.Position == 0:
if macd > 0 and self._prev_macd <= 0 and close > ema:
self._entry_price = close
self.BuyMarket()
elif macd < 0 and self._prev_macd >= 0 and close < ema:
self._entry_price = close
self.SellMarket()
self._prev_macd = macd
def CreateClone(self):
return multi_lot_scalper_strategy()