How I built a fully automated, rule-based Japanese stock research pipeline with Claude Code, J-Quants, and a macOS cron job — and why "the AI is not allowed to predict anything" turned out to be the most useful constraint in the whole project.
Everyone's first idea for "AI + stocks" is the same: ask the model whether a stock will go up. That idea has been tested, and it fails — LLMs guessing post-event price direction land around coin-flip accuracy. I'm a freelance web developer in Japan, not a quant, and I knew that if I let an LLM "predict" anything I would just be laundering my own wishful thinking through a chatbot.
So I gave Claude Code a different job description. In my project's CLAUDE.md — the standing instructions file the agent reads every session — there's a hard rule:
No predictions. Data only. The AI's role is limited to four things: (1) structuring data, (2) computing factual metrics, (3) checking facts against pre-defined rules, (4) explaining results with sources. Trading decisions are rule-based. Overfitting to past data is a hidden prediction — prefer economically sensible rules.
Claude Code's job was to be the engineer: build the data pipeline, implement backtests I specified, and then — this is the important part — kill my ideas with evidence. Over a few weeks it killed three of them. Here's the honest record.
The stack
Nothing exotic. The point was cheap and reproducible:
-
J-Quants API — the official Japan Exchange data service. The free personal tier has a 12-week delay on prices, which is fine for backtesting; I fill in current prices with
yfinancefor live tracking. - Python + pandas for backtests, with a local file cache so repeated runs don't hammer the API. (One lesson learned: cache empty responses too. Half my early runtime was re-requesting data that legitimately didn't exist.)
- Claude Code as the pair engineer. I describe the rule, it writes the backtest, I interrogate the output.
- launchd (macOS's cron) for the daily tracking job. No cloud, no server bill.
Round 1: technical strategies — dead
First I had Claude Code implement the classics on Nikkei-universe data: breakout entries, RSI mean-reversion, with realistic costs and taxes included.
Ten-year portfolio result: +15.4% total. Sounds okay until you put it next to the benchmark: buy-and-holding the Nikkei over the same period returned +138.8% (with a −26% max drawdown). My "strategy" wasn't a strategy; it was an expensive way to sit out a bull market.
Verdict: dead. Next.
Round 2: earnings drift (PEAD) — killed by a single filter
Post-earnings announcement drift is one of the best-documented anomalies: stocks that beat guidance keep drifting up for weeks. On large caps, my backtest found nothing — no monotonicity across surprise sizes, slightly negative drift everywhere. Institutional money has eaten that edge.
Small caps looked genuinely exciting: 276 stocks, 1,385 earnings events, 423 trades. The threshold sweep was beautifully monotonic — bigger guidance beats, bigger drift: +1.6% → +2.1% → +2.7% → +3.4% per trade as the threshold rose.
Then I asked Claude Code to add one boring, adult filter: only keep stocks with at least ¥100M in daily trading value — i.e., stocks I could actually buy without moving the price.
The edge evaporated: −0.34% per trade on the 33 surviving trades. At ¥300M/day it got worse (−2.48%).
The anomaly was real, but it lives exclusively in stocks too illiquid to trade at size. This was the single most valuable chart the pipeline ever produced, and it's a negative result. If your backtest doesn't include an executability filter, it isn't a backtest — it's fan fiction.
Round 3: small-cap value × quality — the survivor
Third idea: a quarterly-rebalanced screen. Rank small caps by valuation (with quality guards: equity ratio ≥ 25%, positive forecast EPS, and a filter against one-off earnings spikes), split into quintiles, hold the cheapest.
This one behaved differently. The cheapest quintile (Q1) returned +8.89% per quarter, returns were fully monotonic down the ranks, the cheap-vs-expensive spread was +8.96% per quarter, and Q1 beat the universe average in all six backtest quarters.
And the crucial difference from PEAD: the edge survived the liquidity filter. Restricted to ≥¥100M/day stocks, Q1 still returned +10.06% per quarter (+78.7% cumulative vs +42.2% for the universe). The sign didn't flip.
One strategy out of three survived its own audit. That's the pipeline working as intended.
Automating the paper trade
A backtest that survives in-sample is still just a hypothesis, so the surviving strategy went into "Phase 0": a paper portfolio tracked automatically every trading day, with a pre-committed bar to clear — beat the benchmark over 2–3 months without touching the parameters — before real money scales beyond pocket change.
The automation is deliberately low-tech. A launchd job fires at 15:45 JST on weekdays (after the Tokyo close) and runs a shell script:
#!/bin/zsh
# Runs from launchd on weekdays at 15:45 JST.
# 1) Record any not-yet-entered positions at today's opening price
# 2) Append the daily report to the log, push a summary to macOS notifications
REPO="$HOME/work/makemoney"
PY="$REPO/.venv/bin/python"
{
echo "===== $(date +%F) ====="
"$PY" phase0_track.py open --date "$(date +%F)"
report=$("$PY" phase0_track.py report)
echo "$report"
} >> "$REPO/data/phase0_daily.log"
summary=$(echo "$report" | grep "Portfolio" | head -1)
osascript -e "display notification \"$summary\" with title \"Phase 0\""
The Python side is a small CLI with two subcommands. open records entries at the day's opening price (same conditions as the fractional-share "opening auction" orders I'd use with real money) with an idempotency guard, so re-running never double-records:
# double-entry guard
existing = {p["ticker"] for p in journal.open_positions()
if p["strategy"] == STRATEGY}
lst = lst[~lst["ticker"].isin(existing)]
report marks the portfolio to market and compares it against two benchmarks (Nikkei 225 and a TOPIX ETF) measured from the same entry date — the comparison that Round 1 taught me never to skip. The result lands in a log file and a macOS notification. Total infrastructure cost: ¥0.
One month in: the honest numbers
As of 2026-08-14, one month after entry (18 positions, a small paper portfolio):
- Portfolio: +4.32%
- Nikkei 225: +2.55%
- TOPIX ETF: +4.66%
Beating the Nikkei, narrowly losing to TOPIX. Inside the portfolio the dispersion is exactly what a small-cap quintile bet looks like: the best position is +46%, the worst is −33%. The daily report ends with a line I wrote for my own discipline: "The pass bar is 2–3 months against the benchmark. Do not react to daily noise."
Maybe it clears the bar in October. Maybe it doesn't and the strategy joins the graveyard with the other three. Either outcome is the system working.
What Claude Code was actually good at
A few workflow notes for anyone trying something similar:
-
Put your constraints in
CLAUDE.md, not in your willpower. "No predictions", "never commit to main", "every research claim needs a source URL" — encoded as standing project rules, the agent enforces them even when I'm tempted not to. - Make the agent a strategy killer, not a strategy generator. LLMs are dangerously good at producing plausible-sounding trading ideas. They are actually useful at implementing the boring falsification machinery — cost models, liquidity filters, benchmark alignment — fast enough that you'll really run it.
- Commit each negative result. My git log reads like a lab notebook: "large-cap PEAD: no edge", "PEAD edge concentrates in illiquid names", "value×quality survives liquidity filter". Claude Code picks up this context every session and stops me from re-testing yesterday's dead idea with today's optimism.
- The agent writes the pipeline; the human owns the rules. Every threshold in the system (quality guards, liquidity floor, rebalance cadence) was decided by me and frozen before the evaluation window. The fastest way to fool yourself with an eager coding agent is to let it "just try a few more parameter values."
Nothing here is investment advice — it's a build log of a personal research tool, running on pocket-money stakes precisely because the evidence isn't in yet. J-Quants data is used under its personal-use license.
Top comments (0)