Back to blog
Trading Systems

Building 100k: a year of iterating on an automated trading system

How a weekend script turned into a market-scanning, signal-generating, order-placing system — and what a year of building it taught me about trading, backtesting, and trusting my own code.

100k — a market-scanning and execution engine

The origin story

100k started on August 7th, 2025, as the kind of project every developer recognizes: a single script that fetched a price and printed it to a console. Within a few days it could call GPT with some collected data, place a paper trade, log the result to Notion, and ping me on Telegram. It worked just often enough to be dangerous, and just badly enough that I put it down for a few weeks.

Then I put it down for a few months. Between September 2025 and February 2026, the repo didn't move. This is the part of the journey most build-in-public posts skip — the long stretch where a side project just... sits there, half-working, while life happens. I'm including it because I think it's the more honest version of "growth and iteration": it wasn't a straight line. It was a spike, a long flat line, and then — starting in March 2026 — a real rebuild.

From March onward the project turned into something closer to an actual system: a stock universe, a backtesting engine, a deterministic review strategy, real market data via Alpaca. By June, iteration hit a different gear entirely — 122 commits in a single month, more than the rest of the project's history combined. That's when 100k went from "a bot that sometimes trades" to a layered pipeline with its own setup catalog, risk governor, and a habit of second-guessing its own results, which turned out to be the most important part.

Commit activity by month, August 2025 to July 2026

What it actually does

At its core, 100k is a market-scanning, signal-generation, and execution engine for crypto and stocks. It runs on a schedule, works through a rotating universe of symbols, and passes each one through a pipeline: pull OHLCV data, run it through deterministic technical filters (volume floors, volatility caps, momentum thresholds, proximity to range highs), optionally layer on LLM and alt-data annotation, construct a full trade plan with entry/stop-loss/take-profit and position sizing, persist everything to SQLite, and — only when explicitly armed with an AUTO_EXECUTE flag — place the order through a real broker.

Two brokers, deliberately kept separate: Alpaca for stocks, running against a $100k paper account, and Coinbase Advanced Trade for crypto, running against a genuinely small real balance (low hundreds of dollars, sized at $13–14 per trade). That split was intentional — paper-test the more complex stock logic where mistakes are free, and keep the live-money experiment small enough that a bug costs a coffee, not a mortgage payment.

The part I'm proudest of isn't any single strategy — it's the feedback loop. Closed trades write back into SQLite with actual entry/exit prices and P&L, a performance module computes win rate, average R-multiple, and expectancy by strategy and by score bucket, and a Streamlit console surfaces all of it. The system is designed to eventually tell me where my own filters are wrong, not just to generate signals and hope.

The 100k signal pipeline, from universe selection to performance feedback

The technology underneath

The stack stayed unglamorous on purpose — it's a trading system, not a place to test unfamiliar infrastructure:

  • Data & execution: ccxt against Binance for crypto OHLCV, Alpaca's REST and data APIs for stocks, Coinbase Advanced Trade with JWT/ECDSA-signed requests for crypto order placement.
  • Core logic: Python throughout, pandas for the indicator math, a from-scratch deterministic scoring engine rather than an off-the-shelf TA library, so every filter threshold is something I can explain in one sentence.
  • LLM layer: OpenAI for headline sentiment scoring and trade-thesis annotation, plus an experimental multi-agent "bull vs. bear" debate layer (TradingAgents) for a second opinion on conviction.
  • Storage & ops: SQLite as the system of record, Supabase for a remote ledger and sync, Streamlit as the operator console, Telegram for real-time notifications (including a free public signal channel).
  • Free alt-data: CFTC positioning, DeFi TVL, CNN/Alternative.me Fear & Greed, StockTwits sentiment, SEC Form 4 insider filings, funding rates and open interest from Binance — all zero-cost signals folded into scoring as nudges, never as hard gates, unless specifically proven to deserve one.

By early July the setup catalog held 29 distinct, swappable strategy definitions — mean-reversion, breakout, trend-following, momentum — each one expressed as data (filters and score weights) rather than a bespoke code path, so adding a new idea from a TradingView script or a TASC article is now an afternoon, not a rewrite.

The two findings that mattered most

Everything above is infrastructure. The more interesting part of the year was what the system taught me about not trusting my own assumptions.

Finding one: a gate can fail silently while still costing money. I'd built a conviction layer using a multi-agent LLM debate framework to add a second opinion on top of the deterministic filters — bull case, bear case, a synthesized confidence score. It looked like it was working. It wasn't. A library-level incompatibility meant every single debate call was rejected by the OpenAI API and the gate, built to fail open rather than block trades on an error, quietly waved every candidate through while still burning API calls. The lesson wasn't really about that one bug — it was that "the gate is enabled" and "the gate is doing anything" are different claims, and I now check for the second one, not just the first. It's why the roadmap doc I keep on this project has separate evidence tiers for "code exists," "tests pass," and "observed doing something in the live ledger" — three very different levels of confidence that are easy to blur together when a dashboard just says "Done."

Finding two: an intuitive signal can be exactly half right. I had a hypothesis that crypto funding rates and open interest trends — a decent proxy for how leveraged and one-sided the market is — should nudge every trade's score up or down symmetrically. Before wiring it into anything live, I ran it retrospectively against 15,875 historical filled trades. The symmetric version didn't hold up: "favorable" conditions produced a 25.3% win rate, barely different from the 26.8% baseline. But the "unfavorable" bucket was a different story — 15.9% win rate against a 95% confidence interval of roughly 12–20%, with no overlap against the neutral baseline. The data was telling me something more specific than my original hypothesis: this signal isn't good at picking winners, but it's decent at flagging trades likely to be losers. So that's exactly what shipped — a one-sided reject filter, not the symmetric nudge I'd originally designed, and both are gated independently in config so the honest, weaker version stays off unless proven otherwise. It's a small result — it reduces loss severity on an already-unprofitable pocket of trades, it doesn't manufacture an edge — but it's the kind of small, specific, testable result I trust a lot more than a strong intuition.

Win rate by derivatives-positioning bucket, tested against 15,875 historical trades

Where the numbers actually stand

In the interest of not writing a highlight reel: live performance so far is modest and early, and worth stating plainly rather than dressing up. Across every closed trade to date, win rate sits around 20% on a still-small sample, and total realized P&L — after trading fees — is a net loss of under $3. That number took a bit of chasing to trust: a handful of early trades had briefly looked far worse than that due to a broker-ledger reconciliation bug (duplicated rows from a single stale fill inflating the loss column), which a later fix tracked down and cleaned up. Once the ledger matched what the broker actually reported, the real number was this close to flat — which, at this sample size, is roughly the most honest thing the data can tell me either way.

I'm sharing that number because a system that only reports the flattering statistics isn't one I'd trust with real money, and a blog post that only reports the flattering statistics isn't one worth reading. The current sector-and-setup routing system only went live a couple of weeks ago, so the sample under today's rules is still thin by design — that's expected at this stage, not a verdict on any individual strategy. The near-term plan isn't to declare victory — it's to let the current setups run long enough to accumulate a real sample, keep every claim tied to something observed in the ledger rather than something merely built, and only turn up position sizing once a strategy has actually earned it.

What's next

The roadmap ahead is less about adding new signal sources and more about proving the ones that already exist: accumulating enough closed trades per strategy-sector combination to trust a hit rate, and wiring the crypto-derivatives reject filter into the backtest engine itself for a proper before/after comparison. Once that evidence bar is cleared, the next real step is introducing Kelly-criterion position sizing — letting each strategy's own track record decide how much weight it earns, rather than sizing every idea the same. It's slower and less exciting than shipping another indicator combination, but it's the difference between a system that looks sophisticated and one that's actually earned any of it.

If you want to follow along, the sanitized public signal feed is on Telegram at t.me/tradingsignals100k. And if you're building something similar: the boring parts — the audit trail, the "did this actually run" checks, the willingness to publish a losing statistic — turned out to matter more than any single strategy idea.

Nothing above is financial advice, and nothing in 100k is running with money I'm not fully prepared to lose.