Docs › MTS Lab
Components and libraries
The engine computes prices, clocks, features and signals from components declared in a cfg. Use the built-in classes, write your own in C++, and keep the ones every project needs in a library.
The cfg
A cfg declares components in sections; every entry is [name, [Class, {params}]] and entries refer to each other by name:
"pricing_models": [["mid_bn", ["MidPx", {"port": ["BTCUSDT", "binance"]}]]],
"samplers": [["ts_1s", ["TimeSampler", {"msecs": 1000, "halflife": 10}]]],
"variables": [["trend", ["Trend", {"pm": "mid_bn", "sampler": "ts_1s"}]]],
"models": [["alpha", ["LinearModel", {"variable": "fit_sum"}]]],
"strategies": [["mm", ["SampleMaker", {"port": ["BTCUSDT", "binance"], "qty": 0.01, "model": "alpha"}]]]
In Python you build it with mtslab.Cfg (cfg.variable(…) returns the name, so components chain). The engine checks every cfg before a run: an unknown or missing parameter, a wrong name or kind, a cycle — each is refused with a clear message. Pricing models and stateless variables compute from the books on read; samplers fire on market events and the variables listening to them update then. Values are doubles with a ready flag.
Built-in classes
| kind | class | what it is | params |
|---|---|---|---|
| pricing model | MidPx | (bid + ask) / 2 | port |
| pricing model | MicroPricePx | the micro price, leaning toward the side with less quantity; optionally smoothed on a sampler | port, sampler, smooth |
| sampler | TimeSampler | fires every msecs of market time | msecs, halflife |
| sampler | PxcSampler | fires when a price has moved bps | pm, bps, halflife |
| variable | Trend | pm / ema − 1, the average taken on the sampler's fires | pm, sampler, base_pm |
| variable | BboQtyImb | (bid_qty − ask_qty) / (bid_qty + ask_qty) | port |
| variable | Basis | beta · src_trend − dep_trend | src_trend, dep_trend, beta |
| variable | LinearSum | a weighted sum: how a fitted model comes back into a cfg | components, fill_unready |
| variable | Const, Expr | a fixed value; a formula (below) | value; expr |
| model | LinearModel | the signal a strategy reads | variable, coerce_non_finite_to_zero, treat_unready_as_zero, trading_condition |
| risk / account | RiskFormula, Account | weights that turn positions into one risk number; a risk book strategies share (max_risk, max_notional per strategy) | components; id, risk_formulas |
| strategy | SampleMaker, BasisMrMaker | a two-sided maker around the mid or a pricing model, shifted by a model; a cross-venue basis mean-reversion maker | port, half_spread_bps, qty, model, … |
The full list with every parameter's type, default and meaning: python -m mtslab describe, or ask the agent. A half-life is in sampler fires. Classes and parameter names follow BTS, so a BTS cfg fragment reads the same.
Formulas
For a combination of existing variables no C++ is needed:
["slope", ["Expr", {"expr": "basis_30s - basis_120s + 0.01*clip(imb_bn, -0.5, 0.5)"}]]
+ - * / ^, parentheses, abs sqrt log exp tanh sign min max clip; names are variables, else pricing models.
Your own C++
A strategy or a variable can be a plugin: one C++ file built into a shared object against the SDK's plain C interface, so it does not depend on how the engine was compiled. The leadlag template has one of each (strategies/maker.cpp, variables/trade_flow.cpp); engine.build_plugin("variables/my_var.cpp", out("libmy_var.so")) builds it and the cfg names it as "plugin:/path/libmy_var.so".
class TradeFlow : public mts::Variable {
public:
TradeFlow(const mts::VarHost& h, std::string name, const std::string& params) : mts::Variable(h, std::move(name)) {
// find inputs by name here: host.need(MTS_SAMPLER, "ts_1s"); host.listen(…)
}
void on_event(const MtsMdEvent& ev) override { … } // every market event, books already updated
void on_sample(int64_t sampler, uint64_t t_ns) override { … } // a sampler fired
bool value(double& out) override { … return ready; }
};
MTS_EXPORT_VARIABLE(TradeFlow, R"json([{"name": "port", "type": "port"}, …])json")
Declared parameters are checked by the engine before the constructor runs. A strategy derives from mts::Strategy, reads components with host.find / host.read, and sends orders through the host — the same object paper-trades on live data. The SDK reference is in the install (lab/sdk/README.md) and the agent reads it.
Libraries — your quant code base
Classes every project needs belong in a library: one repository, one shared object with any number of classes of every kind, Python beside them, and tests. Home page → Libraries → Create (template qlib); it opens like a project.
qlib/ cpp/variables/ cpp/pricing_models/ cpp/samplers/ cpp/models/ cpp/strategies/ one class per file cpp/include/qlib/ code the classes share python/qlib/ cfg helpers and shared analysis — `import qlib` in projects and notebooks tests/test_*.py each class pinned by a test
Each class registers itself at the end of its file: MTS_LIBRARY_VARIABLE(TradeFlow, R"json({"doc": "…", "params": […]})json") (also _PRICING_MODEL, _SAMPLER, _MODEL, _STRATEGY). Its steps: build (one lib<name>.so, incrementally, and a table of its classes), test, describe. Release tags a version. Move to library… on a project's plugin turns it into a library class.
A project uses it in project.json: "libs": {"qlib": "main"} — main follows committed code, a tag ("v0.2") pins a release, "work" uses the library's uncommitted edits to try them first. The classes are then used by name like the engine's own (qlib.TradeFlow when a name is ambiguous); every experiment records the library commit, and each version is built once per computer.
The graph view
Any cfg in a project or its outputs has a Graph button: the engine's own graph of it — instruments, prices, clocks, variables, models, the strategy, and what reads what. Click a component for its definition; drag to pan, the mouse wheel zooms.