Extending FinLab Development Guide
This document explains how to contribute code, set up the development environment, follow code style conventions, and extend FinLab's functionality.
Development Environment Setup
1. Fork and Clone the Project
# Fork finlab-python/finlab to your GitHub account
# Clone your fork
git clone https://github.com/YOUR_USERNAME/finlab.git
cd finlab
# Add upstream remote
git remote add upstream https://github.com/finlab-python/finlab.git
2. Install Development Dependencies
# Use uv for dependency management
pip install uv
# Install all development dependencies
uv sync --all-groups
# Or use pip
pip install -e ".[dev,docs,test]"
3. Run Tests
# Run all tests
uv run pytest
# Run specific tests
uv run pytest tests/backtest_test.py
# Run tests with coverage report
uv run pytest --cov=finlab --cov-report=html
Code Style Guide
Python Style
Follow PEP 8 and the Google Python Style Guide.
# Good example
def calculate_moving_average(prices, window=20):
"""計算移動平均
Args:
prices (pd.Series): 價格序列
window (int): 視窗大小,預設 20
Returns:
pd.Series: 移動平均序列
"""
return prices.rolling(window).mean()
# Bad example
def calc_ma(p, w=20): # 命名不清楚
return p.rolling(w).mean() # 缺少文檔
Docstring Format
Use Google style:
def sim(position, resample='M', **kwargs):
"""執行回測
Args:
position (pd.DataFrame): 持倉訊號
resample (str): 重採樣頻率
**kwargs: 其他參數
Returns:
Report: 回測報告物件
Raises:
ValueError: 當 position 格式不正確時
Examples:
```python
position = close > close.average(20)
report = sim(position, resample='M')
```
"""
pass
Extension Examples
1. Add a Custom Technical Indicator
Add a method to finlab/dataframe.py:
class FinlabDataFrame(pd.DataFrame):
# ... existing code
def custom_indicator(self, param1, param2=10):
"""自訂技術指標
Args:
param1 (float): 參數 1
param2 (int): 參數 2,預設 10
Returns:
FinlabDataFrame: 計算結果
"""
result = self.rolling(param2).apply(
lambda x: your_calculation(x, param1)
)
return FinlabDataFrame(result)
2. Add a Custom Analysis Module
Inherit from the Analysis class:
# finlab/analysis/custom_analysis.py
from finlab.analysis import Analysis
class CustomAnalysis(Analysis):
def calculate_trade_info(self, report):
"""計算額外交易資訊"""
custom_data = ... # 你的邏輯
return [['custom_field', custom_data, 'entry_sig_date']]
def analyze(self, report):
"""執行分析"""
trades = report.get_trades()
result = ... # 你的分析邏輯
return result
def display(self):
"""顯示結果"""
return self.result
3. Add a Custom Market
Inherit from the Market class:
# finlab/markets/custom.py
from finlab.market import Market
import pandas as pd
class CustomMarket(Market):
@staticmethod
def get_name():
return 'custom_market'
def get_price(self, trade_at_price='close', adj=True):
# 你的資料載入邏輯
df = pd.read_csv('path/to/data.csv')
return df
Testing Guide
Test Structure
# tests/test_custom_feature.py
import unittest
from finlab import data
from finlab.dataframe import FinlabDataFrame
class TestCustomFeature(unittest.TestCase):
def setUp(self):
"""測試前準備"""
self.close = data.get('price:收盤價')
def test_custom_indicator(self):
"""測試自訂指標"""
result = self.close.custom_indicator(param1=0.5, param2=10)
# Check result type
self.assertIsInstance(result, FinlabDataFrame)
# Check result shape
self.assertEqual(result.shape, self.close.shape)
# Check result values
self.assertFalse(result.isna().all().all())
def tearDown(self):
"""測試後清理"""
pass
Contribution Workflow
1. Create a Branch
# Update main branch
git checkout main
git pull upstream main
# Create a new branch
git checkout -b feature/your-feature-name
2. Develop and Test
# Write your code
# ...
# Run tests
uv run pytest
# Run linter
uv run ruff check .
uv run ruff format .
3. Commit Your Code
# Commit changes
git add .
git commit -m "feat: add custom indicator"
# Push to your fork
git push origin feature/your-feature-name
4. Create a Pull Request
- Go to your fork on GitHub
- Click "New Pull Request"
- Fill in the PR description (explain the changes and their purpose)
- Wait for code review