Loading...
Searching...
No Matches
SmartDOM columns

Overview

A SmartDOM column is a vertical strip of the ATAS SmartDOM module: one cell per price row, under a header. Besides the built-in columns (Price, Bid/Ask, Volume, Trades and so on) the SmartDOM can show columns from your own assembly. Such a column:

  • draws its header and one cell per visible price row with the regular drawing API (RenderContext, see «Graphics»);
  • receives the trades and the order book changes of the SmartDOM instrument;
  • reads the order book, the best prices, the visible price range, the fonts and colors of the SmartDOM through a read-only context;
  • has user settings — public properties shown in the column settings and saved with the workspace, exactly like indicator settings.

A column cannot place, modify or cancel orders and has no access to accounts or positions: it is a visualization of market data.

SmartDOM columns work the same way in the classic ATAS and in ATAS X.

Building a column in your own assembly

Create a class library exactly as for a chart indicator (see «Development of a user indicator»). Besides ATAS.Indicators.dll, reference OFT.Rendering.dll (drawing) and ATAS.DataFeedsCore.dll (instrument data) from the platform folder, with <Private>false</Private>.

A column is:

  • a public class derived from SmartDomColumn (namespace ATAS.Indicators.SmartDom) with a public parameterless constructor;
  • marked with the [SmartDomColumn(id)] attribute — the stable column id (see below) and, optionally, DisplayName: the name in the list of available columns and the default header;
  • an override of OnDrawCell; every other method is optional.

Lifecycle

Method Called
constructor when the column is added or a saved layout is opened (and the tariff allows custom columns); the saved settings are applied right after it
OnInitialize() when the column is attached to a SmartDOM; Context is available from here on
OnSettingsChanged() after OnInitialize and every time the user changes a setting
OnInstrumentChanged() after the SmartDOM switched to another instrument, before the first data of the new one — reset accumulated data here; data of the previous instrument that was not delivered yet is discarded
OnNewTrades(trades) new trades (MarketDataArg, DataType == Trade), oldest first
OnMarketDepthChanged(levels) changed order book levels (DataType is Bid or Ask; volume 0 removes a level)
OnBestBidAskChanged(bestPrice) the best bid or ask changed
OnTimer() about ten times per second while the SmartDOM is visible
OnDrawColumn(context, bounds) once per frame, before the cells; bounds is the whole column, header included
OnDrawCell(context, cell) once per visible row of every frame
OnDrawHeader(context, bounds, text) once per frame; the default draws text (the header set by the user) like the built-in columns
OnCellClick(cell, e) the user clicked a cell; return true if you handled it (a handled right click opens no context menu)
OnDispose() the platform stops using the instance: the column was removed or its layout replaced, the SmartDOM 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 (template), 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

SmartDomCell gives the row Price, the cell Bounds and IsMouseOver. Before OnDrawCell the platform fills the cell with the column background (the Background setting every column has), so you draw only the content.

Threading

  • The platform never calls one column concurrently: data callbacks, the timer, the settings, drawing and clicks are serialized. Your fields need no locks.
  • Data callbacks are not called on the data feed thread: the platform copies the data and delivers it in arrival order shortly afterwards (before the next frame or on the next timer tick). The lists passed to you belong to the call — do not keep them.
  • Keep OnDrawCell cheap: it runs for every row of every frame on the drawing thread. Do heavy work in the data callbacks or OnTimer and only look up prepared values when drawing.
  • Delivery is time-sliced: each delivery hands the column only the batches that were waiting when it started, and stops after about 4 ms; the rest follows on the next frame or timer tick. A column that is slower than its data falls behind, and when too much data is waiting (or while the SmartDOM panel is hidden and not drawn) newer data is skipped. Re-read the current state from Context instead of relying on a complete history.
  • Time limit. A single call (or the drawing of one frame of the column) 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»).

The context

Context (ISmartDomColumnContext) is a read-only view of the SmartDOM:

Member Meaning
Instrument, TickSize the instrument the SmartDOM displays (IInstrumentInfo: name, exchange, tick size, and TimeZone / TimeZoneOffset — the time zone set for the instrument in the platform, as chart indicators get it; null while none is selected). For cross-trading and synthetic instruments this is the displayed instrument whose book and trades you receive, not the one orders go to
Scale, RowPriceStep ticks per row and the price step between rows
VisibleHigh, VisibleLow, RowHeight the rows of the last frame
GetBidVolume(price), GetAskVolume(price) volume of the order book level shown in a row (0 if none)
GetDepth(isAsk) snapshot of all bid or ask levels
BestBid, BestAsk, LastTrade the top of the book and the last trade (null while unknown)
CumulativeBids, CumulativeAsks, MaxBidVolume, MaxAskVolume totals of the shown order book
HighOfDay, LowOfDay the current day range
Font, BoldFont, HeaderFont, TextFormat the fonts and the text alignment chosen for the SmartDOM
BackgroundColor, HeaderBackgroundColor, HeaderTextColor standard colors
FormatPrice(price), FormatVolume(volume) the SmartDOM formatting of prices and volumes
GetY(price), GetPrice(y) conversions between rows and coordinates: GetY returns the top Y of the row in the last drawn frame, or null if the row is not visible
Redraw() asks for a new frame (for example, after OnTimer changed something)

Settings

Public properties with a getter and a setter are the column settings. They are shown in the column settings of the SmartDOM below the common column settings (header, width, colors) and saved in the workspace and SmartDOM templates. Use [Display(Name = …, GroupName = …, Order = …)] for names, groups and order, [Range] for limits and [Browsable(false)] for values you want to keep but not show. Settings must be of concrete serializable types (numbers, strings, bool, enums, colors, filters); a setting that cannot be restored is skipped and keeps its default value. The values themselves are saved as ordinary JSON objects, so a nested settings class may use constructor parameters, read-only properties or public fields.

Settings are read and written by the settings window under the same lock as your other methods, with one exception: a nested settings object (for example a filter such as FilterInt) is edited in place by its editor on the UI thread, while your column may be running. The platform notices the change (the object must implement INotifyPropertyChanged, as the filters do) and calls OnSettingsChanged afterwards, serialized as usual. So read nested settings only in OnSettingsChanged and copy the values you need into your own fields; use those fields in the data and drawing methods.

Errors

If one of your methods throws (or the column repeatedly exceeds the time limit, see «Threading»), the platform writes the reason to the log, releases the instance (OnDispose) and shows «Error» in its header; the rest of the SmartDOM keeps working. A new instance with the saved settings starts when the user changes one of the column settings (header, width, colors), or when the layout is loaded again (workspace or SmartDOM template). Do not rely on this as a control flow — check your inputs instead.

Example

The column below shows, for every row, the bid volume minus the ask volume of the order book levels within a few rows around it: a bar in the color of the dominating side and the value.

using System;
using System.ComponentModel.DataAnnotations;
using Color = System.Windows.Media.Color;
using Colors = System.Windows.Media.Colors;
namespace MyCompany.SmartDomColumns;
[SmartDomColumn("mycompany.bid-ask-imbalance", DisplayName = "Bid/Ask imbalance")]
public sealed class BidAskImbalanceColumn : SmartDomColumn
{
private decimal _scaleMax; // largest |imbalance| of the previous frame: scales the bars
private decimal _frameMax; // largest |imbalance| of the frame being drawn
[Display(Name = "Rows around the price", GroupName = "Calculation", Order = 10)]
[Range(0, 20)]
public int Depth { get; set; } = 2;
[Display(Name = "Bid dominance", GroupName = "Colors", Order = 20)]
public Color BidColor { get; set; } = Color.FromArgb(160, 0, 150, 70);
[Display(Name = "Ask dominance", GroupName = "Colors", Order = 30)]
public Color AskColor { get; set; } = Color.FromArgb(160, 200, 40, 40);
[Display(Name = "Text", GroupName = "Colors", Order = 40)]
public Color TextColor { get; set; } = Colors.Black;
[Display(Name = "Show values", GroupName = "Colors", Order = 50)]
public bool ShowValues { get; set; } = true;
protected override void OnSettingsChanged()
{
if (Depth < 0)
Depth = 0;
}
protected override void OnInstrumentChanged()
{
_scaleMax = 0;
_frameMax = 0;
}
protected override void OnDrawColumn(RenderContext context, System.Drawing.Rectangle bounds)
{
// Called once per frame before the cells.
_scaleMax = _frameMax;
_frameMax = 0;
}
protected override void OnDrawCell(RenderContext context, SmartDomCell cell)
{
var imbalance = 0m;
for (var row = -Depth; row <= Depth; row++)
{
var price = cell.Price + row * Context.RowPriceStep;
imbalance += Context.GetBidVolume(price) - Context.GetAskVolume(price);
}
if (imbalance == 0)
return;
var size = Math.Abs(imbalance);
_frameMax = Math.Max(_frameMax, size);
var share = _scaleMax > 0 ? Math.Min(1m, size / _scaleMax) : 1m;
var bar = new System.Drawing.Rectangle(cell.Bounds.X, cell.Bounds.Y, (int)(cell.Bounds.Width * share), cell.Bounds.Height);
context.FillRectangle((imbalance > 0 ? BidColor : AskColor).Convert(), bar);
if (ShowValues)
context.DrawString(Context.FormatVolume(imbalance), Context.Font, TextColor.Convert(), cell.Bounds, Context.TextFormat);
}
}
Represents an order for trading on a financial exchange.
Definition Order.cs:13
Base class of a SmartDOM column written in a custom assembly. Derive from it, mark the class with Sma...
Definition SmartDomColumn.cs:36
Represents an abstract rendering context for drawing on a chart.
Definition RenderContext.cs:17
int FillRectangle(Color brush1, Color brush2, Rectangle rect)
Fills a rectangle with a gradient using two specified colors.
Definition RenderContext.cs:85
abstract int DrawString(string s, RenderFont font, Color brush, int x, int y)
Draws a string at the specified position using the specified font and brush.
Definition ISmartDomColumnContext.cs:1
Definition IIndicatorAnalyticsService.cs:1
Definition IRenderContextSettings.cs:1
One cell of a SmartDOM column: the price row it belongs to and where it is drawn.
Definition SmartDomCell.cs:7
Rectangle Bounds
Cell rectangle in the coordinates of the SmartDOM control.
Definition SmartDomCell.cs:14

The colors are System.Windows.Media.Color settings, as in chart indicators (reference PresentationCore.dll); Convert() from ATAS.Indicators turns them into the System.Drawing.Color the drawing API expects. The Convert() for System.Windows.Media.Color exists only in the assemblies of the classic ATAS, so build such a column against them (net10.0-windows); the same DLL runs in ATAS X, where the platform converts these types automatically (see «Developing indicators for ATAS X»).

The column id

The argument of [SmartDomColumn] is the column id: the platform saves the column in workspaces and SmartDOM templates under this id, not under the class name. Rules:

  • use the form <vendor>.<column-name> in lowercase letters, digits and dashes, for example mycompany.bid-ask-imbalance; 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 columns cannot share an id; if they do, only one of them is listed.

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 and SmartDOM columns together.

The columns appear in the SmartDOM settings, Columns tab, in the Custom group of the available columns. 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, open the columns settings again to see a new column; columns already in a SmartDOM switch to the new build and keep their settings.

If the DLL is removed, saved SmartDOM 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 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 SmartDOM columns can be restricted by the tariff (see «Developer API and tariffs»). In that case your columns are still listed in the columns settings, but adding one shows the tariff banner; columns already in a layout are shown empty with «Not available» in the header, and the platform does not create or call them. The tariff is checked on every frame, so the columns start working as soon as the restriction is lifted (for example, after the user logs on with another account) — no need to add them again.

Related articles