This project builds a TypeScript-based neural trading engine that consumes KuCoin trade data, aggregates it into feature-rich windows, feeds those windows into a temporal convolutional network (TCN), and adjusts model-driven signals with a risk map to produce bounded exposure guidance. The training runtime lives under training/, while a Go websocket API under api/ streams new predictions to subscribers.
The sections below walk through the entire flow—data ingestion, feature construction, model training, risk management, and decision output—in the exact order that the application executes. Setup and run instructions follow at the end.
-
System prerequisites
- Node.js 18+ (the repo was developed with Node 22.13.1).
- npm (bundled with Node installations).
-
Install dependencies
cd training npm installThis pulls the runtime libraries (
@tensorflow/tfjs-node,axios) and development tooling (typescript,ts-node,@types/node). -
Project layout essentials
training/src/– TypeScript trading engine (client, data, model, risk, orchestration).training/package.json– npm scripts and dependencies for the trainer.api/– Go websocket API that relays prediction inserts from Postgres to connected clients (/ws).fed/– React + Tailwind dashboard (shadcn-inspired UI) that subscribes to the websocket feed.docker-compose.yml– Spins up the trainer, Postgres, and websocket API on a single network.
File: training/src/client/kucoinClient.ts
- Builds an
AxiosInstancethat defaults tohttps://api.kucoin.com. - Exposes
fetchRecentTrades(symbol, limit):- Enforces KuCoin’s 1–500 trade limit.
- Calls
/api/v1/market/historieswith the requested symbol. - Coerces price and size fields to floating-point numbers.
- Returns an array of
KucoinTraderecords (sequence,price,size,side,time).
This is the only network-aware component; everything onward manipulates in-memory data structures.
File: src/data/barBuilder.ts
- Sorts raw KuCoin trades by ascending timestamp.
- Buckets trades into fixed-duration candles (
intervalMs, defaulted later to one minute). - Tracks mutable accumulator state per bucket:
- OHLC price fields (
open,high,low,close). - Volume, buy volume, sell volume, notional amount, trade count.
- OHLC price fields (
- On bucket rollover or at the end of the series, flushes the accumulator into a
TradeBar:{ startTime, endTime, open, high, low, close, volume, buyVolume, sellVolume, notional, tradeCount, vwap }
- Optionally trims to the latest
maxBarsif requested.
This step transforms tick-level trades into evenly spaced bars that downstream components consume.
File: training/src/data/featureExtractor.ts
For each contiguous sequence of bars:
- Compute per-bar log returns and rolling volatility estimates.
- Construct a 22-dimensional feature vector comprising:
- Price action ratios: close, VWAP, log return, spread/close, body/open, and upper/lower shadows.
- Volume analytics: raw volume, notional value, buy-volume share, signed volume delta, and volume vs. 20-bar SMA.
- Volatility metrics: rolling return stdev, ATR/close, and Bollinger-percent position.
- Trend/oscillator signals: close vs. SMA20/EMA12, RSI14, MACD line & signal, TSI (25/13), and 5-bar momentum.
- Slice the bar series into overlapping
windowSizesequences (default 64 bars). - Optionally z-score each column within the window to normalize features.
- Derive a binary label for the next bar (
1ifnextClose > lastClose, else0). - Package each window into:
{ window: number[][], // shape [windowSize, FEATURE_COUNT] label: number, // 0 or 1 lastClose: number, nextClose: number, bars: TradeBar[] }
The final window is reserved for inference; earlier windows feed model training.
File: training/src/model/tcn.ts
- Defines
TcnConfigwith tunable hyperparameters:filtersPerLayer,kernelSize,dropoutRate,denseUnits,learningRate.
- Builds a causal temporal convolutional network:
- Stacks residual Conv1D blocks (dilation fixed at 1 in tfjs-node for gradient stability).
- Applies layer normalization and ReLU activations inside each block.
- Aggregates skip connections, performs global average pooling.
- Adds a dense “embedding” layer before a single-unit sigmoid output for probability.
- Compiles the model with Adam optimizer and binary cross-entropy loss.
The architecture outputs the probability that the next bar closes higher than the current bar.
File: training/src/model/trainer.ts
tensorsFromWindows:- Converts an array of feature windows into
tf.Tensor3Dinputs (batch × window × features) andtf.Tensor2Dlabels. - Provides shape validation—throws if the feature vectors are empty.
- Converts an array of feature windows into
trainModel:- Wraps
model.fitwith configurable epochs, batch size, validation split, and shuffle flag. - Includes early stopping (patience = 3) to avoid overfitting on limited data.
- Wraps
The trading engine uses these helpers to train (or fine-tune) the TCN on recent windows.
File: training/src/risk/riskMap.ts
computeReturns– converts bars to log returns (for volatility assessment).computeForecastVolatility– calculates trailing volatility using a configurable lookback, defaulting to the target if insufficient data exists.applyRiskMap– transforms model probabilities into real-valued exposure:- Converts probability to a centered signal (
p ∈ [0,1] → signal ∈ [-1,1]). - Applies a neutral-zone deadband (returns zero inside the band).
- Scales inversely with forecast volatility (capped at 1×).
- Clamps the final value to
[-maxExposure, maxExposure].
- Converts probability to a centered signal (
The risk map is where the “secret sauce” modulates raw model output according to market conditions.
File: training/src/tradingEngine.ts
Detailed execution order when runTradingCycle is invoked:
- Configuration unpacking – reads symbol, history length, bar interval, window size, client/model/training/risk configs, and volatility lookback.
- Trade fetch – retrieves the requested history (default 500, but the engine can page back thousands of trades when
TRADE_HISTORY_LIMITis higher). - Bar construction – aggregates trades into
TradeBars using the requested interval. - Feature window generation – builds overlapping normalized windows and labels, with 22 engineered features per bar (price/volume ratios, moving-average differentials, RSI, MACD, TSI, ATR scaling, momentum, Bollinger position, and more).
- Training sample split – uses all but the last window for training; reserves the last for inference.
- Model instantiation – builds a TCN tailored to the effective window size and feature count, overlaying any user-defined hyperparameters.
- Model training (optional) – if training samples exist:
- materialize tensors,
- fit the model with early stopping,
- dispose of tensors to release native memory.
- Inference – feed the latest window through the model to obtain
probability. - Volatility & risk adaptation:
- compute log returns,
- estimate forecast volatility (respecting
volatilityLookback), - apply the risk map to produce a bounded
exposure.
- Return payload – packages probability, exposure, volatility, latest window, bar count, and number of trained samples.
If insufficient data is available at any checkpoint, the engine returns a neutral decision (probability=0.5, exposure=0).
File: src/index.ts
- Reads environment variables to build a
TradingEngineConfig:TICKET(preferred) orSYMBOL,TRADE_HISTORY_LIMIT,BAR_INTERVAL_MINUTES,FEATURE_WINDOW_SIZE.- TCN overrides (
TCN_FILTERS,TCN_KERNEL,TCN_DROPOUT,TCN_DENSE_UNITS,TCN_LEARNING_RATE). - Training overrides (
TRAINING_EPOCHS,TRAINING_BATCH_SIZE,TRAINING_VAL_SPLIT,TRAINING_SHUFFLE). - Risk overrides (
RISK_NEUTRAL_ZONE,RISK_VOL_TARGET,RISK_VOL_FLOOR,RISK_MAX_EXPOSURE). - Persistence overrides (
MODEL_STORE_PATHdefaults tomodels/). - Client overrides (
KUCOIN_BASE_URL,KUCOIN_TIMEOUT_MS). - Scheduler & logging overrides (
RUN_INTERVAL_MINUTES,LOG_FILE).
- Runs trading cycles in perpetuity on a 15-minute interval by default:
- Each cycle generates descriptive logs (start, configuration summary, prediction summary).
- Logs are streamed to STDOUT and appended to the configured log file (default
logs/trading.log). - A detailed JSON payload of every prediction is written inside the log file.
- The console still outputs the same messages that are persisted to disk, making live monitoring and post-mortem reviews consistent:
{ "probability": 0.63, "exposure": 0.28, "forecastVolatility": 0.012, "barsCount": 256, "trainedSamples": 191, "windowShape": { "rows": 64, "cols": 12 }, "symbol": "BTC-USDT" }
This entry point is how you interact with the system in batch mode.
-
Compile TypeScript to JavaScript
cd training npm run build -
Run the trading cycle directly (on-the-fly compilation)
cd training npm startor provide custom environment variables:
TICKET=ETH-USDT RUN_INTERVAL_MINUTES=5 LOG_FILE=logs/eth.log npm start
For symbols with sparse trade data, you can shrink the feature window and the candle interval using the provided helper script:
cd training npm run start:short-windowThis script sets
FEATURE_WINDOW_SIZE=32,BAR_INTERVAL_MINUTES=0.25,TRAINING_EPOCHS=12, andRUN_INTERVAL_MINUTES=5, which helps ensure enough samples are available for training.To gather a larger historical lookback each cycle, use the deep-lookback helper which pages through the KuCoin trade history endpoint until it amasses ~5,000 trades:
cd training npm run start:deep-lookbackThe trained model is saved under
models/<symbol>/by default; override withMODEL_STORE_PATHif you need a different location. When running in Docker, mount a host volume to this directory so weights persist across container restarts. The process will continue to execute every configured interval until interrupted (Ctrl+C). When running with Postgres (see docker-compose), providePG_HOST,PG_PORT,PG_USER,PG_PASSWORD, andPG_DATABASEso prediction payloads are persisted automatically. -
Execute the compiled output (after
npm run build)node training/dist/index.js
-
Optional verifications
- Ensure KuCoin API credentials and rate limits are respected (public endpoints do not require keys, but high-frequency usage should be considerate).
- Monitor console output for JSON decisions; integrate this output into downstream strategy components as needed.
- Inspect the log file for historical predictions, configuration summaries, and failure diagnostics.
- Check the
MODEL_STORE_PATHdirectory to confirmmodel.jsonandweights.binare being updated per symbol. - If the websocket API is running, connect to
ws://<host>:<API_PORT>/wsto receive live prediction payloads as they are inserted into Postgres.
docker compose up --buildThis boots the trainer (Node.js), Postgres, the websocket API (Go), and the React dashboard. By default the dashboard is reachable at http://localhost:4173 and the websocket at ws://localhost:8080/ws (configurable via compose environment variables).
Or use the provided Makefile:
make up # build images and start the stack
make down # stop everything
make logs # tail all service logsSample environment files are provided as .env.docker.local.example and .env.docker.prod.example. Copy one to .env (Docker Compose loads it automatically) and adjust values as needed—particularly VITE_WS_URL when exposing the websocket through Cloudflare tunnels.
- Build locally via
cd api && go build ./.... - Run the server (for local testing) with
go run ./api/cmd/wsapi. - When running through Docker Compose, the service listens on
/ws(default port 8080). Every insert into thepredictionstable triggers a PostgresNOTIFY, which the API broadcasts to subscribed websocket clients as raw JSON payloads. - A simple HTTP endpoint is also available at
GET /recentto fetch the most recent predictions (defaultlimit=10; configurable via?limit=). Example:curl http://localhost:8080/recent?limit=10.
- The server sends websocket PING control frames on an interval to keep intermediaries (e.g., Cloudflare tunnels) from closing idle connections, and expects a PONG in response. These are configurable via environment variables:
WS_PING_SECONDS– interval between server pings (default25seconds). Accepts either an integer (seconds) or a Go duration string (e.g.,30s,1m).WS_PONG_WAIT_SECONDS– read deadline extension after receiving a PONG (default60seconds, but at least2× WS_PING_SECONDS).
The frontend also sends a lightweight JSON heartbeat {type:"ping"} on the same cadence and logs {type:"pong"} responses for visibility.
- Install dependencies with
cd fed && npm install. - Set the websocket endpoint via
VITE_WS_URL(defaults tows://localhost:8080/ws). - Optional heartbeat interval via
VITE_WS_PING_MS(defaults to25000ms). This controls how often the UI sends{type:"ping"}. - Start the dev server using
npm run dev; the UI shows descriptive context on the left and a live feed of prediction cards on the right, with the newest payload inserted at the top. - Build for production using
npm run build(outputs tofed/dist/).
Each card represents one model inference persisted to Postgres and streamed to the UI.
- Symbol: Trading pair for which the inference was produced (e.g.,
BTC-USDT). - Timestamp: The time the payload was created or received.
- Probability: Model-estimated probability that the next bar will close higher than the current bar. Displayed as a percentage (e.g.,
13.59%). Values near 50% indicate low directional edge; values closer to 0%/100% indicate stronger directional conviction. This is not a price forecast—only directional likelihood for the immediate next bar. - Exposure: Final position sizing after the risk map is applied, within
[-100%, 100%]. Positive values indicate long bias, negative values indicate short bias. Exposure scales with conviction and inversely with forecast volatility, and is clipped to the configured max exposure. This number is what you would trade in a simple proportional strategy. - Forecast volatility: A near-term volatility estimate (e.g.,
3.67e-5) computed from recent log returns. Lower values imply calmer markets; higher values imply choppier markets. The risk map reduces exposure as this value rises. - Bars: How many candles were available after aggregation for this cycle (e.g.,
68). A larger number generally provides more context for features, but does not directly indicate training length. - Samples: The number of windowed samples used to train or fine‑tune the model before inference (e.g.,
3).0means the model skipped training in that cycle and only performed inference on the latest window. - Window: The shape of the input matrix
[rows × cols]used for inference (e.g.,64×22).rowsequals the time steps per window;colsequals engineered feature count. This determines the temporal receptive field the model sees per inference.
Front-end layout
- Left panel: Context about the system, current connection status, and where the websocket is connected.
- Right panel: A scrollable feed of the newest predictions at the top (limited to the viewport height for usability). When the page first loads, the UI queries
GET /recentto seed the list, then listens for live updates via the websocket.
- Hyperparameter tuning – Adjust TCN filter stacks or training epochs via environment variables; the engine rebuilds the model dynamically each run.
- Alternative data intervals – Change
BAR_INTERVAL_MINUTESto create longer/shorter bars (e.g., 5-minute candles). - Custom risk logic – Modify
applyRiskMapif you need advanced exposure curves. - Adding persistence – Persist trained weights by calling
model.saveinsidetradingEngine.tsif you want continuity between runs. - Debugging data issues – Log the intermediate
TradeBars or feature matrices to confirm normalization and labeling when experimenting with new symbols. - Database persistence – The TypeScript runtime can insert each prediction into Postgres. Configure
PG_HOST,PG_PORT,PG_USER,PG_PASSWORD, andPG_DATABASE(defaults provided indocker-compose.yml). - Websocket API – The
api/service (Go) listens on/wsand streams new prediction payloads using PostgresLISTEN/NOTIFY. Build locally withcd api && go build ./...or run via Docker Compose. - React dashboard – The
fed/app consumes the websocket stream. Start withnpm run dev(after configuringVITE_WS_URL) to visualise predictions in real time.
By following the steps above, you can understand, configure, and run the neural trading application from raw KuCoin trades through to exposure-adjusted decisions. Feel free to build additional tooling or dashboards on top of the JSON output emitted by the CLI.