Exchange-agnostic real-time price crash and pump detector over WebSocket
Project description
PriceFlare
Exchange-agnostic real-time price crash and pump detector over WebSocket.
Any trading bot needs to react immediately to sudden price movements. PriceFlare handles the detection — you supply the WebSocket URL, a price parser, and your callbacks.
WebSocket stream → price parser → rolling window → CRASH / PUMP → your callback
Install
pip install priceflare
Quickstart
from priceflare import Sentinel
from priceflare import parsers
def on_alert(alert):
print(f"[{alert['type']}] {alert['change_pct']:+.2f}% in {alert['window_sec']//60}min")
print(f" {alert['ref_price']} → {alert['cur_price']}")
def on_crash(price):
print(f"Crash at {price} — checking stop-loss...")
def on_pump(price):
print(f"Pump at {price} — checking opportunity...")
sentinel = Sentinel(
ws_url = "wss://stream.binance.com:9443/ws/btcusdt@trade",
price_parser = parsers.binance,
crash_threshold = 3.0, # % drop to trigger CRASH
pump_threshold = 3.0, # % rise to trigger PUMP
window_seconds = 300, # rolling window (5 min)
cooldown_seconds = 600, # min delay between alerts (10 min)
on_alert = on_alert,
on_crash = on_crash, # optional — CRASH only
on_pump = on_pump, # optional — PUMP only
)
thread = sentinel.start()
Built-in parsers
| Parser | Exchange | WebSocket URL |
|---|---|---|
parsers.binance |
Binance Spot/Futures | wss://stream.binance.com:9443/ws/<symbol>@trade |
parsers.kraken |
Kraken | wss://ws.kraken.com/v2 |
parsers.coinbase |
Coinbase Advanced Trade | wss://advanced-trade-ws.coinbase.com |
Custom parser
A parser is any callable (raw: str) -> float | None.
Return None to silently skip non-price messages.
import json
def my_parser(raw: str) -> float | None:
data = json.loads(raw)
if data.get("type") != "trade":
return None
return float(data["price"])
sentinel = Sentinel(
ws_url = "wss://my-exchange.com/ws",
price_parser = my_parser,
on_alert = on_alert,
)
Exchanges requiring a subscription message (Kraken, Coinbase)
Some exchanges expect a JSON subscription message after the WebSocket connection is established.
Use the subscribe_message parameter:
import json
sentinel = Sentinel(
ws_url = "wss://ws.kraken.com/v2",
price_parser = parsers.kraken,
subscribe_message = json.dumps({
"method": "subscribe",
"params": {"channel": "ticker", "symbol": ["BTC/USD"]},
}),
on_alert = on_alert,
)
# Coinbase Advanced Trade
sentinel = Sentinel(
ws_url = "wss://advanced-trade-ws.coinbase.com",
price_parser = parsers.coinbase,
subscribe_message = json.dumps({
"type": "subscribe",
"product_ids": ["BTC-USD"],
"channel": "ticker",
}),
on_alert = on_alert,
)
Alert format
{
"type": "CRASH", # or "PUMP"
"change_pct": -4.21, # signed percentage
"ref_price": 67000.0, # price at start of window
"cur_price": 64178.0, # current price
"window_sec": 300,
"timestamp": "2026-03-31T08:00:00+00:00",
}
Backtesting / offline use
Feed prices directly without a WebSocket connection using feed().
Leave ws_url empty — start() is never called.
sentinel = Sentinel(
ws_url = "", # unused in offline mode
price_parser = parsers.binance,
crash_threshold = 3.0,
cooldown_seconds = 0,
on_alert = on_alert,
)
for price, ts in zip(historical_prices, historical_timestamps):
alert = sentinel.feed(price, timestamp=ts) # timestamp = Unix float
if alert:
print(alert)
API reference
Sentinel(ws_url, price_parser, **kwargs)
| Parameter | Type | Default | Description |
|---|---|---|---|
ws_url |
str | required | WebSocket URL. Empty string allowed for offline/feed-only use. |
price_parser |
callable | required | (str) -> float | None |
crash_threshold |
float | 3.0 |
% drop to trigger CRASH |
pump_threshold |
float | 3.0 |
% rise to trigger PUMP |
window_seconds |
int | 300 |
Rolling window in seconds |
cooldown_seconds |
int | 600 |
Min seconds between alerts |
max_reconnects |
int | 50 |
Max reconnection attempts (must be > 0) |
subscribe_message |
str | None | None |
JSON string sent after connection (Kraken, Coinbase) |
on_alert |
callable | None |
Called on CRASH or PUMP with alert dict |
on_crash |
callable | None |
Called on CRASH with current price |
on_pump |
callable | None |
Called on PUMP with current price |
Methods
| Method | Description |
|---|---|
start() |
Start in a background daemon thread. Returns the thread. Raises SentinelError if ws_url is empty. |
stop() |
Signal the sentinel to stop. |
feed(price, timestamp=None) |
Inject a price directly. timestamp: Unix float for historical data. Returns alert dict or None. |
status() |
Returns current state dict. |
Tests
pip install -e ".[dev]"
pytest # 61 tests
pytest --cov=priceflare # with coverage
License
MIT — free to use, modify, and redistribute.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file priceflare-0.2.0.tar.gz.
File metadata
- Download URL: priceflare-0.2.0.tar.gz
- Upload date:
- Size: 12.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
79302f526f995de9f37873a1a78ddf26ac3f497574ef3a26f18ff32367b3e57d
|
|
| MD5 |
bb05a44efb35a6f140a8adeafd94ae59
|
|
| BLAKE2b-256 |
3b5cd3b817905220cdd7b4a358076179d118f0d713ab03ef4c240f662ac5fa73
|
File details
Details for the file priceflare-0.2.0-py3-none-any.whl.
File metadata
- Download URL: priceflare-0.2.0-py3-none-any.whl
- Upload date:
- Size: 9.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
558c132d6c2d4d71b686cf9f0ebb80079a8435d3fff88a32e2c4651c38b4d6c5
|
|
| MD5 |
694d58f6b9f91cc1f36cfcdb41e91c7b
|
|
| BLAKE2b-256 |
47e16ebe75e2173337e946051412383b7bc5afc34e9ec461ba6b1d0750f9b802
|