GitHub で見る
MACD Power戦略
概要
MACD Power戦略は、元のMetaTraderエキスパートアドバイザーから変換されたマルチタイムフレームのモメンタムシステムです。ロジックは、プライマリタイムフレームで計算された線形加重移動平均(LWMA)のペア、2つのMACD変種、上位タイムフレームのモメンタムフィルター、月次MACDバイアスを組み合わせています。モメンタムと上位タイムフレームのトレンド条件が一致すると、強力な動きへの参加を試みます。
コアロジック
- プライマリ移動平均 – ローソク足の典型価格(((High + Low + Close) / 3))の高速・低速LWMAのペア。戦略は、月次バイアスの方向にエントリーする前に、支配的な下降傾斜内での押し目を待つ元のコードを反映し、信号を考慮する前に高速平均が低速平均を下回ることを必要とします。
- デュアルMACD確認 – パラメーター
(12, 26, 1)と(6, 13, 1)の2つのMACDインジケーターが、ロング取引ではともにゼロより上、ショート取引ではともにゼロより下でなければなりません。これらの値は、短期加速度を測定するMQLエキスパートのMacdMAIN1とMacdMAIN2条件を再現します。
- モメンタムフィルター – モメンタム(長さ14)は、プライマリローソク足サイズから導出された上位タイムフレームで計算されます(例:15分ベース → 1時間モメンタム)。100からの絶対距離が最新3回のモメンタム読み取りで監視され、少なくとも1つが設定されたしきい値を超えて価格が決定的に動いていることを確認する必要があります。
- 月次MACDバイアス – 月次
(12, 26, 9) MACD(EAのMacdMAIN0/MacdSIGNAL0と同一)は、ロング取引ではメインラインがシグナルラインより上、ショートでは下でなければなりません。これはドミナントなマクロトレンドに逆らって取引することを防ぎます。
取引管理
- エントリーサイジング –
OrderVolumeパラメーターが基本注文サイズを定義します。ポジションの反転が必要な場合、エンジンは自動的に反対ポジションの大きさを加算し、ネットボリュームを単一の市場注文で反転させます。
- テイクプロフィット / ストップロス – 絶対距離は計器ポイントで表され、
Security.PriceStepを使用して価格に変換されます(1への安全なフォールバック付き)。
- Trailingストップ – 価格が
TrailingActivationPoints分有利に動いたら、ストップはTrailingOffsetPointsで定義されたオフセットで最高値(ロング)または最安値(ショート)を追跡します。
- ブレークイーブン – 価格が
BreakEvenTriggerPointsに達すると、エントリー ± BreakEvenOffsetPointsに合成ブレークイーブンストップが設定されます。価格がそのレベルに戻ると、ポジションは決済されます。
- 取引制限 –
MaxTradesは実行ごとのポジション開始数を制限します。しきい値に達すると新規エントリーは発行されません。
パラメーター
| 名前 |
説明 |
デフォルト |
CandleType |
シグナル生成のプライマリ時間軸。 |
15分ローソク足 |
FastMaLength |
高速LWMAの長さ(典型価格)。 |
6 |
SlowMaLength |
低速LWMAの長さ(典型価格)。 |
85 |
MomentumLength |
上位タイムフレームでのモメンタムルックバック。 |
14 |
MomentumBuyThreshold |
強気モメンタムに必要な100からの最小絶対距離。 |
0.3 |
MomentumSellThreshold |
弱気モメンタムに必要な100からの最小絶対距離。 |
0.3 |
TakeProfitPoints |
計器ポイントでのテイクプロフィット距離。 |
50 |
StopLossPoints |
計器ポイントでのストップロス距離。 |
20 |
TrailingActivationPoints |
Trailingが有効になる前に必要な利益(ポイント)。 |
40 |
TrailingOffsetPoints |
Trailingストップと極端な価格との差(ポイント)。 |
40 |
BreakEvenTriggerPoints |
ブレークイーブン保護を有効にする利益(ポイント)。 |
30 |
BreakEvenOffsetPoints |
ストップをブレークイーブンに移動する際に適用するオフセット(ポイント)。 |
30 |
MaxTrades |
セッションあたりの最大取引数。 |
10 |
OrderVolume |
基本注文ボリューム。 |
1 |
MQLエキスパートとの違い
- 戦略は直接ティックポーリングの代わりにStockSharp高レベルAPI(
SubscribeCandles + Bind/BindEx)を使用します。インジケーター値はローソク足完了後にのみ処理されます。
- 元のコードのマネーベースのTrailingと資産ストップブロックはポートされていません。アカウントレベルのマネー管理は通常StockSharpリスクフレームワークによって処理されるためです。代わりに、ポイントベースのTrailingとブレークイーブンが残り、EAの動作をエミュレートするように設定できます。
- MQLのアラート、通知、手動注文変更ヘルパーは省略されています。StockSharpエンジンは市場コールを介して直接注文を処理します。
使用上の注意
CandleTypeを設定してプライマリ時間軸を選択します。上位タイムフレームのモメンタムと月次MACDはGetMomentumCandleType()に実装されたマッピングに従って自動的に導出されます。
TakeProfitPoints、StopLossPoints、およびTrailing/ブレークイーブンパラメーターを計器のティックサイズに合わせます。デフォルト値はEAの5桁Forex設定を反映していますが、他の市場に適応できます。
- 自動化されたバックテスト実行時は
MaxTradesカウンターを監視してください。元のEAのマーチンゲール風のスタッキング動作が必要な場合は大きな数に設定してください。
- ビジュアル分析のためには、GUIでチャートを有効にします – 実装はデフォルトでローソク足と2つのLWMAカーブを描画します。
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;
public class MacdPowerStrategy : Strategy
{
private readonly StrategyParam<int> _fastPeriod;
private readonly StrategyParam<int> _slowPeriod;
private readonly StrategyParam<int> _stopLossPoints;
private readonly StrategyParam<int> _takeProfitPoints;
private ExponentialMovingAverage _fast;
private ExponentialMovingAverage _slow;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private int _cooldown;
public int FastPeriod { get => _fastPeriod.Value; set => _fastPeriod.Value = value; }
public int SlowPeriod { get => _slowPeriod.Value; set => _slowPeriod.Value = value; }
public int StopLossPoints { get => _stopLossPoints.Value; set => _stopLossPoints.Value = value; }
public int TakeProfitPoints { get => _takeProfitPoints.Value; set => _takeProfitPoints.Value = value; }
public MacdPowerStrategy()
{
_fastPeriod = Param(nameof(FastPeriod), 12).SetGreaterThanZero().SetDisplay("Fast Period", "Fast EMA period", "Indicator");
_slowPeriod = Param(nameof(SlowPeriod), 50).SetGreaterThanZero().SetDisplay("Slow Period", "Slow EMA period", "Indicator");
_stopLossPoints = Param(nameof(StopLossPoints), 200).SetNotNegative().SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk");
_takeProfitPoints = Param(nameof(TakeProfitPoints), 400).SetNotNegative().SetDisplay("Take Profit", "Take-profit in price steps", "Risk");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_fast = null; _slow = null;
_prevFast = 0; _prevSlow = 0; _entryPrice = 0; _cooldown = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fast = new ExponentialMovingAverage { Length = FastPeriod };
_slow = new ExponentialMovingAverage { Length = SlowPeriod };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription.Bind(_fast, _slow, ProcessCandle);
subscription.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished) return;
if (!_fast.IsFormed || !_slow.IsFormed) { _prevFast = fastValue; _prevSlow = slowValue; return; }
if (_cooldown > 0) { _cooldown--; _prevFast = fastValue; _prevSlow = slowValue; return; }
var close = candle.ClosePrice;
var step = Security?.PriceStep ?? 1m;
if (Position > 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close <= _entryPrice - StopLossPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close >= _entryPrice + TakeProfitPoints * step) { SellMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
else if (Position < 0 && _entryPrice > 0)
{
if (StopLossPoints > 0 && close >= _entryPrice + StopLossPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
if (TakeProfitPoints > 0 && close <= _entryPrice - TakeProfitPoints * step) { BuyMarket(); _entryPrice = 0; _cooldown = 100; _prevFast = fastValue; _prevSlow = slowValue; return; }
}
if (_prevFast <= _prevSlow && fastValue > slowValue && Position <= 0)
{ if (Position < 0) BuyMarket(); BuyMarket(); _entryPrice = close; _cooldown = 100; }
else if (_prevFast >= _prevSlow && fastValue < slowValue && Position >= 0)
{ if (Position > 0) SellMarket(); SellMarket(); _entryPrice = close; _cooldown = 100; }
_prevFast = fastValue; _prevSlow = slowValue;
}
}
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 macd_power_strategy(Strategy):
"""
MACD Power: EMA crossover with manual SL/TP via price steps.
"""
def __init__(self):
super(macd_power_strategy, self).__init__()
self._fast_period = self.Param("FastPeriod", 12).SetDisplay("Fast Period", "Fast EMA period", "Indicator")
self._slow_period = self.Param("SlowPeriod", 50).SetDisplay("Slow Period", "Slow EMA period", "Indicator")
self._stop_loss_points = self.Param("StopLossPoints", 200).SetDisplay("Stop Loss", "Stop-loss in price steps", "Risk")
self._take_profit_points = self.Param("TakeProfitPoints", 400).SetDisplay("Take Profit", "Take-profit in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candles", "General")
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(macd_power_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._cooldown = 0
def OnStarted2(self, time):
super(macd_power_strategy, self).OnStarted2(time)
self._fast_ind = ExponentialMovingAverage()
self._fast_ind.Length = self._fast_period.Value
self._slow_ind = ExponentialMovingAverage()
self._slow_ind.Length = self._slow_period.Value
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._fast_ind, self._slow_ind, self._process_candle).Start()
def _process_candle(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
fast = float(fast_val)
slow = float(slow_val)
if not self._fast_ind.IsFormed or not self._slow_ind.IsFormed:
self._prev_fast = fast
self._prev_slow = slow
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_fast = fast
self._prev_slow = slow
return
close = float(candle.ClosePrice)
step = float(self.Security.PriceStep) if self.Security is not None and self.Security.PriceStep is not None else 1.0
if self.Position > 0 and self._entry_price > 0:
if self._stop_loss_points.Value > 0 and close <= self._entry_price - self._stop_loss_points.Value * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast
self._prev_slow = slow
return
if self._take_profit_points.Value > 0 and close >= self._entry_price + self._take_profit_points.Value * step:
self.SellMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast
self._prev_slow = slow
return
elif self.Position < 0 and self._entry_price > 0:
if self._stop_loss_points.Value > 0 and close >= self._entry_price + self._stop_loss_points.Value * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast
self._prev_slow = slow
return
if self._take_profit_points.Value > 0 and close <= self._entry_price - self._take_profit_points.Value * step:
self.BuyMarket()
self._entry_price = 0.0
self._cooldown = 100
self._prev_fast = fast
self._prev_slow = slow
return
if self._prev_fast <= self._prev_slow and fast > slow and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = close
self._cooldown = 100
elif self._prev_fast >= self._prev_slow and fast < slow and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = close
self._cooldown = 100
self._prev_fast = fast
self._prev_slow = slow
def CreateClone(self):
return macd_power_strategy()