Skip to content

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

  1. Go to your fork on GitHub
  2. Click "New Pull Request"
  3. Fill in the PR description (explain the changes and their purpose)
  4. Wait for code review

Reference Resources