Build a public market monitor that starts from REST state, follows realtime trades, confirms candle availability, and fails visibly when its stream becomes stale. This pattern is useful both for a terminal ticker and as the market-data health input to a trading system.
Create the client and load catalogs
client, err := polyester.New(polyester.Config{HydrateCatalogs: true})
if err != nil { log.Fatal(err) }
defer client.Close()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
if err := client.WaitForCatalogs(ctx); err != nil { log.Fatal(err) }Catalog hydration resolves symbols to engine IDs and attaches the correct quantity scale to public trades.
Bootstrap from recent trades and candles
symbol := "BTC-USDT"
recent, err := client.MarketData.GetTrades(ctx, &symbol, nil, 20, nil)
if err != nil { log.Fatal(err) }
for _, trade := range recent.Trades {
fmt.Println(trade.MatchID, trade.Side, trade.Price, trade.Qty, trade.TsNs)
}
candles, err := client.Candles.GetCandles(
ctx, &symbol, nil, "1m", 50, nil, nil, true,
)
if err != nil { log.Fatal(err) }
if len(candles.Candles) > 0 {
latest := candles.Candles[0]
fmt.Println(latest.TsSec, latest.Close, latest.Volume)
}REST bootstrap prevents a blank screen while waiting for the next trade. Keep the match ID and
timestamp of the newest row so consumers can deduplicate a reconnect overlap. Candle rows are
newest-first, and an included open candle is prepended. For indicators, request closed candles and
reverse or sort the rows by TsSec into chronological order first.
Follow the public trade tape
sub, err := client.MarketData.SubscribeTrades(ctx, &symbol, nil)
if err != nil { log.Fatal(err) }
defer sub.Close()
select {
case trade, ok := <-sub.Messages():
if !ok {
if err := sub.Err(); err != nil { log.Fatal(err) }
break
}
fmt.Println(trade.MatchID, trade.Side, trade.Price, trade.Qty, trade.TsNs)
case <-time.After(30 * time.Second):
log.Print("no trade in 30s; market may be quiet or stream may be stale")
case <-ctx.Done():
break
}SubscribeTrades completes its handshake before returning. A closed message channel must always be
followed by sub.Err(); queue overflow is terminal and requires a fresh subscription.
Add candle updates
candleSub, err := client.Candles.SubscribeCandles(ctx, &symbol, nil, "1m")
if err != nil { log.Fatal(err) }
defer candleSub.Close()
if candle, ok := <-candleSub.Messages(); ok {
fmt.Println(candle.TsSec, candle.Open, candle.High, candle.Low, candle.Close)
}
if err := candleSub.Err(); err != nil { log.Print(err) }models.Candle does not expose IsClosed. Request REST candles with includeIncomplete=false for indicators that must not repaint. For the stream, treat a TsSec bucket as forming until the next bucket begins.
Operate and shut down
Use signal.NotifyContext for SIGINT and SIGTERM. Track a last-event timestamp separately for
trades and candles. A quiet market can legitimately produce no events, so verify staleness with a
bounded REST read before declaring the websocket unhealthy. Cancel the context, close each
subscription, and finally close the client.
API map
| Method | Important inputs | Use |
|---|---|---|
MarketData.GetTrades | symbol/ID, limit, page token | Initial tape and reconnect overlap |
MarketData.SubscribeTrades | symbol/ID | Live public executions |
Candles.GetCandles | symbol/ID, timeframe, bounds, incomplete flag | Historical/bootstrap OHLCV |
Candles.GetCurrentCandle | symbol/ID, timeframe | Lightweight health check |
Candles.SubscribeCandles | symbol/ID, timeframe | Forming and closed candle updates |
Orderbook.CreateSubscription | symbol, depth, bucket, callbacks | Sequence-checked bid/ask state |
Tested source
Example 01 performs live REST market reads and example 04 runs the public trade stream in polyester-examples-go. The example suite
compiles against the exact v0.1.0a25 commit; the REST path and candle-backed RSI dry-run were
executed on devnet.