Back
// Quantitative Systems & Compiler Engineering/22 min read/Published: 2026-07-09

Quantitative Futures Research: Defensive Veto Clustering and the Python-to-C# AST Compiler

How we engineered a zero-copy market data bus, vectorized candle geometry snapshots, dynamic defensive veto rule discovery, and an automated Python-to-C# AST compiler for NinjaTrader 8.

  • Python AST
  • C# NinjaScript
  • Quantitative Finance
  • Zero-Copy Data Bus
  • Vectorization
  • Futures Trading
  • Compiler Design
Aviram Yagena
Aviram Yagena
Lead Systems Architect & Co-Founder

The Quant Research Bottleneck: Curve Fitting and the Two-Language Problem

// Why traditional indicator optimization fails out-of-sample and why manual C# rewrites destroy algorithmic agility

Quantitative strategy development in futures markets is historically plagued by two fundamental engineering bottlenecks: statistical curve fitting and the notorious Two-Language Problem.

First, traditional backtesting workflows focus on optimizing entry indicator parameters (e.g. tweaking RSI lengths or moving average periods) until historical equity curves look enticing. This practice inevitably fits noise rather than market structure, resulting in strategies that collapse immediately when deployed in live trading. To build durable models, researchers must stop over-optimizing entries and instead focus on discovering defensive filters: structural rules that systematically veto trades during hostile market regimes.

Second, while quantitative research thrives in Python (leveraging NumPy, Pandas, and SciPy), high-performance execution platforms like NinjaTrader 8 require C# running on .NET Framework 4.8. Rewriting complex strategies by hand from Python to C# takes weeks, introduces subtle mathematical translation discrepancies (such as floating-point precision drifts or bar-indexing differences), and delays deployment. We engineered Smartush to solve both challenges: an autonomous research engine that discovers defensive veto rules and compiles them directly into production-ready C# NinjaScript via an Abstract Syntax Tree (AST) compiler.

  • ▪Elimination of curve fitting by holding entry rules constant and training machine learning clusters to identify defensive veto conditions.
  • ▪Direct resolution of the Two-Language Problem via an automated Python-to-C# AST compiler delivering verified classes in milliseconds.
  • ▪Radical memory efficiency through an in-memory zero-copy market data bus that shares multi-gigabyte tick feeds across parallel CPU workers.

The Zero-Copy Market Data Bus: High-Throughput Time-Series Slicing & Multi-Timeframe Aggregation

// Ingesting raw CME tick data, synthesizing atomic 1m and N-tick bars, and aggregating higher-timeframe series for rapid optimization

Backtesting thousands of strategy variations across 10 years of CME E-mini Nasdaq (NQ) tick and bar data requires streaming billions of data points. In standard Python multiprocessing architectures, passing large DataFrames between worker processes incurs heavy object serialization (pickle) overhead, multiplying RAM usage and choking CPU throughput.

To overcome this bottleneck, Smartush implements an in-memory Zero-Copy Market Data Bus utilizing Python shared memory buffers (multiprocessing.shared_memory) and contiguous NumPy structured arrays. Market quotes (timestamp, open, high, low, close, volume, tick count) are loaded into shared physical memory once upon daemon startup.

Because the engine ingests and indexes granular tick data at the lowest level, it can synthesize arbitrary atomic candle structures: standard 1-minute bars, volume-sliced intervals, or discrete N-tick bars (such as 10-tick or 100-tick charts). From the foundational 1-minute bar series, the data bus runs an in-memory hierarchical aggregation engine that rolls bars upward into 5-minute, 10-minute, and 60-minute (1-hour) bar series.

In real-world quantitative research and strategy optimization, running calculations across higher-timeframe bar series (such as 5-minute or 10-minute bars) is frequently preferable to evaluating raw ticks. Higher-timeframe aggregation filters high-frequency microstructure noise, produces mathematically stable indicator signals, and drastically accelerates backtesting throughput during massive parameter grid searches. Crucially, the architecture preserves full duality: researchers can evaluate strategies directly on raw tick data when intra-bar execution precision and microsecond fill modeling are required, or dispatch computations across dynamically aggregated multi-timeframe bar series for high-speed macro optimization.

Technical indicators (EMA, RSI, ATR, VWAP) are computed once across the desired timeframe resolutions using optimized C-extensions (TA-Lib) and mapped directly into the shared buffer. Worker processes receive lightweight read-only memory views without copying a single byte of time-series data. This architecture reduces total memory consumption by over 80% and accelerates parallel backtesting throughput by 4.8x compared to standard distributed multiprocessing.

Standard Multiprocessing (Pickle IPC)

Memory Footprint:18.4 GB RAM for 8 workers
CPU Scaling100% CPU (bound by serialization)
Evaluation Latency42.6 ms per backtest iteration
Worker Concurrency8 workers (RAM constrained)
Architectural Bottleneck & Tradeoff:

Pickling large Pandas DataFrames across process boundaries saturates memory buses and triggers heavy operating system paging.

In-Memory Threading (GIL-Constrained)

Memory Footprint:4.2 GB RAM (shared memory)
CPU Scaling18% effective multi-core CPU usage
Evaluation Latency31.2 ms per backtest iteration
Worker Concurrency1 active worker (GIL bottleneck)
Architectural Bottleneck & Tradeoff:

While memory is shared in a single process, the Python Global Interpreter Lock (GIL) serializes numerical evaluations, preventing true multi-core scaling.

Smartush Zero-Copy Shared Memory Bus

Memory Footprint:2.1 GB RAM total across all workers
CPU Scaling96% parallel multi-core CPU usage
Evaluation Latency8.8 ms per backtest iteration
Worker Concurrency32 parallel workers
Architectural Bottleneck & Tradeoff:

Shared memory arrays allow arbitrary parallel worker processes to read indicator slices concurrently without GIL contention or memory copying.

Codebase Topology and Compiler Pipeline Structure

// From raw tick ingestion and geometry vectorization to defensive clustering and AST emission

The Smartush repository separates exploratory quantitative research from deterministic code generation. Raw market data flows linearly through feature vectorization, backtest simulation, condition clustering, and finally code synthesis.

Every module adheres to strict contract interfaces: the data bus exposes immutable memory views; the geometry engine produces vectorized condition snapshots; the clustering engine isolates statistical edge; and the compiler translates verified logic into C# NinjaScript classes without external dependencies.

Repository Layout & Service Boundaries
  • 📁 quant-generator/# Root quantitative research and compilation repository
    • 📁 data_bus/# Shared memory manager and zero-copy time-series storage
      • 📄 shared_buffer.py# POSIX/Windows shared memory allocator
      • 📄 indicator_cache.py# Pre-computed TA-Lib indicator vector store
      • 📄 tick_normalizer.py# CME tick ingester and atomic 1m / N-tick consolidator
      • 📄 bar_aggregator.py# Hierarchical 1m to 5m, 10m, and 60m bar aggregator
    • 📁 geometry/# Vectorized price action and candle geometry extraction
      • 📄 candle_features.py# Wick ratios, body expansion, and volume metrics
      • 📄 vwap_bands.py# Rolling session VWAP and standard deviation bands
      • 📄 snapshot_vectorizer.py# Multi-lookback feature snapshot generator
    • 📁 clustering/# Defensive veto rule discovery and condition clustering
      • 📄 loss_cohort_cluster.py# Unsupervised clustering on losing trade setups
      • 📄 veto_scorer.py# Parsimony scoring and overfitting penalty model
      • 📄 recursive_gate.py# Out-of-sample backtest validation loop
    • 📁 compiler/# Python AST parser and C# NinjaScript code generator
      • 📄 ast_parser.py# Parses Python strategy rules into syntax trees
      • 📄 type_mapper.py# Maps Python types to C# .NET equivalents
      • 📄 csharp_emitter.py# Synthesizes clean NinjaTrader 8 Strategy classes
      • 📄 templates.py# Production NinjaScript boilerplate templates
    • 📁 tests/# End-to-end unit tests and compiler verification suites
      • 📄 test_ast_compiler.py# Validates C# code emission parity
      • 📄 test_data_bus.py# Shared memory integrity and leak tests

Candle Geometry Vectorization: Multi-Lookback Technical Snapshots

// Capturing holistic market state at trade entry to understand why trades fail

Rather than relying on simplistic single-indicator signals, Smartush constructs a comprehensive multidimensional market snapshot at the exact timestamp of every simulated trade entry.

The geometry vectorizer calculates candle body-to-wick ratios, momentum acceleration across lookback intervals from 1 to 30 bars, rolling volume standard deviation, and distance from multi-timeframe moving averages. When a simulated trade is executed, an immutable vector of 48 normalized parameters is captured.

This snapshot serves as the foundation for defensive rule discovery. By treating every trade entry as an empirical observation in a multidimensional parameter space, the machine learning pipeline can isolate the precise geometric conditions that precede losing trades.

geometry/snapshot_vectorizer.py
python
import numpy as np
from typing import NamedTuple

class CandleSnapshot(NamedTuple):
    body_to_wick_ratio: float
    upper_wick_percentage: float
    volume_surge_factor: float
    vwap_distance_atr_multiple: float
    momentum_delta_5bar: float
    momentum_delta_20bar: float

def extract_candle_geometry(opens: np.ndarray, highs: np.ndarray, lows: np.ndarray, 
                            closes: np.ndarray, volumes: np.ndarray, vwap: np.ndarray, 
                            idx: int) -> CandleSnapshot:
    """
    Extracts a zero-allocation vectorized geometric snapshot of the market state
    at bar index [idx] without generating intermediary Pandas objects.
    """
    c_open, c_high, c_low, c_close = opens[idx], highs[idx], lows[idx], closes[idx]
    c_vol = volumes[idx]
    
    # Candle structural metrics
    candle_range = max(c_high - c_low, 1e-6)
    candle_body = abs(c_close - c_open)
    upper_wick = c_high - max(c_open, c_close)
    
    body_ratio = candle_body / candle_range
    upper_wick_pct = upper_wick / candle_range
    
    # 20-period rolling volume surge factor
    lookback_start = max(0, idx - 20)
    avg_vol = np.mean(volumes[lookback_start:idx]) if idx > 0 else c_vol
    vol_surge = c_vol / max(avg_vol, 1.0)
    
    # Price distance from session VWAP normalized by rolling 14-bar true range
    tr = np.maximum(highs[lookback_start:idx] - lows[lookback_start:idx], 1e-6)
    atr = np.mean(tr) if len(tr) > 0 else 1.0
    vwap_dist = (c_close - vwap[idx]) / atr
    
    # Multi-lookback momentum deltas
    mom_5 = (c_close - closes[max(0, idx - 5)]) / c_close
    mom_20 = (c_close - closes[max(0, idx - 20)]) / c_close
    
    return CandleSnapshot(
        body_to_wick_ratio=float(body_ratio),
        upper_wick_percentage=float(upper_wick_pct),
        volume_surge_factor=float(vol_surge),
        vwap_distance_atr_multiple=float(vwap_dist),
        momentum_delta_5bar=float(mom_5),
        momentum_delta_20bar=float(mom_20)
    )
Rationale: Running directly on pre-allocated NumPy contiguous arrays enables the snapshot extractor to evaluate 100,000 bars in under 120 milliseconds, providing instantaneous feature extraction for cluster analysis.

Defensive Veto Discovery: Condition Clustering vs. Entry Guessing

// Systematically eliminating losing cohorts using multi-factor cluster scoring and recursive verification

The core scientific philosophy of Smartush is that profitable algorithmic trading is achieved not by finding exotic entry signals, but by eliminating trades taken during statistically toxic market regimes.

The clustering engine analyzes the entire population of simulated trades generated by a simple baseline entry rule. It isolates all trades that resulted in losses and applies density-based clustering across their entry geometry snapshots. The engine identifies recurring clusters of failure: for instance, identifying that Long breakouts fail 82% of the time when the 5-bar momentum exceeds 3.2 standard deviations while volume is below the 20-period median.

Each candidate veto rule is scored using a multi-factor fitness function that heavily penalizes complexity (number of boolean clauses). Only rules that demonstrate high statistical significance and improve net profit across out-of-sample test splits are approved for compilation.

  • ▪Unsupervised failure clustering: Automatically groups losing trades to identify recurring technical environments that degrade performance.
  • ▪Complexity penalty function: Parsimony scoring rewards simple inequality constraints (e.g. ATR > 2.5) and discards fragile, multi-layered combinations.
  • ▪Out-of-sample recursive validation: Candidate veto rules must demonstrate consistent drawdown reduction across holdout data splits before acceptance.

The Universal AST Compiler: Decoupling Strategy Logic from Execution Platforms

// Traversing Python syntax trees, mapping multi-timeframe series, and emitting verified code for NinjaTrader, MetaTrader, or custom APIs

Once a trading strategy and its defensive veto gates are mathematically validated, deploying the model to live markets traditionally required an engineer to rewrite the algorithm from scratch. This manual translation phase frequently introduced subtle calculation discrepancies (such as bar-indexing drift or floating-point variance) and delayed deployment.

Smartush eliminates manual rewrites through a modular Abstract Syntax Tree (AST) compiler. The engine ingests the Python strategy specification, parses the Python code into an in-memory syntax tree using python ast, and translates logical constructs, multi-timeframe bar series, and indicator dependencies into native target platform code.

Because the Python research engine constructs multi-timeframe bar series (such as 1-minute base bars alongside 5-minute, 10-minute, 60-minute aggregations, or discrete N-tick series), the compiler emits explicit multi-series lifecycle configurations into the target code. In NinjaScript, this automatically synthesizes AddDataSeries() calls and binds secondary time-series indices (BarsArray[1]) to multi-timeframe indicators without requiring manual chart setup.

Crucially, the compiler pipeline is platform-agnostic. While C# and NinjaTrader 8 served as our primary production deployment engine for CME futures and equities, the AST intermediate representation decouples strategy logic from any single runtime. By understanding the target platform execution model and API constraints, the compiler can be extended with pluggable emitters for MetaTrader (MQL4/MQL5), C++, or direct broker REST and WebSocket APIs. This guarantees that identical strategy logic, multi-timeframe aggregations, and defensive vetoes can be deployed seamlessly across diverse asset classes and execution ecosystems.

compiler/csharp_emitter.py
python
import ast
from typing import List

class CSharpNinjaScriptEmitter(ast.NodeVisitor):
    """
    Translates validated Python strategy AST nodes into production-ready
    C# NinjaScript class code adhering to NinjaTrader 8 standards.
    """
    def __init__(self, strategy_name: str, veto_rules: List[str]):
        self.strategy_name = strategy_name
        self.veto_rules = veto_rules
        self.output_lines: List[str] = []
        self._indent_level = 0

    def emit(self, text: str):
        indent = "    " * self._indent_level
        self.output_lines.append(f"{indent}{text}")

    def generate_csharp_class(self) -> str:
        self.emit("using System;")
        self.emit("using NinjaTrader.NinjaScript;")
        self.emit("using NinjaTrader.NinjaScript.Strategies;")
        self.emit("using NinjaTrader.NinjaScript.Indicators;")
        self.emit("")
        self.emit("namespace NinjaTrader.NinjaScript.Strategies")
        self.emit("{")
        self._indent_level += 1
        self.emit(f"public class {self.strategy_name} : Strategy")
        self.emit("{")
        self._indent_level += 1
        
        # Indicator field declarations
        self.emit("private EMA _fastEma;")
        self.emit("private EMA _slowEma;")
        self.emit("private ATR _atr;")
        self.emit("private EMA _macroEma;")
        self.emit("")
        
        # OnStateChange lifecycle
        self.emit("protected override void OnStateChange()")
        self.emit("{")
        self._indent_level += 1
        self.emit("if (State == State.SetDefaults)")
        self.emit("{")
        self.emit("    Description = "Auto-generated by Smartush Quant Compiler";")
        self.emit("    Name = "" + self.strategy_name + "";")
        self.emit("    Calculate = Calculate.OnBarClose;")
        self.emit("    EntriesPerDirection = 1;")
        self.emit("}")
        self.emit("else if (State == State.Configure)")
        self.emit("{")
        self.emit("    // Register multi-timeframe bar series synthesized from tick stream")
        self.emit("    AddDataSeries(BarsPeriodType.Minute, 5);  // 5-minute secondary bar series")
        self.emit("    AddDataSeries(BarsPeriodType.Minute, 60); // 60-minute macro bar series")
        self.emit("}")
        self.emit("else if (State == State.DataLoaded)")
        self.emit("{")
        self.emit("    _fastEma = EMA(14);")
        self.emit("    _slowEma = EMA(50);")
        self.emit("    _atr = ATR(14);")
        self.emit("    _macroEma = EMA(BarsArray[2], 20); // Computed on 60m aggregated series")
        self.emit("}")
        self._indent_level -= 1
        self.emit("}")
        self.emit("")
        
        # OnBarUpdate with synthesized defensive veto gates
        self.emit("protected override void OnBarUpdate()")
        self.emit("{")
        self._indent_level += 1
        self.emit("if (CurrentBar < 50 || CurrentBars[1] < 20) return;")
        self.emit("")
        self.emit("// --- Automated Defensive Veto Gates ---")
        for idx, rule in enumerate(self.veto_rules):
            self.emit(f"bool vetoGate_{idx} = ({rule});")
            self.emit(f"if (vetoGate_{idx}) return; // Suppress trade entry")
        self.emit("")
        self.emit("// --- Core Strategy Execution ---")
        self.emit("if (CrossAbove(_fastEma, _slowEma, 1))")
        self.emit("{")
        self.emit("    EnterLong("LongEntry");")
        self.emit("}")
        self._indent_level -= 1
        self.emit("}")
        
        self._indent_level -= 1
        self.emit("}")
        self._indent_level -= 1
        self.emit("}")
        
        return "\n".join(self.output_lines)
Rationale: The AST emitter generates complete, type-safe target code. It configures multi-timeframe bar series (such as secondary 5m and 60m aggregations), instantiates indicators mapped to respective series, and enforces defensive veto gates natively.

Production NinjaScript Generation and Pre-Trade Safety Gates

// Ensuring generated C# code adheres to exchange requirements, session windows, and execution throttles

Code generated by the Smartush compiler is not merely a theoretical backtest script; it is fully hardened for production brokerage execution. The generated classes include native parameter serialization, configurable user inputs exposed via NinjaScript attributes ([NinjaScriptProperty], [Range]), and robust execution safety checks.

Before any order method (EnterLong or EnterShort) is dispatched, the compiled NinjaScript executes pre-trade safety gates directly inside OnBarUpdate(). These gates enforce maximum intraday loss ceilings, check current account margin ratios against broker minimums, and verify that the current exchange timestamp falls within approved trading sessions.

By embedding both algorithmic logic and defensive risk boundaries into a single compiled C# class, the deployment pipeline guarantees 100% mathematical fidelity between the Python quantitative research environment and live trading execution.

  • ▪Immediate compiler readiness: Synthesized .cs files compile directly inside NinjaTrader 8 without requiring manual syntax adjustments or missing dependencies.
  • ▪Embedded safety gates: Pre-trade margin checks, session time filters, and dynamic veto conditions execute natively before order transmission.
  • ▪Zero execution divergence: Guarantees that historical backtest math matches live tick evaluation down to the floating-point decimal.

Engineering Takeaways for Quantitative and Compiler Systems

// Foundational architectural principles for building automated research and code generation pipelines

Bridging the gap between scientific quantitative research and deterministic production execution requires rigorous software architecture. Keep these four principles in mind:

  • ▪Prioritize defensive vetoes over entry optimization: Finding conditions that systematically characterize bad trades yields far higher out-of-sample durability than tweaking entry thresholds.
  • ▪Leverage zero-copy shared memory for time-series: Shared memory buffers eliminate inter-process serialization bottlenecks, accelerating parallel backtesting throughput.
  • ▪Support multi-resolution bar aggregation from tick primitives: Retain raw tick fidelity for microsecond order execution while dynamically synthesizing 1m, 5m, 10m, and 60m series to filter noise and accelerate quant optimization.
  • ▪Decouple quant logic from execution platforms via universal AST compilation: Emitting code from an intermediate syntax tree allows the same strategy and multi-timeframe series to target NinjaTrader (C#), MetaTrader (MQL), or custom broker APIs with zero manual rewrites.
  • ▪Embed pre-trade risk gates directly into compiled code: Ensure that quantitative models enforce their own execution boundaries regardless of external platform stability.

Engineering automated quantitative research or AST compilers?

We build robust time-series processing pipelines, defensive clustering engines, and automated code-generation toolchains.

Your next step

Let’s define what you need to build.

Bring the idea or the process you want to improve. We’ll start with the requirements, the risks and the right scope.

Discuss Your Project