Personal project 2026 Ongoing

BOONO: Building an Autonomous Trading System That Can’t Lie to Itself

Most of its trades won. One trade erased all of it. The engineering story of BOONO, an autonomous Solana trading system built on data and on-chain truth.

boono

Why I Built BOONO

I wanted to build autonomous software under conditions that punish every wrong assumption. The Solana memecoin market is exactly that. New tokens appear every few seconds. Some of them are dead twenty minutes later. Liquidity can disappear while your transaction is in flight. Market data feeds disagree with the blockchain. Many tokens are outright scams designed to trap buyers.

BOONO was never a get-rich-quick project. The amount of money I let it trade with was deliberately tiny. The real question was an engineering one: can I build a system that watches this market in real time, makes its own decisions, executes them irreversibly on-chain, and keeps an honest record of what actually happened?

That question took months to answer properly. A lot of it was late nights watching live logs, finding an assumption that was wrong, rewriting a component, measuring again. The most interesting parts of this article are the places where BOONO failed, because those failures changed the architecture more than any feature I planned up front.

What BOONO Is

BOONO is an autonomous trading system for Solana. The pipeline, end to end: it discovers tokens, analyzes them, scores them, applies risk gates, executes trades, confirms them on-chain, monitors open positions, exits them, and reconciles its internal ledger against the blockchain.

The backend is headless. It runs the whole loop on its own, whether or not anyone is watching. The React dashboard and the Telegram bot are observation and control surfaces. They show me what the engine is doing and let me pause it, but the engine does not depend on them. Nothing in the decision path waits on a UI.

The stack is Python (FastAPI, asyncio) for the engine, React for the dashboard, PostgreSQL for the ledger, Redis for hot state, and WebSockets everywhere data needs to move fast.

                 market data feeds (streams + REST)
                              |
                              v
                    +------------------+
                    |   data engine    |  ingest, normalize, cache
                    +------------------+
                              |
                              v
                    +------------------+
                    |  decision engine |  risk gates -> scoring -> sizing
                    +------------------+
                              |
                              v
        Jupiter (routing/quotes) --> Jito (MEV-protected submission)
                              |
                              v
                        Solana network
                              |
                              v
              Helius RPC/WebSocket (confirmation, balances)
                              |
                              v
                    +------------------+
                    | settlement +     |  on-chain truth -> PostgreSQL ledger
                    | reconciliation   |
                    +------------------+

   observation/control (not dependencies): React dashboard, Telegram bot
   specialized path: PumpPortal (launch-time data; emergency sell fallback)

Each provider has one job. Jupiter finds swap routes and quotes. Jito submits transactions with MEV protection. Helius gives BOONO direct access to Solana: balances, transaction confirmation, and a WebSocket view of the wallet. PumpPortal covers the launchpad corner of the market with specialized real-time data, and serves as an emergency sell path when normal routing cannot exit a collapsing token.

The important idea is specialization. No provider is treated as the universal source of truth. That decision looks obvious in a diagram. It took real losses to learn, and most of this article is about why.

Building the Data Engine

Before any algorithm can decide anything, the data has to exist. That was the first hard problem.

BOONO continuously scans the Solana token market through several discovery lenses: token creation events as they fire, trending movers, tokens that known profitable wallets are accumulating, and launchpad tokens progressing along their bonding curve. New token creation events can enter the pipeline within seconds of launch.

For each candidate, the data engine builds a live profile. A few representative examples of what it tracks: how concentrated the holders are (how much the deployer holds, how much snipers and bundlers hold, what the top ten wallets control), the deployer’s history across previous launches, the liquidity depth and whether the LP is locked or burned, and the quality of the trade flow, meaning organic buying versus wash trading.

The plumbing behind this matters more than the field list. Data arrives over WebSocket streams when they are available and falls back to REST polling when they are not. Everything is normalized into one shape, serialized once, and shared by the REST API and the WebSocket layer, so the dashboard and the engine always see the same picture.

Three rules run through the whole pipeline. First, every cached value has exactly one writer. Early on I had two code paths updating the same cache, and the bugs from that were miserable to find. Second, every price carries its age. A consumer that needs fresh data rejects a stale price instead of silently using it. The dashboard shows a “stale” badge rather than pretending a frozen price is live. Third, everything is rate-limited and coalesced, because real-time data will happily produce more events than a single asyncio event loop can survive. There is a war story about that later.

From Data to a Trading Decision

The decision engine follows one principle: gates before scores.

Anything that looks like a rug pattern is blocked from buying before the score matters. A deployer who has launched and abandoned many tokens. Holder concentration that means a few wallets can dump on everyone else. Security flags on the token contract. Liquidity too thin to exit. Wallet and position limits that are already reached. Some of these checks live inside the scorer and zero the score outright; others run at buy time on tokens that already carry a score. Either way, a token that fails a hard gate can still appear in the scanner with a score, which is useful for calibration, but it is never eligible for purchase.

Tokens that survive the gates are scored from 0 to 100 across six weighted signals, covering momentum at several timescales, volume relative to liquidity, activity, and holder quality. One thing I want to say plainly: the score is a ranking and decision signal. A score of 80 does not mean an 80% chance of profit. It means the token looks better than one scoring 60, according to signals I chose and weighted. Treating a score like a probability is how people fool themselves.

My favorite gate is the pre-buy exit-route check. Before BOONO buys a token, it verifies that it could sell it. In memecoin markets this is not paranoia. Some tokens can be bought but not sold, by design. Others have routes so thin that exiting would move the price against you catastrophically. A buy without a confirmed exit route is not a position, it is a donation.

Position sizing scales with the bankroll and is capped as a percentage of the pool’s liquidity, so the system never becomes its own market impact. Exits are organized as a severity hierarchy rather than a flat list of rules. At the top sits a catastrophic stop that liquidates immediately and skips every discretionary check. Below it come the ordinary protective exits, stops and trailing stops and liquidity monitoring, then profit-taking ladders, and at the bottom a time-based exit for positions that are going nowhere. The design point is the ordering: the worse the situation, the fewer questions the system asks.

Execution: From Decision to Solana

A buy decision starts a chain of steps that all have to work: get a quote from Jupiter, build the swap transaction, sign it locally (the private key never leaves the machine), submit it through Jito for MEV protection, wait for the Solana network to process it, confirm it through Helius, settle the actual amounts, and only then create the position in the ledger.

Every stage of every buy is timestamped in the trade log, so I can measure this instead of guessing. Across 82 live buys, the median time from decision to confirmed buy was about 3.4 seconds. Quoting and building the transaction typically took around half a second combined, signing was nearly free, and most of the rest went to submission and on-chain confirmation, which I cannot control. The tail is real: roughly a quarter of buys took longer than 7 seconds, usually when the network was congested. These are measurements from my runs, not guarantees, and when a buy is slow the per-stage timestamps tell me exactly where the time went.

Exits have more than one path, because exits are where memecoins hurt you. A normal exit goes through Jupiter like any swap. A panic exit, triggered by the catastrophic tier, submits aggressively with a high slippage tolerance, because in a rug the price you get matters less than getting out at all. And when Jupiter cannot even produce a route, which happens when a token’s liquidity is collapsing, BOONO falls back to the specialized PumpPortal path to attempt the exit anyway. Having a fallback for “the normal way out no longer exists” turned out to be one of the most practically important parts of the system.

Then I Let It Trade With Real Money

Simulation can’t reproduce everything. Real fills, real slippage, real RPC behavior under load, real price feeds misbehaving at the worst moment. So I funded the wallet with a deliberately small amount and let BOONO trade unattended. The goal was never to prove profitability with pocket change. The goal was to expose the system to reality and see how it actually behaved.

The first 30 closed positions, a small early sample, came out like this: 73.3% of them were winners. The median winning position returned about +4%, and the best one +6.6%. In absolute terms the amounts were tiny by design: with deliberately small position sizes, a typical winning trade earned a few tens of cents.

The total result was still a net loss.

Nearly three out of four trades won. Not one of them made more than 7%. BOONO still lost money.

The small bankroll explains why the amounts were small. It does not explain the loss. The loss came from the shape of the results: when every winner is modest, the architecture cannot afford to let even one position fail catastrophically, and it did. That taught me more than the previous months of building. A win rate says almost nothing about whether a system is healthy. The distribution and severity of the losses matter far more.

BOONO portfolio screen showing an active Solana position with live unrealized P&L, entry price, holder concentration and risk badges

The Trade That Changed BOONO

One position did almost all of the damage. It fell 92.71% before it was closed, and that single loss was larger than the entire sample’s net result. Take it out and the sample was profitable. In absolute terms, that one trade handed back roughly what thirty-five median winners had earned. The second-worst realized loss in the same window was about −6%, which is what a working stop-loss looks like. So this wasn’t a distribution of bad trades. It was one trade the architecture should never have allowed.

There was also a second, stranger category of damage: a few positions never produced a clean realized number at all. Those tokens were rugged. The developers pulled the liquidity, which means there was no longer any pool to sell into, so the tokens became worthless and unsellable at the same time. The ledger ended up holding positions it couldn’t honestly close. Losses that can’t even be booked properly are their own kind of warning sign.

Every stop-loss mechanism I had built existed precisely to prevent a 92% loss. The system was designed to cut a losing position at a small, controlled cost. Instead it held one most of the way to zero. I stopped trading and started investigating.

When the System Lied to Itself

What the investigation found was worse than a bad strategy. The strategy had barely been given a chance to fail.

The 92.71% loss happened because the price BOONO was reading was old. The runtime price cache could return a stale price without enforcing its age. The feed for that token had frozen near the entry price, so the position looked roughly flat the entire time it was collapsing. The stop-loss logic was watching a still image of a fire. It never fired because, in the data it could see, nothing was wrong.

Around the same time I found related problems. Position state could accept implausible prices, including one glitched tick that was off by several orders of magnitude. A corrupted price like that poisons peak tracking, and trailing stops are computed from the peak, so one bad tick can quietly break the exit logic that follows it. Rug protection reacted too late for the same underlying reason: the data it was watching no longer described reality.

This failure mode is more dangerous than a bad algorithm, and the distinction matters. A bad algorithm makes a bad decision on true information, and you can measure it, tune it, or replace it. But if the system holds a false picture of its own positions, even a perfectly correct stop-loss cannot protect it. It will happily do the right thing with the wrong facts. The system was not wrong about the market. It was wrong about itself.

Two Planes of Truth

The structural fix was to split BOONO into two planes and to stop letting one do the other’s job.

The decision plane is market data: prices, token intelligence, scoring. It has to be fast, and in exchange it is allowed to be occasionally imperfect. It decides what to buy and sell, and it feeds the dashboard.

The accounting plane is the blockchain itself, read directly over Helius RPC. It is the only thing allowed to establish what financially happened. A fill, a balance, a realized profit or loss: none of these are written into the ledger until the on-chain transaction is confirmed and its actual amounts are read from the chain. Every financial record moves through explicit states, pending until confirmed or failed, and a pending record is displayed as pending instead of being filled with an estimate.

The principle, in one line: the accountant must be independent of the advisor.

This sounds abstract until you watch an API return “success” for a transaction that later fails on-chain. A success response is a claim. The blockchain confirming the transaction is a fact. BOONO treats them accordingly, and a background reconciliation process continuously compares the ledger against actual wallet state on-chain. If I sell a position manually from my wallet, outside BOONO entirely, the reconciler notices the balance change, records the real exit, and closes the position. If the ledger and the chain ever disagree, the chain wins.

Building for Catastrophic Failure

The 92.71% loss, and the rugged positions that could not even be closed cleanly, reshaped the exit architecture directly.

Prices used for exit decisions now come with freshness requirements. A price that is too old is not a price, it is a trigger: the system treats an expired feed as an emergency in itself and attempts an urgent exit rather than waiting blindly for fresh data. Incoming prices pass plausibility checks, so a glitched tick can no longer poison peak tracking. Liquidity is monitored while a position is open, because a draining pool is often the earliest reliable signal of a rug.

The panic tier sits above everything. When it triggers, it does not consult the discretionary logic, it does not wait for ideal routing, and if the normal Jupiter path cannot exit, the fallback path tries. Around the trading logic sit independent kill switches, deliberately layered: a time-boxed pause, a durable pause that survives restarts, and a hard execution flag. Circuit breakers watch the account itself and stop trading on a daily loss limit, a streak of consecutive losses, or a drawdown from the high-water mark.

The change is visible in the data that followed. In the roughly two dozen positions closed after these fixes went live, the worst realized loss was an ordinary stop in the −6% range — no position has been allowed to bleed out on a frozen price since. That is exactly the point: the fixes were never about picking better tokens. They were about making sure one abnormal event can no longer erase everything the normal ones earn.

The lesson underneath all of it: the objective is not only to make good trades. The architecture must make it structurally difficult for one abnormal event to destroy the results of dozens of normal ones.

Bugs That Taught Me More Than Features

Three bugs stand out. None of them were exotic. All of them changed how I build.

The WebSocket firehose. I noticed every endpoint getting slow, sometimes by ten seconds, and eventually the HTTP listener died entirely. I suspected the database or a slow external API. The real cause: I had removed a throttle to make held positions “fully real-time,” and a hot token trades more than 100 times per second. Three held positions produced around 400 WebSocket messages per second, each scheduling work on the single asyncio event loop, which starved everything else. The fix was rate caps on the stream plus client-side coalescing that drops intermediate price ticks but never snapshots. The lesson: in event-driven systems your load is not bounded by how many things you subscribe to, but by the rate of the hottest one. Backpressure is not optional.

The fail-closed gate that couldn’t fail closed. BOONO checks a pause flag in Redis before every buy. I wrote it fail-closed: if the flag cannot be read, block the buy. Reviewing it later, I found my fail-closed code was unreachable. The cache layer two levels down caught every Redis error and returned “no value,” which the gate read as “not paused.” The error I was handling could never reach my handler. The fix was a strict read path for safety-critical reads that lets infrastructure errors propagate, plus a test that simulates the Redis outage and asserts that the gate actually blocks. The lesson: fail-closed is a property you demonstrate by causing the dependency to fail, not a property you write in a comment.

The config that overrode itself. For weeks the bot opened up to five concurrent positions while my .env file said the maximum was three. I suspected a race condition in the position counting, and I did find real ones worth fixing. But the primary cause was simpler. A dashboard settings feature persisted values to PostgreSQL with database-beats-file precedence, and a stale row, seeded from old defaults, had been silently overriding my config on every boot. The startup log even printed one line announcing it. I had never read that line. The lesson: any configuration override system must answer “what is the effective value right now, and where did it come from?” in one obvious place.

Testing the Strategy, Not My Intuition

There are two separate findings in this project, and I want to keep them separate because they came from different experiments.

The live trading experiment exposed a correctness problem: catastrophic tail losses caused by stale prices and broken position state. That was about the system lying to itself, not about strategy quality.

The second finding came from simulation. For one subsystem, a launch sniper, I built a paper simulator before letting it trade real money again. It runs the full production decision pipeline against live markets, opens simulated positions, and applies the exact production exit logic to real price feeds, with no transactions. After a few hundred simulated trades, the entry score for that subsystem showed almost no correlation with the outcomes I was measuring (roughly r = +0.04). The exit behavior explained far more of the results, including one stop level that was mathematically guaranteed to close a large share of trades at a loss before winners had time to develop.

I had planned to spend weeks tuning that entry score. The data said the score was not the problem. So I retuned the exits instead, and only with evidence in hand. That reordered how I approach algorithm work permanently: measure first, tune second. My intuition about where the edge lived was simply wrong, and a weekend of simulator work found that out for free.

BOONO Today

What started as a scanner and a trading loop eventually became a collection of independent services for discovery, scoring, execution, settlement, reconciliation, simulation, monitoring and remote control. Today BOONO runs as one autonomous engine with those layers around it. The scanner watches the market through its discovery lenses. The gates and the scoring engine filter candidates. Execution runs through Jupiter and Jito with the emergency paths behind it. Every fill settles against on-chain evidence before it becomes a ledger entry, and reconciliation keeps the ledger honest between trades. Portfolio management rotates capital out of the weakest position when a clearly stronger candidate appears. The paper simulator keeps collecting calibration data for strategy changes before they touch money.

PostgreSQL holds the ledger and the full decision lineage: every trade stores why it happened, the score breakdown, the gates it passed, and the market snapshot at entry, so “why did it buy this three months ago?” has an exact answer. Redis carries the hot state. The React dashboard shows positions within about a second of a fill confirming, and the Telegram bot sends trade alerts and accepts pause and resume commands from my phone. Health monitoring covers each subsystem separately, background loops are supervised and respawn if they die, and the health endpoint distinguishes “process up” from “actually working.”

 

It is not finished, and it is not a money printer. It is a live system I keep auditing, and the audit log itself, every bug with its root cause, fix, and the test that proves the fix, has become my favorite artifact of the project.

$BOONO — Community Token

After publishing BOONO and building around it in public, I launched $BOONO as a community token around the project.

BOONO remains the engineering project. The token does not represent ownership of the system or a claim on its trading results.

Official $BOONO contract address:

2Qd65KE9STzngT6sk1VrphnM9XM8TBcEGcwsuWJMpump

Please verify the contract address before interacting with any token claiming to be $BOONO.

https://pump.fun/coin/2Qd65KE9STzngT6sk1VrphnM9XM8TBcEGcwsuWJMpump

What I Learned

A few lessons I now carry into everything I build:

Separate deciding from accounting. The data that makes decisions fast must never be the record of what happened. Ground every irreversible action in authoritative evidence, because an API saying “success” is a claim, not a fact. A high win rate can hide catastrophic tail risk, so design for the worst tier first and let the average take care of itself. Fail-closed behavior must be tested by actually causing the dependency to fail. Real-time systems need backpressure and coalescing from day one. Observability should tell you why the system is wrong, not just that it is running. Measure an algorithm before tuning it, because intuition about where the edge lives is often wrong. And autonomous systems need independent kill switches, layered, so that no single failure can take the last one away.

BOONO has not solved trading, and I would not claim it makes money. It is an ongoing engineering project that keeps teaching me things, mostly by breaking in ways I did not predict. That was the point of building it.