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.
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证Self-hosted paper-trading robot for US ETFs on Alpaca: connect a paper account → it trades a 60/40 behind hard risk limits → watch it from your phone. Kill switch · volatility target · idle cash in T-bills · walk-forward, deflated Sharpe & overfitting tests · backtests back to 1970 · dashboard + Telegram bot · Docker home server · paper only
翻译准备中
正在加载完整 README。
查看完整 README ↗A trading robot that refuses to fool you. Connect a free Alpaca paper account → it trades a calm stocks-and-bonds portfolio behind hard safety limits → you watch it from your phone, and no "clever" strategy gets a cent until it survives a gauntlet of tests built to catch lucky backtests.
[!WARNING] Paper trading only, on purpose. Live trading is switched off in the code: the app won't start unless
TRADING_MODE=PAPERand it points at Alpaca's paper API. No strategy here has passed out-of-sample validation, so none should be trusted with real money.
Most trading bots you find online come with a gorgeous backtest: a line that goes up and to the right forever. Then they meet the real market. 📉 The backtest was luck, tuned until it looked good.
NOCTURNA is built the other way round. It's a self-hosted robot for one person that trades US ETFs through Alpaca, and it's paranoid by design:
No finance degree required. If you can copy-paste a few commands and use a phone, you can run it.
Every idea below follows its published rules (the six newest had theirs written down before their first backtest) and went through the same tests. The verdicts come from the years the tests held back, January 2020 to October 2026, which a strategy never sees while it is being chosen:
| Idea | Verdict | In plain English |
|---|---|---|
🥧 60/40 stocks and bonds (benchmark) | ✅ Trades on paper, as the yardstick | 6.3% a year, worst drop −12%. Tested back to 1974 on free data: 8.1% a year, worst drop −15%, five kill-switch stops in 52 years |
🔁 Front-running pension rebalancing (rebalancing) | ❌ Rejected | The edge shows up between closes and vanishes by the next morning's open. It would also churn the portfolio about 43 times a year |
📈 Trend following on 12 ETFs (tsmom) | ❌ Rejected | Too weak out of sample |
🪙 Crypto breakouts (crypto-trend) | ❌ Rejected | Too weak out of sample (2024 to 2026: Alpaca's crypto data starts in 2021) |
🧩 The three above, combined (lineup) | ❌ Rejected | Didn't beat the plain 60/40 |
📅 Month-end payment cycle (month-end) | ❌ Rejected | 21.4% in almost seven years, against T-bills' 20.3% |
🌊 Trend filter on the 60/40 (trend-filter) | ❌ Rejected | A tie with the 60/40 since 2017, a little behind it since 1974. It did hit the kill switch only twice in 52 years, against five times |
🐢 Low-volatility funds instead of SPY (low-vol) | ❌ Rejected | 3.6% a year, against the 60/40's 6.3% |
🛢️ Commodities in the 60/40 (commodities) | ❌ Rejected | About level with the 60/40 since 2020 (Sharpe 0.56 against 0.53), with signs of luck in the choice of weight. Since 2001 it fell deeper than the plain 60/40 |
🌍 International stocks in the 60/40 (international) | ❌ Rejected | 5.4% a year since 2020, against the 60/40's 6.3%, and behind it since 1990 too |
🥇 Gold in the 60/40 (gold) | ❌ Rejected | Better since 2020 (7.8% a year, worst drop −11%) because gold quadrupled. But from 1975 to 2000, when gold lost almost two thirds, it earned less per unit of risk than the plain 60/40, and its best gold weight looks overfit |
The 60/40 row is the live portfolio, held at 60/40. The rejected ideas were compared with the 60/40 validated the same way they were, with its stock weight (40–80%) re-chosen each year; that earned about the same, 6.3% a year since 2020 and 8.2% since 1974.
Ten rejections out of ten isn't the robot failing. It's the robot doing its job. 🐺 The numbers, sources and reasoning are in the strategy shortlist and the roadmap.
Sign up at Alpaca and create paper trading API keys. Play money only: NOCTURNA won't start with anything else.
On an Ubuntu machine:
git clone https://github.com/Yog-Sotho/Nocturna-Trading-System.git
cd Nocturna-Trading-System
sudo ./setup.sh --check # only looks: what's installed, and what it would change
sudo ./setup.sh # lists the changes first, then asks before making them
The setup installs what's missing (Docker, Compose, Tailscale) and puts the dashboard on a
private Tailscale address. It writes your .env, asking only for what only you know, like your
Alpaca keys. Then it starts everything, creates your dashboard login and sends a test alert. It
never adds anyone to the docker group, and never touches what Tailscale already serves.
The step-by-step guide, with every setting explained, is HOME_SERVER.md.
Open the dashboard on your phone → Start engine → confirm. Or send /start_engine to your
Telegram bot, then the /confirm code it replies with.
From then on it wakes up each time a new daily bar is final, works out what to hold, and trades at the next opening auction.
Try the real dashboard on a simulated robot with made-up prices. No account, no keys:
git clone https://github.com/Yog-Sotho/Nocturna-Trading-System.git
cd Nocturna-Trading-System
python3 -m venv .venv && source .venv/bin/activate # Python 3.13 or newer
pip install -r requirements.txt
python tools/dashboard/serve_sim.py . /tmp/dashboard.db 5055
Open http://127.0.0.1:5055 and sign in as ops / Harness-Passw0rd!. Start the engine and
one simulated trading day passes every three seconds. The strategy lab runs on the same made-up
prices.
| I want to… | On the dashboard | In Telegram |
|---|---|---|
| see how it's doing | the Account card | /status |
| see what it holds | Positions | /positions |
| start or stop the robot | Start engine / Stop engine | /start_engine / /stop_engine |
| stop trading now, keep my positions | Emergency stop → Keep the positions | /halt |
| stop trading now and sell everything | Emergency stop → Close every position too | /flatten |
| carry on after a halt | Resume after halt | /resume |
| change which strategies trade, and how much each gets | Strategies → Save the mix → Restart now | |
| change a risk limit | Risk → Save the limits | |
| backtest a mix, or validate a strategy | Lab → Run | |
| choose which alerts I get, and where | Alerts | |
| make sure my alerts arrive | Alerts → Send a test alert | |
| change my name, the theme or the accent colour | You |
Every Telegram command that changes something asks for a /confirm <code> first, so a stray
tap does nothing.
On the server (put sudo docker compose exec api in front of each):
| I want to… | Run this |
|---|---|
| check paper trading against a backtest of the same days | flask --app src.main soak report |
| test a strategy idea on Alpaca data | flask --app src.main validate benchmark --start 2017-01-01 |
| test it on free data back to 1970 | flask --app src.main validate benchmark --data history --start 1971-01-01 --end 2026-08-31 |
| make sure my alerts arrive | flask --app src.main send-test-alert |
And on the machine itself, sudo ./setup.sh --check reports what's installed and configured,
without changing anything.
These are the defaults, all as fractions of the account's value. You can change them in .env,
and they apply the same way in backtests and on paper:
| Limit | Default |
|---|---|
| Kill switch: halt and close every position | 10% below the account's peak |
| Shrink exposure gradually as losses grow (down to 25% at the kill switch) | from 5% below the peak |
| Daily loss limit: no new risk for the rest of the day | 3% |
| Volatility target (scales down only, never borrows) | 8% a year |
| Largest position / all crypto together / everything together | 60% / 15% / 100% |
| Largest single order (fat-finger guard) | 60% |
| Limit prices more than this far from the last close are refused | 5% |
| The same order twice within 5 minutes | refused |
| Orders / turnover per day | 50 / 2× the account |
| Market data older than / a one-bar jump bigger than | stocks 5 days / 25%, crypto 2 days / 50% |
| Positions are adjusted only when this far from target | 2% |
| Idle cash | in T-bills (BIL), less a 0.5% buffer |
Orders that only shrink a position are always allowed, so the book can always be reduced. A halt is saved in the database, so it survives a restart. The fine print is in the technical reference.
Run flask validate on any idea, or press Run in the dashboard's Lab, and the robot puts
it through this gauntlet, with the same engine, costs and safety limits as paper trading:
| Test | What it catches | To pass |
|---|---|---|
| Walk-forward | Ideas that only work on the data they were tuned on | Return per unit of risk above T-bills (Sharpe) of at least 1.0, on years held back |
| Deflated Sharpe | A "best result" that is just the luckiest of many tries | At least 0.95 |
| Overfitting test | Settings that look best in-sample and flop out of sample | At most a 20% chance |
| Triple costs | An edge that trading costs would eat | Still beats T-bills |
| Neighbours | A lucky spot in the settings | Nearby settings at least half as good |
| Worst drop | Risk beyond the safety limits | No more than 12% |
| Paper soak | A backtest the real market doesn't confirm | 60 trading days tracking the backtest |
It writes a full report and ends with its verdict: rejected, naming the tests it failed, or eligible for paper trading. The dashboard shows each strategy's verdict next to it, and you can still paper-trade a rejected one once you confirm you read why. How each test works is in the technical reference.
Everything runs on your own machine. Your Alpaca keys live in your .env and go only to Alpaca.
With the home-server setup, the dashboard is reachable only through your private Tailscale
network, and it has one operator account: yours, with public sign-ups switched off. The free long-history data (Kenneth French's
data library and FRED) is downloaded only when you ask for it.
Already available:
Coming next:
Read the guides as a website at yog-sotho.github.io/Nocturna-Trading-System, or right here:
| Guide | What you'll find |
|---|---|
| Technical reference | Every component, the API, every setting, development and CI |
| Architecture | How the engine, risk gate, sizing and validation fit together |
| Home server | Setting up the machine, step by step |
| Runbook | Alerts, upgrades, incidents, backups and restores |
| Roadmap | The plan, the decisions and the promotion gates |
| Strategy shortlist | Every idea tested, its sources, its rules and its result |
Contributions welcome! Fork the repo, make your change, and run the checks before opening a pull request:
pip install --require-hashes -r requirements-dev.txt
pytest src/tests && ruff check src/ tools/ setup/ && mypy
Found a bug? Open an issue with what you ran and the full error message.
MIT: free to use, change and share. See LICENSE.
This software is provided as-is, without warranty. Trading involves a substantial risk of loss. Nothing in this repository is investment advice.
Made with ❤️, caffeine and a healthy distrust of backtests.
If NOCTURNA saved you from a strategy that only worked on paper, a ⭐ on GitHub means the world. Yog-Sotho