大实体K线与RSI背离
该策略寻找实体大于前五根K线的蜡烛,并比较快慢RSI。信号按照蜡烛方向入场,风险管理使用延迟的追踪止损,只有当价格向有利方向移动指定tick后才启动。
当达到利润阈值后,追踪止损以固定距离跟随价格,同时初始固定止损在入场时即生效。
详情
- 入场条件:
- 多头:当前蜡烛实体大于前五根并收阳。
- 空头:当前蜡烛实体大于前五根并收阴。
- 方向:双向。
- 出场条件:初始止损或追踪止损触发。
- 止损:是,延迟追踪止损。
- 默认值:
TrailStartTicks= 200TrailDistanceTicks= 150InitialStopLossTicks= 200CandleType= TimeSpan.FromMinutes(5)
- 筛选:
- 类别:反转
- 方向:双向
- 指标:RSI
- 止损:是
- 复杂度:中等
- 时间框架:日内
- 季节性:否
- 神经网络:否
- 背离:是
- 风险等级:中等
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Big Candle RSI Divergence strategy.
/// When flat, a candle whose body is bigger than each of the previous five bodies opens a trade in its direction. An initial stop
/// InitialStopLossTicks price steps away protects the trade; once price has moved TrailStartTicks steps in profit a trailing stop
/// follows the best price at TrailDistanceTicks steps. Fast RSI(5) and slow RSI(14) are plotted for comparison.
/// </summary>
public class BigCandleRsiDivergenceStrategy : Strategy
{
private const int _bodyLookback = 5;
private readonly StrategyParam<int> _trailStartTicks;
private readonly StrategyParam<int> _trailDistanceTicks;
private readonly StrategyParam<int> _initialStopLossTicks;
private readonly StrategyParam<DataType> _candleType;
private readonly Queue<decimal> _bodies = new();
private decimal _entryPrice;
private decimal _bestPrice;
private bool _trailingActive;
/// <summary>
/// Profit in price steps that activates the trailing stop.
/// </summary>
public int TrailStartTicks
{
get => _trailStartTicks.Value;
set => _trailStartTicks.Value = value;
}
/// <summary>
/// Trailing stop distance in price steps.
/// </summary>
public int TrailDistanceTicks
{
get => _trailDistanceTicks.Value;
set => _trailDistanceTicks.Value = value;
}
/// <summary>
/// Initial stop loss in price steps.
/// </summary>
public int InitialStopLossTicks
{
get => _initialStopLossTicks.Value;
set => _initialStopLossTicks.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public BigCandleRsiDivergenceStrategy()
{
_trailStartTicks = Param(nameof(TrailStartTicks), 200)
.SetNotNegative()
.SetDisplay("Trail Start Ticks", "Profit in price steps that activates the trailing stop", "Risk");
_trailDistanceTicks = Param(nameof(TrailDistanceTicks), 150)
.SetNotNegative()
.SetDisplay("Trail Distance Ticks", "Trailing stop distance in price steps", "Risk");
_initialStopLossTicks = Param(nameof(InitialStopLossTicks), 200)
.SetNotNegative()
.SetDisplay("Initial Stop Loss Ticks", "Initial stop loss in price steps", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
var rsiFast = new RelativeStrengthIndex { Length = 5 };
var rsiSlow = new RelativeStrengthIndex { Length = 14 };
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(rsiFast, rsiSlow, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
var oscillators = CreateChartArea();
if (oscillators != null)
{
DrawIndicator(oscillators, rsiFast);
DrawIndicator(oscillators, rsiSlow);
}
}
}
private void ResetState()
{
_bodies.Clear();
_entryPrice = 0;
_bestPrice = 0;
_trailingActive = false;
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue rsiFastValue, IIndicatorValue rsiSlowValue)
{
if (candle.State != CandleStates.Finished)
return;
var body = Math.Abs(candle.ClosePrice - candle.OpenPrice);
var isBig = _bodies.Count == _bodyLookback && _bodies.All(b => body > b);
_bodies.Enqueue(body);
while (_bodies.Count > _bodyLookback)
_bodies.Dequeue();
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position != 0)
{
ManageStops(candle);
return;
}
if (!isBig)
return;
if (candle.ClosePrice > candle.OpenPrice)
{
BuyMarket(Volume);
StartTrade(candle.ClosePrice);
}
else if (candle.ClosePrice < candle.OpenPrice)
{
SellMarket(Volume);
StartTrade(candle.ClosePrice);
}
}
private void StartTrade(decimal price)
{
_entryPrice = price;
_bestPrice = price;
_trailingActive = false;
}
private void ManageStops(ICandleMessage candle)
{
if (_entryPrice <= 0)
return;
var step = Security?.PriceStep ?? 1m;
var initialStop = InitialStopLossTicks * step;
var trailStart = TrailStartTicks * step;
var trailDistance = TrailDistanceTicks * step;
if (Position > 0)
{
decimal? stop = InitialStopLossTicks > 0 ? _entryPrice - initialStop : null;
if (_trailingActive)
stop = Math.Max(stop ?? decimal.MinValue, _bestPrice - trailDistance);
if (stop is decimal s && candle.LowPrice <= s)
{
SellMarket(Position);
_entryPrice = 0;
return;
}
_bestPrice = Math.Max(_bestPrice, candle.HighPrice);
if (TrailDistanceTicks > 0 && _bestPrice - _entryPrice >= trailStart)
_trailingActive = true;
}
else if (Position < 0)
{
decimal? stop = InitialStopLossTicks > 0 ? _entryPrice + initialStop : null;
if (_trailingActive)
stop = Math.Min(stop ?? decimal.MaxValue, _bestPrice + trailDistance);
if (stop is decimal s && candle.HighPrice >= s)
{
BuyMarket(-Position);
_entryPrice = 0;
return;
}
_bestPrice = Math.Min(_bestPrice, candle.LowPrice);
if (TrailDistanceTicks > 0 && _entryPrice - _bestPrice >= trailStart)
_trailingActive = true;
}
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from collections import deque
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import RelativeStrengthIndex
from StockSharp.Algo.Strategies import Strategy
BODY_LOOKBACK = 5
class big_candle_rsi_divergence_strategy(Strategy):
"""
Big Candle RSI Divergence strategy.
When flat, a candle whose body is bigger than each of the previous five bodies opens a trade in its direction. An initial stop
InitialStopLossTicks price steps away protects the trade; once price has moved TrailStartTicks steps in profit a trailing stop
follows the best price at TrailDistanceTicks steps. Fast RSI(5) and slow RSI(14) are plotted for comparison.
"""
def __init__(self):
super(big_candle_rsi_divergence_strategy, self).__init__()
self._trail_start_ticks = self.Param("TrailStartTicks", 200).SetNotNegative().SetDisplay("Trail Start Ticks", "Profit in price steps that activates the trailing stop", "Risk")
self._trail_distance_ticks = self.Param("TrailDistanceTicks", 150).SetNotNegative().SetDisplay("Trail Distance Ticks", "Trailing stop distance in price steps", "Risk")
self._initial_stop_loss_ticks = self.Param("InitialStopLossTicks", 200).SetNotNegative().SetDisplay("Initial Stop Loss Ticks", "Initial stop loss in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Type of candles to use", "General")
self._reset_state()
@property
def candle_type(self):
return self._candle_type.Value
def _reset_state(self):
self._bodies = deque()
self._entry_price = Decimal(0)
self._best_price = Decimal(0)
self._trailing_active = False
def OnReseted(self):
super(big_candle_rsi_divergence_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(big_candle_rsi_divergence_strategy, self).OnStarted2(time)
self._reset_state()
rsi_fast = RelativeStrengthIndex()
rsi_fast.Length = 5
rsi_slow = RelativeStrengthIndex()
rsi_slow.Length = 14
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(rsi_fast, rsi_slow, self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
oscillators = self.CreateChartArea()
if oscillators is not None:
self.DrawIndicator(oscillators, rsi_fast)
self.DrawIndicator(oscillators, rsi_slow)
def _process_candle(self, candle, rsi_fast_value, rsi_slow_value):
if candle.State != CandleStates.Finished:
return
body = abs(candle.ClosePrice - candle.OpenPrice)
is_big = len(self._bodies) == BODY_LOOKBACK and all(body > b for b in self._bodies)
self._bodies.append(body)
while len(self._bodies) > BODY_LOOKBACK:
self._bodies.popleft()
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position != 0:
self._manage_stops(candle)
return
if not is_big:
return
if candle.ClosePrice > candle.OpenPrice:
self.BuyMarket(self.Volume)
self._start_trade(candle.ClosePrice)
elif candle.ClosePrice < candle.OpenPrice:
self.SellMarket(self.Volume)
self._start_trade(candle.ClosePrice)
def _start_trade(self, price):
self._entry_price = price
self._best_price = price
self._trailing_active = False
def _manage_stops(self, candle):
if self._entry_price <= 0:
return
step = self.Security.PriceStep if self.Security is not None and self.Security.PriceStep is not None else Decimal(1)
stop_ticks = self._initial_stop_loss_ticks.Value
trail_ticks = self._trail_distance_ticks.Value
initial_stop = Decimal(stop_ticks) * step
trail_start = Decimal(self._trail_start_ticks.Value) * step
trail_distance = Decimal(trail_ticks) * step
if self.Position > 0:
stop = self._entry_price - initial_stop if stop_ticks > 0 else None
if self._trailing_active:
trail = self._best_price - trail_distance
stop = trail if stop is None else max(stop, trail)
if stop is not None and candle.LowPrice <= stop:
self.SellMarket(self.Position)
self._entry_price = Decimal(0)
return
self._best_price = max(self._best_price, candle.HighPrice)
if trail_ticks > 0 and self._best_price - self._entry_price >= trail_start:
self._trailing_active = True
elif self.Position < 0:
stop = self._entry_price + initial_stop if stop_ticks > 0 else None
if self._trailing_active:
trail = self._best_price + trail_distance
stop = trail if stop is None else min(stop, trail)
if stop is not None and candle.HighPrice >= stop:
self.BuyMarket(-self.Position)
self._entry_price = Decimal(0)
return
self._best_price = min(self._best_price, candle.LowPrice)
if trail_ticks > 0 and self._entry_price - self._best_price >= trail_start:
self._trailing_active = True
def CreateClone(self):
return big_candle_rsi_divergence_strategy()