GitHub で見る
モメンタム最小利益戦略に関する修士号
概要
この戦略は、モメンタム指標とモメンタム系列に基づいて計算される移動平均の間のクロスオーバーを取引することにより、MetaTrader 5 エキスパート アドバイザー Momentum Min Profit.mq5 の MA を複製します。強気のシグナルは、前のバーが勢いをニュートラル 100 レベル以下に維持している間に、勢いが平均を上回ったときに表示されます。弱気シグナルは、モメンタムが平均を下回り、前のバーが 100 を超えたときに生成されます。実装では、元の金額ベースのエクイティ ストップとポイント単位で測定される固定のテイクプロフィット ディスタンスが維持されます。
取引ロジック
CandleType で定義されたローソク足をリクエストし、モメンタム インジケーターにフィードします。
MomentumMovingAverageType と MomentumMovingAveragePeriod で定義される移動平均を使用して、運動量ストリームを平滑化します。
- 前のバーの値を使用してクロスオーバーを検出し、二重信号を回避します。
- MQL バージョンのオプション機能:
- 生成された信号の方向を反転します。
- 新しい取引を開始する前に逆のエクスポージャーをクローズするか、エントリーを完全にスキップします。
- いつでも単一のネット ポジションを強制します。
- 完全に閉じたバーではなく、現在の (形成中の) ローソク足でトリガーできるようにします。
- リスク管理を適用します。
- 株式のストップインマネー:
PnL + Position * (close - PositionPrice) は StopLossMoney を超えていなければなりません。
Security.PriceStep を通じて変換されたポイント単位のテイクプロフィット距離。
パラメーター
| パラメータ |
種類 |
デフォルト |
説明 |
CandleType |
DataType |
TimeSpan.FromMinutes(5).TimeFrame() |
キャンドルは運動量の計算に使用されます。 |
MomentumPeriod |
int |
14 |
モメンタムインジケーターのルックバック期間。 |
MomentumMovingAveragePeriod |
int |
6 |
モメンタムに適用される移動平均の長さ。 |
MomentumMovingAverageType |
MomentumMovingAverageType |
Smoothed |
移動平均アルゴリズム (単純、指数、平滑化、加重)。 |
ReverseSignals |
bool |
false |
MetaTrader の売買シグナルをミラーリングします。 |
CloseOpposite |
bool |
true |
新しいポジションを開く前に、反対側のエクスポージャーを閉じます。 |
OnlyOnePosition |
bool |
true |
単一のネットポジションを維持します。 |
UseCurrentCandle |
bool |
false |
閉じたバーではなく、現在形成されているローソク足のシグナルを評価します。 |
StopLossMoney |
decimal |
15 |
すべての取引を終了する前に、株式のドローダウンが許可されます。 |
TakeProfitPoints |
decimal |
460 |
商品ポイントでの利益目標(PriceStepを乗算)。 |
MomentumReference |
decimal |
100 |
MQL 戦略からコピーされたニュートラルな運動量レベル。 |
実装メモ
- 移動平均は、StockSharp の組み込み SMA/EMA/SMMA/WMA クラスを再利用するために、
LengthIndicator<decimal> インスタンスで実装されています。
- 元の注文キューとマジックナンバー フィルターは StockSharp のネット ポジションにマッピングされているため、この戦略は、
CloseOpposite が有効な場合に反対側をフラットにし、新しいエクスポージャーをオープンするサイズの単一の成行注文を送信します。
- 株式保護は、浮動損失がしきい値に達すると、
CloseAll() を介してすべてのポジションをクローズします。これは、手数料、スワップ、利益の合計を監視する MetaTrader の動作と正確に一致します。
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>
/// Momentum crossing its own moving average strategy.
/// Converted from MetaTrader 5 (MA on Momentum Min Profit.mq5).
/// </summary>
public class MaOnMomentumMinProfitStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _momentumPeriod;
private readonly StrategyParam<int> _maPeriod;
private Momentum _momentum;
private readonly Queue<decimal> _momentumHistory = new();
private decimal? _prevMomentum;
private decimal? _prevSignal;
/// <summary>
/// Candle type used for signal calculation.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Momentum period.
/// </summary>
public int MomentumPeriod
{
get => _momentumPeriod.Value;
set => _momentumPeriod.Value = value;
}
/// <summary>
/// Moving average period applied to momentum values.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Initialize <see cref="MaOnMomentumMinProfitStrategy"/>.
/// </summary>
public MaOnMomentumMinProfitStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(60).TimeFrame())
.SetDisplay("Candle Type", "Type of candles used for the momentum calculation", "General");
_momentumPeriod = Param(nameof(MomentumPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("Momentum Period", "Lookback for the momentum indicator", "Momentum");
_maPeriod = Param(nameof(MaPeriod), 10)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Period of the moving average applied to momentum", "Momentum");
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevMomentum = null;
_prevSignal = null;
_momentumHistory.Clear();
_momentum = new Momentum { Length = MomentumPeriod };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_momentum, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal momentumValue)
{
if (candle.State != CandleStates.Finished)
return;
_momentumHistory.Enqueue(momentumValue);
while (_momentumHistory.Count > MaPeriod)
_momentumHistory.Dequeue();
if (!_momentum.IsFormed)
return;
if (_momentumHistory.Count < MaPeriod)
{
_prevMomentum = momentumValue;
return;
}
// Calculate SMA of momentum
var sum = 0m;
var history = _momentumHistory.ToArray();
foreach (var v in history)
sum += v;
var signalValue = sum / history.Length;
if (_prevMomentum is null || _prevSignal is null)
{
_prevMomentum = momentumValue;
_prevSignal = signalValue;
return;
}
var crossUp = _prevMomentum < _prevSignal && momentumValue > signalValue;
var crossDown = _prevMomentum > _prevSignal && momentumValue < signalValue;
var volume = Volume;
if (volume <= 0)
volume = 1;
var minSpread = 0.5m;
if (crossUp && Math.Abs(momentumValue - signalValue) >= minSpread)
{
if (Position <= 0)
BuyMarket(Position < 0 ? Math.Abs(Position) + volume : volume);
}
else if (crossDown && Math.Abs(momentumValue - signalValue) >= minSpread)
{
if (Position >= 0)
SellMarket(Position > 0 ? Math.Abs(Position) + volume : volume);
}
_prevMomentum = momentumValue;
_prevSignal = signalValue;
}
/// <inheritdoc />
protected override void OnReseted()
{
_prevMomentum = null;
_prevSignal = null;
_momentum = null;
_momentumHistory.Clear();
base.OnReseted();
}
}
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 Momentum
from StockSharp.Algo.Strategies import Strategy
class ma_on_momentum_min_profit_strategy(Strategy):
def __init__(self):
super(ma_on_momentum_min_profit_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._momentum_period = self.Param("MomentumPeriod", 20)
self._ma_period = self.Param("MaPeriod", 10)
self._mom_history = []
self._prev_mom = 0.0
self._prev_signal = 0.0
self._has_prev = False
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def MomentumPeriod(self):
return self._momentum_period.Value
@MomentumPeriod.setter
def MomentumPeriod(self, value):
self._momentum_period.Value = value
@property
def MaPeriod(self):
return self._ma_period.Value
@MaPeriod.setter
def MaPeriod(self, value):
self._ma_period.Value = value
def OnReseted(self):
super(ma_on_momentum_min_profit_strategy, self).OnReseted()
self._mom_history = []
self._prev_mom = 0.0
self._prev_signal = 0.0
self._has_prev = False
def OnStarted2(self, time):
super(ma_on_momentum_min_profit_strategy, self).OnStarted2(time)
self._mom_history = []
self._prev_mom = 0.0
self._prev_signal = 0.0
self._has_prev = False
mom = Momentum()
mom.Length = self.MomentumPeriod
subscription = self.SubscribeCandles(self.CandleType)
subscription.Bind(mom, self._process_candle).Start()
def _process_candle(self, candle, mom_value):
if candle.State != CandleStates.Finished:
return
mom_val = float(mom_value)
ma_len = self.MaPeriod
self._mom_history.append(mom_val)
while len(self._mom_history) > ma_len:
self._mom_history.pop(0)
if len(self._mom_history) < ma_len:
self._prev_mom = mom_val
return
signal_val = sum(self._mom_history) / ma_len
if self._has_prev:
cross_up = self._prev_mom < self._prev_signal and mom_val > signal_val
cross_down = self._prev_mom > self._prev_signal and mom_val < signal_val
min_spread = 0.5
if cross_up and abs(mom_val - signal_val) >= min_spread and self.Position <= 0:
self.BuyMarket()
elif cross_down and abs(mom_val - signal_val) >= min_spread and self.Position >= 0:
self.SellMarket()
self._prev_mom = mom_val
self._prev_signal = signal_val
self._has_prev = True
def CreateClone(self):
return ma_on_momentum_min_profit_strategy()