5 MA マルチタイムフレーム戦略
概要
Five MA マルチタイムフレーム戦略 は、StockSharp の高レベルの API を使用して、元の MT4「5matf」エキスパート アドバイザーを複製します。この戦略は、3 つの時間枠 (主、高値、最遅) にわたる 5 つの単純な移動平均を分析し、各平均の傾きをアクセラレーター オシレーターと組み合わせて段階的なエントリー シグナルを生成します。すべての時間枠で十分な強気または弱気の証拠が存在する場合、戦略はそれに応じてポジションをオープンまたはクローズします。
指標とデータ
- 単純移動平均 (SMA): 3 つの時間枠すべての期間 5、8、13、21、および 34。
- アクセラレーター オシレーター (AC): 運動量の加速を評価するために、第 1 および第 3 のタイムフレームに適用されます。
- タイムフレーム: デフォルトは 15 分 (シグナル)、60 分 (確認)、および 240 分 (トレンド フィルター) に設定されています。すべての時間枠はパラメータによって調整できます。
信号ロジック
- 各 SMA は、現在の値を前のローソク足と比較して、上昇または下降の傾きを決定します。
- アクセラレータ オシレータは、最新の 4 つの値を使用して強気シーケンスまたは弱気シーケンスをチェックします。
- スロープ カウントとオシレーターの寄与は、時間枠ごとにパーセンテージ スコアに集計されます。
- 3 つの時間枠すべてで 50% を超える強気スコアがある場合、BUY シグナルが生成されます。 75% を超えるスコアは信号を強化します。
- 同じしきい値を逆方向に適用すると、SELL シグナルが生成されます。
- 逆のシグナルが設定されたクローズレベルを超えると、ポジションはクローズされます。新しい取引はアクティブなポジションがない場合にのみオープンされ、元のエキスパートアドバイザーの動作を反映します。
パラメーター
| 名前 | デフォルト | 説明 |
|---|---|---|
CandleType |
15分キャンドル | トレードシグナルに使用される主な時間枠。 |
HigherTimeframe1 |
60分キャンドル | 確認のための最初のより高い時間枠。 |
HigherTimeframe2 |
240分のキャンドル | スロー トレンド フィルターの 2 番目に高い時間枠。 |
FirstPeriod – FifthPeriod |
5、8、13、21、34 | 各タイムフレームに適用される SMA の長さ。 |
OpenLevel |
0 | 新しいポジションをオープンするために必要な最低限の信号グレード。 |
CloseLevel |
1 | 既存のポジションを決済するには反対のシグナルグレードが必要です。 |
すべてのパラメータは、StockSharp の戦略 UI 内で最適化または微調整できます。
使用上の注意
- この戦略は成行注文を使用し、同時反転を発行しません。反対方向に開く前に、常に平坦な位置になるまで待機します。
- 選択したすべての時間枠の履歴データ フィードを有効にして、計算の同期を確保します。
- 戦略をさまざまな市場またはボラティリティ状況に適用する場合は、SMA の長さまたはオシレーターの使用を調整することを検討してください。
変換メモ
この実装は、StockSharp のサブスクリプションおよびインジケーター バインディング システムを利用しながら、MT4「5matf」エキスパート アドバイザのコア動作を保持します。アクセラレータ ロジックでは、元のスクリプトと同様に、信号がアクティブになる前に 4 つの完了したキャンドルが必要です。
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Five moving averages across three timeframes with Accelerator Oscillator confirmation.
/// </summary>
public class FiveMaMultiTimeframeStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<DataType> _higherTimeframe1;
private readonly StrategyParam<DataType> _higherTimeframe2;
private readonly StrategyParam<int> _firstPeriod;
private readonly StrategyParam<int> _secondPeriod;
private readonly StrategyParam<int> _thirdPeriod;
private readonly StrategyParam<int> _fourthPeriod;
private readonly StrategyParam<int> _fifthPeriod;
private readonly StrategyParam<int> _openLevel;
private readonly StrategyParam<int> _closeLevel;
private readonly Dictionary<string, FrameState> _frames = new(StringComparer.Ordinal);
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public DataType HigherTimeframe1 { get => _higherTimeframe1.Value; set => _higherTimeframe1.Value = value; }
public DataType HigherTimeframe2 { get => _higherTimeframe2.Value; set => _higherTimeframe2.Value = value; }
public int FirstPeriod { get => _firstPeriod.Value; set => _firstPeriod.Value = value; }
public int SecondPeriod { get => _secondPeriod.Value; set => _secondPeriod.Value = value; }
public int ThirdPeriod { get => _thirdPeriod.Value; set => _thirdPeriod.Value = value; }
public int FourthPeriod { get => _fourthPeriod.Value; set => _fourthPeriod.Value = value; }
public int FifthPeriod { get => _fifthPeriod.Value; set => _fifthPeriod.Value = value; }
public int OpenLevel { get => _openLevel.Value; set => _openLevel.Value = value; }
public int CloseLevel { get => _closeLevel.Value; set => _closeLevel.Value = value; }
public FiveMaMultiTimeframeStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame()).SetDisplay("Candle Type", "Primary signal timeframe.", "Timeframes");
_higherTimeframe1 = Param(nameof(HigherTimeframe1), TimeSpan.FromMinutes(60).TimeFrame()).SetDisplay("Higher Timeframe 1", "First confirmation timeframe.", "Timeframes");
_higherTimeframe2 = Param(nameof(HigherTimeframe2), TimeSpan.FromMinutes(240).TimeFrame()).SetDisplay("Higher Timeframe 2", "Slow trend timeframe.", "Timeframes");
_firstPeriod = Param(nameof(FirstPeriod), 5).SetGreaterThanZero();
_secondPeriod = Param(nameof(SecondPeriod), 8).SetGreaterThanZero();
_thirdPeriod = Param(nameof(ThirdPeriod), 13).SetGreaterThanZero();
_fourthPeriod = Param(nameof(FourthPeriod), 21).SetGreaterThanZero();
_fifthPeriod = Param(nameof(FifthPeriod), 34).SetGreaterThanZero();
_openLevel = Param(nameof(OpenLevel), 0).SetNotNegative().SetDisplay("Open Level", "Minimum signal grade required to open.", "Signal");
_closeLevel = Param(nameof(CloseLevel), 1).SetNotNegative().SetDisplay("Close Level", "Opposite signal grade required to close.", "Signal");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType), (Security, HigherTimeframe1), (Security, HigherTimeframe2)];
protected override void OnReseted()
{
base.OnReseted();
_frames.Clear();
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartFrame("primary", CandleType, true);
StartFrame("higher1", HigherTimeframe1, false);
StartFrame("higher2", HigherTimeframe2, true);
}
private void StartFrame(string key, DataType candleType, bool useAc)
{
var frame = new FrameState([FirstPeriod, SecondPeriod, ThirdPeriod, FourthPeriod, FifthPeriod], useAc);
_frames[key] = frame;
var subscription = SubscribeCandles(candleType);
subscription.Bind(candle =>
{
if (candle.State != CandleStates.Finished)
return;
frame.Process(candle);
Evaluate();
}).Start();
var area = CreateChartArea();
if (area != null)
DrawCandles(area, subscription);
}
private void Evaluate()
{
if (_frames.Count != 3 || _frames.Values.Any(f => !f.IsReady))
return;
var bullishGrade = _frames.Values.Min(f => Grade(f.BullishScore));
var bearishGrade = _frames.Values.Min(f => Grade(f.BearishScore));
if (Position > 0)
{
if (bearishGrade > CloseLevel)
SellMarket(Math.Abs(Position));
return;
}
if (Position < 0)
{
if (bullishGrade > CloseLevel)
BuyMarket(Math.Abs(Position));
return;
}
if (bullishGrade > OpenLevel && bullishGrade > bearishGrade)
BuyMarket();
else if (bearishGrade > OpenLevel && bearishGrade > bullishGrade)
SellMarket();
}
private static int Grade(decimal score)
=> score > 75m ? 2 : score > 50m ? 1 : 0;
private sealed class FrameState(int[] periods, bool useAc)
{
private readonly int[] _periods = periods;
private readonly bool _useAc = useAc;
private readonly List<decimal> _closes = [];
private readonly List<decimal> _medians = [];
private readonly List<decimal> _ao = [];
private readonly List<decimal> _ac = [];
private decimal[] _previousMas;
public decimal BullishScore { get; private set; }
public decimal BearishScore { get; private set; }
public bool IsReady { get; private set; }
public void Process(ICandleMessage candle)
{
_closes.Add(candle.ClosePrice);
if (_useAc)
{
_medians.Add((candle.HighPrice + candle.LowPrice) / 2m);
if (_medians.Count >= 34)
{
var ao = AverageTail(_medians, 5) - AverageTail(_medians, 34);
_ao.Add(ao);
if (_ao.Count >= 5)
_ac.Add(ao - AverageTail(_ao, 5));
}
}
if (_closes.Count < _periods.Max())
return;
var mas = _periods.Select(p => AverageTail(_closes, p)).ToArray();
if (_previousMas is null)
{
_previousMas = mas;
return;
}
var bullishVotes = 0;
var bearishVotes = 0;
for (var i = 0; i < mas.Length; i++)
{
if (mas[i] > _previousMas[i])
bullishVotes++;
else if (mas[i] < _previousMas[i])
bearishVotes++;
}
if (_useAc && _ac.Count >= 4)
{
var last = _ac.Count - 1;
var bullishAc = _ac[last - 3] < _ac[last - 2] && _ac[last - 2] < _ac[last - 1] && _ac[last - 1] < _ac[last];
var bearishAc = _ac[last - 3] > _ac[last - 2] && _ac[last - 2] > _ac[last - 1] && _ac[last - 1] > _ac[last];
if (bullishAc)
bullishVotes++;
else if (bearishAc)
bearishVotes++;
}
var total = _useAc ? 6m : 5m;
BullishScore = bullishVotes / total * 100m;
BearishScore = bearishVotes / total * 100m;
IsReady = true;
_previousMas = mas;
var keep = Math.Max(_periods.Max(), 40);
if (_closes.Count > keep)
_closes.RemoveRange(0, _closes.Count - keep);
if (_medians.Count > 40)
_medians.RemoveRange(0, _medians.Count - 40);
if (_ao.Count > 10)
_ao.RemoveRange(0, _ao.Count - 10);
if (_ac.Count > 10)
_ac.RemoveRange(0, _ac.Count - 10);
}
private static decimal AverageTail(List<decimal> values, int count)
{
var start = values.Count - count;
var sum = 0m;
for (var i = start; i < values.Count; i++)
sum += values[i];
return sum / count;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
class five_ma_multi_timeframe_strategy(Strategy):
def __init__(self):
super(five_ma_multi_timeframe_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15)))
self._higher1 = self.Param("HigherTimeframe1", DataType.TimeFrame(TimeSpan.FromMinutes(60)))
self._higher2 = self.Param("HigherTimeframe2", DataType.TimeFrame(TimeSpan.FromMinutes(240)))
self._p1 = self.Param("FirstPeriod", 5).SetGreaterThanZero()
self._p2 = self.Param("SecondPeriod", 8).SetGreaterThanZero()
self._p3 = self.Param("ThirdPeriod", 13).SetGreaterThanZero()
self._p4 = self.Param("FourthPeriod", 21).SetGreaterThanZero()
self._p5 = self.Param("FifthPeriod", 34).SetGreaterThanZero()
self._open_level = self.Param("OpenLevel", 0).SetNotNegative()
self._close_level = self.Param("CloseLevel", 1).SetNotNegative()
self._frames = {}
def GetWorkingSecurities(self):
return [
(self.Security, self._candle_type.Value),
(self.Security, self._higher1.Value),
(self.Security, self._higher2.Value),
]
def OnReseted(self):
super(five_ma_multi_timeframe_strategy, self).OnReseted()
self._frames = {}
def OnStarted2(self, time):
super(five_ma_multi_timeframe_strategy, self).OnStarted2(time)
self._start_frame("primary", self._candle_type.Value, True)
self._start_frame("higher1", self._higher1.Value, False)
self._start_frame("higher2", self._higher2.Value, True)
def _start_frame(self, key, candle_type, use_ac):
periods = [int(self._p1.Value), int(self._p2.Value), int(self._p3.Value), int(self._p4.Value), int(self._p5.Value)]
state = {
"periods": periods, "use_ac": use_ac, "closes": [], "medians": [], "ao": [], "ac": [],
"previous_mas": None, "bull": 0.0, "bear": 0.0, "ready": False
}
self._frames[key] = state
def on_candle(candle):
if candle.State != CandleStates.Finished:
return
self._process_frame(state, candle)
self._evaluate()
self.SubscribeCandles(candle_type).Bind(on_candle).Start()
def _process_frame(self, state, candle):
closes = state["closes"]
medians = state["medians"]
ao_values = state["ao"]
ac_values = state["ac"]
use_ac = state["use_ac"]
closes.append(float(candle.ClosePrice))
if use_ac:
medians.append((float(candle.HighPrice) + float(candle.LowPrice)) / 2.0)
if len(medians) >= 34:
ao = self._avg_tail(medians, 5) - self._avg_tail(medians, 34)
ao_values.append(ao)
if len(ao_values) >= 5:
ac_values.append(ao - self._avg_tail(ao_values, 5))
if len(closes) < max(state["periods"]):
return
mas = [self._avg_tail(closes, p) for p in state["periods"]]
previous = state["previous_mas"]
if previous is None:
state["previous_mas"] = mas
return
bull = sum(1 for i in range(5) if mas[i] > previous[i])
bear = sum(1 for i in range(5) if mas[i] < previous[i])
if use_ac and len(ac_values) >= 4:
last4 = ac_values[-4:]
if last4[0] < last4[1] < last4[2] < last4[3]:
bull += 1
elif last4[0] > last4[1] > last4[2] > last4[3]:
bear += 1
total = 6.0 if use_ac else 5.0
state["bull"] = bull / total * 100.0
state["bear"] = bear / total * 100.0
state["ready"] = True
state["previous_mas"] = mas
keep = max(max(state["periods"]), 40)
if len(closes) > keep:
del closes[:-keep]
if len(medians) > 40:
del medians[:-40]
if len(ao_values) > 10:
del ao_values[:-10]
if len(ac_values) > 10:
del ac_values[:-10]
def _evaluate(self):
if len(self._frames) != 3 or not all(f["ready"] for f in self._frames.values()):
return
bull_grade = min(self._grade(f["bull"]) for f in self._frames.values())
bear_grade = min(self._grade(f["bear"]) for f in self._frames.values())
close_level = int(self._close_level.Value)
open_level = int(self._open_level.Value)
if self.Position > 0:
if bear_grade > close_level:
self.SellMarket(Math.Abs(self.Position))
return
if self.Position < 0:
if bull_grade > close_level:
self.BuyMarket(Math.Abs(self.Position))
return
if bull_grade > open_level and bull_grade > bear_grade:
self.BuyMarket()
elif bear_grade > open_level and bear_grade > bull_grade:
self.SellMarket()
@staticmethod
def _grade(score):
return 2 if score > 75.0 else (1 if score > 50.0 else 0)
@staticmethod
def _avg_tail(values, count):
return sum(values[-count:]) / float(count)
def CreateClone(self):
return five_ma_multi_timeframe_strategy()