HFT Tables: Strategies
Adding a Strategy
ENABLE STRATEGY "basic" ("74300") ON SYMBOL 1 COLUMN_NO 0 TICKS 1;To enable a strategy on a specific HFT table symbol, execute this query. The statement accepts the strategy name (e.g. "basic"), its custom parameters like threshold limits in parenthesized quotes (e.g. "74300"), the symbol index (SYMBOL 1), the column number, and the ticks interval. Ticks indicate how often the strategy will evaluate conditions to optimize CPU consumption.
Listing All Active Strategies
LIST STRATEGY;
To inspect the database's strategy registry and view all currently compiled or active strategies, run this statement. The database returns a response mapping the registered strategy names to their respective compiled binary source file paths on disk.
Adding a Strategy from a JSON File
ADD STRATEGY FROM JSON FILE '/path/to/strategy.json';
You can define a strategy entirely via a JSON configuration file and load it at runtime with a single command. No recompilation is needed. Create a JSON file with the following structure and run the command above (replace the path with the actual location of your JSON file):
{
"name": "custom",
"symbol": "6",
"threshold": "10",
"operator": "greater-than",
"result": "(0+1)*(2/3)*6z"
}Field Reference
| Field | Description |
|---|---|
| name | The unique name for this strategy (used when enabling it later). |
| symbol | The HFT table symbol index this strategy targets. |
| threshold | The numeric threshold value to compare the computed result against. |
| operator | The comparison operator. Supported values: "greater-than", "less-than", "equal-to". |
| result | An expression combining indicator indices with + and - operators. For example, "(0+1)*(2/3)*6z" means: result of (indicator[0] + indicator[1]) * (indicator[2] / indicator[3])*6. The final value is compared against the threshold using the operator. |
Enabling a JSON-Loaded Strategy
Once loaded, enable the strategy the usual way. Since the parameter slot has no meaning for JSON-defined strategies, pass -1 as a placeholder:
ENABLE STRATEGY "custom" ("-1") ON SYMBOL 6 COLUMN_NO 0 TICKS 1;Adding a Custom Strategy (C++ Codebase)
To integrate a new custom strategy into the database codebase, you need to add it directly to the engine source code. Follow these four steps:
Step 1: Create the strategy file inside faststrategy folder
Users have to create the strategy header file (e.g. basic2.hpp) and place it inside the 'faststrategy' folder in the codebase. The custom strategy must follow the standard template structure, detailed block-by-block below:
// Place your strategy class header file under ./faststrategy/
(Part 1): Member Variables & State Definition
Here, the local parameters of the strategy are defined.
class alignas(CACHELINE) BASIC {
private:
int64_t tick = 0;
int64_t count = 0;
int64_t window = 1;
HFT::TableColumn *tableColumn = nullptr;
std::array<HFT::TableColumn, HFT::MAXHFTSYMBOL> *symbolAccessArr;
const int64_t *precision = nullptr;(Part 2): Constructor
In the constructor, the tableColumn and symbolAccessArr are initialized with global values. It is suggested to keep this part as is.
public:
BASIC(std::array<HFT::TableColumn, HFT::MAXHFTSYMBOL> &symbolAccessArr,
int64_t tick, HFT::TableColumn &tableColumn) {
this->symbolAccessArr = &symbolAccessArr;
this->tick = tick;
this->count = 0;
this->tableColumn = &tableColumn;
this->precision = tableColumn.precisions;
}(Part 3): Parameter Parsing
Here the parameters passed in the SQL query are parsed (e.g., setting the window size).
void set_parameter(const std::vector<std::string> &win) {
std::string param = win[0];
int64_t window = 1;
auto [ptr, ec] = std::from_chars(param.data(), param.data() + param.size(), window);
if (ec != std::errc() || window <= 0) {
throw std::runtime_error("Invalid Strategy parameter");
}
this->window = window;
}(Part 4): Strategy Decision Engine
Here the actual strategy logic is implemented. It reads the computed indicator values and compares them with the parameters.
inline bool result() {
count++;
if (LIKELY(count != tick)) {
return false;
}
count = 0;
auto const * __restrict entry = &this->tableColumn->indicators[0];
int64_t val = entry->result_fn(entry->ptr);
return val > this->window;
}(Part 5): Wrapper Callbacks
These are wrapper callbacks used by the engine to efficiently execute your strategy's update and decision logic.
void on_tick() {}
static void run(void *p) { static_cast<BASIC *>(p)->on_tick(); }
static bool get_result(void *p) { return static_cast<BASIC *>(p)->result(); }
FastStrategy::StrategyEntry create() {
FastStrategy::StrategyEntry e;
e.checked = 1;
e.ptr = this; // Pointer to this strategy instance
e.fn = &BASIC::run; // Static updater callback
e.result_fn = &BASIC::get_result; // Static decision logic callback
e.strategyIndex = -1;
return e;
}
};Step 2: Add registration line in strategyHandler.hpp
Add the registration line inside the 'registerAllStrategy' function of 'strategyHandler.hpp'. Here 'basic2' is replaced by the name of the indicator and make sure 'BASIC2' is the name of the class in the written indicator file.
registerStrategy<BASIC2, HFT::MAXHFTSYMBOL>(registry, "basic2", basicPool2); // Inside registerAllStrategy function
Step 3: Include the path inside strategyinclude.hpp
Inside 'strategyinclude.hpp', write the include statement:
#include "./faststrategy/basic2.hpp"
Step 4: Add the strategy pool inside strategyPool.hpp
Inside 'strategyPool.hpp', add the static StrategyPool declaration. Make sure the name of the pool in the 2nd step and this step is exactly the same.
static StrategyPool<BASIC3, HFT::MAXHFTSYMBOL> basicPool3;
Important Note on Compilation
After making any changes to the codebase (such as adding a custom strategy), you must recompile the C++ engine regardless of how you installed the database:
./compile.sh
If you cloned from the GitHub repository:
You only need to run ./compile.sh. The resulting binary can be used directly — no further steps required.
If you installed via a Debian package (.deb):
After compiling, you must also rename the output binary from main.exe to nanodb, otherwise the database will not be usable:
mv main.exe nanodb
Natively Available Strategies
Basic Strategy (basic)
An in-memory threshold crossover strategy. It queries the first active indicator (index 0) associated with the HFT table symbol. If the computed indicator's value exceeds the specified parameter window threshold, the strategy signals a true (buy) trigger, otherwise false. This execution happens natively inside the engine loop for sub-microsecond latency.
Again Strategy (again)
A repeating crossover logic strategy similar to Basic. It polls the leading indicator on index 0 on every tick interval and triggers signals when values breach the threshold. It runs concurrently with other strategies, isolating CPU instructions and avoiding lock contentions.
Note: Strategies and indicators are not stored in disk for a table. So, if a server restarts you will again have to apply the strategies by using ENABLE STRATEGY command.