Algo Framework¶
This article is meant to give you a quick overview of the new algo framework.
Note
A template project exists on GitHub demonstrating how to get started when building your own strategies. Everything shown in this article leans heavily on that template project.
The algo interfaces are almost similar to the lower level client framework and you should be able to upgrade existing strategies with little effort. Please reach out if you have trouble with this upgrade and/or need our help.
Important
This is an early version and we expect interfaces and implementations to change as we work through various strategies. Client feedback is also likely to result in changes. We will try to keep this article up to date with the most recent version of the algo framework.
Design¶

Strategy¶
An algo strategy implements event handlers to manage internal state and optionally send order actions to connected gateways.
A minimal implementation would look something like this:
struct MyStrategy final : public algo::Strategy {
MyStrategy(
algo::Strategy::Dispatcher &dispatcher,
algo::OrderCache &order_cache,
algo::strategy::Config const &)
: dispatcher_{dispatcher}, order_cache_{order_cache} {
// TODO use strategy config to create internal state management
// TODO add additional constructor arguments to configure model parameters
}
protected:
void operator()(Event<Connected> const &) override {}
void operator()(Event<Disconnected> const &) override {}
void operator()(Event<Ready> const &) override {}
// TODO other event handlers...
// note! event handlers for order management now include cached order state
void operator()(Event<OrderAck> const &, cache::Order const &) override {}
void operator()(Event<OrderUpdate> const &, cache::Order const &) override {}
void operator()(Event<TradeUpdate> const &, cache::Order const &) override {}
private:
algo::Strategy::Dispatcher &dispatcher_; // used to send order actions
algo::OrderCache &order_cache_; // used to fetch most recent order state
};
References
Config (Strategy)¶
The strategy config is a list of legs, each defining an instrument and (optional) parameters for order management.
A .toml representation could look something like this
[[legs]]
source=0
account="A1"
exchange="deribit"
symbol="BTC-PERPETUAL"
time_in_force="gtc"
[[legs]]
source=1
account="A1"
exchange="bybit"
symbol="BTCUSD"
time_in_force="gtc"
You can read a .toml file like this
auto strategy_config = algo::strategy::Config::parse_file(path);
References
Trader¶
You will have to implement a factory class allowing the trader application to mange the life-time of the strategy
struct MyFactory final : public client::Trader2::Factory {
std::unique_ptr<algo::Strategy> create_strategy(
algo::Strategy::Dispatcher &dispatcher,
algo::OrderCache &order_cache,
algo::strategy::Config const &config) const override {
// note! this is where you can pass e.g. model parameters to your strategy
return std::make_unique<MyStrategy>(dispatcher, order_cache, strategy_config);
}
The trader application is then executed like this
MyFactory factory;
// note! settings are typically derived from command-line flags (see the template project for an example)
// note! params will typically be the list of command-line arguments (.sock paths)
client::Trader2::dispatch(settings, factory, config, params);
References
Matcher¶
The matcher interface is used to when simulating order matching.
Note
The simulator will take care of offsetting time and the queueing of events to simulate latency between strategy and exchange.
Although you can, you don’t have to implement your own matcher: the roq-algo
project comes with some simple implementations
// typically the simulator factory method
auto create_matcher(auto &dispatcher, auto &order_cache, auto &config) {
auto type = algo::matcher::Type::SIMPLE; // note! very conservative assumptions
return algo::matcher::Factory::create(type, dispatcher, order_cache, config);
}
References
Reporter¶
The reporter interfaces are used when implementing a specific reporting style.
Note
The reporter will see all events entering a strategy as well as all actions leaving the strategy. A specific reporter has full control over what has to be collected and what kind of derived statistics should be computed.
Although you can, you don’t have to implement your own reporter: the roq-algo
project comes with some simple implementations
// typically the simulator factory method
auto create_repoter() {
auto type = algo::reporter::Type::SUMMARY; // note! sampled statistics + order/fill history
return algo::reporter::Factory::create(type);
}
References
Config (Simulator)¶
The simulator config defines latencies by source and optionally the initial positions.
A .toml representation could look something like this
[[sources]]
market_data_latency_ms=5
order_management_latency_ms=10
[[sources.accounts.A1]]
exchange="deribit"
symbol="BTC-PERPETUAL"
long_position=3
[[sources]]
market_data_latency_ms=175
order_management_latency_ms=180
[[sources.accounts.A1]]
exchange="bybit"
symbol="BTCUSD"
short_position=3
You can read a .toml file like this
auto simulator_config = algo::simulator::Config::parse_file(path);
References
Simulator¶
Note
The simulator factory derives from the trader factory. You can
struct MyFactory final : public client::Simulator::Factory {
std::unique_ptr<algo::Strategy> create_strategy(
algo::Strategy::Dispatcher &dispatcher,
algo::OrderCache &order_cache,
algo::strategy::Config const &config) const override {
// note! this is where you can pass e.g. model parameters to your strategy
return std::make_unique<MyStrategy>(dispatcher, order_cache, config);
}
std::unique_ptr<algo::Matcher> create_matcher(
algo::Matcher::Dispatcher &dispatcher, algo::OrderCache &order_cache, algo::matcher::Config const &config) const {
auto type = algo::matcher::Type::SIMPLE; // note! simple and conservative matching
return algo::matcher::Factory::create(type, dispatcher, order_cache, config);
}
std::unique_ptr<algo::Reporter> Factory::create_reporter() const {
auto market_data_source = algo::MarketDataSource::MARKET_BY_PRICE; // note! we may need impact price for profit/loss calculations
auto sample_frequency = 10s;
auto config = algo::reporter::Summary::Config{
.market_data_source = market_data_source,
.sample_frequency = sample_frequency,
};
return algo::reporter::Summary::create(config);
}
A simulator application is then executed like this
MyFactory factory;
// note! settings are typically derived from command-line flags (see the template project for an example)
// note! params is this list of command-line arguments, typically .sock paths
auto reporter = client::Simulator::dispatch(settings, factory, strategy_config, simulator_config, params);
References
Arbitrage (Example)¶
An arbitrage strategy (named Simple
) was implemented to demo certain features of a real strategy.
You could use this as the basis for starting your own development.
Important
We don’t recommend to use this strategy in production.
References
Arbitrage strategy on GitHub
Python¶
Binding exists allowing you to simulate strategies from Python. This is particularly useful when experimenting with model parameters where one can leverage the powerful data analysis tools from the Python eco-system.
One could also imagine using Python’s multi-processing when optimizing model parameters.
A Jupyter notebook was implemented to demo these capabilities.
References
Jupyter notebook on GitHub