A Estratégia de Sincronização de Símbolos replica o utilitário MetaTrader SymbolSyncEA dentro do ambiente StockSharp. A estratégia mantém sincronizados o símbolo da estratégia principal e todas as estratégias vinculadas cadastradas. Sempre que o símbolo primário muda, a estratégia propaga automaticamente a nova segurança para cada estratégia vinculada, garantindo que todo o espaço de trabalho siga o mesmo instrumento sem intervenção manual.
Ideias centrais
Capture a segurança da estratégia inicial na inicialização e reutilize-a como uma opção alternativa.
Mantenha uma lista configurável de estratégias vinculadas que sempre devem espelhar a segurança principal.
Permitir alterações de símbolos acionadas por uma atribuição Security direta ou pela especificação de um novo identificador de segurança.
Fornece sincronização manual e operações de redefinição para corresponder ao comportamento original do Expert Advisor.
Parâmetros
Nome
Descrição
Padrão
ChartLimit
Número máximo de estratégias vinculadas que podem ser sincronizadas. Evita atualizações em massa acidentais.
10
SyncSecurityId
Identificador do título propagado para estratégias vinculadas. Um valor vazio retorna para a segurança da estratégia.
""
Métodos públicos
RegisterLinkedStrategy(Strategy strategy) – adiciona uma instância de estratégia à lista de sincronização. Retorna true quando registrado com sucesso.
UnregisterLinkedStrategy(Strategy strategy) – remove uma estratégia da lista.
ChangeSyncSecurity(Security security) – alterna para a instância de segurança fornecida e a propaga para todas as estratégias vinculadas.
ChangeSyncSecurity(string securityId) – resolve o identificador por meio do SecurityProvider atual e chama ChangeSyncSecurity(Security).
ResetToInitialSecurity() – restaura o símbolo capturado na inicialização.
SyncSymbols() – força uma ressincronização manual sem alterar o identificador armazenado.
Fluxo de trabalho de uso
Instancie SymbolSyncStrategy e defina o Security primário ou atribua SyncSecurityId antes de iniciar a estratégia.
Chame RegisterLinkedStrategy para cada estratégia filha que deve espelhar o símbolo ativo (por exemplo, diferentes períodos de tempo ou painéis).
Sempre que o símbolo principal mudar, chame ChangeSyncSecurity(Security) ou ChangeSyncSecurity(string).
Opcionalmente, chame SyncSymbols() para forçar a propagação se um componente externo modificou uma estratégia vinculada.
Diferenças em comparação com a versão MQL
Funciona com StockSharp Strategy instâncias em vez de MetaTrader janelas de gráfico.
Usa a abstração SecurityProvider para resolver identificadores.
Adiciona registro defensivo e um limite configurável para estratégias sincronizadas.
Oferece métodos explícitos de redefinição e sincronização manual para cenários de automação avançados.
Notas
A estratégia não emite ordens de mercado; funciona como um auxiliar de infraestrutura.
Todos os comentários do código são mantidos em inglês para atender aos requisitos do projeto.
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>
/// Strategy that synchronizes the security of linked strategies whenever the main symbol changes.
/// </summary>
public class SymbolSyncStrategy : Strategy
{
private readonly StrategyParam<int> _chartLimit;
private readonly StrategyParam<string> _syncSecurityId;
private readonly List<Strategy> _linkedStrategies = new();
private Security _initialSecurity;
public SymbolSyncStrategy()
{
_chartLimit = Param(nameof(ChartLimit), 10)
.SetNotNegative()
.SetDisplay("Chart limit", "Maximum number of linked strategies that can be synchronized.", "General")
;
_syncSecurityId = Param(nameof(SyncSecurityId), string.Empty)
.SetDisplay("Sync security ID", "Identifier of the security propagated to linked strategies.", "General")
;
}
/// <summary>
/// Maximum number of linked strategies that can follow the symbol changes.
/// </summary>
public int ChartLimit
{
get => _chartLimit.Value;
set => _chartLimit.Value = value;
}
/// <summary>
/// Identifier of the security that must be mirrored by linked strategies.
/// </summary>
public string SyncSecurityId
{
get => _syncSecurityId.Value;
set => _syncSecurityId.Value = value ?? string.Empty;
}
private SimpleMovingAverage _smaFast;
private SimpleMovingAverage _smaSlow;
private int _candleCount;
private int _lastTradeCandle;
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, TimeSpan.FromMinutes(5).TimeFrame());
}
protected override void OnReseted()
{
base.OnReseted();
_linkedStrategies.Clear();
_initialSecurity = default;
_smaFast = default;
_smaSlow = default;
_candleCount = default;
_lastTradeCandle = default;
_syncSecurityId.Value = string.Empty;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_initialSecurity = Security;
if (SyncSecurityId.IsEmpty() && Security != null)
SyncSecurityId = Security.Id;
_smaFast = new SimpleMovingAverage { Length = 10 };
_smaSlow = new SimpleMovingAverage { Length = 30 };
var subscription = SubscribeCandles(TimeSpan.FromMinutes(5).TimeFrame());
subscription
.Bind(_smaFast, _smaSlow, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal fast, decimal slow)
{
if (candle.State != CandleStates.Finished)
return;
_candleCount++;
if (_candleCount - _lastTradeCandle < 200)
return;
if (fast > slow && Position <= 0)
{
if (Position < 0)
BuyMarket();
BuyMarket();
_lastTradeCandle = _candleCount;
}
else if (fast < slow && Position >= 0)
{
if (Position > 0)
SellMarket();
SellMarket();
_lastTradeCandle = _candleCount;
}
}
/// <inheritdoc />
protected override void OnStopped()
{
base.OnStopped();
_initialSecurity = null;
}
/// <summary>
/// Registers an additional strategy that must mirror the current security.
/// </summary>
/// <param name="strategy">Strategy that will receive symbol updates.</param>
/// <returns><c>true</c> when the strategy is registered; otherwise <c>false</c>.</returns>
public bool RegisterLinkedStrategy(Strategy strategy)
{
if (strategy == null)
throw new ArgumentNullException(nameof(strategy));
if (_linkedStrategies.Contains(strategy))
return false;
var limit = Math.Max(ChartLimit, 0);
if (_linkedStrategies.Count >= limit)
{
LogWarning($"Chart limit of {limit} reached. Strategy '{strategy.Name}' cannot be synchronized.");
return false;
}
_linkedStrategies.Add(strategy);
ApplySymbol(strategy);
return true;
}
/// <summary>
/// Removes a strategy from the synchronization list.
/// </summary>
/// <param name="strategy">Strategy previously added with <see cref="RegisterLinkedStrategy"/>.</param>
/// <returns><c>true</c> when the strategy was removed.</returns>
public bool UnregisterLinkedStrategy(Strategy strategy)
{
if (strategy == null)
throw new ArgumentNullException(nameof(strategy));
return _linkedStrategies.Remove(strategy);
}
/// <summary>
/// Restores the initial security captured when the strategy started.
/// </summary>
public void ResetToInitialSecurity()
{
if (_initialSecurity == null)
return;
ChangeSyncSecurity(_initialSecurity);
}
/// <summary>
/// Changes the synchronization security using a resolved <see cref="Security"/> instance.
/// </summary>
/// <param name="security">Security that should be mirrored by linked strategies.</param>
/// <returns><c>true</c> when the identifier changed.</returns>
public bool ChangeSyncSecurity(Security security)
{
if (security == null)
throw new ArgumentNullException(nameof(security));
if (Security != security)
Security = security;
if (SyncSecurityId.EqualsIgnoreCase(security.Id))
{
SyncSymbols();
return false;
}
SyncSecurityId = security.Id;
SyncSymbols();
return true;
}
/// <summary>
/// Changes the synchronization security by resolving the identifier through <see cref="Strategy.Connector"/>.
/// </summary>
/// <param name="securityId">Identifier of the security to use for synchronization.</param>
/// <returns><c>true</c> when the identifier resolved to a new security.</returns>
public bool ChangeSyncSecurity(string securityId)
{
if (securityId.IsEmpty())
throw new ArgumentNullException(nameof(securityId));
if (Connector != null)
{
var resolved = Connector.LookupById(securityId);
if (resolved != null)
return ChangeSyncSecurity(resolved);
LogWarning($"Security '{securityId}' not found by the security provider.");
}
SyncSecurityId = securityId;
SyncSymbols();
return false;
}
/// <summary>
/// Synchronizes the security across every registered strategy.
/// </summary>
/// <returns><c>true</c> when a security was resolved and propagated.</returns>
public bool SyncSymbols()
{
var security = ResolveSecurity();
if (security == null)
{
LogWarning("No synchronization security resolved. Linked strategies keep their current assignments.");
return false;
}
if (Security != security)
Security = security;
foreach (var strategy in _linkedStrategies)
ApplySymbol(strategy);
return true;
}
private void ApplySymbol(Strategy strategy)
{
if (strategy == null)
return;
var security = ResolveSecurity();
if (security == null)
return;
if (strategy.Security == security)
return;
strategy.Security = security;
}
private Security ResolveSecurity()
{
if (Security != null && (SyncSecurityId.IsEmpty() || SyncSecurityId.EqualsIgnoreCase(Security.Id)))
return Security;
if (!SyncSecurityId.IsEmpty() && Connector != null)
{
var resolved = Connector.LookupById(SyncSecurityId);
if (resolved != null)
return resolved;
}
return Security;
}
}
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 SimpleMovingAverage
from StockSharp.Algo.Strategies import Strategy
class symbol_sync_strategy(Strategy):
"""Fast/slow SMA crossover (10/30) on 5-min candles."""
def __init__(self):
super(symbol_sync_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(5))).SetDisplay("Candle Type", "Candle timeframe", "General")
@property
def CandleType(self): return self._candle_type.Value
@CandleType.setter
def CandleType(self, value): self._candle_type.Value = value
def OnStarted2(self, time):
super(symbol_sync_strategy, self).OnStarted2(time)
fast = SimpleMovingAverage()
fast.Length = 10
slow = SimpleMovingAverage()
slow.Length = 30
sub = self.SubscribeCandles(self.CandleType)
sub.Bind(fast, slow, self.OnProcess).Start()
area = self.CreateChartArea()
if area is not None:
self.DrawCandles(area, sub)
self.DrawIndicator(area, fast)
self.DrawIndicator(area, slow)
self.DrawOwnTrades(area)
def OnProcess(self, candle, fast_val, slow_val):
if candle.State != CandleStates.Finished:
return
if fast_val > slow_val and self.Position <= 0:
if self.Position < 0:
self.BuyMarket()
self.BuyMarket()
elif fast_val < slow_val and self.Position >= 0:
if self.Position > 0:
self.SellMarket()
self.SellMarket()
def CreateClone(self):
return symbol_sync_strategy()