Inicio
/
Ejemplos de estrategias
Ver en GitHub
VQ EA
Overview
Conversion of the MetaTrader expert "VQ_EA" that trades using the Volatility Quality (VQ) indicator.
The StockSharp version approximates the VQ line with a smoothed median price to keep the logic within the high-level API.
Positions are opened on direction changes of the smoothed line and managed with optional protective orders.
Original MQL behaviour
Requests buy or sell signals from the VQ custom indicator (buffers 3 and 4).
Opens a new market position when a fresh signal appears and no trade is active in that direction.
Closes the opposite position immediately on an opposite signal.
Optional money-management features: fixed lots, fractional lots, break-even, trailing stop, manual log output and alert/email notifications.
StockSharp implementation
Instead of the proprietary VQ indicator the strategy applies a simple moving average to the median price and optionally smooths it once more.
The slope of the smoothed series plays the role of the original colour change of the VQ line.
A configurable filter expressed in points prevents signals caused by minor fluctuations.
Market orders are used for entries and exits, mirroring the original EA behaviour.
Signal generation
Subscribe to the selected candle type and calculate the median price for each completed candle.
Apply the base moving average (Length) and, if requested, an additional smoothing (Smoothing).
Compare the current smoothed value with the previous one. If the absolute change exceeds FilterPoints (converted into price units), mark the direction as rising or falling.
When the direction flips from down to up a long entry is issued. A flip from up to down produces a short entry. Existing positions are reversed by adding the absolute position volume to the order size.
Risk management
StopLossPoints, TakeProfitPoints and TrailingStopPoints are converted into absolute prices by multiplying with the instrument price step.
If at least one of these protections is enabled, StartProtection is called with market-order adjustments so that stops follow the position like in the MQL expert.
The optional trailing stop is activated only when UseTrailing is true and the trailing distance is greater than zero.
Parameters
Length – base smoothing period of the median price. Default: 5.
Smoothing – secondary smoothing period. Default: 1 (disabled).
FilterPoints – minimal move in points required to confirm that the slope changed. Default: 5.
StopLossPoints – protective stop-loss in points. Default: 60 (0 disables it).
TakeProfitPoints – protective take-profit in points. Default: 0 (disabled).
UseTrailing – enable or disable trailing stops. Default: false.
TrailingStopPoints – trailing distance in points. Default: 0 (ignored when UseTrailing is false).
CandleType – timeframe used for calculations. Default: 1-hour candles.
Volume – inherited from Strategy.Volume, defaults to 1 contract and is used for every fresh entry.
Differences from the original expert
The exact VQ buffer values are approximated by smoothed median prices; the indicator is not ported one-to-one.
Advanced features such as break-even shifts, alert sound scheduling, manual log output and fractional-lot money management are not reproduced.
Trailing step handling is simplified to StockSharp's built-in trailing stop manager.
Usage notes
Signals are generated only on finished candles, matching the "trade at bar close" mode of the original EA.
Ensure that the instrument has a proper PriceStep; otherwise the strategy falls back to a step of 1.0 when converting point-based parameters.
The strategy is intended for demonstration and can be extended with additional money-management rules if required.
using System;
using StockSharp.Algo.Indicators;
using StockSharp.Algo.Strategies;
using StockSharp.BusinessEntities;
using StockSharp.Messages;
namespace StockSharp.Samples.Strategies;
public class VqEaStrategy : Strategy
{
private readonly StrategyParam<int> _emaPeriod;
private readonly StrategyParam<int> _momentumPeriod;
private readonly StrategyParam<DataType> _candleType;
private decimal _prevMom; private bool _hasPrev;
private int _cooldown;
public int EmaPeriod { get => _emaPeriod.Value; set => _emaPeriod.Value = value; }
public int MomentumPeriod { get => _momentumPeriod.Value; set => _momentumPeriod.Value = value; }
public DataType CandleType { get => _candleType.Value; set => _candleType.Value = value; }
public VqEaStrategy()
{
_emaPeriod = Param(nameof(EmaPeriod), 20).SetDisplay("EMA Period", "EMA filter", "Indicators");
_momentumPeriod = Param(nameof(MomentumPeriod), 14).SetDisplay("Momentum", "Momentum period", "Indicators");
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(15).TimeFrame()).SetDisplay("Candle Type", "Candle timeframe", "General");
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_prevMom = default;
_hasPrev = default;
_cooldown = default;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_hasPrev = false;
var ema = new ExponentialMovingAverage { Length = EmaPeriod };
var mom = new Momentum { Length = MomentumPeriod };
var subscription = SubscribeCandles(CandleType);
subscription.Bind(ema, mom, ProcessCandle).Start();
}
private void ProcessCandle(ICandleMessage candle, decimal ema, decimal mom)
{
if (candle.State != CandleStates.Finished) return;
if (!IsFormedAndOnlineAndAllowTrading()) return;
var close = candle.ClosePrice;
if (!_hasPrev) { _prevMom = mom; _hasPrev = true; return; }
if (_cooldown > 0)
{
_cooldown--;
_prevMom = mom;
return;
}
if (close > ema && _prevMom <= 0 && mom > 0 && Position <= 0)
{
var volume = Volume + Math.Abs(Position);
BuyMarket(volume);
_cooldown = 2;
}
else if (close < ema && _prevMom >= 0 && mom < 0 && Position >= 0)
{
var volume = Volume + Math.Abs(Position);
SellMarket(volume);
_cooldown = 2;
}
_prevMom = mom;
}
}
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.Indicators import ExponentialMovingAverage, Momentum
from StockSharp.Algo.Strategies import Strategy
from datatype_extensions import *
from indicator_extensions import *
class vq_ea_strategy(Strategy):
"""EMA trend filter with Momentum zero-cross for entries and cooldown."""
def __init__(self):
super(vq_ea_strategy, self).__init__()
self._ema_period = self.Param("EmaPeriod", 20).SetDisplay("EMA Period", "EMA filter", "Indicators")
self._mom_period = self.Param("MomentumPeriod", 14).SetDisplay("Momentum", "Momentum period", "Indicators")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(15))).SetDisplay("Candle Type", "Timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnReseted(self):
super(vq_ea_strategy, self).OnReseted()
self._prev_mom = 0
self._has_prev = False
self._cooldown = 0
def OnStarted2(self, time):
super(vq_ea_strategy, self).OnStarted2(time)
self._prev_mom = 0
self._has_prev = False
self._cooldown = 0
ema = ExponentialMovingAverage()
ema.Length = self._ema_period.Value
mom = Momentum()
mom.Length = self._mom_period.Value
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(ema, mom, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, ema)
self.DrawOwnTrades(area)
def OnProcess(self, candle, ema_val, mom_val):
if candle.State != CandleStates.Finished:
return
if not self.IsFormedAndOnlineAndAllowTrading():
return
ev = float(ema_val)
mv = float(mom_val)
close = float(candle.ClosePrice)
if not self._has_prev:
self._prev_mom = mv
self._has_prev = True
return
if self._cooldown > 0:
self._cooldown -= 1
self._prev_mom = mv
return
if close > ev and self._prev_mom <= 0 and mv > 0 and self.Position <= 0:
volume = self.Volume + abs(self.Position)
self.BuyMarket(volume)
self._cooldown = 2
elif close < ev and self._prev_mom >= 0 and mv < 0 and self.Position >= 0:
volume = self.Volume + abs(self.Position)
self.SellMarket(volume)
self._cooldown = 2
self._prev_mom = mv
def CreateClone(self):
return vq_ea_strategy()