field notes
I let software spend my money while I sleep. Here's what nearly went wrong.
verdict
Not one of the dangerous bugs crashed anything. Every single one ran cleanly, logged success, and did the wrong thing quietly — which is the entire lesson. Software that spends money needs to prove its numbers before it acts, not report that it finished. The safety came from gates and proof, never from confidence.
On the same small server that hosts this site, a program watches a handful of markets and places real orders on a crypto exchange with my own money. It runs unattended. Nobody is awake when it acts.
I'm not going to publish returns — this isn't a performance brag, and anyone showing you their equity curve is selling something. What's worth writing down is the failure modes, because they turned out to be the same ones that bite any unattended program on a cheap server: a backup script, a cron job, an invoice generator. Money just makes them obvious faster.
1. Time was running backwards
The exchange returns price history oldest-first. My code reversed it, on the reasonable-sounding assumption that newest-first was the norm. So the program treated the oldest bar in its window as "right now" — reading a price from months ago as the current one and deciding what to do about it.
It never errored. It fetched real data, ran a real calculation, and produced a confident, precisely wrong answer. It surfaced only because I compared what the bot said against what a chart said and the two disagreed. Zero orders had been placed — caught by a mismatch, not by a crash.
2. I audited it, declared it clean, and was wrong
After fixing that, I swept the codebase for the same mistake, found three copies, fixed all three, and wrote down that it was resolved. There was a fourth. It had been written the same day, after the sweep started, so it was never in the inventory being checked.
That fourth copy was the one that mattered — it fed a live signal, and it was reading an old price as current. The lesson is uncomfortable and general: an audit is only ever as good as its list of things to audit, and a codebase that's still being written invalidates the list while you work. Now the sweep gets re-run after the changes land, not before.
3. The setting that silently didn't apply
Before opening a position the bot sets its leverage. One day that call quietly failed — and the code, having asked, went ahead and opened the position anyway. It opened at ten times the intended leverage. Same trade, same logic, ten times the exposure, no error anywhere.
The fix is a pattern worth stealing for anything that configures something before using it: set it, then read it back, and refuse to continue if what came back isn't what you asked for. Don't trust that a request succeeded because it didn't throw. Once that rule existed in one bot, I applied it to the other before it could bite there too.
4. It could open a position but not close one
For a while the program was, in effect, one-way. Entries worked and had been proven live. The exit path had never actually run — and it was wrong. The exchange account was in a mode where closing a long requires a specific combination of parameters, and my code sent the intuitive one, which the exchange rejected outright.
Nothing was broken until the day it needed to sell. Then it would have retried, failed, and held a position I'd asked it to exit. Untested exits are the most dangerous code you can run, because they only execute in the situation you least want to be improvising in — and every day they don't run feels like evidence they work.
5. The restart that placed real trades
The bot remembers what each market's signal was last time it looked, so it can spot the moment one flips. After I corrected the maths, I restarted it — and it compared the new, correct signals against the old, wrong ones it had saved. Half a dozen markets appeared to flip simultaneously. They hadn't. Nothing had happened at all; the bot had simply changed its mind and read that as the world changing.
It opened real positions on the strength of it. I closed them and added a rule: any change to the decision logic means wiping the saved state first, so there's nothing stale to compare against. Later I found the sharper version of the same lesson — clearing that memory prevents phantom entries, but a restart still re-checks live prices against stop levels, so it can still act. If you need a restart to change nothing, timing matters as much as state.
What actually keeps it safe
None of these were caught by being careful. They were caught by structure:
| The rule | What it prevents |
|---|---|
| Three independent switches must all be on to trade | One wrong config or stray restart can't arm it |
| Run in dry-run and print what it would do | Every bug above shows up in a log line instead of an order |
| Prove the computed numbers before restarting | Catches wrong-but-plausible maths that runs without error |
| Timestamped backup and a diff before every edit | Any change is reversible in seconds |
| Exchange keys cannot withdraw, and are IP-locked | Worst case is bad trades, never an empty account |
| Separate keys per program | A problem with one can't reach the other |
| Reconcile against reality on startup | Catches the bot believing it holds something it doesn't |
Every one of those is unglamorous and none of them improve returns. They're the reason the mistakes above are anecdotes instead of losses.
The honest verdict on the strategy
Here's the part most people writing about trading bots leave out. I backtested the strategy properly — walk-forward, across years, with realistic trading costs included rather than assumed away. The result: the edge is marginal. Several "improvements" I was confident about made it measurably worse once costs were real. What genuinely helped was risk control — tighter loss limits and better exits — not cleverness about entries.
That's a boring conclusion and it's the true one. A bot is a way to execute a plan without flinching; it is not a way to conjure an edge that isn't there. If you build one, expect your best work to be the parts that stop you losing.
Why this runs on a cheap server
The whole thing — the bots, this website, an app backend, a public API — sits on a single small VPS. Unattended software doesn't need scale, it needs to still be running unattended tomorrow: restart on reboot, a log you can actually read, and a bill so small you never have a reason to turn it off. That's a $5-a-month problem, not a cloud-architecture one.
The full setup is in how I run everything from a $5 VPS, and the shortlist of where to put it is in the best VPS under $10. The code was written with an AI agent, reviewed line by line before anything went live — that workflow is in installing Claude Code.
Start on DigitalOcean (free credit)
Nothing here is financial advice, and none of it is a suggestion to trade. It's a report on running unattended software that spends real money — including the parts that went wrong.
Some links on this page are affiliate links — if you sign up through them, SoloStack earns a commission at no extra cost to you. I only list tools I'd run on my own server. Full disclosure.