Стратегия Puria
Puria — трендовая стратегия, сочетающая быструю EMA и две медленные LWMA по минимальным ценам, а также фильтр MACD. Длинная позиция открывается, когда EMA(5) находится выше LWMA(75) и LWMA(85), предыдущая цена закрытия выше EMA, а линия MACD положительная. Короткая позиция открывается при обратных условиях. Стратегия использует фиксированные уровни тейк‑профита и стоп‑лосса и допускает только одну позицию в каждом направлении до появления противоположного сигнала.
Подробности
- Условия входа: EMA(5) выше LWMA(75) и LWMA(85), предыдущая цена закрытия выше EMA, MACD(15,26) > 0 для покупок; обратное для продаж.
- Направление: Лонг и шорт.
- Условия выхода: Стоп‑лосс или тейк‑профит.
- Стопы: Фиксированные расстояния стоп‑лосса и тейк‑профита в пунктах цены.
- Параметры по умолчанию:
StopLoss= 14TakeProfit= 15Ma1Period= 75Ma2Period= 85Ma3Period= 5CandleType= таймфрейм 1 минута
- Фильтры: Фильтр по нулевой линии MACD.
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>
/// Puria strategy based on three moving averages and MACD filter.
/// Enters long when fast EMA is above two slow EMAs and MACD is positive.
/// Enters short when fast EMA is below two slow EMAs and MACD is negative.
/// </summary>
public class PuriaStrategy : Strategy
{
private readonly StrategyParam<int> _ma1Period;
private readonly StrategyParam<int> _ma2Period;
private readonly StrategyParam<int> _ma3Period;
private readonly StrategyParam<decimal> _stopLossPct;
private readonly StrategyParam<decimal> _takeProfitPct;
private readonly StrategyParam<DataType> _candleType;
private MovingAverageConvergenceDivergence _macd;
private decimal _prevMa75;
private decimal _prevMa85;
private decimal _prevMa5;
private decimal _prevClose;
private decimal _prevMacd;
private bool _initialized;
public int Ma1Period { get => _ma1Period.Value; set => _ma1Period.Value = value; }
public int Ma2Period { get => _ma2Period.Value; set => _ma2Period.Value = value; }
public int Ma3Period { get => _ma3Period.Value; set => _ma3Period.Value = value; }
public decimal StopLossPct { get => _stopLossPct.Value; set => _stopLossPct.Value = value; }
public decimal TakeProfitPct { get => _takeProfitPct.Value; set => _takeProfitPct.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public PuriaStrategy()
{
_ma1Period = Param(nameof(Ma1Period), 30)
.SetGreaterThanZero()
.SetDisplay("MA1 Period", "Slow EMA period", "Moving Averages");
_ma2Period = Param(nameof(Ma2Period), 40)
.SetGreaterThanZero()
.SetDisplay("MA2 Period", "Second slow EMA period", "Moving Averages");
_ma3Period = Param(nameof(Ma3Period), 5)
.SetGreaterThanZero()
.SetDisplay("MA3 Period", "Fast EMA period", "Moving Averages");
_stopLossPct = Param(nameof(StopLossPct), 2m)
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk");
_takeProfitPct = Param(nameof(TakeProfitPct), 3m)
.SetDisplay("Take Profit %", "Take profit percentage", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for strategy", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_macd = default;
_prevMa75 = default;
_prevMa85 = default;
_prevMa5 = default;
_prevClose = default;
_prevMacd = default;
_initialized = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var ma75 = new ExponentialMovingAverage { Length = Ma1Period };
var ma85 = new ExponentialMovingAverage { Length = Ma2Period };
var ma5 = new ExponentialMovingAverage { Length = Ma3Period };
_macd = new MovingAverageConvergenceDivergence
{
ShortMa = { Length = 15 },
LongMa = { Length = 26 }
};
Indicators.Add(_macd);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ma75, ma85, ma5, ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(TakeProfitPct, UnitTypes.Percent),
stopLoss: new Unit(StopLossPct, UnitTypes.Percent),
useMarketOrders: true);
}
private void ProcessCandle(ICandleMessage candle, decimal ma75Value, decimal ma85Value, decimal ma5Value)
{
if (candle.State != CandleStates.Finished)
return;
// Process MACD manually
var macdResult = _macd.Process(candle.ClosePrice, candle.OpenTime, true);
if (!macdResult.IsFormed)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var macdVal = macdResult.ToDecimal();
if (!_initialized)
{
_prevMa75 = ma75Value;
_prevMa85 = ma85Value;
_prevMa5 = ma5Value;
_prevClose = candle.ClosePrice;
_prevMacd = macdVal;
_initialized = true;
return;
}
var buySignal = _prevMa5 > _prevMa75 && _prevMa5 > _prevMa85 && _prevClose > _prevMa5 && _prevMacd > 0m;
var sellSignal = _prevMa5 < _prevMa75 && _prevMa5 < _prevMa85 && _prevClose < _prevMa5 && _prevMacd < 0m;
if (buySignal && Position <= 0)
{
if (Position < 0) BuyMarket();
BuyMarket();
}
else if (sellSignal && Position >= 0)
{
if (Position > 0) SellMarket();
SellMarket();
}
_prevMa75 = ma75Value;
_prevMa85 = ma85Value;
_prevMa5 = ma5Value;
_prevClose = candle.ClosePrice;
_prevMacd = macdVal;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage, MovingAverageConvergenceDivergence
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class puria_strategy(Strategy):
def __init__(self):
super(puria_strategy, self).__init__()
self._ma1_period = self.Param("Ma1Period", 30) \
.SetDisplay("MA1 Period", "Slow EMA period", "Moving Averages")
self._ma2_period = self.Param("Ma2Period", 40) \
.SetDisplay("MA2 Period", "Second slow EMA period", "Moving Averages")
self._ma3_period = self.Param("Ma3Period", 5) \
.SetDisplay("MA3 Period", "Fast EMA period", "Moving Averages")
self._stop_loss_pct = self.Param("StopLossPct", 2.0) \
.SetDisplay("Stop Loss %", "Stop loss percentage", "Risk")
self._take_profit_pct = self.Param("TakeProfitPct", 3.0) \
.SetDisplay("Take Profit %", "Take profit percentage", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for strategy", "General")
self._macd = None
self._prev_ma75 = 0.0
self._prev_ma85 = 0.0
self._prev_ma5 = 0.0
self._prev_close = 0.0
self._prev_macd = 0.0
self._initialized = False
@property
def Ma1Period(self):
return self._ma1_period.Value
@Ma1Period.setter
def Ma1Period(self, value):
self._ma1_period.Value = value
@property
def Ma2Period(self):
return self._ma2_period.Value
@Ma2Period.setter
def Ma2Period(self, value):
self._ma2_period.Value = value
@property
def Ma3Period(self):
return self._ma3_period.Value
@Ma3Period.setter
def Ma3Period(self, value):
self._ma3_period.Value = value
@property
def StopLossPct(self):
return self._stop_loss_pct.Value
@StopLossPct.setter
def StopLossPct(self, value):
self._stop_loss_pct.Value = value
@property
def TakeProfitPct(self):
return self._take_profit_pct.Value
@TakeProfitPct.setter
def TakeProfitPct(self, value):
self._take_profit_pct.Value = value
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
def OnStarted2(self, time):
super(puria_strategy, self).OnStarted2(time)
self._initialized = False
ma75 = ExponentialMovingAverage()
ma75.Length = self.Ma1Period
ma85 = ExponentialMovingAverage()
ma85.Length = self.Ma2Period
ma5 = ExponentialMovingAverage()
ma5.Length = self.Ma3Period
self._macd = MovingAverageConvergenceDivergence()
self._macd.ShortMa.Length = 15
self._macd.LongMa.Length = 26
self.Indicators.Add(self._macd)
self.SubscribeCandles(self.CandleType) \
.Bind(ma75, ma85, ma5, self.ProcessCandle) \
.Start()
self.StartProtection(
takeProfit=Unit(float(self.TakeProfitPct), UnitTypes.Percent),
stopLoss=Unit(float(self.StopLossPct), UnitTypes.Percent),
useMarketOrders=True
)
def ProcessCandle(self, candle, ma75_val, ma85_val, ma5_val):
if candle.State != CandleStates.Finished:
return
t = candle.OpenTime
macd_result = process_float(self._macd, candle.ClosePrice, t, True)
if not macd_result.IsFormed:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
close = float(candle.ClosePrice)
ma75_f = float(ma75_val)
ma85_f = float(ma85_val)
ma5_f = float(ma5_val)
macd_val = float(macd_result)
if not self._initialized:
self._prev_ma75 = ma75_f
self._prev_ma85 = ma85_f
self._prev_ma5 = ma5_f
self._prev_close = close
self._prev_macd = macd_val
self._initialized = True
return
buy_signal = (self._prev_ma5 > self._prev_ma75 and
self._prev_ma5 > self._prev_ma85 and
self._prev_close > self._prev_ma5 and
self._prev_macd > 0)
sell_signal = (self._prev_ma5 < self._prev_ma75 and
self._prev_ma5 < self._prev_ma85 and
self._prev_close < self._prev_ma5 and
self._prev_macd < 0)
if buy_signal and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif sell_signal and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_ma75 = ma75_f
self._prev_ma85 = ma85_f
self._prev_ma5 = ma5_f
self._prev_close = close
self._prev_macd = macd_val
def OnReseted(self):
super(puria_strategy, self).OnReseted()
self._macd = None
self._prev_ma75 = 0.0
self._prev_ma85 = 0.0
self._prev_ma5 = 0.0
self._prev_close = 0.0
self._prev_macd = 0.0
self._initialized = False
def CreateClone(self):
return puria_strategy()