From 5c5d9b594ef7cd019c8eccae42c12ec80409ffb9 Mon Sep 17 00:00:00 2001 From: Prabhat Ranjan Date: Mon, 27 Apr 2026 08:25:52 +1000 Subject: [PATCH 1/2] refactor: modularize frontend components - Extract ScannerStatus, ScoringLegend from ScannerControls - Extract ScanProgressSkeleton, EmptyState, ExportToolbar from ResultsTable - Consolidate docs in .github/AGENTS.md and .github/SECURITY.md - Add docs/README.md entry point Build: 504KB JS, 158KB gzip --- .github/AGENTS.md | 224 ++++++++++++++++-- .github/SECURITY.md | 100 ++++++++ AGENTS.md | 14 +- docs/README.md | 89 +++++++ frontend/src/components/EmptyState.tsx | 17 ++ frontend/src/components/ExportToolbar.tsx | 34 +++ frontend/src/components/ResultsTable.tsx | 70 +----- .../src/components/ScanProgressSkeleton.tsx | 33 +++ frontend/src/components/ScannerControls.tsx | 37 +-- frontend/src/components/ScannerStatus.tsx | 14 ++ frontend/src/components/ScoringLegend.tsx | 33 +++ 11 files changed, 546 insertions(+), 119 deletions(-) create mode 100644 .github/SECURITY.md create mode 100644 docs/README.md create mode 100644 frontend/src/components/EmptyState.tsx create mode 100644 frontend/src/components/ExportToolbar.tsx create mode 100644 frontend/src/components/ScanProgressSkeleton.tsx create mode 100644 frontend/src/components/ScannerStatus.tsx create mode 100644 frontend/src/components/ScoringLegend.tsx diff --git a/.github/AGENTS.md b/.github/AGENTS.md index 9d01ccf..120d827 100644 --- a/.github/AGENTS.md +++ b/.github/AGENTS.md @@ -1,22 +1,214 @@ -# .github - Agent Guide +# Qullamaggie Scanner - Agent Guide -## Overview -GitHub Actions CI/CD workflows for the Qullamaggie scanner. +**Version**: 1.0 | **Last Updated**: 2026-04-27 -## Workflows -- **ci.yml**: Runs tests on push/PR. Checks Python 3.10, 3.11, 3.12. -- **release.yml**: Builds standalone apps (Windows, macOS, Linux) and creates GitHub releases. +This is the main agent instructions for the Qullamaggie Scanner repository. All agent work should follow these guidelines. -## Conventions -- Workflows use `setup‑python` for Python version matrix. -- Release workflow uses `pyinstaller` for bundling. -- Secrets required for PyPI/GitHub releases. +--- + +## Table of Contents + +1. [Quick Start](#quick-start) +2. [Repository Structure](#repository-structure) +3. [Branch Protection Workflow](#branch-protection-workflow) +4. [Testing Requirements](#testing-requirements) +5. [Domain Guides](#domain-guides) + +--- + +## Quick Start + +```bash +# Clone and setup +git clone https://github.com/Sensible-Analytics/qullamaggie_scanner.git +cd qullamaggie_scanner + +# Frontend setup +cd frontend && npm install && npm run dev + +# Run tests +npm test # Frontend tests +npm run lint # Lint check +npm run build # Production build +``` + +--- + +## Repository Structure + +``` +qullamaggie_scanner/ +├── .github/ # GitHub config, workflows, issue templates +├── docs/ # Architecture docs, ADRs +├── data/ # Runtime data (universes.json, scan history) +├── frontend/ # React + Vite scanner UI (main application) +├── ibkr_tws/ # Python IBKR service (legacy) +├── tests/ # Test suite +└── ui/ # Streamlit UI (legacy) +``` + +### Domain Directories + +| Directory | Description | Tech Stack | +|----------|------------|-----------| +| `frontend/` | Main scanner application | React, TypeScript, Vite | +| `ibkr_tws/` | Interactive Brokers service | Python | +| `tests/` | Test suite | Vitest, Playwright | +| `ui/` | Legacy Streamlit UI | Streamlit | + +--- + +## Branch Protection Workflow + +> ⚠️ **IMPORTANT**: This repository has branch protection enabled. Direct pushes to `main`/`master` are BLOCKED. + +### Required Workflow + +1. **Create a feature branch**: + ```bash + git checkout -b feat/your-feature-name + git checkout -b fix/issue-description + ``` + +2. **Make changes and commit**: + ```bash + git add . + git commit -m "feat: descriptive commit message" + ``` + +3. **Push and create PR**: + ```bash + git push origin feat/your-feature-name + gh pr create --title "feat: Add new feature" --body "Description" + ``` + +4. **Merge after review**: + ```bash + gh pr merge --squash --delete-branch + ``` + +### Branch Prefixes + +- `feat/` - New features +- `fix/` - Bug fixes +- `docs/` - Documentation +- `refactor/` - Code refactoring +- `test/` - Test additions +- `chore/` - Maintenance + +### What You MUST NOT Do + +- ❌ Never push directly to `main` or `master` +- ❌ Never use `git push --force` on protected branches +- ❌ Never delete the main branch +- ❌ Never commit directly without a PR + +--- + +## Testing Requirements + +> All PRs MUST include automated tests. No manual testing allowed per Sensible Analytics standards. + +### Test Levels + +| Level | Scope | Tool | +|-------|-------|------| +| **Unit** | Functions/methods | Vitest | +| **Integration** | Module seams | Vitest | +| **E2E** | User flows | Playwright | + +### Commands -## Commands ```bash -# Validate workflow syntax -act -l # if act installed +# Frontend tests +npm test + +# Frontend + lint +npm run lint && npm run build +``` + +--- + +## Domain Guides + +### Frontend (`frontend/`) + +React + Vite application serving the main scanner UI. + +**Tech Stack**: React 19, TypeScript, Vite, TailwindCSS, Zustand + +**Key Files**: +- `src/App.tsx` - Main app component +- `src/store/scannerStore.ts` - State management +- `src/services/stockApi.ts` - Yahoo Finance integration +- `src/utils/calculations.ts` - Qullamaggie scoring algorithm + +**Commands**: +```bash +cd frontend +npm run dev # Development server +npm test # Run tests +npm run lint # Lint check +npm run build # Production build +``` + +--- + +### IBKR Service (`ibkr_tws/`) + +Python Interactive Brokers service integration (legacy). + +**Tech Stack**: Python 3.10+, IB Gateway API + +**Key Files**: +- `ib_service.py` - IB connection +- `scanner_engine.py` - Core scanner logic + +**Commands** (requires virtual environment): +```bash +source .venv/bin/activate +python -m ibkr_tws.scanner_engine +``` + +--- + +### Test Suite (`tests/`) + +Unit and E2E tests for the scanner. + +**Tech Stack**: pytest, pytest-asyncio, Playwright + +**Commands**: +```bash +# Run all tests (excluding E2E) +pytest tests/ -k "not e2e" + +# Run E2E tests +pytest tests/test_e2e_dashboard.py +``` + +--- + +## Git Configuration + +```bash +git config user.name "Your Name" +git config user.email "your.email@example.com" +``` + +--- + +## Security + +- All changes go through PR review +- No force pushes allowed +- Branch deletion is prevented +- CI checks must pass before merge + +--- + +## Related Documentation -# Run CI locally (if act installed) -act push -``` \ No newline at end of file +- [Security Policy](.github/SECURITY.md) +- [Code of Conduct](.github/CODE_OF_CONDUCT.md) +- [Frontend README](frontend/README.md) \ No newline at end of file diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..75ff944 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,100 @@ +# Security Policy + +## Supported Versions + +We release patches for security vulnerabilities in the following versions: + +| Version | Supported | +| ------- | ------------------ | +| Latest | ✅ | +| < 1.0 | ⚠️ Limited support | + +## Reporting a Vulnerability + +**⚠️ Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them via email to: + +📧 **security@sensibleanalytics.co** + +### What to Include + +When reporting a vulnerability, please include: + +1. **Description** - Clear description of the vulnerability +2. **Steps to Reproduce** - Detailed steps to reproduce the issue +3. **Impact Assessment** - Potential impact and severity +4. **Affected Versions** - Which versions are affected +5. **Suggested Fix** - If you have suggestions for fixing the vulnerability (optional) +6. **Proof of Concept** - Code or demonstration that shows the vulnerability (if applicable) + +### Response Timeline + +We take security seriously and aim to respond to security reports within the following timeframes: + +| Severity | Initial Response | Assessment Complete | Fix Released | +|----------|-----------------|---------------------|--------------| +| Critical | Within 24 hours | 7 days | 14 days | +| High | Within 48 hours | 14 days | 30 days | +| Medium | Within 7 days | 30 days | 60 days | +| Low | Within 14 days | 60 days | 90 days | + +### Our Process + +1. **Acknowledgment** - We'll acknowledge receipt of your report within the initial response time +2. **Assessment** - We'll assess the vulnerability and determine its severity +3. **Communication** - We'll keep you informed of our progress +4. **Fix** - We'll develop and test a fix +5. **Disclosure** - We'll coordinate disclosure with you +6. **Release** - We'll release the fix and publish a security advisory +7. **Credit** - We'll credit you in our security advisory (if you wish) + +## Security Best Practices + +### For Users + +- Always use the latest version of our software +- Enable two-factor authentication (2FA) on your accounts +- Keep your dependencies up to date +- Report any suspicious activity immediately + +### For Contributors + +- Never commit secrets, API keys, or credentials to the repository +- Use environment variables for sensitive configuration +- Follow secure coding practices +- Review our Contributing Guide for security requirements + +## Security Measures + +We implement the following security measures: + +- ✅ Automated dependency scanning with Dependabot +- ✅ Code scanning with CodeQL +- ✅ Branch protection with required reviews +- ✅ Signed commits (where applicable) +- ✅ Regular security audits +- ✅ Responsible disclosure program + +## Bug Bounty + +We may offer bug bounties for significant security vulnerabilities at our discretion. +Bounties are determined based on: + +- Severity of the vulnerability +- Quality of the report +- Potential impact on users +- Novelty of the vulnerability + +Please contact us at [security@sensibleanalytics.co](mailto:security@sensibleanalytics.co) to discuss bounty eligibility. + +## Past Security Advisories + +We maintain a list of past security advisories in our [Security Advisories](https://github.com/Sensible-Analytics/REPO_NAME/security/advisories) section. + +## Contact + +- 📧 Email: [security@sensibleanalytics.co](mailto:security@sensibleanalytics.co) +- 🔐 PGP Key: [Available upon request] + +Thank you for helping keep Sensible Analytics and our users safe! \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index c4653bb..024cfb4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,10 +1,12 @@ # Agent Instructions - Branch Protection Workflow -## ⚠️ IMPORTANT: This repository has branch protection enabled +> ⚠️ **For complete agent guidelines, see [.github/AGENTS.md](.github/AGENTS.md)** -Direct pushes to `main`/`master` are **BLOCKED**. All changes must go through Pull Requests. +## Quick Reference + +### Branch Protection -## Required Workflow +This repository has branch protection enabled. All changes must go through Pull Requests. ### Making Changes @@ -89,7 +91,7 @@ git checkout main && git pull --- -## 📄 Governance Documents +## Governance Documents -- [Code of Conduct](docs/CODE_OF_CONDUCT.md) -- [Security Policy](docs/SECURITY.md) +- [Code of Conduct](.github/CODE_OF_CONDUCT.md) +- [Security Policy](.github/SECURITY.md) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..805855f --- /dev/null +++ b/docs/README.md @@ -0,0 +1,89 @@ +# Qullamaggie Scanner - Documentation + +**Version**: 1.0 | **Last Updated**: 2026-04-27 + +Welcome to the Qullamaggie Scanner documentation. This page explains the repository structure and helps you get started. + +--- + +## Quick Links + +| Resource | Location | +|----------|----------| +| **Agent Instructions** | [.github/AGENTS.md](.github/AGENTS.md) | +| **Security Policy** | [.github/SECURITY.md](.github/SECURITY.md) | +| **Code of Conduct** | [.github/CODE_OF_CONDUCT.md](.github/CODE_OF_CONDUCT.md) | +| **Frontend README** | [frontend/README.md](frontend/README.md) | + +--- + +## Repository Structure + +``` +qullamaggie_scanner/ +├── .github/ # GitHub config, workflows, governance docs +│ ├── AGENTS.md # ← Main agent instructions +│ ├── SECURITY.md # ← Security policy +│ ├── CODE_OF_CONDUCT.md +│ ├── workflows/ # CI/CD pipelines +│ └── ISSUE_TEMPLATE/ +├── docs/ # Architecture documentation +│ ├── README.md # ← You are here +│ ├── adr/ # Architecture Decision Records +│ └── architecture/ # Technical diagrams +├── frontend/ # Main scanner UI (React + Vite) +├── ibkr_tws/ # Python IBKR service (legacy) +├── tests/ # Test suite +└── ui/ # Streamlit UI (legacy) +``` + +--- + +## Getting Started + +### Frontend (Main Application) + +```bash +cd frontend +npm install +npm run dev +``` + +### Running Tests + +```bash +npm test # Run tests +npm run lint # Lint check +npm run build # Production build +``` + +--- + +## Domains + +| Domain | Tech Stack | Description | +|--------|-----------|------------| +| `frontend/` | React, TypeScript, Vite | Main scanner application | +| `ibkr_tws/` | Python | Interactive Brokers service (legacy) | +| `tests/` | Vitest, Playwright | Test suite | +| `ui/` | Streamlit | Legacy Streamlit UI | + +--- + +## Architecture + +See [docs/architecture/](architecture/) for technical diagrams and design documents. + +See [docs/adr/](adr/) for Architecture Decision Records (ADRs). + +--- + +## Contributing + +1. Create a feature branch (`feat/your-feature`) +2. Make your changes +3. Commit with descriptive message +4. Push and create a Pull Request +5. CI must pass before merge + +See [.github/AGENTS.md](.github/AGENTS.md) for full guidelines. \ No newline at end of file diff --git a/frontend/src/components/EmptyState.tsx b/frontend/src/components/EmptyState.tsx new file mode 100644 index 0000000..8c1799b --- /dev/null +++ b/frontend/src/components/EmptyState.tsx @@ -0,0 +1,17 @@ +interface EmptyStateProps {} + +export function EmptyState({}: EmptyStateProps) { + return ( +
+
+
[]
+

+ No signals found +

+

+ Try lowering the min score or expanding your universe +

+
+
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/ExportToolbar.tsx b/frontend/src/components/ExportToolbar.tsx new file mode 100644 index 0000000..5557afb --- /dev/null +++ b/frontend/src/components/ExportToolbar.tsx @@ -0,0 +1,34 @@ +import type { StockMetrics } from '../utils/calculations'; +import { exportToCSV, exportToWatchlist, exportToJson } from '../utils/export'; + +interface ExportToolbarProps { + results: StockMetrics[]; +} + +export function ExportToolbar({ results }: ExportToolbarProps) { + return ( +
+ + + +
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/ResultsTable.tsx b/frontend/src/components/ResultsTable.tsx index 407ee9a..de3c052 100644 --- a/frontend/src/components/ResultsTable.tsx +++ b/frontend/src/components/ResultsTable.tsx @@ -1,5 +1,7 @@ import { useScannerStore } from '../store/scannerStore'; -import { exportToCSV, exportToWatchlist, exportToJson } from '../utils/export'; +import { ScanProgressSkeleton } from './ScanProgressSkeleton'; +import { EmptyState } from './EmptyState'; +import { ExportToolbar } from './ExportToolbar'; export default function ResultsTable() { const { results, selectedSymbol, setSelectedSymbol, progress, isLoading } = useScannerStore(); @@ -11,49 +13,11 @@ export default function ResultsTable() { }; if (isLoading && progress) { - return ( -
-
- - Scanning markets... -
-
-
- - {progress.symbol} - - {progress.current}/{progress.total} -
-
-
-
-
-
-
-
-
-
-
- ); + return ; } if (!results.length) { - return ( -
-
-
[]
-

- No signals found -

-

- Try lowering the min score or expanding your universe -

-
-
- ); + return ; } return ( @@ -62,29 +26,7 @@ export default function ResultsTable() {

Scan Results ({results.length} stocks)

-
- - - -
+
diff --git a/frontend/src/components/ScanProgressSkeleton.tsx b/frontend/src/components/ScanProgressSkeleton.tsx new file mode 100644 index 0000000..baca086 --- /dev/null +++ b/frontend/src/components/ScanProgressSkeleton.tsx @@ -0,0 +1,33 @@ +interface ScanProgressSkeletonProps { + progress: { current: number; total: number; symbol: string }; +} + +export function ScanProgressSkeleton({ progress }: ScanProgressSkeletonProps) { + return ( +
+
+ + Scanning markets... +
+
+
+ + {progress.symbol} + + {progress.current}/{progress.total} +
+
+
+
+
+
+
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/ScannerControls.tsx b/frontend/src/components/ScannerControls.tsx index 7793f50..254e839 100644 --- a/frontend/src/components/ScannerControls.tsx +++ b/frontend/src/components/ScannerControls.tsx @@ -3,6 +3,8 @@ import { useScannerStore } from '../store/scannerStore'; import { calculateMetrics, filterAndSortResults } from '../utils/calculations'; import { fetchBatchHistoricalData, STOCK_UNIVERSES, type StockUniverse } from '../services/stockApi'; import { UniverseSelect, FilterInputs } from './FilterComponents'; +import { ScannerStatus } from './ScannerStatus'; +import { ScoringLegend } from './ScoringLegend'; export default function ScannerControls() { const { @@ -76,12 +78,7 @@ export default function ScannerControls() {

Scanner Config

-
- - - {isLoading ? 'running' : 'ready'} - -
+
@@ -114,33 +111,7 @@ export default function ScannerControls() {
)} -
-

- 20-Point System -

-
    -
  • - ADR% - 5 max -
  • -
  • - RS Momentum - 4 max -
  • -
  • - EMA Alignment - 7 max -
  • -
  • - Tightness - 2 max -
  • -
  • - Volume Surge - 2 max -
  • -
-
+
); } diff --git a/frontend/src/components/ScannerStatus.tsx b/frontend/src/components/ScannerStatus.tsx new file mode 100644 index 0000000..dad470d --- /dev/null +++ b/frontend/src/components/ScannerStatus.tsx @@ -0,0 +1,14 @@ +interface ScannerStatusProps { + isLoading: boolean; +} + +export function ScannerStatus({ isLoading }: ScannerStatusProps) { + return ( +
+ + + {isLoading ? 'running' : 'ready'} + +
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/ScoringLegend.tsx b/frontend/src/components/ScoringLegend.tsx new file mode 100644 index 0000000..999cd3b --- /dev/null +++ b/frontend/src/components/ScoringLegend.tsx @@ -0,0 +1,33 @@ +interface ScoringLegendProps {} + +export function ScoringLegend({}: ScoringLegendProps) { + return ( +
+

+ 20-Point System +

+
    +
  • + ADR% + 5 max +
  • +
  • + RS Momentum + 4 max +
  • +
  • + EMA Alignment + 7 max +
  • +
  • + Tightness + 2 max +
  • +
  • + Volume Surge + 2 max +
  • +
+
+ ); +} \ No newline at end of file From 223c1e020669c1f110ad9fb2bf9e065af8f35d50 Mon Sep 17 00:00:00 2001 From: Prabhat Ranjan Date: Mon, 27 Apr 2026 08:28:18 +1000 Subject: [PATCH 2/2] fix: remove empty interface declarations causing lint errors --- frontend/src/components/EmptyState.tsx | 4 +--- frontend/src/components/ScoringLegend.tsx | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/EmptyState.tsx b/frontend/src/components/EmptyState.tsx index 8c1799b..d742a51 100644 --- a/frontend/src/components/EmptyState.tsx +++ b/frontend/src/components/EmptyState.tsx @@ -1,6 +1,4 @@ -interface EmptyStateProps {} - -export function EmptyState({}: EmptyStateProps) { +export function EmptyState() { return (
diff --git a/frontend/src/components/ScoringLegend.tsx b/frontend/src/components/ScoringLegend.tsx index 999cd3b..a243499 100644 --- a/frontend/src/components/ScoringLegend.tsx +++ b/frontend/src/components/ScoringLegend.tsx @@ -1,6 +1,4 @@ -interface ScoringLegendProps {} - -export function ScoringLegend({}: ScoringLegendProps) { +export function ScoringLegend() { return (