GitHub で見る
ComFracti フラクタル RSI 戦略
概要
ComFracti Fractal RSI 戦略は、MetaTrader の専門家 ComFracti の StockSharp 移植です。このアルゴリズムは、2 つのタイムフレームでビル Williams フラクタルを使用して方向性のバイアスを検索し、毎日のローソク足で計算された高速な RSI でシグナルをフィルター処理します。有効な設定が表示されると、この戦略は単一のポジションをオープンし、設定可能なストップロスおよびテイクプロフィットディスタンスでポジションを保護し、シグナルが反転したときまたは保持時間制限に達したときにオプションで決済することもできます。
デフォルトの設定では、元のエキスパートと同様に、ローソク足の始値を使用して、1 時間の確認時間枠と 3 期間の毎日の RSI の長さを持つ 15 分の取引時間枠を複製します。
取引ロジック
- フラクタル バイアス検出
- 取引時間枠とより高い時間枠からの完成したローソク足は、5 バーのフラクタル ウィンドウを通じて処理されます。
Primary*Shift パラメータと Higher*Shift パラメータは、戦略が最新の確認済みフラクタルを検査するバー数を定義します。デフォルトは、3 の元の値と一致します。これは、コードが 3 キャンドル前に確認されたフラクタルを評価することを意味します。
- 上昇フラクタルを伴わない下降フラクタル (スイングが低く) は強気 (+1) として扱われます。ダウン フラクタルのないアップ フラクタルは弱気 (-1) として扱われます。
- 毎日の RSI フィルター
- 構成可能な
RsiPeriod (デフォルトは 3) を持つ RelativeStrengthIndex は、毎日のタイムフレームで実行され、MetaTrader の実装と一致するローソク足の始値を使用します。
- セットアップに時間がかかる場合は、RSI を
50 - RsiBuyOffset 未満にする必要があります。短いセットアップでは、RSI が 50 + RsiSellOffset より大きい必要があります。
- エントリー条件
- 購入: どちらのフラクタル トラッカーも +1 をレポートし、RSI フィルターは強気です。この戦略は、フラットまたはショートの場合にロングポジションをオープンし、ロングサイドに反転するのに十分なボリュームを送信します。
- 売り: どちらのフラクタル トラッカーも -1 をレポートし、RSI フィルターは弱気です。この戦略は、フラットまたはロングの場合にショート ポジションをオープンし、ショート サイドに反転するのに十分なボリュームを送信します。
- ポジション管理
- 保護的なストップロスとテイクプロフィットのレベルは、ポジションが変更された直後に、
StopLossPips と TakeProfitPips に商品のピップサイズを掛けたものに基づいて計算されます。
- 価格がストップまたはターゲットに達したとき、
ExpiryMinutes が経過したとき、または CloseOnOppositeSignal が有効になってシグナルが反転したとき、ポジションを閉じることができます。
パラメーター
| 名前 |
説明 |
デフォルト |
Volume |
各エントリーに使用される注文量。 |
0.1 |
TakeProfitPips |
利益目標距離 (pips)。無効にするには、0 に設定します。 |
700 |
StopLossPips |
ピップス単位のストップロス距離。無効にするには、0 に設定します。 |
2500 |
ExpiryMinutes |
強制終了するまでの最大保持時間 (分単位)。 0 はタイマーを無効にします。 |
5555 |
CloseOnOppositeSignal |
信号が反対方向に反転すると、アクティブなポジションを閉じます。 |
false |
PrimaryBuyShift |
バーバックして取引時間枠の強気のフラクタルを検査します。 |
3 |
HigherBuyShift |
バーバックして、より高い時間枠での強気のフラクタルを検査します。 |
3 |
PrimarySellShift |
バーバックして取引時間枠の弱気フラクタルを検査します。 |
3 |
HigherSellShift |
バーバックして、より高い時間枠で弱気のフラクタルを検査します。 |
3 |
RsiBuyOffset |
長いセットアップには 50 未満のオフセットが必要です。 |
3 |
RsiSellOffset |
短いセットアップには 50 を超えるオフセットが必要です。 |
3 |
RsiPeriod |
毎日の時間枠での長さは RSI です。 |
3 |
CandleType |
取引時間枠のローソク足タイプ。 |
15分キャンドル |
HigherTimeFrame |
確認タイムフレームキャンドルタイプ。 |
1時間キャンドル |
DailyTimeFrame |
毎日の RSI に使用されるキャンドルの種類。 |
1dayキャンドル |
実装メモ
- この戦略は、高レベルのローソク足サブスクリプション API (
SubscribeCandles().Bind(...)) を使用し、ガイドラインで要求されているように、Strategy.Indicators を介してインジケーターを公開することなく、内部でインジケーターを管理します。
- Fractals は、ローリング 5 キャンドル ウィンドウを保存し、フラクタルが確認された後にのみ信号を更新する内部ヘルパーを介して計算されます。
- RSI の値は、MetaTrader
PRICE_OPEN モードと一致するローソク足の始値を使用して RelativeStrengthIndex.Process(...) を介して取得されます。
- 一度に維持されるポジションは 1 つだけです。成行注文は、既存のエクスポージャーをカバーするために必要な量を追加することで、必要に応じてポジションを反転します。
- Pip サイズは、小数点以下 3 桁以上で引用されたアセットに 10 倍の乗数を使用して、
Security.PriceStep と Security.Decimals から推定され、MetaTrader Point から pip への変換が再現されます。
使用のヒント
- フラクタル シフトは、要求されたローソク足インデックスが存在することを保証するのに十分な大きさでなければなりません。シフトが 3 の場合、トラッカーはシグナルを生成する前に、タイムフレームごとに少なくとも 5 つの完成したローソク足を必要とします。
- 異なるティックサイズの商品(指数や株式など)を取引する場合は、商品のピップ定義と一致するように
TakeProfitPips と StopLossPips を調整します。
CloseOnOppositeSignal を無効にすると、元のエキスパート アドバイザの動作 (デフォルトで無効になっています) が複製され、停止、ターゲット、または終了の有効期限タイマーのみに依存します。
- この戦略では、マーチンゲール法やリスクベースのサイジングは実装されていません。 MetaTrader のロット計算は、StockSharp では利用できない口座証拠金関数に依存していました。動的なポジションサイジングが必要な場合は、
Volume パラメータを使用するか、ポートフォリオ マネージャーで戦略をラップします。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// ComFracti Fractal RSI: Fractal breakout with RSI filter and ATR stops.
/// </summary>
public class ComFractiFractalRsiStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _atrLength;
private decimal _entryPrice;
private decimal _prevHigh5;
private decimal _prevLow5;
private decimal _high1, _high2, _high3, _high4, _high5;
private decimal _low1, _low2, _low3, _low4, _low5;
private int _barCount;
public ComFractiFractalRsiStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(8).TimeFrame())
.SetDisplay("Candle Type", "Timeframe.", "General");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "RSI period.", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetDisplay("ATR Length", "ATR period.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_entryPrice = 0;
_barCount = 0;
_prevHigh5 = 0;
_prevLow5 = 0;
_high1 = _high2 = _high3 = _high4 = _high5 = 0;
_low1 = _low2 = _low3 = _low4 = _low5 = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_entryPrice = 0;
_barCount = 0;
_prevHigh5 = 0;
_prevLow5 = 0;
_high1 = _high2 = _high3 = _high4 = _high5 = 0;
_low1 = _low2 = _low3 = _low4 = _low5 = 0;
var rsi = new RelativeStrengthIndex { Length = RsiLength };
var atr = new AverageTrueRange { Length = AtrLength };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(rsi, atr, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal rsiVal, decimal atrVal)
{
if (candle.State != CandleStates.Finished)
return;
// Shift fractal window
_high5 = _high4; _high4 = _high3; _high3 = _high2; _high2 = _high1;
_high1 = candle.HighPrice;
_low5 = _low4; _low4 = _low3; _low3 = _low2; _low2 = _low1;
_low1 = candle.LowPrice;
_barCount++;
if (_barCount < 5 || atrVal <= 0)
return;
var close = candle.ClosePrice;
// Detect fractal high (center bar _high3 is highest)
var fractalUp = _high3 > _high1 && _high3 > _high2 && _high3 > _high4 && _high3 > _high5;
// Detect fractal low (center bar _low3 is lowest)
var fractalDown = _low3 < _low1 && _low3 < _low2 && _low3 < _low4 && _low3 < _low5;
if (Position > 0)
{
if (close >= _entryPrice + atrVal * 3m || close <= _entryPrice - atrVal * 2m || fractalDown)
{
SellMarket();
_entryPrice = 0;
}
}
else if (Position < 0)
{
if (close <= _entryPrice - atrVal * 3m || close >= _entryPrice + atrVal * 2m || fractalUp)
{
BuyMarket();
_entryPrice = 0;
}
}
if (Position == 0)
{
if (fractalDown && rsiVal < 45)
{
_entryPrice = close;
BuyMarket();
}
else if (fractalUp && rsiVal > 55)
{
_entryPrice = close;
SellMarket();
}
}
_prevHigh5 = _high5;
_prevLow5 = _low5;
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import RelativeStrengthIndex, AverageTrueRange
class com_fracti_fractal_rsi_strategy(Strategy):
def __init__(self):
super(com_fracti_fractal_rsi_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(8))) \
.SetDisplay("Candle Type", "Timeframe.", "General")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period.", "Indicators")
self._atr_length = self.Param("AtrLength", 14) \
.SetDisplay("ATR Length", "ATR period.", "Indicators")
self._entry_price = 0.0
self._prev_high5 = 0.0
self._prev_low5 = 0.0
self._high1 = 0.0
self._high2 = 0.0
self._high3 = 0.0
self._high4 = 0.0
self._high5 = 0.0
self._low1 = 0.0
self._low2 = 0.0
self._low3 = 0.0
self._low4 = 0.0
self._low5 = 0.0
self._bar_count = 0
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiLength(self):
return self._rsi_length.Value
@property
def AtrLength(self):
return self._atr_length.Value
def OnStarted2(self, time):
super(com_fracti_fractal_rsi_strategy, self).OnStarted2(time)
self._entry_price = 0.0
self._bar_count = 0
self._prev_high5 = 0.0
self._prev_low5 = 0.0
self._high1 = self._high2 = self._high3 = self._high4 = self._high5 = 0.0
self._low1 = self._low2 = self._low3 = self._low4 = self._low5 = 0.0
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiLength
self._atr = AverageTrueRange()
self._atr.Length = self.AtrLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._rsi, self._atr, self.ProcessCandle).Start()
def ProcessCandle(self, candle, rsi_val, atr_val):
if candle.State != CandleStates.Finished:
return
rv = float(rsi_val)
av = float(atr_val)
# Shift fractal window
self._high5 = self._high4
self._high4 = self._high3
self._high3 = self._high2
self._high2 = self._high1
self._high1 = float(candle.HighPrice)
self._low5 = self._low4
self._low4 = self._low3
self._low3 = self._low2
self._low2 = self._low1
self._low1 = float(candle.LowPrice)
self._bar_count += 1
if self._bar_count < 5 or av <= 0:
return
close = float(candle.ClosePrice)
# Detect fractal high (center bar _high3 is highest)
fractal_up = (self._high3 > self._high1 and self._high3 > self._high2 and
self._high3 > self._high4 and self._high3 > self._high5)
# Detect fractal low (center bar _low3 is lowest)
fractal_down = (self._low3 < self._low1 and self._low3 < self._low2 and
self._low3 < self._low4 and self._low3 < self._low5)
if self.Position > 0:
if close >= self._entry_price + av * 3.0 or close <= self._entry_price - av * 2.0 or fractal_down:
self.SellMarket()
self._entry_price = 0.0
elif self.Position < 0:
if close <= self._entry_price - av * 3.0 or close >= self._entry_price + av * 2.0 or fractal_up:
self.BuyMarket()
self._entry_price = 0.0
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_high5 = self._high5
self._prev_low5 = self._low5
return
if self.Position == 0:
if fractal_down and rv < 45:
self._entry_price = close
self.BuyMarket()
elif fractal_up and rv > 55:
self._entry_price = close
self.SellMarket()
self._prev_high5 = self._high5
self._prev_low5 = self._low5
def OnReseted(self):
super(com_fracti_fractal_rsi_strategy, self).OnReseted()
self._entry_price = 0.0
self._bar_count = 0
self._prev_high5 = 0.0
self._prev_low5 = 0.0
self._high1 = self._high2 = self._high3 = self._high4 = self._high5 = 0.0
self._low1 = self._low2 = self._low3 = self._low4 = self._low5 = 0.0
def CreateClone(self):
return com_fracti_fractal_rsi_strategy()