GitHub で見る
チャネル戦略
この戦略は、ゴルダゴの公共図書館に含まれる MetaTrader 4 つの「チャネル」エキスパート アドバイザを直接移植したものです。非常に高速な指数移動平均 (EMA) と 3 つの EMA ベースのエンベロープを組み合わせて、価格が圧縮ゾーンから抜け出す瞬間を検出します。単一のポジションがオープンになると、元の MQL の実装と同様に、戦略はストップ注文とオプションのトレーリングストップに依存してエグジットを管理します。
取引ロジック
- この戦略はデフォルトで 1 時間足のローソク足をサブスクライブし、以下を計算します。
- ローソク足 終値価格を使用した高速な EMA (長さ 2)。
- エキスパートアドバイザーのショートエントリールールで必要とされる、ローソク足オープン価格を使用した2番目の高速EMA (長さ2)。
- クローズ時の遅い EMA (長さ 220) は、±1.0%、±0.7%、±0.3% の 3 つのエンベロープ偏差のベースとして機能します。
- ロング ポジションは、クローズベースのファースト EMA が 6 つの過去のクロスチェックのいずれかを満たしたときにオープンされます。
- 外側の 1% の下側エンベロープを通って上向きに交差します。
- 0.7%の下側エンベロープを通過して上向きにクロスします。
- 2つの連続したバーが0.3%の下限エンベロープを下回ります(売られ過ぎの状態)。
- 低速の EMA 自体を上向きに横切ります。
- 0.3%の上側エンベロープを通過して上向きにクロスします。
- 0.7%の上側エンベロープを通過して上向きにクロスします。
- ショート ポジションは、オープンベースの高速 EMA が対称ショート ルールのいずれかをトリガーするとオープンされます。
- 外側の 1% 上部エンベロープを下に通過します。
- 0.7%の上側エンベロープを下向きに通過します。
- 0.3%の上側エンベロープを下向きに通過します。
- ゆっくりとしたEMAを下っていきます。
- 0.3%の下側エンベロープを下向きに通過します。
- 0.7%の下側エンベロープを下向きに通過しています。
- 一度に存在できる市場ポジションは 1 つだけです。取引がアクティブな間は新しいシグナルは無視され、MetaTrader エキスパートの動作と一致します。
リスク管理
- ロングトレードとショートトレードに合わせて、個別のストップロスとテイクプロフィットの距離を設定できます。ゼロに設定すると、これらの保護命令はスキップされ、元のソースからデフォルトで無効になった状態が複製されます。
- オプションのトレーリングストップは、ポイントで測定されるトレーリング距離を超えて価格がポジションに有利に動くと、保護注文を強化します。
- ポジションが横ばいになるか戦略が停止すると、すべての保護注文は自動的にキャンセルされます。
パラメーター
| 名前 |
説明 |
Candle Type |
価格分析に使用される時間枠 (デフォルト: 1 時間)。 |
Volume |
すべてのエントリに使用される注文サイズ。 |
Fast EMA / Slow EMA |
高速 EMA と低速 EMA の期間。 |
Envelope 1%, Envelope 0.7%, Envelope 0.3% |
3 つのエンベロープ バンドの幅のパーセンテージ。 |
Buy Stop-Loss, Sell Stop-Loss |
ロングまたはショートトレードのエントリー価格と最初のストップロスの間のポイント単位の距離。 |
Buy Take-Profit, Sell Take-Profit |
オプションの固定テイクプロフィットレベルのポイント単位の距離。 |
Buy Trailing, Sell Trailing |
ロングまたはショートポジションのトレーリングストップ距離 (ポイント単位)。 |
Use Trading Hours |
タイムウィンドウフィルターを有効にします。 |
From Hour, To Hour |
新しいポジションをオープンするための包括的な時間帯の境界。 From が To より大きい場合、ウィンドウは午前 0 時を中心にラップされます。 |
使用上の注意
- 停止距離はポイントで定義されているため、内部的にセキュリティ
PriceStep が乗算されます。このステップが取引に使用される商品と一致していることを確認してください。
- 高速の EMA の長さは、MT4 エキスパートを反映するために意図的に非常に短くしています。増加させると信号周波数が大幅に変化します。
- 元のアドバイザーでは、アカウントのホワイトリスト登録と音声アラートも許可していました。これらはプラットフォーム固有であり、注文ロジックには影響しないため、省略されました。
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>
/// Channels strategy - EMA envelope breakout.
/// Buys when price breaks above fast EMA + offset, sells when it breaks below.
/// Uses slow EMA as trend filter.
/// </summary>
public class ChannelsStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private bool _hasPrev;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public ChannelsStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 10)
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators");
_slowPeriod = Param(nameof(SlowPeriod), 30)
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Candle timeframe", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities() => [(Security, CandleType)];
protected override void OnReseted() { base.OnReseted(); _prevFast = 0m; _prevSlow = 0m; _hasPrev = false; }
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var fast = new ExponentialMovingAverage { Length = FastPeriod };
var slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(fast, slow, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
if (!_hasPrev)
{
_prevFast = fast;
_prevSlow = slow;
_hasPrev = true;
return;
}
var close = candle.ClosePrice;
// Buy: price crosses above fast EMA, fast EMA above slow EMA (uptrend)
if (close > fast && fast > slow && _prevFast <= _prevSlow && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
}
// Sell: price crosses below fast EMA, fast EMA below slow EMA (downtrend)
else if (close < fast && fast < slow && _prevFast >= _prevSlow && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
}
// Exit long when fast crosses below slow
else if (Position > 0 && fast < slow && _prevFast >= _prevSlow)
{
SellMarket();
}
// Exit short when fast crosses above slow
else if (Position < 0 && fast > slow && _prevFast <= _prevSlow)
{
BuyMarket();
}
_prevFast = fast;
_prevSlow = slow;
}
}
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 ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class channels_strategy(Strategy):
def __init__(self):
super(channels_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 10) \
.SetDisplay("Fast EMA", "Fast EMA period", "Indicators")
self._slow_period = self.Param("SlowPeriod", 30) \
.SetDisplay("Slow EMA", "Slow EMA period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Candle timeframe", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
@property
def fast_period(self):
return self._fast_period.Value
@property
def slow_period(self):
return self._slow_period.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(channels_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(channels_strategy, self).OnStarted2(time)
self._has_prev = False
fast = ExponentialMovingAverage()
fast.Length = self.fast_period
slow = ExponentialMovingAverage()
slow.Length = self.slow_period
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(fast, slow, self.process_candle).Start()
def process_candle(self, candle, fast, slow):
if candle.State != CandleStates.Finished:
return
fast_val = float(fast)
slow_val = float(slow)
if not self._has_prev:
self._prev_fast = fast_val
self._prev_slow = slow_val
self._has_prev = True
return
close = float(candle.ClosePrice)
if close > fast_val and fast_val > slow_val and self._prev_fast <= self._prev_slow and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif close < fast_val and fast_val < slow_val and self._prev_fast >= self._prev_slow and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
elif self.Position > 0 and fast_val < slow_val and self._prev_fast >= self._prev_slow:
self.SellMarket()
elif self.Position < 0 and fast_val > slow_val and self._prev_fast <= self._prev_slow:
self.BuyMarket()
self._prev_fast = fast_val
self._prev_slow = slow_val
def CreateClone(self):
return channels_strategy()