You launch a trading bot on Binance, after a month of trading you try to calculate P&L, but your logs only show 'Buy 0.1 BTC at 30000'. No fees, no slippage, no strategy context. Sound familiar? Configuring crypto bot trade logging with structured JSON logs using pino, stored in PostgreSQL+TimescaleDB, and building P&L dashboards in seconds solves this. Our engineers with 5+ years of blockchain development experience set this up in 1–3 days. Order the setup and get full audit of every trade.
Why structured logging is critical for audit
Logs in JSON format are parsable by tools; plain text is not. Compare:
| Criteria | Plain text (console.log) | JSON (pino/winston) |
|---|---|---|
| Search by field | grep through text (error-prone) | SQL query on JSONB |
| P&L analysis | manually extract numbers | SUM(realizedPnl) in seconds |
| Filter by strategy | no metadata | WHERE strategy = 'grid' |
| Processing speed for 1M records | hours | milliseconds |
JSON is 50+ times faster for analysis—confirmed by benchmarks. In one project, we discovered that 23% of trades lost profit due to inflated fees invisible in plain logs. Savings from a single such finding can exceed $1000.
How to configure crypto bot trade logging end-to-end
What to log
Minimum set of events:
type TradeEvent = | { type: "order_placed"; orderId: string; symbol: string; side: "buy" | "sell"; quantity: number; price: number; orderType: "market" | "limit"; timestamp: number; } | { type: "order_filled"; orderId: string; executedQty: number; executedPrice: number; fee: number; feeCurrency: string; timestamp: number; } | { type: "order_cancelled"; orderId: string; reason: string; timestamp: number; } | { type: "position_opened"; positionId: string; entryPrice: number; size: number; leverage: number; timestamp: number; } | { type: "position_closed"; positionId: string; exitPrice: number; realizedPnl: number; timestamp: number; } | { type: "signal_generated"; strategy: string; signal: string; params: Record<string, unknown>; timestamp: number; } | { type: "error"; code: string; message: string; context: Record<string, unknown>; timestamp: number; }; Every event must include strategy, exchange, sessionId to filter logs by specific run. Below are mandatory fields per type:
| Event Type | Mandatory Fields |
|---|---|
| order_placed | orderId, symbol, side, quantity, price, orderType |
| order_filled | orderId, executedQty, executedPrice, fee, feeCurrency |
| order_cancelled | orderId, reason |
| position_opened | positionId, entryPrice, size, leverage |
| position_closed | positionId, exitPrice, realizedPnl |
| signal_generated | strategy, signal, params |
| error | code, message, context |
Step-by-step pino configuration
- Replace
console.logwith pino using JSON format. - Add base fields (
strategy,exchange,sessionId) to every call. - Configure transport: pino-pretty for dev, JSON for production.
- Connect async writer to stdout or file.
- Integrate log shipping to PostgreSQL via logstash or direct writer.
Minimal configuration example:
import pino from "pino"; const logger = pino({ level: process.env.LOG_LEVEL ?? "info", base: { strategy: process.env.STRATEGY_NAME, exchange: process.env.EXCHANGE, sessionId: process.env.SESSION_ID ?? Date.now().toString(36), }, transport: process.env.NODE_ENV === "development" ? { target: "pino-pretty" } : undefined, }); logger.info({ type: "order_filled", orderId, executedQty, executedPrice, fee }, "Order filled"); Storage: PostgreSQL + TimescaleDB
TimescaleDB automatically partitions data by time (hypertables), giving millisecond query speed on millions of rows. Plain PostgreSQL without the extension slows down on logs exceeding 100 events/second.
CREATE TABLE trade_events ( id BIGSERIAL, session_id TEXT NOT NULL, strategy TEXT NOT NULL, exchange TEXT NOT NULL, event_type TEXT NOT NULL, payload JSONB NOT NULL, ts TIMESTAMPTZ NOT NULL DEFAULT now(), PRIMARY KEY (id, ts) ); SELECT create_hypertable('trade_events', 'ts'); CREATE INDEX ON trade_events (session_id, ts DESC); CREATE INDEX ON trade_events USING GIN (payload); With TimescaleDB the query "all trades of strategy X in the past week" executes in milliseconds even on millions of rows.
How to set up P&L analysis from logs
SELECT strategy, date_trunc('day', ts) AS day, COUNT(*) FILTER (WHERE event_type = 'order_filled') AS trades, SUM((payload->>'executedQty')::numeric * (payload->>'executedPrice')::numeric) FILTER (WHERE payload->>'side' = 'sell') AS gross_revenue, SUM((payload->>'fee')::numeric) FILTER (WHERE event_type = 'order_filled') AS total_fees, SUM((payload->>'realizedPnl')::numeric) FILTER (WHERE event_type = 'position_closed') AS realized_pnl FROM trade_events WHERE ts > now() - interval '30 days' GROUP BY strategy, day ORDER BY day DESC; This query delivers daily P&L by strategy, accounting for fees—what typically goes missing in simple logs.
Alerts for anomalies
Logging is useless without reaction. Simple monitoring via periodic query:
async function checkAnomalies() { const recentErrors = await db.countEvents({ type: "error", since: minutesAgo(5) }); if (recentErrors > 10) await alertService.send("High error rate: " + recentErrors + " errors in 5m"); const lastFill = await db.lastEventTime({ type: "order_filled" }); if (minutesSince(lastFill) > 60 && isMarketHours()) { await alertService.send("No fills in 60 minutes — bot may be stuck"); } } Add a Telegram alert when errors exceed N or fills stop. A slippage of just 0.1% can cost $500 per day—timely notification saves the budget.
What's included in the work
- Replace
console.logwith pino/winston using JSON format - Add
sessionId,strategy,exchangeto all events - Create
trade_eventstable in PostgreSQL with indexes and TimescaleDB - Async writer (buffered batch inserts, does not block the trading loop)
- Basic daily P&L SQL query
- Telegram alert when errors > N per period
- Documentation of log schema and query examples
- Deploy to your server or cloud
Why choose us
- 5+ years of blockchain development experience (Ethereum, Solana, BNB Chain)
- 50+ projects for trading bots and DeFi
- Setup guarantee: if something doesn't work after a month, we fix it free of charge
- We use only proven tools: pino, TimescaleDB, Telegram Bot API
- Remote and in-office work (Minsk, BY)
Timeline and cost
Basic setup takes 1 to 3 days. Cost is calculated individually based on strategy complexity and log volume. One timely discovered error can save up to $2000, so the project pays for itself within 1–2 weeks.
For an accurate estimate, describe your bot and logging requirements. We will prepare a commercial proposal within 24 hours. Contact us via Telegram or email to discuss details. Don't put it off—order the setup now.







