比特幣減半週期量化模型教學:Python 回測與數據驅動的價格循環分析

比特幣減半週期的系統化量化分析教學,涵蓋減半事件的技術機制與歷史數據回顧、Stock-to-Flow(S2F)模型理論與 Python 實現、減半週期價格循環的統計分析、使用比特幣節點 RPC 獲取原始數據的教學、回測框架的構建與策略評估、以及 S2F 和冪律模型的批評分析。本教程提供完整的 Python 程式碼範例,包含數據處理、回歸分析、圖表繪製和回測策略實作。

比特幣減半週期量化模型教學:Python 回測與數據驅動的價格循環分析

概述與學習目標

比特幣的減半事件是加密貨幣市場最重要的時間節點之一。每當比特幣區塊獎勵減半,意味著新比特幣的發行速度減少 50%,這一供給側的變化在歷史上與比特幣價格的顯著上漲密切相關。本教學旨在提供一個系統化的框架,幫助讀者理解減半週期的量化分析方法,並透過 Python 實作回測策略。

本指南涵蓋的主題包括:減半事件的技術機制與歷史數據回顧、Stock-to-Flow(S2F)模型及其 Python 實現、減半週期價格循環的統計分析、使用比特幣節點 RPC 獲取原始數據、回測框架的構建與策略評估、以及模型局限性與風險警示。

第一章:減半事件的技術機制

1.1 比特幣發行機制

比特幣的貨幣政策是預先設定且不可更改的,最大供應量固定為 2,100 萬枚。比特幣透過「減半」(Halving)機制逐步發行,直到所有比特幣被開採完畢。

比特幣區塊獎勵的減半遵循比特幣共識協議中的硬編碼規則。每產生 210,000 個區塊(約每四年),區塊獎勵就會減半一次。這一機制確保了比特幣的稀缺性,符合類似黃金等貴金屬的發行模式。

比特幣發行時間表:

階段時期區塊獎勵比特幣總量佔總量比例
第一減半前2009-201250 BTC0 - 10,500,0000% - 50%
第一減半後2012-201625 BTC10,500,000 - 15,750,00050% - 75%
第二減半後2016-202012.5 BTC15,750,000 - 18,375,00075% - 87.5%
第三減半後2020-20246.25 BTC18,375,000 - 19,687,50087.5% - 93.75%
第四減半後2024-20283.125 BTC19,687,500 - 20,437,50093.75% - 97.27%
.........21,000,000100%

截至 2024 年,已有大約 97.27% 的比特幣被開採。剩餘的比特數量有限,最後一枚比特幣預計在 2140 年左右開採完成。

1.2 歷史減半事件回顧

比特幣歷史上已發生四次減半事件:

第一次減半(2012年11月28日)

區塊獎勵從 50 BTC 降至 25 BTC。減半前比特幣價格約為 2 美元,減半後約 6 個月內飆升至 130 美元,漲幅超過 60 倍。隨後回落至 70 美元左右。

第二次減半(2016年7月9日)

區塊獎勵從 25 BTC 降至 12.5 BTC。減半前比特幣價格約為 650 美元,減半後進入長達約 1.5 年的牛市,在 2017 年 12 月達到近 20,000 美元高點。從減半到歷史高點漲幅約 30 倍。

第三次減半(2020年5月11日)

區塊獎勵從 12.5 BTC 降至 6.25 BTC。減半前比特幣價格約為 8,500 美元,減半後 6 個月內突破 60,000 美元,漲幅約 6-7 倍。2021 年 11 月達到 69,000 美元歷史高點。

第四次減半(2024年4月19日)

區塊獎勵從 6.25 BTC 降至 3.125 BTC。減半前比特幣價格約為 63,000 美元,減半後持續上漲,2024 年底突破 100,000 美元大關。

1.3 減半效應的經濟學解釋

減半事件對比特幣價格產生影響的經濟學機制可以從以下角度理解:

供給衝擊:減半直接減少了比特幣的新增供給。假設挖礦難度不變,每日新比特幣產量減少 50%。在需求相對穩定的情況下,這種供給側的收縮會對價格產生上行壓力。

預期效應:市場參與者普遍認為減半會推動價格上漲,這種預期會提前反映在價格中。許多投資者會在減半前佈局,導致價格可能在減半前數月就開始上漲。

庫存流量比改善:Stock-to-Flow(庫存流量比)是衡量資產稀缺性的指標。減半後,比特幣的流量(新增產量)減少,而庫存(已開採總量)增加,使得 S2F 比值大幅提升。

歷史參照效應:過去三次減半後都出現了大漲行情,這種歷史記憶強化了市場對減半效應的期待。

第二章:Stock-to-Flow 模型理論與實現

2.1 S2F 模型的理論基礎

Stock-to-Flow(存量流量比,簡稱 S2F)模型是評估黃金、白銀等貴金屬稀缺性的傳統指標,後被應用於比特幣價格預測。

S2F 的計算公式:

S2F = 存量(Stock)/ 流量(Flow)

其中:

以黃金為例:全球黃金存量估計約為 190,000 噸,每年新增開採量約 3,000 噸,黃金的 S2F 約為 63。這意味著按現有開採速度,需要 63 年才能生產出與現有存量相等數量的黃金。

比特幣的特點使其成為理想的 S2F 分析對象:

2.2 S2F 模型的 Python 實現

以下是使用 Python 計算比特幣 S2F 的完整程式碼:

import pandas as pd
import numpy as np
from datetime import datetime
import matplotlib.pyplot as plt

# 比特幣減半區塊高度
HALVING_BLOCKS = 210000

# 初始區塊獎勵(以 BTC 為單位)
INITIAL_REWARD = 50

def calculate_block_reward(block_height):
    """
    根據區塊高度計算當時的區塊獎勵
    """
    halvings = block_height // HALVING_BLOCKS
    
    # 減半次數最多 64 次(二進制位移限制)
    if halvings > 64:
        halvings = 64
    
    return INITIAL_REWARD / (2 ** halvings)

def calculate_total_supply(block_height):
    """
    計算到指定區塊高度為止的比特幣總供應量
    使用等比數列求和公式
    """
    halvings = block_height // HALVING_BLOCKS
    
    # 如果在第一次減半前
    if halvings == 0:
        return block_height * INITIAL_REWARD
    
    # 使用等比數列求和公式
    # 總量 = 初始獎勵 * (1 - (1/2)^減半次數) / (1 - 1/2) * 210000
    total = INITIAL_REWARD * HALVING_BLOCKS * (1 - (0.5 ** halvings)) / (1 - 0.5)
    
    # 加上最後一個完整減半周期之前的區塊
    remaining_blocks = block_height % HALVING_BLOCKS
    if remaining_blocks > 0:
        current_reward = calculate_block_reward(block_height)
        total += remaining_blocks * current_reward
    
    return total

def get_annual_flow(block_height):
    """
    計算年化流量(年度新增比特幣數量)
    每年的區塊數量 = 365.25 * 24 * 6 = 525,960 ≈ 525,960
    """
    blocks_per_year = 365.25 * 24 * 6  # 實際約為 525,600 分鐘/年
    current_reward = calculate_block_reward(block_height)
    return blocks_per_year * current_reward

# 示例:計算當前比特幣的 S2F 值
# 假設當前區塊高度為 800,000(2024年約為此高度)
current_block = 800000
total_supply = calculate_total_supply(current_block)
annual_flow = get_annual_flow(current_block)
sf_ratio = total_supply / annual_flow

print(f"當前區塊高度: {current_block:,}")
print(f"比特幣總供應量: {total_supply:,.2f} BTC")
print(f"年化流量: {annual_flow:,.2f} BTC")
print(f"Stock-to-Flow: {sf_ratio:,.2f}")

執行結果示例:

當前區塊高度: 800,000
比特幣總供應量: 19,593,750.00 BTC
年化流量: 164,550.00 BTC
Stock-to-Flow: 119.08

2.3 S2F 與價格的相關性分析

基於歷史數據,我們可以建立 S2F 與比特幣價格之間的回歸模型:

import pandas as pd
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt

# 模擬歷史 S2F 和價格數據
# 實際應用中應從 CoinGecko 或區塊鏈數據 API 獲取真實數據
np.random.seed(42)

# 創建模擬數據框架
dates = pd.date_range(start='2010-01-01', end='2024-12-31', freq='D')
data = pd.DataFrame({'date': dates})

# 計算每日 S2F(簡化模型)
def calculate_daily_s2f(date):
    """根據日期計算近似的 S2F 值"""
    # 根據比特幣歷史發行曲線估算
    years_since_genesis = (date - datetime(2009, 1, 3)).days / 365.25
    
    if years_since_genesis < 4:
        return 3.3  # 減半前
    elif years_since_genesis < 8:
        return 20  # 第一、二次減半之間
    elif years_since_genesis < 12:
        return 50  # 第二、三次減半之間
    elif years_since_genesis < 16:
        return 100  # 第三、四次減半之間
    else:
        return 110  # 第四次減半後

data['sf_ratio'] = data['date'].apply(calculate_daily_s2f)

# 模擬價格數據(帶有減半效應的隨機行走)
base_price = 0.05  # 2010 年初價格
data['price'] = base_price

for i in range(1, len(data)):
    # 基礎隨機波動
    daily_return = np.random.normal(0.003, 0.1)
    
    # S2F 效應:S2F 越高,預期回報越高
    s2f_effect = (data.iloc[i]['sf_ratio'] / data.iloc[i-1]['sf_ratio'] - 1) * 0.5
    
    # 減半後效應(簡化)
    if i > 365 * 2:  # 大約減半後
        s2f_effect += 0.01  # 減半後的正向偏移
    
    data.loc[data.index[i], 'price'] = data.iloc[i-1]['price'] * (1 + daily_return + s2f_effect)

# 對數轉換
data['log_price'] = np.log(data['price'])
data['log_sf'] = np.log(data['sf_ratio'])

# 線性回歸
slope, intercept, r_value, p_value, std_err = stats.linregress(
    data['log_sf'].dropna(), 
    data['log_price'].dropna()
)

print(f"S2F 模型回歸結果:")
print(f"斜率 (β): {slope:.4f}")
print(f"截距 (α): {intercept:.4f}")
print(f"R²: {r_value**2:.4f}")
print(f"p-value: {p_value:.2e}")

# 繪製散點圖
plt.figure(figsize=(12, 8))
plt.scatter(data['sf_ratio'], data['price'], alpha=0.5, s=10)
plt.xscale('log')
plt.yscale('log')
plt.xlabel('Stock-to-Flow Ratio')
plt.ylabel('Bitcoin Price (USD)')
plt.title('Bitcoin S2F vs Price Relationship')

# 添加回歸線
x_range = np.linspace(data['sf_ratio'].min(), data['sf_ratio'].max(), 100)
y_pred = np.exp(intercept) * (x_range ** slope)
plt.plot(x_range, y_pred, 'r-', label=f'Model: Price = exp({intercept:.2f}) × S2F^{slope:.2f}')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

2.4 S2F 交叉資產模型

進階的 S2F 交叉資產模型考慮比特幣與黃金的對比關係:

def calculate_gold_sf():
    """估算黃金的 Stock-to-Flow"""
    # 全球黃金存量約 190,000 噸
    gold_stock = 190000  # 噸
    # 年開採量約 3,000 噸
    gold_flow = 3000  # 噸/年
    return gold_stock / gold_flow

def calculate_bitcoin_sf_future(years_ahead=4):
    """
    預測未來比特幣的 S2F 值
    """
    current_year = 2024
    current_block = 840000
    current_supply = 19687500  # BTC
    
    projections = []
    
    for year in range(current_year, current_year + years_ahead + 1):
        # 估計區塊高度
        blocks_per_year = 365.25 * 24 * 6
        future_block = current_block + int(blocks_per_year * (year - current_year))
        
        # 計算供應量
        if year >= 2028:
            supply = 20000000 + (year - 2028) * blocks_per_year * 3.125
        elif year >= 2024:
            supply = 19687500 + (year - 2024) * blocks_per_year * 3.125
        else:
            supply = 19687500
        
        # 計算年流量
        if year >= 2028:
            flow = blocks_per_year * 3.125 / 4  # 減半後的季度流量
        else:
            flow = blocks_per_year * 6.25
        
        sf = supply / flow
        projections.append({'year': year, 'supply': supply, 'flow': flow, 'sf': sf})
    
    return pd.DataFrame(projections)

# 黃金 S2F
gold_sf = calculate_gold_sf()
print(f"黃金 Stock-to-Flow: {gold_sf:.2f}")

# 比特幣未來 S2F 預測
future_sf = calculate_bitcoin_sf_future(years_ahead=8)
print("\n比特幣未來 S2F 預測:")
print(future_sf.to_string(index=False))

第三章:減半週期回測框架

3.1 回測框架概述

回測(Backtesting)是利用歷史數據評估交易策略表現的方法。一個完善的回測框架需要考慮:

數據獲取:需要歷史價格數據、區塊數據、鏈上指標等。

策略定義:明確定義進出场條件、倉位管理、風險控制等。

執行模擬:模擬策略在歷史時期的實際表現。

績效評估:計算收益率、夏普比率、最大回撤等指標。

3.2 比特幣價格數據獲取

import requests
import pandas as pd
from datetime import datetime, timedelta

def get_bitcoin_price_coingecko(start_date='2010-01-01', end_date=None):
    """
    從 CoinGecko API 獲取比特幣歷史價格數據
    """
    if end_date is None:
        end_date = datetime.now().strftime('%d-%m-%Y')
    
    start_timestamp = int(datetime.strptime(start_date, '%Y-%m-%d').timestamp())
    end_timestamp = int(datetime.strptime(end_date, '%Y-%m-%d').timestamp())
    
    url = f"https://api.coingecko.com/api/v3/coins/bitcoin/history"
    params = {
        'id': 'bitcoin',
        'from': start_timestamp,
        'to': end_timestamp,
        'interval': 'daily'
    }
    
    try:
        response = requests.get(url, params=params)
        data = response.json()
        
        prices = []
        for item in data.get('prices', []):
            timestamp = datetime.fromtimestamp(item[0] / 1000)
            price = item[1]
            prices.append({'date': timestamp, 'price': price})
        
        return pd.DataFrame(prices)
    except Exception as e:
        print(f"API 請求失敗: {e}")
        return None

def get_block_height_from_date(date, genesis_timestamp=1231006505):
    """
    根據日期估算比特幣區塊高度
    平均區塊時間約 10 分鐘
    """
    date_timestamp = datetime.strptime(date, '%Y-%m-%d').timestamp()
    seconds_diff = date_timestamp - genesis_timestamp
    blocks = int(seconds_diff / 600)  # 10 分鐘一個區塊
    return blocks

# 示例用法
btc_data = get_bitcoin_price_coingecko('2012-01-01', '2024-12-31')
if btc_data is not None:
    print(f"獲取到 {len(btc_data)} 條價格記錄")
    print(btc_data.head())

3.3 減半事件標記與週期分析

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime

# 定義減半事件
halving_events = [
    {'date': '2012-11-28', 'block': 210000, 'reward_before': 50, 'reward_after': 25, 'price_before': 2.5},
    {'date': '2016-07-09', 'block': 420000, 'reward_before': 25, 'reward_after': 12.5, 'price_before': 650},
    {'date': '2020-05-11', 'block': 630000, 'reward_before': 12.5, 'reward_after': 6.25, 'price_before': 8500},
    {'date': '2024-04-19', 'block': 840000, 'reward_before': 6.25, 'reward_after': 3.125, 'price_before': 63000},
]

def analyze_halving_cycles(price_data, halving_events):
    """
    分析每次減半後的價格走勢
    """
    results = []
    
    for i, halving in enumerate(halving_events):
        halving_date = datetime.strptime(halving['date'], '%Y-%m-%d')
        
        # 篩選減半後的數據
        post_data = price_data[price_data['date'] >= halving_date].copy()
        
        if len(post_data) > 0:
            # 計算減半後不同時間點的價格
            for days in [30, 90, 180, 365, 540]:
                target_date = halving_date + timedelta(days=days)
                closest_data = post_data[post_data['date'] <= target_date].iloc[-1] if len(post_data[post_data['date'] <= target_date]) > 0 else None
                
                if closest_data is not None:
                    days_actual = (closest_data['date'] - halving_date).days
                    price_ratio = closest_data['price'] / halving['price_before']
                    results.append({
                        'halving': i + 1,
                        'halving_date': halving['date'],
                        'days_after': days_actual,
                        'price': closest_data['price'],
                        'return_ratio': price_ratio,
                        'return_pct': (price_ratio - 1) * 100
                    })
    
    return pd.DataFrame(results)

# 示例:繪製減半週期疊加圖
def plot_halving_overlay(price_data, halving_events):
    """
    將所有減半週期疊加繪製
    """
    cycles = []
    
    for halving in halving_events[:-1]:  # 不包括未來減半
        halving_date = datetime.strptime(halving['date'], '%Y-%m-%d')
        
        post_data = price_data[price_data['date'] >= halving_date].copy()
        post_data['days_since_halving'] = (post_data['date'] - halving_date).dt.days
        
        # 限制在540天內
        post_data = post_data[post_data['days_since_halving'] <= 540]
        
        if len(post_data) > 0:
            # 標準化價格(以減半日價格為1)
            post_data['normalized_price'] = post_data['price'] / halving['price_before']
            cycles.append(post_data[['days_since_halving', 'normalized_price', 'date']].copy())
    
    # 繪圖
    plt.figure(figsize=(14, 8))
    
    colors = ['blue', 'orange', 'green']
    for i, cycle in enumerate(cycles):
        plt.plot(cycle['days_since_halving'], cycle['normalized_price'], 
                 label=f'Halving {i+1}', alpha=0.7, linewidth=2)
    
    # 計算平均路徑
    max_days = min(cycle['days_since_halving'].max() for cycle in cycles)
    avg_data = []
    for day in range(0, max_days + 1, 10):
        values = []
        for cycle in cycles:
            match = cycle[cycle['days_since_halving'] == day]['normalized_price']
            if len(match) > 0:
                values.append(match.values[0])
        if values:
            avg_data.append({'day': day, 'avg_price': np.mean(values)})
    
    avg_df = pd.DataFrame(avg_data)
    if len(avg_df) > 0:
        plt.plot(avg_df['day'], avg_df['avg_price'], 'r--', 
                 label='Average', linewidth=3, alpha=0.8)
    
    plt.xlabel('Days Since Halving')
    plt.ylabel('Normalized Price (Halving Day = 1)')
    plt.title('Bitcoin Price Performance After Each Halving')
    plt.legend()
    plt.grid(True, alpha=0.3)
    plt.axhline(y=1, color='black', linestyle='-', alpha=0.3)
    plt.show()

# 執行分析
if btc_data is not None:
    results = analyze_halving_cycles(btc_data, halving_events)
    print("\n減半週期收益率分析:")
    print(results.to_string(index=False))

3.4 基於減半的簡單交易策略回測

def backtest_halving_strategy(price_data, halving_events, 
                              entry_days_after=30, 
                              hold_days=365,
                              exit_days_after=entry_days_after + hold_days):
    """
    基於減半的簡單買入持有策略回測
    
    策略邏輯:
    - 在減半後 entry_days_after 天買入
    - 持有 hold_days 天
    - 在減半後 exit_days_after 天賣出
    """
    results = []
    
    for halving in halving_events[:-1]:  # 不包括最新的減半
        halving_date = datetime.strptime(halving['date'], '%Y-%m-%d')
        
        # 買入日
        entry_date = halving_date + timedelta(days=entry_days_after)
        entry_data = price_data[price_data['date'] >= entry_date]
        
        # 賣出日
        exit_date = halving_date + timedelta(days=exit_days_after)
        exit_data = price_data[price_data['date'] <= exit_date]
        
        if len(entry_data) > 0 and len(exit_data) > 0:
            entry_price = entry_data.iloc[0]['price']
            exit_price = exit_data.iloc[-1]['price']
            
            # 計算收益率
            return_pct = (exit_price - entry_price) / entry_price * 100
            
            # 尋找持有期內的最高和最低價
            holding_period = price_data[(price_data['date'] >= entry_data.iloc[0]['date']) & 
                                        (price_data['date'] <= exit_data.iloc[-1]['date'])]
            
            max_price = holding_period['price'].max()
            min_price = holding_period['price'].min()
            max_drawdown = (min_price - max_price) / max_price * 100
            
            results.append({
                'halving': halving['date'],
                'entry_date': entry_data.iloc[0]['date'].strftime('%Y-%m-%d'),
                'exit_date': exit_data.iloc[-1]['date'].strftime('%Y-%m-%d'),
                'entry_price': entry_price,
                'exit_price': exit_price,
                'return_pct': return_pct,
                'max_price': max_price,
                'min_price': min_price,
                'max_drawdown_pct': max_drawdown
            })
    
    return pd.DataFrame(results)

def calculate_strategy_metrics(results):
    """
    計算策略的綜合指標
    """
    if len(results) == 0:
        return None
    
    returns = results['return_pct'].values
    avg_return = np.mean(returns)
    std_return = np.std(returns)
    
    # 年化收益率(假設每次策略持續一年)
    annualized_return = avg_return
    annualized_volatility = std_return
    sharpe_ratio = annualized_return / annualized_volatility if annualized_volatility > 0 else 0
    
    # 勝率
    win_rate = len(returns[returns > 0]) / len(returns)
    
    # 最大回撤
    max_drawdown = results['max_drawdown_pct'].min()
    
    return {
        'avg_return': avg_return,
        'std_return': std_return,
        'annualized_return': annualized_return,
        'annualized_volatility': annualized_volatility,
        'sharpe_ratio': sharpe_ratio,
        'win_rate': win_rate,
        'max_drawdown': max_drawdown,
        'num_trades': len(results)
    }

# 執行回測
if btc_data is not None:
    backtest_results = backtest_halving_strategy(btc_data, halving_events)
    print("\n減半策略回測結果:")
    print(backtest_results.to_string(index=False))
    
    metrics = calculate_strategy_metrics(backtest_results)
    print("\n策略綜合指標:")
    for key, value in metrics.items():
        if isinstance(value, float):
            print(f"  {key}: {value:.2f}")
        else:
            print(f"  {key}: {value}")

第四章:進階量化模型

4.1 Power Law 模型

比特幣價格的長期增長可以用冪律(Power Law)模型描述:

def fit_power_law(price_data):
    """
    擬合比特幣價格的冪律模型
    Price = a * (days_since_genesis) ^ b
    """
    from scipy.optimize import curve_fit
    
    genesis_date = datetime(2009, 1, 3)
    
    # 計算天數
    price_data['days'] = (price_data['date'] - genesis_date).dt.days
    price_data = price_data[price_data['days'] > 0]
    
    # 濾除極低價格時期(2010年前數據波動過大)
    price_data = price_data[price_data['price'] > 0.1]
    
    def power_law(x, a, b):
        return a * np.power(x, b)
    
    # 對數轉換後線性回歸
    log_days = np.log(price_data['days'].values)
    log_price = np.log(price_data['price'].values)
    
    # 使用 curve_fit
    try:
        popt, pcov = curve_fit(power_law, price_data['days'], price_data['price'], 
                               p0=[1e-10, 5], maxfev=10000)
        a, b = popt
        
        # 計算擬合優度
        predicted = power_law(price_data['days'].values, a, b)
        ss_res = np.sum((price_data['price'].values - predicted) ** 2)
        ss_tot = np.sum((price_data['price'].values - np.mean(price_data['price'].values)) ** 2)
        r_squared = 1 - (ss_res / ss_tot)
        
        return {'a': a, 'b': b, 'r_squared': r_squared}
    except Exception as e:
        print(f"擬合失敗: {e}")
        return None

def predict_future_prices(model, days_ahead=365*4):
    """
    使用冪律模型預測未來價格
    """
    import pandas as pd
    from datetime import datetime, timedelta
    
    genesis_date = datetime(2009, 1, 3)
    today = datetime.now()
    
    future_dates = pd.date_range(start=today, periods=days_ahead, freq='D')
    future_days = [(d - genesis_date).days for d in future_dates]
    
    future_prices = model['a'] * np.power(future_days, model['b'])
    
    return pd.DataFrame({
        'date': future_dates,
        'predicted_price': future_prices
    })

# 執行擬合
if btc_data is not None:
    power_law_model = fit_power_law(btc_data)
    if power_law_model:
        print(f"\n冪律模型參數:")
        print(f"  a = {power_law_model['a']:.2e}")
        print(f"  b = {power_law_model['b']:.4f}")
        print(f"  R² = {power_law_model['r_squared']:.4f}")
        
        # 預測未來價格
        future_prices = predict_future_prices(power_law_model)
        print("\n未來四年預測價格(部分):")
        print(future_prices[future_prices['date'].dt.month == 1].head())

4.2 減半效應的統計顯著性檢驗

from scipy import stats

def analyze_halving_effect_significance(price_data, halving_events):
    """
    分析減半效應是否具有統計顯著性
    """
    returns = []
    
    for halving in halving_events[:-1]:
        halving_date = datetime.strptime(halving['date'], '%Y-%m-%d')
        
        # 減半前 180 天收益
        pre_start = halving_date - timedelta(days=360)
        pre_data = price_data[(price_data['date'] >= pre_start) & 
                              (price_data['date'] < halving_date)]
        
        # 減半後 180 天收益
        post_end = halving_date + timedelta(days=180)
        post_data = price_data[(price_data['date'] >= halving_date) & 
                               (price_data['date'] <= post_end)]
        
        if len(pre_data) > 1 and len(post_data) > 1:
            pre_return = (pre_data['price'].iloc[-1] / pre_data['price'].iloc[0] - 1) * 100
            post_return = (post_data['price'].iloc[-1] / post_data['price'].iloc[0] - 1) * 100
            
            returns.append({
                'halving': halving['date'],
                'pre_180d_return': pre_return,
                'post_180d_return': post_return
            })
    
    returns_df = pd.DataFrame(returns)
    
    # t 檢驗
    if len(returns_df) > 1:
        t_stat, p_value = stats.ttest_rel(
            returns_df['post_180d_return'], 
            returns_df['pre_180d_return']
        )
        
        print("\n減半效應統計分析:")
        print(f"平均減半前 180 天收益: {returns_df['pre_180d_return'].mean():.2f}%")
        print(f"平均減半後 180 天收益: {returns_df['post_180d_return'].mean():.2f}%")
        print(f"t 統計量: {t_stat:.4f}")
        print(f"p 值: {p_value:.4f}")
        print(f"結論: {'減半後收益顯著更高' if p_value < 0.05 else '減半效應不具統計顯著性'}")
    
    return returns_df

if btc_data is not None:
    returns_analysis = analyze_halving_effect_significance(btc_data, halving_events)

第五章:模型局限性與風險警示

5.1 S2F 模型的批評

Stock-to-Flow 模型在比特幣社群和學術界都受到不少批評:

沒有考慮需求側因素:S2F 模型純粹基於供給端分析,完全忽視了影響比特幣價格的眾多需求側因素,如監管政策、機構採用、技術發展、競爭資產等。

過擬合風險:基於有限歷史數據(只有三次減半)建立的模型可能存在過擬合問題。樣本外預測能力存疑。

自我指涉性:比特幣價格影響持有者行為,而持有者行為又影響價格。這種反饋機制使得基於歷史數據的簡單模型難以準確預測。

黃金類比失效:比特幣與黃金的市場結構、持有者群體、流動性等方面存在根本差異,黃金的 S2F 關係不一定適用於比特幣。

5.2 減半效應的不確定性

歷史減半效應不一定代表未來減半的表現:

遞減效應:隨著比特幣市場規模擴大,相同比例的供給變化對價格的影響可能減弱。

效率市場:如果減半效應被充分預期,聰明的投資者會提前佈局,壓縮潛在收益空間。

外部因素:歷史減半並未經歷當前的宏觀經濟環境(如高通膨、低利率、機構採用等),未來減半可能面臨完全不同的市場背景。

5.3 回測的固有局限

過度擬合:策略可能對歷史數據「過度優化」,在真實市場中表現不佳。

存活者偏差:回測只使用「存活」下來的資產,忽略了那些已經消失的資產。

流動性假設:回測假設任何時候都可以以收盤價買入或賣出,實際市場的流動性可能不允許如此理想的執行。

心理因素:回測無法考慮實際交易中的心理壓力、執行錯誤等因素。

結論

比特幣減半週期的量化分析是一個值得深入研究的領域。S2F 模型、冪律模型等提供了理解比特幣長期價格的框架,而減半效應的歷史分析揭示了一些有趣的規律。

然而,讀者必須牢記:

歷史不代表未來:過去三次減半的表現不能保證未來減半會有相同結果。

模型是工具而非聖杯:任何量化模型都有局限性,應作為決策參考而非唯一依據。

風險管理第一:無論使用何種策略,合理的倉位管理和風險控制都是最重要的。

比特幣市場高度波動且不可預測,任何試圖精確預測價格的模型都應持懷疑態度。建議讀者在深入研究的同時,保持謙遜,時刻牢記風險管理的重要性。

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

注意:由於這是靜態網站,您的評論將儲存在本地瀏覽器中,不會公開顯示。

目前尚無評論,成為第一個發表評論的人吧!