GitHub で見る
戦略 IFS Fractals
概要
IFS Fractals は、MetaTrader 5 スクリプト IFS_Fractals のポートです。オリジナルの専門家は、点群に 28 のアフィン変換を繰り返し適用することにより、「フラクタル ワード」の反復関数システム (IFS) ビットマップをレンダリングします。 StockSharp バージョンは、同じカオス プロセスを方向性発振器に変えます。生成されたポイントの X 座標はスケーリングされ、指数移動平均 (EMA) で平滑化され、ロングエントリーとショートエントリーを駆動する勢いゲージとして解釈されます。
戦略ロジック
反復関数システム
- アフィン変換 – 完成したキャンドルごとに反復のバッチがトリガーされます (構成可能)。各反復中に、元の確率の重み (すべて 35 に等しい) に従って 28 の変換のうち 1 つが選択されます。変換は、MQL5 コードからそのまま移植された係数を使用して、現在のポイント
(x, y) を更新します。
- 確率テーブル – この戦略は、開始時に累積確率配列を事前に計算し、総確率集合内で 1 回のランダムな描画を使用して次の変換を迅速に選択できるようにします。
信号工事
- 正規化 – X 座標は、フラクタルをビットマップに投影するときにスクリプトが使用したのと同じスケール係数 (デフォルトでは
50) で除算されます。これにより、機器の価格に関係なく、信号が安定した数値範囲に保たれます。
- EMA 平滑化 – 正規化された系列は、期間が構成可能な EMA にフィードします。 EMA は、カオス反復の主要なドリフトを抽出するローパス フィルターとして機能します。
- エントリーロジック – EMA がプラスのエントリーしきい値を超えると、戦略がオープンまたはロングポジションに反転します。対称的に、EMA が負のしきい値を下回ると、オープンまたは反転してショートになります。
- 決済ロジック – オープンロングは、EMA が決済閾値以下に戻ったときに決済され、ショートは、EMA が負の決済閾値を上回ったときに決済されます。これにより、ゼロ付近での急激なフリップフロップを回避するヒステリシス帯域が作成されます。
リスク管理
- ポジション保護 – オプションの絶対ストップロスおよびテイプロフィットディスタンスは、
StartProtection を通じて有効にすることができます。 0 の値はそれぞれのレベルを無効にし、保護命令なしで動作したソース スクリプトの動作と一致させます。
- 出来高コントロール – エントリーは固定の市場出来高パラメーターを使用します。既存の逆エクスポージャーは、単一方向のポジションを維持するために新しい取引が開始される前にクローズされます。
パラメーター
- ボリューム – 新規参入者の市場ボリューム。
- ローソク足タイプ – フラクタル反復を駆動する時間枠 (デフォルト: 5 分ローソク足)。
- 反復 – キャンドルが終了するたびに処理される IFS 反復の数。
- スケール – EMA に入力する前に X 座標に適用される除数。
- エントリーしきい値 – ポジションをオープンするために必要な絶対値 EMA (ロングの場合は正、ショートの場合は負のミラー値)。
- 終了しきい値 – 信号がゼロに戻ったときに終了をトリガーする EMA 値。
- EMA 期間 – 正規化されたフラクタル信号に適用される指数移動平均の平滑化期間。
- 利益確定 – 利益確定距離の絶対値。無効にするには、
0 に設定します。
- ストップロス – 絶対的なストップロス距離。無効にするには、
0 に設定します。
追加の注意事項
- ソースを変更して決定論的なランダムシードを注入しない限り、実行ごとに異なる取引シーケンスが生成されます。これは、元のビットマップ レンダリング スクリプトのランダム性を反映しています。
- この戦略には市場由来の指標は必要ありません。すべてのデータは IFS 係数から内部で生成されるため、サブスクライブされたローソク足は反復のタイミングを提供するだけです。
- このパッケージには Python 実装は含まれていません。
CS/ では C# 戦略のみが利用可能です。
namespace StockSharp.Samples.Strategies;
using System;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.Messages;
/// <summary>
/// IFS Fractals strategy: Williams %R oscillator crossover.
/// Buys when WPR crosses above oversold, sells when crosses below overbought.
/// </summary>
public class IfsFractalsStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _wprPeriod;
private readonly StrategyParam<decimal> _oversold;
private readonly StrategyParam<decimal> _overbought;
private readonly StrategyParam<int> _signalCooldownCandles;
private decimal _prevWpr;
private int _candlesSinceTrade;
private bool _hasPrev;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int WprPeriod { get => _wprPeriod.Value; set => _wprPeriod.Value = value; }
public decimal Oversold { get => _oversold.Value; set => _oversold.Value = value; }
public decimal Overbought { get => _overbought.Value; set => _overbought.Value = value; }
public int SignalCooldownCandles { get => _signalCooldownCandles.Value; set => _signalCooldownCandles.Value = value; }
public IfsFractalsStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
_wprPeriod = Param(nameof(WprPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("WPR Period", "Williams %R period", "Indicators");
_oversold = Param(nameof(Oversold), -85m)
.SetDisplay("Oversold", "WPR oversold level", "Signals");
_overbought = Param(nameof(Overbought), -15m)
.SetDisplay("Overbought", "WPR overbought level", "Signals");
_signalCooldownCandles = Param(nameof(SignalCooldownCandles), 4)
.SetGreaterThanZero()
.SetDisplay("Signal Cooldown", "Bars to wait between trades", "Trading");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevWpr = 0;
_candlesSinceTrade = SignalCooldownCandles;
_hasPrev = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevWpr = 0;
_candlesSinceTrade = SignalCooldownCandles;
_hasPrev = false;
var wpr = new WilliamsR { Length = WprPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(wpr, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal wprValue)
{
if (candle.State != CandleStates.Finished) return;
if (_candlesSinceTrade < SignalCooldownCandles)
_candlesSinceTrade++;
if (_hasPrev)
{
if (_prevWpr < Oversold && wprValue >= Oversold && Position <= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
BuyMarket();
_candlesSinceTrade = 0;
}
else if (_prevWpr > Overbought && wprValue <= Overbought && Position >= 0 && _candlesSinceTrade >= SignalCooldownCandles)
{
SellMarket();
_candlesSinceTrade = 0;
}
}
_prevWpr = wprValue;
_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
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import WilliamsR
from StockSharp.Algo.Strategies import Strategy
class ifs_fractals_strategy(Strategy):
def __init__(self):
super(ifs_fractals_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._wpr_period = self.Param("WprPeriod", 14)
self._oversold = self.Param("Oversold", -85.0)
self._overbought = self.Param("Overbought", -15.0)
self._signal_cooldown_candles = self.Param("SignalCooldownCandles", 4)
self._prev_wpr = 0.0
self._candles_since_trade = 4
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def WprPeriod(self):
return self._wpr_period.Value
@WprPeriod.setter
def WprPeriod(self, value):
self._wpr_period.Value = value
@property
def Oversold(self):
return self._oversold.Value
@Oversold.setter
def Oversold(self, value):
self._oversold.Value = value
@property
def Overbought(self):
return self._overbought.Value
@Overbought.setter
def Overbought(self, value):
self._overbought.Value = value
@property
def SignalCooldownCandles(self):
return self._signal_cooldown_candles.Value
@SignalCooldownCandles.setter
def SignalCooldownCandles(self, value):
self._signal_cooldown_candles.Value = value
def OnReseted(self):
super(ifs_fractals_strategy, self).OnReseted()
self._prev_wpr = 0.0
self._candles_since_trade = self.SignalCooldownCandles
self._has_prev = False
def OnStarted2(self, time):
super(ifs_fractals_strategy, self).OnStarted2(time)
self._prev_wpr = 0.0
self._candles_since_trade = self.SignalCooldownCandles
self._has_prev = False
wpr = WilliamsR()
wpr.Length = self.WprPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(wpr, self._process_candle).Start()
def _process_candle(self, candle, wpr_value):
if candle.State != CandleStates.Finished:
return
if self._candles_since_trade < self.SignalCooldownCandles:
self._candles_since_trade += 1
wpr_val = float(wpr_value)
if self._has_prev:
if self._prev_wpr < self.Oversold and wpr_val >= self.Oversold and self.Position <= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.BuyMarket()
self._candles_since_trade = 0
elif self._prev_wpr > self.Overbought and wpr_val <= self.Overbought and self.Position >= 0 and self._candles_since_trade >= self.SignalCooldownCandles:
self.SellMarket()
self._candles_since_trade = 0
self._prev_wpr = wpr_val
self._has_prev = True
def CreateClone(self):
return ifs_fractals_strategy()