Back to DashboardGitHub Repo
Technical Documentation

System Architecture

Under the hood of not-financial-advice — a 3-engine, AI-powered money maker.

System Overview

The architecture uses scheduled crons to orchestrate data across market intelligence providers, filter through a 3-engine conviction algorithm, and enforce a strict 3:1 Risk/Reward gate via Gemini Flash before persisting any recommendation.

graph TD classDef external fill:#1e293b,stroke:#475569,stroke-width:2px,color:#e2e8f0 classDef core fill:#1e3a8a,stroke:#3b82f6,stroke-width:2px,color:#e0e7ff classDef ui fill:#064e3b,stroke:#10b981,stroke-width:2px,color:#d1fae5 classDef database fill:#7c2d12,stroke:#f97316,stroke-width:2px,color:#ffedd5 classDef engine fill:#581c87,stroke:#a855f7,stroke-width:2px,color:#f3e8ff classDef money fill:#065f46,stroke:#34d399,stroke-width:2px,color:#d1fae5 subgraph External["External APIs"] AV["Alpha Vantage<br/>Market Data"]:::external XP["Xpoz<br/>Social Sentiment"]:::external GM["Gemini Flash<br/>3:1 R/R Engine"]:::external end subgraph Core["Core Pipeline"] direction TB CRON["Cron Scheduler<br/>3x Daily"]:::core SCAN["Scanner Service<br/>Top Gainers + Active"]:::core subgraph Engines["3-Engine Analysis"] E1["RVOL Engine"]:::engine E2["Float Rotation"]:::engine E3["Sentiment Velocity"]:::engine end JUDGE["Conviction Score<br/>Weighted Algorithm"]:::core DB[("Supabase<br/>Database")]:::database end subgraph Money["Money Maker"] TP["Take-Profit Target"]:::money SL["Stop-Loss Target"]:::money RR["3:1 Risk/Reward Gate"]:::money PT["Paper Trades<br/>$1,000/position"]:::money end subgraph Validation["Outcome Validation"] VCRON["Validate Cron"]:::core WIN["closed_win"]:::money LOSS["closed_loss"]:::external EXP["expired"]:::external end subgraph UI["Frontend"] DASH["Dashboard"]:::ui SCANNER["Market Scanner"]:::ui HISTORY["History Table"]:::ui PORTFOLIO["Paper Portfolio"]:::ui HEALTH["Health Monitor"]:::ui end CRON --> SCAN SCAN -->|"Top Gainers"| AV SCAN --> Engines E1 & E2 -->|"Metrics"| AV E3 -->|"Social Data"| XP Engines --> JUDGE JUDGE -->|"Score + Data"| GM GM -->|"TP / SL / Verdict"| RR RR -->|"Pass"| DB RR -->|"Reject"| SCAN DB --> TP & SL DB -->|"Buy Signal"| PT VCRON -->|"Price Check"| AV VCRON --> WIN & LOSS & EXP DB --> DASH & SCANNER & HISTORY & PORTFOLIO HEALTH -.->|"Monitor"| AV & XP & GM

Execution Pipeline

The scanner executes 3x daily. Mock/rate-limited responses are automatically discarded. Buy signals trigger $1,000 paper trades. A separate validation cron closes positions when TP, SL, or a 14-day expiry is hit.

sequenceDiagram autonumber participant C as Cron participant S as Scanner participant AV as Alpha Vantage participant X as Xpoz participant AI as Gemini Flash participant DB as Supabase participant PT as Paper Trades C->>S: WAKE_UP (9:30, 12:00, 16:00) S->>AV: FETCH_TOP_GAINERS AV-->>S: Symbols + Gap% loop Per Candidate S->>AV: FETCH_TECHNICALS (RVOL, Float) S->>X: FETCH_SENTIMENT Note over S: Calculate Conviction Score S->>AI: ANALYZE (3:1 R/R Enforced) AI-->>S: {recommendation, take_profit, stop_loss} alt Mock/Rate-Limited Response S--xDB: DISCARD (DB Pollution Guard) else Valid Response S->>DB: PERSIST (opportunity + targets) alt recommendation == Buy DB->>PT: AUTO_OPEN ($1,000 position) end end end Note over C,PT: Later (Validation Cron) C->>DB: FETCH unvalidated recs loop Per Recommendation DB->>AV: FETCH_CURRENT_PRICE alt Price >= Take Profit DB->>PT: CLOSE (closed_win) else Price <= Stop Loss DB->>PT: CLOSE (closed_loss) else Age > 14 days DB->>PT: CLOSE (expired) end end

3:1 Risk/Reward Gate

Gemini Flash is explicitly banned from recommending "Buy" unless the profit target is ≥ 3× the stop-loss risk. If the math doesn't pass, the AI must output "Hold".

graph TD classDef pass fill:#064e3b,stroke:#34d399,stroke-width:2px,color:#d1fae5 classDef fail fill:#7f1d1d,stroke:#ef4444,stroke-width:2px,color:#fecaca classDef calc fill:#1e3a8a,stroke:#3b82f6,stroke-width:2px,color:#dbeafe classDef gate fill:#581c87,stroke:#a855f7,stroke-width:3px,color:#f3e8ff INPUT["Stock Data + Sentiment"]:::calc AI["Gemini Flash Analysis"]:::calc TP_CALC["Calculate Take Profit"]:::calc SL_CALC["Calculate Stop Loss"]:::calc RR_CHECK{"R/R >= 3:1?"}:::gate INPUT --> AI AI --> TP_CALC & SL_CALC TP_CALC & SL_CALC --> RR_CHECK RR_CHECK -->|"Yes"| BUY["BUY Signal<br/>+ Paper Trade"]:::pass RR_CHECK -->|"No"| HOLD["HOLD<br/>Rejected"]:::fail

Paper Trade Lifecycle

Every Buy signal auto-opens a $1,000 simulated position. The validation cron closes it when the price hits the AI's exact take-profit or stop-loss target, or after 14 days.

stateDiagram-v2 [*] --> open: Buy Signal ($1,000) open --> closed_win: Price >= Take Profit open --> closed_loss: Price <= Stop Loss open --> expired: Age > 14 days closed_win --> [*] closed_loss --> [*] expired --> [*]

API Call Budget

StageCalls/ScanDaily (×3)
Discovery (Top Gainers)13
Deep Dive (per candidate)618
Validation (price checks)1010
Total1731
Dual API keys rotate to stay within 50 calls/day

Mock Mode & DB Protection

When API limits are hit, mock data is generated for UI display but is explicitly discarded from the database. DB pollution guards prevent fake data from corrupting historical analytics.

graph LR classDef check fill:#1e293b,stroke:#475569,color:#e2e8f0 classDef real fill:#064e3b,stroke:#10b981,color:#d1fae5 classDef mock fill:#7c2d12,stroke:#f97316,color:#ffedd5 API["API Call"]:::check --> Check{"Limit Hit?"}:::check Check -->|No| Real["Real Data"]:::real Check -->|Yes| Mock["Mock Data"]:::mock Mock --> Tag["Tag: mock_intraday"]:::mock Mock --> DISCARD["Discard from DB"]:::mock

The Gainer Fade Thesis

Empirical observation: stocks that gap up significantly on the daily Top Gainers list tend to fade sharply within 1-3 days. The larger the gap, the higher the probability of mean-reversion.

Low Fade Risk

3-8%

Moderate gap, sustainable momentum

Medium Fade Risk

8-15%

Overextended, watch for reversal

High Fade Risk

15%+

Extreme gap, likely to retrace hard

The Scanner UI now highlights the "Fade Risk" level on each candidate card. High-gap candidates that the AI rejects as "Hold" are potential short/fade opportunities.