Estrategia Quantum Sentiment Flux (Principiante)
Esta estrategia entra en largo cuando la EMA rápida cruza por encima de la EMA lenta y la diferencia entre ellas supera un umbral basado en ATR. Entra en corto con la señal opuesta. Las posiciones se cierran cuando el precio se mueve un múltiplo de ATR contra la operación o alcanza un objetivo de beneficio de dos múltiplos de ATR. Un período de enfriamiento limita la frecuencia de operaciones.
Parámetros
- Tipo de vela
- Longitud de EMA rápida
- Longitud de EMA lenta
- Período ATR
- Multiplicador ATR
- Umbral de fuerza de MA
- Barras de enfriamiento
- Cantidad
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()