Research in Python, production in C++
The intended workflow crosses languages, because research and production rarely want the same one. Develop and backtest a strategy in Python, where the model tooling lives; use ML to select the best candidate out of the many the engine has scored; persist that candidate with one call; load it in C++ with one call; attach fresh market adapters and an execution; and put the candidate to trading.
What a save writes
What tse_save writes is a recipe, at persistence schema version 3, not live
state: the strategy graph — contracts, inputs, patterns, rules and the robot — and nothing
else.
Market adapters and the execution are deliberately absent from the recipe, which is exactly what makes the recipe portable: the same graph is attached to a CSV replay in research and to a live feed and a broker in production.
A processor is referenced by key
A data processor is a function pointer and cannot be serialized, so a savable node references it by a string key that the loading process registers again on its own side. The recipe is a SQLite file and the keys are plain strings, which is what makes the round trip cross-language by construction: register the same keys on the far side — the processors themselves may be reimplemented in the host language — then load, bind and start.
Each loaded input arrives unbound
Loading reconstructs the inputs unbound, and the rebinding sequence before the start is fixed. Create fresh market adapters and an execution, bind each loaded input to an adapter whose type matches the input's saved market-data type — binding registers the input's contract subset on the adapter — and only then start the robot. Starting a robot while any loaded input is still unbound is an error.
In code, the round trip is a save on the Python side and a load, a bind and a start on the C++ side.
account.save("momentum", "momentum.db")
account.load("momentum", "momentum.db");
account.bindInput("sma", market);
account.start("momentum");
The direction is not mandatory
A strategy researched in C++ can be saved and reloaded from Python just as easily, and a team that lives in one language never has to cross the boundary at all. The saved recipe is a convenience, not a required stage of the workflow.
The three language surfaces are not tiers of capability either. The C ABI is the whole surface, and the wrappers stand directly on it: they add ergonomics — objects with lifetimes and the idioms native to each language — and a set of conventional defaults on top of a C surface that has none, but no behaviour of their own.
Version 5.0