Loading...
Searching...
No Matches
Watchlist columns

Overview

The ATAS Watchlist shows one row per instrument and one column per value: last price, best bid and ask, day open/high/low, volume, change and so on. Besides these built-in columns the Watchlist can show columns from your own assembly. Such a column:

  • computes the cell of one instrument from the instrument data: trades, best bid/ask changes and the exchange statistics (day open/high/low, volume, previous close, …);
  • publishes the cell as a value — text, an optional number to sort by and optional colors; the platform draws it like the built-in cells, so the column looks the same in the classic ATAS and in ATAS X, and the user can sort by it;
  • has settings — public properties saved with the workspace and the Watchlist lists.

A column has no access to orders, accounts or positions and does not draw by itself.

Building a column in your own assembly

Create a class library exactly as for a chart indicator (see «Development of a user indicator»). Reference ATAS.Indicators.dll from the platform folder with <Private>false</Private>.

A column is:

  • a public class derived from WatchlistColumn (namespace ATAS.Indicators.Watchlist) with a public parameterless constructor;
  • marked with the [WatchlistColumn(id)] attribute — the stable column id (see below) and, optionally, DisplayName: the column header and its name in the column chooser.

One instance per row

The platform creates one instance of your class per Watchlist row and attaches it to the instrument of that row (Instrument: name, exchange, tick size, and TimeZone / TimeZoneOffset — the time zone set for the instrument in the platform, as chart indicators get it). Write the column for a single instrument: keep its state in ordinary fields, no dictionaries keyed by instrument are needed. A Watchlist holds up to 500 instruments, so keep the instance small and its callbacks cheap.

Instances exist only while the column is shown: hiding the column drops them, showing it again creates new ones.

Lifecycle

Method Called
constructor when the column is shown (and the account and the tariff allow it), once per row; the saved settings are applied right after it
OnInitialize() when the instance is attached to its row; Instrument is available from here on
OnSettingsChanged() after OnInitialize, and after the user changed the column settings
OnSummaryChanged(summary) right after OnSettingsChanged when the data feed has already reported the exchange statistics of the instrument, and every time they change
OnNewTrades(trades) new trades of the instrument (MarketDataArg, DataType == Trade), oldest first
OnBestBidAskChanged(bestPrice) the best bid or ask changed (DataType is Bid or Ask)
OnTimer() about once per second while the column is shown
OnDispose() the platform stops using the instance: its row or the column was removed, the column was hidden, the Watchlist was closed (also on a workspace switch or exit), the column was stopped by an error, the account or the tariff no longer allows it, its settings were replaced as a whole (a layout or Watchlist list applied), or the DLL was reloaded. Called exactly once for every instance whose constructor completed, even if OnInitialize was not reached or threw; release timers, files and your own subscriptions here. No other method is called afterwards; an exception here is logged and ignored

InstrumentDailySummary (namespace ATAS.Indicators, also used by custom modules) carries the statistics the built-in columns show: best prices and volumes, the last trade, the current day (DayOpen, DayHigh, DayLow, DayVolume, DayTurnover), PreviousDayClose, the last 24 hours (crypto exchanges), OpenInterest, SettlementPrice. A value is null while the feed has not reported it; which values a feed reports depends on the exchange.

The cell

Assign Value whenever the cell changes; null shows an empty cell. A WatchlistCellValue is immutable — create a new one to change the cell:

Member Meaning
Text the text shown in the cell
SortKey the number the column is sorted by; a cell with a number is right-aligned like the built-in numeric columns. null sorts the cell by its text and aligns it to the left
TextColor, BackgroundColor System.Drawing.Color of the text and the cell background, or null for the regular Watchlist colors. The background is not drawn while the row is selected
WatchlistCellValue.FromNumber(value, format) a numeric cell: the formatted value, sorted by it

When the user sorts by your column, empty cells come first in ascending order, then the cells with a number (by the number), then the text-only cells (by the text). The platform reads the published values when it draws and sorts, it never calls your column for that — so drawing and sorting stay fast however many rows there are.

Custom columns cannot be used in the Watchlist filter yet.

Threading

  • The platform never calls one instance concurrently, and all the rows of a column share one lock: your fields need no locks.
  • The callbacks are not called on the data feed thread and never while the Watchlist is drawn: the platform copies the data and delivers it shortly afterwards (within about 0.1 s) on a background thread. The lists passed to you belong to the call — do not keep them.
  • Data reaches your column only while it is shown; a hidden column costs nothing.
  • Trades are delivered in arrival order. Best prices and the exchange statistics are a current state: if they change several times between two deliveries, your column gets only the latest value.
  • Delivery is time-sliced: one pass hands your column what was waiting when it started and stops after about 8 ms; the rest follows on the next pass. A column that is slower than its data falls behind, and when too much data is waiting newer trades are skipped.
  • Time limit. A single call that takes longer than 100 ms is written to the log once. If that happens 5 times within 10 seconds, the platform stops the column like a column that threw an exception (see «Errors»).

Settings

Public properties with a getter and a setter are the column settings. They are saved in the workspace and in the user's Watchlist lists, and restored into every row instance right after its constructor, before OnInitialize. The first time the column is shown, the values your constructor sets are saved as the column's settings. Settings must be of concrete serializable types (numbers, strings, bool, enums, colors); a setting that cannot be restored is skipped and keeps its default value.

The user edits them in the Watchlist settings window, Custom columns tab (shown when a custom column with settings is shown), one group per column; use [Display(Name = …, GroupName = …, Order = …)] for names, groups and order and [Browsable(false)] for values you want to keep but not show. The editor works on the saved values, not on your instances: after a change every instance gets the new values and OnSettingsChanged, on the processing thread as usual. Keep settings of simple types — a nested settings object is edited as a copy and its changes are not saved.

The same settings are what an AI agent connected through ATAS Connect sees and changes (the settings scope watchlistColumn of a custom column): it reads the names of [Display] and the limits of [Range], so describe your settings with them.

Errors

If one of your methods throws (in any row), or the column repeatedly exceeds the time limit, the platform writes the reason to the log, stops the whole column — every row — and shows «Error» in its header; its cells become empty and the rest of the Watchlist keeps working. The column starts again with new instances when the user hides and shows it again, or when the layout is loaded again (workspace or Watchlist list); the settings of a stopped column are not offered in the settings window until it runs again. Do not rely on this as a control flow — check your inputs instead.

Example

The column below shows the spread — the distance between the best ask and the best bid — in ticks, and highlights wide spreads.

using System.ComponentModel.DataAnnotations;
using System.Drawing;
namespace MyCompany.WatchlistColumns;
[WatchlistColumn("mycompany.spread-ticks", DisplayName = "Spread, ticks")]
public sealed class SpreadInTicksColumn : WatchlistColumn
{
private decimal? _bid;
private decimal? _ask;
[Display(Name = "Wide spread, ticks", Order = 10)]
public int WideSpreadTicks { get; set; } = 3;
[Display(Name = "Wide spread color", Order = 20)]
public Color WideSpreadColor { get; set; } = Color.OrangeRed;
protected override void OnSummaryChanged(InstrumentDailySummary summary)
{
// The exchange statistics carry the best prices too: a value before the first quote arrives.
_bid ??= summary.BestBidPrice;
_ask ??= summary.BestAskPrice;
Update();
}
protected override void OnBestBidAskChanged(MarketDataArg bestPrice)
{
if (bestPrice.DataType == MarketDataType.Bid)
_bid = bestPrice.Price;
else
_ask = bestPrice.Price;
Update();
}
private void Update()
{
if (_bid is not { } bid || _ask is not { } ask || Instrument.TickSize <= 0)
{
Value = null;
return;
}
var ticks = (ask - bid) / Instrument.TickSize;
Value = new WatchlistCellValue(ticks.ToString("0.#"), ticks)
{
TextColor = ticks >= WideSpreadTicks ? WideSpreadColor : null
};
}
}
Represents an order for trading on a financial exchange.
Definition Order.cs:13
The statistics of an instrument reported by the exchange (best prices, the last trade,...
Definition InstrumentDailySummary.cs:10
decimal? BestAskPrice
Best ask price.
Definition InstrumentDailySummary.cs:20
decimal? BestBidPrice
Best bid price.
Definition InstrumentDailySummary.cs:14
Represents a data point in the market.
Definition MarketDataArg.cs:11
decimal Price
Price of the market data.
Definition MarketDataArg.cs:17
MarketDataType DataType
Type of the market data (Bid, Ask, or Trade).
Definition MarketDataArg.cs:42
The content of one Watchlist cell, published by a WatchlistColumn through WatchlistColumn....
Definition WatchlistCellValue.cs:17
Base class of a Watchlist column written in a custom assembly. Derive from it, mark the class with Wa...
Definition WatchlistColumn.cs:35
MarketDataType
Specifies the type of market data.
Definition DataFeedsCore/MarketDataType.cs:7
Definition WatchlistCellValue.cs:1
Definition IIndicatorAnalyticsService.cs:1
@ Value
Represents a value-based data series.

The colors are System.Drawing.Color: the cell colors are drawn by the platform in both ATAS and ATAS X, so the column needs no WPF types.

The column id

The argument of [WatchlistColumn] is the column id: the platform saves the column in workspaces and Watchlist lists under this id, not under the class name. The rules are the same as for SmartDOM columns:

  • use the form <vendor>.<column-name> in lowercase letters, digits and dashes, for example mycompany.spread-ticks; the vendor part should identify you (company or author name);
  • the vendor atas is reserved: a column from your assembly whose id starts with atas. is rejected when the assembly is loaded, and the reason is written to the platform log;
  • never change the id of a released column — saved layouts would lose it; renaming the class or the namespace, on the contrary, is safe;
  • two Watchlist columns cannot share an id; if they do, only one of them is listed. A Watchlist column and a SmartDOM column may use the same id.

Problems found while loading (an invalid, reserved or duplicate id, a missing public parameterless constructor) are written to the platform log, and the column is not listed.

Installing the column

Put the DLL into the Extensions folder or into the Indicators folder next to it, where chart indicators are installed (AppData%\ATAS\Extensions or AppData%\ATAS\Indicators for the classic ATAS, AppData%\ATAS X\Extensions or AppData%\ATAS X\Indicators for ATAS X; the platform reads both), or distribute it through the marketplace — one DLL may contain chart indicators, heatmap indicators, SmartDOM columns and Watchlist columns together.

Your columns are offered in the column chooser of the Watchlist — the list where the built-in columns are shown and hidden — marked «(Custom)» after the name; check one to show it. The platform watches both folders. ATAS X loads a new or replaced DLL by itself about a second after the files stop changing; the classic ATAS shows a reload button in the status bar and loads the libraries when the user presses it (an AI agent uses the ATAS Connect tool extensions_reload). A file that could not be loaded is named with the reason and does not stop the other files — see Extending %ATAS, «Reloading a library». After the libraries are reloaded, a new DLL is offered in the open Watchlists without a restart, and columns already shown switch to the new build and keep their settings.

If the DLL is removed, saved Watchlist layouts keep the column: it is shown empty with «Not available» in the header and comes back with its settings when the DLL is installed again.

To sell a column, mark its class with [FeatureId("…")] (namespace OFT.Attributes), as for chart and heatmap indicators: the column is offered in the column chooser only to accounts that have the feature, and a column saved in a layout of an account without it stays empty with «Not available» in the header (its constructor is not called).

Tariff restriction

Custom Watchlist columns are restricted together with the custom SmartDOM columns (see «Developer API and tariffs»). In that case your columns are still listed in the column chooser, but showing one opens the tariff banner and the column stays hidden; columns already shown in a saved layout are empty with «Not available» in the header, and the platform does not create or call them. The tariff is checked continuously, so the columns start working as soon as the restriction is lifted (for example, after the user logs on with another account).

Related articles