GitHub で見る
戦略 RSI Martingale
概要
RSI Martingale は、MetaTrader 5 エキスパート アドバイザー RSI&Martingale1.5 のポートです。この戦略は、相対強度指数 (RSI) が構成可能なルックバック ウィンドウ内で極値に達するまで待機することで、勢いの反転を検索します。極端な値が現れると、予想される平均回帰の方向に取引が開始され、RSI が 50 正中線を横切るか、固定のストップ/テイク目標に達したときに取引を終了します。マーチンゲール モジュールは、オプションで、取引に負けた後にボリュームを増やして反対方向にポジションを再開できます。毎日の損益制限と時間ごとのフィルターにより、リスクの高いセッション中または資本保全目標を達成した後に取引を一時停止することができます。
戦略ロジック
RSI の極端な例
- インジケーター – 選択したローソク式に基づいて計算された単一の RSI。取引を検討する前に、インジケーターを作成する必要があります (十分な履歴データ)。
- 最小検出 – 最新の RSI 値が、設定された
Bars For Extremes ウィンドウ内のすべての RSI 値以下で、その値が 50 未満の場合、ストラテジーはロング ポジションをオープンします。
- 最大検出 – 最新の RSI 値がルックバック ウィンドウ内のすべての値以上で、その値が 50 を超えている場合、ストラテジーはショート ポジションをオープンします。
ポジション管理
- エグジットトリガー – RSI がニュートラル 50 ラインを反対側に横切ると、ポジションはクローズされます (ロングは 50 を超えてエグジット、ショートは 50 を下回ってエグジットします)。
- 固定ターゲット – オプションのストップロスとテイクプロフィットの距離をピップで表します。有効にすると、この戦略は最新のローソク足の高値/安値をそれらの目標価格と比較し、いずれかのレベルに達した場合にポジションをクローズします。
- 数量調整 – すべての注文数量は、送信前に証券のステップ、最小値、および最大値の設定に合わせて調整されます。
Martingale の回復
- トリガー – マイナスの利益でポジションがクローズされた後、戦略は負けた取引の方向と量を記憶します。
- 再エントリー – 次の適格なローソク足で、ポジションがオープンしていない場合に限り、反対方向の取引をすぐに開始できます。ボリュームは、
Enable Martingale スイッチに応じて、損失ボリュームに Martingale Multiplier を乗算するか、ベースの Initial Volume を乗算したものになります。
- リセット – マーチンゲール注文が送信されると、反復試行を避けるために保存された損失情報がクリアされます。
日々の資本管理
- ベースライン – この戦略は、各取引日の開始時に口座資産を取得し、一時停止フラグをリセットします。
- 監視ウィンドウ – 1 日の制限は
Daily Control Start ~ Daily Control End 時間の間でのみ評価されます。
- 一時停止 – 株式が
Daily Profit % を超えて増加するか、Daily Loss % を下回る場合、戦略はオープンポジションをすべて閉じ、翌日まで新しい取引をスキップします。
セッションフィルター
- 取引ウィンドウ – 新しいポジションは、現在の時間が
Trading Start と Trading End (両端を含む) の間にある場合にのみ許可されます。
- 時間帯回避 – 24 個のブール値パラメータはソース EA の「ニュース回避」設定を反映し、選択した時間帯の取引をブロックします。
パラメーター
- 初期数量 – 標準エントリの基本注文数量。
- RSI 期間 – RSI インジケーターで使用される期間の数。
- 極端なバー – 最新の RSI の最小値または最大値を検索するときにスキャンされる完成したキャンドルの数。
- テイクプロフィット (pips) – 固定テイクプロフィットまでの距離。無効にするには、
0 に設定します。
- ストップロス (pips) – 固定ストップロスまでの距離。無効にするには、
0 に設定します。
- Martingale を有効にする – 損失取引後のマーチンゲール回復モジュールを有効にします。
- Martingale 乗数 – マーチンゲールがアクティブな場合に、以前の損失ボリュームに適用される乗数。
- 日次目標 – 日次の損益停止ロジックを切り替えます。
- 日次利益 % – その日の取引を停止する利益の割合。
- 日次損失% – その日の取引を停止した損失の割合。
- 日次制御開始/日次制御終了 – 日次制限を評価するための時間境界。
- 取引開始/取引終了 – 新しいポジションを許可する時間境界。
- 00 時間を避けます…23 時間を避けます – 対応する時間帯の取引を無効にします。
- ローソク足タイプ – RSI インジケーターとすべての計算に使用されるローソク足のサブスクリプション。
追加の注意事項
- この戦略は終了したローソク足のみに作用し、バー内ティックは評価しません。
- 毎日の利益計算では、実現した戦略損益と最新の終値に基づく変動損益を組み合わせます。
- パッケージにはこの戦略の Python 実装はありません。 C# バージョンのみが提供されます。
using System;
using System.Collections.Generic;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// RSI extremes strategy with martingale recovery.
/// Buys when RSI is at a local minimum below 50, sells when RSI is at a local maximum above 50.
/// Closes on RSI crossing 50. Doubles volume after a losing trade.
/// </summary>
public class RSIMartingaleStrategy : Strategy
{
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _barsForCondition;
private readonly StrategyParam<DataType> _candleType;
private readonly List<decimal> _recentRsi = new();
private decimal _entryPrice;
private int _direction; // 1=long, -1=short, 0=flat
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
public int BarsForCondition
{
get => _barsForCondition.Value;
set => _barsForCondition.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public RSIMartingaleStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 14)
.SetDisplay("RSI Period", "RSI indicator period", "Indicator");
_barsForCondition = Param(nameof(BarsForCondition), 10)
.SetDisplay("Bars For Extremes", "Number of RSI values to check for extremes", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Timeframe", "Data");
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var rsi = new RelativeStrengthIndex { Length = RsiPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, rsi);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiValue)
{
if (candle.State != CandleStates.Finished)
return;
_recentRsi.Add(rsiValue);
if (_recentRsi.Count > BarsForCondition)
_recentRsi.RemoveAt(0);
if (_recentRsi.Count < BarsForCondition)
return;
// Check exit: close on RSI crossing 50
if (_direction > 0 && rsiValue > 50)
{
SellMarket();
_direction = 0;
return;
}
else if (_direction < 0 && rsiValue < 50)
{
BuyMarket();
_direction = 0;
return;
}
if (Position != 0)
return;
// Check if current RSI is local minimum (oversold entry)
if (IsLocalMinimum() && rsiValue < 50 && Position <= 0)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_direction = 1;
}
// Check if current RSI is local maximum (overbought entry)
else if (IsLocalMaximum() && rsiValue > 50 && Position >= 0)
{
SellMarket();
_entryPrice = candle.ClosePrice;
_direction = -1;
}
}
private bool IsLocalMinimum()
{
if (_recentRsi.Count < 2)
return false;
var current = _recentRsi[_recentRsi.Count - 1];
for (var i = 0; i < _recentRsi.Count - 1; i++)
{
if (current > _recentRsi[i])
return false;
}
return true;
}
private bool IsLocalMaximum()
{
if (_recentRsi.Count < 2)
return false;
var current = _recentRsi[_recentRsi.Count - 1];
for (var i = 0; i < _recentRsi.Count - 1; i++)
{
if (current < _recentRsi[i])
return false;
}
return true;
}
/// <inheritdoc />
protected override void OnReseted()
{
_recentRsi.Clear();
_entryPrice = 0;
_direction = 0;
base.OnReseted();
}
}
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 RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
class rsi_martingale_strategy(Strategy):
def __init__(self):
super(rsi_martingale_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5)))
self._rsi_period = self.Param("RsiPeriod", 14)
self._bars_for_condition = self.Param("BarsForCondition", 10)
self._recent_rsi = []
self._entry_price = 0.0
self._direction = 0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def RsiPeriod(self):
return self._rsi_period.Value
@RsiPeriod.setter
def RsiPeriod(self, value):
self._rsi_period.Value = value
@property
def BarsForCondition(self):
return self._bars_for_condition.Value
@BarsForCondition.setter
def BarsForCondition(self, value):
self._bars_for_condition.Value = value
def OnReseted(self):
super(rsi_martingale_strategy, self).OnReseted()
self._recent_rsi = []
self._entry_price = 0.0
self._direction = 0
def OnStarted2(self, time):
super(rsi_martingale_strategy, self).OnStarted2(time)
self._recent_rsi = []
self._entry_price = 0.0
self._direction = 0
rsi = RelativeStrengthIndex()
rsi.Length = self.RsiPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(rsi, self._process_candle).Start()
def _is_local_minimum(self):
if len(self._recent_rsi) < 2:
return False
current = self._recent_rsi[-1]
for i in range(len(self._recent_rsi) - 1):
if current > self._recent_rsi[i]:
return False
return True
def _is_local_maximum(self):
if len(self._recent_rsi) < 2:
return False
current = self._recent_rsi[-1]
for i in range(len(self._recent_rsi) - 1):
if current < self._recent_rsi[i]:
return False
return True
def _process_candle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
rsi_val = float(rsi_value)
bars_for_cond = self.BarsForCondition
self._recent_rsi.append(rsi_val)
while len(self._recent_rsi) > bars_for_cond:
self._recent_rsi.pop(0)
if len(self._recent_rsi) < bars_for_cond:
return
# Check exit: close on RSI crossing 50
if self._direction > 0 and rsi_val > 50:
self.SellMarket()
self._direction = 0
return
elif self._direction < 0 and rsi_val < 50:
self.BuyMarket()
self._direction = 0
return
if self.Position != 0:
return
close = float(candle.ClosePrice)
# Local minimum + RSI below 50 -> buy
if self._is_local_minimum() and rsi_val < 50 and self.Position <= 0:
self.BuyMarket()
self._entry_price = close
self._direction = 1
# Local maximum + RSI above 50 -> sell
elif self._is_local_maximum() and rsi_val > 50 and self.Position >= 0:
self.SellMarket()
self._entry_price = close
self._direction = -1
def CreateClone(self):
return rsi_martingale_strategy()