My wife and I used to trade from separate Robinhood accounts. One of us would buy something, tell the other about it later, and hope we hadn't done the same thing twice. We were running two portfolios in parallel with no shared language.
Robinhood is a good app. It is not designed for two people to think together about money.
So I built something that was. It took roughly two hours.
Why we built this
The problem wasn't access. We both had accounts, we both had positions. The problem was visibility. There was no API to query, no way to ask "what does our combined picture look like?" without manually reconciling two apps on two phones. Research we'd each done separately, decisions made in parallel, no history either of us could pull up.
The fix we needed wasn't another app. It was an account that spoke HTTP.
Most retail traders don't know that Charles Schwab has a free, fully documented developer API. You register on their developer portal, wait a day or two for approval, and you have OAuth2 access to your own brokerage account: quotes, positions, order placement, transaction history, all of it. We transferred both our Robinhood accounts to Schwab. The whole migration took a week, including the ACATS transfer.
That API was the unlock.
What we built
The trading core is about 1,500 lines of Python with zero pip dependencies. The Telegram bot that wraps it is another 1,400. Getting to a working first trade took roughly two hours.
The structure is four pieces.
A trading CLI with about twenty commands: quote, research, theme, buy, sell, pnl, audit, and more. This is the engine. It talks to Schwab's REST API directly, with no third-party trading library in the way.
A risk reviewer that runs before every order. Hard limits are enforced here in Python, not in a prompt: 2× maximum leverage, $250k maximum notional per order, a 10% daily-loss kill switch, a 20% drawdown kill switch. These cannot be overridden, regardless of what the AI says.
A Telegram bot that wraps the CLI. This is the shared UI. Both of us are in the same group, both can ask questions and see results. The bot talks to Claude Sonnet by default, with a hard daily AI spend cap of $5.
OAuth tokens live in Cloudflare KV. Every order is written to a local JSONL file first, then to Cloudflare D1. The ledger never loses data, even if the database is temporarily unreachable.
The one decision worth stealing
The Reviewer is code, not a prompt.
I've written about this before in a different context. The dangerous failure mode in any agent system isn't the AI doing something malicious. It's the AI doing something irreversible because nobody put a real checkpoint between "recommend" and "execute." A language model confident enough to say "buy 200 shares of NVDA" is not the right place to also ask "but is this within our risk limits?"
Those two things need to be separate. One is judgment. The other is a guard rail.
So the Reviewer runs in Python before any call to Schwab's order API. It fetches the current portfolio snapshot, estimates the post-trade leverage, checks every hard limit, logs its verdict to the database, and either lets the order through or kills it with a reason. The LLM can propose. The code decides if it is safe to proceed.
The order of operations matters: Propose → Review → Confirm → Execute. The human sits between Review and Execute. The AI sits before Review. The code sits between.
How we use it
We have a shared Telegram group: me, my wife, and the bot.
She'll message the group asking about a stock. The bot fetches a brief: price, analyst targets, thematic context, risk flags. Both of us read the same answer in the same thread. We talk about it. If we want to do something, she proposes a trade. The bot sizes it, runs the Reviewer, and shows the verdict before anything is sent to Schwab.
She taps Confirm. The order goes to Schwab. Both of us see the fill confirmation in the group. Both of us can check P&L and see the same picture.
It is not a black box anymore.
What changed
Not the returns. I want to be honest about that. I have no idea whether this makes us better investors. Markets are hard, anyone who tells you their system reliably beats them is either lying or hasn't run it long enough.
What changed is the texture of how we talk about money.
Before, investing was something one of us did and mentioned to the other after the fact. Now it's a shared conversation. She asks the AI a question. I read the answer too. We disagree about something. We ask a follow-up. We decide together, or we decide not to.
The AI is the third voice in that conversation. It does the research, runs the numbers, surfaces what the Reviewer raises. We make the call.
That was worth the two hours.
What doesn't work
There is no backtesting, no paper trading mode. If you want to test a strategy before it touches real money, the Reviewer's dry-run mode is as close as it gets. You test by doing, bounded by the hard limits.
Schwab's refresh token expires after seven days. Once a week, one of us visits a URL, logs in, and re-authorizes. It takes thirty seconds. It is also annoying in the precise way that a real production OAuth2 implementation in a licensed brokerage is annoying. They require it for compliance, and there is no way around it.
The Telegram bot runs on a Mac mini at home via launchd. If the Mac is off, the bot is off. For now, that's fine.
We have only run this in roughly normal market conditions. The kill switches exist and have been tested in development. Whether they behave well under a real crash, I do not know. Untested is untested.
What else is out there
schwagent is the closest project I found: same stack (Python + Schwab + Telegram + LLM), open source. It is much larger: 35+ technical indicators, options strategies (wheel, iron condors, covered calls), Monte Carlo backtesting, a web dashboard. It is a serious system.
This is the opposite of that. No indicators, no backtesting, no options. Zero pip dependencies for the core. The goal was something we could deploy in an afternoon and trust because we understood every line of it.
If you want full autonomy, where the AI makes every call without a human in the loop, the agent-run trading fund post on HN is worth reading. The author documented every dead strategy with the same rigor as the wins. That discipline is admirable. It is not what we were building. We were building a tool for two people to think together, with the AI helping and the humans deciding.
Reflections
I did not go into this knowing how to build a trading system. I knew Python, I had worked with APIs before, and I had spent years thinking about where human judgment should sit inside an automated loop. That was the product at Phantom Auto, and it is the product at Mixus. Trading turned out to be the same design problem at a smaller scale.
The architecture is simpler than it sounds. Schwab's API is well-documented and easier to use than I expected. Claude is good at reading financial context and structuring a brief that two people can actually talk about. The hard part was deciding what the system should not do: drawing the line, putting it in code, and not moving it.
The code is not yet open-sourced. The trading core has no external dependencies and could run on most machines, but the OAuth flow and Cloudflare infrastructure are specific to how we have set things up. If you want the code, email me at shai@magzimof.com or find me at magzimof.com. If there is enough interest I will clean it up and publish it properly.
If you have built something similar, or transferred from Robinhood to Schwab for API access, happy to compare notes.
The thing I keep coming back to: we didn't build this to beat the market. We built it because we wanted to be in the same conversation about money instead of two separate ones. The bot made that possible. The API made the bot possible. And the API was free and waiting the whole time.
Compute helped me draft and proof this story.