The Symbol Swap Strategy is the StockSharp port of the MetaTrader 5 utility "Symbol Swap". The original MQL5 program opens a panel where a trader can enter any ticker, immediately switch the current chart to that symbol, and monitor a compact data window with the latest time, OHLC prices, tick volume, and spread. This C# conversion keeps the same responsibilities while relying exclusively on StockSharp's high-level subscription API.
Behaviour
On start the strategy resolves the instrument to watch. It first tries WatchedSecurityId; if the field is empty it falls back to Strategy.Security that is configured in the launcher.
Candle data of the chosen CandleType is streamed through SubscribeCandles(...). Finished bars deliver the open, high, low, close, and tick volume that populate the panel.
Real-time best bid/ask values arrive via SubscribeLevel1(...). The spread is recalculated on every quote update to mirror the MQL data window.
The formatted block is either written to the strategy log (OutputMode = Log) or rendered on a chart (OutputMode = Chart) with DrawText(...), recreating the floating panel from MetaTrader.
Calling SwapSecurity("TICKER") during execution resolves the new security through SecurityProvider.LookupById and seamlessly resubscribes both the candle and Level 1 feeds to the requested instrument.
The strategy is informational only; it does not place orders. It can run standalone as a market dashboard or alongside other trading bots.
Parameters
Name
Description
Default
CandleType
Time frame that defines the candle subscription used to build OHLC and tick volume data.
TimeFrame(1 minute)
WatchedSecurityId
Optional instrument identifier. Leave empty to use Strategy.Security.
empty
OutputMode
Rendering destination of the information block. Choose between Chart (overlay near the price) or Log (strategy log).
Chart
Public methods
Method
Description
SwapSecurity(string securityId)
Resolves the provided ticker through the active SecurityProvider and immediately switches the panel to that symbol. The method can be called multiple times; each call clears previous candle/Level 1 subscriptions before adding the new feeds.
Usage notes
Ensure the connector exposes the requested identifier; otherwise SecurityProvider.LookupById throws an exception.
When OutputMode = Chart, the strategy automatically creates a chart area, draws the subscribed candles, and overlays the status block. For log mode only the textual updates are produced.
Tick volume equals the candle's TotalVolume, which is how MetaTrader reports its per-bar tick count.
Spread is shown only when both best bid and best ask are available. Otherwise the field displays n/a.
Conversion details
The MetaTrader timer loop is replaced with StockSharp subscriptions. Candles trigger once per finished bar and Level 1 quotes refresh the spread in real time.
The MQL panel labels are represented by a single multi-line text block. The text uses the exact ordering from the original tool: Time, Period, Symbol, Close, Open, High, Low, Tick Volume, Spread.
Runtime symbol swaps no longer need manual Market Watch management—the strategy resolves instruments directly via the StockSharp security provider.
Only high-level API calls are used (SubscribeCandles, SubscribeLevel1, DrawText, AddInfo). There are no manual indicator calculations or direct connector manipulations, satisfying the repository coding rules.
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>
/// Market monitoring strategy that tracks price metrics and trades on significant
/// spread changes. Simplified from the MetaTrader "Symbol Swap" display panel.
/// </summary>
public class SymbolSwapStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _smaPeriod;
private readonly StrategyParam<decimal> _spreadThreshold;
private SimpleMovingAverage _sma;
private decimal _entryPrice;
/// <summary>
/// Candle type for monitoring.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// SMA period for trend detection.
/// </summary>
public int SmaPeriod
{
get => _smaPeriod.Value;
set => _smaPeriod.Value = value;
}
/// <summary>
/// Price deviation threshold for entry signals.
/// </summary>
public decimal SpreadThreshold
{
get => _spreadThreshold.Value;
set => _spreadThreshold.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public SymbolSwapStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromHours(1).TimeFrame())
.SetDisplay("Candle Type", "Candle series for signals", "General");
_smaPeriod = Param(nameof(SmaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("SMA Period", "Moving average period", "Indicators");
_spreadThreshold = Param(nameof(SpreadThreshold), 3m)
.SetGreaterThanZero()
.SetDisplay("Spread Threshold", "Price deviation from SMA to trigger entry", "Signals");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_sma = null;
_entryPrice = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_sma = new SimpleMovingAverage { Length = SmaPeriod };
SubscribeCandles(CandleType)
.Bind(_sma, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
if (!IsFormed)
return;
var price = candle.ClosePrice;
// Exit on mean reversion
if (Position != 0 && _entryPrice > 0m)
{
var pnl = Position > 0
? price - _entryPrice
: _entryPrice - price;
// Exit on profit or loss threshold
if (pnl >= SpreadThreshold || pnl <= -SpreadThreshold * 2m)
{
if (Position > 0)
SellMarket(Math.Abs(Position));
else
BuyMarket(Math.Abs(Position));
_entryPrice = 0m;
return;
}
}
// Entry on deviation
if (Position == 0)
{
var deviation = price - smaValue;
if (deviation > SpreadThreshold)
{
SellMarket();
_entryPrice = price;
}
else if (deviation < -SpreadThreshold)
{
BuyMarket();
_entryPrice = price;
}
}
}
}
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_swap_strategy(Strategy):
def __init__(self):
super(symbol_swap_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromHours(1))) \
.SetDisplay("Candle Type", "Candle series for signals", "General")
self._sma_period = self.Param("SmaPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("SMA Period", "Moving average period", "Indicators")
self._spread_threshold = self.Param("SpreadThreshold", 3.0) \
.SetGreaterThanZero() \
.SetDisplay("Spread Threshold", "Price deviation from SMA to trigger entry", "Signals")
self._entry_price = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def SmaPeriod(self):
return self._sma_period.Value
@SmaPeriod.setter
def SmaPeriod(self, value):
self._sma_period.Value = value
@property
def SpreadThreshold(self):
return self._spread_threshold.Value
@SpreadThreshold.setter
def SpreadThreshold(self, value):
self._spread_threshold.Value = value
def OnReseted(self):
super(symbol_swap_strategy, self).OnReseted()
self._entry_price = 0.0
def OnStarted2(self, time):
super(symbol_swap_strategy, self).OnStarted2(time)
sma = SimpleMovingAverage()
sma.Length = self.SmaPeriod
self.SubscribeCandles(self.CandleType) \
.Bind(sma, self._process_candle) \
.Start()
def _process_candle(self, candle, sma_value):
if candle.State != CandleStates.Finished:
return
if not self.IsFormed:
return
price = float(candle.ClosePrice)
sma_v = float(sma_value)
threshold = float(self.SpreadThreshold)
# Exit on mean reversion
if self.Position != 0 and self._entry_price > 0:
pnl = price - self._entry_price if self.Position > 0 else self._entry_price - price
if pnl >= threshold or pnl <= -threshold * 2:
if self.Position > 0:
self.SellMarket(abs(self.Position))
else:
self.BuyMarket(abs(self.Position))
self._entry_price = 0.0
return
# Entry on deviation
if self.Position == 0:
deviation = price - sma_v
if deviation > threshold:
self.SellMarket()
self._entry_price = price
elif deviation < -threshold:
self.BuyMarket()
self._entry_price = price
def CreateClone(self):
return symbol_swap_strategy()