Event-driven trading platform for market data ingestion, strategy execution, and order execution.
Most of the code in this repository was written with AI assistance.
market-data-gatewayconsumes exchange kline data.market-data-gatewaypublisheskline.dataevents.market-data-workerconsumeskline.dataand stores records inmarket_dataDB.strategyconsumeskline.dataand decides: buy, sell, or no action.- If execution is required,
strategypublishesorder_engine.place_order. order-engine-gatewayconsumes order events and executes to exchange.order-engine-gatewaystores initial order history inorder_engineDB.order-engine-workerpolls tracked orders and fetches latest execution state from exchange.order-engine-workerupdatesorder_historiesstatus inorder_engineDB.
flowchart TB
EX[(Exchange)]
NATS[(NATS JetStream)]
MDG[market-data-gateway]
MDW[market-data-worker]
STRAT[strategy]
OEG[order-engine-gateway]
OEW[order-engine-worker]
MDB[(market_data DB)]
ODB[(order_engine DB)]
EX -->|kline websocket| MDG
MDG -->|publish kline.data| NATS
NATS -->|consume kline.data| MDW
NATS -->|consume kline.data| STRAT
MDW -->|insert kline| MDB
STRAT -->|publish order_engine.place_order| NATS
NATS -->|consume place_order| OEG
OEG -->|execute order| EX
OEG -->|store order history| ODB
ODB -->|load tracked orders| OEW
OEW -->|fetch latest order status| EX
OEW -->|update order_histories status| ODB
- Connects to exchange websocket stream.
- Normalizes incoming kline payload.
- Publishes
kline.datato JetStream.
- Consumes
kline.dataevents. - Persists market kline records.
- Retries failed processing (bounded by config).
- Consumes closed kline events.
- Runs strategy logic (buy/sell/hold).
- Publishes
order_engine.place_orderwhen execution is needed.
- Grid levels are unlimited by default (no hard cap).
- Optional
MaxLongLevelsin the lazy-grid config caps concurrent long levels;0means unlimited. strategy.lazy_grid.reset_state_on_startclears persisted lazy-grid state on startup.
- Consumes place-order events.
- Converts order payload to exchange API format.
- Sends order and stores order history.
- Syncs
order_historiesrecords with the latest execution state from exchange. - Fetches latest order status from exchange for tracked orders.
- Updates local order history status to keep it consistent with exchange execution result.
- Runs DB migration for
market_dataandorder_engine.
- Tokocrypto (currently supported).
- Go
1.25+ - Docker + Docker Compose
Copy config template:
copy config.yml.example config.ymlUpdate required values in config.yml:
exchanges.tokocrypto.api_keyexchanges.tokocrypto.api_secretapi_keys[*].key
Host mapping reminder:
- Run app locally (
go run): uselocalhosthosts. - Run app in Docker: use compose service names (
postgresql,redis,nats).
docker compose -f compose-dev.yaml up -d postgresql redis natsRun in PostgreSQL:
CREATE DATABASE market_data;
CREATE DATABASE order_engine;go run . migrate --databaseName=market_data
go run . migrate --databaseName=order_engineINSERT INTO kline_subscriptions
(id, exchange, symbol, "interval", payload, created_at, updated_at)
VALUES
('2675c22f-d94b-4ede-bb27-5b6c375a1089', 'tokocrypto', 'TKO_IDR', '1m', '{"id": 1, "method": "SUBSCRIBE", "params": ["tkoidr@kline_1m"]}'::jsonb, '2026-02-22 13:46:45.903', '2026-02-22 13:46:45.903');
INSERT INTO symbol_mappings
(id, exchange, symbol, kline_symbol, order_symbol, created_at, updated_at)
VALUES
('4494faed-468c-46b5-b7ca-389419ad63ad', 'tokocrypto', 'TKOIDR', 'tkoidr', 'TKO_IDR', '2026-02-22 14:36:22.978', '2026-02-22 14:36:22.978');
-- Example: create TKOIDR partition
-- SELECT create_symbol_partition('TKOIDR');
-- SELECT create_month_partition('TKOIDR', 2026, 2);go run . market-data-gatewaygo run . market-data-workergo run . order-engine-gatewaygo run . order-engine-workergo run . strategycurl --request POST \
--url http://localhost:9801/order-engine/v1/orders \
--header 'Content-Type: application/json' \
--data '{
"api_key": "REPLACE_WITH_SECURE_API_KEY_A",
"request_id": "e1580b74-1af6-47e4-9b90-354875f1f19z",
"user_id": "2cb989ec-5aea-422a-bb74-d735ae5d7230",
"order_id": "1d421596-2ff1-4798-b1b5-5336ed39122z",
"exchange": "tokocrypto",
"symbol": "TKO_IDR",
"type": "LIMIT",
"side": "BUY",
"price": "920",
"quantity": "30",
"source": "http-hit",
"strategy_id": "d9b0ba9a-db44-4bb4-b862-a60d2e0299a6",
"is_paper_trading": true
}'{
"request_id": "e1580b74-1af6-47e4-9b90-354875f1f19e",
"user_id": "2cb989ec-5aea-422a-bb74-d735ae5d7230",
"order_id": "1d421596-2ff1-4798-b1b5-5336ed391227",
"exchange": "tokocrypto",
"symbol": "TKO_IDR",
"type": "LIMIT",
"side": "BUY",
"price": "920",
"quantity": "30",
"source": "grpc-hit",
"strategy_id": "d9b0ba9a-db44-4bb4-b862-a60d2e0299a6",
"is_paper_trading": true
}- Build analytic service that consumes market data.
- Support HA deployment for all services.
- Support multiple user credentials.
- Support multiple exchanges.
Contributions are welcome and appreciated. I am open for contributions from the community.
If you want to contribute:
- Fork this repository.
- Create a feature/fix branch.
- Make focused changes with clear commit messages.
- Open a Pull Request with a short description of the problem and solution.