Rock Trader Neuro Strategy
该策略结合布林带和简单神经元进行交易。 最近七根K线的布林带宽度被归一化到 [-1,1] 范围,并与固定权重相乘。 加权和经过双曲正切激活。输出为负时开多,输出为正时开空。 仓位在触及止损或止盈时关闭。
细节
- 入场条件:
- 多头:神经元输出 < 0
- 空头:神经元输出 > 0
- 多/空:双向
- 出场条件:
- 触发止损或止盈
- 止损:价格绝对距离
- 默认值:
StopLoss= 30TakeProfit= 100Lot= 1CandleType= TimeSpan.FromMinutes(5).TimeFrame()
- 过滤器:
- 分类:Neural
- 方向:双向
- 指标:Bollinger Bands
- 止损:有
- 复杂度:中等
- 时间框架:短期
- 季节性:无
- 神经网络:是
- 背离:无
- 风险等级:中
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 using Bollinger Bands and a simple neuron to generate signals.
/// </summary>
public class RockTraderNeuroStrategy : Strategy
{
private readonly StrategyParam<decimal> _stopLoss;
private readonly StrategyParam<decimal> _takeProfit;
private readonly StrategyParam<decimal> _lot;
private readonly StrategyParam<DataType> _candleType;
private decimal _band1;
private decimal _band2;
private decimal _band3;
private decimal _band4;
private decimal _band5;
private decimal _band6;
private decimal _band7;
private decimal _entryPrice;
private decimal _stopPrice;
private decimal _takePrice;
public decimal StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
public decimal TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
public decimal Lot
{
get => _lot.Value;
set => _lot.Value = value;
}
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
public RockTraderNeuroStrategy()
{
_stopLoss = Param(nameof(StopLoss), 30m)
.SetDisplay("Stop Loss", "Stop loss in price units", "Risk");
_takeProfit = Param(nameof(TakeProfit), 100m)
.SetDisplay("Take Profit", "Take profit in price units", "Risk");
_lot = Param(nameof(Lot), 1m)
.SetDisplay("Lot", "Lots to trade", "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();
_band1 = _band2 = _band3 = _band4 = _band5 = _band6 = _band7 = 0m;
_entryPrice = 0m;
_stopPrice = 0m;
_takePrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
Volume = Lot;
_band1 = _band2 = _band3 = _band4 = _band5 = _band6 = _band7 = 0m;
_entryPrice = 0m;
_stopPrice = 0m;
_takePrice = 0m;
var bb = new BollingerBands
{
Length = 20,
Width = 2m
};
var subscription = SubscribeCandles(CandleType);
subscription
.BindEx(bb, ProcessCandleEx)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandleEx(ICandleMessage candle, IIndicatorValue value)
{
if (candle.State != CandleStates.Finished)
return;
var bb = (BollingerBandsValue)value;
if (bb.UpBand is not decimal upper ||
bb.LowBand is not decimal lower ||
bb.MovingAverage is not decimal middle)
return;
// Calculate current Bollinger Band width
var bandWidth = middle == 0m ? 0m : (upper - lower) / middle;
// Shift previous values to keep last seven widths
_band7 = _band6;
_band6 = _band5;
_band5 = _band4;
_band4 = _band3;
_band3 = _band2;
_band2 = _band1;
_band1 = bandWidth;
// Wait until all values are filled
if (_band7 == 0m)
return;
var min = Math.Min(Math.Min(Math.Min(_band1, _band2), Math.Min(_band3, _band4)), Math.Min(_band5, Math.Min(_band6, _band7)));
var max = Math.Max(Math.Max(Math.Max(_band1, _band2), Math.Max(_band3, _band4)), Math.Max(_band5, Math.Max(_band6, _band7)));
if (max == min)
return;
const decimal d1 = -1m;
const decimal d2 = 1m;
decimal Normalize(decimal x) => (x - min) * (d2 - d1) / (max - min) + d1;
var n1 = Normalize(_band1);
var n2 = Normalize(_band2);
var n3 = Normalize(_band3);
var n4 = Normalize(_band4);
var n5 = Normalize(_band5);
var n6 = Normalize(_band6);
var n7 = Normalize(_band7);
// Weighted sum of inputs
var net = n1 * 0.8m + n2 * -0.9m + n3 * 0.7m + n4 * 0.9m + n5 * -1m + n6 * 0.5m + n7 * 0m;
// Activation using hyperbolic tangent
var output = Tanh(net * 2m);
if (Position > 0)
{
if (candle.LowPrice <= _stopPrice || candle.HighPrice >= _takePrice)
SellMarket();
}
else if (Position < 0)
{
if (candle.HighPrice >= _stopPrice || candle.LowPrice <= _takePrice)
BuyMarket();
}
else
{
if (output < -0.5m)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _entryPrice - StopLoss;
_takePrice = _entryPrice + TakeProfit;
}
else if (output > 0.5m)
{
SellMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _entryPrice + StopLoss;
_takePrice = _entryPrice - TakeProfit;
}
}
}
private static decimal Tanh(decimal x)
{
var d = Math.Clamp((double)x, -20d, 20d);
var ePos = Math.Exp(d);
var eNeg = Math.Exp(-d);
return (decimal)((ePos - eNeg) / (ePos + eNeg));
}
}
import clr
import math
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 BollingerBands
from StockSharp.Algo.Strategies import Strategy
class rock_trader_neuro_strategy(Strategy):
def __init__(self):
super(rock_trader_neuro_strategy, self).__init__()
self._sl = self.Param("StopLoss", 30.0).SetDisplay("Stop Loss", "SL in price units", "Risk")
self._tp = self.Param("TakeProfit", 100.0).SetDisplay("Take Profit", "TP in price units", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))).SetDisplay("Candle Type", "Candle type", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(rock_trader_neuro_strategy, self).OnReseted()
self._bands = [0.0] * 7
self._entry_price = 0
self._stop_price = 0
self._take_price = 0
def OnStarted2(self, time):
super(rock_trader_neuro_strategy, self).OnStarted2(time)
self._bands = [0.0] * 7
self._entry_price = 0
self._stop_price = 0
self._take_price = 0
bb = BollingerBands()
bb.Length = 20
bb.Width = 2
sub = self.SubscribeCandles(self.CandleType)
sub.BindEx(bb, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawOwnTrades(area)
def OnProcess(self, candle, bb_val):
if candle.State != CandleStates.Finished:
return
upper = bb_val.UpBand
lower = bb_val.LowBand
middle = bb_val.MovingAverage
if upper is None or lower is None or middle is None:
return
if middle == 0:
return
band_width = float((upper - lower) / middle)
# Shift previous values
for i in range(6, 0, -1):
self._bands[i] = self._bands[i - 1]
self._bands[0] = band_width
if self._bands[6] == 0:
return
mn = min(self._bands)
mx = max(self._bands)
if mx == mn:
return
def normalize(x):
return (x - mn) * 2.0 / (mx - mn) - 1.0
n = [normalize(b) for b in self._bands]
# Weighted sum
weights = [0.8, -0.9, 0.7, 0.9, -1.0, 0.5, 0.0]
net = sum(n[i] * weights[i] for i in range(7))
# Tanh activation
d = max(-20.0, min(20.0, net * 2.0))
e_pos = math.exp(d)
e_neg = math.exp(-d)
output = (e_pos - e_neg) / (e_pos + e_neg)
close = candle.ClosePrice
if self.Position > 0:
if candle.LowPrice <= self._stop_price or candle.HighPrice >= self._take_price:
self.SellMarket()
elif self.Position < 0:
if candle.HighPrice >= self._stop_price or candle.LowPrice <= self._take_price:
self.BuyMarket()
else:
if output < -0.5:
self.BuyMarket()
self._entry_price = close
self._stop_price = self._entry_price - self._sl.Value
self._take_price = self._entry_price + self._tp.Value
elif output > 0.5:
self.SellMarket()
self._entry_price = close
self._stop_price = self._entry_price + self._sl.Value
self._take_price = self._entry_price - self._tp.Value
def CreateClone(self):
return rock_trader_neuro_strategy()