Skip to content

Commit c505f3e

Browse files
chore: Add tests, linting, CI workflow, and improved docs
- Add 19 pytest tests for TradingSimulator, simulate_drawdown, metric_box - Configure ruff (pyproject.toml) with import sorting and style checks - Add GitHub Actions CI workflow (Python 3.10 + 3.12, ruff + pytest) - Rewrite README with setup, run, test, lint instructions and methodology - Add CHANGELOG.md with full change summary and verification commands - Fix import ordering across all files (ruff I001) - Fix trailing whitespace in Welcome.py (ruff W291) - Use flexible version ranges in requirements.txt for Python 3.10-3.13 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d5f094e commit c505f3e

File tree

12 files changed

+346
-77
lines changed

12 files changed

+346
-77
lines changed

.github/workflows/ci.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
lint-and-test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: ["3.10", "3.12"]
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- name: Set up Python ${{ matrix.python-version }}
20+
uses: actions/setup-python@v5
21+
with:
22+
python-version: ${{ matrix.python-version }}
23+
24+
- name: Install dependencies
25+
run: |
26+
python -m pip install --upgrade pip
27+
pip install -r requirements.txt
28+
pip install ruff pytest
29+
30+
- name: Lint with ruff
31+
run: ruff check .
32+
33+
- name: Run tests
34+
run: pytest -v

CHANGELOG.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Changelog
2+
3+
## [1.1.0] - 2026-02-19
4+
5+
### Audit & Documentation
6+
- Added `docs/audit_report.md` with full codebase audit (blockers, majors, minors)
7+
- Added `CHANGELOG.md`
8+
- Improved `README.md` with setup, run, test, and lint instructions
9+
10+
### Bug Fixes (Blockers)
11+
- **Removed duplicate `st.set_page_config`** from Asset Correlation page — previously crashed on navigation
12+
- **Fixed JavaScript syntax error** in `utils/style.py` footer (trailing `.` replaced with `;`)
13+
14+
### Bug Fixes (Majors)
15+
- **Removed duplicated `SLIDER_CONFIGS`** from page 1; now imports from `config.slider_configs`
16+
- **Removed unnecessary `sys.path.append` hack** from page 1
17+
- **Fixed malformed HTML comment** (unclosed `<!---`) in page 1
18+
- **Added missing `utils/__init__.py`** for reliable imports
19+
- **Cached `simulate_drawdown`** so it doesn't rerun 10,000 simulations on every slider change
20+
- **Restructured Asset Correlation page** — wrapped all logic in functions, added "Run Simulation" button to avoid recomputing on every widget interaction
21+
- **Removed unused functions** (`average_trade_progression`, `simulate_portfolio_risk`) from `risk_simulation.py`
22+
- **Added footer to page 2** for consistency with page 1
23+
- **Updated pandas** from 1.5.3 to 2.x (compatible with modern numpy/Python)
24+
25+
### Bug Fixes (Minors)
26+
- **Fixed win-rate chart annotation** x-value mismatch (was `f"{value}"`, now `f"{value}%"` to match bar labels)
27+
28+
### Refactoring
29+
- Extracted `_viridis_colors` and `_add_expected_return_annotation` shared helpers in page 1
30+
- Extracted `simulate_portfolio`, `calculate_metrics`, `style_metrics_table` in page 2
31+
- Moved drawdown simulation to `utils/risk_simulation.py` as `simulate_drawdown`
32+
- Added type hints throughout `risk_simulation.py`
33+
- Reduced metric display duplication with loop pattern
34+
35+
### Tooling & CI
36+
- Added `pyproject.toml` with ruff and pytest configuration
37+
- Added `tests/` with 19 tests covering `TradingSimulator`, `simulate_drawdown`, and `metric_box`
38+
- Added `.github/workflows/ci.yml` running ruff + pytest on Python 3.10 and 3.12
39+
- Updated `requirements.txt` to use flexible version ranges for Python 3.10-3.13 compatibility
40+
41+
### Verification Notes
42+
43+
```bash
44+
# Run the app
45+
streamlit run Welcome.py
46+
47+
# Run tests
48+
pytest -v
49+
50+
# Run linter
51+
ruff check .
52+
```

README.md

Lines changed: 62 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99

1010
## Features
1111

12-
- **Risk-Return Analysis**: Simulate various risk-return scenarios using adjustable parameters like win rate, trades per year, risk per trade, and return per unit of risk (RPUR).
13-
- **Asset Correlation Simulator**: Generate and visualize portfolio performance based on customizable asset correlation matrices, with options for single or random correlation ranges.
12+
- **Risk-Return Analysis**: Simulate various risk-return scenarios using adjustable parameters like win rate, trades per year, risk per trade, and return per unit of risk (RPUR). Uses Monte Carlo simulation to estimate expected returns and drawdowns.
13+
- **Asset Correlation Simulator**: Generate and visualize portfolio performance based on customizable asset correlation matrices, with options for single or random correlation ranges. Computes Sharpe, Sortino, Calmar ratios, max drawdown, and more.
1414

1515
## Screenshots
1616

@@ -28,105 +28,104 @@
2828

2929
## Project Structure
3030

31-
```bash
31+
```
3232
RiskSim/
33-
33+
├── Welcome.py # Streamlit entrypoint (landing page)
3434
├── pages/
35-
│ ├── 1_🎯_risk_return_analysis.py # Main page for risk-return simulations
36-
│ └── 2_📈_asset_correlation.py # Main page for asset correlation simulations
37-
35+
│ ├── 1_🎯_risk_return_analysis.py # Risk-return Monte Carlo simulation
36+
│ └── 2_📈_asset_correlation.py # Asset correlation portfolio simulator
3837
├── config/
39-
│ ├── slider_configs.py # Configuration for slider inputs in the Streamlit UI
40-
│ └── __init__.py # Init file for config
41-
38+
│ └── slider_configs.py # Centralized slider defaults and bounds
4239
├── utils/
43-
│ ├── risk_simulation.py # Core simulation logic for risk management and portfolio performance
44-
│ └── style.py # Styling and layout helpers for Streamlit app
45-
46-
├── docs/images/ # Image assets for documentation and app UI
47-
│ ├── header_image.jpg
48-
│ ├── image.png
49-
│ ├── image_1.png
50-
│ ├── image_2.png
51-
│ ├── image_3.png
52-
│ └── image_4.png
53-
54-
├── venv/ # Python virtual environment (optional)
55-
├── Welcome.py # Main entry point for the app
56-
├── README.md # This file
57-
├── LICENSE.txt # License information
58-
├── .gitignore # Files and directories to be ignored by git
59-
└── requirements.txt # Python dependencies
40+
│ ├── risk_simulation.py # TradingSimulator class + drawdown simulation
41+
│ └── style.py # Footer and metric box HTML helpers
42+
├── tests/
43+
│ ├── test_risk_simulation.py # Tests for simulation logic
44+
│ └── test_style.py # Tests for style helpers
45+
├── docs/
46+
│ ├── audit_report.md # Codebase audit report
47+
│ └── images/ # Screenshots and header image
48+
├── .github/workflows/ci.yml # GitHub Actions CI (ruff + pytest)
49+
├── pyproject.toml # Ruff and pytest configuration
50+
├── requirements.txt # Python dependencies
51+
├── CHANGELOG.md # Change log
52+
├── LICENSE.txt # MIT License
53+
└── README.md
6054
```
6155

62-
## Installation
63-
64-
To get started with **RiskSim**, follow these steps:
56+
## Setup
6557

6658
### Prerequisites
6759

68-
Ensure you have Python 3.8+ installed. You'll also need `pip` to install the required dependencies.
60+
- Python 3.10 or higher
61+
- pip
6962

70-
### Clone the Repository
63+
### Installation
7164

7265
```bash
73-
git clone https://github.com/your-username/RiskSim.git
66+
# Clone the repository
67+
git clone https://github.com/chrisduvillard/RiskSim.git
7468
cd RiskSim
69+
70+
# Create and activate a virtual environment
71+
python -m venv .venv
72+
# On Windows:
73+
.venv\Scripts\activate
74+
# On macOS/Linux:
75+
source .venv/bin/activate
76+
77+
# Install dependencies
78+
pip install -r requirements.txt
7579
```
7680

77-
### Create a Virtual Environment (Optional but Recommended)
81+
### Run the App
7882

7983
```bash
80-
python -m venv venv
81-
source venv/bin/activate # On Windows use `venv\Scriptsctivate`
84+
streamlit run Welcome.py
8285
```
8386

84-
### Install Dependencies
87+
This launches the app in your browser. Use the sidebar to navigate between pages.
8588

86-
Install the required Python packages by running:
89+
### Run Tests
8790

8891
```bash
89-
pip install -r requirements.txt
92+
pip install pytest
93+
pytest -v
9094
```
9195

92-
### Run the Application
93-
94-
Start the application using Streamlit:
96+
### Run Linter
9597

9698
```bash
97-
streamlit run Welcome.py
99+
pip install ruff
100+
ruff check .
98101
```
99102

100-
This will launch the **RiskSim** app in your browser.
101-
102-
## Usage
103+
## Methodology
103104

104105
### Risk-Return Analysis
105106

106-
- Navigate to the **Risk-Return Analysis** section to simulate various risk-return scenarios.
107-
- You can adjust the number of trades per year, win rate, risk per trade, and RPUR.
108-
- View detailed metrics such as expected drawdown, Sharpe ratio, and more.
109-
107+
The Risk-Return Analysis page uses **Monte Carlo simulation** to model trading outcomes. Given a set of parameters (number of trades per year, win rate, risk per trade, and return per unit of risk), the simulator:
108+
109+
1. Generates random trade sequences where each trade is a win or loss based on the specified win rate
110+
2. Applies compounding: each trade's P&L is a percentage of the *current* AUM (not the initial capital)
111+
3. Repeats this process thousands of times to build a distribution of outcomes
112+
4. Reports average returns across different RPUR levels and win rates
113+
5. Estimates the expected maximum consecutive-loss drawdown
114+
110115
### Asset Correlation Simulation
111116

112-
- Use the **Asset Correlation** page to simulate portfolios with different asset correlations.
113-
- Choose between fixed or random correlation settings.
114-
- Visualize performance and compute portfolio metrics under various correlation regimes.
117+
The Asset Correlation page generates synthetic multi-asset portfolios using **correlated geometric Brownian motion**:
115118

119+
1. Builds a correlation matrix (uniform or random within a range), ensuring positive definiteness
120+
2. Constructs a covariance matrix from per-asset volatilities and the correlation matrix
121+
3. Samples multivariate normal log-returns and converts to price paths
122+
4. Computes per-asset and portfolio performance metrics (Sharpe, Sortino, Calmar, max drawdown)
123+
5. Sweeps across correlation levels to show how diversification affects portfolio risk
116124

117125
## License
118126

119127
This project is licensed under the MIT License. See the [LICENSE.txt](LICENSE.txt) file for details.
120128

121129
## Author
122130

123-
Made with ❤️ by [Chris](https://www.linkedin.com/in/christopheduvillard/)
124-
125-
## Acknowledgments
126-
127-
- Streamlit
128-
- Plotly
129-
130-
## Contributing
131-
132-
Contributions are welcome! Feel free to fork the repository, make improvements, and submit a pull request.
131+
Made by [Christophe Duvillard](https://www.linkedin.com/in/christopheduvillard/)

Welcome.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
#### 🔍 Explore the Features of RiskSim:
4545
**Navigate Through the App Using the Sidebar:**
4646
47-
- **📊 Risk-Return Analysis**:
47+
- **📊 Risk-Return Analysis**:
4848
- Simulate how various trading parameters, such as win rate, number of trades, and risk per trade, affect your overall performance.
4949
- Use Monte Carlo simulations to explore potential outcomes based on your trading strategy.
5050
- Visualize the trade-off between risk and return to optimize your approach.

pages/1_🎯_risk_return_analysis.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
from utils.style import footer, metric_box
2-
from utils.risk_simulation import TradingSimulator, simulate_drawdown
3-
from config.slider_configs import SLIDER_CONFIGS
4-
import streamlit as st
1+
import numpy as np
52
import plotly.express as plotly_express
63
import plotly.graph_objects as go
7-
import numpy as np
4+
import streamlit as st
5+
6+
from config.slider_configs import SLIDER_CONFIGS
7+
from utils.risk_simulation import TradingSimulator, simulate_drawdown
8+
from utils.style import footer, metric_box
89

910

1011
def _viridis_colors(values: list[float]) -> list[str]:

pages/2_📈_asset_correlation.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
import streamlit as st
21
import numpy as np
32
import pandas as pd
4-
import plotly.graph_objs as go
53
import plotly.express as px
4+
import plotly.graph_objs as go
5+
import streamlit as st
6+
67
from utils.style import footer
78

89

pyproject.toml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[project]
2+
name = "risksim"
3+
version = "1.0.0"
4+
description = "Risk-Return and Asset Correlation Simulator"
5+
requires-python = ">=3.10"
6+
7+
[tool.ruff]
8+
target-version = "py310"
9+
line-length = 100
10+
11+
[tool.ruff.lint]
12+
select = ["E", "F", "W", "I"]
13+
ignore = [
14+
"E501", # line too long — handled by formatter where practical
15+
]
16+
17+
[tool.ruff.lint.per-file-ignores]
18+
"pages/*.py" = ["E402"] # module-level imports not at top (Streamlit pages)
19+
20+
[tool.pytest.ini_options]
21+
testpaths = ["tests"]

requirements.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
numpy==1.26.4
2-
plotly==5.22.0
3-
streamlit==1.35.0
4-
htbuilder==0.6.2
5-
pandas==2.2.3
1+
numpy>=1.26.4,<3
2+
plotly>=5.22.0,<6
3+
streamlit>=1.35.0,<2
4+
htbuilder>=0.6.2,<1
5+
pandas>=2.0,<3

tests/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)