Стратегия Q2MA Cross торгует на пересечении сглаженных скользящих средних, построенных по ценам закрытия и открытия свечи. Длинная позиция открывается, когда средняя закрытия опускается ниже средней открытия после нахождения выше, короткая позиция открывается при обратном пересечении. Позиции закрываются при появлении противоположного тренда. Также используются уровни стоп-лосса и тейк-профита в тиках.
Детали
Критерий входа: пересечение скользящих средних по закрытию и открытию
Длин/Шорт: обе стороны
Критерий выхода: противоположное пересечение или стоп-лосс/тейк-профит
Стопы: да
Значения по умолчанию:
Length = 8
StopLoss = 1000
TakeProfit = 2000
CandleType = TimeSpan.FromHours(4).TimeFrame()
Volume = 1
BuyPosOpen = true
SellPosOpen = true
BuyPosClose = true
SellPosClose = true
Invert = false
Фильтры:
Категория: Тренд
Направление: Обе стороны
Индикаторы: Moving Average
Стопы: Да
Сложность: Средняя
Таймфрейм: H4
Сезонность: Нет
Нейросети: Нет
Дивергенция: Нет
Уровень риска: Средний
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>
/// Q2MA cross strategy based on open and close moving averages.
/// Buys when close MA crosses above open MA, sells on opposite.
/// </summary>
public class Q2maCrossStrategy : Strategy
{
private readonly StrategyParam<int> _length;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _closeMa;
private ExponentialMovingAverage _openMa;
private decimal? _prevUp;
private decimal? _prevDn;
public int Length { get => _length.Value; set => _length.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public Q2maCrossStrategy()
{
_length = Param(nameof(Length), 8)
.SetDisplay("Length", "Moving average length", "Indicator");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Indicator timeframe", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_closeMa = null;
_openMa = null;
_prevUp = null;
_prevDn = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevUp = null;
_prevDn = null;
_closeMa = new ExponentialMovingAverage { Length = Length };
_openMa = new ExponentialMovingAverage { Length = Length };
Indicators.Add(_closeMa);
Indicators.Add(_openMa);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var t = candle.ServerTime;
var upResult = _closeMa.Process(new DecimalIndicatorValue(_closeMa, candle.ClosePrice, t) { IsFinal = true });
var dnResult = _openMa.Process(new DecimalIndicatorValue(_openMa, candle.OpenPrice, t) { IsFinal = true });
if (!_closeMa.IsFormed || !_openMa.IsFormed)
return;
var up = upResult.GetValue<decimal>();
var dn = dnResult.GetValue<decimal>();
if (_prevUp is null || _prevDn is null)
{
_prevUp = up;
_prevDn = dn;
return;
}
// Close MA crosses above Open MA -> buy signal
if (_prevUp <= _prevDn && up > dn && Position <= 0)
BuyMarket();
// Close MA crosses below Open MA -> sell signal
else if (_prevUp >= _prevDn && up < dn && Position >= 0)
SellMarket();
_prevUp = up;
_prevDn = dn;
}
}
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
from indicator_extensions import *
class q2ma_cross_strategy(Strategy):
def __init__(self):
super(q2ma_cross_strategy, self).__init__()
self._length = self.Param("Length", 8) \
.SetDisplay("Length", "Moving average length", "Indicator")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Indicator timeframe", "General")
self._close_ma = None
self._open_ma = None
self._prev_up = None
self._prev_dn = None
@property
def length(self):
return self._length.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(q2ma_cross_strategy, self).OnReseted()
self._close_ma = None
self._open_ma = None
self._prev_up = None
self._prev_dn = None
def OnStarted2(self, time):
super(q2ma_cross_strategy, self).OnStarted2(time)
self._prev_up = None
self._prev_dn = None
self._close_ma = ExponentialMovingAverage()
self._close_ma.Length = self.length
self._open_ma = ExponentialMovingAverage()
self._open_ma.Length = self.length
self.Indicators.Add(self._close_ma)
self.Indicators.Add(self._open_ma)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
t = candle.ServerTime
up_result = process_float(self._close_ma, float(candle.ClosePrice), t, True)
dn_result = process_float(self._open_ma, float(candle.OpenPrice), t, True)
if not self._close_ma.IsFormed or not self._open_ma.IsFormed:
return
up = float(up_result)
dn = float(dn_result)
if self._prev_up is None or self._prev_dn is None:
self._prev_up = up
self._prev_dn = dn
return
if self._prev_up <= self._prev_dn and up > dn and self.Position <= 0:
self.BuyMarket()
elif self._prev_up >= self._prev_dn and up < dn and self.Position >= 0:
self.SellMarket()
self._prev_up = up
self._prev_dn = dn
def CreateClone(self):
return q2ma_cross_strategy()