I4 DRF v2 策略
I4 DRF v2 策略使用 i4_DRF_v2 指标,该指标统计最近周期内上涨和下跌的收盘数。 根据 TrendModes 参数,可选择反趋势模式(Direct)或顺趋势模式(NotDirect)。 当指标符号翻转时开仓或平仓,并支持以价格步长计的止损和止盈。
细节
- 入场条件:根据 TrendModes 的指标符号翻转
- 多空方向:双向
- 出场条件:反向信号或止损/止盈
- 止损:有
- 默认值:
Period= 11BuyPosOpen= trueSellPosOpen= trueBuyPosClose= trueSellPosClose= trueTrendModes= DirectStopLoss= 1000TakeProfit= 2000
- 过滤器:
- 分类: 趋势
- 方向: 双向
- 指标: 自定义
- 止损: 有
- 复杂度: 基础
- 时间框架: 日内
- 季节性: 否
- 神经网络: 否
- 背离: 否
- 风险等级: 中等
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>
/// I4 DRF v2 strategy.
/// Uses a custom indicator counting up and down closes to generate signals.
/// </summary>
public class I4DrfV2Strategy : Strategy
{
/// <summary>
/// Trend mode options.
/// </summary>
public enum TrendModes
{
/// <summary>Contrarian trading.</summary>
Direct,
/// <summary>Trend following.</summary>
NotDirect
}
private readonly StrategyParam<int> _period;
private readonly StrategyParam<bool> _buyPosOpen;
private readonly StrategyParam<bool> _sellPosOpen;
private readonly StrategyParam<bool> _buyPosClose;
private readonly StrategyParam<bool> _sellPosClose;
private readonly StrategyParam<TrendModes> _trendMode;
private readonly StrategyParam<int> _stopLoss;
private readonly StrategyParam<int> _takeProfit;
private readonly StrategyParam<DataType> _candleType;
private DrfIndicator _drf = null!;
private int? _prevColor;
private decimal _entryPrice;
private decimal _stopPrice;
private decimal _takePrice;
/// <summary>
/// Indicator period.
/// </summary>
public int Period
{
get => _period.Value;
set => _period.Value = value;
}
/// <summary>
/// Allow opening long positions.
/// </summary>
public bool BuyPosOpen
{
get => _buyPosOpen.Value;
set => _buyPosOpen.Value = value;
}
/// <summary>
/// Allow opening short positions.
/// </summary>
public bool SellPosOpen
{
get => _sellPosOpen.Value;
set => _sellPosOpen.Value = value;
}
/// <summary>
/// Allow closing long positions on signal.
/// </summary>
public bool BuyPosClose
{
get => _buyPosClose.Value;
set => _buyPosClose.Value = value;
}
/// <summary>
/// Allow closing short positions on signal.
/// </summary>
public bool SellPosClose
{
get => _sellPosClose.Value;
set => _sellPosClose.Value = value;
}
/// <summary>
/// Trend mode.
/// </summary>
public TrendModes TrendMode
{
get => _trendMode.Value;
set => _trendMode.Value = value;
}
/// <summary>
/// Stop loss in price steps.
/// </summary>
public int StopLoss
{
get => _stopLoss.Value;
set => _stopLoss.Value = value;
}
/// <summary>
/// Take profit in price steps.
/// </summary>
public int TakeProfit
{
get => _takeProfit.Value;
set => _takeProfit.Value = value;
}
/// <summary>
/// Candle type for processing.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes a new instance of <see cref="I4DrfV2Strategy"/>.
/// </summary>
public I4DrfV2Strategy()
{
_period = Param(nameof(Period), 11)
.SetDisplay("Period", "Indicator period", "Indicator")
.SetGreaterThanZero();
_buyPosOpen = Param(nameof(BuyPosOpen), true)
.SetDisplay("Buy Open", "Allow opening longs", "Trading");
_sellPosOpen = Param(nameof(SellPosOpen), true)
.SetDisplay("Sell Open", "Allow opening shorts", "Trading");
_buyPosClose = Param(nameof(BuyPosClose), true)
.SetDisplay("Buy Close", "Allow closing longs", "Trading");
_sellPosClose = Param(nameof(SellPosClose), true)
.SetDisplay("Sell Close", "Allow closing shorts", "Trading");
_trendMode = Param(nameof(TrendModes), TrendModes.Direct)
.SetDisplay("Trend Mode", "DIRECT - contrarian, NOTDIRECT - trend following", "General");
_stopLoss = Param(nameof(StopLoss), 1000)
.SetDisplay("Stop Loss", "Stop loss in price steps", "Risk");
_takeProfit = Param(nameof(TakeProfit), 2000)
.SetDisplay("Take Profit", "Take profit in price steps", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe of candles", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_drf?.Reset();
_prevColor = null;
_entryPrice = 0m;
_stopPrice = 0m;
_takePrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
StartProtection(null, null);
_drf = new DrfIndicator { Length = Period };
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_drf, ProcessCandle)
.Start();
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _drf);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle, decimal drfValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!_drf.IsFormed)
{
_prevColor = drfValue > 0 ? 1 : 0;
return;
}
var currentColor = drfValue > 0 ? 1 : 0;
var step = Security.PriceStep ?? 1m;
// Handle protective stops
if (Position > 0)
{
if ((StopLoss > 0 && candle.ClosePrice <= _stopPrice) || (TakeProfit > 0 && candle.ClosePrice >= _takePrice))
{
SellMarket();
_prevColor = currentColor;
return;
}
}
else if (Position < 0)
{
if ((StopLoss > 0 && candle.ClosePrice >= _stopPrice) || (TakeProfit > 0 && candle.ClosePrice <= _takePrice))
{
BuyMarket();
_prevColor = currentColor;
return;
}
}
if (_prevColor == null)
{
_prevColor = currentColor;
return;
}
if (TrendMode == TrendModes.Direct)
{
if (_prevColor == 1 && currentColor == 0)
{
if (SellPosClose && Position < 0)
BuyMarket();
if (BuyPosOpen && Position <= 0)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _entryPrice - StopLoss * step;
_takePrice = _entryPrice + TakeProfit * step;
}
}
else if (_prevColor == 0 && currentColor == 1)
{
if (BuyPosClose && Position > 0)
SellMarket();
if (SellPosOpen && Position >= 0)
{
SellMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _entryPrice + StopLoss * step;
_takePrice = _entryPrice - TakeProfit * step;
}
}
}
else
{
if (_prevColor == 0 && currentColor == 1)
{
if (SellPosClose && Position < 0)
BuyMarket();
if (BuyPosOpen && Position <= 0)
{
BuyMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _entryPrice - StopLoss * step;
_takePrice = _entryPrice + TakeProfit * step;
}
}
else if (_prevColor == 1 && currentColor == 0)
{
if (BuyPosClose && Position > 0)
SellMarket();
if (SellPosOpen && Position >= 0)
{
SellMarket();
_entryPrice = candle.ClosePrice;
_stopPrice = _entryPrice + StopLoss * step;
_takePrice = _entryPrice - TakeProfit * step;
}
}
}
_prevColor = currentColor;
}
private sealed class DrfIndicator : DecimalLengthIndicator
{
private readonly Queue<int> _signs = new();
private decimal? _prevPrice;
private int _sum;
protected override IIndicatorValue OnProcess(IIndicatorValue input)
{
var price = input.GetValue<decimal>();
if (_prevPrice is null)
{
_prevPrice = price;
return new DecimalIndicatorValue(this, 0m, input.Time);
}
var sign = price > _prevPrice ? 1 : -1;
_prevPrice = price;
_signs.Enqueue(sign);
_sum += sign;
if (_signs.Count > Length)
_sum -= _signs.Dequeue();
if (_signs.Count < Length)
{
IsFormed = false;
return new DecimalIndicatorValue(this, 0m, input.Time);
}
IsFormed = true;
var value = (decimal)_sum / Length * 100m;
return new DecimalIndicatorValue(this, value, input.Time);
}
public override void Reset()
{
base.Reset();
_signs.Clear();
_prevPrice = null;
_sum = 0;
}
}
}
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.Strategies import Strategy
class i4_drf_v2_strategy(Strategy):
"""
I4 DRF v2: custom indicator counting up/down close directions.
Trades color (direction) flips with SL/TP management.
TrendMode 0 = contrarian, 1 = trend following.
"""
def __init__(self):
super(i4_drf_v2_strategy, self).__init__()
self._period = self.Param("Period", 11) \
.SetDisplay("Period", "Indicator period", "Indicator")
self._buy_pos_open = self.Param("BuyPosOpen", True) \
.SetDisplay("Buy Open", "Allow opening longs", "Trading")
self._sell_pos_open = self.Param("SellPosOpen", True) \
.SetDisplay("Sell Open", "Allow opening shorts", "Trading")
self._buy_pos_close = self.Param("BuyPosClose", True) \
.SetDisplay("Buy Close", "Allow closing longs", "Trading")
self._sell_pos_close = self.Param("SellPosClose", True) \
.SetDisplay("Sell Close", "Allow closing shorts", "Trading")
self._trend_mode = self.Param("TrendMode", 0) \
.SetDisplay("Trend Mode", "0=contrarian, 1=trend following", "General")
self._stop_loss = self.Param("StopLoss", 1000) \
.SetDisplay("Stop Loss", "Stop loss in price steps", "Risk")
self._take_profit = self.Param("TakeProfit", 2000) \
.SetDisplay("Take Profit", "Take profit in price steps", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe of candles", "General")
self._signs = []
self._sign_sum = 0
self._prev_price = None
self._prev_color = None
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(i4_drf_v2_strategy, self).OnReseted()
self._signs = []
self._sign_sum = 0
self._prev_price = None
self._prev_color = None
self._entry_price = 0.0
self._stop_price = 0.0
self._take_price = 0.0
def OnStarted2(self, time):
super(i4_drf_v2_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._process_candle).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def _process_candle(self, candle):
if candle.State != CandleStates.Finished:
return
close = float(candle.ClosePrice)
period = self._period.Value
if self._prev_price is None:
self._prev_price = close
return
sign = 1 if close > self._prev_price else -1
self._prev_price = close
self._signs.append(sign)
self._sign_sum += sign
if len(self._signs) > period:
self._sign_sum -= self._signs.pop(0)
if len(self._signs) < period:
current_color = 1 if self._sign_sum > 0 else 0
self._prev_color = current_color
return
is_formed = True
drf_value = float(self._sign_sum) / period * 100.0
current_color = 1 if drf_value > 0 else 0
step = 1.0
if self.Security is not None and self.Security.PriceStep is not None:
step = float(self.Security.PriceStep)
if step <= 0:
step = 1.0
sl = self._stop_loss.Value
tp = self._take_profit.Value
if not self.IsFormedAndOnlineAndAllowTrading():
self._prev_color = current_color
return
# Handle protective stops
if self.Position > 0:
if (sl > 0 and close <= self._stop_price) or (tp > 0 and close >= self._take_price):
self.SellMarket()
self._prev_color = current_color
return
elif self.Position < 0:
if (sl > 0 and close >= self._stop_price) or (tp > 0 and close <= self._take_price):
self.BuyMarket()
self._prev_color = current_color
return
if self._prev_color is None:
self._prev_color = current_color
return
trend_mode = self._trend_mode.Value
if trend_mode == 0:
# Direct (contrarian)
if self._prev_color == 1 and current_color == 0:
if self._sell_pos_close.Value and self.Position < 0:
self.BuyMarket()
if self._buy_pos_open.Value and self.Position <= 0:
self.BuyMarket()
self._entry_price = close
self._stop_price = close - sl * step
self._take_price = close + tp * step
elif self._prev_color == 0 and current_color == 1:
if self._buy_pos_close.Value and self.Position > 0:
self.SellMarket()
if self._sell_pos_open.Value and self.Position >= 0:
self.SellMarket()
self._entry_price = close
self._stop_price = close + sl * step
self._take_price = close - tp * step
else:
# NotDirect (trend following)
if self._prev_color == 0 and current_color == 1:
if self._sell_pos_close.Value and self.Position < 0:
self.BuyMarket()
if self._buy_pos_open.Value and self.Position <= 0:
self.BuyMarket()
self._entry_price = close
self._stop_price = close - sl * step
self._take_price = close + tp * step
elif self._prev_color == 1 and current_color == 0:
if self._buy_pos_close.Value and self.Position > 0:
self.SellMarket()
if self._sell_pos_open.Value and self.Position >= 0:
self.SellMarket()
self._entry_price = close
self._stop_price = close + sl * step
self._take_price = close - tp * step
self._prev_color = current_color
def CreateClone(self):
return i4_drf_v2_strategy()