GitHub で見る
BrainTrend2 + AbsolutelyNoLagLWMA戦略
概要
この戦略はMetaTrader 5で元々実装された2つの独立したモジュールを組み合わせます:BrainTrend2_V2とAbsolutelyNoLagLWMA。各モジュールは独自のローソク足サブスクリプションを分析し、ロングに入るか、ショートに入るか、またはフラットに戻るかを決定します。C#ポートは両方の決定フローをそのまま維持し、それらの希望するエクスポージャーを単一のStockSharp戦略に集約します。
- BrainTrend2モジュール。 BrainTrend2インジケーターによって生成されたトレンドフォローのカラー状態を使用します。状態は、価格が反対の境界を違反するたびに反転するATRベースのチャンネルから導出されます。
- AbsolutelyNoLagLWMAモジュール。 選択可能な応用価格で計算された二重平滑化された線形加重移動平均の傾きを追跡します。
モジュールのいずれかが新しいポジション方向を要求すると、戦略は組み合わせた目標ボリュームを再計算し、そのエクスポージャーに達するために成行注文を送ります。デフォルトのセットアップは両方のインジケーターでH4ローソク足で取引しますが、各モジュールは異なる時間軸をサブスクライブできます。
インジケーター
BrainTrend2
BrainTrend2インジケーターはオリジナルのMQLファイルから5色のローソク足オーバーレイを再構築します:
- 三角形に加重された真の範囲シリーズ(期間パラメーター)は0.7の係数でスケーリングされ、動的バンド(
widcha)を形成します。
- 浮動参照レベル(
Emaxtra)は現在のレジーム内の価格の極値に従います。
- 安値が
Emaxtra - widchaを下回ると、レジームは弱気に転換します。高値がEmaxtra + widchaを超えると、レジームは強気に転換します。
- 結果のレジームが色を決定します:ライム/ティール(値0または1)は強気コンテキスト用、マルーン/マゼンタ(値3または4)は弱気コンテキスト用、グレー(値2)はインジケーターが準備できていない場合。
C#インジケーターは三角形ATR推定を含む同じメカニクスを保持するため、生成された色はMQL参照と一致します。
AbsolutelyNoLagLWMA
AbsolutelyNoLagLWMAモジュールは選択された価格シリーズに2つの連続した線形加重移動平均を適用します。結果の線の傾きがカラー値を駆動します:
- 2(青) – ラインが上昇しています。
- 1(グレー) – ラインはフラットです。
- 0(バイオレット) – ラインが下降しています。
両方のインジケーターはIsFormedを公開するため、戦略は色に反応する前に十分な履歴が利用可能になるまで待機します。
取引ロジック
戦略は2つの内部ターゲット、_brainTrendTargetと_lwmaTargetを維持し、各モジュールの希望するボリュームを表します。モジュールのいずれかがターゲットを変更するたびに、戦略はRebalancePositionを呼び出して集約ポジションを_brainTrendTarget + _lwmaTargetに調整します。
BrainTrend2モジュール
SignalBar期間前のローソク足(デフォルト1)の色と先行する色を評価して状態遷移を検出します。
- 現在の色が強気(
< 2)で前の色が強気でなかった(> 1)場合、モジュールは:
- このモジュールによって作成されたショートエクスポージャーをクローズします。
- ロングエントリーが有効な場合、
BrainTrendVolumeでロングポジションを開設します。
- 現在の色が弱気(
> 2)で前の色が弱気でなかった(< 3)場合、モジュールは:
- 未解決のロングエクスポージャーをクローズします。
- ショートエントリーが有効な場合、
BrainTrendVolumeでショートポジションを開設します。
AbsolutelyNoLagLWMAモジュール
- 同じ
SignalBarロジックを使用しますが、カラー値2(上)と0(下)に反応します。
- 色が2になり前の色が異なった場合:
- オプションのショートエクスポージャーのクローズ(
LwmaCloseShortAllowed)。
LwmaBuyAllowedが真の場合、LwmaVolumeでロングポジションを任意で開設します。
- 色が0になり前の色が異なった場合:
- オプションのロングエクスポージャーのクローズ(
LwmaCloseLongAllowed)。
LwmaSellAllowedが真の場合、LwmaVolumeでショートポジションを任意で開設します。
各モジュールは自分のターゲットボリュームのみを変更するため、両方を同時にアクティブにできます。例えば、BrainTrend2モジュールはロングを維持しながら、LWMAモジュールはコアポジション周辺でショートをスキャルプできます。
パラメーター
| 名前 |
説明 |
BrainTrendAtrPeriod |
BrainTrend2が使用する三角形ATRの期間。 |
BrainTrendSignalBar |
BrainTrend2シグナルをシフトするために使用される完了ローソク足の数。1は戦略が前のバーのクローズを待つことを意味します。 |
BrainTrendBuyAllowed / BrainTrendSellAllowed |
BrainTrend2モジュールのロング/ショートエントリーを有効または無効にします。 |
BrainTrendVolume |
BrainTrend2モジュールがポジションに入るときに配置するボリューム。 |
BrainTrendCandleType |
BrainTrend2モジュールがサブスクライブするローソク足タイプ(時間軸)。 |
LwmaLength |
AbsolutelyNoLagLWMAインジケーターの各加重平均の長さ。 |
LwmaSignalBar |
LWMAモジュールのシグナルオフセット(BrainTrendモジュールと同じセマンティクス)。 |
LwmaAppliedPrice |
LWMAを構築するために使用される応用価格(クローズ、オープン、中間値、Demarkなど)。 |
LwmaBuyAllowed / LwmaSellAllowed |
LWMAモジュールのロング/ショートエントリーを有効または無効にします。 |
LwmaCloseLongAllowed / LwmaCloseShortAllowed |
シグナルが反転したときにLWMAモジュールが反対のエクスポージャーをクローズすることを許可します。 |
LwmaVolume |
LWMAモジュールがトレードを開設するときに送るボリューム。 |
LwmaCandleType |
LWMAモジュールがサブスクライブするローソク足タイプ(時間軸)。 |
ポジション管理と注文
- 戦略は集約された目標ボリュームに達するために常に成行注文(
BuyMarket / SellMarket)を使用します。
- 両方のモジュールのボリュームは加算的です。例えば、各モジュールが反対の方向に
1ロットを要求すると、ネットポジションはゼロになり、口座を実質的にヘッジします。
- オリジナルのエキスパートアドバイザーの自動ストップロスまたはテイクプロフィットは再現されません。これらの機能はMQLではブローカー固有でした。必要に応じてStockSharpの保護でリスク管理を追加できます。
- 両方のモジュールが異なる時間軸をサブスクライブする場合、戦略は自動的に両方のローソク足ストリームをサブスクライブし、フィルと一緒にチャートエリアに描画します。
注意事項
- 実装はインジケーターの計算を自己完結的に保持するため、外部インジケーターライブラリは必要ありません。
SignalBar = 0は最も最近完了したローソク足に即座に反応することを許可し、より大きなオフセットは追加の確認を強制します。
- BrainTrend2は有効な色を発行する前に少なくとも
AtrPeriod + 2の履歴ローソク足が必要です;AbsolutelyNoLagLWMAは少なくともLengthローソク足が必要です。
- 両方のモジュールは同じ
Strategy.Securityを共有するため、異なるマジックナンバーを使用したオリジナルのMT5エキスパートアドバイザーと同様に、同じポートフォリオ接続を通じてトレードが調整されます。
戦略の拡張
- MLQバージョンの固定ストップが必要な場合は、StockSharpのリスク保護(例:トレーリングストップ)を追加します。
BrainTrendVolumeとLwmaVolumeを個別に調整して、トレンドフォローまたは平均回帰の動作を強調します。
ProcessBrainTrendとProcessLwma内で提供されるインジケーター値を観察することで、追加フィルターでモジュールを組み合わせます。
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// BrainTrend2 + AbsolutelyNoLagLwma strategy (simplified). Uses ATR-based
/// trend detection combined with weighted MA direction for entries.
/// Implemented as EMA crossover with ATR channel filter.
/// </summary>
public class BrainTrend2AbsolutelyNoLagLwmaStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaFastLength;
private readonly StrategyParam<int> _emaSlowLength;
private readonly StrategyParam<int> _atrLength;
private readonly StrategyParam<decimal> _atrCoefficient;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaFastLength
{
get => _emaFastLength.Value;
set => _emaFastLength.Value = value;
}
public int EmaSlowLength
{
get => _emaSlowLength.Value;
set => _emaSlowLength.Value = value;
}
public int AtrLength
{
get => _atrLength.Value;
set => _atrLength.Value = value;
}
public decimal AtrCoefficient
{
get => _atrCoefficient.Value;
set => _atrCoefficient.Value = value;
}
public BrainTrend2AbsolutelyNoLagLwmaStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candles", "General");
_emaFastLength = Param(nameof(EmaFastLength), 7)
.SetGreaterThanZero()
.SetDisplay("EMA Fast", "Fast EMA period", "Indicators");
_emaSlowLength = Param(nameof(EmaSlowLength), 21)
.SetGreaterThanZero()
.SetDisplay("EMA Slow", "Slow EMA period", "Indicators");
_atrLength = Param(nameof(AtrLength), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Length", "ATR period", "Indicators");
_atrCoefficient = Param(nameof(AtrCoefficient), 0.7m)
.SetGreaterThanZero()
.SetDisplay("ATR Coeff", "ATR multiplier for channel", "Logic");
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var emaFast = new ExponentialMovingAverage { Length = EmaFastLength };
var emaSlow = new ExponentialMovingAverage { Length = EmaSlowLength };
decimal prevFast = 0, prevSlow = 0;
var hasPrev = false;
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(emaFast, emaSlow, (ICandleMessage candle, decimal fastVal, decimal slowVal) =>
{
if (candle.State != CandleStates.Finished)
return;
if (!hasPrev)
{
prevFast = fastVal;
prevSlow = slowVal;
hasPrev = true;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
prevFast = fastVal;
prevSlow = slowVal;
return;
}
var close = candle.ClosePrice;
// Fast EMA crosses above slow - bullish
var bullishCross = prevFast <= prevSlow && fastVal > slowVal;
// Fast EMA crosses below slow - bearish
var bearishCross = prevFast >= prevSlow && fastVal < slowVal;
if (bullishCross && Position <= 0)
BuyMarket();
else if (bearishCross && Position >= 0)
SellMarket();
prevFast = fastVal;
prevSlow = slowVal;
})
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, emaFast);
DrawIndicator(area, emaSlow);
DrawOwnTrades(area);
}
}
}
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 brain_trend2_absolutely_no_lag_lwma_strategy(Strategy):
def __init__(self):
super(brain_trend2_absolutely_no_lag_lwma_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candles", "General")
self._ema_fast_length = self.Param("EmaFastLength", 7) \
.SetDisplay("EMA Fast", "Fast EMA period", "Indicators")
self._ema_slow_length = self.Param("EmaSlowLength", 21) \
.SetDisplay("EMA Slow", "Slow EMA period", "Indicators")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@property
def EmaFastLength(self):
return self._ema_fast_length.Value
@property
def EmaSlowLength(self):
return self._ema_slow_length.Value
def OnReseted(self):
super(brain_trend2_absolutely_no_lag_lwma_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(brain_trend2_absolutely_no_lag_lwma_strategy, self).OnStarted2(time)
self._prev_fast = 0.0
self._prev_slow = 0.0
self._has_prev = False
ema_fast = ExponentialMovingAverage()
ema_fast.Length = self.EmaFastLength
ema_slow = ExponentialMovingAverage()
ema_slow.Length = self.EmaSlowLength
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(ema_fast, ema_slow, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, ema_fast)
self.DrawIndicator(area, ema_slow)
self.DrawOwnTrades(area)
def _on_process(self, candle, fast_value, slow_value):
if candle.State != CandleStates.Finished:
return
fv = float(fast_value)
sv = float(slow_value)
if not self._has_prev:
self._prev_fast = fv
self._prev_slow = sv
self._has_prev = True
return
if self._prev_fast <= self._prev_slow and fv > sv and self.Position <= 0:
self.BuyMarket()
elif self._prev_fast >= self._prev_slow and fv < sv and self.Position >= 0:
self.SellMarket()
self._prev_fast = fv
self._prev_slow = sv
def CreateClone(self):
return brain_trend2_absolutely_no_lag_lwma_strategy()