launch_anchor
launch_anchor is a master switch wired to the world's rocket launch schedule. Every three hours we pull the list of upcoming launches — who's flying, what rocket, when, and whether the time is confirmed — and you pick a condition off it: "only when a SpaceX launch is within 7 days", "only when any launch is confirmed-Go within 3 days". While your condition holds, the bot trades. While it doesn't, the bot places no trades at all that cycle, in any market. It gates the whole bot, not just space markets.
- Bots using it
- 1 (out-of-sample; none live)
- Data source
- Launch Library 2 · thespacedevs
- Refresh
- Every 3 hours (0 */3 * * *)
- Rows stored
- 63 upcoming launches forward-looking only
- Backtest-replayable
- No — see catch #10
- Anchor module
- quants/launch_anchor.py
That is the tidy version, and it is the version the builder sells you. Here is what we found when we followed the wires, and it is the least flattering thing in this dictionary so far: the one bot built on this signal has placed 93 trades, and 85 of them have nothing to do with rockets. They are crypto token launches — "Will Base launch a token by December 31, 2027?", "GRVT FDV above $500M one day after launch?" — reached because the bot picks markets by looking for the word launch in the title. Meanwhile the rocket gate that is supposed to be the whole idea has been open on every single one of the 62 days the bot has been running.
A switch that is always on is not a switch. This page is mostly about what that means, and what the signal would need in order to be worth using.
Where the data comes from
One place: Launch Library 2, the open launch database run by The Space Devs. It is free, needs no key and no account, and the free tier allows roughly fifteen requests an hour — which is why we poll every three hours rather than every five minutes. The exact call is https://ll.thespacedevs.com/2.2.0/launch/upcoming/?limit=50, made by predictive/space_launch_poller.py on cron 0 */3 * * *, landing in the space_launches table in predictive.db.
For each of the next fifty launches the feed gives a provider ("SpaceX", "Arianespace", "China Aerospace Science and Technology Corporation"), a rocket ("Falcon 9", "Electron", "Starship"), a scheduled T-0 called net — "no earlier than" — and a status. We keep those four. The status is stored as the feed's short code, and the ones that actually appear in our table are Go, TBC, TBD and Success. In the feed's own words, Go means "current T-0 confirmed by official or reliable sources". Hold on to that sentence; catch #9 is about what it does and does not promise.
There is a fifth field, net_precision, and we throw it away. That is catch #5.
Two things the registry gets wrong about this signal
Our own quants/signal_registry.py is the tool that resolves every signal's provenance from the code rather than from anyone's memory, and it reports this one as source: "undocumented" with a poller called db and no cron schedule. Both are artefacts, and both are worth explaining because they will show up again:
- The registry only reads the anchor module's docstring. launch_anchor.py names the table and the poller but never names the vendor — so the source comes back "undocumented". The vendor is documented, in the poller's docstring one directory over, which the registry never opens. It is a real gap in the code, just not the one it looks like.
- The poller is resolved by a regular expression looking for predictive.<name> in the docstring. That docstring says the anchor "reads predictive.db.space_launches", so the regex proudly returns db, and then finds no cron job called "db". The real poller and its real three-hourly cron are both sitting in the crontab under their proper name. This is the third signal in a row to trip that same regex, after energy_anchor and tropical_anchor.
And the familiar one: the registry's "live bots" column counts bots whose status is oos or live. Its "1" here is one out-of-sample bot and no live ones — the sixth signal running where that number has not meant what it says.
What it means for your bot
Practically: launch_anchor turns your entire bot on and off on a calendar. It is not a filter that finds space markets for you, and it does not check that the market you are about to buy has anything to do with the launch that opened the gate. It answers one question — "is a launch matching my description scheduled in the next N days?" — and if the answer is yes, every other rule in your bot proceeds exactly as it would have anyway.
That distinction is the whole story of this page, so it is worth being blunt about it. The gate's window is measured from now. The market's outcome is measured from its own resolution date. Nothing connects the two. A bot can be switched on because a Falcon 9 is flying on Thursday, and spend that permission buying a contract that resolves in December 2027. Our one bot does this ninety-one times out of ninety-three.
You configure it three ways, and you must supply at least one of the first three or the gate refuses to open at all:
- provider — matched as a substring, case-insensitively. "SpaceX" works; so does "Space", which also matches the European Space Agency and Roscosmos.
- rocket — same substring rule. "Starship", "Falcon", "Electron".
- any: true — any launch, anywhere, by anyone.
- within_days — how far ahead to look. Defaults to 7.
- status — optional, and matched exactly against the feed's short code.
The catch
Ten of them, worst first. The first three are why we would not build on this signal as it stands.
#1 🔴 The gate is about rockets. The bot is not.
The one bot using this signal, Launch Go Rider, finds its markets with title_contains: ["launch"], which the engine turns into UPPER(title) LIKE '%LAUNCH%'. That is a plain substring search on the market title, and on Polymarket the word "launch" overwhelmingly belongs to crypto.
Of its 93 fills: 27 are "Will X launch a token by <date>?", 55 are "X FDV above $N one day after launch?", 3 are about OpenAI shipping consumer hardware, and 8 are about an actual rocket. Eighty-five of ninety-three — 91% — are markets where a rocket schedule is not merely a weak signal but a completely unrelated one.
This is not the anchor's bug; the anchor did what it was asked. It is what happens when a whole-bot switch is paired with a market filter that was never checked against it. But it means that whatever this bot has been measuring for two months, it has not been measuring launch_anchor.
#2 🔴 The switch has never been observed to turn off.
The bot asks for {any: true, within_days: 3, status: "Go"} — is any launch, anywhere on Earth, confirmed-Go in the next three days? The honest answer is: essentially always.
We measured it two independent ways. From our own engine records: over 17,426 runs since 11 June, the bot produced trade candidates on every one of its 62 days, and in 96.0% of individual runs. From Launch Library's historical endpoint, fetched fresh: there were 65 orbital launch attempts in 71 days — about 6.4 a week — falling on 45 distinct days, with a longest quiet stretch of 4 days. A real launch occurred within three days on 61 of the bot's 62 live days.
Be careful with that 98.4%: it measures "a launch actually happened within three days", which is not the same as "a launch showing status Go was listed within three days". It is a proxy, and it can drift in both directions — a mission can sit at Go and then slip, and a TBD mission can fly. But it points the same way our engine records do, and the two together are as close to a duty-cycle measurement as this signal permits.
The consequence: a gate that is open ~100% of the time is indistinguishable from no gate at all. We cannot attribute any part of this bot's results to launch_anchor, because we have never seen it decline anything. That would take an ablation — the same bot run with the gate forced off — and we have not run one.
#3 🔴 Its own kill rule has fired, and nothing was listening.
Every house bot ships with a pre-registered falsifier — the condition, written before it traded, under which we agreed to retire it. This one's reads: "kill if Go-gated entries settle YES <80% over 20 trades, or avg fill >0.87." It has settled 27 trades at 66.7% YES. The first clause is met, comfortably.
We have a tool for exactly this, quants/falsifier_watch.py, which reads each bot's falsifier and evaluates the machine-checkable part. Run today it flags Launch Go Rider as the only tripped bot of the nine that carry a falsifier. It is also not in the crontab. Nobody has run it, so nobody knew.
This one is a process failure rather than a code defect, and it is a platform-wide finding rather than something peculiar to this signal — but it belongs on this page, because it is the reason a bot whose gate is a no-op has been quietly filling for two months. See the record section below for the complication: by expectancy, this bot is not actually losing.
#4 🔴 The feed is dead, and a dead feed props the gate open.
The poller's last eleven consecutive runs have all died with sqlite3.OperationalError: database is locked — 246 such crashes in its lifetime, against 265 successful runs. The newest row in the table was written at 21:00 UTC on 9 August; this page was assembled at 08:30 UTC on 11 August, so the schedule the gate is reading is 35 hours old. This is the sixth job we have found wedged on the same shared predictive.db write contention, after resolution_buckets, sentiment_poller, eia_poller and tropical_poller.
On its own that would be ordinary bad luck. What makes it a design problem is that launch_anchor_satisfied() has no freshness check of any kind — no FRESH_SECS, no _STALE_DAYS, nothing. Every other anchor we have written up has one. And because launches are stored with future dates, a table that stops updating does not go quiet: it keeps asserting that launches are coming, for days. The docstring's promise, "fails closed if no matching launch", is true only of an empty table. A stale table fails open.
That is not hypothetical. Compared against the live feed this morning, five of our six nearest launches are wrong: we carry a Long March 7A as Go when it in fact failed; a Starlink mission dated 10 August that slipped to the 11th; an H3 that has already flown; a Zhuque-3 we still show as Go for 10 August which has slipped three weeks and reverted to month-only precision; and a mission from 8 August that should have been deleted two days ago but wasn't, because the delete is in the same transaction that keeps crashing. The single row still approximately correct — Starlink 17-49 on 12 August — is, by coincidence, the one row currently holding the bot's gate open.
#5 "Sometime in September" is stored as 30 September, 00:00:00
Launch Library is careful about this and we are not. Every launch carries a net_precision field — SEC, MIN, HOUR, DAY or MONTH — saying how much of that timestamp to believe. Our poller fetches it and never stores it.
The result is that a launch known only to the month arrives as a precise-looking timestamp on the last day of that month. 47 of our 63 rows sit at exactly midnight, and 44 pile onto month-end dates — twenty on 30 September, ten on 31 August, seven on 31 December. A within_days window that happens to reach one of those cliffs does not see one launch; it sees twenty, none of which are real appointments.
For this bot the damage is limited, because month-precision rows arrive as TBD and it filters for Go. Any config without a status filter — including the docstring's own example {"rocket":"Starship","within_days":14} — eats them whole.
#6 A launch that drops out of the top 50 is frozen forever
The poller asks for the next fifty launches and upserts them. It never re-reads anything outside that window, and it only deletes rows whose date is more than two days past. So a launch sitting further out than 50th place keeps whatever schedule and status it had the last time it was inside — indefinitely.
Right now 12 of our 63 rows have not been refreshed since before 9 August, and the oldest was last touched on 1 July, 41 days ago. If one of those has slipped, been cancelled, or already flown, we will not find out until its stale date rolls past and the prune eventually eats it.
#7 Two configs that validate cleanly and can never open
within_days below 1 collapses to zero. The window is built with int(within), so within_days: 0.5 becomes "+0 days" and the gate asks for a launch in the next zero seconds. We ran it: 0.5 returns False against a launch six hours away that 1 accepts. Meanwhile validate_config() explicitly permits floats — it checks isinstance(x, (int, float)) — so the builder tells you the config is fine. Negative numbers pass validation too, and never open.
The status must be the feed's short code, not its name. Matching is exact (both sides upper-cased), so "Go", "GO" and "go" all work — but "Go for Launch", which is what the feed calls that status in plain English and what a reasonable person would type, matches nothing, forever, silently. validate_config() does not check the status against any list of valid values.
#8 The English the builder shows you is broken
Every signal renders itself into a sentence for the builder. Fed this bot's exact live config, the renderer produces: "only when a Go any launch is within 3 days". The any: true case falls through to the literal string "any" and lands in the middle of the sentence.
Small, but this is the copy on the page where somebody decides whether to fork the bot — and it is precisely the config a forker inherits.
#9 "Go" is a statement about the schedule, not about the rocket
The bot's stated thesis is that "a confirmed Go status within 72h is live ops info… so confirmed-Go missions ≤0.85 may be underpriced". But Go means, in the feed's own words, that the T-0 is confirmed by official or reliable sources. It is a claim about whether the countdown is real. It is not a claim about whether the vehicle will reach orbit.
For a market asking "will this rocket launch by date X", that distinction mostly doesn't matter, and the thesis is coherent. For the markets this bot actually buys — will a token launch, what will its valuation be the day after — it is not a signal about anything. Our own table demonstrates the gap: we carried a Long March 7A at Go right up to a launch that failed.
#10 The backtester cannot replay it — and here the excuse is half fair
launch_anchor sits in _UNSUPPORTED_ENTRY in quants/backtest.py. A backtest of a bot using it runs without the gate and still returns a curve, flagging unsupported_rules. If you backtest this bot, you are not testing this signal.
The code's stated reason is that these anchors "need a forward-captured series we don't have retroactively". With energy_anchor we said that excuse was simply wrong, because the vendor serves decades of history on the endpoint we already call. Here it is half right, and the distinction matters. Launch Library does keep history — 7,586 past launches on the same free API — so a gate written purely on provider, rocket and within_days could be replayed today. But a historical row records the outcome (Success, Failure) and the time it actually flew, not the Go-or-TBD status it was showing three days beforehand. This bot's gate is keyed on exactly that status, and that as-of view is genuinely gone unless we start storing it. Half of this signal is replayable now; the half this bot uses is not.
The real record
Launch Go Rider (id 43, house bot, status oos) has been armed since 11 June 2026. It buys YES between 40¢ and 85¢ on markets with the word "launch" in the title and at least 1,000 contracts of 24-hour volume, $5 flat, at most 3 fills a day, holding to expiry. It is configured for both Kalshi and Polymarket; all 93 fills are Polymarket.
| 11 Jun – 11 Aug 2026 | Settled | Avg entry | YES rate | vs break-even | P&L |
|---|---|---|---|---|---|
| All settled trades | 27 | 66.1¢ | 66.7% | +0.6 pp | +$3.82 |
| Rocket markets (Starship) | 6 | 64.2¢ | 50.0% | −14.2 pp | −$8.03 |
| Everything else | 21 | 66.7¢ | 71.4% | +4.7 pp | +$11.85 |
| Still open (not counted above) | 66 | 61.3¢ | — | — | $330 staked |
Read the "vs break-even" column, not the YES rate. A binary bought at 66.1¢ needs to settle YES 66.1% of the time just to stand still — so a 66.7% hit rate is not a two-thirds success story, it is six-tenths of a percentage point of daylight. On $135 staked that is +$3.82, and our own falsifier_watch puts the expectancy at +$0.14 per trade with a spread of ±$1.53 — statistically indistinguishable from zero.
Do not read the rocket row as "rockets lose". Those six settled trades are not six bets. They are one rocket seen six ways: every one is SpaceX Starship Flight Test 13, bought at six different deadlines — will it fly by 15 July, by 20 July, by 23 July, by 25 July, by 26 July, by 31 July. Starship Flight 13 flew successfully at 22:51 UTC on 24 July (confirmed against Launch Library's historical record), which settled the first three NO and the last three YES. It is a single event, and a sample of one tells you nothing about whether the launch thesis works. What it does tell you is that in two months of running, the bot found exactly one real rocket story to bet on.
Also note the shape of the book: 66 of the 93 fills are still open, most of them in token markets resolving in 2027. Just under a third of this bot's history has actually settled. The +$3.82 is a real number about a small, unfinished, and mostly-not-about-rockets sample.
The case for the defence
In fairness to the bot, and against our own headline: it was created as an explicitly "lowest-confidence cheap experiment", it is out-of-sample and has never touched a live account, and by expectancy it is very slightly up rather than down. Its falsifier trips on a hit-rate threshold of 80%, which at an average entry of 66¢ was never the right bar — a 66.7% hit rate at those prices is roughly break-even, not a failure. If you retired it today you would be retiring it for missing a target that was mis-specified when it was written.
And "crypto token launch" markets are, linguistically, launches. If the bot were honestly described as "buy YES on launch-ish markets in the 40–85¢ band", its behaviour would match its label and it would be an unremarkable little momentum bot. The problem is not that it does something incoherent. The problem is that the thing it does has no relationship to the signal on the tin — and it is the tin that gets forked.
How to actually use it
Never pair it with any: true and a short window. That is a constant, not a condition. If you want the gate to mean something, name a provider or a rocket: {"rocket": "Starship", "within_days": 7} is a genuine question, because Starship flies a handful of times a year. {"any": true, "within_days": 3} is the string "true" with extra steps.
Make sure your markets are about the launch you gated on. Nothing in the platform enforces it, and title_contains: ["launch"] will hand you the entire crypto token calendar. If you mean rockets, say so — title_contains: ["STARSHIP"] or ["SPACEX"] — and accept that you will find very few markets, because there are very few.
Watch the resolution date, not just the price. This anchor's window starts now. A market resolving in eighteen months cannot be informed by a rocket flying on Thursday, no matter how confidently the gate opens. The signal is only meaningful on markets whose own deadline sits near the launch.
Use whole numbers for within_days, and the short code for status. Because of catch #7, 0.5 and "Go for Launch" both produce a gate that is silently welded shut.
Treat a quiet table as suspect, not as calm. Until the anchor learns a freshness rule, the only way to know whether the schedule is current is to look at when the poller last wrote. A gate that is open because the feed died looks exactly like a gate that is open because a rocket is flying.
Where to go next
eco_anchor is the closest relative — the other pure calendar gate, and the other signal whose value is entirely "is it that time yet". It is the interesting contrast, too: eco_anchor's window opens for about 1% of the clock, this one's for roughly all of it, and neither number is obviously the right one. tropical_anchor is the mirror image of this page's failure: another switch on a live event feed, another dead poller on the same locked database, but one whose bot has never traded at all rather than trading the wrong thing. energy_anchor is where the backtest-history argument in catch #10 gets its harder version.
If you want the signal this one is often confused with: there isn't one. Rockets are the only space data in the library, and launch_anchor is the only gate that reads it.
Written 11 August 2026. Every number on this page was read from production that day — the live host at /var/www/tinycorp.ai, not a local mirror: the output of quants/signal_registry.py, the source of quants/launch_anchor.py and predictive/space_launch_poller.py, the live crontab and the poller's log, read-only queries against space_launches in predictive.db and the bot's run and trade records in quants.db, a run of quants/falsifier_watch.py, and Launch Library's own upcoming and historical endpoints fetched directly from that same host. The gate behaviours in catches #7 and #8 were confirmed by running the real anchor functions against synthetic in-memory rows — no production data was written. Two things the registry reports about this signal are wrong and are corrected above: the poller resolves as db with no schedule, and the source resolves as "undocumented" when the poller's docstring names Launch Library outright. Its "live bots" column counts status oos and live together, so the 1 it reports is one out-of-sample bot and no live ones — the sixth signal running where that count has not meant "live". All TinyCorp bots trade simulated money. Nothing on this page is a projection.