流動性スイング戦略
流動性スイング戦略は、最近のピボット高値と安値を追跡して抵抗線とサポートラインを定義します。安値がサポートを上抜けながら終値が抵抗を下回っているときにロング取引が発生します。高値が抵抗を下抜けながら終値がサポートを上回っているときにショート取引が発動します。リスク管理には、バッファー付きのレベルでのストップロスと、そのリスク距離の2倍のテイクプロフィットを使用し、1:2のリスクリワード比を実現します。
詳細
- エントリー条件:
- ロング: 安値がサポートを上抜けかつ終値 < 抵抗。
- ショート: 高値が抵抗を下抜けかつ終値 > サポート。
- ロング/ショート: 両方向。
- エグジット条件:
- レベルまたはバッファー値でのストップロス。
- リスク距離の2倍でのテイクプロフィット。
- ストップ: ストップロスとテイクプロフィット。
- デフォルト値:
Lookback= 5StopLossBuffer= 0.5
- フィルター:
- カテゴリ: ブレイクアウト
- 方向: 両方
- インジケーター: Pivot highs/lows
- ストップ: はい
- 複雑さ: 低
- 時間軸: 1h(デフォルト)
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
namespace StockSharp.Samples.Strategies;
using System;
using System.Collections.Generic;
using System.Linq;
using Ecng.Common;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
/// <summary>
/// Liquidity Swings Strategy.
/// The latest pivot high (Lookback bars on each side) is resistance and the latest pivot low is support. A long opens when
/// the low crosses above support with the close below resistance; a short opens when the high crosses below resistance with
/// the close above support. The stop sits StopLossBuffer beyond the level and the target is twice the risk from the entry.
/// </summary>
public class LiquiditySwingsStrategy : Strategy
{
private const decimal RewardMultiplier = 2m;
private readonly StrategyParam<int> _lookback;
private readonly StrategyParam<decimal> _stopLossBuffer;
private readonly StrategyParam<DataType> _candleType;
private readonly List<ICandleMessage> _window = [];
private decimal? _support;
private decimal? _resistance;
private decimal? _prevLow;
private decimal? _prevHigh;
private decimal? _stopPrice;
private decimal? _targetPrice;
/// <summary>
/// Bars on each side of a pivot.
/// </summary>
public int Lookback
{
get => _lookback.Value;
set => _lookback.Value = value;
}
/// <summary>
/// Price distance of the stop beyond the level.
/// </summary>
public decimal StopLossBuffer
{
get => _stopLossBuffer.Value;
set => _stopLossBuffer.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public LiquiditySwingsStrategy()
{
_lookback = Param(nameof(Lookback), 5)
.SetGreaterThanZero()
.SetDisplay("Lookback", "Bars on each side of a pivot", "Pivots");
_stopLossBuffer = Param(nameof(StopLossBuffer), 0.5m)
.SetNotNegative()
.SetDisplay("Stop Loss Buffer", "Price distance of the stop beyond the level", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
=> [(Security, CandleType)];
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
ResetState();
}
private void ResetState()
{
_window.Clear();
_support = null;
_resistance = null;
_prevLow = null;
_prevHigh = null;
_stopPrice = null;
_targetPrice = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
ResetState();
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;
UpdatePivots(candle);
var prevLow = _prevLow;
var prevHigh = _prevHigh;
_prevLow = candle.LowPrice;
_prevHigh = candle.HighPrice;
if (!IsFormedAndOnlineAndAllowTrading())
return;
if (Position > 0)
{
if ((_stopPrice is decimal stop && candle.LowPrice <= stop) || (_targetPrice is decimal target && candle.HighPrice >= target))
SellMarket(Position);
return;
}
if (Position < 0)
{
if ((_stopPrice is decimal stop && candle.HighPrice >= stop) || (_targetPrice is decimal target && candle.LowPrice <= target))
BuyMarket(-Position);
return;
}
if (_support is not decimal support || _resistance is not decimal resistance || prevLow is not decimal lastLow || prevHigh is not decimal lastHigh)
return;
var close = candle.ClosePrice;
if (lastLow <= support && candle.LowPrice > support && close < resistance)
{
var stop = support - StopLossBuffer;
var risk = close - stop;
if (risk <= 0m)
return;
BuyMarket(Volume);
_stopPrice = stop;
_targetPrice = close + RewardMultiplier * risk;
}
else if (lastHigh >= resistance && candle.HighPrice < resistance && close > support)
{
var stop = resistance + StopLossBuffer;
var risk = stop - close;
if (risk <= 0m)
return;
SellMarket(Volume);
_stopPrice = stop;
_targetPrice = close - RewardMultiplier * risk;
}
}
private void UpdatePivots(ICandleMessage candle)
{
_window.Add(candle);
var size = Lookback * 2 + 1;
if (_window.Count > size)
_window.RemoveAt(0);
if (_window.Count < size)
return;
// The middle candle is a pivot once Lookback candles on each side confirm it.
var center = _window[Lookback];
var others = _window.Where((_, i) => i != Lookback).ToArray();
if (others.All(c => c.HighPrice < center.HighPrice))
_resistance = center.HighPrice;
if (others.All(c => c.LowPrice > center.LowPrice))
_support = center.LowPrice;
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Strategies import Strategy
REWARD_MULTIPLIER = 2.0
class liquidity_swings_strategy(Strategy):
"""
Liquidity Swings Strategy.
The latest pivot high (Lookback bars on each side) is resistance and the latest pivot low is support. A long opens when
the low crosses above support with the close below resistance; a short opens when the high crosses below resistance with
the close above support. The stop sits StopLossBuffer beyond the level and the target is twice the risk from the entry.
"""
def __init__(self):
super(liquidity_swings_strategy, self).__init__()
self._lookback = self.Param("Lookback", 5).SetGreaterThanZero().SetDisplay("Lookback", "Bars on each side of a pivot", "Pivots")
self._stop_loss_buffer = self.Param("StopLossBuffer", 0.5).SetNotNegative().SetDisplay("Stop Loss Buffer", "Price distance of the stop beyond the level", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))).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._window = []
self._support = None
self._resistance = None
self._prev_low = None
self._prev_high = None
self._stop_price = None
self._target_price = None
def OnReseted(self):
super(liquidity_swings_strategy, self).OnReseted()
self._reset_state()
def OnStarted2(self, time):
super(liquidity_swings_strategy, self).OnStarted2(time)
self._reset_state()
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
high = float(candle.HighPrice)
low = float(candle.LowPrice)
close = float(candle.ClosePrice)
self._update_pivots(high, low)
prev_low = self._prev_low
prev_high = self._prev_high
self._prev_low = low
self._prev_high = high
if not self.IsFormedAndOnlineAndAllowTrading():
return
if self.Position > 0:
if (self._stop_price is not None and low <= self._stop_price) or (self._target_price is not None and high >= self._target_price):
self.SellMarket(self.Position)
return
if self.Position < 0:
if (self._stop_price is not None and high >= self._stop_price) or (self._target_price is not None and low <= self._target_price):
self.BuyMarket(-self.Position)
return
if self._support is None or self._resistance is None or prev_low is None or prev_high is None:
return
support = self._support
resistance = self._resistance
buffer = float(self._stop_loss_buffer.Value)
if prev_low <= support and low > support and close < resistance:
stop = support - buffer
risk = close - stop
if risk <= 0:
return
self.BuyMarket(self.Volume)
self._stop_price = stop
self._target_price = close + REWARD_MULTIPLIER * risk
elif prev_high >= resistance and high < resistance and close > support:
stop = resistance + buffer
risk = stop - close
if risk <= 0:
return
self.SellMarket(self.Volume)
self._stop_price = stop
self._target_price = close - REWARD_MULTIPLIER * risk
def _update_pivots(self, high, low):
self._window.append((high, low))
lookback = self._lookback.Value
size = lookback * 2 + 1
if len(self._window) > size:
self._window.pop(0)
if len(self._window) < size:
return
# The middle candle is a pivot once Lookback candles on each side confirm it.
center = self._window[lookback]
others = [c for i, c in enumerate(self._window) if i != lookback]
if all(c[0] < center[0] for c in others):
self._resistance = center[0]
if all(c[1] > center[1] for c in others):
self._support = center[1]
def CreateClone(self):
return liquidity_swings_strategy()