Open Net Worth
Open Net Worth는 직접 운영하는 오픈소스 개인 AI 자산 비서로, 전체 재무를 하나의 원장에 기록하고 사용자가 제어하는 AI가 관리
Alpaca에서 미국 ETF를 대상으로 자체 호스팅하는 모의거래 로봇, 강력한 위험 제한과 검증 절차 제공
A self-hosted paper-trading robot for US ETFs on Alpaca, with hard risk limits and a validation gauntlet that catches lucky backtests.
금융Python · JavaScriptGitHub @Yog-Sotho온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기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