The recommendations below apply to any custom ATAS indicator — both for the classic platform and for ATAS X.
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:
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.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).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.GetAllPriceLevels()) on every tick is expensive — do it only when necessary and cache intermediate results where possible.If your indicator draws via OnRender (see the «Drawing» article), keep in mind that this is also a hot path:
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.FirstVisibleBarNumber to LastVisibleBarNumber — rather than the whole history; obtain coordinates via ChartInfo.GetXByBar/ChartInfo.GetYByPrice.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.RedrawChart() sparingly — only when the picture actually needs updating; calling it unnecessarily (e.g. on every tick) produces redundant repaints.The platform invokes your callbacks on its own threads — not the UI thread. Follow this contract instead of relying on internal thread details:
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.OnNewTrade, OnCumulativeTrade, and MarketDepthChanged are delivered on the data-feed thread.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).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.