The Symbol Swap Panel Strategy is a StockSharp conversion of the MQL panel "Symbol Swap Panel". The original expert acted as a chart widget that allowed traders to type a symbol, switch the active chart to that symbol, and monitor real-time market information such as OHLC values, tick volume, and spread. The converted strategy recreates the same workflow in the StockSharp environment. It can be launched on any security and provides a manual toggle to jump to another instrument while continuously logging the most relevant market metrics.
Core behaviour
Subscribes to candle data and level-one quotes for the active security.
Logs every completed candle with open, high, low, close, total volume, and the latest computed spread.
Stores bid/ask quotes and derives an up-to-date spread that mirrors the MQL panel readout.
Reacts to manual swap requests and replaces the monitored security with the chosen identifier without requiring the strategy to restart.
Maintains the previously selected security so that redundant swaps are ignored and accidental double activations do not disrupt the subscriptions.
Parameters
Name
Type
Description
TargetSecurityId
string
Security identifier that should be activated when the swap request is triggered. Empty strings are ignored with a warning.
CandleType
DataType
Candle aggregation for periodic updates (defaults to 1-hour candles, replicating the MQL panel timeframe).
SwapRequested
bool
Manual flag that requests an immediate switch to TargetSecurityId. It resets to false after the swap attempt is processed.
Data subscriptions
Candle subscription created with CandleType for the currently active security.
Level-one subscription used to track bid/ask quotes and compute a live spread value.
Subscriptions are safely restarted whenever the security changes, ensuring stale data streams are not left running.
Workflow
When the strategy starts it resolves the initial security from Strategy.Security or, if missing, from TargetSecurityId.
Candle and level-one subscriptions are opened for that instrument.
Each completed candle triggers a detailed log message that mirrors the text shown in the original panel labels.
Incoming level-one updates refresh the cached bid/ask values.
Setting SwapRequested to true and supplying a valid TargetSecurityId immediately switches the monitored security and restarts the subscriptions.
Usage notes
The strategy is designed for manual monitoring and does not place orders.
The spread is only reported when both bid and ask values are present and positive.
When an invalid or unknown symbol is provided, a warning is logged and the request is discarded without interrupting the running subscriptions.
Because the original tool refreshed the UI once per second, you can lower the candle timeframe if you need more frequent log updates.
Original MQL features preserved
Manual symbol switching through a textual identifier.
Real-time display of OHLC values, volume, and spread for the chosen symbol.
Safeguards against empty inputs and failed Market Watch additions (translated into StockSharp warnings).
Differences from the MQL implementation
The StockSharp strategy uses log messages instead of on-screen labels. This matches the typical workflow inside StockSharp while still exposing the same information.
Chart switching is implemented by reassigning the strategy security and recreating subscriptions instead of altering a terminal chart window.
Timer-based refresh logic is replaced by candle completion events to stay aligned with high-level StockSharp APIs.
Requirements
StockSharp connector with access to the desired securities.
Level-one data feed to obtain bid/ask quotes for spread calculation.
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>
/// Price monitoring strategy that logs OHLC metrics and trades on candle patterns.
/// Simplified from the "Symbol Swap Panel" MQL display widget.
/// </summary>
public class SymbolSwapPanelStrategy : Strategy
{
private readonly StrategyParam<DataType> _candleType;
private readonly StrategyParam<int> _maPeriod;
private SimpleMovingAverage _sma;
private decimal _entryPrice;
private decimal _prevClose;
/// <summary>
/// Candle type for monitoring.
/// </summary>
public DataType CandleType
{
get => _candleType.Value;
set => _candleType.Value = value;
}
/// <summary>
/// Moving average period for trend signals.
/// </summary>
public int MaPeriod
{
get => _maPeriod.Value;
set => _maPeriod.Value = value;
}
/// <summary>
/// Initializes strategy parameters.
/// </summary>
public SymbolSwapPanelStrategy()
{
_candleType = Param(nameof(CandleType), TimeSpan.FromMinutes(30).TimeFrame())
.SetDisplay("Candle Type", "Candle series for monitoring and signals", "General");
_maPeriod = Param(nameof(MaPeriod), 20)
.SetGreaterThanZero()
.SetDisplay("MA Period", "Moving average period for entry signals", "Indicators");
}
/// <inheritdoc />
public override IEnumerable<(Security sec, DataType dt)> GetWorkingSecurities()
{
yield return (Security, CandleType);
}
/// <inheritdoc />
protected override void OnReseted()
{
base.OnReseted();
_sma = null;
_entryPrice = 0m;
_prevClose = 0m;
}
/// <inheritdoc />
protected override void OnStarted2(DateTime time)
{
base.OnStarted2(time);
_sma = new SimpleMovingAverage { Length = MaPeriod };
SubscribeCandles(CandleType)
.Bind(_sma, ProcessCandle)
.Start();
}
private void ProcessCandle(ICandleMessage candle, decimal smaValue)
{
if (candle.State != CandleStates.Finished)
return;
var price = candle.ClosePrice;
var high = candle.HighPrice;
var low = candle.LowPrice;
// Log price info
LogInfo(
$"Time: {candle.CloseTime:O}, O: {candle.OpenPrice}, H: {high}, L: {low}, C: {price}, " +
$"Vol: {candle.TotalVolume}, SMA: {smaValue:F5}");
// Exit: reversal or profit target
if (Position != 0 && _entryPrice > 0m)
{
var pnl = Position > 0
? price - _entryPrice
: _entryPrice - price;
// Exit on trend reversal
if ((Position > 0 && price < smaValue) ||
(Position < 0 && price > smaValue))
{
if (Position > 0)
SellMarket(Math.Abs(Position));
else
BuyMarket(Math.Abs(Position));
_entryPrice = 0m;
_prevClose = price;
return;
}
}
// Entry: follow MA trend with momentum confirmation
if (Position == 0 && _prevClose > 0m)
{
if (price > smaValue && _prevClose <= smaValue)
{
BuyMarket();
_entryPrice = price;
}
else if (price < smaValue && _prevClose >= smaValue)
{
SellMarket();
_entryPrice = price;
}
}
_prevClose = 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_panel_strategy(Strategy):
def __init__(self):
super(symbol_swap_panel_strategy, self).__init__()
self._candle_type = self.Param("CandleType", DataType.TimeFrame(TimeSpan.FromMinutes(30))) \
.SetDisplay("Candle Type", "Candle series for monitoring and signals", "General")
self._ma_period = self.Param("MaPeriod", 20) \
.SetGreaterThanZero() \
.SetDisplay("MA Period", "Moving average period for entry signals", "Indicators")
self._entry_price = 0.0
self._prev_close = 0.0
@property
def CandleType(self):
return self._candle_type.Value
@CandleType.setter
def CandleType(self, value):
self._candle_type.Value = value
@property
def MaPeriod(self):
return self._ma_period.Value
@MaPeriod.setter
def MaPeriod(self, value):
self._ma_period.Value = value
def OnReseted(self):
super(symbol_swap_panel_strategy, self).OnReseted()
self._entry_price = 0.0
self._prev_close = 0.0
def OnStarted2(self, time):
super(symbol_swap_panel_strategy, self).OnStarted2(time)
sma = SimpleMovingAverage()
sma.Length = self.MaPeriod
self.SubscribeCandles(self.CandleType) \
.Bind(sma, self._process_candle) \
.Start()
def _process_candle(self, candle, sma_value):
if candle.State != CandleStates.Finished:
return
price = float(candle.ClosePrice)
sma_v = float(sma_value)
# Exit: reversal against trend
if self.Position != 0 and self._entry_price > 0:
if (self.Position > 0 and price < sma_v) or \
(self.Position < 0 and price > sma_v):
if self.Position > 0:
self.SellMarket(abs(self.Position))
else:
self.BuyMarket(abs(self.Position))
self._entry_price = 0.0
self._prev_close = price
return
# Entry: follow MA trend with momentum confirmation
if self.Position == 0 and self._prev_close > 0:
if price > sma_v and self._prev_close <= sma_v:
self.BuyMarket()
self._entry_price = price
elif price < sma_v and self._prev_close >= sma_v:
self.SellMarket()
self._entry_price = price
self._prev_close = price
def CreateClone(self):
return symbol_swap_panel_strategy()