Volatility Quality
该示例策略演示如何通过平滑后的中位价方向变化进行交易。原始的 MQL 专家使用 Volatility Quality 指标,此处通过对中位价应用简单移动平均线来进行近似。
策略逻辑
- 计算每根K线的中位价
(High + Low) / 2。 - 使用简单移动平均线 (SMA) 对中位价进行平滑处理。
- 线的方向决定颜色:上升视为颜色 0,下降视为颜色 1。
- 当颜色从 0 变为 1 时,策略平掉空头仓位并开多头。
- 当颜色从 1 变为 0 时,策略平掉多头仓位并开空头。
- 使用固定止损和止盈进行基本风险控制。
参数
| 名称 | 说明 |
|---|---|
Length |
应用于中位价的 SMA 平滑周期。 |
Candle Type |
用于计算的K线时间框架。 |
说明
该示例仅用于学习目的,对原算法进行了简化,可能与 MQL 版本的行为不同,请自行承担风险。
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>
/// Volatility Quality strategy. Trades when smoothed median price slope changes direction.
/// </summary>
public class VolatilityQualityStrategy : Strategy
{
private readonly StrategyParam<int> _lengthParam;
private readonly StrategyParam<DataType> _candleTypeParam;
private ExponentialMovingAverage _sma;
private decimal _prevSma;
private int _prevColor;
/// <summary>
/// Smoothing period for median price.
/// </summary>
public int Length
{
get => _lengthParam.Value;
set => _lengthParam.Value = value;
}
/// <summary>
/// Candle type used for analysis.
/// </summary>
public DataType CandleType
{
get => _candleTypeParam.Value;
set => _candleTypeParam.Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="VolatilityQualityStrategy"/> class.
/// </summary>
public VolatilityQualityStrategy()
{
_lengthParam = Param(nameof(Length), 5)
.SetGreaterThanZero()
.SetDisplay("Length", "Smoothing period for median price", "Parameters")
.SetOptimize(5, 30, 5);
_candleTypeParam = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Candle Type", "Timeframe for strategy", "Common");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
return [(Security, CandleType)];
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_sma = null;
_prevSma = 0m;
_prevColor = -1;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
// Create indicator for smoothed median price
_sma = new ExponentialMovingAverage { Length = Length };
// Subscribe to candle series and bind indicator
var subscription = SubscribeCandles(CandleType);
subscription
.Bind(_sma, ProcessCandle)
.Start();
// Draw chart elements if available
var area = CreateChartArea();
if (area != null)
{
DrawCandles(area, subscription);
DrawIndicator(area, _sma);
DrawOwnTrades(area);
}
// Basic position protection
StartProtection(
takeProfit: new Unit(2m, UnitTypes.Percent),
stopLoss: new Unit(1m, UnitTypes.Percent));
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
// Determine indicator color based on slope
int color;
if (_prevSma == 0m)
{
_prevSma = smaValue;
_prevColor = -1;
return;
}
if (smaValue > _prevSma)
color = 0; // rising line
else if (smaValue < _prevSma)
color = 1; // falling line
else
color = _prevColor; // unchanged
// Check for color change and trade accordingly
if (_prevColor == 1 && color == 0 && Position <= 0)
{
// Slope turned up - buy
if (Position < 0)
BuyMarket();
BuyMarket();
}
else if (_prevColor == 0 && color == 1 && Position >= 0)
{
// Slope turned down - sell
if (Position > 0)
SellMarket();
SellMarket();
}
_prevSma = smaValue;
_prevColor = color;
}
}
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, Unit, UnitTypes
from StockSharp.Algo.Indicators import ExponentialMovingAverage
from StockSharp.Algo.Strategies import Strategy
class volatility_quality_strategy(Strategy):
def __init__(self):
super(volatility_quality_strategy, self).__init__()
self._length = self.Param("Length", 5) \
.SetDisplay("Length", "Smoothing period for median price", "Parameters")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Candle Type", "Timeframe for strategy", "Common")
self._sma = None
self._prev_sma = 0.0
self._prev_color = -1
@property
def length(self):
return self._length.Value
@property
def candle_type(self):
return self._candle_type.Value
def OnReseted(self):
super(volatility_quality_strategy, self).OnReseted()
self._sma = None
self._prev_sma = 0.0
self._prev_color = -1
def OnStarted2(self, time):
super(volatility_quality_strategy, self).OnStarted2(time)
self._sma = ExponentialMovingAverage()
self._sma.Length = self.length
subscription = self.SubscribeCandles(self.candle_type)
subscription.Bind(self._sma, self.process_candle).Start()
self.StartProtection(
takeProfit=Unit(2.0, UnitTypes.Percent),
stopLoss=Unit(1.0, UnitTypes.Percent))
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, subscription)
self.DrawIndicator(area, self._sma)
self.DrawOwnTrades(area)
def process_candle(self, candle, sma_value):
if candle.State != CandleStates.Finished:
return
sma_value = float(sma_value)
if self._prev_sma == 0.0:
self._prev_sma = sma_value
self._prev_color = -1
return
if sma_value > self._prev_sma:
color = 0 # rising
elif sma_value < self._prev_sma:
color = 1 # falling
else:
color = self._prev_color
# Slope turned up - buy
if self._prev_color == 1 and color == 0 and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
# Slope turned down - sell
elif self._prev_color == 0 and color == 1 and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._prev_sma = sma_value
self._prev_color = color
def CreateClone(self):
return volatility_quality_strategy()