Dekidaka-Ashi ローソク出来高戦略
この戦略はDekidaka-Ashiアプローチを使用して、ローソク足の実体と平滑化された出来高を組み合わせます。強気シグナルで買い、弱気シグナルで売ります。両レンジにまたがるローソク足はオープンポジションを決済します。
詳細
- エントリー条件:
- 強い・弱い強気シグナル: 高値が上限レンジを上回り、安値が下限レンジを上回る。
- 強い・弱い弱気シグナル: 高値が上限レンジを下回り、安値が下限レンジを下回る。
- ロング/ショート: 両方。
- エグジット条件:
- 逆シグナルまたは両レンジにまたがるローソク足(不確実性)。
- ストップ: なし。
- デフォルト値:
BodySize= 1VolumeSmooth= 1CandleType= 5分の時間軸
- フィルター:
- カテゴリ: パターン
- 方向: ロング&ショート
- インジケーター: EMA、出来高
- ストップ: いいえ
- 複雑さ: 低
- 時間軸: 任意
- 季節性: いいえ
- ニューラルネットワーク: いいえ
- ダイバージェンス: いいえ
- リスクレベル: 中
using System;
using System.Collections.Generic;
using Ecng.Common;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
/// <summary>
/// Dekidaka-Ashi candles volume strategy.
/// The range is the body of the previous candle, scaled around its middle by BodySize and by the ratio of that candle's volume
/// to its VolumeSmooth EMA, so heavy candles give a wider range.
/// A candle with its high above the upper bound and its low above the lower bound is bullish and goes long; one with its high below
/// the upper bound and its low below the lower bound is bearish and goes short, reversing an opposite position.
/// A candle spanning both bounds closes any position.
/// </summary>
public class DekidakaAshiCandlesVolumeStrategy : Strategy
{
private readonly StrategyParam<decimal> _bodySize;
private readonly StrategyParam<int> _volumeSmooth;
private readonly StrategyParam<DataType> _candleType;
private ExponentialMovingAverage _volumeEma;
private decimal? _prevUpper;
private decimal? _prevLower;
/// <summary>
/// Multiplier of the body range.
/// </summary>
public decimal BodySize
{
get => _bodySize.Value;
set => _bodySize.Value = value;
}
/// <summary>
/// EMA length of the volume smoothing.
/// </summary>
public int VolumeSmooth
{
get => _volumeSmooth.Value;
set => _volumeSmooth.Value = value;
}
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Constructor.
/// </summary>
public DekidakaAshiCandlesVolumeStrategy()
{
_bodySize = Param(nameof(BodySize), 1m)
.SetGreaterThanZero()
.SetDisplay("Body Size", "Multiplier of the body range", "Range");
_volumeSmooth = Param(nameof(VolumeSmooth), 1)
.SetGreaterThanZero()
.SetDisplay("Volume Smooth", "EMA length of the volume smoothing", "Range");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(5).TimeFrame())
.SetDisplay("Candle Type", "Type of candles to use", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevUpper = null;
_prevLower = null;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_prevUpper = null;
_prevLower = null;
_volumeEma = new ExponentialMovingAverage { Length = VolumeSmooth };
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 upper = _prevUpper;
var lower = _prevLower;
var smoothed = _volumeEma.Process(candle.TotalVolume, candle.ServerTime, true);
if (smoothed.IsFormed)
{
var avgVolume = smoothed.GetValue<decimal>();
var ratio = avgVolume > 0 ? candle.TotalVolume / avgVolume : 1m;
var middle = (candle.OpenPrice + candle.ClosePrice) / 2;
var halfBody = Math.Abs(candle.ClosePrice - candle.OpenPrice) / 2 * BodySize * ratio;
_prevUpper = middle + halfBody;
_prevLower = middle - halfBody;
}
if (upper is not decimal up || lower is not decimal low)
return;
if (!IsFormedAndOnlineAndAllowTrading())
return;
var bullish = candle.HighPrice > up && candle.LowPrice > low;
var bearish = candle.HighPrice < up && candle.LowPrice < low;
var spansBoth = candle.HighPrice >= up && candle.LowPrice <= low;
if (bullish && Position <= 0)
BuyMarket(Volume + Math.Abs(Position));
else if (bearish && Position >= 0)
SellMarket(Volume + Math.Abs(Position));
else if (spansBoth && Position > 0)
SellMarket(Position);
else if (spansBoth && Position < 0)
BuyMarket(-Position);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, Decimal
from StockSharp.Messages import DataType, CandleStates
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
from indicator_extensions import *
class dekidaka_ashi_candles_volume_strategy(Strategy):
"""
Dekidaka-Ashi candles volume strategy.
The range is the body of the previous candle, scaled around its middle by BodySize and by the ratio of that candle's volume
to its VolumeSmooth EMA, so heavy candles give a wider range.
A candle with its high above the upper bound and its low above the lower bound is bullish and goes long; one with its high below
the upper bound and its low below the lower bound is bearish and goes short, reversing an opposite position.
A candle spanning both bounds closes any position.
"""
def __init__(self):
super(dekidaka_ashi_candles_volume_strategy, self).__init__()
self._body_size = self.Param("BodySize", 1.0) \
.SetGreaterThanZero() \
.SetDisplay("Body Size", "Multiplier of the body range", "Range")
self._volume_smooth = self.Param("VolumeSmooth", 1) \
.SetGreaterThanZero() \
.SetDisplay("Volume Smooth", "EMA length of the volume smoothing", "Range")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))) \
.SetDisplay("Candle Type", "Type of candles to use", "General")
self._volume_ema = None
self._prev_upper = None
self._prev_lower = None
@property
def candle_type(self):
return self._candle_type.Value
def GetWorkingSecurities(self):
return [(self.Security, self.candle_type)]
def OnReseted(self):
super(dekidaka_ashi_candles_volume_strategy, self).OnReseted()
self._prev_upper = None
self._prev_lower = None
def OnStarted2(self, time):
super(dekidaka_ashi_candles_volume_strategy, self).OnStarted2(time)
self._prev_upper = None
self._prev_lower = None
self._volume_ema = ExponentialMovingAverage()
self._volume_ema.Length = self._volume_smooth.Value
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
upper = self._prev_upper
lower = self._prev_lower
smoothed = process_float(self._volume_ema, candle.TotalVolume, candle.ServerTime, True)
if smoothed.IsFormed:
avg_volume = float(smoothed.GetValue[Decimal](None))
volume = float(candle.TotalVolume)
ratio = volume / avg_volume if avg_volume > 0 else 1.0
open_price = float(candle.OpenPrice)
close = float(candle.ClosePrice)
middle = (open_price + close) / 2.0
half_body = abs(close - open_price) / 2.0 * float(self._body_size.Value) * ratio
self._prev_upper = middle + half_body
self._prev_lower = middle - half_body
if upper is None or lower is None:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
high = float(candle.HighPrice)
low = float(candle.LowPrice)
bullish = high > upper and low > lower
bearish = high < upper and low < lower
spans_both = high >= upper and low <= lower
if bullish and self.Position <= 0:
self.BuyMarket(self.Volume + abs(self.Position))
elif bearish and self.Position >= 0:
self.SellMarket(self.Volume + abs(self.Position))
elif spans_both and self.Position > 0:
self.SellMarket(self.Position)
elif spans_both and self.Position < 0:
self.BuyMarket(-self.Position)
def CreateClone(self):
return dekidaka_ashi_candles_volume_strategy()