DEV Community

drake
drake

Posted on

Sharpe Ratio— Python 实现(from gpt)

from typing import List, Dict, Iterable, Tuple
import math

def sharpe_from_history(
    account_value_history: Iterable[Tuple[int, float]],
    pnl_history: Iterable[Tuple[int, float]],
    min_periods: int = 2
) -> float:
    """
    按照上传 JS 的逻辑计算 Sharpe Ratio(简化版):
      - account_value_history: list of (timestamp, total_value)
      - pnl_history: list of (timestamp, cumulative_pnl) 或者每 period 的 pnl 也可(JS 用的是 pnlHistory 和 accountValueHistory 对应索引)
      - 实现细节与 JS 对齐:
         1. 构造一个列表 l,其元素包含 time, totalValue, pnl (period pnl)
         2. 计算 avg_total = average of totalValue across the period (JS 用 sum/len)
         3. 对每个 period(跳过第一个),计算 r_i = 100 * pnl / avg_total
         4. 计算 mean_r = mean(r)
         5. 计算样本标准差(分母 n-1):std = sqrt( sum((r - mean_r)^2) / (n-1) )
         6. 若 std == 0 返回 0;否则返回 mean_r / std
    """
    # 将输入转换为 list,确保索引访问
    av = list(account_value_history)
    ph = list(pnl_history)

    # 边界:长度不一致或长度太小
    if not av or len(av) != len(ph) or len(av) < min_periods:
        return 0.0

    # 构造每 period 的记录(与 JS 逻辑一致)
    records = []
    for i in range(len(av)):
        t_av, total_value = av[i]
        # JS 用 pnlHistory[t][1] - pnlHistory[t-1][1] 做 period pnl
        if i == 0:
            period_pnl = 0.0
        else:
            prev_pnl = ph[i-1][1] if i-1 < len(ph) else 0.0
            cur_pnl = ph[i][1]
            period_pnl = cur_pnl - prev_pnl
        records.append({"time": t_av, "totalValue": float(total_value), "pnl": float(period_pnl)})

    # avg totalValue (JS: s = l.reduce(...); i = ue(s, l.length))
    total_sum = sum(r["totalValue"] for r in records)
    avg_total = total_sum / len(records) if len(records) > 0 else 0.0
    if avg_total == 0:
        return 0.0

    # 计算 r:从 records 的 index 1 开始(JS: if (0 === t) continue; they skip first in building r)
    r_vals: List[float] = []
    for idx, rec in enumerate(records):
        if idx == 0:
            continue
        # JS 使用 te(100*e.pnl/i) 然后 Number(a) -> 100 * pnl / avg_total
        r_vals.append(100.0 * rec["pnl"] / avg_total)

    n = len(r_vals)
    if n < 2:
        return 0.0

    mean_r = sum(r_vals) / n
    # 样本方差(分母 n-1),与 JS 一致
    sum_sq = sum((x - mean_r) ** 2 for x in r_vals)
    variance = sum_sq / (n - 1)
    stddev = math.sqrt(variance)
    if stddev == 0:
        return 0.0
    return mean_r / stddev

Enter fullscreen mode Exit fullscreen mode

Top comments (0)