Skip to content

Commit d37462d

Browse files
Add new finance utilities, linting configuration, and GitHub Actions workflow
- Added Beta Calculator utility with levered/unlevered beta, upside/downside beta - Added Volatility Calculator with historical, Parkinson, Garman-Klass, EWMA methods - Added Correlation Analysis utility with Pearson, rolling, EWMA, tail correlation - Set up Ruff linting with pyproject.toml configuration - Added GitHub Actions workflow for automated linting - Added requirements.txt and requirements-dev.txt - Updated README to v1.6.0 with lint status badge - Formatted all Python files with Ruff - Added comprehensive tests for new utilities
1 parent 2280020 commit d37462d

85 files changed

Lines changed: 1391 additions & 927 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/lint.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Lint
2+
3+
on:
4+
push:
5+
branches: [ main, master ]
6+
pull_request:
7+
branches: [ main, master ]
8+
9+
jobs:
10+
lint:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- name: Set up Python
17+
uses: actions/setup-python@v5
18+
with:
19+
python-version: '3.11'
20+
21+
- name: Install dependencies
22+
run: |
23+
python -m pip install --upgrade pip
24+
pip install ruff
25+
26+
- name: Run Ruff linter
27+
run: ruff check . --output-format=github
28+
29+
- name: Run Ruff formatter check
30+
run: ruff format --check .

README.md

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,22 @@
1-
# Learn-Quant: Wanna learn quant through code? (v1.5.0)
1+
# Learn-Quant: Wanna learn quant through code? (v1.6.0)
2+
3+
[![Lint](https://github.com/MeridianAlgo/Learn-Quant/actions/workflows/lint.yml/badge.svg)](https://github.com/MeridianAlgo/Learn-Quant/actions/workflows/lint.yml)
24

35
Welcome to **Learn-Quant**, your comprehensive, beginner-friendly toolkit for mastering quantitative finance, algorithmic trading, and Python programming!
46

57
---
68

79
## What is This Repo?
810
A massive, curated collection of Python modules, strategies, and reference materials designed to help you:
9-
- **NEW IN v1.5.0:** Master advanced strategies like **Pairs Trading** and **Kalman Filters**.
10-
- **NEW IN v1.5.0:** Learn high-frequency data engineering with **AsyncIO**.
11+
- Master advanced strategies like **Pairs Trading** and **Kalman Filters**.
12+
- Learn high-frequency data engineering with **AsyncIO**.
1113
- Understand core quant concepts (Risk, Return, Derivatives, Portfolio Theory).
1214
- Practice with realistic trading algorithms and backtesting simulators.
1315
- Learn Python from scratch: from basic loops to advanced OOP and Decorators.
1416
- Master computer science algorithms (Sorting, Searching, Graphs, DP) tailored for finance.
1517

1618
---
1719

18-
## NEW: Interactive Learning Platform
19-
20-
**Experience algorithms like never before with our browser-based learning platform!**
21-
22-
### Features
23-
- **Interactive Algorithm Browser**: Filter by category and difficulty.
24-
- **In-Browser Python Execution**: Run code directly using PyScript.
25-
- **Live Code Editor**: Edit and test algorithms in real-time.
26-
- **Visual Output**: See results instantly in your browser.
27-
2820
### Quick Start
2921
```bash
3022
# Option 1: Interactive launcher (Recommended)
@@ -65,7 +57,10 @@ python run_learning_platform.py demo
6557
- `UTILS - Quantitative Methods - Regression/` — Beta calculation and factor models.
6658

6759
### Finance Utilities & Simulators
68-
- `UTILS - Strategies - Pairs Trading/`**(New)** Statistical arbitrage simulation.
60+
- `UTILS - Strategies - Pairs Trading/` — Statistical arbitrage simulation.
61+
- `UTILS - Finance - Beta Calculator/`**(New)** Beta, levered/unlevered beta, upside/downside beta.
62+
- `UTILS - Finance - Volatility Calculator/`**(New)** Historical, Parkinson, Garman-Klass, EWMA volatility.
63+
- `UTILS - Finance - Correlation Analysis/`**(New)** Pearson, rolling, EWMA, tail correlation.
6964
- `UTILS - Black-Scholes Option Pricing/` — Valuation of derivatives.
7065
- `UTILS - Portfolio Optimizer/` — Efficient Frontier and Sharpe Ratio maximization.
7166
- `UTILS - Monte Carlo Portfolio Simulator/` — Stress testing portfolios.
@@ -118,6 +113,6 @@ We welcome contributions!
118113

119114
---
120115

121-
**Learn-Quant** v1.5.0
116+
**Learn-Quant** v1.6.0
122117
_Quantitative Finance, Algorithms, and Python Mastery._
123118
_Made by MeridianAlgo_

UTILS - AI Development/chatbot.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@
1010

1111
import os
1212
import sys
13-
from typing import Optional, TYPE_CHECKING
13+
from typing import TYPE_CHECKING, Optional
1414

1515
if TYPE_CHECKING:
1616
from google import genai
1717

1818

19-
def initialize_client() -> Optional["genai.Client"]:
19+
def initialize_client() -> Optional[genai.Client]:
2020
"""Initialise the Gemini client when dependencies and keys are available."""
2121

2222
try:
@@ -37,7 +37,7 @@ def initialize_client() -> Optional["genai.Client"]:
3737
return None
3838

3939

40-
def chat_loop(client: "genai.Client") -> None:
40+
def chat_loop(client: genai.Client) -> None:
4141
"""Interactive chat loop using an initialised Gemini client."""
4242

4343
print("Gemini Chatbot (type 'exit' to quit)")

UTILS - Advanced Python - AsyncIO/async_fetching.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
"""
99

1010
import asyncio
11-
import time
1211
import random
13-
from typing import List, Dict
12+
import time
13+
from typing import Dict, List
1414

1515

1616
async def fetch_ticker(symbol: str) -> Dict[str, float]:

UTILS - Advanced Python - Decorators and Generators/decorators_generators_tutorial.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
- Generators: Memory-efficient iteration (streaming data)
99
"""
1010

11-
import time
1211
import functools
13-
from typing import Iterator, List, Callable
1412
import random
13+
import time
14+
from typing import Callable, Iterator, List
1515

1616

1717
def intro() -> None:
@@ -178,7 +178,7 @@ def price_stream(start_price: float) -> Iterator[float]:
178178
# Consume only 5 items from infinite stream
179179
for i in range(5):
180180
price = next(stream)
181-
print(f" Tick {i+1}: ${price:.2f}")
181+
print(f" Tick {i + 1}: ${price:.2f}")
182182

183183
# 3. Generator Expression (Memory Efficient)
184184
print("\n3. Generator Expression vs List Comprehension:")
@@ -241,7 +241,7 @@ def market_data_feed(tickers: List[str], periods: int):
241241
"""Yield market data for each period."""
242242
for i in range(periods):
243243
data = {
244-
"date": f"2023-01-{i+1:02d}",
244+
"date": f"2023-01-{i + 1:02d}",
245245
"prices": {t: random.uniform(100, 200) for t in tickers},
246246
}
247247
yield data
@@ -301,9 +301,7 @@ def main() -> None:
301301
generators_demo()
302302
practical_example_backtest()
303303
print("\n🎉 Decorators and Generators tutorial complete!")
304-
print(
305-
"Use decorators for cross-cutting concerns and generators for efficient data pipelines."
306-
)
304+
print("Use decorators for cross-cutting concerns and generators for efficient data pipelines.")
307305

308306

309307
if __name__ == "__main__":

UTILS - Advanced Python - Error Handling/error_handling_tutorial.py

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
This module teaches robust error handling for financial applications.
77
"""
88

9-
from typing import Optional, List
109
import logging
10+
from typing import List, Optional
1111

1212

1313
def intro() -> None:
@@ -150,9 +150,7 @@ class InsufficientFundsError(TradingError):
150150
def __init__(self, required: float, available: float):
151151
self.required = required
152152
self.available = available
153-
super().__init__(
154-
f"Insufficient funds: need ${required:,.2f}, have ${available:,.2f}"
155-
)
153+
super().__init__(f"Insufficient funds: need ${required:,.2f}, have ${available:,.2f}")
156154

157155
class InvalidTickerError(TradingError):
158156
"""Raised when ticker symbol is invalid."""
@@ -162,9 +160,7 @@ def __init__(self, ticker: str):
162160
super().__init__(f"Invalid ticker symbol: {ticker}")
163161

164162
# Use custom exceptions
165-
def execute_trade(
166-
ticker: str, price: float, shares: int, account_balance: float
167-
) -> None:
163+
def execute_trade(ticker: str, price: float, shares: int, account_balance: float) -> None:
168164
"""Execute trade with custom error handling."""
169165
# Validate ticker
170166
valid_tickers = ["AAPL", "GOOGL", "MSFT"]
@@ -323,19 +319,15 @@ def assertion_checking() -> None:
323319
print("ASSERTIONS")
324320
print("=" * 60)
325321

326-
def calculate_sharpe_ratio(
327-
returns: List[float], risk_free_rate: float = 0.02
328-
) -> float:
322+
def calculate_sharpe_ratio(returns: List[float], risk_free_rate: float = 0.02) -> float:
329323
"""
330324
Calculate Sharpe ratio with assertions.
331325
332326
Assertions help catch bugs during development.
333327
"""
334328
# Assertions for debugging (removed in production with -O flag)
335329
assert len(returns) > 0, "Returns list cannot be empty"
336-
assert all(
337-
isinstance(r, (int, float)) for r in returns
338-
), "Returns must be numbers"
330+
assert all(isinstance(r, (int, float)) for r in returns), "Returns must be numbers"
339331
assert 0 <= risk_free_rate <= 1, "Risk-free rate must be between 0 and 1"
340332

341333
avg_return = sum(returns) / len(returns)

UTILS - Advanced Python - OOP/oop_tutorial.py

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
This module teaches OOP concepts through building trading and portfolio classes.
77
"""
88

9-
from typing import List, Dict, Optional
109
from datetime import datetime
1110
from decimal import Decimal
11+
from typing import Dict, List, Optional
1212

1313

1414
def intro() -> None:
@@ -70,9 +70,7 @@ def __str__(self) -> str:
7070

7171
def __repr__(self) -> str:
7272
"""Developer-friendly representation."""
73-
return (
74-
f"Stock(ticker='{self.ticker}', price={self.price}, shares={self.shares})"
75-
)
73+
return f"Stock(ticker='{self.ticker}', price={self.price}, shares={self.shares})"
7674

7775

7876
class Portfolio:
@@ -170,10 +168,10 @@ def get_allocation(self) -> Dict[str, float]:
170168

171169
def __str__(self) -> str:
172170
"""String representation of portfolio."""
173-
lines = [f"\n{'='*60}"]
171+
lines = [f"\n{'=' * 60}"]
174172
lines.append(f"Portfolio: {self.name}")
175173
lines.append(f"Created: {self.created_at.strftime('%Y-%m-%d %H:%M:%S')}")
176-
lines.append(f"{'='*60}")
174+
lines.append(f"{'=' * 60}")
177175

178176
for stock in self.holdings.values():
179177
lines.append(f" {stock}")
@@ -194,9 +192,7 @@ class Trade:
194192

195193
trade_counter = 0 # Class variable to track trade IDs
196194

197-
def __init__(
198-
self, ticker: str, entry_price: float, shares: int, direction: str = "LONG"
199-
):
195+
def __init__(self, ticker: str, entry_price: float, shares: int, direction: str = "LONG"):
200196
"""
201197
Initialize a Trade.
202198
@@ -283,9 +279,7 @@ def __init__(self, account_id: str, initial_balance: float):
283279
self.trades: List[Trade] = []
284280
self.open_positions: Dict[str, Trade] = {}
285281

286-
def enter_trade(
287-
self, ticker: str, entry_price: float, shares: int, direction: str = "LONG"
288-
) -> Optional[Trade]:
282+
def enter_trade(self, ticker: str, entry_price: float, shares: int, direction: str = "LONG") -> Optional[Trade]:
289283
"""
290284
Enter a new trade.
291285
@@ -368,15 +362,13 @@ def __str__(self) -> str:
368362
"""String representation."""
369363
perf = self.get_performance_summary()
370364

371-
lines = [f"\n{'='*60}"]
365+
lines = [f"\n{'=' * 60}"]
372366
lines.append(f"Trading Account: {self.account_id}")
373-
lines.append(f"{'='*60}")
367+
lines.append(f"{'=' * 60}")
374368
lines.append(f" Balance: ${perf['current_balance']:,.2f}")
375369
lines.append(f" Total P&L: ${perf['total_pnl']:,.2f}")
376370
lines.append(f" Return: {perf['return_pct']:.2%}")
377-
lines.append(
378-
f" Trades: {perf['total_trades']} ({perf['closed_trades']} closed)"
379-
)
371+
lines.append(f" Trades: {perf['total_trades']} ({perf['closed_trades']} closed)")
380372
lines.append(f" Win Rate: {perf['win_rate']:.1%}")
381373
lines.append("=" * 60)
382374

UTILS - Algorithms - Backtracking/backtracking_algorithms.py

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,7 @@ def backtrack(row, col, index):
116116
if index == len(word):
117117
return True
118118

119-
if (
120-
row < 0
121-
or row >= rows
122-
or col < 0
123-
or col >= cols
124-
or board[row][col] != word[index]
125-
):
119+
if row < 0 or row >= rows or col < 0 or col >= cols or board[row][col] != word[index]:
126120
return False
127121

128122
temp = board[row][col]

UTILS - Algorithms - Dynamic Programming/dynamic_programming.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
A comprehensive collection of DP algorithms with detailed explanations and examples.
44
"""
55

6-
from typing import List, Tuple
76
import sys
7+
from typing import List, Tuple
88

99

1010
def fibonacci_memoization(n: int) -> int:
@@ -167,9 +167,7 @@ def knapsack_01(weights: List[int], values: List[int], capacity: int) -> int:
167167
for i in range(1, n + 1):
168168
for w in range(capacity + 1):
169169
if weights[i - 1] <= w:
170-
dp[i][w] = max(
171-
values[i - 1] + dp[i - 1][w - weights[i - 1]], dp[i - 1][w]
172-
)
170+
dp[i][w] = max(values[i - 1] + dp[i - 1][w - weights[i - 1]], dp[i - 1][w])
173171
else:
174172
dp[i][w] = dp[i - 1][w]
175173

@@ -398,11 +396,7 @@ def matrix_chain_multiplication(dimensions: List[int]) -> int:
398396
dp[i][j] = sys.maxsize
399397

400398
for k in range(i, j):
401-
cost = (
402-
dp[i][k]
403-
+ dp[k + 1][j]
404-
+ dimensions[i - 1] * dimensions[k] * dimensions[j]
405-
)
399+
cost = dp[i][k] + dp[k + 1][j] + dimensions[i - 1] * dimensions[k] * dimensions[j]
406400
dp[i][j] = min(dp[i][j], cost)
407401

408402
return dp[1][n]

0 commit comments

Comments
 (0)