GitHub で見る
ビリーエキスパート戦略
概要
- オリジナルの MetaTrader 4 エキスパート「Billy_expert.mq4」から変換されました。
- 4連続の下落高値を待ってオープンしてからエントリーするロングのみのモメンタム戦略。
- 2 つの確率オシレーター (取引時間枠では高速、より高い時間枠では低速) を使用して、勢いが上向きにシフトしていることを確認します。
- スポット FX ペア向けに設計されていますが、分ベースのローソク足を提供するあらゆる商品に適用できます。
信号ロジック
プライスアクションフィルター
- 主要な時間枠で完成したキャンドルを評価します。
- 高値と始値の両方が減少する 4 つの連続したローソク足が必要です。これにより、MT4
High[0] < High[1] < High[2] < High[3] および Open[0] < Open[1] < Open[2] < Open[3] チェックが再作成されます。
- このパターンは、疲れ果てた弱気の動きを示唆し、反転取引の戦略を準備します。
発振器の確認
- 取引時間枠で高速確率オシレーターを計算し、確認時間枠で低速確率オシレーターを計算します。
- 各オシレーターについて、現在および以前に完了したローソク足 (
%K(0) > %D(0) および %K(1) > %D(1)) の両方で、%K ラインが %D ラインより上にあることを要求します。
- 取引は、両方のオシレーターが同時に強気の勢いを確認した場合にのみトリガーされます。
注文管理
- エントリー: 戦略
Volume パラメーターによってサイズ設定された市場の買い (ショート ポジションが存在する場合、自動的にクローズされ、反転されます)。
- ストップロス:
Stop Loss (pts) パラメーターを使用して、約定価格の下の固定距離。 0 の値は停止を無効にします。
- 利益確定:
Take Profit (pts) パラメータを使用して、約定価格を超える固定距離。 0 の値はターゲットを無効にします。
- ポジションキャップ:
Max Orders は、同時にアクティブにできる長いエントリの数を制限します。 StockSharp はネット ポジションを維持しているため、この戦略は現在オープンしている Volume ブロックの数をカウントすることで MT4 の動作を近似します。
- トレーリング ストップ: 元の EA はトレーリング ストップ入力を宣言しましたが、実装していませんでした。変換されたバージョンでは、パリティのための末尾ロジックも省略されます。
パラメーター
| 名前 |
説明 |
デフォルト |
Trading Candle |
価格パターンと高速確率の主要な時間枠。 |
1分 |
Slow Stochastic Candle |
確率的確認に使用されるより高い時間枠。 |
5分 |
Stochastic Length |
%K のルックバック ウィンドウ。 |
5 |
%K Smoothing |
%K ラインに適用されるスムージング。 |
3 |
%D Period |
%D ラインにスムージングが適用されました。 |
3 |
Slowing |
%K の追加の平滑化係数。 |
3 |
Stop Loss (pts) |
価格ステップのストップロス距離。 |
0 |
Take Profit (pts) |
価格ステップでの利益確定距離。 |
12 |
Max Orders |
同時ロングエントリーの最大数。 |
1 |
使用上の注意
- 戦略を開始する前に、
Volume プロパティを設定します。 StockSharp のデフォルトは 0 であり、これにより注文がブロックされます。
- 価格ステップは
Security.PriceStep から読み取られます (Security.Step または 1 にフォールバックします)。正確なストップ/ターゲットレベルを取得するには、機器のメタデータが正しく設定されていることを確認してください。
- 確認時間枠が取引時間枠と異なる場合、元の MT4 スクリプトの動作と一致するように、新しいスロー キャンドルが表示されるまで、最後に完了したスロー キャンドルが再利用されます。
- EA は、ブローカー側のストップロスとテイクプロフィットを超えるエグジットを管理できませんでした。この変換は、レベルに触れたときに保護的な成行注文を送信することで、この動作を反映しています。
- StockSharp は位置を集計するため、各エントリが同じ
Volume サイズを使用する場合に、Max Orders > 1 が最適に機能します。
MT4版との違い
Point をサイレントに使用するのではなく、ログ警告により価格ステップ情報の欠落がないか安全性をチェックします。
- 必要なデータ (価格履歴と両方の確率オシレーター) がすべて利用可能な場合にのみ戦略が取引されるようにするためのガード条項を追加しました。
- この戦略は完成したローソク足でのみ実行されますが、MT4 はティックを処理しますが、バータイムによって調整されます。この変更により、重複した評価が回避され、ロジックの決定性が維持されます。
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Long-only reversal strategy that looks for consecutive descending highs
/// and enters when RSI confirms oversold conditions with momentum turning up.
/// </summary>
public class BillyExpertReversalStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _rsiLength;
private decimal _prevHigh1, _prevHigh2, _prevHigh3;
private int _barCount;
private decimal _prevRsi;
private bool _hasPrevRsi;
private decimal _entryPrice;
public BillyExpertReversalStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for analysis.", "General");
_rsiLength = Param(nameof(RsiLength), 14)
.SetDisplay("RSI Length", "Length for RSI indicator.", "Indicators");
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHigh1 = 0;
_prevHigh2 = 0;
_prevHigh3 = 0;
_barCount = 0;
_prevRsi = 50;
_hasPrevRsi = false;
_entryPrice = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHigh1 = 0;
_prevHigh2 = 0;
_prevHigh3 = 0;
_barCount = 0;
_prevRsi = 50;
_hasPrevRsi = false;
_entryPrice = 0;
var rsi = new RelativeStrengthIndex { Length = RsiLength };
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;
_barCount++;
var high = candle.HighPrice;
var close = candle.ClosePrice;
// Check descending highs pattern (3 consecutive lower highs)
var descendingHighs = _barCount >= 4 &&
high < _prevHigh1 &&
_prevHigh1 < _prevHigh2 &&
_prevHigh2 < _prevHigh3;
// RSI turning up from oversold
var rsiBullish = _hasPrevRsi && _prevRsi < 40 && rsiValue > _prevRsi;
// Manage long position
if (Position > 0)
{
// Exit on take-profit, stop-loss, or RSI overbought
if (_entryPrice > 0 && close >= _entryPrice * 1.015m)
{
SellMarket();
}
else if (_entryPrice > 0 && close <= _entryPrice * 0.985m)
{
SellMarket();
}
else if (rsiValue > 75)
{
SellMarket();
}
}
// Manage short position (exit only, this is mostly long-only)
if (Position < 0)
{
if (rsiValue < 30)
{
BuyMarket();
}
}
// Entry: descending highs (selling exhaustion) + RSI confirms reversal
if (Position == 0)
{
if (descendingHighs && rsiBullish)
{
_entryPrice = close;
BuyMarket();
}
// Also allow short on ascending lows pattern with overbought RSI
else if (_barCount >= 4 && rsiValue > 70 && _prevRsi > 70)
{
_entryPrice = close;
SellMarket();
}
}
// Update history
_prevHigh3 = _prevHigh2;
_prevHigh2 = _prevHigh1;
_prevHigh1 = high;
_prevRsi = rsiValue;
_hasPrevRsi = 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.Strategies import Strategy
from StockSharp.Algo.Indicators import RelativeStrengthIndex
class billy_expert_reversal_strategy(Strategy):
def __init__(self):
super(billy_expert_reversal_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Timeframe for analysis", "General")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "Length for RSI indicator", "Indicators")
self._prev_high1 = 0.0
self._prev_high2 = 0.0
self._prev_high3 = 0.0
self._bar_count = 0
self._prev_rsi = 50.0
self._has_prev_rsi = False
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@property
def RsiLength(self):
return self._rsi_length.Value
def OnStarted2(self, time):
super(billy_expert_reversal_strategy, self).OnStarted2(time)
self._prev_high1 = 0.0
self._prev_high2 = 0.0
self._prev_high3 = 0.0
self._bar_count = 0
self._prev_rsi = 50.0
self._has_prev_rsi = False
self._entry_price = 0.0
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.RsiLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(self._rsi, self.ProcessCandle).Start()
def ProcessCandle(self, candle, rsi_value):
if candle.State != CandleStates.Finished:
return
self._bar_count += 1
high = float(candle.HighPrice)
close = float(candle.ClosePrice)
rsi_val = float(rsi_value)
# Check descending highs pattern (3 consecutive lower highs)
descending_highs = (self._bar_count >= 4 and
high < self._prev_high1 and
self._prev_high1 < self._prev_high2 and
self._prev_high2 < self._prev_high3)
# RSI turning up from oversold
rsi_bullish = self._has_prev_rsi and self._prev_rsi < 40 and rsi_val > self._prev_rsi
# Manage long position
if self.Position > 0:
if self._entry_price > 0 and close >= self._entry_price * 1.015:
self.SellMarket()
elif self._entry_price > 0 and close <= self._entry_price * 0.985:
self.SellMarket()
elif rsi_val > 75:
self.SellMarket()
# Manage short position (exit only)
if self.Position < 0:
if rsi_val < 30:
self.BuyMarket()
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_high3 = self._prev_high2
self._prev_high2 = self._prev_high1
self._prev_high1 = high
self._prev_rsi = rsi_val
self._has_prev_rsi = True
return
# Entry
if self.Position == 0:
if descending_highs and rsi_bullish:
self._entry_price = close
self.BuyMarket()
elif self._bar_count >= 4 and rsi_val > 70 and self._prev_rsi > 70:
self._entry_price = close
self.SellMarket()
# Update history
self._prev_high3 = self._prev_high2
self._prev_high2 = self._prev_high1
self._prev_high1 = high
self._prev_rsi = rsi_val
self._has_prev_rsi = True
def OnReseted(self):
super(billy_expert_reversal_strategy, self).OnReseted()
self._prev_high1 = 0.0
self._prev_high2 = 0.0
self._prev_high3 = 0.0
self._bar_count = 0
self._prev_rsi = 50.0
self._has_prev_rsi = False
self._entry_price = 0.0
def CreateClone(self):
return billy_expert_reversal_strategy()