Стратегия Quantum Sentiment Flux (новичок)
Стратегия открывает длинную позицию при пересечении быстрой EMA выше медленной, если их разница превышает порог на основе ATR. Короткая позиция открывается при обратном сигнале. Выход происходит при достижении стопа в ATR*множитель или тейк-профита в две величины ATR. Параметр задержки ограничивает частоту входов.
Параметры
- Тип свечей
- Длина быстрой EMA
- Длина медленной EMA
- Период ATR
- Множитель ATR
- Порог силы 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>
/// Quantum Sentiment Flux (Beginners).
/// Trades EMA crosses only when the separation is strong enough relative to ATR,
/// and manages the position with ATR-based stop/target distances.
/// </summary>
public class QuantumSentimentFluxBeginnersStrategy : Strategy
{
private readonly StrategyParam<int> _fastEmaPeriod;
private readonly StrategyParam<int> _slowEmaPeriod;
private readonly StrategyParam<int> _atrPeriod;
private readonly StrategyParam<decimal> _atrMultiplier;
private readonly StrategyParam<decimal> _maStrengthThreshold;
private readonly StrategyParam<int> _cooldownBars;
private readonly StrategyParam<decimal> _quantity;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevFast;
private decimal _prevSlow;
private decimal _entryPrice;
private decimal _entryAtr;
private int _cooldownRemaining;
public int FastEmaPeriod { get => _fastEmaPeriod.Value; set => _fastEmaPeriod.Value = value; }
public int SlowEmaPeriod { get => _slowEmaPeriod.Value; set => _slowEmaPeriod.Value = value; }
public int AtrPeriod { get => _atrPeriod.Value; set => _atrPeriod.Value = value; }
public decimal AtrMultiplier { get => _atrMultiplier.Value; set => _atrMultiplier.Value = value; }
public decimal MaStrengthThreshold { get => _maStrengthThreshold.Value; set => _maStrengthThreshold.Value = value; }
public int CooldownBars { get => _cooldownBars.Value; set => _cooldownBars.Value = value; }
public decimal Quantity { get => _quantity.Value; set => _quantity.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public QuantumSentimentFluxBeginnersStrategy()
{
_fastEmaPeriod = Param(nameof(FastEmaPeriod), 7)
.SetGreaterThanZero()
.SetDisplay("Fast EMA", "Fast EMA length.", "Indicators");
_slowEmaPeriod = Param(nameof(SlowEmaPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("Slow EMA", "Slow EMA length.", "Indicators");
_atrPeriod = Param(nameof(AtrPeriod), 14)
.SetGreaterThanZero()
.SetDisplay("ATR Length", "ATR length used for strength and protection.", "Indicators");
_atrMultiplier = Param(nameof(AtrMultiplier), 1m)
.SetGreaterThanZero()
.SetDisplay("ATR Multiplier", "ATR multiple used for the stop distance.", "Risk");
_maStrengthThreshold = Param(nameof(MaStrengthThreshold), 0.18m)
.SetNotNegative()
.SetDisplay("MA Strength Threshold", "Minimum EMA separation expressed as ATR multiples.", "Signals");
_cooldownBars = Param(nameof(CooldownBars), 5)
.SetNotNegative()
.SetDisplay("Cooldown Bars", "Finished bars to wait after a completed trade before a new entry.", "Signals");
_quantity = Param(nameof(Quantity), 1m)
.SetGreaterThanZero()
.SetDisplay("Quantity", "Order volume for entries.", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use.", "General");
}
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
protected override void OnReseted()
{
base.OnReseted();
_prevFast = 0m;
_prevSlow = 0m;
_entryPrice = 0m;
_entryAtr = 0m;
_cooldownRemaining = 0;
}
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var fast = new ExponentialMovingAverage { Length = FastEmaPeriod };
var slow = new ExponentialMovingAverage { Length = SlowEmaPeriod };
var atr = new AverageTrueRange { Length = AtrPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(fast, slow, atr, ProcessCandle).Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, fast);
DrawIndicator(area, slow);
DrawIndicator(area, atr);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow, decimal atr)
{
if (candle.State != CandleStates.Finished)
return;
if (_prevFast == 0m || _prevSlow == 0m)
{
_prevFast = fast;
_prevSlow = slow;
return;
}
// Protection is evaluated regardless of the entry cooldown.
if (Position > 0 && _entryPrice > 0m && _entryAtr > 0m)
{
var stopDistance = _entryAtr * AtrMultiplier;
if (candle.LowPrice <= _entryPrice - stopDistance ||
candle.HighPrice >= _entryPrice + stopDistance * 2m)
{
SellMarket(Math.Abs(Position));
ResetPositionState();
_cooldownRemaining = CooldownBars;
_prevFast = fast;
_prevSlow = slow;
return;
}
}
else if (Position < 0 && _entryPrice > 0m && _entryAtr > 0m)
{
var stopDistance = _entryAtr * AtrMultiplier;
if (candle.HighPrice >= _entryPrice + stopDistance ||
candle.LowPrice <= _entryPrice - stopDistance * 2m)
{
BuyMarket(Math.Abs(Position));
ResetPositionState();
_cooldownRemaining = CooldownBars;
_prevFast = fast;
_prevSlow = slow;
return;
}
}
if (_cooldownRemaining > 0)
_cooldownRemaining--;
var crossUp = _prevFast <= _prevSlow && fast > slow;
var crossDown = _prevFast >= _prevSlow && fast < slow;
if (_cooldownRemaining == 0 && Position == 0 && atr > 0m)
{
var threshold = atr * MaStrengthThreshold;
if (crossUp && fast - slow > threshold)
{
BuyMarket(Quantity);
_entryPrice = candle.ClosePrice;
_entryAtr = atr;
}
else if (crossDown && slow - fast > threshold)
{
SellMarket(Quantity);
_entryPrice = candle.ClosePrice;
_entryAtr = atr;
}
}
_prevFast = fast;
_prevSlow = slow;
}
private void ResetPositionState()
{
_entryPrice = 0m;
_entryAtr = 0m;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, AverageTrueRange
from StockSharp.Algo.Strategies import Strategy
class quantum_sentiment_flux_beginners_strategy(Strategy):
def __init__(self):
super(quantum_sentiment_flux_beginners_strategy, self).__init__()
self._fast_period = self.Param("FastEmaPeriod", 7).SetGreaterThanZero()
self._slow_period = self.Param("SlowEmaPeriod", 14).SetGreaterThanZero()
self._atr_period = self.Param("AtrPeriod", 14).SetGreaterThanZero()
self._atr_multiplier = self.Param("AtrMultiplier", 1.0).SetGreaterThanZero()
self._ma_strength_threshold = self.Param("MaStrengthThreshold", 0.18).SetNotNegative()
self._cooldown_bars = self.Param("CooldownBars", 5).SetNotNegative()
self._quantity = self.Param("Quantity", 1.0).SetGreaterThanZero()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(1)))
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._entry_atr = 0.0
self._cooldown_remaining = 0
@property
def CandleType(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.CandleType)]
def OnReseted(self):
super(quantum_sentiment_flux_beginners_strategy, self).OnReseted()
self._prev_fast = 0.0
self._prev_slow = 0.0
self._entry_price = 0.0
self._entry_atr = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(quantum_sentiment_flux_beginners_strategy, self).OnStarted2(time)
fast = ExponentialMovingAverage()
fast.Length = int(self._fast_period.Value)
slow = ExponentialMovingAverage()
slow.Length = int(self._slow_period.Value)
atr = AverageTrueRange()
atr.Length = int(self._atr_period.Value)
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(fast, slow, atr, self._process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawIndicator(area, atr)
self.DrawOwnTrades(area)
def _process(self, candle, fast_value, slow_value, atr_value):
if candle.State != CandleStates.Finished:
return
fast = float(fast_value)
slow = float(slow_value)
atr = float(atr_value)
if self._prev_fast == 0 or self._prev_slow == 0:
self._prev_fast = fast
self._prev_slow = slow
return
atr_multiplier = float(self._atr_multiplier.Value)
if self.Position > 0 and self._entry_price > 0 and self._entry_atr > 0:
distance = self._entry_atr * atr_multiplier
if float(candle.LowPrice) <= self._entry_price - distance or float(candle.HighPrice) >= self._entry_price + distance * 2:
self.SellMarket(Math.Abs(self.Position))
self._entry_price = 0.0
self._entry_atr = 0.0
self._cooldown_remaining = int(self._cooldown_bars.Value)
self._prev_fast = fast
self._prev_slow = slow
return
elif self.Position < 0 and self._entry_price > 0 and self._entry_atr > 0:
distance = self._entry_atr * atr_multiplier
if float(candle.HighPrice) >= self._entry_price + distance or float(candle.LowPrice) <= self._entry_price - distance * 2:
self.BuyMarket(Math.Abs(self.Position))
self._entry_price = 0.0
self._entry_atr = 0.0
self._cooldown_remaining = int(self._cooldown_bars.Value)
self._prev_fast = fast
self._prev_slow = slow
return
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
cross_up = self._prev_fast <= self._prev_slow and fast > slow
cross_down = self._prev_fast >= self._prev_slow and fast < slow
if self._cooldown_remaining == 0 and self.Position == 0 and atr > 0:
threshold = atr * float(self._ma_strength_threshold.Value)
qty = float(self._quantity.Value)
if cross_up and fast - slow > threshold:
self.BuyMarket(qty)
self._entry_price = float(candle.ClosePrice)
self._entry_atr = atr
elif cross_down and slow - fast > threshold:
self.SellMarket(qty)
self._entry_price = float(candle.ClosePrice)
self._entry_atr = atr
self._prev_fast = fast
self._prev_slow = slow
def CreateClone(self):
return quantum_sentiment_flux_beginners_strategy()