マルチ EMA クロスオーバー戦略
この戦略は、4つのEMAペアについて、速いEMAが遅いEMAを上回ってクロスするときに別々のロングポジションを建てます。各ポジションは速いEMAが遅いEMAを下回ったときに決済されます。
詳細
- エントリー条件:
- ロング: いずれかのペア(1/5、3/10、5/20、10/40)で速いEMAが遅いEMAを上抜けする。
- ロング/ショート: ロングのみ。
- エグジット条件:
- 対応するペアで速いEMAが遅いEMAを下回る。
- ストップ: なし。
- デフォルト値:
EMA1= 1EMA3= 3EMA5= 5EMA10= 10EMA20= 20EMA40= 40
- フィルター:
- カテゴリ: トレンドフォロー
- 方向: ロングのみ
- インジケーター: EMA
- ストップ: なし
- 複雑さ: 基本
- 時間軸: 任意
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 低
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>
/// Multi EMA crossover strategy.
/// Four EMA pairs (EMA1/EMA5, EMA3/EMA10, EMA5/EMA20, EMA10/EMA40) trade independently: each pair adds one long lot when its
/// fast EMA crosses above its slow EMA and removes that lot when the fast EMA falls below the slow EMA. Long only, no stops.
/// </summary>
public class MultiEmaCrossoverStrategy : Strategy
{
private readonly StrategyParam<int> _ema1;
private readonly StrategyParam<int> _ema3;
private readonly StrategyParam<int> _ema5;
private readonly StrategyParam<int> _ema10;
private readonly StrategyParam<int> _ema20;
private readonly StrategyParam<int> _ema40;
private readonly StrategyParam<DataType> _candleType;
private readonly decimal?[] _prevFast = new decimal?[4];
private readonly decimal?[] _prevSlow = new decimal?[4];
private readonly bool[] _isOpen = new bool[4];
/// <summary>
/// Period of EMA1 (fast EMA of the first pair).
/// </summary>
public int EMA1
{
get => _ema1.Value;
set => _ema1.Value = value;
}
/// <summary>
/// Period of EMA3 (fast EMA of the second pair).
/// </summary>
public int EMA3
{
get => _ema3.Value;
set => _ema3.Value = value;
}
/// <summary>
/// Period of EMA5 (slow EMA of the first pair, fast EMA of the third pair).
/// </summary>
public int EMA5
{
get => _ema5.Value;
set => _ema5.Value = value;
}
/// <summary>
/// Period of EMA10 (slow EMA of the second pair, fast EMA of the fourth pair).
/// </summary>
public int EMA10
{
get => _ema10.Value;
set => _ema10.Value = value;
}
/// <summary>
/// Period of EMA20 (slow EMA of the third pair).
/// </summary>
public int EMA20
{
get => _ema20.Value;
set => _ema20.Value = value;
}
/// <summary>
/// Period of EMA40 (slow EMA of the fourth pair).
/// </summary>
public int EMA40
{
get => _ema40.Value;
set => _ema40.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public MultiEmaCrossoverStrategy()
{
_ema1 = Param(nameof(EMA1), 1)
.SetGreaterThanZero()
.SetDisplay("EMA1", "Fast EMA of the 1/5 pair", "Indicators");
_ema3 = Param(nameof(EMA3), 3)
.SetGreaterThanZero()
.SetDisplay("EMA3", "Fast EMA of the 3/10 pair", "Indicators");
_ema5 = Param(nameof(EMA5), 5)
.SetGreaterThanZero()
.SetDisplay("EMA5", "Slow EMA of the 1/5 pair and fast EMA of the 5/20 pair", "Indicators");
_ema10 = Param(nameof(EMA10), 10)
.SetGreaterThanZero()
.SetDisplay("EMA10", "Slow EMA of the 3/10 pair and fast EMA of the 10/40 pair", "Indicators");
_ema20 = Param(nameof(EMA20), 20)
.SetGreaterThanZero()
.SetDisplay("EMA20", "Slow EMA of the 5/20 pair", "Indicators");
_ema40 = Param(nameof(EMA40), 40)
.SetGreaterThanZero()
.SetDisplay("EMA40", "Slow EMA of the 10/40 pair", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var ema1 = new ExponentialMovingAverage { Length = EMA1 };
var ema3 = new ExponentialMovingAverage { Length = EMA3 };
var ema5 = new ExponentialMovingAverage { Length = EMA5 };
var ema10 = new ExponentialMovingAverage { Length = EMA10 };
var ema20 = new ExponentialMovingAverage { Length = EMA20 };
var ema40 = new ExponentialMovingAverage { Length = EMA40 };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ema1, ema3, ema5, ema10, ema20, ema40, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, ema1);
DrawIndicator(area, ema3);
DrawIndicator(area, ema5);
DrawIndicator(area, ema10);
DrawIndicator(area, ema20);
DrawIndicator(area, ema40);
DrawOwnTrades(area);
}
}
private void ResetState()
{
Array.Clear(_prevFast);
Array.Clear(_prevSlow);
Array.Clear(_isOpen);
}
private void ProcessCandle(ICandleMessage candle, decimal ema1, decimal ema3, decimal ema5, decimal ema10, decimal ema20, decimal ema40)
{
if (candle.State != CandleStates.Finished)
return;
var fast = new[] { ema1, ema3, ema5, ema10 };
var slow = new[] { ema5, ema10, ema20, ema40 };
var canTrade = IsFormedAndOnlineAndAllowTrading();
for (var i = 0; i < 4; i++)
{
var prevFast = _prevFast[i];
var prevSlow = _prevSlow[i];
_prevFast[i] = fast[i];
_prevSlow[i] = slow[i];
if (!canTrade)
continue;
// Each pair owns one lot of the combined long position.
if (_isOpen[i])
{
if (fast[i] < slow[i])
{
SellMarket(Volume);
_isOpen[i] = false;
}
}
else if (prevFast is decimal pf && prevSlow is decimal ps && pf <= ps && fast[i] > slow[i])
{
BuyMarket(Volume);
_isOpen[i] = true;
}
}
}
}
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 multi_ema_crossover_strategy(Strategy):
"""
Multi EMA crossover strategy.
Four EMA pairs (EMA1/EMA5, EMA3/EMA10, EMA5/EMA20, EMA10/EMA40) trade independently: each pair adds one long lot when its
fast EMA crosses above its slow EMA and removes that lot when the fast EMA falls below the slow EMA. Long only, no stops.
"""
def __init__(self):
super(multi_ema_crossover_strategy, self).__init__()
self._ema1 = self.Param("EMA1", 1).SetGreaterThanZero().SetDisplay("EMA1", "Fast EMA of the 1/5 pair", "Indicators")
self._ema3 = self.Param("EMA3", 3).SetGreaterThanZero().SetDisplay("EMA3", "Fast EMA of the 3/10 pair", "Indicators")
self._ema5 = self.Param("EMA5", 5).SetGreaterThanZero().SetDisplay("EMA5", "Slow EMA of the 1/5 pair and fast EMA of the 5/20 pair", "Indicators")
self._ema10 = self.Param("EMA10", 10).SetGreaterThanZero().SetDisplay("EMA10", "Slow EMA of the 3/10 pair and fast EMA of the 10/40 pair", "Indicators")
self._ema20 = self.Param("EMA20", 20).SetGreaterThanZero().SetDisplay("EMA20", "Slow EMA of the 5/20 pair", "Indicators")
self._ema40 = self.Param("EMA40", 40).SetGreaterThanZero().SetDisplay("EMA40", "Slow EMA of the 10/40 pair", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._prev_fast = [None] * 4
self._prev_slow = [None] * 4
self._is_open = [False] * 4
def OnReseted(self):
super(multi_ema_crossover_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(multi_ema_crossover_strategy, self).OnStarted2(time)
self._reset_state()
emas = []
for p in (self._ema1, self._ema3, self._ema5, self._ema10, self._ema20, self._ema40):
ema = ExponentialMovingAverage()
ema.Length = p.Value
emas.append(ema)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(emas[0], emas[1], emas[2], emas[3], emas[4], emas[5], self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
for ema in emas:
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def _process_candle(self, candle, ema1, ema3, ema5, ema10, ema20, ema40):
if candle.State != CandleStates.Finished:
return
fast = (ema1, ema3, ema5, ema10)
slow = (ema5, ema10, ema20, ema40)
can_trade = self.IsFormedAndOnlineAndAllowTrading()
for i in range(4):
prev_fast = self._prev_fast[i]
prev_slow = self._prev_slow[i]
self._prev_fast[i] = fast[i]
self._prev_slow[i] = slow[i]
if not can_trade:
continue
# Each pair owns one lot of the combined long position.
if self._is_open[i]:
if fast[i] < slow[i]:
self.SellMarket(self.Volume)
self._is_open[i] = False
elif prev_fast is not None and prev_slow is not None and prev_fast <= prev_slow and fast[i] > slow[i]:
self.BuyMarket(self.Volume)
self._is_open[i] = True
def CreateClone(self):
return multi_ema_crossover_strategy()