Live forex order book charts
Order book data for 12 currency pairs, streamed into QuestDB and queried live.
Top of book and candlestick (OHLC) chart
Recent core prices matched to the order book with an ASOF JOIN to find the level each bid would fill at, and today's 15-minute candles of the best bid.
Top-of-book Price vs Core Price - EURUSD
WITHp AS (SELECT timestamp, bid_price, bid_volume, symbol, ecnFROM core_priceWHERE dateadd('m', -1, now()) < timestamp AND symbol = 'EURUSD'LIMIT -6),levels AS (SELECTmarket_data.timestamp AS md_timestamp,insertion_point(bids[2], bid_volume) AS level,bids[1][insertion_point(bids[2], bid_volume)] AS price,bids[2][insertion_point(bids[2], bid_volume)] AS volume,p.timestamp,p.bid_price,p.bid_volume AS bid_volume,p.ecnFROM pASOF JOIN market_data ON symbol)SELECTmd_timestamp AS MarketData_Time,CASE WHEN price IS NULL THEN NULL ELSE level END AS Level,Price,Volume,timestamp AS CorePrice_Time,bid_price AS Bid_Price,bid_volume1 AS Bid_Volume,ecn AS ECNFROM levels;
OHLC - Bids - 15m - (EURUSD)
SELECT timestamp, open, high, low, close, total_volumeFROM market_data_ohlc_15mWHERE date_trunc('day', now()) < timestampAND symbol = 'EURUSD';
Market depth and bid-ask volume
Cumulative volume across the top seven levels each side, and 30-second bid and ask volume over the past hour against the best bid and offer.
Market Depth - EURUSD
WITHsnapshot AS (SELECT timestamp, bids, asksFROM market_dataWHERE dateadd('m', -10, now()) < timestamp AND symbol = 'EURUSD'ORDER BY timestamp DESCLIMIT 1),bid_levels AS (SELECT timestamp, 'bid' AS side, bids[1,1] AS price, bids[2,1] AS volumeFROM snapshotUNION ALL SELECT timestamp, 'bid', bids[1,2], bids[2,2] FROM snapshotUNION ALL SELECT timestamp, 'bid', bids[1,3], bids[2,3] FROM snapshotUNION ALL SELECT timestamp, 'bid', bids[1,4], bids[2,4] FROM snapshotUNION ALL SELECT timestamp, 'bid', bids[1,5], bids[2,5] FROM snapshotUNION ALL SELECT timestamp, 'bid', bids[1,6], bids[2,6] FROM snapshotUNION ALL SELECT timestamp, 'bid', bids[1,7], bids[2,7] FROM snapshot),ask_levels AS (SELECT timestamp, 'ask' AS side, asks[1,1] AS price, asks[2,1] AS volumeFROM snapshotUNION ALL SELECT timestamp, 'ask', asks[1,2], asks[2,2] FROM snapshotUNION ALL SELECT timestamp, 'ask', asks[1,3], asks[2,3] FROM snapshotUNION ALL SELECT timestamp, 'ask', asks[1,4], asks[2,4] FROM snapshotUNION ALL SELECT timestamp, 'ask', asks[1,5], asks[2,5] FROM snapshotUNION ALL SELECT timestamp, 'ask', asks[1,6], asks[2,6] FROM snapshotUNION ALL SELECT timestamp, 'ask', asks[1,7], asks[2,7] FROM snapshot),bid_cum AS (SELECTtimestamp,side,price,volume,sum(volume) OVER (ORDER BY price DESC) AS cum_volumeFROM bid_levelsWHERE price IS NOT NULL),ask_cum AS (SELECTtimestamp,side,price,volume,sum(volume) OVER (ORDER BY price ASC) AS cum_volumeFROM ask_levelsWHERE price IS NOT NULL)SELECT * FROM bid_cumUNION ALLSELECT * FROM ask_cumORDER BY side, price;
Bid vs Ask Volume (30s) - BBO (1s) - (EURUSD)
SELECTtimestamp time,symbol,sum(bids[1,1] * bids[2,1]) AS bid,-1 * sum(asks[1,1] * asks[2,1]) AS askFROM market_dataWHERE dateadd('h', -1, now()) < timestampAND symbol IN 'EURUSD'SAMPLE BY 30s;
SELECT timestamp AS Time, Symbol, Bid, Ask, (bid + ask) / 2 AS MidFROM "bbo_1s"WHERE dateadd('h', -1, now()) < timestampAND symbol IN 'EURUSD';
Bid-ask spread, VWAP, RSI and Bollinger Bands
Spread and bid and ask volume for each of the last six seconds, and today's candles overlaid with VWAP, a 12-hour RSI and Bollinger Bands (20, 2).
Spread and Volume per second - EURUSD
SELECTtimestamp AS Time,sum(asks[1,1] - bids[1,1]) AS Spread,sum(bids[1,1] * bids[2,1]) AS Bid_Volume,sum(asks[1,1] * asks[2,1]) AS Ask_VolumeFROM market_dataWHERE dateadd('m', -1, now()) < timestampAND symbol = 'EURUSD'SAMPLE BY 1sORDER BY timestamp DESCLIMIT 6;
VWAP - Relative Strength Index 12h - Bollinger Bands (20,2) (EURUSD)
WITHsampled AS (SELECTtimestamp,symbol,total_volume,((open + close) / 2) * total_volume AS traded_valueFROM market_data_ohlc_1mWHERE date_trunc('day', now()) < timestampAND symbol IN 'EURUSD'),cumulative AS (SELECTtimestamp,symbol,sum(traded_value) OVER (ORDER BY timestamp) AS cumulative_value,sum(total_volume) OVER (ORDER BY timestamp) AS cumulative_volumeFROM sampled),vwap AS (SELECT timestamp, cumulative_value / cumulative_volume AS vwapFROM cumulative)SELECT * FROM vwap;
SELECT timestamp, open, high, low, close, total_volumeFROM market_data_ohlc_15mWHERE date_trunc('day', now()) < timestampAND symbol = 'EURUSD';
WITHprice_changes AS (SELECTtimestamp,symbol,bid,bid - prev_bid price_changeFROM (SELECTtimestamp,symbol,bid,lag(bid) OVER (PARTITION BY symbol ORDER BY timestamp) AS prev_bidFROM bbo_1hWHERE date_trunc('day', now()) < timestampAND symbol = 'EURUSD')),gains_losses AS (SELECTtimestamp,symbol,bid,CASE WHEN price_change > 0 THEN price_change ELSE 0 END AS gain,CASE WHEN price_change < 0 THEN abs(price_change) ELSE 0 END AS lossFROM price_changes),avg_gains_losses AS (SELECTtimestamp,symbol,bid,avg(gain) OVER (PARTITION BY symbol ORDER BY timestampROWS BETWEEN 11 PRECEDING AND CURRENT ROW) AS avg_gain,avg(loss) OVER (PARTITION BY symbol ORDER BY timestampROWS BETWEEN 11 PRECEDING AND CURRENT ROW) AS avg_lossFROM gains_losses)SELECTtimestamp,Bid,CASEWHEN avg_loss = 0 THEN 100ELSE 100 - (100 / (1 + (avg_gain / nullif(avg_loss, 0))))END AS RSI_12hFROM avg_gains_lossesORDER BY timestamp;
WITHstats AS (SELECTtimestamp,close,avg(close) OVER (ORDER BY timestampROWS BETWEEN 19 PRECEDING AND CURRENT ROW) AS sma20,avg(close * close) OVER (ORDER BY timestampROWS BETWEEN 19 PRECEDING AND CURRENT ROW) AS avg_close_sqFROM market_data_ohlc_15m -- 20 periods of 15 minutes = SMA 6hWHERE date_trunc('day', now()) < timestampAND symbol IN 'EURUSD'),bollinger AS (SELECTtimestamp,sma20,-- sqrt(avg_close_sq - (sma20 * sma20)) as stdev20,sma20 + 2 * sqrt(avg_close_sq - (sma20 * sma20)) AS upper_band,sma20 - 2 * sqrt(avg_close_sq - (sma20 * sma20)) AS lower_bandFROM statsORDER BY timestamp)SELECT * FROM bollinger;
Build your own on QuestDB
Every panel on this page is a SQL query against QuestDB, rendered in Grafana. Run the queries yourself in the live demo, or use our Grafana tutorials to chart your own data.