Стратегия Vela Superada
Стратегия Vela Superada торгует разворотный паттерн из двух свечей. Бычий сигнал появляется, когда за медвежьей свечой следует бычья, закрывающаяся выше открытия предыдущей. Для фильтрации используются краткосрочная EMA, RSI и MACD, что помогает избегать сигналов против тренда. Возможна работа как в длинную, так и в короткую сторону.
Стратегия использует процентные уровни тейк-профита и стоп-лосса и динамически подтягивает трейлинг-стоп по мере движения цены в прибыль. Это позволяет удерживать длительные движения и одновременно защищает от разворота.
Подробности
- Условия входа:
- Лонг: предыдущая свеча медвежья, текущая бычья, закрытие и предыдущее закрытие выше EMA, RSI < 65, MACD растёт.
- Шорт: предыдущая свеча бычья, текущая медвежья, закрытие и предыдущее закрытие ниже EMA, RSI > 35, MACD падает.
- Направление: настраиваемое (по умолчанию только лонг).
- Условия выхода:
- Трейлинг-стоп или противоположный сигнал.
- Стопы: процентный стоп и тейк-профит.
- Параметры по умолчанию:
EmaLength= 10RsiLength= 14ShowLong= TrueShowShort= FalseTpPercent= 1.2SlPercent= 1.8
- Фильтры:
- Категория: Паттерн + индикаторы
- Направление: Обе
- Индикаторы: EMA, RSI, MACD
- Стопы: Да
- Сложность: Средняя
- Таймфрейм: Любой
- Сезонность: Нет
- Нейросети: Нет
- Дивергенция: Да
- Уровень риска: Средний
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Vela Superada Strategy.
/// Trades on candle pattern reversals with EMA, RSI and MACD filters.
/// Buys on bullish reversal pattern above EMA with rising MACD.
/// Sells on bearish reversal pattern below EMA with falling MACD.
/// </summary>
public class VelaSuperadaStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _emaLength;
private readonly StrategyParam<int> _rsiLength;
private readonly StrategyParam<int> _cooldownBars;
private ExponentialMovingAverage _ema;
private RelativeStrengthIndex _rsi;
private MovingAverageConvergenceDivergence _macd;
private decimal _prevClose;
private decimal _prevOpen;
private decimal _prevMacd;
private int _cooldownRemaining;
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public int EmaLength
{
get => _emaLength.Value;
set => _emaLength.Value = value;
}
public int RsiLength
{
get => _rsiLength.Value;
set => _rsiLength.Value = value;
}
public int CooldownBars
{
get => _cooldownBars.Value;
set => _cooldownBars.Value = value;
}
public VelaSuperadaStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
_emaLength = Param(nameof(EmaLength), 10)
.SetGreaterThanZero()
.SetDisplay("EMA Length", "EMA period", "Moving Averages");
_rsiLength = Param(nameof(RsiLength), 14)
.SetGreaterThanZero()
.SetDisplay("RSI Length", "RSI period", "RSI");
_cooldownBars = Param(nameof(CooldownBars), 10)
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_ema = null;
_rsi = null;
_macd = null;
_prevClose = 0;
_prevOpen = 0;
_prevMacd = 0;
_cooldownRemaining = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_ema = new ExponentialMovingAverage { Length = EmaLength };
_rsi = new RelativeStrengthIndex { Length = RsiLength };
_macd = new MovingAverageConvergenceDivergence();
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_ema, _rsi, _macd, OnProcess)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _ema);
DrawOwnTrades(area);
}
}
private void OnProcess(ICandleMessage candle, decimal emaVal, decimal rsiVal, decimal macdVal)
{
if (candle.State != CandleStates.Finished)
return;
if (!_ema.IsFormed || !_rsi.IsFormed || !_macd.IsFormed)
{
_prevClose = candle.ClosePrice;
_prevOpen = candle.OpenPrice;
_prevMacd = macdVal;
return;
}
if (!IsFormedAndOnlineAndAllowTrading())
{
_prevClose = candle.ClosePrice;
_prevOpen = candle.OpenPrice;
_prevMacd = macdVal;
return;
}
if (_cooldownRemaining > 0)
{
_cooldownRemaining--;
_prevClose = candle.ClosePrice;
_prevOpen = candle.OpenPrice;
_prevMacd = macdVal;
return;
}
if (_prevClose == 0)
{
_prevClose = candle.ClosePrice;
_prevOpen = candle.OpenPrice;
_prevMacd = macdVal;
return;
}
// Candle pattern detection
var bullishReversal = _prevClose < _prevOpen && candle.ClosePrice > candle.OpenPrice; // Red->Green
var bearishReversal = _prevClose > _prevOpen && candle.ClosePrice < candle.OpenPrice; // Green->Red
// MACD momentum
var macdRising = macdVal > _prevMacd;
var macdFalling = macdVal < _prevMacd;
// Buy: bullish reversal + above EMA + RSI not overbought + MACD rising
if (bullishReversal && candle.ClosePrice > emaVal && rsiVal < 65 && macdRising && Position <= 0)
{
if (Position < 0)
BuyMarket(Math.Abs(Position));
BuyMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Sell: bearish reversal + below EMA + RSI not oversold + MACD falling
else if (bearishReversal && candle.ClosePrice < emaVal && rsiVal > 35 && macdFalling && Position >= 0)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
SellMarket(Volume);
_cooldownRemaining = CooldownBars;
}
// Exit long: bearish reversal below EMA
else if (Position > 0 && bearishReversal && candle.ClosePrice < emaVal)
{
SellMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
// Exit short: bullish reversal above EMA
else if (Position < 0 && bullishReversal && candle.ClosePrice > emaVal)
{
BuyMarket(Math.Abs(Position));
_cooldownRemaining = CooldownBars;
}
_prevClose = candle.ClosePrice;
_prevOpen = candle.OpenPrice;
_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, Math
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage, RelativeStrengthIndex, MovingAverageConvergenceDivergence
from StockSharp.Algo.Strategies import Strategy
class vela_superada_strategy(Strategy):
"""Vela Superada Strategy."""
def __init__(self):
super(vela_superada_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._ema_length = self.Param("EmaLength", 10) \
.SetDisplay("EMA Length", "EMA period", "Moving Averages")
self._rsi_length = self.Param("RsiLength", 14) \
.SetDisplay("RSI Length", "RSI period", "RSI")
self._cooldown_bars = self.Param("CooldownBars", 10) \
.SetDisplay("Cooldown Bars", "Bars to wait between trades", "Risk")
self._ema = None
self._rsi = None
self._macd = None
self._prev_close = 0.0
self._prev_open = 0.0
self._prev_macd = 0.0
self._cooldown_remaining = 0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(vela_superada_strategy, self).OnReseted()
self._ema = None
self._rsi = None
self._macd = None
self._prev_close = 0.0
self._prev_open = 0.0
self._prev_macd = 0.0
self._cooldown_remaining = 0
def OnStarted2(self, time):
super(vela_superada_strategy, self).OnStarted2(time)
self._ema = ExponentialMovingAverage()
self._ema.Length = int(self._ema_length.Value)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = int(self._rsi_length.Value)
self._macd = MovingAverageConvergenceDivergence()
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._ema, self._rsi, self._macd, self._on_process).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._ema)
self.DrawOwnTrades(area)
def _on_process(self, candle, ema_val, rsi_val, macd_val):
if candle.State != CandleStates.Finished:
return
if not self._ema.IsFormed or not self._rsi.IsFormed or not self._macd.IsFormed:
self._prev_close = float(candle.ClosePrice)
self._prev_open = float(candle.OpenPrice)
self._prev_macd = float(macd_val)
return
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_close = float(candle.ClosePrice)
self._prev_open = float(candle.OpenPrice)
self._prev_macd = float(macd_val)
return
close = float(candle.ClosePrice)
open_price = float(candle.OpenPrice)
ema = float(ema_val)
rsi = float(rsi_val)
macd = float(macd_val)
if self._cooldown_remaining > 0:
self._cooldown_remaining -= 1
self._prev_close = close
self._prev_open = open_price
self._prev_macd = macd
return
if self._prev_close == 0.0:
self._prev_close = close
self._prev_open = open_price
self._prev_macd = macd
return
cooldown = int(self._cooldown_bars.Value)
bullish_reversal = self._prev_close < self._prev_open and close > open_price
bearish_reversal = self._prev_close > self._prev_open and close < open_price
macd_rising = macd > self._prev_macd
macd_falling = macd < self._prev_macd
if bullish_reversal and close > ema and rsi < 65 and macd_rising and self.Position <= 0:
if self.Position < 0:
self.BuyMarket(Math.Abs(self.Position))
self.BuyMarket(self.Volume)
self._cooldown_remaining = cooldown
elif bearish_reversal and close < ema and rsi > 35 and macd_falling and self.Position >= 0:
if self.Position > 0:
self.SellMarket(Math.Abs(self.Position))
self.SellMarket(self.Volume)
self._cooldown_remaining = cooldown
elif self.Position > 0 and bearish_reversal and close < ema:
self.SellMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
elif self.Position < 0 and bullish_reversal and close > ema:
self.BuyMarket(Math.Abs(self.Position))
self._cooldown_remaining = cooldown
self._prev_close = close
self._prev_open = open_price
self._prev_macd = macd
def CreateClone(self):
return vela_superada_strategy()