JBrainUltraRSI 策略
该示例策略结合相对强弱指数 (RSI) 与 随机振荡指标(Stochastic) 来生成交易信号。 原始的 MetaTrader 专家顾问使用 JBrainTrendSig1 与 UltraRSI 指标。本改写版本中使用随机振荡指标作为趋势过滤器,RSI 用于给出入场信号。
工作原理
- 指标
- RSI:比较近期上涨和下跌幅度。当 RSI 向上穿过 50 表示看涨动能,向下穿过 50 表示看跌动能。
- 随机振荡指标:比较收盘价与近期区间的位置。%K 与 %D 线的交叉确认趋势方向。
- 模式
- JBrainSig1Filter – RSI 产生信号,随机振荡指标确认方向。
- UltraRsiFilter – 随机振荡指标产生信号,RSI 进行过滤。
- Composition – 只有当两个指标方向一致时才开仓。
- 交易规则
- 出现买入信号且没有空头头寸时开多头。
- 出现卖出信号且没有多头头寸时开空头。
- 反向信号在允许的情况下关闭已有头寸。
参数
| 参数 | 说明 |
|---|---|
RsiPeriod |
RSI 计算周期 |
StochLength |
随机指标 %K 周期 |
SignalLength |
随机指标 %D 周期 |
Mode |
指标组合模式 |
AllowLongEntry / AllowShortEntry |
是否允许开多/开空 |
AllowLongExit / AllowShortExit |
是否允许平多/平空 |
CandleType |
使用的K线周期 |
说明
- 策略使用 StockSharp 高级 API,通过
Bind/BindEx处理指标。 - 可以通过
StartProtection()方法设置止盈止损保护。 - 若图表区域可用,策略会绘制K线、指标以及自身交易。
using System;
using System.Linq;
using System.Collections.Generic;
using Ecng.Common;
using Ecng.Collections;
using Ecng.Serialization;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Strategy combining RSI and Stochastic oscillator signals.
/// </summary>
public class JBrainUltraRsiStrategy : Strategy
{
/// <summary>
/// Combination modes for indicators.
/// </summary>
public enum AlgorithmModes
{
JBrainSig1Filter,
UltraRsiFilter,
Composition
}
private readonly StrategyParam<int> _rsiPeriod;
private readonly StrategyParam<int> _stochLength;
private readonly StrategyParam<int> _signalLength;
private readonly StrategyParam<AlgorithmModes> _mode;
private readonly StrategyParam<bool> _allowLongEntry;
private readonly StrategyParam<bool> _allowShortEntry;
private readonly StrategyParam<bool> _allowLongExit;
private readonly StrategyParam<bool> _allowShortExit;
private readonly StrategyParam<DataType> _candleType;
private RelativeStrengthIndex _rsi;
private StochasticOscillator _stochastic;
private decimal? _prevRsi;
private decimal? _prevK;
private decimal? _prevD;
/// <summary>
/// RSI calculation period.
/// </summary>
public int RsiPeriod
{
get => _rsiPeriod.Value;
set => _rsiPeriod.Value = value;
}
/// <summary>
/// Stochastic %K period.
/// </summary>
public int StochLength
{
get => _stochLength.Value;
set => _stochLength.Value = value;
}
/// <summary>
/// Stochastic %D period.
/// </summary>
public int SignalLength
{
get => _signalLength.Value;
set => _signalLength.Value = value;
}
/// <summary>
/// Mode combining indicators.
/// </summary>
public AlgorithmModes Mode
{
get => _mode.Value;
set => _mode.Value = value;
}
/// <summary>
/// Permission to open long positions.
/// </summary>
public bool AllowLongEntry
{
get => _allowLongEntry.Value;
set => _allowLongEntry.Value = value;
}
/// <summary>
/// Permission to open short positions.
/// </summary>
public bool AllowShortEntry
{
get => _allowShortEntry.Value;
set => _allowShortEntry.Value = value;
}
/// <summary>
/// Permission to close long positions.
/// </summary>
public bool AllowLongExit
{
get => _allowLongExit.Value;
set => _allowLongExit.Value = value;
}
/// <summary>
/// Permission to close short positions.
/// </summary>
public bool AllowShortExit
{
get => _allowShortExit.Value;
set => _allowShortExit.Value = value;
}
/// <summary>
/// Candle type to process.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="JBrainUltraRsiStrategy"/>.
/// </summary>
public JBrainUltraRsiStrategy()
{
_rsiPeriod = Param(nameof(RsiPeriod), 13)
.SetGreaterThanZero()
.SetDisplay("RSI Period", "RSI calculation period", "Indicators")
;
_stochLength = Param(nameof(StochLength), 9)
.SetGreaterThanZero()
.SetDisplay("Stochastic %K", "Period for %K line", "Indicators")
;
_signalLength = Param(nameof(SignalLength), 3)
.SetGreaterThanZero()
.SetDisplay("Stochastic %D", "Period for %D line", "Indicators")
;
_mode = Param(nameof(Mode), AlgorithmModes.Composition)
.SetDisplay("Mode", "Algorithm to enter the market", "General");
_allowLongEntry = Param(nameof(AllowLongEntry), true)
.SetDisplay("Allow Long Entry", "Permission to open long positions", "Trading");
_allowShortEntry = Param(nameof(AllowShortEntry), true)
.SetDisplay("Allow Short Entry", "Permission to open short positions", "Trading");
_allowLongExit = Param(nameof(AllowLongExit), true)
.SetDisplay("Allow Long Exit", "Permission to close long positions", "Trading");
_allowShortExit = Param(nameof(AllowShortExit), true)
.SetDisplay("Allow Short Exit", "Permission to close short positions", "Trading");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Type of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_rsi = default;
_stochastic = default;
_prevRsi = default;
_prevK = default;
_prevD = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
_rsi = new RelativeStrengthIndex { Length = RsiPeriod };
_stochastic = new StochasticOscillator
{
K = { Length = StochLength },
D = { Length = SignalLength },
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(_stochastic, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _rsi);
DrawIndicator(area, _stochastic);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, IIndicatorValue stochValue)
{
if (candle.State != CandleStates.Finished)
return;
// process RSI manually
var rsiResult = _rsi.Process(candle.ClosePrice, candle.OpenTime, true);
if (!rsiResult.IsFormed)
return;
var rsi = rsiResult.ToDecimal();
var stoch = (StochasticOscillatorValue)stochValue;
if (stoch.K is not decimal k || stoch.D is not decimal d)
{
_prevRsi = rsi;
return;
}
var rsiUp = _prevRsi is decimal pr && pr <= 50m && rsi > 50m;
var rsiDown = _prevRsi is decimal pr2 && pr2 >= 50m && rsi < 50m;
var stochUp = _prevK is decimal pk && _prevD is decimal pd && pk <= pd && k > d;
var stochDown = _prevK is decimal pk2 && _prevD is decimal pd2 && pk2 >= pd2 && k < d;
var buySignal = false;
var sellSignal = false;
switch (Mode)
{
case AlgorithmModes.JBrainSig1Filter:
buySignal = rsiUp && k > d;
sellSignal = rsiDown && k < d;
break;
case AlgorithmModes.UltraRsiFilter:
buySignal = stochUp && rsi > 50m;
sellSignal = stochDown && rsi < 50m;
break;
case AlgorithmModes.Composition:
buySignal = rsiUp && stochUp;
sellSignal = rsiDown && stochDown;
break;
}
if (buySignal)
{
if (Position < 0 && AllowShortExit)
BuyMarket();
if (AllowLongEntry && Position <= 0)
BuyMarket();
}
else if (sellSignal)
{
if (Position > 0 && AllowLongExit)
SellMarket();
if (AllowShortEntry && Position >= 0)
SellMarket();
}
_prevRsi = rsi;
_prevK = k;
_prevD = d;
}
}
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
from StockSharp.Algo.Indicators import RelativeStrengthIndex, StochasticOscillator
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class j_brain_ultra_rsi_strategy(Strategy):
# Algorithm modes
JBRAIN_SIG1_FILTER = 0
ULTRA_RSI_FILTER = 1
COMPOSITION = 2
def __init__(self):
super(j_brain_ultra_rsi_strategy, self).__init__()
self._rsi_period = self.Param("RsiPeriod", 13) \
.SetGreaterThanZero() \
.SetDisplay("RSI Period", "RSI calculation period", "Indicators")
self._stoch_length = self.Param("StochLength", 9) \
.SetGreaterThanZero() \
.SetDisplay("Stochastic %K", "Period for %K line", "Indicators")
self._signal_length = self.Param("SignalLength", 3) \
.SetGreaterThanZero() \
.SetDisplay("Stochastic %D", "Period for %D line", "Indicators")
self._mode = self.Param("Mode", 2) \
.SetDisplay("Mode", "Algorithm to enter the market", "General")
self._allow_long_entry = self.Param("AllowLongEntry", True) \
.SetDisplay("Allow Long Entry", "Permission to open long positions", "Trading")
self._allow_short_entry = self.Param("AllowShortEntry", True) \
.SetDisplay("Allow Short Entry", "Permission to open short positions", "Trading")
self._allow_long_exit = self.Param("AllowLongExit", True) \
.SetDisplay("Allow Long Exit", "Permission to close long positions", "Trading")
self._allow_short_exit = self.Param("AllowShortExit", True) \
.SetDisplay("Allow Short Exit", "Permission to close short positions", "Trading")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._rsi = None
self._prev_rsi = None
self._prev_k = None
self._prev_d = None
@property
def rsi_period(self):
return self._rsi_period.Value
@property
def stoch_length(self):
return self._stoch_length.Value
@property
def signal_length(self):
return self._signal_length.Value
@property
def mode(self):
return self._mode.Value
@property
def allow_long_entry(self):
return self._allow_long_entry.Value
@property
def allow_short_entry(self):
return self._allow_short_entry.Value
@property
def allow_long_exit(self):
return self._allow_long_exit.Value
@property
def allow_short_exit(self):
return self._allow_short_exit.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(j_brain_ultra_rsi_strategy, self).OnReseted()
self._rsi = None
self._prev_rsi = None
self._prev_k = None
self._prev_d = None
def OnStarted2(self, time):
super(j_brain_ultra_rsi_strategy, self).OnStarted2(time)
self.StartProtection(None, None)
self._rsi = RelativeStrengthIndex()
self._rsi.Length = self.rsi_period
stochastic = StochasticOscillator()
stochastic.K.Length = self.stoch_length
stochastic.D.Length = self.signal_length
subscription = self.SubscribeCandles(self.candle_type)
subscription.BindEx(stochastic, self.process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._rsi)
self.DrawIndicator(area, stochastic)
self.DrawOwnTrades(area)
def process_candle(self, candle, stoch_value):
if candle.State != CandleStates.Finished:
return
# process RSI manually
rsi_result = process_float(self._rsi, candle.ClosePrice, candle.OpenTime, True)
if not rsi_result.IsFormed:
return
rsi = float(rsi_result)
k_val = stoch_value.K
d_val = stoch_value.D
if k_val is None or d_val is None:
self._prev_rsi = rsi
return
k = float(k_val)
d = float(d_val)
rsi_up = self._prev_rsi is not None and self._prev_rsi <= 50.0 and rsi > 50.0
rsi_down = self._prev_rsi is not None and self._prev_rsi >= 50.0 and rsi < 50.0
stoch_up = self._prev_k is not None and self._prev_d is not None and self._prev_k <= self._prev_d and k > d
stoch_down = self._prev_k is not None and self._prev_d is not None and self._prev_k >= self._prev_d and k < d
buy_signal = False
sell_signal = False
m = self.mode
if m == self.JBRAIN_SIG1_FILTER:
buy_signal = rsi_up and k > d
sell_signal = rsi_down and k < d
elif m == self.ULTRA_RSI_FILTER:
buy_signal = stoch_up and rsi > 50.0
sell_signal = stoch_down and rsi < 50.0
elif m == self.COMPOSITION:
buy_signal = rsi_up and stoch_up
sell_signal = rsi_down and stoch_down
if buy_signal:
if self.Position < 0 and self.allow_short_exit:
self.BuyMarket()
if self.allow_long_entry and self.Position <= 0:
self.BuyMarket()
elif sell_signal:
if self.Position > 0 and self.allow_long_exit:
self.SellMarket()
if self.allow_short_entry and self.Position >= 0:
self.SellMarket()
self._prev_rsi = rsi
self._prev_k = k
self._prev_d = d
def CreateClone(self):
return j_brain_ultra_rsi_strategy()