日内成交量摆动策略
该策略在价格进入当天或前一天基于成交量的摆动区域时进行交易。
细节
- 入场条件:
- 多头: 价格进入上方摆动区域。
- 空头: 价格进入下方摆动区域。
- 多空方向: 双向。
- 出场条件:
- 相反信号。
- 止损: 无。
- 默认值:
RegionMustClose= trueCandleType= 1 minute
- 筛选:
- 类别: 突破
- 方向: 双向
- 指标: 成交量
- 止损: 无
- 复杂度: 中等
- 时间框架: 日内
- 季节性: 否
- 神经网络: 否
- 背离: 否
- 风险等级: 中
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Intraday volume swing based breakout strategy.
/// Buys when price pushes into swing high regions and sells on swing low
/// regions.
/// </summary>
public class IntradayVolumeSwingsStrategy : Strategy {
private readonly StrategyParam<bool> _regionMustClose;
private readonly StrategyParam<DataType> _candleType;
public bool RegionMustClose {
get => _regionMustClose.Value;
set => _regionMustClose.Value = value;
}
public DataType CandleType {
get => _candleType.Value;
set => _candleType.Value = value;
}
public IntradayVolumeSwingsStrategy() {
_regionMustClose =
Param(nameof(RegionMustClose), true)
.SetDisplay("Region Must Close", "Close in region to trigger",
"General");
_candleType =
Param(nameof(CandleType), TimeSpan.FromMinutes(15).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();
_currentDay = default;
_prevOpen = _prevClose = _high1 = _high2 = _low1 = _low2 = _volume1 =
0m;
_lowBar1 = _lowBar2 = _highBar1 = _highBar2 = false;
_prevSwingLow = _prevSwingHigh = false;
_currentSwingLowTop = _currentSwingLowBottom = _currentSwingHighTop =
_currentSwingHighBottom = null;
_dailySwingLowTop = _dailySwingLowBottom = _dailySwingHighTop =
_dailySwingHighBottom = null;
_prevDaySwingLowTop = _prevDaySwingLowBottom = _prevDaySwingHighTop =
_prevDaySwingHighBottom = null;
}
private DateTime _currentDay;
private decimal _prevOpen;
private decimal _prevClose;
private decimal _high1;
private decimal _high2;
private decimal _low1;
private decimal _low2;
private decimal _volume1;
private bool _lowBar1;
private bool _lowBar2;
private bool _highBar1;
private bool _highBar2;
private bool _prevSwingLow;
private bool _prevSwingHigh;
private decimal? _currentSwingLowTop;
private decimal? _currentSwingLowBottom;
private decimal? _currentSwingHighTop;
private decimal? _currentSwingHighBottom;
private decimal? _dailySwingLowTop;
private decimal? _dailySwingLowBottom;
private decimal? _dailySwingHighTop;
private decimal? _dailySwingHighBottom;
private decimal? _prevDaySwingLowTop;
private decimal? _prevDaySwingLowBottom;
private decimal? _prevDaySwingHighTop;
private decimal? _prevDaySwingHighBottom;
/// <inheritdoc />
protected override void OnStarted2(DateTime time) {
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ProcessCandle).Start();
var area = CreateChartArea();
if (area != null) {
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle) {
if (candle.State != CandleStates.Finished)
return;
var day = candle.OpenTime.Date;
var isNewDay = _currentDay != day;
if (isNewDay) {
_currentDay = day;
_prevDaySwingLowTop = _dailySwingLowTop;
_prevDaySwingLowBottom = _dailySwingLowBottom;
_prevDaySwingHighTop = _dailySwingHighTop;
_prevDaySwingHighBottom = _dailySwingHighBottom;
_dailySwingLowTop = _dailySwingLowBottom = null;
_dailySwingHighTop = _dailySwingHighBottom = null;
}
var increasingVolume = candle.TotalVolume > _volume1;
var lowerLow = candle.LowPrice < _low1;
var higherHigh = candle.HighPrice > _high1;
var lowBar = increasingVolume && lowerLow;
var highBar = increasingVolume && higherHigh;
var swingLow = lowBar && _lowBar1 && _lowBar2;
var swingHigh = highBar && _highBar1 && _highBar2;
var hh3 = Math.Max(candle.HighPrice, Math.Max(_high1, _high2));
var ll3 = Math.Min(candle.LowPrice, Math.Min(_low1, _low2));
if (swingLow && !_prevSwingLow) {
_currentSwingLowTop = hh3;
_currentSwingLowBottom = ll3;
} else if (swingLow && _prevSwingLow) {
_currentSwingLowTop =
Math.Max(_currentSwingLowTop ?? hh3, candle.HighPrice);
_currentSwingLowBottom =
Math.Min(_currentSwingLowBottom ?? ll3, candle.LowPrice);
}
if (swingHigh && !_prevSwingHigh) {
_currentSwingHighTop = hh3;
_currentSwingHighBottom = ll3;
} else if (swingHigh && _prevSwingHigh) {
_currentSwingHighTop =
Math.Max(_currentSwingHighTop ?? hh3, candle.HighPrice);
_currentSwingHighBottom =
Math.Min(_currentSwingHighBottom ?? ll3, candle.LowPrice);
}
if (_prevSwingLow && !swingLow && _currentSwingLowBottom.HasValue) {
if (!_dailySwingLowBottom.HasValue ||
_currentSwingLowBottom < _dailySwingLowBottom) {
_dailySwingLowTop = _currentSwingLowTop;
_dailySwingLowBottom = _currentSwingLowBottom;
}
_currentSwingLowTop = _currentSwingLowBottom = null;
}
if (_prevSwingHigh && !swingHigh && _currentSwingHighTop.HasValue) {
if (!_dailySwingHighTop.HasValue ||
_currentSwingHighTop > _dailySwingHighTop) {
_dailySwingHighTop = _currentSwingHighTop;
_dailySwingHighBottom = _currentSwingHighBottom;
}
_currentSwingHighTop = _currentSwingHighBottom = null;
}
CheckRegions(candle);
_volume1 = candle.TotalVolume;
_prevOpen = candle.OpenPrice;
_prevClose = candle.ClosePrice;
_high2 = _high1;
_high1 = candle.HighPrice;
_low2 = _low1;
_low1 = candle.LowPrice;
_lowBar2 = _lowBar1;
_lowBar1 = lowBar;
_highBar2 = _highBar1;
_highBar1 = highBar;
_prevSwingLow = swingLow;
_prevSwingHigh = swingHigh;
}
private void CheckRegions(ICandleMessage candle) {
if (_prevDaySwingHighBottom.HasValue) {
var level = _prevDaySwingHighBottom.Value;
if (RegionMustClose) {
if (_prevOpen < level && _prevClose >= level && Position <= 0)
BuyMarket();
} else {
if (candle.OpenPrice < level && candle.HighPrice >= level &&
Position <= 0)
BuyMarket();
}
}
if (_prevDaySwingLowTop.HasValue) {
var level = _prevDaySwingLowTop.Value;
if (RegionMustClose) {
if (_prevOpen > level && _prevClose <= level && Position >= 0)
SellMarket();
} else {
if (candle.OpenPrice > level && candle.LowPrice <= level &&
Position >= 0)
SellMarket();
}
}
if (_dailySwingHighBottom.HasValue) {
var level = _dailySwingHighBottom.Value;
if (RegionMustClose) {
if (_prevOpen < level && _prevClose >= level && Position <= 0)
BuyMarket();
} else {
if (candle.OpenPrice < level && candle.HighPrice >= level &&
Position <= 0)
BuyMarket();
}
}
if (_dailySwingLowTop.HasValue) {
var level = _dailySwingLowTop.Value;
if (RegionMustClose) {
if (_prevOpen > level && _prevClose <= level && Position >= 0)
SellMarket();
} else {
if (candle.OpenPrice > level && candle.LowPrice <= level &&
Position >= 0)
SellMarket();
}
}
}
}
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 intraday_volume_swings_strategy(Strategy):
def __init__(self):
super(intraday_volume_swings_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._high1 = 0.0
self._high2 = 0.0
self._low1 = 0.0
self._low2 = 0.0
self._volume1 = 0.0
self._low_bar1 = False
self._low_bar2 = False
self._high_bar1 = False
self._high_bar2 = False
self._prev_swing_low = False
self._prev_swing_high = False
self._daily_swing_low_top = None
self._daily_swing_high_bottom = None
self._current_day = None
@property
def candle_type(self):
return self._candle_type.Value
@candle_type.setter
def candle_type(self, value):
self._candle_type.Value = value
def OnReseted(self):
super(intraday_volume_swings_strategy, self).OnReseted()
self._high1 = 0.0
self._high2 = 0.0
self._low1 = 0.0
self._low2 = 0.0
self._volume1 = 0.0
self._low_bar1 = False
self._low_bar2 = False
self._high_bar1 = False
self._high_bar2 = False
self._prev_swing_low = False
self._prev_swing_high = False
self._daily_swing_low_top = None
self._daily_swing_high_bottom = None
self._current_day = None
def OnStarted2(self, time):
super(intraday_volume_swings_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
def OnProcess(self, candle):
if candle.State != CandleStates.Finished:
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
vol = float(candle.TotalVolume)
day = candle.OpenTime.Date
if self._current_day is None or self._current_day != day:
self._current_day = day
self._daily_swing_low_top = None
self._daily_swing_high_bottom = None
inc_vol = vol > self._volume1
lower_low = low < self._low1
higher_high = high > self._high1
low_bar = inc_vol and lower_low
high_bar = inc_vol and higher_high
swing_low = low_bar and self._low_bar1 and self._low_bar2
swing_high = high_bar and self._high_bar1 and self._high_bar2
hh3 = max(high, max(self._high1, self._high2))
ll3 = min(low, min(self._low1, self._low2))
if swing_low and not self._prev_swing_low:
self._current_sl_top = hh3
self._current_sl_bottom = ll3
elif swing_low and self._prev_swing_low:
self._current_sl_top = max(getattr(self, '_current_sl_top', hh3), high)
self._current_sl_bottom = min(getattr(self, '_current_sl_bottom', ll3), low)
if swing_high and not self._prev_swing_high:
self._current_sh_top = hh3
self._current_sh_bottom = ll3
elif swing_high and self._prev_swing_high:
self._current_sh_top = max(getattr(self, '_current_sh_top', hh3), high)
self._current_sh_bottom = min(getattr(self, '_current_sh_bottom', ll3), low)
if self._prev_swing_low and not swing_low:
sl_bottom = getattr(self, '_current_sl_bottom', None)
sl_top = getattr(self, '_current_sl_top', None)
if sl_bottom is not None:
if self._daily_swing_low_top is None or sl_bottom < (self._daily_swing_low_top or 0):
self._daily_swing_low_top = sl_top
if self._prev_swing_high and not swing_high:
sh_top = getattr(self, '_current_sh_top', None)
sh_bottom = getattr(self, '_current_sh_bottom', None)
if sh_top is not None:
if self._daily_swing_high_bottom is None or sh_top > 0:
self._daily_swing_high_bottom = sh_bottom
if self._daily_swing_high_bottom is not None:
level = self._daily_swing_high_bottom
if close > level and self.Position <= 0:
self.BuyMarket()
if self._daily_swing_low_top is not None:
level = self._daily_swing_low_top
if close < level and self.Position >= 0:
self.SellMarket()
self._volume1 = vol
self._high2 = self._high1
self._high1 = high
self._low2 = self._low1
self._low1 = low
self._low_bar2 = self._low_bar1
self._low_bar1 = low_bar
self._high_bar2 = self._high_bar1
self._high_bar1 = high_bar
self._prev_swing_low = swing_low
self._prev_swing_high = swing_high
def CreateClone(self):
return intraday_volume_swings_strategy()