diff --git a/Cargo.lock b/Cargo.lock index fbf5362..44b5d82 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3569,7 +3569,7 @@ dependencies = [ [[package]] name = "laminar-connectors" -version = "0.15.1" +version = "0.16.0" dependencies = [ "arrow-array", "arrow-avro", @@ -3612,7 +3612,7 @@ dependencies = [ [[package]] name = "laminar-core" -version = "0.15.1" +version = "0.16.0" dependencies = [ "ahash", "anyhow", @@ -3659,8 +3659,9 @@ dependencies = [ [[package]] name = "laminar-db" -version = "0.15.1" +version = "0.16.0" dependencies = [ + "ahash", "anyhow", "arrow", "arrow-array", @@ -3689,7 +3690,7 @@ dependencies = [ [[package]] name = "laminar-sql" -version = "0.15.1" +version = "0.16.0" dependencies = [ "anyhow", "arrow", @@ -3714,7 +3715,7 @@ dependencies = [ [[package]] name = "laminar-storage" -version = "0.15.1" +version = "0.16.0" dependencies = [ "anyhow", "async-trait", @@ -3739,7 +3740,7 @@ dependencies = [ [[package]] name = "laminardb" -version = "0.15.1" +version = "0.16.0" dependencies = [ "anyhow", "arrow", @@ -6140,9 +6141,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9daff607c6d2bf6c16fd681ccb7eecc83e4e2cdc1ca067ffaadfca5de7f084" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" dependencies = [ "futures-util", "log", @@ -6281,9 +6282,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "tungstenite" -version = "0.26.2" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ "bytes", "data-encoding", diff --git a/Cargo.toml b/Cargo.toml index 66733a5..6de9d1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "laminardb" -version = "0.15.1" +version = "0.16.0" edition = "2024" rust-version = "1.85" license = "MIT" diff --git a/examples/binance_signals.py b/examples/binance_signals.py new file mode 100644 index 0000000..9e61f60 --- /dev/null +++ b/examples/binance_signals.py @@ -0,0 +1,59 @@ +"""Binance BTC trade signals in 30 lines of Python. + +WebSocket → Stream → Signal — zero infrastructure, pure SQL. + +Run: python examples/binance_signals.py +""" + +import laminardb + +db = laminardb.open(":memory:") + +# 1. Ingest live BTC trades straight from Binance WebSocket +db.execute(""" + CREATE SOURCE trades ( + s VARCHAR, p DOUBLE, q DOUBLE, "T" BIGINT, + WATERMARK FOR "T" AS "T" - INTERVAL '0' SECOND + ) FROM WEBSOCKET ( + url = 'wss://stream.binance.com:9443/ws/btcusdt@trade', + format = 'json' + ) +""") + +# 2. 10-second VWAP windows with BUY/SELL/HOLD signal +db.execute(""" + CREATE STREAM signals AS + SELECT + s AS symbol, + SUM(p * q) / SUM(q) AS vwap, + AVG(p) AS avg_price, + COUNT(*) AS trades, + CASE + WHEN AVG(p) > SUM(p * q) / SUM(q) * 1.001 THEN 'SELL' + WHEN AVG(p) < SUM(p * q) / SUM(q) * 0.999 THEN 'BUY' + ELSE 'HOLD' + END AS signal + FROM trades + GROUP BY s, TUMBLE("T", INTERVAL '10' SECOND) + EMIT ON WINDOW CLOSE +""") + +# 3. Start pipeline and print signals as they arrive +db.start() + +print("Listening for BTC signals...\n") +sub = db.subscribe_stream("signals") + +try: + while True: + batch = sub.next() + if batch: + for row in batch.fetchall(): + symbol, vwap, avg_price, num_trades, signal = row + print(f" {signal:4s} {symbol} vwap=${vwap:,.2f} avg=${avg_price:,.2f} ({num_trades} trades)") +except KeyboardInterrupt: + print("\nStopped.") +finally: + sub.cancel() + db.shutdown() + db.close()