体积分布策略
本策略简化自体积分布指标,跟踪每个交易日的最高价、最低价以及由最高成交量决定的控制点价格。当价格高于控制点时买入,低于控制点时卖出,当价格回到当日中点时平仓。
参数
- K线类型 – 输入K线的时间框架。
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>
/// Simplified volume profile strategy.
/// Tracks session high, low, mid and point of control based on maximum candle volume.
/// Buys when price is above POC and sells when below.
/// </summary>
public class VolumeProfileMakit0Strategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private DateTime _currentSession;
private decimal _sessionHigh;
private decimal _sessionLow;
private decimal _sessionMid;
private decimal _pocPrice;
private decimal _maxVolume;
private bool _sessionTradeDone;
/// <summary>
/// Candle type.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes <see cref="VolumeProfileMakit0Strategy"/>.
/// </summary>
public VolumeProfileMakit0Strategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).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();
_currentSession = default;
_sessionHigh = 0;
_sessionLow = 0;
_sessionMid = 0;
_pocPrice = 0;
_maxVolume = 0;
_sessionTradeDone = false;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(ProcessCandle)
.Start();
StartProtection(
takeProfit: new Unit(2, UnitTypes.Percent),
stopLoss: new Unit(1, UnitTypes.Percent)
);
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawOwnTrades(area);
}
}
private void ProcessCandle(ICandleMessage candle)
{
if (candle.State != CandleStates.Finished)
return;
var sessionStart = GetSessionStart(candle.OpenTime.Date);
// start new session on session boundary
if (_currentSession != sessionStart)
{
if (Position > 0)
SellMarket(Position);
else if (Position < 0)
BuyMarket(Math.Abs(Position));
_currentSession = sessionStart;
_sessionHigh = candle.HighPrice;
_sessionLow = candle.LowPrice;
_sessionMid = candle.ClosePrice;
_pocPrice = candle.ClosePrice;
_maxVolume = candle.TotalVolume;
_sessionTradeDone = false;
return;
}
_sessionHigh = Math.Max(_sessionHigh, candle.HighPrice);
_sessionLow = Math.Min(_sessionLow, candle.LowPrice);
_sessionMid = (_sessionHigh + _sessionLow) / 2m;
if (candle.TotalVolume > _maxVolume)
{
_maxVolume = candle.TotalVolume;
_pocPrice = candle.ClosePrice;
}
if (!IsFormedAndOnlineAndAllowTrading())
return;
var bullishProfile = candle.ClosePrice > _pocPrice && candle.ClosePrice > _sessionMid;
var bearishProfile = candle.ClosePrice < _pocPrice && candle.ClosePrice < _sessionMid;
if (!_sessionTradeDone && Position == 0 && bullishProfile)
{
BuyMarket(Volume);
_sessionTradeDone = true;
}
else if (!_sessionTradeDone && Position == 0 && bearishProfile)
{
SellMarket(Volume);
_sessionTradeDone = true;
}
if (Position > 0 && candle.ClosePrice < _pocPrice && candle.ClosePrice < _sessionMid)
SellMarket(Position);
else if (Position < 0 && candle.ClosePrice > _pocPrice && candle.ClosePrice > _sessionMid)
BuyMarket(Math.Abs(Position));
}
private static DateTime GetSessionStart(DateTime date)
{
return new DateTime(date.Year, date.Month, 1);
}
}
import clr
clr.AddReference("StockSharp.Messages")
clr.AddReference("StockSharp.Algo")
clr.AddReference("StockSharp.Algo.Indicators")
clr.AddReference("StockSharp.Algo.Strategies")
from System import TimeSpan, DateTime
from StockSharp.Messages import DataType, CandleStates, Unit, UnitTypes
from StockSharp.Algo.Strategies import Strategy
class volume_profile_makit0_strategy(Strategy):
def __init__(self):
super(volume_profile_makit0_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Type of candles", "General")
self._current_session = None
self._session_high = 0.0
self._session_low = 0.0
self._session_mid = 0.0
self._poc_price = 0.0
self._max_volume = 0.0
self._session_trade_done = False
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(volume_profile_makit0_strategy, self).OnReseted()
self._current_session = None
self._session_high = 0.0
self._session_low = 0.0
self._session_mid = 0.0
self._poc_price = 0.0
self._max_volume = 0.0
self._session_trade_done = False
def OnStarted2(self, time):
super(volume_profile_makit0_strategy, self).OnStarted2(time)
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self.on_process).Start()
self.StartProtection(Unit(2, UnitTypes.Percent), Unit(1, UnitTypes.Percent))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawOwnTrades(area)
@staticmethod
def _get_session_start(date):
return DateTime(date.Year, date.Month, 1)
def on_process(self, candle):
if candle.State != CandleStates.Finished:
return
session_start = self._get_session_start(candle.OpenTime.Date)
close = float(candle.ClosePrice)
high = float(candle.HighPrice)
low = float(candle.LowPrice)
vol = float(candle.TotalVolume)
# start new session on session boundary
if self._current_session is None or self._current_session != session_start:
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
self._current_session = session_start
self._session_high = high
self._session_low = low
self._session_mid = close
self._poc_price = close
self._max_volume = vol
self._session_trade_done = False
return
self._session_high = max(self._session_high, high)
self._session_low = min(self._session_low, low)
self._session_mid = (self._session_high + self._session_low) / 2.0
if vol > self._max_volume:
self._max_volume = vol
self._poc_price = close
bullish_profile = close > self._poc_price and close > self._session_mid
bearish_profile = close < self._poc_price and close < self._session_mid
if not self._session_trade_done and self.Position == 0 and bullish_profile:
self.BuyMarket()
self._session_trade_done = True
elif not self._session_trade_done and self.Position == 0 and bearish_profile:
self.SellMarket()
self._session_trade_done = True
if self.Position > 0 and close < self._poc_price and close < self._session_mid:
self.SellMarket()
elif self.Position < 0 and close > self._poc_price and close > self._session_mid:
self.BuyMarket()
def CreateClone(self):
return volume_profile_makit0_strategy()