最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Python中量化數(shù)據(jù)的處理技巧(附實(shí)戰(zhàn)代碼)

 更新時(shí)間:2026年05月21日 08:26:07   作者:用戶941614693365  
量化研究中,拿到數(shù)據(jù)只是第一步,本文系統(tǒng)講解量化數(shù)據(jù)處理(復(fù)權(quán)、對(duì)齊、缺失值與換手率計(jì)算)中最常見(jiàn)的幾個(gè)坑,以及對(duì)應(yīng)的解決方案,有需要的小伙伴可以

量化研究中,拿到數(shù)據(jù)只是第一步。如果不理解復(fù)權(quán)、不處理缺失值、不會(huì)做多股票數(shù)據(jù)對(duì)齊,算出來(lái)的指標(biāo)可能全是錯(cuò)的。本文系統(tǒng)講解量化數(shù)據(jù)處理中最常見(jiàn)的幾個(gè)坑,以及對(duì)應(yīng)的解決方案。

一、這些問(wèn)題你一定遇到過(guò)

  • 策略回測(cè)結(jié)果特別好,一查發(fā)現(xiàn)是沒(méi)復(fù)權(quán),除權(quán)日價(jià)格跳空導(dǎo)致假信號(hào)
  • 多只股票做橫截面分析,日期對(duì)不齊,merge 后一堆 NaN
  • 計(jì)算換手率但不知道流通股本怎么來(lái)
  • 停牌股的空數(shù)據(jù)把整個(gè)分析搞亂

這些都是量化數(shù)據(jù)處理中的典型問(wèn)題,下面逐一解決。

二、環(huán)境準(zhǔn)備

pip install "tickflow[all]" --upgrade
from tickflow import TickFlow

# 免費(fèi)服務(wù)(日 K 線夠用)
tf_free = TickFlow.free()

# 完整服務(wù)(需要實(shí)時(shí)行情、財(cái)務(wù)數(shù)據(jù)等)
tf = TickFlow(api_key="your-api-key")

TickFlow 文檔:docs.tickflow.org

三、復(fù)權(quán)處理

為什么必須復(fù)權(quán)

上市公司會(huì)進(jìn)行分紅派息、送股轉(zhuǎn)增等操作,導(dǎo)致股價(jià)在除權(quán)日出現(xiàn)跳空。如果不復(fù)權(quán),技術(shù)指標(biāo)會(huì)在除權(quán)日產(chǎn)生錯(cuò)誤信號(hào)。

舉個(gè)例子:某股票原價(jià) 20 元,10 送 10 后變成 10 元。如果不復(fù)權(quán),均線系統(tǒng)會(huì)認(rèn)為股價(jià)"暴跌"了 50%。

TickFlow 支持的復(fù)權(quán)方式

from tickflow import TickFlow

tf = TickFlow.free()

symbol = "600519.SH"

# 比例前復(fù)權(quán)(默認(rèn),推薦用于量化回測(cè))
df_forward = tf.klines.get(symbol, period="1d", count=2000, adjust="forward", as_dataframe=True)

# 差值前復(fù)權(quán)(與東方財(cái)富/同花順價(jià)格一致)
df_additive = tf.klines.get(symbol, period="1d", count=2000, adjust="forward_additive", as_dataframe=True)

# 不復(fù)權(quán)(原始成交價(jià)格)
df_none = tf.klines.get(symbol, period="1d", count=2000, adjust="none", as_dataframe=True)

# 比例后復(fù)權(quán)
df_backward = tf.klines.get(symbol, period="1d", count=2000, adjust="backward", as_dataframe=True)

# 差值后復(fù)權(quán)
df_backward_add = tf.klines.get(symbol, period="1d", count=2000, adjust="backward_additive", as_dataframe=True)

不同復(fù)權(quán)方式的對(duì)比

import pandas as pd

compare = pd.DataFrame({
    "trade_date": df_none["trade_date"],
    "不復(fù)權(quán)": df_none["close"],
    "比例前復(fù)權(quán)": df_forward["close"],
    "差值前復(fù)權(quán)": df_additive["close"],
    "比例后復(fù)權(quán)": df_backward["close"],
})

print(compare.tail(5))
print(f"\n不復(fù)權(quán)最新價(jià): {df_none['close'].iloc[-1]}")
print(f"前復(fù)權(quán)最新價(jià): {df_forward['close'].iloc[-1]}")
print(f"后復(fù)權(quán)最新價(jià): {df_backward['close'].iloc[-1]}")

選哪種復(fù)權(quán)

場(chǎng)景推薦復(fù)權(quán)方式原因
收益率計(jì)算、量化回測(cè)forward(比例前復(fù)權(quán))收益率連續(xù),不失真
與東方財(cái)富/同花順價(jià)格核對(duì)forward_additive(差值前復(fù)權(quán))價(jià)格數(shù)值一致
長(zhǎng)期投資收益對(duì)比backward(后復(fù)權(quán))能直觀看到真實(shí)漲幅
原始價(jià)格分析none(不復(fù)權(quán))看真實(shí)成交價(jià)格

關(guān)鍵原則:做回測(cè)和計(jì)算收益率一定要用前復(fù)權(quán)。

查看除權(quán)因子

tf = TickFlow(api_key="your-api-key")

factors = tf.klines.ex_factors(["600519.SH", "000001.SZ"], as_dataframe=True)

# 按標(biāo)的分組查看
for symbol in ["600519.SH", "000001.SZ"]:
    sym_factors = factors[factors["symbol"] == symbol]
    print(f"\n{symbol} 最近 5 次除權(quán):")
    print(sym_factors[["trade_date", "ex_factor"]].tail(5).to_string(index=False))

四、多股票日期對(duì)齊

問(wèn)題:日期不一致

不同股票可能因?yàn)橥E?、上市時(shí)間不同等原因,K 線日期不完全對(duì)齊。直接用 merge 會(huì)產(chǎn)生大量 NaN。

方案一:基于交易日歷對(duì)齊

import pandas as pd
from tickflow import TickFlow

tf = TickFlow.free()

symbols = ["600519.SH", "000858.SZ", "601318.SH"]
dfs = tf.klines.batch(symbols, period="1d", count=500, adjust="forward", as_dataframe=True)

# 提取所有交易日的并集
all_dates = set()
for df in dfs.values():
    all_dates.update(df["trade_date"].tolist())
all_dates = sorted(all_dates)

# 對(duì)齊到統(tǒng)一日期索引
aligned = pd.DataFrame({"trade_date": all_dates})
for symbol, df in dfs.items():
    close = df[["trade_date", "close"]].rename(columns={"close": symbol})
    aligned = aligned.merge(close, on="trade_date", how="left")

print(f"對(duì)齊后形狀: {aligned.shape}")
print(aligned.tail())

方案二:只保留共有日期

# 取交集(所有股票都有數(shù)據(jù)的日期)
common_dates = None
for df in dfs.values():
    dates = set(df["trade_date"].tolist())
    common_dates = dates if common_dates is None else common_dates & dates

common_dates = sorted(common_dates)

aligned_common = pd.DataFrame({"trade_date": common_dates})
for symbol, df in dfs.items():
    close = df[["trade_date", "close"]].rename(columns={"close": symbol})
    aligned_common = aligned_common.merge(close, on="trade_date", how="inner")

print(f"共有交易日: {len(aligned_common)}")
print(aligned_common.tail())

方案三:前向填充停牌日

停牌期間用最后一個(gè)有效價(jià)格填充:

# 在方案一的基礎(chǔ)上,前向填充
aligned_filled = aligned.copy()
for symbol in symbols:
    aligned_filled[symbol] = aligned_filled[symbol].ffill()

# 檢查填充效果
null_before = aligned[symbols].isnull().sum()
null_after = aligned_filled[symbols].isnull().sum()
print("填充前缺失值:")
print(null_before)
print("\n填充后缺失值:")
print(null_after)

五、缺失值處理

常見(jiàn)缺失值來(lái)源

  1. 停牌:股票停牌期間沒(méi)有交易數(shù)據(jù)
  2. 上市時(shí)間晚:某只股票的歷史數(shù)據(jù)比其他股票短
  3. 退市:股票退市后沒(méi)有新數(shù)據(jù)
  4. 數(shù)據(jù)異常:極少數(shù)情況下數(shù)據(jù)源缺失

檢查缺失值

import pandas as pd
from tickflow import TickFlow

tf = TickFlow.free()

symbols = ["600519.SH", "000858.SZ", "601318.SH", "000001.SZ", "600036.SH"]
dfs = tf.klines.batch(symbols, period="1d", count=1000, adjust="forward", as_dataframe=True)

print("各標(biāo)的數(shù)據(jù)量:")
for symbol, df in dfs.items():
    start = df["trade_date"].iloc[0]
    end = df["trade_date"].iloc[-1]
    print(f"  {symbol}: {len(df)} 根 K 線 ({start} ~ {end})")

處理策略

# 將多只股票收盤價(jià)合并為一個(gè) DataFrame
close_df = pd.DataFrame()
for symbol, df in dfs.items():
    s = df.set_index("trade_date")["close"]
    close_df[symbol] = s

# 方法 1:前向填充(適合停牌)
close_filled = close_df.ffill()

# 方法 2:刪除含有任何缺失值的行(保守但干凈)
close_clean = close_df.dropna()
print(f"原始行數(shù): {len(close_df)}, 清洗后: {len(close_clean)}")

# 方法 3:僅刪除缺失值過(guò)多的行(某天超過(guò)半數(shù)股票無(wú)數(shù)據(jù)才刪)
threshold = len(symbols) // 2
close_filtered = close_df.dropna(thresh=threshold)
print(f"過(guò)濾后: {len(close_filtered)}")

六、換手率計(jì)算

換手率是衡量股票活躍度的重要指標(biāo),但很多數(shù)據(jù)源不直接提供換手率,需要用成交量和流通股本自己算。

公式

換手率 = 成交量(手) × 100 / 流通股本(股)

A 股成交量單位為"手"(1 手 = 100 股),所以需要乘以 100 換算為股。

實(shí)現(xiàn)

import pandas as pd
from tickflow import TickFlow

tf = TickFlow(api_key="your-api-key")

symbol = "600000.SH"

# 獲取日 K 線
kdf = tf.klines.get(symbol, period="1d", count=2000, as_dataframe=True)

# 獲取股本數(shù)據(jù)
sdf = tf.financials.shares([symbol], as_dataframe=True)

# 轉(zhuǎn)為日期類型
kdf["date"] = pd.to_datetime(kdf["trade_date"])
sdf["date"] = pd.to_datetime(sdf["period_end"])

# 使用 merge_asof 為每根 K 線匹配當(dāng)時(shí)有效的流通股本
merged = pd.merge_asof(
    kdf.sort_values("date"),
    sdf[["date", "float_shares"]].sort_values("date"),
    on="date",
    direction="backward",
)

# 計(jì)算換手率
merged["turnover_rate"] = (merged["volume"] * 100 / merged["float_shares"]).round(4)
merged["turnover_pct"] = merged["turnover_rate"] * 100

print(merged[["trade_date", "close", "volume", "float_shares", "turnover_pct"]].tail(10))

輸出示例:

   trade_date  close  volume  float_shares  turnover_pct
   2026-04-22   9.61  685905  3.330584e+10          0.21
   2026-04-23   9.54  806247  3.330584e+10          0.24
   2026-04-24   9.45  848590  3.330584e+10          0.25
   2026-04-27   9.36  872815  3.330584e+10          0.26
   2026-04-28   9.37  594550  3.330584e+10          0.18

為什么用 merge_asof

流通股本不是每天都變。公司只在特定時(shí)間點(diǎn)(增發(fā)、回購(gòu)、限售股解禁等)更新股本數(shù)據(jù)。merge_asof 會(huì)為每個(gè)交易日匹配最近一次公布的流通股本,這是正確的做法。

批量計(jì)算換手率

symbols = ["600519.SH", "000858.SZ", "601318.SH"]

# 批量獲取 K 線
kline_dfs = tf.klines.batch(symbols, period="1d", count=60, as_dataframe=True)

# 批量獲取股本
shares_df = tf.financials.shares(symbols, as_dataframe=True)

for symbol in symbols:
    kdf = kline_dfs.get(symbol)
    sdf = shares_df[shares_df["symbol"] == symbol].copy()

    if kdf is None or len(sdf) == 0:
        continue

    kdf["date"] = pd.to_datetime(kdf["trade_date"])
    sdf["date"] = pd.to_datetime(sdf["period_end"])

    merged = pd.merge_asof(
        kdf.sort_values("date"),
        sdf[["date", "float_shares"]].sort_values("date"),
        on="date",
        direction="backward",
    )

    merged["turnover_pct"] = (merged["volume"] * 100 / merged["float_shares"] * 100).round(2)
    avg_turnover = merged["turnover_pct"].mean()
    print(f"{symbol}: 近 60 日平均換手率 {avg_turnover:.2f}%")

七、收益率計(jì)算的正確姿勢(shì)

簡(jiǎn)單收益率 vs 對(duì)數(shù)收益率

import numpy as np
from tickflow import TickFlow

tf = TickFlow.free()

df = tf.klines.get("600519.SH", period="1d", count=250, adjust="forward", as_dataframe=True)

# 簡(jiǎn)單收益率(常用)
df["simple_return"] = df["close"].pct_change()

# 對(duì)數(shù)收益率(適合統(tǒng)計(jì)分析,可加性更好)
df["log_return"] = np.log(df["close"] / df["close"].shift(1))

print(df[["trade_date", "close", "simple_return", "log_return"]].tail(10))

什么時(shí)候用哪種

場(chǎng)景推薦
策略收益計(jì)算簡(jiǎn)單收益率
統(tǒng)計(jì)分析(相關(guān)性、因子收益)對(duì)數(shù)收益率
多期累計(jì)收益簡(jiǎn)單收益率用乘法,對(duì)數(shù)收益率用加法

累計(jì)收益率

# 簡(jiǎn)單收益率的累計(jì)
df["cum_return"] = (1 + df["simple_return"]).cumprod() - 1

# 或用對(duì)數(shù)收益率
df["cum_return_log"] = np.exp(df["log_return"].cumsum()) - 1

print(f"期間總收益: {df['cum_return'].iloc[-1] * 100:.2f}%")

八、數(shù)據(jù)質(zhì)量檢查

拿到數(shù)據(jù)后,建議做一些基礎(chǔ)的數(shù)據(jù)質(zhì)量檢查:

from tickflow import TickFlow

tf = TickFlow.free()

df = tf.klines.get("600000.SH", period="1d", count=5000, adjust="forward", as_dataframe=True)

# 1. 檢查是否有重復(fù)日期
dup = df[df["trade_date"].duplicated()]
print(f"重復(fù)日期: {len(dup)} 個(gè)")

# 2. 檢查價(jià)格異常(收盤價(jià)為 0 或負(fù)數(shù))
bad_price = df[df["close"] <= 0]
print(f"異常價(jià)格: {len(bad_price)} 個(gè)")

# 3. 檢查 OHLC 邏輯(high >= low, high >= open, high >= close)
ohlc_err = df[(df["high"] < df["low"]) | (df["high"] < df["open"]) | (df["high"] < df["close"])]
print(f"OHLC 邏輯異常: {len(ohlc_err)} 個(gè)")

# 4. 檢查日期連續(xù)性(是否有異常缺失)
df["date"] = pd.to_datetime(df["trade_date"])
df["gap"] = df["date"].diff().dt.days
big_gaps = df[df["gap"] > 5]  # 超過(guò) 5 天的間隔(排除周末和短假)
print(f"超過(guò) 5 天的間隔: {len(big_gaps)} 個(gè)")
if len(big_gaps) > 0:
    print(big_gaps[["trade_date", "gap"]].head())

九、數(shù)據(jù)緩存與增量更新

每次運(yùn)行策略都重新下載全量數(shù)據(jù)會(huì)浪費(fèi)時(shí)間。可以做簡(jiǎn)單的本地緩存:

import os
import pandas as pd
from tickflow import TickFlow

CACHE_DIR = "./kline_cache"
os.makedirs(CACHE_DIR, exist_ok=True)


def get_klines_cached(tf, symbol, period="1d", count=10000):
    """帶本地緩存的 K 線獲取"""
    cache_file = os.path.join(CACHE_DIR, f"{symbol}_{period}.csv")

    if os.path.exists(cache_file):
        cached = pd.read_csv(cache_file)
        last_date = cached["trade_date"].iloc[-1]
        print(f"{symbol}: 緩存到 {last_date},增量更新...")

        # 獲取緩存之后的新數(shù)據(jù)
        import datetime
        last_ts = int(datetime.datetime.strptime(last_date, "%Y-%m-%d").timestamp() * 1000)
        new_data = tf.klines.get(
            symbol, period=period, start_time=last_ts + 86400000,
            count=count, as_dataframe=True,
        )

        if len(new_data) > 0:
            # 過(guò)濾掉可能重復(fù)的最后一天
            new_data = new_data[new_data["trade_date"] > last_date]
            combined = pd.concat([cached, new_data], ignore_index=True)
            combined.to_csv(cache_file, index=False)
            print(f"  新增 {len(new_data)} 根 K 線")
            return combined
        return cached
    else:
        print(f"{symbol}: 首次獲取...")
        df = tf.klines.get(symbol, period=period, count=count, as_dataframe=True)
        df.to_csv(cache_file, index=False)
        return df


tf = TickFlow.free()
df = get_klines_cached(tf, "600519.SH")
print(f"總計(jì) {len(df)} 根 K 線")

十、總結(jié)

量化數(shù)據(jù)處理中最容易踩坑的幾個(gè)地方:

問(wèn)題正確做法
不復(fù)權(quán)就算指標(biāo)回測(cè)必須用前復(fù)權(quán)
多股票日期不對(duì)齊merge + ffill 或取交集
缺失值導(dǎo)致計(jì)算出錯(cuò)先檢查再?zèng)Q定填充或刪除
換手率不知道怎么算volume * 100 / float_shares,用 merge_asof
收益率用錯(cuò)方法策略用簡(jiǎn)單收益率,統(tǒng)計(jì)分析用對(duì)數(shù)收益率
每次都全量下載做本地緩存 + 增量更新

這些技巧看似基礎(chǔ),但每一個(gè)處理不當(dāng)都可能導(dǎo)致回測(cè)失真或策略結(jié)論錯(cuò)誤。

TickFlow 在數(shù)據(jù)層面已經(jīng)做了很多:多種復(fù)權(quán)方式開(kāi)箱即用、批量接口高效穩(wěn)定、股本數(shù)據(jù)可直接獲取。在此基礎(chǔ)上做好數(shù)據(jù)處理,你的量化研究才有可靠的地基。

相關(guān)鏈接

數(shù)據(jù)處理不性感,但它決定了你的回測(cè)結(jié)果是真是假。

以上就是詳解Python中量化數(shù)據(jù)的處理技巧(附實(shí)戰(zhàn)代碼)的詳細(xì)內(nèi)容,更多關(guān)于Python量化數(shù)據(jù)處理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論

临猗县| 罗城| 固始县| 五莲县| 霸州市| 诸暨市| 化州市| 普定县| 东丽区| 化隆| 奉节县| 陆良县| 措美县| 布尔津县| 沈阳市| 高邑县| 巧家县| 海南省| 宁南县| 惠安县| 哈尔滨市| 阿拉善左旗| 上饶县| 满洲里市| 德令哈市| 来凤县| 金平| 清镇市| 新竹县| 台山市| 麻阳| 寿宁县| 武乡县| 綦江县| 曲周县| 屏南县| 永城市| 永仁县| 崇礼县| 呼和浩特市| 英德市|