A modern web application for Vietnam stock portfolio analysis featuring a React frontend with Ant Design and Flask backend using vnstock and quantstats.
- Modern React UI: Built with Vite, React Router, and Ant Design components
- Dual Interface: Both modern React SPA and legacy HTML templates supported
- Real-time Analysis: Fetches historical price data for Vietnam stock tickers using vnstock
- Interactive Results: Displays portfolio performance metrics, charts, and downloadable QuantStats HTML tearsheet
- Responsive Design: Mobile-first design with Ant Design's responsive grid system
- Form Validation: Client-side and server-side validation for portfolio inputs
- Session Management: Results stored in Flask sessions for seamless user experience
- Docker Support: Containerization with GitHub Container Registry workflow
- Cloud Deployment: Render.com deployment configuration
- app.py: Flask backend with JSON API and HTML template support
- frontend/: Vite React application with Ant Design components
- src/components/: React components (PortfolioForm, ResultsPage)
- src/App.jsx: Main app with routing configuration
- data_loader.py: Data fetching module for Vietnam stock data
- templates/: Legacy HTML templates (Flask/Jinja2)
- static/: Static assets, CSS, JS, generated reports, and React build output
- tests/: Unit and integration tests
- Create and activate a virtual environment
python -m venv venv
source venv/bin/activate # macOS/Linux
# or
venv\Scripts\activate # Windows
- Install Python dependencies
pip install uv
uv pip install --all --upgrade --refresh
- Set environment variables
export SECRET_KEY=your-secret-key-here
- Install Node.js dependencies
cd frontend
npm install
- Build React app for production (optional)
npm run build
# Terminal 1: Start Flask backend
python app.py
# Terminal 2: Start React frontend
cd frontend && npm run dev
- Flask API: http://localhost:5001
- React App: http://localhost:5173
python app.py
- Full app: http://localhost:5001
The React app builds to /static/react-build/
and is automatically served by Flask when built.
The data loader is implemented in data_loader.py
:
from data_loader import fetch_historical_data, get_close_prices
symbols = ['REE', 'FMC', 'DHC']
data = fetch_historical_data(symbols, '2024-01-01', '2024-03-19')
close_prices = get_close_prices(data, symbols)
- Unit and integration tests are located in
tests/
- Tests verify:
- Routing and redirection to the QuantStats HTML report
- HTML report file creation
- Matplotlib backend is set to 'Agg' (for server-side rendering)
To run all tests:
pytest tests/test_app.py
- Requires Python >= 3.9
- Matplotlib GUI errors: The backend is set to
'Agg'
for server-side image generation. If you see errors about "main thread is not in main loop", ensure this setting is present at the top ofapp.py
:import matplotlib matplotlib.use('Agg')
- Navigation: For best UX, add a "Back to Home" button/link in the QuantStats HTML report pointing to
/
. - Static report cleanup: The generated HTML report (
static/reports/quantstats-results.html
) may be overwritten on each new analysis.
This repository includes an automated workflow to build and publish Docker images to GitHub Container Registry (GHCR) on every push to main
or master
branches.
- Trigger: Workflow runs automatically on push to main/master branches
- Build: Uses Docker Buildx for multi-platform builds
- Publish: Pushes to
ghcr.io/{username}/quantstatswebapp
- Tags: Includes branch name, commit SHA, and
latest
for main branch
- Enable GitHub Packages: Ensure GitHub Packages is enabled for your repository
- Set Permissions: The workflow automatically uses
GITHUB_TOKEN
for authentication - View Images: Published images appear in your repository's "Packages" section
docker pull ghcr.io/{your-username}/quantstatswebapp:latest
docker run -p 5000:5000 -e PORT=5000 ghcr.io/{your-username}/quantstatswebapp:latest
If you want to build locally:
docker build -t quantstatswebapp .
docker run -p 5000:5000 -e PORT=5000 quantstatswebapp
The container accepts the following environment variables:
PORT
: Port to run the application on (default: 5001)
See CHANGELOG.md for detailed version history and release notes.
MIT