Estrategia Vector
Descripción general
La Estrategia Vector es un sistema de seguimiento de tendencia multi-divisa convertido del experto "Vector" de MetaTrader 5. Opera cuatro pares forex principales — EURUSD, GBPUSD, USDCHF y USDJPY — simultáneamente. La estrategia calcula medias móviles suavizadas sobre el precio mediano de cada par y abre posiciones sincronizadas cuando la tendencia combinada apunta en la misma dirección. Un objetivo de pips dinámico basado en la volatilidad de cuatro horas y umbrales de ganancia y pérdida a nivel de cartera controlan las salidas.
Ideas principales
- Usar medias móviles suavizadas (SMMA) construidas sobre precios medianos para medir la dirección en cada par de divisas.
- Resumir las medias rápidas y lentas de todos los instrumentos para determinar un sesgo alcista o bajista común.
- Ingresar una única orden de mercado por par cuando el sesgo global y el cruce rápido/lento local coinciden.
- Gestionar posiciones con un objetivo de pips flotante derivado del rango promedio de 50 velas de 4 horas completadas en EURUSD.
- Cerrar todas las operaciones simultáneamente si la ganancia o pérdida flotante alcanza el porcentaje configurado del saldo inicial.
Parámetros
| Parámetro |
Descripción |
| Fast MA |
Longitud de la media móvil suavizada utilizada para la tendencia rápida en cada par. |
| Slow MA |
Longitud de la media móvil suavizada utilizada para la tendencia lenta en cada par. |
| MA Shift |
Número adicional de velas finalizadas requeridas antes de que se evalúen las señales, reflejando la configuración de desplazamiento en el EA original. |
| Equity Take Profit % |
Porcentaje de ganancia flotante que activa el cierre de todas las posiciones abiertas. |
| Equity Stop Loss % |
Porcentaje de pérdida flotante que activa una salida de emergencia para todas las operaciones. |
| Signal Timeframe |
Marco temporal de velas usado para las medias móviles suavizadas (por defecto 15 minutos). |
| Range Timeframe |
Marco temporal de velas usado para el promedio de volatilidad (por defecto 4 horas). |
| Range Period |
Número de velas de marco temporal superior usadas para calcular el objetivo de pips promedio. |
| EURUSD / GBPUSD / USDCHF / USDJPY |
Valores que corresponden a cada instrumento operado. |
Todos los parámetros soportan rangos de optimización idénticos al asesor experto original donde aplica.
Lógica de trading
- Actualización de indicador — Cada vela finalizada en un marco temporal de trading actualiza las medias móviles suavizadas rápida y lenta para el par correspondiente. Los valores solo se consideran después de que se completa el precalentamiento configurado (MA Shift).
- Cálculo de sesgo — La estrategia suma las últimas medias rápidas de todos los pares y resta la suma de medias lentas. Un resultado positivo indica presión alcista, mientras que uno negativo indica presión bajista.
- Condiciones de entrada — Cuando no existe posición para un par, la estrategia ingresa una orden de compra si el sesgo global es alcista y la media rápida del par está por encima de la lenta. Abre una orden de venta en el caso contrario.
- Salida por objetivo de pips — La suscripción de cuatro horas de EURUSD calcula el rango de vela promedio sobre el período configurado. El objetivo de pips actual es el mayor entre este promedio y 13 pips. Los largos cierran una vez que el precio gana al menos el número objetivo de pips, y los cortos cierran después de un movimiento favorable equivalente.
- Protección de capital — Siempre que la ganancia flotante supere el porcentaje de take-profit, o la pérdida flotante supere el porcentaje de stop-loss, la estrategia cierra inmediatamente todas las posiciones gestionadas.
Notas de uso
- Adjunte la estrategia a una cartera que proporcione acceso a los cuatro instrumentos forex y establezca cada parámetro de seguridad explícitamente.
- El marco temporal de señal predeterminado es 15 minutos; asegúrese de que las velas coincidentes estén disponibles para cada par de divisas.
- Solo se mantiene una posición abierta por par en cualquier momento. El parámetro de volumen de la estrategia base se usa para cada entrada.
- Dado que las salidas dependen de la ganancia/pérdida flotante, la estrategia está pensada para operación continua en lugar de solo backtesting barra a barra.
- El objetivo de pips dinámico usa la volatilidad de EURUSD en línea con la implementación original. Ajuste el marco temporal de rango o el período si prefiere adaptar el objetivo a un entorno de mercado diferente.
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>
/// Trend strategy using smoothed moving averages. Simplified from multi-currency Vector.
/// </summary>
public class VectorStrategy : Strategy
{
private readonly StrategyParam<int> _fastMaPeriod;
private readonly StrategyParam<int> _slowMaPeriod;
private readonly StrategyParam<int> _maShift;
private readonly StrategyParam<decimal> _profitPercent;
private readonly StrategyParam<decimal> _lossPercent;
private readonly StrategyParam<DataType> _candleType;
private SmoothedMovingAverage _fastMa;
private SmoothedMovingAverage _slowMa;
private decimal _entryPrice;
private decimal _initialBalance;
private int _processedBars;
/// <summary>
/// Fast smoothed moving average period.
/// </summary>
public int FastMaPeriod
{
get => _fastMaPeriod.Value;
set => _fastMaPeriod.Value = value;
}
/// <summary>
/// Slow smoothed moving average period.
/// </summary>
public int SlowMaPeriod
{
get => _slowMaPeriod.Value;
set => _slowMaPeriod.Value = value;
}
/// <summary>
/// Additional warm-up shift in bars.
/// </summary>
public int MaShift
{
get => _maShift.Value;
set => _maShift.Value = value;
}
/// <summary>
/// Floating profit target percent of balance.
/// </summary>
public decimal ProfitPercent
{
get => _profitPercent.Value;
set => _profitPercent.Value = value;
}
/// <summary>
/// Floating loss limit percent of balance.
/// </summary>
public decimal LossPercent
{
get => _lossPercent.Value;
set => _lossPercent.Value = value;
}
/// <summary>
/// Candle type for signals.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public VectorStrategy()
{
_fastMaPeriod = Param(nameof(FastMaPeriod), 3)
.SetGreaterThanZero()
.SetDisplay("Fast MA", "Fast smoothed moving average period", "Indicators")
.SetOptimize(3, 15, 1);
_slowMaPeriod = Param(nameof(SlowMaPeriod), 7)
.SetGreaterThanZero()
.SetDisplay("Slow MA", "Slow smoothed moving average period", "Indicators")
.SetOptimize(5, 25, 1);
_maShift = Param(nameof(MaShift), 8)
.SetNotNegative()
.SetDisplay("MA Shift", "Additional warm-up bars before signals", "Indicators");
_profitPercent = Param(nameof(ProfitPercent), 0.5m)
.SetNotNegative()
.SetDisplay("Equity TP %", "Close all when floating profit reaches this percent", "Risk");
_lossPercent = Param(nameof(LossPercent), 30m)
.SetNotNegative()
.SetDisplay("Equity SL %", "Close all when floating loss reaches this percent", "Risk");
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(4).TimeFrame())
.SetDisplay("Signal Timeframe", "Timeframe for moving averages", "General");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_fastMa = null;
_slowMa = null;
_entryPrice = 0m;
_initialBalance = 0m;
_processedBars = 0;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_fastMa = new SmoothedMovingAverage { Length = FastMaPeriod };
_slowMa = new SmoothedMovingAverage { Length = SlowMaPeriod };
_initialBalance = Portfolio?.CurrentValue ?? 0m;
SubscribeCandles(CandleType)
.Bind(_fastMa, _slowMa, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fastValue, decimal slowValue)
{
if (candle.State != CandleStates.Finished)
return;
_processedBars++;
if (!IsFormed)
return;
if (_processedBars <= MaShift)
return;
// Check equity thresholds
if (Position != 0 && _initialBalance > 0m)
{
var equity = Portfolio?.CurrentValue ?? 0m;
var floating = equity - _initialBalance;
var profitThreshold = _initialBalance * ProfitPercent / 100m;
var lossThreshold = _initialBalance * LossPercent / 100m;
if ((profitThreshold > 0m && floating >= profitThreshold) ||
(lossThreshold > 0m && floating <= -lossThreshold))
{
if (Position > 0) SellMarket();
else if (Position < 0) BuyMarket();
_entryPrice = 0m;
return;
}
}
// Entry/exit logic
if (fastValue > slowValue && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_entryPrice = candle.ClosePrice;
}
else if (fastValue < slowValue && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_entryPrice = candle.ClosePrice;
}
}
}
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.Strategies import Strategy
from StockSharp.Algo.Indicators import SmoothedMovingAverage
class vector_strategy(Strategy):
"""Trend strategy using smoothed moving averages with equity-based exit."""
def __init__(self):
super(vector_strategy, self).__init__()
self._fast_ma_period = self.Param("FastMaPeriod", 3) \
.SetGreaterThanZero() \
.SetDisplay("Fast MA", "Fast smoothed moving average period", "Indicators")
self._slow_ma_period = self.Param("SlowMaPeriod", 7) \
.SetGreaterThanZero() \
.SetDisplay("Slow MA", "Slow smoothed moving average period", "Indicators")
self._ma_shift = self.Param("MaShift", 8) \
.SetNotNegative() \
.SetDisplay("MA Shift", "Additional warm-up bars before signals", "Indicators")
self._profit_percent = self.Param("ProfitPercent", 0.5) \
.SetNotNegative() \
.SetDisplay("Equity TP %", "Close all when floating profit reaches this percent", "Risk")
self._loss_percent = self.Param("LossPercent", 30.0) \
.SetNotNegative() \
.SetDisplay("Equity SL %", "Close all when floating loss reaches this percent", "Risk")
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(4))) \
.SetDisplay("Signal Timeframe", "Timeframe for moving averages", "General")
self._entry_price = 0.0
self._initial_balance = 0.0
self._processed_bars = 0
@property
def FastMaPeriod(self):
return self._fast_ma_period.Value
@property
def SlowMaPeriod(self):
return self._slow_ma_period.Value
@property
def MaShift(self):
return self._ma_shift.Value
@property
def ProfitPercent(self):
return self._profit_percent.Value
@property
def LossPercent(self):
return self._loss_percent.Value
@property
def CandleType(self):
return self._candle_type.Value
def OnStarted2(self, time):
super(vector_strategy, self).OnStarted2(time)
fast = SmoothedMovingAverage()
fast.Length = self.FastMaPeriod
slow = SmoothedMovingAverage()
slow.Length = self.SlowMaPeriod
portfolio = self.Portfolio
self._initial_balance = float(portfolio.CurrentValue) if portfolio is not None and portfolio.CurrentValue is not None else 0.0
subscription = self.SubscribeCandles(self.CandleType)
subscription \
.Bind(fast, slow, self.process_candle) \
.Start()
def process_candle(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
self._processed_bars += 1
if not self.IsFormed:
return
if self._processed_bars <= self.MaShift:
return
fast = float(fast_val)
slow = float(slow_val)
# Check equity thresholds
if self.Position != 0 and self._initial_balance > 0:
portfolio = self.Portfolio
equity = float(portfolio.CurrentValue) if portfolio is not None and portfolio.CurrentValue is not None else 0.0
floating = equity - self._initial_balance
profit_threshold = self._initial_balance * float(self.ProfitPercent) / 100.0
loss_threshold = self._initial_balance * float(self.LossPercent) / 100.0
if (profit_threshold > 0 and floating >= profit_threshold) or \
(loss_threshold > 0 and floating <= -loss_threshold):
if self.Position > 0:
self.SellMarket()
elif self.Position < 0:
self.BuyMarket()
self._entry_price = 0.0
return
# Entry/exit logic
if fast > slow and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
self._entry_price = float(candle.ClosePrice)
elif fast < slow and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
self._entry_price = float(candle.ClosePrice)
def OnReseted(self):
super(vector_strategy, self).OnReseted()
self._entry_price = 0.0
self._initial_balance = 0.0
self._processed_bars = 0
def CreateClone(self):
return vector_strategy()