Releases: simonro/edge-scanner
Release list
Edge Scanner v1.3.1
Fixes a startup crash in v1.2.1 and v1.3.0: the scanner stopped at once with SyntaxError: unterminated f-string literal in scripts/run_live.py.
Fix
The new port-in-use message was written with line breaks inside a Python string, so run_live.py could not start. The test suite checked the scanner's modules but never compiled the startup script, so the error was not caught. It now compiles every script and module on each run.
If you are on v1.2.1 or v1.3.0, update now.
Update
git pull, then npm --prefix dashboard-v2 run build, then restart the scanner. Or download the zip below.
Edge Scanner v1.3.0
The dashboard now tells you whether market data is still flowing, lets you freeze a scanner window while you read it, and its top bar fits a 1280px screen.
Not financial advice. The sample setups are starting points, not a strategy.
Market data age in the top bar
The green dot in the top bar only ever meant the alert connection to the scanner was up. Market data could stop while it stayed green.
- A new chip shows the data provider, the newest bar and how long ago it arrived, for example
Alpaca SIP · bar 10:42 · 8s. - In regular hours it turns amber after 90 seconds without a bar and red after 3 minutes.
- The old dot is now labelled ALERTS, which is what it measures.
- The browser tab names the provider, so an Alpaca scanner and a Schwab scanner open side by side are easy to tell apart.
Hold on scanner windows
A Hold button on each Scanner window freezes its rows while you read them.
- New alerts are still collected and still play their sound; the button counts them (
Resume +3). - Resume shows everything that arrived. Changing the window's filters still applies to the held rows.
- Hold is per window and is not saved with the layout.
Top bar fits 1280px
Save layout as, theme, hide the bar and fullscreen moved into a ⋯ menu. The save status appears only while saving or if a save fails, the enable-sound button is an icon, and the name hides below 1400px. On a narrower window the bar wraps to a second row instead of cutting controls off.
Update
git pull, then npm --prefix dashboard-v2 run build, then restart the scanner. Or download the zip below.
Edge Scanner v1.2.1
Range break fires on every real breakout again, saved range-break thresholds are kept exactly, quote-built volume stops double counting, and fresh installs get tested library versions.
Not financial advice. The sample setups are starting points, not a strategy.
Range break: fires again after price comes back inside
After a breakout, a quiet bar back inside the range should re-arm the trigger for the next breakout. It did not: the volume check ran first, so the quiet bar was ignored and the trigger stayed spent. In practice a range break setup gave at most one alert per direction per day.
- A return inside the range now re-arms it, however quiet the bar. Each real breakout alerts once.
- A quiet drift outside the range followed by a high-volume bar while still outside still fires, as before.
- Expect more range break alerts than on v1.2.0 and earlier.
Saved range break thresholds are no longer clipped
v1.1.6 converted saved setups from the old candle multiple to the new per-minute multiple and promised the same alerts. That held for most setups, but the result was capped at 30x, and a re-save capped it again. A setup saved as 10x on 60-minute candles needs 600x a minute and loaded as 30x, so it fired on far less volume than intended. Anything on 15-minute or longer candles, or above 6x on 5-minute candles, was affected.
- The limit is now 600x and the conversion is exact. A setup already re-saved at the capped value keeps that value; set it back by hand if needed (old multiple times the candle minutes).
Schwab quote-built bars
- Volume counted once. When Schwab's running day volume dipped slightly (a correction or a stale snapshot) and came back, the climb back was counted again, repeatedly if it flapped. Volume now counts only above the highest total seen.
- Small universes keep polling. With fewer than 300 symbols, if Schwab reported a lower real-bar limit, the overflow was moved to polling but nothing polled it. The poller now always runs when quote-built bars are on.
Startup: a taken port is reported at once
If an old scanner is still running, the new one used to warm up for several minutes and then stop with a traceback. It now checks the port first and says which program holds it and how to stop it.
Dependencies
A fresh install had no upper limits and pulled pandas 3, Starlette 1.7 and websockets 17 untested. The full test suite now passes on both the oldest and the newest supported versions, and each library is capped below its next major release. Minimums rose where that picks up a security fix: pyarrow 14.0.1, FastAPI 0.120.1, yfinance 1.2.2.
Also
- Setup validation errors say what is wrong ("unknown trigger ...") instead of always "a numeric field has an invalid value".
Update
git pull, then pip install -r requirements.txt, then npm --prefix dashboard-v2 run build, then restart the scanner. Or download the zip below.
Edge Scanner v1.2.0
Safer by default against web pages in your browser, a confirmed VWAP cross, and a Liquid movers universe that stops shutting out high-priced stocks.
Not financial advice. The sample setups are starting points, not a strategy.
Security: the scanner only answers this machine
The scanner already listened on localhost only. That still left a gap: a web page open in your browser could send the scanner a request. It could not read the answer, but a plain POST could reset your settings. A page using DNS rebinding (its own hostname re-pointed at your machine) could read and change everything.
- Every request must now be addressed to
localhost,127.0.0.1or[::1]. Other names are refused before anything runs. - Changes must come from a page this machine served, and POST requests must be JSON. Scripts that send no Origin header keep working; a script that POSTs needs
Content-Type: application/json. - The same checks cover the alert WebSocket.
- The dashboard's file route no longer accepts paths that Windows would treat as a network share.
- The update check accepts only a well-formed version tag, always links to this repo's releases page, and never prints control characters. It sends your IP and the scanner version, nothing else.
- Opening the dashboard from another machine with
--host 0.0.0.0: list the names it will use inSCANNER_ALLOWED_HOSTSin.env(see.env.example). There is still no login, so only do this on a network you trust.
VWAP cross, confirmed
New trigger VWAP cross (confirmed): a completed candle closes across VWAP and the next candle closes on the same side, in the same session, with no missing candle between. Wicks and candle color do not matter; the closes do. The VWAP hold condition also gains a side option, Opposite the trade direction, so one setup can cover both directions.
Liquid movers: dollar volume only
The Liquid movers universe filter (and the build_universe.py defaults) dropped the 5M shares-a-day floor. Above the $15 price floor it did nothing except shut out high-priced stocks: a $500 stock needed $2.5B a day to clear it. The dollar-volume floor rose from $50M to $150M a day to keep the alert count reasonable.
- Setups and rankings using Liquid movers now see roughly 700 symbols instead of about 240, including large caps such as LLY, ASML, CAT, GS and COST. Expect more alerts.
- An existing install keeps its current filter: the copy under
data/is yours and is not overwritten. To adopt the new default, edit the Liquid movers filter in Config: remove the average share volume condition and set average dollar volume to 150M. New installs get it automatically.
Chart history cache keyed by bar size
Intraday chart history is now cached separately per bar size, so a 1-minute and a 5-minute download of the same symbol can no longer overwrite each other.
Update
git pull, then npm --prefix dashboard-v2 run build, then restart the scanner. Or download the zip below.
Edge Scanner v1.1.6
The Range break volume setting now means what it says, and the browser tab shows which version you are running.
Not financial advice. The sample setups are starting points, not a strategy.
Range break volume, minute against minute
Range break checks the breakout on a 1-minute bar but compared its volume with a whole range candle. On a 5-minute range, a setting of 1.2x really asked for 6x a normal minute's volume. Thanks again to @neusse for spotting it in #18.
- The setting is now Breakout volume, x avg minute: the breaking minute against the range's average minute. The limit is 30x.
- Your saved setups keep exactly the alerts they had. A setup saved with the old setting is converted when it loads: the old multiple times the candle size, so 1.2x on 5-minute candles becomes 6.0x. Replaying a full day of 200 symbols, the sample Compression Breakout setup fired the same 313 alerts at the same times before and after.
- To see more breakouts, lower the number. It is now the real multiple of normal volume.
Version in the browser tab
The dashboard tab reads "Edge Scanner v1.1.6", and so does the startup banner in the terminal. /api/v2/version also reports it.
Update
git pull
npm --prefix dashboard-v2 run build
Then restart the scanner. Your .env and everything in data/ are untouched.
Edge Scanner v1.1.5
Fixes to how custom setup triggers remember what they already alerted on. They come from the alert-state audit @neusse filed in #18. Thank you for a careful, reproducible report.
Not financial advice. The sample setups are starting points, not a strategy.
Fixes
- One trigger with different settings in two setups no longer interferes with itself. Each trigger configuration now keeps its own memory of what it already fired on. Before, two setups using the same trigger with different settings shared that memory, so the first one to fire could silence the other: RVOL crossing 1.5x and 3x, for example, or Break over recent high with lookbacks of 3 and 5. Replaying one day of 200 symbols, the affected Break over recent high/low setups fired about twice as often, which is what their settings always asked for.
- Starting after the open no longer alerts on the morning's events. Today's bars were replayed into the charts at startup but never shown to the triggers, so the first live bar treated a 9:30 gap, an RVOL already above threshold, or a streak already running as new. Startup now records where every trigger stands before live bars arrive. On a simulated start at 11:00, the first two minutes sent 480 alerts instead of 700.
- Gap at the open requires the scanner to have seen the open. A symbol caught up from quotes mid-session could report its current price as the gap.
- Near high/low of day and Near last high/low need the bar to stay inside the level. A bar that wicked through and closed just under it broke the level; it did not approach it.
- The Short % of float condition can pass. The fundamentals cache stores short interest as a fraction and the setting is a percent, so "at least 10" never matched.
Not changed on purpose
- Crosses and streaks keep tracking the market through premarket, including for setups that alert only in the regular session. Otherwise a cross made at 7:00 reads as a new cross at 9:30.
- Range break still compares the breaking bar's volume with the average range candle. Comparing per minute is arguably more correct, but it makes existing setups fire several times as often at their current settings, so it waits for a setting that accounts for it.
Update
git pull
Then restart the scanner. There are no dashboard or dependency changes. Your .env and everything in data/ are untouched.
Edge Scanner v1.1.4
From this release on, the scanner tells you when a newer release exists. Everyone on an earlier version has to find out the old way, so this is the one to install.
Not financial advice. The sample setups are starting points, not a strategy.
Update notification
At startup the scanner asks GitHub's public releases page once, on a background thread, whether a newer release exists. If one does, it prints a line in the terminal and the dashboard shows a small "vX.Y.Z available" link next to the feed status, pointing at the release notes.
- One anonymous request. GitHub sees your IP and nothing else: no key, no symbols, no settings.
- Nothing is downloaded or installed. The update is still
git pulland a dashboard rebuild. - Off with
UPDATE_CHECK=0in.env. Offline or rate-limited, it stays quiet. - Disclosed in the README under what leaves your machine.
The code now carries its version number (scanner.__version__), and /api/v2/version reports it.
Update
git pull
pip install -r requirements.txt
npm --prefix dashboard-v2 install
npm --prefix dashboard-v2 run build
Your .env and everything in data/ are untouched.
Edge Scanner v1.1.3
Schwab's bars built from quotes now carry the right volume, and a whole-market Schwab start no longer takes two hours every morning.
Not financial advice. The sample setups are starting points, not a strategy.
Volume in bars built from quotes
Two defects, both measured against a consolidated feed on the second live session:
- The day's volume was being re-added. Schwab's cumulative day volume is sometimes revised down by a small amount (a cancelled or corrected trade). Any decrease was taken as "the counter was reset", which added the whole day's volume again. By mid afternoon some symbols carried 2.5 times the volume Schwab itself reported. Only a collapse to a fraction of the previous total is a reset now; a small decrease adds nothing.
- Even an exact count read high. Schwab's own minute bars carry about 77% of the volume its quotes report (measured 0.64 to 0.82 across symbols, even through the day). Relative volume divides today's volume by a baseline built from those minute bars, so a bar built from quotes read 20 to 40% high even when it tracked the quotes exactly. Built volume is now scaled to the same footing.
Result, median difference from a consolidated feed on symbols 301 to 3,300 (bars built from the live quote stream): price 0.00%, VWAP 0.07%, high and low of day 0.00%, relative volume 2%. Alerts on those symbols agreed 62% of the time with the same setups on the paid feed, against 70% on the 300 symbols where Schwab sends real bars. Matched alerts fired in the same minute at the same price. Symbols past 3,300 (polled quotes) produce too few alerts to measure yet.
Faster Schwab starts
- The history cache is now reused when it was written after the last session's close. The old rule re-downloaded every file every morning (a file written Monday morning holds Friday's bars, and Tuesday asked for Monday): two hours on a whole-market universe.
- New
scripts/refresh_schwab_cache.py: downloads the day's history after the close. Run it as a scheduled task at 17:30 ET on weekdays and the morning start takes minutes. REST only, safe while a scanner is live.
Update
git pull
pip install -r requirements.txt
npm --prefix dashboard-v2 install
npm --prefix dashboard-v2 run build
Your .env and everything in data/ are untouched.
Edge Scanner v1.1.2
One fix that matters to everyone, on any data provider: restarting the scanner after the open no longer floods you with false "new high of day" alerts.
Not financial advice. The sample setups are starting points, not a strategy.
Fix: a start after the open left the candle rings empty
At startup the scanner replays today's bars so a late start has the right VWAP, volume and levels. It replayed them into each symbol's state but not into its candle rings, which hold the day's high and low, the opening-range candle and today's candles for every composed trigger. So after a restart during the session:
- every new local high counted as a new high of day (and the same for lows) for the rest of the session;
- the opening range did not exist, so opening-range alerts could not fire.
Measured side by side: a scanner restarted at 09:46 fired 683 New HOD / New LOD alerts in half an hour against 241 on one that had run since before the open, while both reported the same high of day. With the fix, the two agreed on 27 of 27 of those alerts.
Startup now replays today's bars into the candle rings too, in the same order as live.
Schwab: a start after the open
- Symbols caught up from quotes give the candle rings the day's high and low only. The catch-up bar holds the whole session's volume, and as a candle it read as a 10x to 25x volume spike when its first 5-minute candle closed.
scripts/compare_live_feeds.py --since HH:MMcompares only alerts from after a restart.
Agreement with Alpaca, measured on the first full morning
Same universe (6,450 symbols), same setups. Median difference, Schwab against Alpaca:
| Symbols (most liquid first) | Price | VWAP | High / low of day | Relative volume |
|---|---|---|---|---|
| 1 to 300, real bars | 0.00% | 0.02% | 0.00% | 0.5% |
| 301 to 3,300, built from streamed quotes | 0.00% | 0.13% | 0.00% | reads high after a mid-session start |
| 3,301 and up, built from polled quotes | 0.00% | 0.28% | 0.00% | reads high after a mid-session start |
Alerts on the real-bar tier agreed 81% of the time (same symbol, setup and direction within three minutes), with matched alerts at the same minute and the same price. New HOD, New LOD and 60-day high/low agreed fully. Alert agreement on the two quote-built tiers has not been measured on a clean start before the open yet; on a mid-session start their relative volume includes premarket volume, so volume-gated setups differ there.
Update
git pull
pip install -r requirements.txt
npm --prefix dashboard-v2 install
npm --prefix dashboard-v2 run build
Your .env and everything in data/ are untouched.
Edge Scanner v1.1.1
Fixes from the first live session running Charles Schwab and Alpaca side by side on a 6,450-symbol universe. If you use DATA_PROVIDER=schwab, update: v1.1.0 can leave the dashboard unresponsive on a large universe.
Not financial advice. The sample setups are starting points, not a strategy.
Schwab fixes
- The dashboard stalled on Schwab. The premarket lists re-downloaded today's bars on every refresh. Alpaca serves that in one batched call; Schwab serves one symbol per request at about 120 a minute, so the requests backed up and charts took 27 seconds, the premarket screen stayed blank, and rankings and Setup Check appeared dead. On Schwab the lists now come from the premarket price and volume the scanner already holds, with no requests. Charts load in under a second again.
- Odd lots no longer set prices in bars built from quotes. A quote's last price is the latest trade of any size, and thin premarket trading is mostly odd lots at off-market prices. Built bars had premarket highs and lows about 0.7% wider than real bars, up to 8%. A price now counts only when the last trade was a round lot for that price (100, 40, 10 or 1 shares by price tier); odd lots still add their volume. This is the rule real bars follow.
- Starts were slow every day. Recently listed symbols can never reach back to the start of the history window, so they were downloaded again on every start: about 1,800 extra requests, fifteen minutes. The provider now remembers what it has already asked for. A warm start on the full universe takes about 8 minutes instead of 25.
- A start after the open left most symbols at zero volume, so their relative volume read 60 to 80 percent low all day. Symbols past the 600 that get today's bars back-filled are now caught up from quotes in a few seconds: the day's open, high, low, last and volume so far. Their VWAP is approximate, and their volume includes premarket, until the next start before the open.
Measured agreement with Alpaca so far
Same universe, same setups, first half hour of the session. Median difference, Schwab against Alpaca:
| Symbols (most liquid first) | VWAP | High / low of day |
|---|---|---|
| 1 to 300, real bars | 0.09% | 0.00% |
| 301 to 3,300, built from streamed quotes | 0.18% | 0.06% |
| 3,301 and up, built from polled quotes | 0.31% | 0.4% |
Alert-by-alert agreement is still being measured and will be reported in the next release.
New
start_scanner_schwab.bat: a second scanner on Schwab data beside the first one (port 7787, its own alert archive).scripts/compare_live_feeds.py: compares two running scanners, symbol state and alerts, per data tier.
Update
git pull
pip install -r requirements.txt
npm --prefix dashboard-v2 install
npm --prefix dashboard-v2 run build
Your .env and everything in data/ are untouched.