All docs

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

kindclasswhat it isparams
pricing modelMidPx(bid + ask) / 2port
pricing modelMicroPricePxthe micro price, leaning toward the side with less quantity; optionally smoothed on a samplerport, sampler, smooth
samplerTimeSamplerfires every msecs of market timemsecs, halflife
samplerPxcSamplerfires when a price has moved bpspm, bps, halflife
variableTrendpm / ema − 1, the average taken on the sampler's firespm, sampler, base_pm
variableBboQtyImb(bid_qty − ask_qty) / (bid_qty + ask_qty)port
variableBasisbeta · src_trend − dep_trendsrc_trend, dep_trend, beta
variableLinearSuma weighted sum: how a fitted model comes back into a cfgcomponents, fill_unready
variableConst, Expra fixed value; a formula (below)value; expr
modelLinearModelthe signal a strategy readsvariable, coerce_non_finite_to_zero, treat_unready_as_zero, trading_condition
risk / accountRiskFormula, Accountweights that turn positions into one risk number; a risk book strategies share (max_risk, max_notional per strategy)components; id, risk_formulas
strategySampleMaker, BasisMrMakera two-sided maker around the mid or a pricing model, shifted by a model; a cross-venue basis mean-reversion makerport, 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.