docs: create professional and detailed README files for all subfolders
Browse files- alembic/README.md +48 -0
- backend/README.md +90 -0
- frontend/README.md +32 -23
- worker/README.md +53 -0
alembic/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Database Migrations (Alembic)
|
| 2 |
+
|
| 3 |
+
This directory contains the database migration scripts managed by Alembic. Alembic handles automatic schema updates, tracking revisions, and syncing database tables with SQLAlchemy models.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## Migration Commands Reference
|
| 8 |
+
|
| 9 |
+
### 1. Run Pending Migrations
|
| 10 |
+
To bring your local database (PostgreSQL/NeonDB) up to date with the latest schema changes:
|
| 11 |
+
```bash
|
| 12 |
+
uv run alembic upgrade head
|
| 13 |
+
```
|
| 14 |
+
|
| 15 |
+
### 2. Generate a New Migration
|
| 16 |
+
To create a new migration after editing SQLAlchemy models in `backend/app/database/models.py`:
|
| 17 |
+
```bash
|
| 18 |
+
uv run alembic revision --autogenerate -m "describe your changes"
|
| 19 |
+
```
|
| 20 |
+
|
| 21 |
+
### 3. Revert Migrations
|
| 22 |
+
To roll back the last applied migration:
|
| 23 |
+
```bash
|
| 24 |
+
uv run alembic downgrade -1
|
| 25 |
+
```
|
| 26 |
+
|
| 27 |
+
---
|
| 28 |
+
|
| 29 |
+
## Database Schema Evolution
|
| 30 |
+
|
| 31 |
+
The database schema has evolved through the following sequential revisions located in the `versions/` folder:
|
| 32 |
+
|
| 33 |
+
1. **Initial Schema (`3d7629451f7d_initial_schema.py`)**
|
| 34 |
+
- Creates the core tables: `users` (credentials and subscription states), `watchlists` (user ticker lists), and `stock_history` (OHLCV candles).
|
| 35 |
+
2. **User Verification (`99690d55ee16_add_user_verification_columns.py`)**
|
| 36 |
+
- Appends email verification tracking, registration verification codes, and expiration timestamps to the `users` table.
|
| 37 |
+
3. **Subscriptions & Message Limits (`bbdbf37359be_add_subscription_and_message_limit_.py`)**
|
| 38 |
+
- Adds monthly credit bounds, API request trackers, and usage counters to enforce tiered subscription levels.
|
| 39 |
+
4. **Strategy Execution Logs (`887a33df9c3e_add_strategy_logs_table.py`)**
|
| 40 |
+
- Creates the `strategy_logs` table to store quantitative backtesting parameters, accuracy scores, and performance logs.
|
| 41 |
+
5. **Saved Strategy Configurations (`1ffb20394fc4_add_saved_strategies.py`)**
|
| 42 |
+
- Creates the `saved_strategies` table, allowing users to save and load strategy setups.
|
| 43 |
+
6. **Model Predictions Tracking (`31f4439fab74_add_prediction_logs_table.py`)**
|
| 44 |
+
- Creates the `prediction_logs` table to log probability outputs, actual outcomes, and model scores from the specialized ONNX models.
|
| 45 |
+
7. **Asset Category Fields (`ea96300d7e57_add_asset_class_columns.py`)**
|
| 46 |
+
- Adds category indicators (e.g. tech, crypto, index) to refine indicator logic per asset class.
|
| 47 |
+
8. **Threshold Alerts (`15639881888f_add_alert_trigger_columns.py`)**
|
| 48 |
+
- Appends trigger states to the price alerts table to track when a stock crosses user-specified price bounds.
|
backend/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Backend Service Layer
|
| 2 |
+
|
| 3 |
+
The Backend is a high-performance FastAPI service designed to orchestrate the machine learning model pipeline, execute the Gemini ReAct agent loop, manage GraphQL subscriptions, handle HTTP endpoints, and process background Celery tasks.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## Architecture & Service Directory
|
| 8 |
+
|
| 9 |
+
### 1. ONNX Model Hub & Dynamic Hot-Reloading
|
| 10 |
+
Upon service startup (`startup_event` in `backend/app/main.py`), the backend automatically connects to Hugging Face Hub via `hf_hub_download` to pull specialized machine learning models:
|
| 11 |
+
- `model_tech.onnx` (for technology stocks)
|
| 12 |
+
- `model_crypto.onnx` (for cryptocurrency assets)
|
| 13 |
+
- `model_index.onnx` (for broad market indices)
|
| 14 |
+
|
| 15 |
+
If downloading fails, the server falls back to a general `model.onnx` file stored locally.
|
| 16 |
+
|
| 17 |
+
#### Hot-Reloading Engine:
|
| 18 |
+
To support model retraining without system downtime, the function `get_onnx_session_for_type` monitors the model files. When a retraining task saves a new model file, the system detects the changed modification time (`os.path.getmtime`), terminates the old session, and hot-reloads the new `ort.InferenceSession` in-memory.
|
| 19 |
+
|
| 20 |
+
---
|
| 21 |
+
|
| 22 |
+
### 2. Gemini ReAct Agent Loop
|
| 23 |
+
The core analytical capabilities of QuantIQ are powered by the Gemini GenAI SDK (`gemini-2.5-flash`), running a reasoning-action loop.
|
| 24 |
+
|
| 25 |
+
#### Synchronous Tool closures:
|
| 26 |
+
The Gemini automatic function calling API executes tools synchronously. However, the database operations inside our FastAPI application are asynchronous. To solve this, the agent loop uses `asyncio.run_coroutine_threadsafe` and closures to safely schedule async queries back onto the main event loop from inside the synchronous tool calls.
|
| 27 |
+
|
| 28 |
+
#### Tool Ecosystem:
|
| 29 |
+
- `get_user_watchlist`: Retrieves the active ticker watchlist for the logged-in user.
|
| 30 |
+
- `get_stock_history_and_indicators`: Extracts recent price candles and calculates technical indicator values.
|
| 31 |
+
- `create_alert_threshold`: Allows the model to programmatically set alert boundaries.
|
| 32 |
+
- `trigger_model_prediction`: Evaluates the specialized ONNX model predictions.
|
| 33 |
+
|
| 34 |
+
---
|
| 35 |
+
|
| 36 |
+
### 3. Mathematical Indicator Formulas
|
| 37 |
+
|
| 38 |
+
The backend computes technical indicators over a 60-day historical window. The exact formulas are detailed below:
|
| 39 |
+
|
| 40 |
+
#### Volatility Target and Stop-Loss Levels (ATR-14)
|
| 41 |
+
To calculate risk-managed boundaries:
|
| 42 |
+
1. Compute the True Range (TR) for each candle:
|
| 43 |
+
$$\text{TR} = \max(\text{High} - \text{Low}, |\text{High} - \text{Close}_{\text{prev}}|, |\text{Low} - \text{Close}_{\text{prev}}|)$$
|
| 44 |
+
2. Compute the 14-period Average True Range (ATR) using Wilder's Smoothing:
|
| 45 |
+
$$\text{ATR}_t = \frac{\text{ATR}_{t-1} \times 13 + \text{TR}_t}{14}$$
|
| 46 |
+
3. Set the target and stop-loss boundaries based on the signal action (with a default multiplier of 1.5):
|
| 47 |
+
- **BUY**:
|
| 48 |
+
$$\text{Stop Loss} = \text{Close} - (1.5 \times \text{ATR})$$
|
| 49 |
+
$$\text{Target Price} = \text{Close} + (3.0 \times \text{ATR})$$
|
| 50 |
+
- **SELL**:
|
| 51 |
+
$$\text{Stop Loss} = \text{Close} + (1.5 \times \text{ATR})$$
|
| 52 |
+
$$\text{Target Price} = \text{Close} - (3.0 \times \text{ATR})$$
|
| 53 |
+
|
| 54 |
+
If the asset history has fewer than 14 days, the system falls back to asset-class default percentages:
|
| 55 |
+
- **Crypto**: Target = 8%, Stop = 4%
|
| 56 |
+
- **Indices**: Target = 1.5%, Stop = 0.75%
|
| 57 |
+
- **Stocks (Tech/General)**: Target = 4%, Stop = 2%
|
| 58 |
+
|
| 59 |
+
#### Relative Strength Index (RSI-14)
|
| 60 |
+
Computes momentum boundaries using upward and downward price changes over 14 candles:
|
| 61 |
+
$$\text{RS} = \frac{\text{EMA}(\text{Gain}, 14)}{\text{EMA}(\text{Loss}, 14)}$$
|
| 62 |
+
$$\text{RSI} = 100 - \left(\frac{100}{1 + \text{RS}}\right)$$
|
| 63 |
+
|
| 64 |
+
#### MACD (Moving Average Convergence Divergence)
|
| 65 |
+
Measures trend-following momentum:
|
| 66 |
+
$$\text{MACD Line} = \text{EMA}(\text{Close}, 12) - \text{EMA}(\text{Close}, 26)$$
|
| 67 |
+
$$\text{Signal Line} = \text{EMA}(\text{MACD Line}, 9)$$
|
| 68 |
+
$$\text{Histogram} = \text{MACD Line} - \text{Signal Line}$$
|
| 69 |
+
|
| 70 |
+
---
|
| 71 |
+
|
| 72 |
+
### 4. Celery Tasks & Redis Logical Separation
|
| 73 |
+
Redis serves as both our Celery task broker and backend database cache. To prevent packet collisions and memory corruption, the databases are logically isolated:
|
| 74 |
+
- **Broker Channel**: Celery occupies Redis Logical Database 0 (`redis://localhost:6379/0`).
|
| 75 |
+
- **Cache Channel**: Technical indicators caching, yfinance hourly caching, and ATR calculations occupy Redis Logical Database 1 (`redis://localhost:6379/1`).
|
| 76 |
+
|
| 77 |
+
---
|
| 78 |
+
|
| 79 |
+
### 5. Strawberry GraphQL Integration
|
| 80 |
+
The client connects to a Strawberry-powered GraphQL router mounted at `/graphql`.
|
| 81 |
+
- **JWT Authorization Parser**: On each request, the custom `get_graphql_context` dependency extracts the HTTP `Authorization: Bearer <token>` header, decodes the JWT signature, extracts the user ID UUID, and fetches the user ORM object to inject it directly into the execution context.
|
| 82 |
+
- **Database Context**: Attaches the active `AsyncSession` to the GraphQL context, ensuring database queries run within safe transaction boundaries.
|
| 83 |
+
|
| 84 |
+
---
|
| 85 |
+
|
| 86 |
+
### 6. Prometheus Metrics Instrumentation
|
| 87 |
+
The backend uses `prometheus-fastapi-instrumentator` to export operational metrics.
|
| 88 |
+
- Enpoints metrics are published on the `/metrics` path.
|
| 89 |
+
- The route is protected using HTTP Basic Authentication (`admin` / `admin`).
|
| 90 |
+
- Tracks API latency, HTTP response codes, active WebSocket counts, Gemini token expenditures, and agent reasoning steps.
|
frontend/README.md
CHANGED
|
@@ -1,32 +1,41 @@
|
|
| 1 |
-
#
|
| 2 |
|
| 3 |
-
|
| 4 |
|
| 5 |
-
|
| 6 |
|
| 7 |
-
|
| 8 |
-
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/)
|
| 9 |
|
| 10 |
-
##
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
|
| 12 |
-
|
| 13 |
|
| 14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
-
|
| 17 |
|
| 18 |
-
|
| 19 |
-
{
|
| 20 |
-
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
| 21 |
-
"plugins": ["react", "typescript", "oxc"],
|
| 22 |
-
"options": {
|
| 23 |
-
"typeAware": true
|
| 24 |
-
},
|
| 25 |
-
"rules": {
|
| 26 |
-
"react/rules-of-hooks": "error",
|
| 27 |
-
"react/only-export-components": ["warn", { "allowConstantExport": true }]
|
| 28 |
-
}
|
| 29 |
-
}
|
| 30 |
-
```
|
| 31 |
|
| 32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Frontend Client Workspace
|
| 2 |
|
| 3 |
+
The frontend is a single-page React client built with TypeScript, Vite, Tailwind CSS, and lightweight UI components. It renders the trading workspace, manages websocket subscriptions, and displays real-time price trends.
|
| 4 |
|
| 5 |
+
---
|
| 6 |
|
| 7 |
+
## Technical Components & Integration Details
|
|
|
|
| 8 |
|
| 9 |
+
### 1. TradingView Lightweight Charts (`StockChart.tsx`)
|
| 10 |
+
The centerpiece of the UI is the stock chart widget, which utilizes the `lightweight-charts` library:
|
| 11 |
+
- **WebSocket Real-Time Streaming:** The chart opens a connection to the backend WebSocket server (`ws://localhost:8000/api/v1/ws/ticks`). As ticks arrive from the Redpanda broker, they are pushed directly to the candlestick series update channel.
|
| 12 |
+
- **Tick Accumulation:** If a tick arrives for the current minute, its price updates the close of the current candle. If a new minute arrives, a new candle is created.
|
| 13 |
+
- **Historical Data Backfill:** When switching tickers, the chart calls the API to download the last 100 historical candles from NeonDB to prevent gaps in chart history.
|
| 14 |
|
| 15 |
+
### 2. Component Directory Map
|
| 16 |
|
| 17 |
+
- **`App.tsx`**: Core routing, layout shell, and global authentication states.
|
| 18 |
+
- **`components/StockChart.tsx`**: Embeds the main candlestick chart, indicators toggle, and price-scaling canvas handlers.
|
| 19 |
+
- **`components/AIAnalyst.tsx`**: Interactive side-panel allowing users to query the Gemini ReAct agent loop. It displays reasoning steps, tool calls, and model outputs in real time.
|
| 20 |
+
- **`components/TrendingHub.tsx`**: A social trading feed displaying strategies and prediction records generated by the background ML models.
|
| 21 |
+
- **`components/WatchlistSidebar.tsx`**: Sidebar allowing users to add, remove, and manage monitored tickers. Saves state directly to NeonDB.
|
| 22 |
+
- **`components/PriceAlerts.tsx`**: Threshold settings page where users configure price levels for alerts.
|
| 23 |
+
- **`components/RechargeModal.tsx`**: A payment interface managing balance credits and subscription upgrades.
|
| 24 |
|
| 25 |
+
---
|
| 26 |
|
| 27 |
+
## Production Build & Deploy Verification
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
|
| 29 |
+
### Strict TypeScript Constraints
|
| 30 |
+
The project runs clean type verification during compilation:
|
| 31 |
+
```bash
|
| 32 |
+
npm run build
|
| 33 |
+
```
|
| 34 |
+
Under the hood, this executes `tsc -b` and `vite build`. To prevent production crashes:
|
| 35 |
+
- Any declared variables, parameters, or import statements that are unused will throw compiler error `TS6133` and fail the build.
|
| 36 |
+
- Always check that your dependencies are imported correctly and clean up unused code before deploying.
|
| 37 |
+
|
| 38 |
+
### Deployment Configuration
|
| 39 |
+
The client is optimized for Vercel deployment:
|
| 40 |
+
- **`vercel.json`**: Sets up URL routing rules to redirect all page requests to `index.html` to support React client-side routing.
|
| 41 |
+
- **Vite Proxy Config**: Proxy rules are configured to redirect `/api/v1` routes to the backend port during local development.
|
worker/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Ingestion Worker
|
| 2 |
+
|
| 3 |
+
The Ingestion Worker is a standalone service responsible for real-time asset price tracking, data streaming, and database candle aggregation. It operates on a continuous async loop to fetch ticker ticks and publish them to a message broker.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## Technical Architecture
|
| 8 |
+
|
| 9 |
+
### 1. Multi-Threaded yfinance Ingestion
|
| 10 |
+
The Yahoo Finance (`yfinance`) library runs synchronous blocking network calls when querying tickers. To prevent blocking the main asyncio event loop, the worker runs all `fast_info` metrics retrievals inside a thread pool executor using `loop.run_in_executor(None, ...)` inside `get_latest_stock_data`.
|
| 11 |
+
|
| 12 |
+
```python
|
| 13 |
+
loop = asyncio.get_event_loop()
|
| 14 |
+
yf_ticker = yf.Ticker(ticker)
|
| 15 |
+
info = await loop.run_in_executor(None, lambda: yf_ticker.fast_info)
|
| 16 |
+
```
|
| 17 |
+
|
| 18 |
+
This ensures high concurrency and prevents websocket read latency on the frontend during polling cycles.
|
| 19 |
+
|
| 20 |
+
### 2. Exponential Rate-Limit Cooldown
|
| 21 |
+
To bypass strict IP rate-limiting blocks from third-party APIs:
|
| 22 |
+
- If a `429 Too Many Requests` or rate-limiting exception string is caught during execution, the worker sets a global cooldown timestamp (`COOLDOWN_UNTIL`).
|
| 23 |
+
- The rate-limiting cooldown backoff starts at 60 seconds.
|
| 24 |
+
- On consecutive rate-limit violations, the backoff doubles exponentially up to a maximum limit of 15 minutes (900 seconds).
|
| 25 |
+
- Once the cooldown timer expires, the backoff resets to 60 seconds on the first successful fetch.
|
| 26 |
+
|
| 27 |
+
### 3. Redpanda Event Streaming
|
| 28 |
+
All successfully fetched ticker events are formatted as JSON payloads containing the ticker name, latest price, cumulative volume, and UTC timestamp, then published to the Redpanda Cloud topic `stock-ticks`.
|
| 29 |
+
- The worker uses the asynchronous `aiokafka` client (`AIOKafkaProducer`).
|
| 30 |
+
- Payload serialization is handled using a native UTF-8 JSON encoder.
|
| 31 |
+
|
| 32 |
+
### 4. 1-Minute Candle Aggregation
|
| 33 |
+
In addition to streaming, the worker buffers price and volume data points in memory to calculate 1-minute OHLCV candles:
|
| 34 |
+
- **Open**: The first price point of the minute.
|
| 35 |
+
- **High**: The maximum price point recorded in the minute.
|
| 36 |
+
- **Low**: The minimum price point recorded in the minute.
|
| 37 |
+
- **Close**: The last price point of the minute.
|
| 38 |
+
- **Volume**: The difference between the maximum and minimum cumulative volumes recorded in that minute.
|
| 39 |
+
|
| 40 |
+
#### Closed-Market Skipping Rules:
|
| 41 |
+
To avoid bloat in the database (e.g. NeonDB storage limitations), the worker filters out flat, inactive closed-market candles (where open equals high, low, and close, and the volume difference is zero).
|
| 42 |
+
- Non-crypto tickers (stocks and indices) will not be committed to the database during closed hours.
|
| 43 |
+
- Crypto tickers (e.g. ending in `-USD` or `-BTC`) bypass this rule and are saved 24/7 since cryptocurrency markets never close.
|
| 44 |
+
|
| 45 |
+
---
|
| 46 |
+
|
| 47 |
+
## Service Operations
|
| 48 |
+
|
| 49 |
+
### Core Main Loop
|
| 50 |
+
The worker queries the database dynamically on each cycle to fetch active tickers from users' watchlists (`select(Watchlist.ticker).distinct()`). This guarantees that:
|
| 51 |
+
- The worker only polls tickers that users are actively monitoring.
|
| 52 |
+
- Adding a ticker to a watchlist dynamically begins polling it without requiring a worker restart.
|
| 53 |
+
- Polling calls are staggered with a 1.5-second sleep interval to spread load evenly.
|