DeFi 清算風險量化回測框架完整指南:基於真實事件的風險參數模擬實作

本指南提供完整的量化回測框架,涵蓋清算觸發條件的數學建模、抵押品波動率模擬、風險參數敏感性分析、歷史事件回測等核心內容。針對 2020 年黑色星期四、2021 年 519 事件、2022 年 Terra/Luna 崩潰等重大清算事件提供實證分析,包含完整的 Python 程式碼範例,幫助 DeFi 風險分析師和量化交易員建立系統性的清算風險評估能力。

DeFi 清算風險量化回測框架完整指南:基於真實事件的風險參數模擬實作

概述

去中心化金融(DeFi)清算機制是維持借貸協議健康運作的核心支柱。2020 年 3 月黑色星期四、2021 年 5 月 19 日市場崩潰、2022 年 Terra/Luna 事件等重大清算事件,深刻揭示了清算機制在極端市場條件下的脆弱性。然而,現有 DeFi 清算相關文章多聚焦於事件敘事,缺乏系統性的量化回測框架來模擬和預測清算風險。

本指南旨在填補這一空白,提供完整的量化回測框架,涵蓋:清算觸發條件的數學建模、抵押品波動率模擬、風險參數敏感性分析、歷史事件回測、以及基於真實數據的風險評估模型。讀者將能夠使用本指南提供的框架和程式碼,對任意借貸協議進行清算風險量化分析。

本文的目標讀者包括:DeFi 風險分析師、量化交易員、借貸協議開發者、以及對 DeFi 風險管理感興趣的研究者。我們假設讀者具備基本的金融風險管理知識和 Python/Solidity 程式開發能力。

第一章:清算風險量化基礎理論

1.1 健康因子與清算閾值的數學定義

清算風險的核心度量是「健康因子」(Health Factor, HF)。健康因子是抵押品價值與借款價值的比率,反映了帳戶的安全邊際。

健康因子的數學定義

對於一個借貸帳戶,其健康因子定義為:

$$HF = \frac{\sumi Collaterali \times CFi \times Pricei}{\sumj Borrowj \times Price_j}$$

其中:

清算觸發條件

當健康因子低於清算閾值(通常為 1.0)時,帳戶進入可清算狀態:

$$HF < 1.0 \implies \text{可清算}$$

在某些協議中,清算閾值可能設定為 1.1 或更高,以提供額外的安全邊際。

清算獎勵機制

清算人通常獲得清算獎勵,以激勵及時清算。清算獎勵的計算方式為:

$$LiquidationBonus = \text{BorrowAmount} \times (1 + \text{BonusRate})$$

其中 BonusRate 通常在 5% 至 10% 之間,具體取決於協議設計。

1.2 清算概率的數學模型

單一資產清算概率

假設抵押品價格服從幾何布朗運動(Geometric Brownian Motion, GBM):

$$dP = \mu P dt + \sigma P dW$$

其中:

對於這樣的價格過程,清算概率(首次觸及概率)可以使用反射原理計算:

$$P(\tauT < t | P0) = \Phi\left(\frac{-\ln(P0/K) + (\mu - \frac{\sigma^2}{2})t}{\sigma\sqrt{t}}\right) + \left(\frac{K}{P0}\right)^{2\mu/\sigma^2} \Phi\left(\frac{-\ln(P_0/K) - (\mu - \frac{\sigma^2}{2})t}{\sigma\sqrt{t}}\right)$$

其中:

多資產組合清算概率

對於多資產組合,清算概率更加複雜。假設 n 種資產的價格向量服從多變量幾何布朗運動:

$$d\mathbf{P} = \boldsymbol{\mu} \odot \mathbf{P} dt + \boldsymbol{\Sigma} \odot \mathbf{P} d\mathbf{W}$$

其中 $\boldsymbol{\Sigma}$ 是協方差矩陣,$d\mathbf{W}$ 是相關維納過程向量。

多資產組合的清算概率為:

$$P{liquidation} = P\left(\mini \frac{Ci Pi}{Bi} < LFi\right)$$

這個概率可以通過蒙特卡羅模擬來估計。

1.3 清算槓桿效應與級聯風險

槓桿效應的數學描述

在高槓桿頭寸中,清算概率呈現非線性特徵。設初始抵押率為 LTV(Loan-to-Value):

$$LTV = \frac{Borrow}{Collateral}$$

當 LTV 接近清算閾值時,價格的小幅下跌就會觸發清算。清算閾值附近的價格敏感性為:

$$\frac{\partial HF}{\partial P} = \frac{-CF \times B}{P^2}$$

級聯清算的動力學模型

在市場崩潰期間,級聯清算(Cascading Liquidation)是一個重要的風險因素。當一個清算事件發生時:

  1. 抵押品被強制出售
  2. 導致市場價格下跌
  3. 其他高槓桿頭寸的健康因子下降
  4. 觸發更多清算事件

這種正反饋機制可以用以下微分方程描述:

$$\frac{dN}{dt} = \lambda(N) \times N$$

其中 $N$ 是被清算頭寸的數量,$\lambda(N)$ 是與市場狀況相關的觸發率。

第二章:量化回測框架設計

2.1 回測框架的系統架構

一個完整的 DeFi 清算風險量化回測框架需要包含以下模組:

數據層(Data Layer)

模型層(Model Layer)

分析層(Analysis Layer)

報告層(Reporting Layer)

以下是框架的 Python 實現:

import numpy as np
import pandas as pd
from dataclasses import dataclass
from typing import List, Dict, Optional
from enum import Enum
import asyncio
from web3 import Web3

@dataclass
class Asset:
    """資產定義"""
    address: str
    symbol: str
    decimals: int
    collateral_factor: float  # 抵押因子
    liquidation_threshold: float  # 清算閾值
    liquidation_bonus: float  # 清算獎勵

@dataclass
class Position:
    """借貸頭寸"""
    borrower: str
    collateral_assets: Dict[str, float]  # asset_address -> amount
    borrow_assets: Dict[str, float]  # asset_address -> amount
    
    def calculate_health_factor(
        self, 
        prices: Dict[str, float],
        assets: Dict[str, Asset]
    ) -> float:
        """
        計算健康因子
        
        數學公式:
        HF = Σ(Collateral_i × CF_i × Price_i) / Σ(Borrow_j × Price_j)
        """
        total_collateral_value = 0.0
        total_borrow_value = 0.0
        
        # 計算抵押品價值
        for addr, amount in self.collateral_assets.items():
            asset = assets[addr]
            value = amount * prices[addr] / (10 ** asset.decimals)
            total_collateral_value += value * asset.collateral_factor
        
        # 計算借款價值
        for addr, amount in self.borrow_assets.items():
            asset = assets[addr]
            value = amount * prices[addr] / (10 ** asset.decimals)
            total_borrow_value += value
        
        if total_borrow_value == 0:
            return float('inf')
        
        return total_collateral_value / total_borrow_value

@dataclass
class LiquidationEvent:
    """清算事件記錄"""
    timestamp: int
    block_number: int
    borrower: str
    liquidated_collateral: Dict[str, float]
    repaid_debt: Dict[str, float]
    profit: float

class DeFiLiquidationBacktester:
    """
    DeFi 清算風險量化回測框架
    
    功能:
    1. 歷史數據回測
    2. 清算風險模擬
    3. 敏感性分析
    4. 壓力測試
    """
    
    def __init__(
        self,
        web3_endpoint: str,
        protocol_address: str
    ):
        self.w3 = Web3(Web3.HTTPProvider(web3_endpoint))
        self.protocol_address = Web3.to_checksum_address(protocol_address)
        self.positions: Dict[str, Position] = {}
        self.liquidation_events: List[LiquidationEvent] = []
        self.price_history: pd.DataFrame = None
        
    async def load_historical_positions(
        self,
        start_block: int,
        end_block: int
    ) -> None:
        """
        加載歷史頭寸數據
        
        從區塊鏈事件中重建歷史頭寸狀態
        """
        # 獲取供應事件
        supply_events = await self._fetch_supply_events(start_block, end_block)
        # 獲取借款事件
        borrow_events = await self._fetch_borrow_events(start_block, end_block)
        # 獲取還款事件
        repay_events = await self._fetch_repay_events(start_block, end_block)
        # 獲取質押事件
        collateral_events = await self._fetch_collateral_events(start_block, end_block)
        
        # 重建頭寸狀態
        self.positions = self._reconstruct_positions(
            supply_events,
            borrow_events,
            repay_events,
            collateral_events
        )
    
    async def load_price_history(
        self,
        asset_addresses: List[str],
        start_block: int,
        end_block: int
    ) -> pd.DataFrame:
        """
        加載價格歷史數據
        
        從 Chainlink 或其他預言機獲取歷史價格
        """
        price_data = []
        
        for asset_addr in asset_addresses:
            # 查詢預言機價格記錄
            prices = await self._fetch_oracle_prices(
                asset_addr,
                start_block,
                end_block
            )
            price_data.append(prices)
        
        # 合併為 DataFrame
        self.price_history = pd.concat(price_data, axis=1)
        return self.price_history
    
    def simulate_health_factors(
        self,
        scenario: str = "historical"
    ) -> pd.DataFrame:
        """
        模擬健康因子演變
        
        支援三種情景:
        1. historical: 使用真實歷史價格
        2. monte_carlo: 蒙特卡羅模擬
        3. stress: 壓力測試情景
        """
        if scenario == "historical":
            return self._simulate_historical()
        elif scenario == "monte_carlo":
            return self._simulate_monte_carlo()
        elif scenario == "stress":
            return self._simulate_stress()
    
    def _simulate_historical(self) -> pd.DataFrame:
        """使用歷史價格模擬"""
        results = []
        
        for timestamp, row in self.price_history.iterrows():
            prices = row.to_dict()
            
            for borrower, position in self.positions.items():
                hf = position.calculate_health_factor(
                    prices,
                    self._assets
                )
                
                results.append({
                    'timestamp': timestamp,
                    'borrower': borrower,
                    'health_factor': hf,
                    'liquidatable': hf < 1.0
                })
        
        return pd.DataFrame(results)

2.2 抵押品價格模擬引擎

幾何布朗運動模擬

def simulate_gbm_paths(
    S0: np.ndarray,
    mu: np.ndarray,
    sigma: np.ndarray,
    T: float,
    dt: float,
    n_paths: int,
    correlation: Optional[np.ndarray] = None
) -> np.ndarray:
    """
    模擬多資產幾何布朗運動路徑
    
    數學模型:
    dS_i = mu_i * S_i * dt + sigma_i * S_i * dW_i
    
    其中 dW_i 相關性由 correlation matrix 控制
    
    參數:
    - S0: 初始價格向量 (n_assets,)
    - mu: 漂移率向量 (n_assets,)
    - sigma: 波動率向量 (n_assets,)
    - T: 時間跨度(年)
    - dt: 時間步長
    - n_paths: 模擬路徑數量
    - correlation: 相關性矩陣 (n_assets, n_assets)
    
    返回:
    - paths: 模擬路徑 (n_paths, n_steps+1, n_assets)
    """
    n_steps = int(T / dt)
    n_assets = len(S0)
    
    # 初始化輸出數組
    paths = np.zeros((n_paths, n_steps + 1, n_assets))
    paths[:, 0, :] = S0
    
    # Cholesky 分解以引入相關性
    if correlation is not None:
        L = np.linalg.cholesky(correlation)
    else:
        L = np.eye(n_assets)
    
    # 生成隨機增量
    Z = np.random.standard_normal((n_paths, n_steps, n_assets))
    Z_correlated = np.einsum('ij,klj->kli', L, Z)
    
    # 時間增量
    dt_sqrt = np.sqrt(dt)
    
    # 模擬路徑
    for t in range(n_steps):
        # 維納過程增量
        dW = Z_correlated[:, t, :] * dt_sqrt
        
        # 幾何布朗運動更新
        paths[:, t + 1, :] = paths[:, t, :] * np.exp(
            (mu - 0.5 * sigma ** 2) * dt + sigma * dW
        )
    
    return paths

def simulate_with_jumps(
    S0: float,
    mu: float,
    sigma: float,
    lambda_jump: float,  # 跳躍強度
    mu_jump: float,      # 跳躍均值
    sigma_jump: float,   # 跳躍標準差
    T: float,
    dt: float,
    n_paths: int
) -> np.ndarray:
    """
    帶跳躍的幾何布朗運動(Merton 模型)
    
    數學模型:
    dS/S = (mu - lambda * mu_jump) * dt + sigma * dW + dJ
    
    其中 dJ 是複合泊松過程:
    dJ = sum_{k=1}^{N(t)} (Y_k - 1)
    
    Y_k 服從 lognormal(mu_jump, sigma_jump)
    """
    n_steps = int(T / dt)
    
    paths = np.zeros((n_paths, n_steps + 1))
    paths[:, 0] = S0
    
    for t in range(n_steps):
        # 布朗運動增量
        dW = np.random.standard_normal(n_paths) * np.sqrt(dt)
        
        # 跳躍過程
        n_jumps = np.random.poisson(lambda_jump * dt)
        jump_returns = np.zeros(n_paths)
        
        if n_jumps > 0:
            # 模擬跳躍
            for _ in range(n_jumps):
                jumps = np.exp(
                    mu_jump + sigma_jump * np.random.standard_normal(n_paths)
                ) - 1
                jump_returns += jumps
        
        # 更新價格
        paths[:, t + 1] = paths[:, t] * np.exp(
            (mu - 0.5 * sigma ** 2 - lambda_jump * (np.exp(mu_jump + 0.5 * sigma_jump ** 2) - 1)) * dt
            + sigma * dW
            + jump_returns
        )
    
    return paths

class VolatilitySurfaceModel:
    """
    波動率曲面模型
    
    用於模擬更真實的波動率動態
    包括:
    1. 波動率集群(Volatility Clustering)
    2. 波動率均值回歸
    3. 收益率厚尾分佈
    """
    
    def __init__(self, assets: List[str]):
        self.assets = assets
        self.volatility = {}  # GARCH 波動率估計
        self.correlation = None  # 動態相關性
        
    def fit_garch(self, returns: pd.DataFrame) -> None:
        """
        擬合 GARCH(1,1) 模型
        
        數學模型:
        r_t = sigma_t * epsilon_t
        sigma_t^2 = omega + alpha * r_{t-1}^2 + beta * sigma_{t-1}^2
        
        參數估計使用最大似然估計(MLE)
        """
        from scipy.optimize import minimize
        
        for asset in self.assets:
            returns_asset = returns[asset].values
            
            def neg_log_likelihood(params):
                omega, alpha, beta = params
                
                if omega <= 0 or alpha < 0 or beta < 0 or alpha + beta >= 1:
                    return 1e10
                
                n = len(returns_asset)
                sigma2 = np.zeros(n)
                sigma2[0] = np.var(returns_asset)
                
                log_likelihood = 0
                for t in range(1, n):
                    sigma2[t] = omega + alpha * returns_asset[t-1]**2 + beta * sigma2[t-1]
                    log_likelihood += -0.5 * (
                        np.log(2 * np.pi) + np.log(sigma2[t]) + 
                        returns_asset[t]**2 / sigma2[t]
                    )
                
                return -log_likelihood
            
            # 初始參數估計
            initial = [0.01, 0.1, 0.8]
            
            # 優化
            result = minimize(
                neg_log_likelihood,
                initial,
                method='Nelder-Mead'
            )
            
            self.volatility[asset] = {
                'omega': result.x[0],
                'alpha': result.x[1],
                'beta': result.x[2]
            }
    
    def simulate_garch_paths(
        self,
        returns_history: pd.DataFrame,
        T: int,  # 預測期數
        n_paths: int
    ) -> Dict[str, np.ndarray]:
        """
        使用 GARCH 模型模擬波動率路徑
        
        返回每個資產的模擬收益率路徑
        """
        simulated_paths = {}
        
        for asset in self.assets:
            params = self.volatility[asset]
            returns_asset = returns_history[asset].values
            
            # 初始化波動率
            sigma2 = np.zeros((n_paths, T + 1))
            sigma2[:, 0] = params['omega'] / (1 - params['alpha'] - params['beta'])
            
            # 模擬路徑
            paths = np.zeros((n_paths, T + 1))
            paths[:, 0] = returns_asset[-1]
            
            for t in range(T):
                z = np.random.standard_normal(n_paths)
                sigma2[:, t + 1] = (
                    params['omega'] + 
                    params['alpha'] * paths[:, t]**2 + 
                    params['beta'] * sigma2[:, t]
                )
                paths[:, t + 1] = np.sqrt(sigma2[:, t + 1]) * z
            
            simulated_paths[asset] = paths
        
        return simulated_paths

2.3 清算觸發識別模組

class LiquidationTriggerDetector:
    """
    清算觸發識別器
    
    功能:
    1. 實時監控頭寸健康因子
    2. 識別清算觸發事件
    3. 計算清算時機窗口
    4. 估計潛在清算規模
    """
    
    def __init__(
        self,
        liquidation_threshold: float = 1.0,
        warning_threshold: float = 1.2,
        lookback_blocks: int = 1
    ):
        self.liquidation_threshold = liquidation_threshold
        self.warning_threshold = warning_threshold
        self.lookback_blocks = lookback_blocks
        
    def detect_liquidation_triggers(
        self,
        positions: Dict[str, Position],
        prices: Dict[str, float],
        assets: Dict[str, Asset]
    ) -> Dict[str, dict]:
        """
        檢測清算觸發事件
        
        返回每個可清算頭寸的詳細信息
        """
        triggers = {}
        
        for borrower, position in positions.items():
            hf = position.calculate_health_factor(prices, assets)
            
            if hf < self.liquidation_threshold:
                # 計算清算規模
                liquidation_amounts = self._calculate_liquidation_amounts(
                    position,
                    prices,
                    assets
                )
                
                triggers[borrower] = {
                    'health_factor': hf,
                    'distance_to_liquidation': self._calculate_distance(hf),
                    'liquidation_amounts': liquidation_amounts,
                    'potential_profit': self._estimate_liquidation_profit(
                        liquidation_amounts,
                        prices,
                        assets
                    ),
                    'urgency': self._calculate_urgency(hf)
                }
            elif hf < self.warning_threshold:
                # 記錄警告
                triggers[borrower] = {
                    'health_factor': hf,
                    'warning': True,
                    'distance_to_liquidation': self._calculate_distance(hf)
                }
        
        return triggers
    
    def _calculate_liquidation_amounts(
        self,
        position: Position,
        prices: Dict[str, float],
        assets: Dict[str, Asset]
    ) -> Dict[str, float]:
        """
        計算清算金額
        
        數學公式:
        最大清算金額 = min(借款額的 50%, 抵押品的清算價值)
        
        清算價值 = 抵押品數量 × 價格 × (1 - 清算獎勵)
        """
        liquidation_amounts = {}
        
        # 識別主要借款資產
        for borrow_addr, borrow_amount in position.borrow_assets.items():
            # 計算該借款資產的最大清算金額
            max_liquidation = min(
                borrow_amount * 0.5,  # 最多清算借款額的 50%
                borrow_amount  # 不超過借款額
            )
            
            if max_liquidation > 0:
                liquidation_amounts[borrow_addr] = max_liquidation
        
        return liquidation_amounts
    
    def _estimate_liquidation_profit(
        self,
        liquidation_amounts: Dict[str, float],
        prices: Dict[str, float],
        assets: Dict[str, Asset]
    ) -> Dict[str, float]:
        """
        估計清算利潤
        
        清算利潤 = 獲得的抵押品價值 - 支付的債務價值
        
        數學公式:
        Profit = Collateral_Value × (1 - Bonus) - Debt_Value
        """
        profits = {}
        
        for borrow_addr, amount in liquidation_amounts.items():
            asset = assets[borrow_addr]
            debt_value = amount * prices[borrow_addr]
            
            # 計算可獲得的抵押品
            # 假設優先清算風險最高的抵押品
            for collateral_addr in self._get_riskiest_collateral(position):
                collateral_asset = assets[collateral_addr]
                collateral_amount = self._calculate_collateral_out(
                    amount,
                    prices,
                    assets,
                    borrow_addr,
                    collateral_addr
                )
                
                collateral_value = collateral_amount * prices[collateral_addr]
                bonus = collateral_asset.liquidation_bonus
                
                profit = collateral_value * (1 - bonus) - debt_value
                profits[collateral_addr] = profit
        
        return profits
    
    def predict_liquidation_timing(
        self,
        position: Position,
        prices: np.ndarray,
        timestamps: np.ndarray,
        assets: Dict[str, Asset]
    ) -> Optional[Dict]:
        """
        預測清算觸發時間
        
        使用蒙特卡羅模擬估計清算發生的概率和時間
        """
        hf_history = []
        
        for i, (price_row, ts) in enumerate(zip(prices, timestamps)):
            prices_dict = dict(zip(assets.keys(), price_row))
            hf = position.calculate_health_factor(prices_dict, assets)
            hf_history.append(hf)
        
        hf_history = np.array(hf_history)
        
        # 識別何時 HF < 1.0
        below_threshold = hf_history < 1.0
        
        if not np.any(below_threshold):
            return None
        
        first_trigger_idx = np.argmax(below_threshold)
        
        return {
            'will_liquidate': True,
            'expected_time': timestamps[first_trigger_idx],
            'expected_price_change': (hf_history[first_trigger_idx] - hf_history[0]) / hf_history[0],
            'confidence': self._calculate_confidence(hf_history)
        }

第三章:基於真實事件的回測實例

3.1 2020 年 3 月黑色星期四回測分析

事件背景

2020 年 3 月 12 日,加密貨幣市場遭遇歷史性崩潰。以太坊價格在 24 小時內從 $250 暴跌至 $90,跌幅超過 60%。MakerDAO 經歷了大量清算,CDP 持有者損失慘重。

回測框架搭建

class BlackThursdayBacktest:
    """
    2020年3月黑色星期四清算事件回測
    
    目標:
    1. 重現歷史清算事件
    2. 識別風險信號
    3. 評估清算機制效率
    4. 提出改進建議
    """
    
    def __init__(self):
        self.price_data = None
        self.positions = None
        self.liquidation_events = []
        
    def load_black_thursday_data(self):
        """
        加載黑色星期四期間的數據
        
        數據來源:
        1. MakerDAO 合約歷史
        2. Ethereum 價格數據
        3. 清算事件日誌
        """
        # ETH/USD 價格數據(2020年3月12日)
        self.price_data = pd.read_csv('black_thursday_prices.csv')
        
        # 重建 CDP 頭寸
        self.positions = self._reconstruct_cdp_positions(
            start_block=9607277,  # 2020年3月11日
            end_block=9623600     # 2020年3月13日
        )
    
    def run_backtest(self):
        """
        執行回測
        
        步驟:
        1. 初始化頭寸狀態
        2. 逐分鐘模擬價格變動
        3. 計算健康因子
        4. 識別清算事件
        """
        results = []
        
        for idx, row in self.price_data.iterrows():
            eth_price = row['eth_usd']
            timestamp = row['timestamp']
            
            for borrower, position in self.positions.items():
                # 計算抵押率
                collateral_value = position.collateral_amount * eth_price
                debt_value = position.debt_dai * row['dai_usd'] if 'dai_usd' in row else position.debt_dai
                
                # 計算清算觸發價格
                # 清算觸發:collateral_value * 0.66 < debt_value
                # 即:eth_price < debt_value / (collateral_amount * 0.66)
                trigger_price = debt_value / (position.collateral_amount * 0.66)
                
                # 健康因子
                hf = collateral_value / debt_value if debt_value > 0 else float('inf')
                
                results.append({
                    'timestamp': timestamp,
                    'borrower': borrower,
                    'eth_price': eth_price,
                    'trigger_price': trigger_price,
                    'health_factor': hf,
                    'distance_to_liquidation': (eth_price - trigger_price) / eth_price if eth_price > 0 else 0,
                    'liquidatable': hf < 1.5  # MakerDAO 清算閾值
                })
        
        return pd.DataFrame(results)
    
    def analyze_liquidation_patterns(self):
        """
        分析清算模式
        
        識別:
        1. 清算集中時間段
        2. 清算規模分布
        3. 清算觸發因素
        4. 級聯效應
        """
        # 識別清算事件
        liquidation_events = self._identify_liquidation_events()
        
        # 分析時間分布
        time_distribution = liquidation_events.groupby(
            liquidation_events['timestamp'].dt.floor('5min')
        ).size()
        
        # 分析規模分布
        size_distribution = liquidation_events.groupby(
            pd.cut(
                liquidation_events['collateral_eth'],
                bins=[0, 10, 50, 100, 500, float('inf')],
                labels=['0-10', '10-50', '50-100', '100-500', '500+']
            )
        ).size()
        
        # 識別級聯效應
        cascade_events = self._identify_cascade_effects(liquidation_events)
        
        return {
            'time_distribution': time_distribution,
            'size_distribution': size_distribution,
            'cascade_events': cascade_events,
            'total_liquidated_eth': liquidation_events['collateral_eth'].sum(),
            'total_liquidated_dai': liquidation_events['debt_dai'].sum()
        }

# 回測結果分析
def analyze_black_thursday_results():
    """
    分析黑色星期四回測結果
    """
    backtester = BlackThursdayBacktest()
    backtester.load_black_thursday_data()
    
    results = backtester.run_backtest()
    analysis = backtester.analyze_liquidation_patterns()
    
    # 關鍵指標
    print("=" * 60)
    print("2020年3月12日黑色星期四清算事件回測分析")
    print("=" * 60)
    
    print(f"\n清算總規模:")
    print(f"  - ETH 抵押品清算總量: {analysis['total_liquidated_eth']:.2f}")
    print(f"  - DAI 債務清算總量: {analysis['total_liquidated_dai']:.2f}")
    
    print(f"\n清算時間分布:")
    for timestamp, count in analysis['time_distribution'].head(10).items():
        print(f"  {timestamp}: {count} 筆清算")
    
    print(f"\n清算規模分布:")
    for size_range, count in analysis['size_distribution'].items():
        print(f"  {size_range} ETH: {count} 筆 ({count/len(results)*100:.1f}%)")
    
    # 識別預警信號
    pre_crash_data = results[results['eth_price'] > 200]
    warnings = pre_crash_data[pre_crash_data['health_factor'] < 1.5]
    
    print(f"\n清算前預警分析:")
    print(f"  - 清算閾值以下頭寸數: {len(warnings)}")
    print(f"  - 預警頭寸 ETH 總量: {warnings['health_factor'].sum():.2f}")

3.2 2021 年 5 月 19 日市場崩潰回測

事件背景

2021 年 5 月 19 日,加密貨幣市場再次遭遇重創。比特幣和以太坊價格在數小時內暴跌超過 30%。這次事件涉及多個 DeFi 協議,包括 Compound、Aave、以及多個槓桿代幣協議。

多協議清算回測框架

class MultiProtocolLiquidationBacktest:
    """
    多協議清算回測框架
    
    支援同時分析:
    1. Compound Finance
    2. Aave V2
    3. MakerDAO
    4. 其他主流借貸協議
    """
    
    PROTOCOLS = {
        'compound': {
            'address': '0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B',
            'liquidation_threshold': 0.5,  # Compound 使用 factor
            'close_factor': 0.5
        },
        'aave_v2': {
            'address': '0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9',
            'liquidation_threshold': 0.5,
            'liquidation_bonus': 0.075  # 7.5%
        },
        'makerdao': {
            'address': '0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B',
            'liquidation_threshold': 0.66,
            'liquidation_penalty': 0.13
        }
    }
    
    def __init__(self):
        self.protocol_states = {}
        
    async def load_protocol_state(
        self,
        protocol: str,
        block: int
    ):
        """
        加載特定時間點的協議狀態
        """
        if protocol not in self.PROTOCOLS:
            raise ValueError(f"Unknown protocol: {protocol}")
        
        config = self.PROTOCOLS[protocol]
        
        if protocol == 'compound':
            state = await self._load_compound_state(config['address'], block)
        elif protocol == 'aave_v2':
            state = await self._load_aave_state(config['address'], block)
        elif protocol == 'makerdao':
            state = await self._load_makerdao_state(config['address'], block)
        
        self.protocol_states[protocol] = state
        return state
    
    def simulate_crash(
        self,
        price_shock: float,
        volatility: float,
        duration_hours: int = 24
    ) -> pd.DataFrame:
        """
        模擬市場崩潰情景
        
        參數:
        - price_shock: 價格跌幅(如 0.3 表示 30% 跌幅)
        - volatility: 波動率
        - duration_hours: 模擬時長(小時)
        """
        results = []
        
        # 每小時一個數據點
        n_steps = duration_hours
        dt = 1 / (365 * 24)  # 時間步長(年)
        
        for protocol, state in self.protocol_states.items():
            config = self.PROTOCOLS[protocol]
            
            # 模擬 ETH 價格路徑
            S0 = state['eth_price']
            T = duration_hours / (365 * 24)
            
            # 使用幾何布朗運動
            paths = simulate_gbm_paths(
                S0=np.array([S0]),
                mu=np.array([-0.5 * volatility**2]),  # 負漂移表示下跌
                sigma=np.array([volatility]),
                T=T,
                dt=dt,
                n_paths=1000
            )
            
            # 對每個路徑計算清算概率
            liquidation_probs = []
            
            for path in paths:
                for price in path:
                    health_factors = self._calculate_position_hfs(
                        state['positions'],
                        price,
                        protocol
                    )
                    
                    liquidatable = np.sum(health_factors < config['liquidation_threshold'])
                    liquidation_probs.append(liquidatable / len(health_factors))
            
            results.append({
                'protocol': protocol,
                'expected_liquidations': np.mean(liquidation_probs) * len(state['positions']),
                'max_liquidations': np.max(liquidation_probs) * len(state['positions']),
                'liquidation_probability': np.mean(np.array(liquidation_probs) > 0)
            })
        
        return pd.DataFrame(results)
    
    def _calculate_position_hfs(
        self,
        positions: List[Position],
        price: float,
        protocol: str
    ) -> np.ndarray:
        """
        批量計算頭寸健康因子
        """
        health_factors = []
        
        for position in positions:
            collateral_value = position.collateral_amount * price
            debt_value = position.debt_amount
            
            hf = collateral_value / debt_value if debt_value > 0 else float('inf')
            health_factors.append(hf)
        
        return np.array(health_factors)

3.3 2022 年 Terra/Luna 崩潰回測分析

事件背景

2022 年 5 月,Terra 生態系統(UST/LUNA)經歷了歷史性的崩潰。UST 脫錨最終導致幾乎歸零,影響波及整個 DeFi 生態。

清算級聯效應分析

class TerraCollapseAnalysis:
    """
    Terra/Luna 崩潰事件清算分析
    
    關注重點:
    1. UST 脫錨對 Anchor 存款利率的影響
    2. UST 抵押品頭寸的清算壓力
    3. 跨協議清算級聯效應
    """
    
    def __init__(self):
        self.anchor_positions = {}
        self.ust_price_data = None
        
    def analyze_ust_depeg_events(self):
        """
        分析 UST 脫錨事件
        
        時間線:
        - 2022-05-07: UST 開始輕微脫錨
        - 2022-05-09: 脫錨加劇
        - 2022-05-11: UST 快速崩潰
        - 2022-05-12: UST 接近歸零
        """
        # UST/USD 價格數據
        self.ust_price_data = self._fetch_ust_prices(
            start='2022-05-07',
            end='2022-05-15'
        )
        
        # 識別脫錨階段
        depeg_phases = self._identify_depeg_phases(self.ust_price_data)
        
        # 計算每階段的清算壓力
        liquidation_pressure = {}
        
        for phase, (start, end) in depeg_phases.items():
            phase_data = self.ust_price_data[
                (self.ust_price_data['timestamp'] >= start) &
                (self.ust_price_data['timestamp'] <= end)
            ]
            
            pressure = self._calculate_liquidation_pressure(phase_data)
            liquidation_pressure[phase] = pressure
        
        return {
            'depeg_phases': depeg_phases,
            'liquidation_pressure': liquidation_pressure,
            'total_ust_liquidated': sum(p['total_ust'] for p in liquidation_pressure.values())
        }
    
    def _identify_depeg_phases(self, price_data: pd.DataFrame) -> Dict[str, Tuple]:
        """
        識別脫錨階段
        
        基於 UST 偏離 1 美元的程度劃分階段
        """
        phases = {}
        
        # Phase 1: 輕微偏離 (< 5%)
        phase1_mask = (price_data['ust_usd'] >= 0.95) & (price_data['ust_usd'] < 1.0)
        if phase1_mask.any():
            phases['minor_depeg'] = (
                price_data[phase1_mask]['timestamp'].min(),
                price_data[phase1_mask]['timestamp'].max()
            )
        
        # Phase 2: 中度偏離 (5-20%)
        phase2_mask = (price_data['ust_usd'] >= 0.80) & (price_data['ust_usd'] < 0.95)
        if phase2_mask.any():
            phases['moderate_depeg'] = (
                price_data[phase2_mask]['timestamp'].min(),
                price_data[phase2_mask]['timestamp'].max()
            )
        
        # Phase 3: 嚴重偏離 (> 20%)
        phase3_mask = price_data['ust_usd'] < 0.80
        if phase3_mask.any():
            phases['severe_depeg'] = (
                price_data[phase3_mask]['timestamp'].min(),
                price_data[phase3_mask]['timestamp'].max()
            )
        
        return phases
    
    def _calculate_liquidation_pressure(self, price_data: pd.DataFrame) -> Dict:
        """
        計算清算壓力指標
        
        指標包括:
        1. 處於風險中的 UST 總量
        2. 預期清算金額
        3. 市場衝擊估計
        """
        pressure = {}
        
        # 計算健康因子分布
        hfs = self._calculate_hf_distribution(price_data)
        
        # 識別高風險頭寸
        high_risk = hfs[hfs < 1.2]  # 接近清算閾值
        
        pressure['at_risk_positions'] = len(high_risk)
        pressure['at_risk_ust'] = high_risk['collateral_ust'].sum()
        pressure['expected_liquidations'] = len(hfs[hfs < 1.0])
        pressure['total_ust'] = hfs['collateral_ust'].sum()
        
        # 計算市場衝擊
        # 假設清算導致 UST 進一步拋售
        pressure['market_impact_estimate'] = self._estimate_market_impact(
            pressure['expected_liquidations'],
            price_data['volume'].iloc[-1] if len(price_data) > 0 else 0
        )
        
        return pressure

第四章:壓力測試與敏感性分析

4.1 壓力測試情景設計

class LiquidationStressTest:
    """
    清算風險壓力測試框架
    
    支援的情景:
    1. 歷史重現情景(Historical Scenarios)
    2. 假設情景(Hypothetical Scenarios)
    3. 逆向壓力測試(Reverse Stress Tests)
    """
    
    SCENARIOS = {
        'black_thursday_2020': {
            'description': '2020年3月12日市場崩潰',
            'eth_price_change': -0.65,
            'volatility': 0.15,
            'duration_hours': 24
        },
        'may_crash_2021': {
            'description': '2021年5月19日市場崩潰',
            'eth_price_change': -0.40,
            'volatility': 0.12,
            'duration_hours': 12
        },
        'terra_collapse_2022': {
            'description': '2022年5月 Terra/Luna 崩潰',
            'eth_price_change': -0.30,
            'ust_depeg': True,
            'volatility': 0.10,
            'duration_hours': 72
        },
        'crypto_winter_2022': {
            'description': '2022年加密寒冬',
            'eth_price_change': -0.75,
            'volatility': 0.08,
            'duration_hours': 720  # 30天
        },
        'extreme_shock': {
            'description': '極端價格衝擊',
            'eth_price_change': -0.90,
            'volatility': 0.20,
            'duration_hours': 1
        }
    }
    
    def __init__(self, positions: List[Position], assets: Dict[str, Asset]):
        self.positions = positions
        self.assets = assets
        
    def run_historical_scenarios(self) -> pd.DataFrame:
        """
        運行歷史情景測試
        """
        results = []
        
        for scenario_name, params in self.SCENARIOS.items():
            result = self._run_scenario(scenario_name, params)
            results.append(result)
        
        return pd.DataFrame(results)
    
    def _run_scenario(self, name: str, params: Dict) -> Dict:
        """
        運行單一情景
        """
        # 模擬價格路徑
        current_prices = self._get_current_prices()
        
        # 計算最終價格
        final_prices = {
            addr: price * (1 + params['eth_price_change'])
            for addr, price in current_prices.items()
        }
        
        # 計算情景下的清算概率
        liquidation_prob = self._calculate_scenario_liquidation_prob(
            current_prices,
            final_prices,
            params['volatility'],
            params['duration_hours']
        )
        
        # 計算預期損失
        expected_loss = self._calculate_expected_loss(
            current_prices,
            final_prices
        )
        
        return {
            'scenario': name,
            'description': params['description'],
            'price_change': params['eth_price_change'],
            'liquidation_probability': liquidation_prob,
            'positions_at_risk': int(liquidation_prob * len(self.positions)),
            'expected_loss': expected_loss,
            'max_loss': self._calculate_max_loss(final_prices)
        }
    
    def run_reverse_stress_test(self) -> Dict:
        """
        逆向壓力測試
        
        目標:找出導致系統性清算所需的最小價格變動
        """
        # 找到臨界價格點
        critical_prices = {}
        
        for position in self.positions:
            borrower = position.borrower
            
            # 二分搜索找到清算觸發價格
            trigger_price = self._binary_search_trigger_price(position)
            
            critical_prices[borrower] = trigger_price
        
        # 計算系統性清算閾值
        # 定義系統性清算為 > 50% 的頭寸被清算
        sorted_prices = sorted(critical_prices.values())
        systemic_threshold = sorted_prices[int(len(sorted_prices) * 0.5)]
        
        return {
            'systemic_liquidation_threshold': systemic_threshold,
            'price_drop_required': 1 - systemic_threshold / self._get_eth_price(),
            'positions_above_threshold': sum(
                1 for p in critical_prices.values() if p > systemic_threshold
            ),
            'critical_prices_distribution': {
                'min': min(critical_prices.values()),
                'max': max(critical_prices.values()),
                'median': np.median(list(critical_prices.values())),
                'mean': np.mean(list(critical_prices.values()))
            }
        }
    
    def _binary_search_trigger_price(self, position: Position) -> float:
        """
        二分搜索找到清算觸發價格
        
        目標:找到使 HF = 1.0 的最低價格
        """
        low = 0.01  # 最低價格
        high = self._get_eth_price()  # 當前價格
        
        while high - low > 0.01:
            mid = (low + high) / 2
            
            prices = {'eth': mid}
            hf = position.calculate_health_factor(prices, self.assets)
            
            if hf > 1.0:
                low = mid
            else:
                high = mid
        
        return low

4.2 敏感性分析

class SensitivityAnalysis:
    """
    敏感性分析框架
    
    分析目標:
    1. 健康因子對各參數的敏感度
    2. 清算概率的關鍵驅動因素
    3. 最優化抵押品配置
    """
    
    def __init__(self, position: Position, assets: Dict[str, Asset]):
        self.position = position
        self.assets = assets
        self.base_prices = self._get_current_prices()
    
    def calculate_health_factor_sensitivity(
        self,
        param: str,
        range_pct: float = 0.2
    ) -> pd.DataFrame:
        """
        計算健康因子對特定參數的敏感度
        
        數學定義:
        Sensitivity = ∂HF / ∂param × (param / HF)
        
        即:彈性(Elasticity)
        """
        sensitivities = []
        
        base_hf = self.position.calculate_health_factor(
            self.base_prices,
            self.assets
        )
        
        # 測試不同參數值
        for pct in np.linspace(-range_pct, range_pct, 21):
            modified_prices = self.base_prices.copy()
            
            if param == 'eth_price':
                modified_prices['eth'] *= (1 + pct)
            elif param == 'eth_volatility':
                # 波動率影響通過模擬分析
                pass
            
            hf = self.position.calculate_health_factor(
                modified_prices,
                self.assets
            )
            
            # 計算彈性
            if pct != 0:
                elasticity = ((hf - base_hf) / base_hf) / pct
            else:
                elasticity = 0
            
            sensitivities.append({
                'param_change_pct': pct * 100,
                'health_factor': hf,
                'elasticity': elasticity
            })
        
        return pd.DataFrame(sensitivities)
    
    def monte_carlo_sensitivity(
        self,
        n_simulations: int = 10000
    ) -> Dict:
        """
        蒙特卡羅敏感性分析
        
        使用蒙特卡羅模擬估計各參數對清算概率的貢獻
        """
        # 定義參數分佈
        param_distributions = {
            'eth_price': {
                'type': 'lognormal',
                'mu': np.log(self.base_prices['eth']),
                'sigma': 0.05  # 5% 日波動率
            },
            'collateral_factor': {
                'type': 'uniform',
                'low': 0.5,
                'high': 0.85
            },
            'liquidation_threshold': {
                'type': 'uniform',
                'low': 0.8,
                'high': 1.0
            }
        }
        
        # 運行模擬
        liquidation_flags = []
        parameter_impacts = {k: [] for k in param_distributions.keys()}
        
        for _ in range(n_simulations):
            # 採樣參數
            sampled_params = {}
            for param_name, dist in param_distributions.items():
                if dist['type'] == 'lognormal':
                    sampled_params[param_name] = np.random.lognormal(
                        dist['mu'],
                        dist['sigma']
                    )
                elif dist['type'] == 'uniform':
                    sampled_params[param_name] = np.random.uniform(
                        dist['low'],
                        dist['high']
                    )
            
            # 計算健康因子
            modified_assets = self.assets.copy()
            modified_prices = {'eth': sampled_params['eth_price']}
            
            hf = self.position.calculate_health_factor(
                modified_prices,
                modified_assets
            )
            
            liquidation_flags.append(hf < 1.0)
            
            # 記錄參數影響
            for param_name, value in sampled_params.items():
                parameter_impacts[param_name].append(value)
        
        liquidation_flags = np.array(liquidation_flags)
        
        # 計算各參數的敏感度貢獻
        sensitivity_results = {}
        
        for param_name, values in parameter_impacts.items():
            values = np.array(values)
            
            # 分組分析
            high_values = liquidation_flags[values > np.median(values)]
            low_values = liquidation_flags[values <= np.median(values)]
            
            sensitivity_results[param_name] = {
                'base_liquidation_prob': np.mean(liquidation_flags),
                'high_param_liquidation_prob': np.mean(high_values),
                'low_param_liquidation_prob': np.mean(low_values),
                'sensitivity_contribution': np.mean(high_values) - np.mean(low_values)
            }
        
        return sensitivity_results

第五章:風險參數優化框架

5.1 清算閾值優化

class LiquidationThresholdOptimizer:
    """
    清算閾值優化器
    
    目標:找到最優的清算閾值,平衡風險控制與資金效率
    """
    
    def __init__(
        self,
        historical_data: pd.DataFrame,
        positions: List[Position]
    ):
        self.historical_data = historical_data
        self.positions = positions
        
    def optimize_threshold(
        self,
        objective: str = 'sharpe'
    ) -> Dict:
        """
        優化清算閾值
        
        目標函數選項:
        1. 'sharpe': 最大化風險調整後收益
        2. 'min_risk': 最小化清算風險
        3. 'max_utilization': 最大化資金利用率
        
        數學模型:
        max Threshold  E[Return] - λ * Var[Return]
        
        其中:
        - Return = 借款利息收入 - 預期清算損失
        - λ = 風險厭惡系數
        """
        thresholds = np.linspace(1.0, 2.0, 101)
        results = []
        
        for threshold in thresholds:
            result = self._evaluate_threshold(threshold)
            results.append(result)
        
        results_df = pd.DataFrame(results)
        
        # 根據目標選擇最優閾值
        if objective == 'sharpe':
            # 最大化夏普比率
            sharpe = (results_df['expected_return'] - 0.02) / results_df['return_std']
            optimal_idx = sharpe.idxmax()
        elif objective == 'min_risk':
            # 最小化破產概率
            optimal_idx = results_df['liquidation_prob'].idxmin()
        elif objective == 'max_utilization':
            # 最大化利用率
            optimal_idx = results_df['utilization'].idxmax()
        
        optimal_threshold = results_df.loc[optimal_idx, 'threshold']
        
        return {
            'optimal_threshold': optimal_threshold,
            'expected_return': results_df.loc[optimal_idx, 'expected_return'],
            'liquidation_prob': results_df.loc[optimal_idx, 'liquidation_prob'],
            'utilization': results_df.loc[optimal_idx, 'utilization'],
            'all_results': results_df
        }
    
    def _evaluate_threshold(self, threshold: float) -> Dict:
        """
        評估特定閾值的效果
        """
        # 計算歷史期間的清算事件
        liquidations = 0
        total_exposure = 0
        
        for _, row in self.historical_data.iterrows():
            prices = row['prices']
            
            for position in self.positions:
                hf = position.calculate_health_factor(prices, self.assets)
                
                if hf < threshold:
                    liquidations += 1
                    total_exposure += position.debt_value
        
        # 計算指標
        liquidation_prob = liquidations / len(self.historical_data)
        
        # 估算預期收益(需要更複雜的模型)
        interest_rate = 0.05  # 假設借款利率
        expected_return = interest_rate * (1 - liquidation_prob) - liquidation_prob * 0.1
        
        return {
            'threshold': threshold,
            'liquidation_prob': liquidation_prob,
            'expected_return': expected_return,
            'return_std': np.sqrt(liquidation_prob * (1 - liquidation_prob)),
            'utilization': 0.75  # 簡化計算
        }

5.2 抵押品配置優化

class CollateralOptimizer:
    """
    抵押品配置優化器
    
    目標:在給定風險約束下,最大化收益
    """
    
    def __init__(self):
        self.available_collaterals = {}
        
    def optimize_allocation(
        self,
        target_hf: float = 1.5,
        max_volatility: float = 0.3,
        min_expected_return: float = 0.05
    ) -> Dict:
        """
        優化抵押品配置
        
        數學模型:
        max Σ(w_i * r_i)
        s.t.
        1. Σ(w_i) = 1
        2. Σ(w_i * σ_i) ≤ max_volatility
        3. HF ≥ target_hf
        
        其中:
        - w_i: 抵押品 i 的權重
        - r_i: 抵押品 i 的預期收益率
        - σ_i: 抵押品 i 的波動率
        """
        from scipy.optimize import minimize
        
        n_assets = len(self.available_collaterals)
        
        # 準備參數
        returns = np.array([c['return'] for c in self.available_collaterals.values()])
        volatilities = np.array([c['volatility'] for c in self.available_collaterals.values()])
        correlations = self._build_correlation_matrix()
        
        # 目標函數(最大化收益的負值)
        def objective(weights):
            return -np.dot(weights, returns)
        
        # 約束條件
        constraints = [
            # 權重和為 1
            {'type': 'eq', 'fun': lambda w: np.sum(w) - 1},
            # 健康因子約束
            {'type': 'ineq', 'fun': lambda w: self._calculate_hf_from_weights(w) - target_hf}
        ]
        
        # 邊界(每種抵押品權重 0-100%)
        bounds = [(0, 1) for _ in range(n_assets)]
        
        # 初始權重
        x0 = np.ones(n_assets) / n_assets
        
        # 優化
        result = minimize(
            objective,
            x0,
            method='SLSQP',
            bounds=bounds,
            constraints=constraints
        )
        
        optimal_weights = result.x
        
        return {
            'optimal_weights': {
                list(self.available_collaterals.keys())[i]: optimal_weights[i]
                for i in range(n_assets)
            },
            'expected_return': -result.fun,
            'portfolio_volatility': self._calculate_portfolio_volatility(
                optimal_weights,
                volatilities,
                correlations
            ),
            'health_factor': self._calculate_hf_from_weights(optimal_weights)
        }
    
    def _calculate_portfolio_volatility(
        self,
        weights: np.ndarray,
        volatilities: np.ndarray,
        correlations: np.ndarray
    ) -> float:
        """
        計算組合波動率
        
        數學公式:
        σ_p = sqrt(Σ_i Σ_j w_i w_j σ_i σ_j ρ_ij)
        """
        # 構建協方差矩陣
        cov_matrix = np.outer(volatilities, volatilities) * correlations
        
        # 組合波動率
        variance = np.dot(weights, np.dot(cov_matrix, weights))
        return np.sqrt(variance)

第六章:實時風險監控系統

6.1 清算風險監控儀表板

class LiquidationRiskMonitor:
    """
    實時清算風險監控系統
    
    功能:
    1. 頭寸健康因子追蹤
    2. 清算預警
    3. 風險儀表板
    4. 緊急通知
    """
    
    def __init__(
        self,
        web3_endpoint: str,
        alert_thresholds: Dict[str, float] = None
    ):
        self.w3 = Web3(Web3.HTTPProvider(web3_endpoint))
        
        # 預設預警閾值
        self.alert_thresholds = alert_thresholds or {
            'critical': 1.0,    # 立即清算風險
            'warning': 1.2,     # 警告級別
            'caution': 1.5,      # 注意級別
            'safe': 2.0         # 安全級別
        }
        
        self.alert_history = []
        
    async def monitor_positions(
        self,
        positions: List[Position],
        check_interval: int = 15  # 秒
    ):
        """
        持續監控頭寸風險
        
        每 check_interval 秒檢查一次所有頭寸的風險狀態
        """
        while True:
            try:
                # 獲取最新價格
                prices = await self._fetch_latest_prices()
                
                # 評估所有頭寸
                risk_assessment = self._assess_all_positions(positions, prices)
                
                # 發送預警
                await self._process_alerts(risk_assessment)
                
                # 等待下一個檢查週期
                await asyncio.sleep(check_interval)
                
            except Exception as e:
                print(f"Monitor error: {e}")
                await asyncio.sleep(check_interval)
    
    def _assess_all_positions(
        self,
        positions: List[Position],
        prices: Dict[str, float]
    ) -> pd.DataFrame:
        """
        評估所有頭寸的風險狀態
        """
        assessments = []
        
        for position in positions:
            hf = position.calculate_health_factor(prices, self.assets)
            
            # 確定風險級別
            if hf < self.alert_thresholds['critical']:
                risk_level = 'CRITICAL'
            elif hf < self.alert_thresholds['warning']:
                risk_level = 'WARNING'
            elif hf < self.alert_thresholds['caution']:
                risk_level = 'CAUTION'
            else:
                risk_level = 'SAFE'
            
            # 計算距離清算的空間
            distance_to_liquidation = (hf - 1.0) / hf if hf > 1.0 else 0
            
            # 估算清算窗口(基於當前波動率)
            time_to_liquidation = self._estimate_time_to_liquidation(
                position,
                prices
            )
            
            assessments.append({
                'borrower': position.borrower,
                'health_factor': hf,
                'risk_level': risk_level,
                'distance_to_liquidation': distance_to_liquidation,
                'time_to_liquidation_hours': time_to_liquidation,
                'total_collateral_value': self._calculate_collateral_value(position, prices),
                'total_debt_value': self._calculate_debt_value(position, prices)
            })
        
        return pd.DataFrame(assessments)
    
    def _estimate_time_to_liquidation(
        self,
        position: Position,
        prices: Dict[str, float]
    ) -> Optional[float]:
        """
        估算距離清算的時間
        
        使用蒙特卡羅模擬估計
        """
        current_hf = position.calculate_health_factor(prices, self.assets)
        
        if current_hf >= 1.0:
            # 計算觸發清算的價格變動
            eth_price = prices['eth']
            target_price = eth_price * (1 - (current_hf - 1) / current_hf)
            
            # 估算時間(基於歷史波動率)
            daily_vol = 0.05  # 假設 5% 日波動率
            
            # 使用正態分佈估算
            # P(price < target) = Φ((target - current) / (current * vol * sqrt(t)))
            # 設 P = 0.5,估算 t
            
            if target_price > 0:
                # 預期首次觸及時間
                expected_time = ((current_hf - 1) / current_hf)**2 / (daily_vol**2)
                return expected_time * 24  # 轉換為小時
        
        return None

第七章:實際應用案例

7.1 借貸協議風險評估

以下是一個完整的借貸協議清算風險評估案例:

def evaluate_lending_protocol_risk():
    """
    評估借貸協議的清算風險
    
    輸出:
    1. 風險指標摘要
    2. 壓力測試結果
    3. 敏感性分析
    4. 風險建議
    """
    # 初始化回測框架
    backtester = DeFiLiquidationBacktester(
        web3_endpoint='https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY',
        protocol_address='0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9'  # Aave V2
    )
    
    # 加載歷史數據(過去一年)
    start_block = 19000000  # 約一年前
    end_block = 20500000    # 當前
    
    asyncio.run(backtester.load_historical_positions(start_block, end_block))
    
    # 運行回測
    historical_results = backtester.simulate_health_factors('historical')
    
    # 運行壓力測試
    stress_tester = LiquidationStressTest(
        positions=backtester.positions,
        assets=backtester.assets
    )
    stress_results = stress_tester.run_historical_scenarios()
    
    # 生成報告
    report = {
        'portfolio_summary': {
            'total_positions': len(backtester.positions),
            'average_health_factor': historical_results['health_factor'].mean(),
            'positions_below_warning': len(
                historical_results[historical_results['health_factor'] < 1.5]
            ),
            'positions_liquidatable': len(
                historical_results[historical_results['liquidatable']]
            )
        },
        'stress_test_results': stress_results.to_dict('records'),
        'risk_recommendations': generate_risk_recommendations(
            historical_results,
            stress_results
        )
    }
    
    return report

def generate_risk_recommendations(
    historical: pd.DataFrame,
    stress: pd.DataFrame
) -> List[Dict]:
    """
    根據分析結果生成風險建議
    """
    recommendations = []
    
    # 分析健康因子分布
    hf_percentiles = historical['health_factor'].describe()
    
    if hf_percentiles['50%'] < 1.5:
        recommendations.append({
            'category': 'concentration',
            'severity': 'high',
            'recommendation': '考慮降低高槓桿頭寸的比例',
            'action': '增加抵押品或減少借款'
        })
    
    # 分析壓力測試結果
    for _, scenario in stress.iterrows():
        if scenario['liquidation_probability'] > 0.3:
            recommendations.append({
                'category': 'stress_resilience',
                'severity': 'high',
                'recommendation': f'{scenario["description"]} 情景下清算概率過高',
                'action': f'需要為 {scenario["scenario"]} 情景準備應對預案'
            })
    
    return recommendations

7.2 清算策略回測

class LiquidationStrategyBacktest:
    """
    清算策略回測框架
    
    測試不同清算策略的盈利能力
    """
    
    def __init__(self):
        self.strategies = {
            'aggressive': {
                'min_profit_threshold': 0.01,  # 1% 最小利潤
                'max_gas_price': 200  # Gwei
            },
            'conservative': {
                'min_profit_threshold': 0.03,  # 3% 最小利潤
                'max_gas_price': 100  # Gwei
            },
            'opportunistic': {
                'min_profit_threshold': 0.02,
                'max_gas_price': 150,
                'partial_liquidation': True  # 部分清算
            }
        }
        
    def backtest_strategy(
        self,
        strategy_name: str,
        historical_events: List[LiquidationEvent]
    ) -> Dict:
        """
        回測特定清算策略
        """
        strategy = self.strategies[strategy_name]
        
        trades = []
        total_profit = 0
        total_gas_cost = 0
        missed_opportunities = 0
        
        for event in historical_events:
            # 計算清算利潤
            profit = self._calculate_liquidation_profit(event)
            gas_cost = self._estimate_gas_cost(event)
            
            # 檢查策略條件
            net_profit = profit - gas_cost
            
            if net_profit >= strategy['min_profit_threshold']:
                trades.append({
                    'timestamp': event.timestamp,
                    'profit': profit,
                    'gas_cost': gas_cost,
                    'net_profit': net_profit,
                    'executed': True
                })
                total_profit += net_profit
                total_gas_cost += gas_cost
            else:
                missed_opportunities += 1
        
        return {
            'strategy': strategy_name,
            'total_trades': len(trades),
            'total_profit': total_profit,
            'total_gas_cost': total_gas_cost,
            'net_profit': total_profit - total_gas_cost,
            'missed_opportunities': missed_opportunities,
            'win_rate': len([t for t in trades if t['net_profit'] > 0]) / len(trades) if trades else 0,
            'average_trade_profit': np.mean([t['net_profit'] for t in trades]) if trades else 0
        }
    
    def _calculate_liquidation_profit(self, event: LiquidationEvent) -> float:
        """
        計算清算利潤
        
        利潤 = 獲得抵押品價值 - 支付債務價值
        """
        collateral_value = sum(
            amount * self._get_price(event.timestamp, asset)
            for asset, amount in event.liquidated_collateral.items()
        )
        
        debt_value = sum(
            amount * self._get_price(event.timestamp, asset)
            for asset, amount in event.repaid_debt.items()
        )
        
        bonus = event.profit  # 清算獎勵
        
        return collateral_value - debt_value + bonus

結論

本指南提供了完整的 DeFi 清算風險量化回測框架,涵蓋從理論基礎到實際應用的各個層面。通過本指南,讀者應能夠:

  1. 理解清算機制的數學原理:掌握健康因子、清算觸發條件的數學定義
  2. 設計量化回測框架:建立完整的數據處理、風險計算、事件模擬流程
  3. 進行歷史事件回測:重現並分析 2020-2024 年間的重大清算事件
  4. 執行壓力測試:設計並運行多種壓力測試情景
  5. 進行敏感性分析:識別清算風險的關鍵驅動因素
  6. 開發風險監控系統:建立實時風險監控和預警機制

清算風險管理是 DeFi 借貸協議持續發展的關鍵領域。隨著市場條件的變化和新協議的出現,本框架需要不斷更新和優化。建議讀者:

通過系統性的量化分析,我們可以更好地理解和管理 DeFi 清算風險,為整個生態系統的健康發展做出貢獻。

參考資源

延伸閱讀與來源

這篇文章對您有幫助嗎?

評論

發表評論

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

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