How to Build Your Own AI Fund House: A Step-by-Step Guide to a Multi-Agent Paper-Trading Fund

What if you could run your own small investment firm, with a macro strategist, a sector analyst, a fundamental analyst, a technical analyst, a risk manager and a fund manager, and every one of them worked for you around the clock?
That is what I set out to build: a multi-agent "fund house" for my own portfolio research. AI agents play the analyst roles, ordinary Python code enforces the risk rules, and I keep the final say on every trade. It runs on an Interactive Brokers (IBKR) paper account, so no real money is at risk while I learn.
This post walks through how to build one yourself: the design, the components, the decision logic, and the bugs I hit along the way.
Important: This is an educational project on a paper (simulated) account. It is not financial advice, and nothing here is a recommendation to buy or sell any security.
What you are building (and what you are not)
You are building a system that:
Screens a universe of stocks every day with a transparent, rules-based score.
Holds a weekly investment committee of AI agents that reads a research pack, searches the news and proposes a target portfolio with written reasons.
Checks every proposal against hard risk limits written in code, not in prompts.
Waits for you to approve before it trades.
Watches the market during trading hours and cuts risk automatically if there is a crash.
Shows everything in a dashboard with a full audit trail.
You are not building a high-frequency bot or a "money printer". The style is mid-to-long-term: holdings are kept for months, and "no change this week" is a perfectly good outcome.
The one design principle that matters most
Split every decision between three kinds of decision-maker, and never let one do another's job:
Deterministic code: Measures, screens, enforces limits, places orders. Predictable, testable, backtestable. An AI can argue with a prompt; it cannot argue with an if statement.
AI committee: Judgement: reads results, news and context; picks names and weights. Good at reading and reasoning, but must never be trusted with the order ticket.
You: Approve or reject each proposal; own the risk limits. Accountability stays with a human.
A fourth category runs without asking anyone: automatic risk actions (stop-losses, drawdown limits, crash rules), because waiting for a human during a crash costs money.

What you need
Broker: Interactive Brokers paper account + IB Gateway. Free paper account. The Gateway runs on your computer and exposes an API on a local port.
Always-on computer: A Mac with launchd jobs. Any machine that stays on during US market hours works (Linux cron, a small VPS).
Language: Python 3 with ib_async, pandas, yfinance. ib_async talks to IB Gateway.
AI agents: Claude (scheduled tasks running in my project folder). Any capable LLM with web search and file access can play the committee roles.
Data (free): SEC EDGAR (fundamentals), FRED (macro), IBKR / Yahoo Finance (daily prices). Free sources are enough for a weekly, long-horizon fund.
Dashboard: A small local Python web server + HTML. Optional, but you will want it.
Step 1: Write the mandate before you write any code
The mandate is the fund's constitution. Every agent reads it on every run, and every limit in it is enforced by code. Mine looks like this:
Market: US single stocks from the S&P 500. No ETFs (my IBKR entity cannot buy US ETFs), no shorts, no options, no leverage.
Style: mid-to-long term; weekly review; holding period of months.
Hard risk limit: maximum drawdown of 20% from the high-water mark.
Portfolio: 10–15 names when invested, 4–10% per stock, max 30% per sector, at least 2% cash, one-way turnover ≤ 40% a month.
Objective: beat a simple quant benchmark (the "shadow portfolio") and an equal-weight index after costs, without breaching the drawdown limit.
Put every number in one configuration file that only you edit. The agents can read the limits; they can never change them.
@dataclass
class Config:
starting_capital: float = 200_000.0
max_drawdown_mandate: float = 0.20
min_weight: float = 0.04
max_weight: float = 0.10
max_sector_weight: float = 0.30
cash_buffer: float = 0.02
candidate_pool: int = 40 # agents may only pick from the top-40 screen
dd_ladder = [(-0.08, 0.75), (-0.12, 0.50), (-0.15, 0.00)]
regime_caps = {"risk_on": 1.00, "neutral": 0.70, "risk_off": 0.30}
stop_loss_from_entry: float = 0.15
trailing_stop_from_peak: float = 0.25
Lesson learned: pick a realistic capital size. I started with US$10,000. With 10–15 names at 4–10% each, a single share of a $1,200 stock was already 12% of the fund, so the portfolio could never match its target weights and every rebalance was a rounding exercise. I restarted the paper fund at US$200,000, which made position sizing work. Before you choose a number, check how close you can get to your target weights when you can only buy whole shares.
Step 2: Build the data pipeline (and assume it will break)
Every trading day, after the US close, a job:
Downloads daily price bars for the universe.
Refreshes fundamentals from SEC EDGAR (weekly is enough).
Pulls macro series from FRED: Treasury yields, credit spreads, VIX, jobless claims, CPI, oil, the dollar.
Marks the fund to market, checks stops and the drawdown ladder.
Runs the screen and the market regime.
Writes a research pack (pack.md + pack.json) for the committee.
Three things went wrong for me, and each one taught me a rule:
The broker's data feed went down. One week IBKR's US historical-data service stopped answering, and every price request timed out. The morning job ran for five hours and saved nothing. Rule: add a fallback source (I switch to Yahoo Finance after a few failures in a row) and a short timeout.
Stale data looked like fresh data. The research pack was still built, but from prices that were three days old. Rule: add a freshness check. If more than 20% of stocks have no price for the report date, the pack is stamped "DATA STALE — do not decide", and the committee refuses to run.
A silent fundamentals bug. Companies change the labels (XBRL tags) they use for revenue in SEC filings. My code took the first label it found, which for 155 of 500 companies was one they had stopped using years earlier. Their revenue growth came out as exactly 0%, and one company showed a 125% operating margin. Rule: merge all candidate labels and let the most recent figure win, and sanity-check outputs (a margin above 100% is always a bug).
Use point-in-time fundamentals (by filing date) so your backtests do not peek into the future.
Step 3: The quant screen: who is allowed to be bought
The agents do not pick from the whole market. Code ranks every eligible stock, and the agents may only choose from the top 40.
Eligibility: price ≥ $10 and 20-day average traded value ≥ $50 million.
Score: each measure is a percentile rank against all other eligible stocks, so the score is relative:
Momentum (35%): 12-month return skipping the last month; 6-month return.
Quality (25%): Return on equity, operating margin, free-cash-flow yield, low debt, no dilution.
Trend (20%): Above the 200-day average; 50-day above 200-day; distance from the 52-week high.
Growth (20%): Revenue growth, earnings growth.
The most volatile 10% of stocks lose 0.10 points, so a wild chart cannot ride momentum alone into the pool.
A reader of my dashboard asked why famous names like Google or Tesla were not in the top 40. The answer is that the screen does not care about size or fame. A great business with a flat six months simply ranks in the middle of the pack. This is a feature: it stops the committee from buying whatever is in the headlines.
The shadow portfolio. Take the top 15 names, equal-weight them (max 4 per sector) and track them every day. This is the benchmark your AI committee has to beat. If the agents cannot explain why their picks are better than the screen's, they should just hold the screen's picks.
Step 4: Decide how much to invest: the market regime
Before deciding what to own, decide how much to own. A simple, backtestable points system sets the maximum exposure each day:
Equal-weight index of your universe above its 10-month average: +2 / −2
Breadth: share of stocks above their 200-day average: > 60%: +1 · < 40%: −1
High-yield credit spread widened ≥ 1 point in 3 months: −1
VIX above 30: −1
2 points or more → Risk on: up to 100% invested.
−1 to 1 point → Neutral: up to 70% invested.
−2 points or less → Risk off: up to 30% invested.
The 10-month trend rule is the backbone. In my stress test on S&P 500 monthly data since 1950, a trend filter like this kept the worst drawdown at roughly 20%, but only on a monthly timescale. Fast crashes happen inside a month, which is why you also need the sentinel in Step 7.
The committee may always invest less than the cap. It can never invest more.
Step 5: The AI investment committee
This is the fun part. Each week (I use Saturday morning), a scheduled AI task reads the research pack and runs eight roles in a fixed order:
Four analysts, in parallel and independently:
Macro strategist: cycle phase, rates, inflation, upcoming events; recommends a maximum exposure at or below the code's cap.
Sector analyst: sector leadership and breadth; hidden themes that span sectors (for example, an "oil shock" theme across energy and insurers).
Fundamental analyst: latest results, guidance and red flags for each candidate; rates Buy / Hold / Avoid; finds the next earnings date.
Technical analyst: trend strength and entry timing (buy now / wait / avoid).
Portfolio strategist: sets the shape: target exposure, sector tilts, theme caps, number of names, how fast to deploy.
Portfolio manager: picks names and weights within that shape, and explains every difference from the shadow portfolio.
Risk manager: runs the code checker, adds judgement (earnings in the next 10 days, concentration, a −10% market scenario). It has a veto; the PM gets one revision.
Fund manager (CIO): makes the final call (deploy / rebalance / hold / de-risk) and writes a one-page memo for you.

Rules that make the committee useful instead of dangerous
Give every agent the same charter, with these rules of evidence:
Every number must come from the research pack or a cited URL. Never from the model's memory, because training data is stale.
If something cannot be verified, say "unverified", and it cannot drive a decision.
Separate facts, interpretation and opinion.
For every stock you favour, write the bear case and what would prove you wrong.
No price targets. Rank relative attractiveness and risk.
Analysts work independently, so they cannot anchor on each other.
Bias to inaction: replacing a holding requires the new name to be clearly better.
Make the output machine-readable
The committee's final product is a JSON file that code can check:
{
"asof": "2026-10-16",
"status": "proposed",
"decision": "initial_deployment",
"target_gross_exposure": 0.54,
"holdings": [
{"symbol": "XYZ", "weight": 0.06, "conviction": "high",
"thesis": "…", "bear_case": "…", "review_trigger": "…"}
],
"deviations_from_shadow": "…"
}
A real example
My first committee for the US$200k period had a 100% exposure cap from the code. The agents chose to invest only 54% across 11 names. They cited a cluster of central-bank, big-tech earnings and GDP events within two weeks, plus the fact that most candidates were about to report results. The risk manager also cut two positions to half size because both reported earnings within 10 days. The plan was to add the rest in stages after those events. A pure rules-based system would not have made that judgement.
Step 6: The code risk check: the veto that cannot be argued with
Every proposal passes through the same code check three times: when the risk manager runs it, when you approve, and again at execution with live prices.
Stock is in the universe (no ETFs, no unknown tickers) → Dropped
In the top 40, or already held → Dropped; a held stock outside the pool cannot be added to
Weight between 4% and 10% → Clipped, or removed if below 4%
Sector ≤ 30% → That sector scaled down
Total invested ≤ the lowest of regime cap, drawdown cap, 98%, and the committee's own target → Every weight scaled down
New buys frozen by the crash sentinel → No weight may increase
Fund halted (kill switch or hard floor) → Only sells allowed
No single order above a fat-finger limit (15% of capital) → Whole rebalance blocked, alert sent
10–15 names; monthly turnover ≤ 40% → Warning shown to you
Step 7: Protect the downside automatically
These rules act without approval:
Per-position stops
Close 15% below the average buy price → sold at the next session.
Once in profit, close 25% below its highest close → sold.
A standing GTC stop order at the broker, 20% below entry, which still works if your computer is off.
Drawdown ladder (fund value vs its high-water mark)
0% to −8%: up to 100% invested (within the regime cap)
From −8%: up to 75% invested
From −12%: up to 50% invested
From −15%: 0%. The fund halts and stays in cash until you restart it
The ladder stops at −15%, which leaves a 5-point buffer before the 20% mandate for overnight gaps and slippage.
Crash sentinel. A small program checks every five minutes during US hours, with no AI in the loop, because it must be fast, cheap and predictable:
Shock (market proxy down 3% in a day, or VIX ≥ 30) → new buys frozen for about three sessions.
Crash (down 5% in a day, 8% over three days, or VIX ≥ 40) → exposure cut to 50% immediately, every holding sold pro-rata (no panicked judgement about which stock is "weakest").
Kill switch: create a file named STOP_TRADING and the fund halts.

Step 8: Approval and execution
When the committee finishes, the proposal appears in the dashboard's Approval tab, with the code's checks and an order preview. You type APPROVE (or reject with a note), and the decision is written to an audit log.
What happens next:
If you approve during US market hours (I use 09:40–15:40 New York time to avoid the opening auction and the close), orders go out within a minute.
Otherwise they go at the next scheduled run, just after the next US open.
Orders are re-sized with live prices, sells first so they fund the buys, and whole shares only.
I use IBKR Adaptive market orders. A free paper account only gets 15-minute-delayed quotes, and limit orders priced from delayed data were repeatedly rejected by the broker's price-protection rules. If an adaptive order is still rejected, it is re-sent as a plain market order.
Unfilled approved orders are retried for up to three sessions, then the committee sees the gap.
A lock file prevents two runs from overlapping, and an approval is never traded twice in a day.

Step 9: Put it on a schedule
Here is my weekly rhythm (Hong Kong time, since I am in Asia):
Tue–Sat 06:30: Data update after each US close; research pack built
Tue–Sat 07:22: Daily risk brief (AI): health, drawdown, automatic actions, material news; sends me a notification
Saturday 09:47: Weekly investment committee (AI)
Any time: My approval
Mon–Fri 21:40 (retry 22:40): Execution after the US open: risk exits first, then de-risking, then the approved rebalance
US hours, every 5 min: Crash sentinel
The deterministic jobs run as launchd services on my Mac. The AI jobs run as scheduled tasks that work directly in the fund folder. Odd minutes (09:47 rather than 10:00) avoid the rush of jobs that everyone else schedules on the hour.
Step 10: Build a dashboard and an audit trail
You will not trust a system you cannot see. My dashboard shows:
Overview: account value, fund vs shadow portfolio vs equal-weight benchmark, holdings with current price and valuation, alerts, and a refresh button.
Approval: the pending proposal and its checks.
Committee & agents: every report from every role, every week.
Market & screen: regime, sectors, the top-40 pool.
Audit trail: every decision, fill and alert with a timestamp.
How it works: a slide deck explaining the whole mechanism. Its numbers are read live from the config file, so it never goes out of date.
Backtest what you can, and be honest about what you cannot
You can backtest the code: the screen, the regime filter, the drawdown ladder and the stops. Do it before trusting anything.
You cannot honestly backtest the AI committee. The model has already "read the future", so any historical test of its judgement is contaminated. Judge it live, against the shadow portfolio, over many months.
Survivorship bias: testing on today's index members flatters results, because the companies that failed are missing. Treat backtest numbers as an upper bound.
Paper fills are kinder than live fills. Expect worse in a real market.
Lessons learned
Hard limits belong in code, not prompts. Agents are excellent analysts and unreliable gatekeepers.
Make every agent cite its sources, and make "unverified" an acceptable answer.
Expect data to fail and design for it: fallbacks, timeouts, freshness checks.
Sanity-check the outputs, not just the code. A 125% margin or a benchmark that jumps 4× overnight is a bug, not an insight.
Size the fund realistically for whole-share trading.
Delayed quotes and limit orders do not mix. Use broker algos or market orders on a paper account.
Keep a human in the loop, but do not make the human the only line of defence during a crash.
"No trade" is a valid decision. Bias the committee toward inaction and make it justify every change.
Where to go next
Once one fund is stable, the same platform can run more. I am now adding a Hong Kong equity fund on the same broker connection, codebase and dashboard. It uses its own capital, its own tighter risk limits, and Hong Kong board-lot rules (shares must be bought in fixed lot sizes, which changes the capital-sizing maths again).
If you build your own, start small: a mandate, a screen, a risk checker and a paper account. Add the agents last.
Disclaimer
This article describes a personal, educational project that trades a paper (simulated) account. It is not investment, financial, legal or tax advice, and it is not a recommendation to buy or sell any security or to use any particular strategy. Past or simulated performance does not guarantee future results. Automated trading carries significant risks, including software errors, data errors and broker outages. If you adapt any of these ideas for real money, do your own research and consider speaking to a licensed financial adviser.



Comments