Safe paper/historical research web app for stocks. This project uses FastAPI, SQLite, and a minimal vanilla JS frontend. It does not connect to any broker and does not place real trades. The canonical production path is /paper-trader.
- Watchlist management
- CSV-based price imports
- Simple signals: 5/20 moving average crossover and 5-day momentum
- Simulated buy/sell trades
- Portfolio tracking for cash, open positions, total value, unrealized PnL, and realized PnL
- Trade history and portfolio snapshot history
- Crypto strategy-lab mode using Upbit public candles, with no exchange keys and no real orders
cp .env.example .env
./start.shOpen http://127.0.0.1:8000.
If you prefer manual setup, create a virtualenv, install requirements.txt, then run uvicorn app.main:app --reload.
Environment variables are optional for local use:
APP_NAMEAPP_ENVAPP_VERSIONAPP_ROOT_PATH(production prefix:/paper-trader)DATABASE_PATHINITIAL_CASH
Production uses app-level authentication backed by auth_users.json and the pt_session cookie. Admin user management is exposed under /paper-trader/users for admin accounts. Keep secrets out of commits.
Health and meta:
GET /healthzGET /healthGET /meta
App API:
GET /api/watchlistPOST /api/watchlistDELETE /api/watchlist/{id}GET /api/prices/{symbol}POST /api/prices/importGET /api/signalsGET /api/signals/{symbol}POST /api/trades/buyPOST /api/trades/sellGET /api/tradesGET /api/portfolioGET /api/portfolio/historyPOST /api/portfolio/reset
The app can import public Upbit candles for crypto paper-trading experiments. This uses public market data only. It does not place real orders and does not require exchange API keys.
curl -X POST http://127.0.0.1:8000/api/crypto/upbit/import \
-H "Content-Type: application/json" \
-d '{"symbol":"KRW-BTC","timeframe":"1h","count":200}'Supported timeframes include 15m, 1h, 4h, and 1d.
The first backtest engine supports ma_cross and rsi_reversion. It includes fee/slippage assumptions and reports return, buy-and-hold return, max drawdown, win rate, profit factor, and recent trades.
curl -X POST http://127.0.0.1:8000/api/backtests/run \
-H "Content-Type: application/json" \
-d '{"symbol":"KRW-BTC","strategy":"ma_cross","initial_cash":100000,"fee_bps":5,"slippage_bps":5}'This is an experiment tool, not investment advice or a profitability guarantee.
The repo includes a sample CSV at sample_data/prices_sample.csv. The app seeds it automatically on first startup, and you can re-import it manually:
curl -X POST http://127.0.0.1:8000/api/prices/import \
-H "Content-Type: application/json" \
-d '{"csv_path":"sample_data/prices_sample.csv"}'CSV schema:
symbol,date,open,high,low,close,volume
- Price import uses repository-relative paths when given a relative
csv_path. - Frontend API calls use relative URLs so the app can sit behind a reverse proxy.
- Set
APP_ROOT_PATH=/paper-traderwhen serving behind Caddy on that prefix. POST /api/portfolio/resetclears simulated trades, positions, and snapshots, but keeps watchlist and imported price data.
This service is managed on Ubuntu with systemd.
sudo systemctl status paper-trader.service
sudo systemctl restart paper-trader.service
journalctl -u paper-trader.service -n 100 --no-pagerExternal base path:
- Canonical:
/paper-trader - Legacy redirects:
/paper-trade/*,/dashboard,/monitor,/users,/loginshould redirect into/paper-trader/...at the reverse proxy layer.
This project is maintained as its own standalone Git repository under:
/service/services/paper_traderTypical workflow:
cd /service/services/paper_trader
git status
git add .
git commit -m "your change"Suggested next product steps:
- improve portfolio UX and empty states
- support additional CSV import flows
- add lightweight tests for trade and signal paths
A read-only monitoring page is available at:
/paper-trader/monitor
It focuses on the bounded stock historical research loop: latest run summary, promoted walk-forward candidates, walk-forward results, and recent backtest runs.
Before deploying recommendation monitor UI changes, run the lightweight card contract check:
python3 scripts/check_monitor_ui_contract.py
node --check static/monitor.jsThe contract check guards the recurring recommendation-card regressions:
duplicated Gate/Risk Gate facts, current price moving back into the price
plan section, broken price-plan section ordering, missing compact-card CSS, and
collapsed stop percentages in the latest recommendation payload.
The 15m forward-test agent records paper signals only. It does not place real orders and does not change paper positions yet.
tools/agents/forward_test_agent.py --symbols KRW-BTC,KRW-ETH --timeframe 15m --count 200Signals are visible in /paper-trader/monitor and via:
GET /api/forward-signals?limit=100
The stock research loop can include Korean equities via yfinance symbols such as:
005930.KS Samsung Electronics
000660.KS SK hynix
035420.KS NAVER
005380.KS Hyundai Motor
068270.KS Celltrion
035720.KS Kakao
051910.KS LG Chem
The root page /paper-trader/ now opens the Stock Research Monitor. The older trading dashboard remains available at /paper-trader/dashboard.
The monitor displays Korean tickers with company names for the default research universe.
Scaffold tools:
tools/agents/import_krx_corp_list.py
tools/agents/opendart_disclosure_agent.pyopendart_disclosure_agent.py requires OPENDART_API_KEY and currently fetches recent disclosure lists only. Disclosure events should be treated as research features, not trading triggers.
To persist recent disclosures:
tools/agents/opendart_disclosure_agent.py --begin 2026-04-29 --end 2026-04-30 --saveStored disclosures are exposed through:
GET /api/disclosures
GET /api/disclosures/features
The monitor uses these as early strategy research features: recent disclosure count, high/medium-risk event count, positive-event count, and latest event per symbol.
The walk-forward agent now reads persisted OpenDART disclosure events with cutoff discipline:
- only disclosures before each cutoff are used for strategy selection/risk gating
- recent high-risk disclosures reject a candidate
- multiple medium-risk disclosures reject a candidate
- positive disclosures are recorded as supporting context, not automatic promotion
This keeps disclosure data as research features while avoiding look-ahead bias.
Run the investment-organization style pipeline:
tools/agents/research_org_run.py --symbols AAPL,MSFT,NVDA,SPY,QQQ,005930.KS,000660.KS,035420.KSOutput:
/tmp/stock_research_org_latest.json
GET /api/research/org/latest
This orchestrates Data Agent, Disclosure Analyst, Strategy Researcher, Skeptic Agent, Risk Manager, Portfolio Manager, and Investment Committee. It produces research recommendations only; no real orders.
tools/agents/universe_scout.py scans symbols already present in price_bars and ranks candidates by momentum, volume surge, 252-day high proximity, and disclosure risk/support.
Use it standalone:
tools/agents/universe_scout.py --limit 20 --exclude-riskOr as the front of the research organization:
tools/agents/research_org_run.py --use-scout --scout-limit 12tools/agents/universe_curator.py maintains universe hygiene. It assigns symbols to:
activewatchquarantineretired
It checks stale price data, insufficient history, zero volume, and high-risk OpenDART disclosures such as trading halt, delisting risk, capital reduction, bankruptcy/rehabilitation, embezzlement, or breach of trust. Universe Scout excludes quarantine and retired symbols.
tools/agents/recommendation_agent.py generates research-only candidate recommendations and target/stop reference levels from active universe members. These are not orders and not financial advice.
tools/agents/recommendation_agent.py --limit 10tools/agents/recommendation_auditor.py validates the recommendation/target logic at historical cutoff dates. It computes entry/target/stop using only data before the cutoff, then judges success/fail/timeout over a future horizon.
The auditor now supports multi-logic comparison (conservative_range_v1, balanced_range_v1, aggressive_range_v1) and monthly cutoffs via --monthly-from / --monthly-step.
Recommendation audit robustness checks now include benchmark excess return, per-symbol concentration, and yearly period stability.
tools/agents/simulation_validation_worker.py continuously consumes untested simulation combinations across symbols, monthly cutoffs, horizons, and recommendation logics. Results are persisted to recommendation_validation_results and exposed through:
GET /api/validation/summary
GET /api/validation/results
This is backlog-driven simulation validation, not daily market monitoring.
tools/agents/strategy_generator_agent.py enumerates strategy candidates. The recommendation auditor now includes baseline range logics plus a generated range_grid_v1 parameter grid across target caps, stop caps, target multipliers, and score thresholds. The simulation validation worker consumes all generated logic names by default, skips completed combinations, and the lifecycle evaluator promotes/demotes strategies from accumulated results.
The production research loop is driven by:
scripts/run_research_org_cron.shIt wraps tools/agents/research_pipeline_agent.py with a flock lock and writes status to:
/tmp/research_pipeline_status.json
/tmp/research_pipeline_latest.json
logs/research_pipeline_cron.log
The loop is historical/paper research only. It must not place real orders.
Core pipeline outputs include:
/tmp/stock_research_latest.json
/tmp/strategy_candidates_latest.json
/tmp/strategy_novelty_pruner_latest.json
/tmp/active_strategy_balancer_latest.json
/tmp/recommendations_latest.json
/tmp/recommendation_audit_latest.json
/tmp/investment_committee_latest.json
/tmp/committee_performance_ledger_latest.json
/tmp/research_org_evaluation_latest.json
/tmp/org_improvement_guardian_latest.json
Routine degraded audit quality is surfaced as warnings, not a service failure. Notify an operator only for actual run failures, consecutive skips/failures, active strategy changes, recommendation anomalies, duplicate/overfit issues, UI/API issues, or concrete non-trivial improvements.
tools/agents/investment_committee_agent.py evaluates recommendations with a fixed evaluator pool:
upside_hunterrisk_guardianevidence_skepticbalanced_allocatorregime_specialist
The committee stores latest output and cautious weight learning state in:
/tmp/investment_committee_latest.json
/tmp/investment_committee_weights.json
/tmp/investment_committee_history.json
Current weight learning is intentionally conservative and based on audit/history proxies until enough future paper outcome data accumulates.
tools/agents/committee_performance_ledger_agent.py connects committee history, recommendation history, and price bars to evaluate future paper outcomes over the recommendation horizon.
Output:
/tmp/committee_performance_ledger_latest.json
Rows can remain pending until enough future bars exist. This is expected.
tools/agents/org_improvement_guardian_agent.py reads organization-evaluator findings and classifies them as:
observemanual_reviewapproval_required- low-risk reversible maintenance auto-apply
Only low-risk reversible maintenance can be auto-applied. Strategy thresholds, evaluator add/remove, pipeline topology changes, external service changes, and destructive changes require explicit approval.
Outputs:
/tmp/org_improvement_guardian_latest.json
/tmp/org_improvement_guardian_history.json
The scheduled research pipeline is a lightweight deterministic multi-agent system. See:
configs/research_agents.yaml— role/mission manifesttools/agents/README.md— implementation conventionstools/agents/research_pipeline_agent.py— cron orchestrator
Safety boundary: historical/paper research only; no real trading/orders.