Node.js/TypeScript Quick Start
Node.js excels at I/O-bound operations, making it ideal for trading bots that spend most of their time waiting on API responses. TypeScript adds type safety that catches configuration errors before deployment. This guide walks through building a realtime price alert bot that monitors prices and emits alerts when thresholds are crossed.
By the end of this guide, you'll have a working realtime bot that continuously monitors prices and emits metrics to the dashboard.
Prerequisites
Before starting, ensure you have the CLI installed and authenticated:
# Clone the repository and build the CLI
git clone https://github.com/alexanderwanyoike/the0.git
cd the0/cli
make install
# Authenticate
the0 auth loginYou'll also need Node.js 20 or higher installed locally for testing.
Project Structure
Create a new directory for your bot:
mkdir price-alerts
cd price-alertsA TypeScript bot requires these essential files:
price-alerts/
├── bot-config.yaml # Bot metadata and runtime settings
├── main.ts # Entry point with trading logic
├── bot-schema.json # Configuration schema for users
├── package.json # Dependencies and build scripts
└── tsconfig.json # TypeScript configurationDefining Bot Metadata
Start with bot-config.yaml:
name: price-alerts
description: "Monitors prices and emits alerts when thresholds are crossed"
version: 1.0.0
author: "your-name"
type: realtime
runtime: nodejs20
entrypoints:
bot: main.js
schema:
bot: bot-schema.json
readme: README.md
metadata:
categories: [alerts, monitoring]
tags: [realtime, beginner]
complexity: beginnerThe type: realtime setting means this bot runs continuously from start until stopped. Unlike scheduled bots that execute once and exit, realtime bots maintain state across iterations and emit metrics continuously. The entry point is main.js because the platform runs the compiled JavaScript, not the TypeScript source.
Defining Configuration Schema
Create bot-schema.json to specify what parameters users can configure:
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"title": "Price Alerts Configuration",
"description": "Configuration for the price alerts bot",
"properties": {
"symbol": {
"type": "string",
"description": "Symbol to monitor",
"default": "BTC/USD"
},
"base_price": {
"type": "number",
"description": "Base price for simulation",
"default": 45000
},
"alert_threshold": {
"type": "number",
"description": "Price change percentage to trigger alert",
"default": 1.0,
"minimum": 0.1,
"maximum": 10
},
"update_interval_ms": {
"type": "number",
"description": "Milliseconds between price updates",
"default": 5000,
"minimum": 1000,
"maximum": 60000
}
},
"additionalProperties": false
}Setting Up TypeScript
Create package.json:
{
"name": "price-alerts",
"version": "1.0.0",
"type": "module",
"main": "main.js",
"scripts": {
"build": "tsc",
"start": "node main.js"
},
"dependencies": {
"@alexanderwanyoike/the0-node": "^0.1.0",
"pino": "^9.0.0"
},
"devDependencies": {
"typescript": "^5.3.0",
"@types/node": "^20.0.0"
}
}Create tsconfig.json:
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "node",
"esModuleInterop": true,
"strict": true,
"outDir": ".",
"rootDir": ".",
"declaration": false
},
"include": ["main.ts"]
}Install the SDK from GitHub Packages:
# Configure npm for the @alexanderwanyoike scope
echo "@alexanderwanyoike:registry=https://npm.pkg.github.com" >> ~/.npmrc
# Install dependencies
npm installWriting the Bot Logic
Create main.ts with your bot implementation. The key pattern for TypeScript bots is exporting the main function—the runtime invokes it directly rather than your code calling it:
import { parse, metric, sleep, success } from "@alexanderwanyoike/the0-node";
import pino from "pino";
const logger = pino({ level: "info" });
interface BotConfig {
symbol: string;
base_price: number;
alert_threshold: number;
update_interval_ms: number;
}
interface BotState {
lastPrice: number;
priceHistory: number[];
lastAlertTime: number;
}
async function main(): Promise<void> {
// Parse configuration from environment
const { id, config } = parse<BotConfig>();
const symbol = config.symbol || "BTC/USD";
const basePrice = config.base_price || 45000;
const alertThreshold = config.alert_threshold || 1.0;
const updateInterval = config.update_interval_ms || 5000;
logger.info({ botId: id, symbol, alertThreshold }, "Bot started");
// Initialize state that persists across iterations
const state: BotState = {
lastPrice: basePrice,
priceHistory: [basePrice],
lastAlertTime: 0,
};
// Main loop - runs until process is terminated
while (true) {
const priceData = simulatePrice(state, symbol, basePrice);
// Emit price metric
metric("price", {
symbol: priceData.symbol,
value: priceData.value,
change_pct: priceData.change_pct,
high_24h: priceData.high_24h,
low_24h: priceData.low_24h,
});
// Check for alert conditions
const alert = checkAlertConditions(state, priceData, alertThreshold, symbol);
if (alert) {
metric("alert", {
symbol: alert.symbol,
type: alert.type,
change_pct: alert.change_pct,
message: alert.message,
severity: alert.severity,
});
}
// Update state
state.lastPrice = priceData.value;
state.priceHistory.push(priceData.value);
if (state.priceHistory.length > 100) {
state.priceHistory.shift();
}
await sleep(updateInterval);
}
}
function simulatePrice(state: BotState, symbol: string, basePrice: number) {
const volatility = 0.002;
const meanReversion = 0.001;
const randomChange = (Math.random() - 0.5) * 2 * volatility;
const reversion = ((basePrice - state.lastPrice) / basePrice) * meanReversion;
const changePercent = randomChange + reversion;
const newPrice = state.lastPrice * (1 + changePercent);
const changePct = ((newPrice - state.lastPrice) / state.lastPrice) * 100;
return {
symbol,
value: Math.round(newPrice * 100) / 100,
change_pct: Math.round(changePct * 1000) / 1000,
high_24h: Math.max(...state.priceHistory, newPrice),
low_24h: Math.min(...state.priceHistory, newPrice),
};
}
function checkAlertConditions(
state: BotState,
priceData: { value: number; change_pct: number },
threshold: number,
symbol: string
) {
const now = Date.now();
// Cooldown: don't alert more than once per 30 seconds
if (now - state.lastAlertTime < 30000) {
return null;
}
const absChange = Math.abs(priceData.change_pct);
if (absChange >= threshold) {
state.lastAlertTime = now;
const direction = priceData.change_pct > 0 ? "spike" : "drop";
const severity = absChange >= threshold * 2 ? "high" : "medium";
return {
symbol,
type: `price_${direction}`,
change_pct: priceData.change_pct,
message: `${symbol} ${direction} of ${absChange.toFixed(2)}%`,
severity,
};
}
return null;
}
// Export main for the runtime to invoke
export { main };Notice that the file ends with export { main }; rather than calling main(). The platform runtime imports your module and invokes the exported main function directly.
SDK Functions
The Node.js SDK provides these core functions:
parse()
Reads BOT_ID and BOT_CONFIG from environment variables. The generic type parameter provides TypeScript type safety for your configuration:
import { parse } from "@alexanderwanyoike/the0-node";
interface MyConfig {
symbol: string;
threshold: number;
}
const { id, config } = parse<MyConfig>();
// config.symbol and config.threshold are now type-checkedmetric(type, data)
Emits a metric to the platform dashboard:
import { metric } from "@alexanderwanyoike/the0-node";
metric("price", { symbol: "BTC/USD", value: 45000, change_pct: 2.5 });
metric("alert", { type: "price_spike", severity: "high", message: "BTC up 5%" });
metric("signal", { direction: "long", confidence: 0.85 });sleep(ms)
Async utility for waiting between iterations:
import { sleep } from "@alexanderwanyoike/the0-node";
await sleep(5000); // Wait 5 secondssuccess(message, data)
Reports successful execution for scheduled bots:
import { success } from "@alexanderwanyoike/the0-node";
success("Analysis complete", { processed: 100 });error(message)
Reports failure and terminates with exit code 1:
import { error } from "@alexanderwanyoike/the0-node";
if (!config.api_key) {
error("API key is required");
}Building and Testing
Compile TypeScript to JavaScript:
npm run buildTest locally by setting environment variables:
export BOT_ID="test-bot"
export BOT_CONFIG='{"symbol":"BTC/USD","base_price":45000,"alert_threshold":1.0,"update_interval_ms":2000}'
export CODE_MOUNT_DIR="/tmp"
npm startThe bot should start emitting price and alert metrics. Press Ctrl+C to stop.
Deploying
Deploy your bot to the platform:
the0 custom-bot deployThe CLI validates configuration, runs npm install to vendor dependencies, packages everything, and uploads to the platform.
Creating Bot Instances
Once deployed, create instances that run your bot. For realtime bots, no schedule is needed—they run continuously:
{
"name": "btc-price-alerts",
"type": "realtime/price-alerts",
"version": "1.0.0",
"config": {
"symbol": "BTC/USD",
"base_price": 45000,
"alert_threshold": 2.0,
"update_interval_ms": 10000
}
}Deploy the instance:
the0 bot deploy instance-config.jsonThe bot starts immediately and runs until you stop it.
Monitoring
Monitor running instances:
# List running instances
the0 bot list
# View logs
the0 bot logs <bot_id>
# Stream logs in real-time
the0 bot logs <bot_id> -w
# Stop a realtime bot
the0 bot delete <bot_id>Alternative: Structured Logging for Metrics
Instead of using the SDK's metric() function, you can emit metrics through pino's structured logging. Include a _metric field:
import pino from "pino";
const logger = pino();
// Emit metrics via logging
logger.info({
_metric: "price",
symbol: "BTC/USD",
value: 45000,
change_pct: 2.5,
}, "price_update");
logger.info({
_metric: "alert",
type: "price_spike",
severity: "high",
message: "BTC up 5%",
}, "alert_triggered");The platform automatically detects and parses JSON log entries with _metric fields. See Metrics for details on both approaches.
JavaScript Alternative
If you prefer plain JavaScript over TypeScript, create main.js directly:
import { parse, metric, sleep } from "@alexanderwanyoike/the0-node";
async function main() {
const { id, config } = parse();
const symbol = config.symbol || "BTC/USD";
let lastPrice = config.base_price || 45000;
while (true) {
const change = (Math.random() - 0.5) * 0.02;
const price = lastPrice * (1 + change);
metric("price", {
symbol,
value: Math.round(price * 100) / 100,
change_pct: change * 100,
});
lastPrice = price;
await sleep(config.update_interval_ms || 5000);
}
}
export { main };Update bot-config.yaml to point to main.js and remove TypeScript dependencies from package.json.
Next Steps
With your first realtime bot deployed, explore these topics:
- Configuration - Complete bot-config.yaml reference
- Bot Types - Scheduled vs realtime execution models
- Metrics - Dashboard metrics and structured logging
- Custom Frontends - Build React dashboards for your bot
- Testing - Local testing patterns and best practices