My trading bot created an orphan position at 2am and quietly bled money until I woke up. That failure became lesson LL-221 in a system that now has 122+ documented lessons. The system read that lesson the next time it tried a similar trade — and didn't repeat the mistake.
That feedback loop is what I want to talk about today.
The Problem With "AI Trading" in the Wild
Search GitHub for "AI trading bot" and you'll find hundreds of repos. Most of them share the same DNA: pull historical prices, run a backtest, generate a beautiful equity curve, call it a strategy.
None of them execute a real trade.
I've been building in this space long enough to know the gap between a backtest and a live execution is where most projects die. Options trading makes that gap enormous. Iron condors have four legs — a short call, long call, short put, and long put. If you try to fill them one at a time and the market moves mid-execution, you end up with a partial fill. One or two legs fill; the others don't. You now have a naked short position you didn't ask for, and it will bleed money until you manually close it.
That's what happened at 2am.
Manual options trading has the same problem, just with a human in the loop making emotional decisions at 2am instead of code. The decision to hold, hedge, or close a losing position is exactly when rational thinking exits the building.
I wanted to build something that:
- Executes iron condors atomically — all four legs or none
- Makes trade decisions via AI consensus, not gut feeling
- Gets smarter from every failure, automatically
The Architecture
Here's the high-level stack before I get into the self-healing part, which is the interesting bit.
Multi-Agent LLM Consensus
Trade decisions aren't made by a single model. Multiple LLMs independently analyze market conditions — IV rank, days to expiration, delta targets, recent price action on SPY — and each returns a vote: trade or no-trade, and proposed strike parameters. The system only enters a position when there's consensus.
This isn't just hedging against model hallucinations. Different models have different failure modes. One model might be overconfident in trending markets; another might be overly conservative. Forcing consensus raises the bar.
def get_consensus_decision(market_data: dict) -> TradeDecision:
agents = [GPT4Agent(), ClaudeAgent(), GeminiAgent()]
votes = [agent.analyze(market_data) for agent in agents]
# Require supermajority (2/3+) to enter
trade_votes = [v for v in votes if v.action == "ENTER"]
if len(trade_votes) / len(votes) >= 0.67:
# Average the strike parameters across agreeing agents
return aggregate_parameters(trade_votes)
return TradeDecision(action="HOLD")
MLeg Atomic Orders via Alpaca
After LL-221, I rearchitected order execution entirely. Alpaca's MLeg (multi-leg) order type sends all four iron condor legs as a single atomic order. The exchange fills everything together or rejects the whole thing. No partial fills. No orphan positions.
def submit_iron_condor(short_call, long_call, short_put, long_put, qty):
legs = [
OptionLeg(symbol=short_call, side="sell", ratio_qty=qty),
OptionLeg(symbol=long_call, side="buy", ratio_qty=qty),
OptionLeg(symbol=short_put, side="sell", ratio_qty=qty),
OptionLeg(symbol=long_put, side="buy", ratio_qty=qty),
]
order = MarketOrderRequest(
legs=legs,
order_class=OrderClass.MLEG,
time_in_force=TimeInForce.DAY,
)
return trading_client.submit_order(order)
This one change eliminated the entire class of partial-fill failures.
GitHub Actions as the Execution Runtime
The system runs on cron-based GitHub Actions workflows — no server to manage, no EC2 instance to babysit. Every trading day, a workflow fires, runs the consensus check, executes if conditions are met, and logs everything.
The tradeoff: GitHub Actions has limits and failure modes of its own (which is how we accumulated several of those 122 lessons). The benefit: fully auditable execution history, free for the usage level we're at, and dead simple to monitor.
The Self-Healing Loop
This is what makes the system different from everything else I've seen in open-source algo trading.
Every failure gets documented. Every workflow abort, every failed order, every unexpected API response, every bad assumption that cost money — it goes into a structured "lesson learned" entry in the format LL-NNN.
LL-221
Date: 2025-11-14
Trigger: Iron condor partial fill — short put leg filled, long put leg rejected
Impact: Naked short put position held overnight, -$47 unrealized
Root Cause: Individual leg orders submitted sequentially; market moved between fills
Fix Applied: Migrated to Alpaca MLeg atomic order type
Prevention: Pre-trade check verifies MLeg order class before submission
Those lessons live in a vector store. Before every trade decision, the system queries the RAG store:
def get_relevant_lessons(trade_context: dict) -> list[Lesson]:
query = f"""
Trade type: iron condor
Underlying: {trade_context['symbol']}
Order type: {trade_context['order_type']}
Market condition: {trade_context['iv_rank']} IV rank
What failures have occurred in similar setups?
"""
return rag_store.similarity_search(query, k=5)
The retrieved lessons get injected into the agent prompts. The agents aren't just analyzing the market — they're analyzing it with institutional memory of what went wrong before.
Two Lessons That Changed Everything
LL-221 — The Orphan Put Crisis
Described above. Sequential leg orders on an iron condor left a naked short put when the long put fill was rejected. The lesson led directly to the MLeg atomic order migration. Every trade since has used MLeg. Zero partial fills since implementation.
LL-282 — Wrong API for Closing Positions
We were using the order submission endpoint to close existing positions — sending a new counter-order manually constructed from the open position. This worked sometimes and failed silently other times, depending on position state. The lesson documented the correct approach: Alpaca's close_position() method, which handles the position lookup and counter-order construction internally.
# Before LL-282 (fragile)
closing_order = MarketOrderRequest(
symbol=position.symbol,
qty=position.qty,
side=OrderSide.BUY, # manually inverting
time_in_force=TimeInForce.DAY,
)
trading_client.submit_order(closing_order)
# After LL-282 (correct)
trading_client.close_position(position.symbol)
One line. The lesson prevented a class of silent close failures that would have left positions open past intended expiry.
This is the moat. Every failure makes the system more reliable. The lessons compound. By the time the system has 300 lessons, it will have navigated failure modes that no new competitor can replicate without going through the same painful discovery process.
Real Numbers (No Cherry-Picking)
I'll be direct about where things stand, because the algo trading space is full of people showing you equity curves from optimal historical windows.
| Metric | Value |
|---|---|
| Paper account equity | $95,431 |
| Starting paper equity | $100,000 |
| Closed trades | 1 |
| Closed trade P&L | +$41 (iron condor, Jan 22 → Feb 6) |
| Open positions | 4 (complete iron condor, Apr 24 expiry) |
| Live account P&L | -$20 |
| Days in paper validation | 61 of 90 |
| Documented lessons | 122+ |
The $41 win is real. The -$20 live loss is also real — it happened before the MLeg fix was deployed. The paper equity dip from $100K to $95,431 reflects the system being halted for weeks while CI issues were resolved (more lessons there: LL-series in the 250s cover the GitHub Actions workflow failures that caused the halt).
We don't cherry-pick backtests. These are real executions on Alpaca with real fills and real timestamps.
The 90-day paper validation target requires 30+ trades and 80%+ win rate before I'll scale live capital. We're 61 days in. The system is running again.
What I'd Do Differently
Start with MLeg orders from day one. I knew iron condors had four legs. I knew partial fills were a risk. I still started with sequential individual leg orders because it was simpler to implement. It cost real money and a lot of debugging time. The atomic order constraint should have been a hard requirement before the first trade.
Build the lessons-learned system before the first trade. I started documenting lessons retroactively. That means the first ~50 lessons were written from memory after the fact, which means some detail was lost. If I'd built the RAG ingestion pipeline first and made lesson documentation a required step for any workflow failure, the knowledge base would be richer.
Paper trade longer before going live. The $20 live loss was avoidable. I went live before the system was stable because I was impatient. The 90-day paper validation rule I'm now following is a direct response to that impatience.
What's Next
The immediate goal is completing the 90-day paper validation with 30+ trades and an 80%+ win rate. We're on track but not there yet.
After that:
- Managed service for retail traders — run AlphaCondor for you, paper or live, at $49/month. You get the strategy and the self-healing system without managing GitHub Actions or Alpaca API keys.
- Enterprise API for prop firms — multi-account support, custom strategy parameters, white-label option, starting at $499/month.
The open-source repo stays free forever. That's the top of the funnel, and it's also the right thing to do for the algo trading community that's given me so much through shared code and forums.
Get Involved
The repo is at github.com/IgorGanapolsky/trading. The lessons-learned system, the multi-agent consensus layer, and the GitHub Actions workflows are all there.
If you're building something similar — especially if you've hit the partial fill problem on options, or you've tried to build self-healing CI for a trading system — I'd genuinely love to hear about it in the comments. The failure stories are always more useful than the success stories.
Follow along here as I document the rest of the 90-day validation. The next milestone is trade #2.
AlphaCondor AI is an open-source project. Nothing in this article is financial advice. Paper trading results are not indicative of live trading performance.