Been noticing a recurring issue with custom order flow indicators freezing the platform right at the New York open or during massive news events. Most of the time it happens because developers are dumping heavy mathematical calculations directly inside the OnRender method synchronously
When you are processing raw tick data or building custom footprint visuals you have to remember that OnRender is called constantly by the UI thread to update the graphics. If you bog it down with volume aggregations or complex bid ask delta loops your whole chart simply locks up and you miss the move. A much better approach we use in low-latency environments is to completely decouple the data ingestion from the drawing logic. You should calculate your cumulative delta or volume nodes dynamically inside OnMarketData and store those pre-calculated values in a lightweight generic List or a Dictionary. Then when OnRender fires it literally just reads the cached state and draws the rectangles or text instantly without doing a single drop of math. It sounds basic but separating state calculation from visual rendering will drop your CPU load from 80% to something barely noticeable even when the tape is flying, just a quick architectural tip for anyone building custom volumetric tools who is tired of their charts freezing up.
Come on man. Donât give all the secrets away.
![]()
And you should probably consider using lock() or concurrent data structures during calculations to ensure those data structures arenât being updated in the middle of a render. This commonly causes indicators to crash with missing dictionary keys with basic dictionaries.
Haha, fair point, I tried to keep the initial concept high-level without diving deep into thread safety mechanics so I wouldnât scare off the beginners, but you are absolutely right to point that out for anyone actually implementing this
If you are decoupling data from the UI thread you essentially must use a ConcurrentDictionary or implement explicit lock objects before reading the cache inside OnRender. Otherwise the exact moment the market data thread writes while the UI thread is looping you get a collection modified exception and the whole thing crashes anyway, good looking out adding that caveat to the thread.
Ă consigliabile calcolare dinamicamente i nodi delta o volume cumulativi allâinterno di OnMarketData e memorizzare questi valori precalcolati in una List o un Dictionary generici e leggeri. Quindi, quando OnRender viene eseguito, legge semplicemente lo stato memorizzato nella cache e disegna i rettangoli o il testo istantaneamente senza eseguire alcun calcolo matematico. Perdonami non sono pratico di NINJa ( usavo Sierra Chart) in pratica e tencicamente ( settting ,chart modification, imput to changes of data ) puoi spiegare i passaggi da eseguire per realizzare quello che giustamente tu affermi. Ti sono molto grato per una tua cortese dettagliata speigazione x realizzare sulla mia piattafroma. Grazie
Just curious which indicator code you have been examining to make this determination or by what deductions you have come to this conclusion. ![]()
Bidder, I donât need to name drop specific commercial vendors here, but Iâve audited memory dumps from at least three major order flow suites that retail traders use daily, the fingerprint is always the same. When the tape speeds up during NFP or FOMC, the NinjaScript UI thread chokes because the developer locked the rendering loop while iterating over thousands of raw tick objects synchronously. It is a fundamental misunderstanding of WPF architecture, not an issue with NT8 itself. Decoupling data ingestion from the drawing context using thread-safe collections instantly solves the bottleneck
Umberto Piacenza, the concept is straightforward but requires strict thread safety, inside your OnMarketData event, you calculate your Delta/Volume and push that data into a ConcurrentDictionary<int, CustomBarObject>. This runs purely in the background. Then, in your OnRender override, you absolutely never do any math. You simply iterate over that dictionary and call RenderTarget.DrawRectangle or DrawTextLayout. The trick is ensuring your rendering thread only reads cached state and never waits for new ticks to be processed, if you need a deeper architectural review of your specific Sierra Chart migration, feel free to shoot me a direct message.
Hereâs the right mental mode, performance optimization in NT8 is about reducing work, not relocating it. The wins I try to captured are the 4 things below.
- Cache instead of allocate (TextLayout, TextFormat, brushes)
- Look up once instead of repeatedly (CheckSlopes hoist)
- Short-circuit instead of iterate fully (CheckTime early exit, signal loop break)
- Build conditionally instead of unconditionally (RegimeUpdate string construction)
These reduce the absolute amount of work the data thread does. Moving work to background threads would just shift the cpu cycles to a different thread while adding synchronization overhead same total work, more complexity, slower decisions.
maverick, you are confusing total cpu cycles with ui thread blocking, yes caching text formats and short-circuiting loops is standard wpf practice but that is basic micro-optimization, the issue isnât the absolute amount of work, it is exactly where the work happens. If you process 5000 raw ticks synchronously inside the onrender dispatcher the ui thread physically cannot paint the screen until the math finishes, so the chart freezes. offloading state calculation to the onmarketdata thread doesnât reduce total cpu work, it simply frees the rendering thread to do its only job which is drawing pixels, in heavy order flow environments preventing visual lockups is vastly more important than saving a few micro-seconds of background thread synchronization, relocating work is exactly how modern asynchronous architecture survives the tape
Fully agree with and understand what Eduardo is attempting to explain here. I wouldnât be able to explain it as well as he has. But if youâre doing any sort of heavy graphics in ninjascript (like footprint, volume profile, dom, etc), youâll be able to make huge improvements in performance if you fully understand what heâs saying and apply it.
Personally, it took me a while to understand and figure this out on my own and I learned it the hard way. I wish Eduardo had posted this and hammered it in my head before I built my first iteration of footprint and volume profile LOL.
Ciao, ho visto il tuo commento sul mio thread riguardo al disaccoppiamento di OnMarketData e OnRender per evitare il congelamento della piattaforma. Il passaggio da Sierra Chart a NinjaTrader 8 introduce parecchie complicazioni strutturali perchĂ© la gestione del multithreading in C# richiede un controllo rigido della memoria per evitare eccezioni di tipo raccolta modificata a mercato aperto. Se provi a scrivere loop di calcolo asincroni senza implementare oggetti lock deterministici o strutture dati concorrenti, lâindicatore andrĂ in crash esattamente durante i picchi di volatilitĂ della sessione di New York
Se hai bisogno di stabilizzare i tuoi strumenti volumetrici o di tradurre la tua vecchia infrastruttura di Sierra Chart in un framework C# isolato e sicuro per evitare ritardi di esecuzione, mandami i dettagli logici del tuo codice qui in privato e posso aiutarti a strutturare lâarchitettura corretta
Why calculate/process in onmarketdata and not onbarupdate?
Didnât need to dig into anyoneâs private source code to see it, just run the native performance monitor or attach Visual Studio to NT8 during an NFP release while running most commercial footprint indicators
When the tick stream accelerates, the render thread queue chokes because developers try to calculate tick volume delta on the fly instead of drawing pre-cached states, itâs just basic thread diagnostics, not a secret guess.
Maybe Iâm misunderstanding but youâre not stating anything new. From NT Docs:
Notes:
1.This method uses the 3rd party SharpDX library to render custom Direct2D Text and Shapes. For a walk through for using the SharpDX, please see the educational resource Using SharpDX for Custom Chart Rendering
2.The OnRender() method frequently runs once the State has reached State.Realtime in response to market data updates or a user interacting with the chart (e.g., clicking, resizing, rescaling, etc.)
3.For performance optimizations, the timing of the calls to OnRender() are buffered to at least 250ms, and re-renders once internal logic determines that values may be out-of-date. See also ForceRefresh() for more details
4.When using the Strategy Analyzer, OnRender() does NOT call until you switch to the âChartâ display and renders from State.Terminated. As a result, this method should NOT be relied on for historical Strategy backtesting logic and should ONLY be used for rendering purposes
5.Unlike market data events and strategy order related events, there is NO guarantee that the barsAgo indexer used for Series objects are in sync with the current bars in progress. As a result, you should favor using an absolute index method to look up values (e.g., .GetValueAt(), Bars.GetOpen(), etc)
6.While OnRender() is an excellent means for customizing and enhancing indicators and strategies, its application can easily be abused, resulting in unforeseen performance issues which you may not catch until the right conditions (e.g., in the hands of your users during an FOMC event)
7.Please limit any calculations or algorithms you may be tempted run in OnRender() simply to rendering. You should always favor precomputed values and store them for rendering later as the preferred approach to working with the OnRender() method (e.g., reusing brushes, passing values from OnBarUpdate(), etc.). See also OnRenderTargetChanged() method for more information on reusing Brushes
8.If you are using this method as an opportunity to âhookâ onto a user related event, such as when a user selects a 3rd party control, you should alternatively consider using the events of that control independent of official NinjaScript events. See also TriggerCustomEvent()
You are exactly right, Bill, it is literally point 7 in the manual, the problem is nobody reads it
I spend half my week fixing paid commercial tools that still dump heavy math right into OnRender. The docs tell you the rule, but most retail devs ignore it and then wonder why their charts lock up at the open, just putting a spotlight on it for the guys who skip the documentation and try to build volume tools blindly
Eduardo, I think everybody knows now that you spend half your life diagnosing enterprise grade, API compliance, 250 ms retrograde, rocket launchers for trading.
My guy, offer actual solutions instead of these useless word salad
of yours.
Literally do the heavy logic and âmathâ on a different thread and youâll be fine. If itâs that complex and computationally heavy, they probably should be looking at something else than NT and MT.