Loading...
Searching...
No Matches
Performance recommendations

The recommendations below apply to any custom ATAS indicator — both for the classic platform and for ATAS X.

Calculation: <tt>OnCalculate</tt>

The OnCalculate method is called by the platform for every historical bar, and on the current bar — on every tick. Therefore the code inside it must be as lightweight as possible:

  • Avoid allocations in OnCalculate. Do not create collections, strings, LINQ queries, lambdas or temporary objects on every call, and avoid boxing. Keep reusable buffers and objects in fields and initialize them in the constructor or OnInitialize.
  • Create data series once. Create ValueDataSeries and other series in the constructor and add them to DataSeries; do not recreate them in OnCalculate. Write the result by bar index (series[bar] = value).
  • Cache the candle. Call GetCandle(bar) once and store the result in a local variable instead of requesting the same candle repeatedly for the same bar. Remember the valid index range 0..CurrentBar-1.
  • Cluster/footprint data is heavier than regular data. Iterating all price levels (GetAllPriceLevels()) on every tick is expensive — do it only when necessary and cache intermediate results where possible.

Rendering: <tt>OnRender</tt>

If your indicator draws via OnRender (see the «Drawing» article), keep in mind that this is also a hot path:

  • The call frequency of OnRender depends on the layouts you subscribe to via SubscribeToDrawingEvents. The Final layout is redrawn on every chart repaint, including mouse movement; LatestBar — on every tick; Historical — on a new candle, scrolling and zooming. Subscribe only to the layouts you need — an unnecessary subscription to Final forces drawing on every cursor move.
  • Draw only what is visible. Iterate only the visible bars — from FirstVisibleBarNumber to LastVisibleBarNumber — rather than the whole history; obtain coordinates via ChartInfo.GetXByBar/ChartInfo.GetYByPrice.
  • Do not compute in OnRender. Perform heavy calculations in OnCalculate and store the result; leave only the drawing itself in OnRender. Reuse pens and fonts (RenderPen/RenderFont) instead of creating them on every frame.
  • Call RedrawChart() sparingly — only when the picture actually needs updating; calling it unnecessarily (e.g. on every tick) produces redundant repaints.

Threads

The platform invokes your callbacks on its own threads — not the UI thread. Follow this contract instead of relying on internal thread details:

  • Do UI work only via DoActionInGuiThread. For anything that must run on the interface thread, call the indicator's DoActionInGuiThread(Action) method instead of doing the work directly from a calculation or data callback.
  • Market-data callbacks arrive on the data-feed thread. OnNewTrade, OnCumulativeTrade, and MarketDepthChanged are delivered on the data-feed thread.
  • Do not assume different callbacks share a thread. If you keep mutable state in fields and access it from more than one callback, guard it — or keep the writes in OnCalculate. Never write to DataSeries from your own background threads.
  • **OnRender should only draw.** Keep computation in OnCalculate and store the result; leave rendering-only code in OnRender (see the rendering section above).

Cross-platform (%ATAS X)

If your indicator must work on ATAS X, do not use custom WPF editors or Windows-only UI components — use attributes ([Display], etc.) for user settings rather than hand-written WPF controls. See the «Developing indicators for ATAS X» article for details.