GitHub で見る
Daydream チャネルブレイクアウト戦略
Daydream Channel Breakout は、MetaTrader のオリジナルエキスパートアドバイザー「Daydream」を StockSharp の高水準ストラテジーフレームワークに直接変換したものです。このロジックは極端な値動きに逆らって取引します:価格がドンチャンチャネルの下限バンドを突き抜けると、アルゴリズムは反発を期待して買い、価格が上限バンドを上回ると空売りポジションを開きます。すべてのエグジットはpipsで表現された「仮想」テイクプロフィットで処理されるため、取引所のオーダーブックにネイティブな注文は残りません。
ストラテジーロジック
- 直近の
ChannelPeriod 本の完成したローソク足からドンチャンチャネルを構築します(現在のバーは除外され、MT5実装と一致します)。
- 終値が前の下限バンドを下回ったときにロングエントリーします。注文量には絶対ポジションサイズが含まれるため、既存のショートポジションは暗黙的にフラットになります。
- 終値が前の上限バンドを上回ったときにショートエントリーします。既存のロングポジションも同様に決済されます。
- 1本のローソク足につき1回のエントリーのみ許可されます。注文が送信された後、ストラテジーは次のバーの始値まで待って新しいシグナルを生成します。
- 各オープンポジションは仮想利益目標を監視されます。未実現利益が
TakeProfitPips(pipサイズの発見的アルゴリズムで価格距離に変換)を超えると、ポジションは成行で決済されます。
パラメーター
| 名前 |
説明 |
デフォルト |
備考 |
OrderVolume |
毎回の新規取引で送信するロットサイズ。実際の注文量には、反転前にフラットにするための反対ポジションの絶対値も含まれます。 |
0.1 |
MT5のデフォルトロットサイズと一致。 |
TakeProfitPips |
pipsで表現した仮想テイクプロフィット距離。 |
50 |
pipサイズは Security.PriceStep から導出。3桁または5桁の銘柄は自動的に10を乗算。 |
ChannelPeriod |
ドンチャンチャネルの計算に使用する完成したローソク足の本数。 |
25 |
オリジナルEAと同じルックバック期間を使用。 |
CandleType |
計算のためにサブスクライブするローソク足のタイプ。 |
TimeSpan.FromHours(1).TimeFrame() |
任意のStockSharpローソク足タイプに変更可能。 |
シグナルフロー
- データサブスクリプション:ストラテジーは
CandleType パラメーターで提供されるローソク足タイプをサブスクライブし、BindEx を使用してドンチャンチャネルインジケーターをバインドします。
- 仮想テイクプロフィットチェック:各完成ローソク足での最初のアクションは、終値と平均エントリー価格の間の距離を測定することです。閾値に達した場合、ポジションは決済され、そのバーに対する新しいエントリーは評価されません。
- チャネル更新:上限バンドと下限バンドの両方が利用可能になると、MQLの「shift=1」ロジックを反映するために前の値がキャッシュされます。シグナルは現在のローソク足で更新されたバンドではなく、前のバンドを使用します。
- エントリー決定:
- 価格 < 前の下限バンド →
OrderVolume + Math.Max(0, -Position) を買い。
- 価格 > 前の上限バンド →
OrderVolume + Math.Max(0, Position) を売り。
- ログと可視化:すべてのエントリーとテイクプロフィット決済に対して情報ログメッセージが生成されます。Designerや他のStockSharp製品でチャートエリアが利用可能な場合、ローソク足、ドンチャンチャネル、取引が自動的に描画されます。
リスク管理
- 仮想テイクプロフィットのみ実装されています。オリジナルアルゴリズムにはストップロスやトレーリング決済が存在しないため、リスクは外部で管理する必要があります(例:ポートフォリオレベルの保護)。
- 注文は絶対ポジションを加算して反転するため、異なるローソク足で連続したシグナルが現れた場合、ストラテジーは同じ方向にピラミッドアップする可能性があります。
- pipサイズヘルパーは、MT5の
Point() からpipへの変換を再現するために、3桁または5桁の銘柄に対して価格ステップを10倍にします。非標準のティックサイズを持つ銘柄では、ロジックをオーバーライドするか、TakeProfitPips を調整してカスタム距離を使用できます。
使用上の注意
- このストラテジーは平均回帰の動作を目的としています。過剰に伸びた動きが反転する傾向がある、レンジ相場で最も効果を発揮します。
- チャネルブレイク後に成行注文でエントリーするため、バックテストには現実的なスプレッドと手数料の設定を含める必要があります。
- ライブ取引所で運用する際は、セッションフィルターやボラティリティベースのストップとストラテジーを組み合わせることを検討してください。
- 実装はStockSharpの高水準API(手動インジケーターコレクションや履歴ダウンロードなし)のみに依存しているため、Designer、Shell、Runnerとそのまま互換性があります。
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>
/// Daydream Channel Breakout strategy. Uses Highest/Lowest channel breakout.
/// </summary>
public class DaydreamChannelBreakoutStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _channelPeriod;
private decimal? _prevHigh;
private decimal? _prevLow;
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public int ChannelPeriod { get => _channelPeriod.Value; set => _channelPeriod.Value = value; }
public DaydreamChannelBreakoutStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame()).SetDisplay("Candle Type", "Timeframe", "General");
_channelPeriod = Param(nameof(ChannelPeriod), 25).SetGreaterThanZero().SetDisplay("Channel Period", "Donchian lookback", "Indicators");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevHigh = null;
_prevLow = null;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevHigh = null; _prevLow = null;
var highest = new Highest { Length = ChannelPeriod };
var lowest = new Lowest { Length = ChannelPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(highest, lowest, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) { DrawCandles(area, subscription); DrawOwnTrades(area); }
}
private void ProcessCandle(ICandleMessage candle, decimal high, decimal low)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) { _prevHigh = high; _prevLow = low; return; }
if (_prevHigh == null || _prevLow == null) { _prevHigh = high; _prevLow = low; return; }
var close = candle.ClosePrice;
if (close > _prevHigh.Value && Position <= 0) { if (Position < 0) BuyMarket(); BuyMarket(); }
else if (close < _prevLow.Value && Position >= 0) { if (Position > 0) SellMarket(); SellMarket(); }
_prevHigh = high; _prevLow = low;
}
}
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 Highest, Lowest
from StockSharp.Algo.Strategies import Strategy
class daydream_channel_breakout_strategy(Strategy):
def __init__(self):
super(daydream_channel_breakout_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe", "General")
self._channel_period = self.Param("ChannelPeriod", 25) \
.SetDisplay("Channel Period", "Donchian lookback", "Indicators")
self._prev_high = None
self._prev_low = None
@property
def CandleType(self):
return self._candle_type.Value
@property
def ChannelPeriod(self):
return self._channel_period.Value
def OnReseted(self):
super(daydream_channel_breakout_strategy, self).OnReseted()
self._prev_high = None
self._prev_low = None
def OnStarted2(self, time):
super(daydream_channel_breakout_strategy, self).OnStarted2(time)
self._prev_high = None
self._prev_low = None
highest = Highest()
highest.Length = self.ChannelPeriod
lowest = Lowest()
lowest.Length = self.ChannelPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(highest, lowest, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _on_process(self, candle, high_value, low_value):
if candle.State != CandleStates.Finished:
return
hv = float(high_value)
lv = float(low_value)
if self._prev_high is None or self._prev_low is None:
self._prev_high = hv
self._prev_low = lv
return
close = float(candle.ClosePrice)
if close > self._prev_high and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif close < self._prev_low and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_high = hv
self._prev_low = lv
def CreateClone(self):
return daydream_channel_breakout_strategy()