Estratégia Vector
Visão geral
A Estratégia Vector é um sistema de seguidor de tendência multi-moeda convertido do expert "Vector" do MetaTrader 5. Opera quatro principais pares forex — EURUSD, GBPUSD, USDCHF e USDJPY — simultaneamente. A estratégia calcula médias móveis suavizadas sobre o preço mediano de cada par e abre posições sincronizadas quando a tendência combinada aponta na mesma direção. Um alvo de pips dinâmico baseado na volatilidade de quatro horas e limiares de lucro e perda em nível de portfólio controlam as saídas.
Ideias centrais
- Usar médias móveis suavizadas (SMMA) construídas sobre preços medianos para medir a direção em cada par de moedas.
- Resumir as médias rápidas e lentas de todos os instrumentos para determinar um viés altista ou baixista comum.
- Inserir uma única ordem a mercado por par quando o viés global e o cruzamento rápido/lento local coincidem.
- Gerenciar posições com um alvo de pips flutuante derivado do alcance médio de 50 candles de 4 horas concluídos no EURUSD.
- Fechar todas as operações simultaneamente se o lucro ou perda flutuante atingir a porcentagem configurada do saldo inicial.
Parâmetros
| Parâmetro |
Descrição |
| Fast MA |
Comprimento da média móvel suavizada usada para a tendência rápida em cada par. |
| Slow MA |
Comprimento da média móvel suavizada usada para a tendência lenta em cada par. |
| MA Shift |
Número adicional de candles finalizados exigidos antes de avaliar os sinais, espelhando a configuração de deslocamento no EA original. |
| Equity Take Profit % |
Porcentagem de lucro flutuante que aciona o fechamento de todas as posições abertas. |
| Equity Stop Loss % |
Porcentagem de perda flutuante que aciona uma saída de emergência para todas as operações. |
| Signal Timeframe |
Período de candles usado para as médias móveis suavizadas (padrão 15 minutos). |
| Range Timeframe |
Período de candles usado para a média de volatilidade (padrão 4 horas). |
| Range Period |
Número de candles de período superior usados para calcular o alvo médio de pips. |
| EURUSD / GBPUSD / USDCHF / USDJPY |
Ativos que correspondem a cada instrumento negociado. |
Todos os parâmetros suportam intervalos de otimização idênticos ao expert advisor original onde aplicável.
Lógica de negociação
- Atualização do indicador — Cada candle finalizado em um período de negociação atualiza as médias móveis suavizadas rápida e lenta para o par correspondente. Os valores só são considerados após a conclusão do aquecimento configurado (MA Shift).
- Cálculo do viés — A estratégia soma as últimas médias rápidas de todos os pares e subtrai a soma das médias lentas. Um resultado positivo indica pressão altista, enquanto um negativo indica pressão baixista.
- Condições de entrada — Quando não existe posição para um par, a estratégia entra com uma ordem de compra se o viés global for altista e a média rápida do par estiver acima da lenta. Abre uma ordem de venda no caso contrário.
- Saída por alvo de pips — A assinatura de quatro horas do EURUSD calcula o alcance médio de candles ao longo do período configurado. O alvo de pips atual é o maior entre esta média e 13 pips. Comprados fecham assim que o preço ganha pelo menos o número alvo de pips, e vendidos fecham após um movimento favorável equivalente.
- Proteção de capital — Sempre que o lucro flutuante exceder a porcentagem de take-profit, ou a perda flutuante ultrapassar a porcentagem de stop-loss, a estratégia fecha imediatamente todas as posições gerenciadas.
Notas de uso
- Anexe a estratégia a um portfólio que forneça acesso a todos os quatro instrumentos forex e defina cada parâmetro de ativo explicitamente.
- O período de sinal padrão é 15 minutos; certifique-se de que candles correspondentes estejam disponíveis para cada par de moedas.
- Apenas uma posição aberta por par é mantida a qualquer momento. O parâmetro de volume da estratégia base é usado para cada entrada.
- Como as saídas dependem do L/P flutuante, a estratégia é destinada à operação contínua em vez de apenas backtesting barra a barra.
- O alvo de pips dinâmico usa a volatilidade do EURUSD em linha com a implementação original. Ajuste o período de range ou o período se preferir adaptar o alvo a um ambiente 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()