Open Net Worth
Open Net Worth is an open-source personal AI wealth assistant you run yourself: one ledger for your whole financial life, worked by an AI agent you control.
翻訳待ち
No updates detected yet. GitHub releases will appear here.
Open-source quantamental portfolio engine: multi-factor screening, LLM agent research, and IPS-driven rebalancing proposals. It ranks, argues, and proposes — it never places an order.
翻訳待ち
Loading the full README. The saved excerpt is shown below.
TradingAgents for your whole portfolio — run it on your holdings daily, not one ticker once.
▶ Live demo — real screener, proposal
and backtest output from one session, frozen into a single static page.
Named after the Kuroshio — the Pacific current that carries apex predators past Taiwan.
Kuroshio is an open-source quantamental portfolio engine:Read the full README ↗Named after the Kuroshio — the Pacific current that carries apex predators past Taiwan.
Kuroshio is an open-source quantamental portfolio engine:
Status: 0.x. Usable today; the API can still change between minor versions.
pip install -e ".[yfinance,dev]"
# rank breakout candidates (zero API keys — yfinance default)
kuroshio screen --market us --tickers NVDA,AMD,AVGO,MSFT,META,LLY,XOM,JPM,COST,NFLX
# check a policy file
kuroshio ips-validate examples/ips-balanced.md
# proposal cards: your holdings vs challengers, governed by your IPS
printf -- '- {ticker: AAPL, weight: 0.08, theme: tech, score: 0.55}\n- {ticker: XOM, weight: 0.22, theme: energy, score: 0.30}\n' > holdings.yml
kuroshio propose --ips examples/ips-balanced.md --holdings holdings.yml --market us
us is 12-1 momentum since 2026-09 (the less bad of the two US screens — neither beats SPY out of
sample, see docs/backtest-2026-09.md); the
previous MA200/breakout leadership screen is still there as --market us-leadership.
score: is optional (same for final_score: in a candidates file) — leave it out and propose
fetches prices from the market's provider and fills it in. Hand-typed values always win, per name.
An auto-filled score is a percentile rank within the names in your own files, not the number
kuroshio screen prints for that name: the pool is your holdings + candidates, not a universe,
and, for us-leadership, propose passes no --sector-map/--asof. Pass --universe-file members.csv (a newline ticker list, or a date,tickers snapshot from scripts/sp500_members.py)
to rank against that index instead, and the card discloses it did. Two things follow. A pool small enough that
your turnover hurdle may not be doing any work gets nothing filled at all — you get the "no
holding has a screener score" ALERT instead of a made-up gap. (That floor is a deliberately
conservative rule of thumb, not an exact cutoff: it will sometimes refuse a pool your hurdle
could have judged fine. Hand-type the scores, or list more names.) Above that size the score is
filled, but percentile ranks pin their extremes to 0.000 and 1.000 however tightly the names
cluster, so every card built from one says which scores were filled and how many names they were
ranked against — and, when the other side of the swap is hand-typed, that the gap spans two
different scales.
Record why you own a position — setup_type: (value_dip | pullback_add | trend_add |
other) plus entry_price: and, for the dip setups, invalidation_price: — and propose
watches each one on its own terms: a trend_add is alerted when it closes more than 3% under
its 50-day mean, a value_dip never is (looking weak against its MAs is the setup) and is alerted only
when it closes at or below the invalidation price you recorded. Add an entry_date: and the
stop moves with the tape: a trend_add — and a pullback_add once its running high has
cleared entry + 2R — trails its invalidation price at caps.trail_atr_mult ATR14s below the
running high since entry (default 3), never downward. On top of that, one rule
reads no setup at all: a position whose worst close since entry_date is at or past
caps.max_adverse_excursion_pct from its entry (default -15%) gets a DECIDE card — kill it, add to it per the plan, or rewrite the thesis.
A position through the exit level it was opened with (its invalidation price, the ratcheted
stop, or a trend_add's 50-day line) gets an EXIT card instead: the plan was written in
advance, so the card says sell it all rather than asking.
Holding it unchanged is not one of the three. Positions missing the fields a rule reads are
named on a card instead of quietly going unwatched. This applies only to a session that
actually fetched prices: a book with no monitored setup_type, no entry_price, and no
caps.book_vol_target_pct needs no price and never fetches one, so it is not "blind" — no
missing-price ALERT, exit 0. When a fetch did happen and a held position came back with no
price (a rate limit, say), that is not "unwatched by design" either — it gets its own
missing-price ALERT naming it, separate from the coverage line, and if every held position
came back unpriced the run was blind: propose exits 3 rather than 0 (cards still print;
--help documents the exit codes, 2 being an invalid IPS or a bad holdings/candidates/
universe/provider input). Every ratchet move is appended to the stop ledger so evaluate
can score the stop that was live on a date, not just the final one; --no-ledger (like
screen and research) still reads that ledger for the never-lower rule but appends
nothing, and says on stderr how many moves went unrecorded — for a dry run, a probe, or the
desk previewing a book without committing state.
A held name's newest kuroshio research rating is a veto, never a ranking input: rating
hit rate is unmeasured until evaluate has 60+ sessions, so it never enters the swap
hurdle or the verdict floor that judge challengers. Instead, a rating at or below
Underweight forces the same three-way DECIDE as MAE — kill it, rewrite the thesis, or hold
with a written reason — quoting the rating, its date, source and model, and the stop the
report gave. It reads no price, so a holding with no session price still gets the card; a
rating an earnings print has since voided (the same rule kuroshio book applies) does not
count; and when MAE also fires on the same name this run, the two fold into one card
rather than two.
This is the beginning of the README. Read the rest at the original. Read the full README ↗