Oracle3
Oracle3: 예측시장 거래 엔진·MCP 서버
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A multi-user MCP server and web dashboard for tracking your Angel One portfolio, with AI-powered daily briefings, a trading agent, and ML-based price predictions.README 전문 보기 ↗A multi-user MCP server and web dashboard for tracking your Angel One portfolio, with AI-powered daily briefings, a trading agent, and calibrated ML price predictions.
OPENROUTER_API_KEY on the serverservices/marketstore). Carries delivery percentage, which the broker candle API does not expose/api/admin/*) for registering premium users with Fernet-encrypted Angel credentials and managing their schedules├── main.py # Combined entry point (MCP + Web on one port)
├── mcp_server.py # MCP FastMCP app (tools: login, portfolio, trading)
├── web_app.py # FastAPI web dashboard + briefing schedule API
├── angel_client.py # Angel One SmartAPI client wrapper
├── session_manager.py # Per-session client management (in-memory)
│
├── db/ # Postgres layer (SQLModel + Neon)
│ ├── engine.py # Engine, init_db, additive column migrations
│ ├── models.py # User, Schedule, Log tables
│ ├── crypto.py # Fernet encrypt/decrypt for stored credentials
│ └── __init__.py
│
├── adminApi/ # Admin REST API (X-Admin-Key protected)
│ ├── router.py # /api/admin/users, /api/admin/schedules, /api/admin/logs
│ └── __init__.py
│
├── agents/ # Google ADK agents
│ ├── factory.py # LlmAgent builder (OpenRouter via LiteLLM)
│ ├── finance/ # Finance research agent
│ └── trading/ # Proposal-based trading agent
│
├── services/ # Domain logic
│ ├── schedular/ # Scheduler pipeline
│ │ ├── runner.py # APScheduler tick (every 1 min)
│ │ ├── repository.py # DB claim / finish (FOR UPDATE SKIP LOCKED)
│ │ ├── job_executor.py # Per-schedule pipeline (session → briefing → WhatsApp)
│ │ ├── daily_briefing.py # LLM briefing generation (holdings + candles + model)
│ │ └── whatsapp.py # WhatsApp delivery via webhook
│ ├── marketstore/ # Local NSE history + point-in-time store
│ │ ├── bhavcopy.py # One day of the whole market, normalised
│ │ ├── store.py # Parquet, partitioned by year, idempotent
│ │ ├── backfill.py # Resumable CLI backfill
│ │ ├── universe.py # 451 names with cap band and NSE industry
│ │ └── pit.py # Point-in-time fundamental snapshots
│ ├── features/ # The versioned feature contract
│ │ ├── contract.py # Ordered feature list + interval guards
│ │ ├── technical.py # Indicators, vectorised across the panel
│ │ ├── cross_sectional.py # Ranks within universe and industry
│ │ ├── market_context.py # Market and industry context
│ │ ├── flow.py # Delivery % and turnover
│ │ ├── fundamental.py # Scores read through the PIT store
│ │ └── sentiment.py # FinBERT aggregates from the news archive
│ ├── compliance/ # Mandate gate over a dated ruleset
│ ├── prediction_service.py # Registry-backed, interval-strict predictions
│ ├── decision_record.py # Append-only audit trail for trades
│ ├── peer_service.py # Valuation percentiles vs industry peers
│ ├── estimates_service.py # Consensus, revision momentum, surprises
│ ├── ownership_service.py # Delivery, deals, insiders (pluggable source)
│ ├── ai_service.py # OpenRouter AI helpers
│ ├── technical_service.py # RSI, MACD, Bollinger, SMA, volume signals
│ ├── fundamental_service.py # Fundamental data
│ ├── sentiment_service.py # News sentiment scoring
│ ├── sector_service.py # Sectoral analysis
│ ├── news_service.py # News fetching
│ ├── risk_profile.py # User risk profile
│ ├── trade_proposals.py # Proposal store + risk and compliance gates
│ └── realtime_feed.py # Real-time price feed
│
├── integrations/ # Adapters bridging sessions ↔ MCP / local accounts
├── models/ # Labelling, validation, registry, training
│ ├── labeling.py # Triple barriers, CUSUM events, weights
│ ├── validation.py # Purged walk-forward CV and metrics
│ ├── registry.py # Model + contract + metrics, versioned
│ ├── train.py # The training CLI
│ └── artifacts/ # Trained models and their reports
├── frontend/ # Jinja2 templates and static assets (CSS, JS)
├── data/ # JSON data files served under /static/data
│ ├── compliance_rules.json # Mandate ruleset (TEMPLATE — see the file)
│ ├── market/ # Parquet daily bars
│ └── pit/ # Point-in-time snapshots
├── requirements.txt # Python dependencies
├── Dockerfile # Container definition
└── .env.example # Environment variable template
One process (main.py) mounts the MCP app under /mcp and the FastAPI web app at / on a single Uvicorn port. The scheduler runs inside the same process (APScheduler async) when SCHEDULER_ENABLED=1.
flowchart TB
subgraph clients [Clients]
Browser[Web browser]
MCPClient[MCP client e.g. Cursor]
WhatsApp[WhatsApp ← user's phone]
end
subgraph server [MyFinanceMCP process]
Starlette[Starlette / Uvicorn]
Web[FastAPI web_app]
MCP[mcp_server FastMCP SSE]
Admin[adminApi router]
SM[session_manager in-memory]
FinanceADK[Finance ADK agent]
TradingADK[Trading ADK agent]
Scheduler[APScheduler tick 1 min]
AngelWrap[angel_client AngelOneClient]
end
subgraph db [Postgres / Neon]
Users[users encrypted creds]
Schedules[schedules next_run status]
Logs[logs run history]
end
subgraph external [External services]
AngelAPI[Angel One SmartAPI]
OR[OpenRouter API]
WAHook[WhatsApp webhook service]
Other[yfinance gnews NSE data etc.]
end
Browser -->|HTTPS session cookie| Starlette
MCPClient -->|Streamable HTTP /mcp/mcp + Bearer token| Starlette
Starlette --> Web
Starlette --> MCP
Web --> Admin
Web --> SM
Web --> FinanceADK
Web --> TradingADK
MCP --> SM
SM --> AngelWrap
AngelWrap --> AngelAPI
FinanceADK --> OR
TradingADK --> OR
Web --> Other
Admin --> db
Scheduler --> db
Scheduler -->|run_one| AngelWrap
Scheduler -->|send| WAHook
WAHook --> WhatsApp
| Path | Purpose |
|---|---|
/, /dashboard, /agent, … | Jinja pages + static assets |
/api/* | JSON for dashboard, news, research, /api/agent/chat, /api/agent/new-chat, /api/ai/* |
/mcp/... | MCP over SSE (login, holdings, orders, etc.) |
/docs | Swagger UI (FastAPI auto-generated) |
AngelOneClient in memory and stores an opaque sid in a signed session cookie.schedules for rows where next_run <= now() → claims them with FOR UPDATE SKIP LOCKED → for each claimed schedule: decrypts credentials from users, creates an ephemeral Angel session, generates the briefing, sends WhatsApp, writes to logs, and arms next_run.Authorization: Bearer <token>: either an OAuth 2.1 access token (claude.ai custom connectors and other OAuth clients; PKCE S256, Dynamic Client Registration, consent at /oauth/consent, tokens hashed and bound to the /mcp/mcp resource) or a personal access token created on /connect. The user is derived from the verified token and their broker session is rebuilt from their encrypted stored credentials; no credentials pass through tools.You need 4 credentials from the Angel One SmartAPI portal:
https://smartapi.angelbroking.com/signin
| Credential | How to get it |
|---|---|
| API Key | SmartAPI → My Apps → Create App → copy the API Key |
| Client ID | Your Angel One account ID (e.g. AB1234) — visible on the SmartAPI dashboard |
| PIN | The 4-digit trading PIN you use to log into Angel One |
| TOTP Secret | SmartAPI → My Profile → Enable TOTP → copy the Base32 secret key |
Note: The TOTP secret is only shown once when you enable it. Save it immediately.
python3 -m venv .venv
source .venv/bin/activate # macOS / Linux
# .venv\Scripts\activate # Windows
pip install -r requirements.txt
The scheduler and admin API require a Postgres database. The easiest option for development is a free Neon database. Tables are created automatically on first startup via init_db().
cp .env.example .env
Edit .env with your values:
| Variable | Required | Description |
|---|---|---|
ANGELONE_API_KEY | For ADK CLI only | Bootstrap credentials for local ADK agent testing |
ANGELONE_CLIENT_ID | For ADK CLI only | See above |
ANGELONE_PASSWORD | For ADK CLI only | See above |
ANGELONE_TOTP_SECRET | For ADK CLI only | See above |
OPENROUTER_API_KEY | Yes (for Agent/Trading/Briefing) | Server-side LLM key |
DATABASE_URL | Yes | Postgres connection string (e.g. Neon pooler URL with ?sslmode=require) |
ENCRYPTION_KEY | Yes | Fernet key for encrypting Angel credentials in DB. Generate with: python -m db.crypto generate |
ADMIN_API_KEY | Yes | Static key protecting all /api/admin/* endpoints |
SCHEDULER_ENABLED | Yes (for briefings) | Set to 1 to start the APScheduler tick on process startup |
SESSION_SECRET | Recommended | Signs session cookies. Random key generated on each restart if unset (invalidates existing cookies) |
ALLOWED_HOSTS | Recommended | Comma-separated hostnames the app answers to. Unset accepts any Host header, which leaves Host-header reflection open in production |
Note on two OpenRouter keys: The server
OPENROUTER_API_KEYis used by the ADK agents and the daily briefing pipeline. There is a separate optional per-browser OpenRouter key for Dashboard → AI Insights / Ask AI (/api/ai/*) — that is stored only in the browser and never sent to the server's env.
Scaling note: The ADK integration uses in-memory runners in the same process. Use a single Uvicorn worker (the default with python main.py) so chat state and Angel sessions stay consistent.
python main.py
The server starts on http://localhost:8000:
http://localhost:8000/http://localhost:8000/docshttp://localhost:8000/mcp/mcpThe app runs without a trained model. Predictions then come from a rule-based technical score, and every response and the dashboard label say so.
1. Build the market store. One HTTP call per trading day pulls the entire NSE cash market, including delivery percentage. Resumable — interrupt it freely.
python -m services.marketstore.backfill --from 2020-01-01
The delivery-enriched bhavcopy archive starts around January 2020; earlier dates
return "data not found". Check progress any time with --status.
2. Train. Walk-forward validated, calibrated, and registered with its full contract:
python models/train.py
Artefacts land in models/artifacts/<version>/ with a report.md you should
read before trusting anything. Use --sample for a fast smoke test and
--no-register to evaluate without making a model live.
What to expect. An out-of-sample AUC near 0.54 on daily Indian equities is a real result. A number far above that is almost always a leak rather than an edge, and should be investigated as a bug rather than celebrated. Shorter horizons are weaker: in the first run the 1-day and 1-week models scored ~0.50 (no signal) while 1-month scored 0.55.
On fundamentals and sentiment. Both feature blocks are forward-only. yfinance reports today's restated figures, so historical values cannot be reconstructed without look-ahead bias. Snapshots accumulate from the day capture begins — every fundamentals fetch the app makes writes one. Until the archive is deep enough, those blocks are null, and the training report says so explicitly rather than implying coverage the model did not have.
Proposed trades pass through a hard gate before reaching the broker. Rules live
in data/compliance_rules.json with an effective date and a source per rule, so
a rule change is a data edit and the ruleset in force on a past date can be
reconstructed.
The shipped file is a template. Its limits are placeholders that exist to exercise the engine. Before it governs a real trade, every limit must be read from and cited to the current SEBI master circular, the scheme information document, and the fund's own policy. The loader logs a warning while the template is in use, and every API response carries
is_template: true.
Every decision — executed, blocked or failed — is written to the append-only
decision_records table and readable at GET /api/trading/decisions.
The scheduler sends a personalised WhatsApp message to each premium user at their configured time each day. The message includes:
POST /api/briefing/schedule with {"time_ist": "08:30"}.SCHEDULER_ENABLED=1 in your environment and restart.APScheduler tick (every 1 min)
└── repository.claim_due_schedules() ← SQL: next_run <= now(), FOR UPDATE SKIP LOCKED
└── job_executor.run_one(claim)
├── Load user + decrypt credentials (Fernet)
├── Create ephemeral Angel session
├── generate_daily_briefing() ← holdings + candles + model + LLM
├── whatsapp.send() ← POST to webhook service
├── Write Log row
└── finish_schedule() ← advance next_run by interval_minutes
All endpoints require the X-Admin-Key: <your ADMIN_API_KEY> header. Browse to /docs for interactive Swagger UI.
| Endpoint | Method | Description |
|---|---|---|
/api/admin/users | GET | List all registered users |
/api/admin/users | POST | Create a user (encrypts password + TOTP before storing) |
/api/admin/users/{id} | GET | Get a single user |
/api/admin/users/{id} | DELETE | Deactivate a user |
/api/admin/schedules | GET | List all schedules |
/api/admin/schedules | POST | Create a schedule for a user |
/api/admin/schedules/{id} | GET | Get a single schedule |
/api/admin/schedules/{id} | PATCH | Update schedule (enable/disable, interval, next_run) |
/api/admin/schedules/{id} | DELETE | Delete a schedule |
/api/admin/logs | GET | Recent scheduler run logs (filterable by user_id) |
curl -X POST http://localhost:8000/api/admin/users \
-H "X-Admin-Key: $ADMIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"whatsapp_number": "918107037133",
"angel_api_key": "",
"angel_client_id": "",
"angel_password": "",
"angel_totp_secret": ""
}'
Settings → Connectors → Add custom connector, URL https://agentfolio.in/mcp/mcp (use the apex domain; www redirects). Claude discovers the OAuth server from /.well-known/oauth-protected-resource/mcp/mcp, registers itself, and sends you to a sign-in page (WhatsApp number + app password) to approve read-only portfolio:read access. No token to paste. Connected apps can be disconnected on /connect.
OAuth needs PUBLIC_BASE_URL set to the exact public origin (e.g. https://agentfolio.in): the issuer, the endpoints and the resource that tokens are bound to are all derived from it. Optional: OAUTH_EXTRA_REDIRECT_URIS (comma-separated, exact) to allow other clients' callbacks beyond claude.ai/claude.com and loopback; OAUTH_ALLOW_LOOPBACK_REDIRECTS=0 to refuse http://localhost callbacks.
Add to your .cursor/mcp.json:
{
"mcpServers": {
"angelone-portfolio": {
"url": "http://localhost:8000/mcp/mcp"
}
}
}
Add an Authorization: Bearer <token> header using a token from the /connect page. See /connect for per-client examples.
Add to your claude_desktop_config.json:
{
"mcpServers": {
"angelone-portfolio": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://localhost:8000/mcp/mcp"]
}
}
}
Go to Settings → Beta features → MCP Servers → Add Server and enter:
npx-y mcp-remote http://localhost:8000/mcp/mcp| Tool | Description |
|---|---|
get_profile | Account profile |
get_holdings | Stock holdings with P&L |
get_all_holdings | Complete holdings overview |
get_positions | Open positions |
get_order_book | Today's orders |
get_trade_book | Executed trades |
get_funds | Account funds / margin |
get_ltp | Last traded price |
search_scrip | Search stocks by name |
portfolio_summary | High-level portfolio summary |
get_candle_data | Historical OHLC data |
get_stock_history | OHLCV history by symbol |
calculate_pnl | P&L for one symbol |
get_market_depth | Best 5 bids/asks |
predict_price_direction | Technical-signal outlook (not advice) |
All tools are read-only. There are no order placement, modification, cancellation or login tools.
DATABASE_URL (Postgres connection string)ENCRYPTION_KEYADMIN_API_KEYOPENROUTER_API_KEYSCHEDULER_ENABLED=1SESSION_SECREThttps://myfinancemcp.up.railway.app)Web users: Visit https://your-app.up.railway.app and log in with Angel One credentials.
MCP users: Update the url in your MCP client config:
{
"mcpServers": {
"angelone-portfolio": {
"url": "https://your-app.up.railway.app/mcp/mcp"
}
}
}
Note: Render free tier spins down after inactivity. The scheduler resumes when the service wakes up, but will immediately fire all overdue schedules on the next tick.
| Location | What is stored | Notes |
|---|---|---|
| Server memory | Transient AngelOneClient instances keyed by web sid or MCP connection id | Not written to DB; cleared on logout, session expiry (~8 hours), or process restart |
| Postgres DB | Premium users rows with angel_password_encrypted and angel_totp_secret_encrypted | Encrypted with Fernet (ENCRYPTION_KEY). Never returned in plaintext by any API response |
Server .env / host env | OPENROUTER_API_KEY, ENCRYPTION_KEY, ADMIN_API_KEY, DATABASE_URL, SESSION_SECRET | Protect the host and use a secrets manager in production |
| Signed session cookie | Opaque sid (web Angel session), adk_chat_session_id (ADK thread) | Signed with SESSION_SECRET. Use HTTPS in production |
| Server memory (ADK) | InMemoryRunner and ADK session/event state per logged-in user | Lost on restart. Use a single Uvicorn worker |
Browser localStorage | Optional encrypted OpenRouter key for Dashboard → AI Insights only | Not used by /agent or scheduler. Encrypted with AES-GCM using client id as key material |
| Browser (typical) | No Angel credentials after login | Sent once over HTTPS at login; session cookie identifies the server-side session |
SESSION_TTL_SECONDS in session_manager.py).FOR UPDATE SKIP LOCKED pattern in repository.py makes the scheduler safe to run across multiple replicas, though only one process should have SCHEDULER_ENABLED=1..env (file permissions, no commits) and use platform secret stores in production.localStorage — treat the device as trusted.MIT