信Day文件解析:從二進(jìn)制到CSV的跨市場(chǎng)量化數(shù)據(jù)轉(zhuǎn)換指南)
簡(jiǎn)介一份專為通達(dá)信本地?cái)?shù)據(jù)使用者準(zhǔn)備的小型轉(zhuǎn)換工具面向需要將day行情轉(zhuǎn)化為CSV的股票、基金及港股分析者。它覆蓋上證、深證與港股目錄針對(duì)股票、基金及港股在字段結(jié)構(gòu)上的差異逐一適配解析后輸出整潔的CSV可顯著減少手工二進(jìn)制解析與格式兼容成本。壓縮包共2個(gè)文件僅8KB內(nèi)含一個(gè)免安裝的stock.exe和一個(gè)C語言源文件exe適合直接下載后復(fù)制到對(duì)應(yīng)行情目錄即用源碼則方便開發(fā)者理解轉(zhuǎn)換邏輯、自行調(diào)整字段或移植到其他場(chǎng)景。目前已有6251人學(xué)習(xí)瀏覽足見其在量化備數(shù)、盤后復(fù)盤等場(chǎng)景下的實(shí)用需求。通過這版小工具用戶既能快速完成通達(dá)信day文件到通用表格數(shù)據(jù)的批量轉(zhuǎn)換也能從C源碼中掌握不同市場(chǎng)、不同品種的底層格式差異為后續(xù)擴(kuò)展或獨(dú)立開發(fā)打下基礎(chǔ)。 打開通達(dá)信的數(shù)據(jù)目錄你會(huì)看到一堆后綴為.day的文件。很多人在本地做量化回測(cè)、遷移歷史數(shù)據(jù)、或者把通達(dá)信數(shù)據(jù)喂給別的軟件時(shí)第一道坎就是怎么把這堆二進(jìn)制文件轉(zhuǎn)成自己能用的格式。這個(gè)需求看似基礎(chǔ)但真做起來牽扯的細(xì)節(jié)不少尤其是當(dāng)你面對(duì)的不只是A股還有港股、場(chǎng)內(nèi)基金這些品種時(shí)很多通用腳本會(huì)直接翻車。我自己在處理這批文件時(shí)踩過不少坑也把格式和轉(zhuǎn)換流程完整摸了一遍。這篇文章就從格式結(jié)構(gòu)、轉(zhuǎn)換方案、實(shí)操代碼到問題排查把通達(dá)信day文件轉(zhuǎn)換這件事一次說透。1. 項(xiàng)目概述與整體設(shè)計(jì)思路1.1 核心需求解析day文件也叫日線數(shù)據(jù)文件是通達(dá)信本地存儲(chǔ)K線數(shù)據(jù)的主要格式之一。它記錄了一只股票或基金從上市以來的每一個(gè)交易日開盤價(jià)、最高價(jià)、最低價(jià)、收盤價(jià)、成交量、成交額等核心信息。對(duì)于做策略回測(cè)、數(shù)據(jù)遷移、或是多平臺(tái)數(shù)據(jù)同步的人來說這些文件是最原始也是最可靠的數(shù)據(jù)源。但問題在于通達(dá)信的day文件是二進(jìn)制格式不是我們常見的CSV或者JSON直接用文本編輯器打開全是亂碼。而且不同市場(chǎng)滬深A(yù)股、港股、基金的數(shù)據(jù)字段含義和處理細(xì)節(jié)有差異一個(gè)不留神就會(huì)解析出錯(cuò)誤的價(jià)格和成交量。這個(gè)項(xiàng)目的核心目標(biāo)就很清晰寫一個(gè)健壯的轉(zhuǎn)換程序把通達(dá)信的.day二進(jìn)制文件解析成通用格式通常是CSV同時(shí)兼容A股、港股和基金三類品種并且處理掉成交量單位、價(jià)格精度、復(fù)權(quán)標(biāo)記這些容易踩坑的細(xì)節(jié)。1.2 為什么需要自行轉(zhuǎn)換而非導(dǎo)出有人會(huì)問通達(dá)信軟件本身不就能導(dǎo)出數(shù)據(jù)嗎確實(shí)能但局限性很大。通達(dá)信自帶的“數(shù)據(jù)導(dǎo)出”功能在菜單里操作一次只能導(dǎo)出一只股票而且導(dǎo)出的是當(dāng)前界面顯示的周期。如果你有幾百只股票的歷史日線要導(dǎo)出手動(dòng)操作會(huì)讓人崩潰。更關(guān)鍵的是導(dǎo)出的數(shù)據(jù)往往經(jīng)過了軟件層面的處理比如復(fù)權(quán)計(jì)算、格式化精度你拿到的不是最原始的底層數(shù)據(jù)。而直接解析.day文件有幾大優(yōu)勢(shì)一次腳本批量處理所有股票拿到的是最底層、未經(jīng)過多處理的原始數(shù)據(jù)不依賴通達(dá)信軟件是否運(yùn)行可以精確控制輸出格式和字段這個(gè)方案本質(zhì)上是一個(gè)“二進(jìn)制解析格式轉(zhuǎn)換”的項(xiàng)目核心難點(diǎn)不在于寫代碼本身而在于理解通達(dá)信的數(shù)據(jù)存儲(chǔ)規(guī)則。下面深入拆解。2. day文件二進(jìn)制格式深入解析2.1 day文件基礎(chǔ)結(jié)構(gòu)通達(dá)信day文件的格式是公開的秘密網(wǎng)上流傳的資料不少我實(shí)測(cè)核對(duì)后確認(rèn)結(jié)構(gòu)如下。每一條記錄固定占用32個(gè)字節(jié)按順序排列偏移量長(zhǎng)度字節(jié)字段名數(shù)據(jù)類型說明04日期int32實(shí)際日期 該值 1900年1月1日的偏移天數(shù)44開盤價(jià)int32實(shí)際價(jià)格 該值 / 10084最高價(jià)int32實(shí)際價(jià)格 該值 / 100124最低價(jià)int32實(shí)際價(jià)格 該值 / 100164收盤價(jià)int32實(shí)際價(jià)格 該值 / 100204成交額float32單位元244成交量int32單位股A股284保留字段int32通常為0我最初在解析時(shí)有個(gè)誤區(qū)以為日期字段是Unix時(shí)間戳直接轉(zhuǎn)就出錯(cuò)了。后來才確認(rèn)它存的是一個(gè)相對(duì)天數(shù)基準(zhǔn)日是1900年1月1日。所以要將它轉(zhuǎn)換成“YYYY-MM-DD”格式需要做一步換算。比如文件中日期字段的十進(jìn)制值是43831那么實(shí)際日期就是1900-01-01加上43831天計(jì)算出來是2020-01-02。注意這個(gè)計(jì)算不是直接用Excel的日期序列號(hào)規(guī)則而是以1900年1月1日為基準(zhǔn)的累計(jì)天數(shù)。2.2 不同市場(chǎng)的字段差異如果只用上面這套結(jié)構(gòu)去解析所有文件很快就會(huì)發(fā)現(xiàn)不對(duì)勁。因?yàn)楦酃珊突鸬膁ay文件在某些細(xì)節(jié)上跟A股不一樣。先說成交量單位。A股和場(chǎng)內(nèi)基金的成交量day文件里記錄的是“股”或“份”這一點(diǎn)比較直觀。但港股不一樣港股交易單位是按“股”記錄的但很多股票的面值、每手股數(shù)差異很大例如騰訊控股每手100股而有些港股每手是1000股甚至2000股。在.day文件中港股的成交量字段記錄的是“股”數(shù)這一點(diǎn)跟A股沒有本質(zhì)區(qū)別。不過真正要小心的是價(jià)格單位。A股價(jià)格精確到分所以存的是“元”乘以100后的整數(shù)。港股因?yàn)閮r(jià)格跨度大有些股票股價(jià)上千元比如騰訊曾到過700多港元有些仙股只有幾分錢但day文件的規(guī)則同樣是乘以100后存整數(shù)。這里有個(gè)風(fēng)險(xiǎn)一旦某個(gè)股票價(jià)格超過一定范圍或者有3位小數(shù)的情況比如某些港股報(bào)價(jià)是0.125港元直接除以100能得到正確結(jié)果但如果遇到5位小數(shù)的港股比如部分渦輪和牛熊證單純除以100就不夠了這類數(shù)據(jù)在普通股票日線里碰不到不需要過度擔(dān)心。但對(duì)于做港股衍生品研究的人來說day文件可能不一定覆蓋到還是要提醒一句。再說份額和單位的問題。不少人在轉(zhuǎn)換基金文件時(shí)發(fā)現(xiàn)成交量數(shù)字大得離譜以為是解析錯(cuò)了。其實(shí)場(chǎng)內(nèi)基金的成交量字段單位和A股股票一樣是“份”比如某ETF一天成交了1億份字段值就是100000000不需要額外處理。但如果你希望輸出到其他軟件時(shí)是“手”為單位那就需要在腳本里做一次除以100的換算。2.3 復(fù)權(quán)標(biāo)記的真相關(guān)于day文件是否包含復(fù)權(quán)信息這是一個(gè)高頻疑問。我的實(shí)測(cè)結(jié)論是標(biāo)準(zhǔn)的.day文件里沒有復(fù)權(quán)因子它存的是不復(fù)權(quán)的原始價(jià)格。通達(dá)信界面默認(rèn)顯示的前復(fù)權(quán)、后復(fù)權(quán)價(jià)格是軟件在讀取day文件之后實(shí)時(shí)計(jì)算的計(jì)算依據(jù)是另一個(gè)文件通常叫g(shù)bbq即股本變遷文件里的除權(quán)除息記錄。這意味著你用腳本直接解析day文件拿到的價(jià)格如果期間有分紅送股看到的價(jià)格是不連續(xù)的跳變。比如某股票10轉(zhuǎn)10之前價(jià)格100元轉(zhuǎn)增后開盤價(jià)可能是50元day文件里記錄的就是這個(gè)50元而不會(huì)自動(dòng)處理成復(fù)權(quán)后的連續(xù)價(jià)格。所以如果你的量化策略需要復(fù)權(quán)數(shù)據(jù)在解析day文件之后還需要額外加載除權(quán)除息數(shù)據(jù)做復(fù)權(quán)計(jì)算。這個(gè)點(diǎn)后面會(huì)在常見問題部分詳細(xì)說。在網(wǎng)上搜索“通達(dá)信股本變遷文件(gbbq)解密”也能看到很多人在研究這個(gè)正因?yàn)樗菑?fù)權(quán)計(jì)算的關(guān)鍵。3. 轉(zhuǎn)換方案選型與工具準(zhǔn)備3.1 方案對(duì)比Python腳本、通達(dá)信導(dǎo)出、第三方工具做這個(gè)轉(zhuǎn)換本質(zhì)上有三條路可以走方案優(yōu)點(diǎn)缺點(diǎn)適用場(chǎng)景Python自寫解析腳本靈活、支持批量、可定制輸出格式需要會(huì)寫代碼主力方案推薦通達(dá)信自帶導(dǎo)出簡(jiǎn)單、無需編程一次只能導(dǎo)一只、效率低臨時(shí)導(dǎo)一兩只用第三方轉(zhuǎn)換工具開箱即用安全風(fēng)險(xiǎn)不明、格式不可控不推薦有腳本風(fēng)險(xiǎn)我自己最終采用的是Python方案。原因很簡(jiǎn)單第一我需要批量處理整個(gè)股票池幾千個(gè)文件第二我需要輸出成特定格式供其他程序讀取第三后續(xù)要做復(fù)權(quán)處理自己寫腳本可以無縫銜接。3.2 Python環(huán)境準(zhǔn)備這一步?jīng)]有任何特殊之處核心依賴只有兩個(gè)Python 3.6任何較新版本都可以pandas用于CSV輸出和處理數(shù)據(jù)表如果你不愿意裝pandas純用csv模塊也可以但pandas會(huì)讓后續(xù)的過濾、排序、去重操作高效很多。pip install pandas然后就沒了。解析二進(jìn)制文件用的是Python內(nèi)置的struct模塊不需要額外安裝。3.3 數(shù)據(jù)文件準(zhǔn)備在動(dòng)手之前先明確你要解析哪些文件。通達(dá)信的安裝路徑通常是類似C:\new_tdx\vipdoc\sh\lday\ C:\new_tdx\vipdoc\sz\lday\ C:\new_tdx\vipdoc\hk\lday\其中sh目錄放上交所股票日線sz目錄放深交所股票日線hk目錄放港股日線文件命名規(guī)則是市場(chǎng)代碼證券代碼.day。例如sh600000.day是浦發(fā)銀行sz000001.day是平安銀行hk00700.day是騰訊控股?;鸷凸善钡拇娣盼恢靡粯游募惨粯颖热鐂z159915.day是易方達(dá)創(chuàng)業(yè)板ETF。需要提醒一下如果你在軟件里看不到港股數(shù)據(jù)本地可能沒有hk目錄或者目錄為空。這個(gè)跟通達(dá)信版本和數(shù)據(jù)下載范圍有關(guān)不是你的腳本有問題。4. 實(shí)操過程與核心代碼實(shí)現(xiàn)4.1 單文件解析核心函數(shù)先把最核心的解析函數(shù)寫出來。這個(gè)函數(shù)負(fù)責(zé)把.day文件二進(jìn)制內(nèi)容讀出來套用struct的解包格式逐條解析成可讀的數(shù)據(jù)行。import struct import pandas as pd from datetime import datetime, timedelta def parse_day_file(filepath): # 32字節(jié)一條記錄 record_size 32 results [] with open(filepath, rb) as f: data f.read() # 文件總長(zhǎng)度必須是32的倍數(shù)否則文件可能損壞或不是day文件 record_count len(data) // record_size for i in range(record_count): offset i * record_size record data[offset:offset record_size] date_raw, open_raw, high_raw, low_raw, close_raw, amount_raw, volume_raw, _ struct.unpack(IIIIIIfI, record) # 注意成交額是float成交量是int順序不要搞反 # 日期換算 date datetime(1900, 1, 1) timedelta(daysdate_raw) date_str date.strftime(%Y-%m-%d) # 價(jià)格換算 open_price open_raw / 100.0 high_price high_raw / 100.0 low_price low_raw / 100.0 close_price close_raw / 100.0 results.append({ date: date_str, open: open_price, high: high_price, low: low_price, close: close_price, amount: amount_raw, # 成交額單位元 volume: volume_raw, # 成交量 }) return pd.DataFrame(results)這段代碼就完成了最核心的解析工作。簡(jiǎn)單測(cè)試一下df parse_day_file(sh600000.day) print(df.head())如果解析正確你會(huì)看到類似這樣的輸出date open high low close amount volume 0 1999-11-10 29.50 29.80 29.01 29.18 1.191912e09 10000000 1 1999-11-11 29.00 29.30 28.80 29.10 1.000000e09 9800000這里有一個(gè)先易后難的經(jīng)驗(yàn)第一次解析時(shí)先用一只最近上市的新股測(cè)試確保數(shù)據(jù)量少、容易核對(duì)。如果新股的解析結(jié)果跟軟件顯示一致說明格式肯定沒錯(cuò)再去跑老股票。4.2 批量轉(zhuǎn)換與跨市場(chǎng)兼容單個(gè)文件解析沒問題之后批量轉(zhuǎn)換就是水到渠成的事。但這里需要處理一個(gè)問題港股成交量單位與A股可能不同至少要做到不搞錯(cuò)。A股和基金的成交量單位是股/份港股也是股但如果你的下游系統(tǒng)要求統(tǒng)一為手就按100股/手換算。不過我建議默認(rèn)不做換算保留最原始的數(shù)據(jù)把換算留給下游處理。寫一個(gè)批量轉(zhuǎn)換函數(shù)import os import glob def batch_convert(source_dir, output_dir, market_typesh): os.makedirs(output_dir, exist_okTrue) # 匹配所有.day文件 pattern os.path.join(source_dir, *.day) day_files glob.glob(pattern) for filepath in day_files: filename os.path.basename(filepath) # 例如 sh600000.day code filename.split(.)[0] # 例如 sh600000 try: df parse_day_file(filepath) if df.empty: continue # 輸出CSV以代碼命名 output_path os.path.join(output_dir, f{code}.csv) df.to_csv(output_path, indexFalse, float_format%.3f) print(f轉(zhuǎn)換完成: {code} - {output_path}共{len(df)}條記錄) except Exception as e: print(f解析失敗: {filepath}錯(cuò)誤: {e})這個(gè)腳本對(duì)sh、sz、hk目錄均適用只要傳入對(duì)應(yīng)的source_dir即可。唯一需要留意的點(diǎn)是不要在循環(huán)里用print輸出太多日志幾千個(gè)文件全打印會(huì)拖慢速度。如果文件量很大建議最后統(tǒng)一匯總生成一個(gè)轉(zhuǎn)換報(bào)告。4.3 輸出格式設(shè)計(jì)與數(shù)據(jù)校驗(yàn)輸出CSV只是最基礎(chǔ)的做法。實(shí)際項(xiàng)目中我通常會(huì)在CSV基礎(chǔ)上增加幾個(gè)字段方便下游使用。擴(kuò)展后的輸出包括date交易日期code證券代碼open, high, low, close四價(jià)格volume成交量A股/基金單位是股/份港股是股amount成交額元pct_change日漲跌幅可選日漲跌幅可以直接在轉(zhuǎn)換時(shí)算出來df[pct_change] df[close].pct_change() * 100為什么建議加這個(gè)字段因?yàn)楹芏嗔炕蚣苋雲(yún)⒓虞d數(shù)據(jù)時(shí)都要求有漲跌幅你在轉(zhuǎn)換階段算好后面省一次遍歷。關(guān)于校驗(yàn)有一個(gè)非常實(shí)用的小技巧檢查解析出的數(shù)據(jù)末尾日期是否和通達(dá)信軟件最新交易日一致。如果不一致說明day文件沒更新需要先在通達(dá)信里下載完整日線數(shù)據(jù)。另外價(jià)格字段的數(shù)值范圍可以用來判斷單位換算是否正確——如果出現(xiàn)某只股票收盤價(jià)超過10000元而它明明不可能是這個(gè)價(jià)那大概率是文件沒找對(duì)或解析字節(jié)順序錯(cuò)了。5. 常見問題與排查技巧實(shí)錄5.1 日期相差8小時(shí)或完全錯(cuò)亂這個(gè)問題非常典型。當(dāng)你用datetime.fromtimestamp()直接把日期字段當(dāng)Unix時(shí)間戳解析時(shí)得到的結(jié)果會(huì)時(shí)區(qū)錯(cuò)亂或者日期明顯不對(duì)。解決方案就是回到格式本身去理解不要猜。日期字段是相對(duì)1900年1月1日的天數(shù)偏移不是Unix時(shí)間戳。正確做法是date datetime(1900, 1, 1) timedelta(daysdate_raw)如果這樣還錯(cuò)檢查一下你讀取文件時(shí)是否用了二進(jìn)制模式。Windows下如果你用文本模式打開.day文件會(huì)被做換行符轉(zhuǎn)換導(dǎo)致字節(jié)錯(cuò)位解析結(jié)果完全亂掉。5.2 成交量單位是股還是手這個(gè)問題我在前面提過但值得再強(qiáng)調(diào)。不少人在轉(zhuǎn)換時(shí)習(xí)慣性把成交量除以100理由是“交易軟件里顯示的是手”。但這個(gè)觀念是錯(cuò)誤的通達(dá)信day文件里存的就是股精確到1股A股不是手。你直接除以100反而把數(shù)據(jù)搞小了。什么時(shí)候需要除以100只有當(dāng)你確認(rèn)下游系統(tǒng)要求以“手”為單位時(shí)才需要。而且港股的“每手股數(shù)”因股票而異有些是100有些是500有些是2000絕對(duì)不能用統(tǒng)一的100來除。我覺得最穩(wěn)妥的做法是day文件解析后保持原始股數(shù)涉及單位換算的交給下游系統(tǒng)處理。5.3 復(fù)權(quán)數(shù)據(jù)怎么處理前面說過day文件里是裸價(jià)格不含復(fù)權(quán)因子。但有很多人拿到數(shù)據(jù)后發(fā)現(xiàn)某天的價(jià)格跳變巨大就來問是不是解析錯(cuò)了。這里要分兩種情況如果跳變正好發(fā)生在某次分紅送股除權(quán)日那說明解析完全正確。那天的價(jià)格本身就出現(xiàn)了臺(tái)階是市場(chǎng)行為不是程序bug。如果你確實(shí)需要復(fù)權(quán)后的連續(xù)價(jià)格那必須去解析gbbq文件股本變遷文件。這是一個(gè)代價(jià)更高的解析任務(wù)gbbq文件的格式比day文件復(fù)雜得多涉及送股比例、配股比例、分紅金額等多個(gè)字段。具體做法是先加載gbbq按日期排序用前復(fù)權(quán)或后復(fù)權(quán)公式一行一行修正價(jià)格。網(wǎng)上關(guān)于“通達(dá)信股本變遷文件(gbbq)解密方法”的討論也很多核心原理都一樣就是要讀懂這個(gè)文件的二進(jìn)制結(jié)構(gòu)。5.4 遇到加密或損壞文件正常情況下day文件沒有加密。如果你用上面的代碼解析某些文件時(shí)報(bào)錯(cuò)或者發(fā)現(xiàn)文件長(zhǎng)度不是32的倍數(shù)有幾個(gè)可能文件下載不完整。在通達(dá)信里重新下載一次日線數(shù)據(jù)。文件根本不是day文件比如你把day和其他的文件弄混了。通達(dá)信版本升級(jí)導(dǎo)致格式變化。目前主流版本V7.x實(shí)測(cè)都能用這套結(jié)構(gòu)解析但不排除未來版本調(diào)整的可能。遇到解析異常我的建議是別急著改代碼先用16進(jìn)制編輯器打開文件看看開頭32字節(jié)是否符合預(yù)期結(jié)構(gòu)。這個(gè)排查方法最直接。5.5 參數(shù)速查表參數(shù)A股港股場(chǎng)內(nèi)基金成交量單位股股份價(jià)格精度0.01元0.01港元0.001元day文件目錄vipdoc/sh/ldayvipdoc/hk/ldayvipdoc/sz/lday文件命名sh600000.dayhk00700.daysz159915.day日期基準(zhǔn)1900-01-01同左同左6. 項(xiàng)目經(jīng)驗(yàn)總結(jié)這套轉(zhuǎn)換腳本我現(xiàn)在還在持續(xù)使用不管是做策略回測(cè)、跨平臺(tái)數(shù)據(jù)同步還是生成自定義格式的歷史數(shù)據(jù)它都是最底層的那個(gè)工具?;叵胝麄€(gè)開發(fā)過程有幾點(diǎn)經(jīng)驗(yàn)值得分享第一二進(jìn)制解析類項(xiàng)目格式理解大于一切。網(wǎng)上能搜到很多版本的day格式說明但搜到之后一定要自己做一次驗(yàn)證。我的核對(duì)方法是拿一只剛上市不久的股票用通達(dá)信軟件看某一天的數(shù)據(jù)再對(duì)比腳本解析出來的數(shù)據(jù)完全一致才算通過。第二兼容性設(shè)計(jì)考慮得越早越好。如果只是轉(zhuǎn)換上證A股代碼可以寫得非常簡(jiǎn)潔但一旦加上港股和基金就必須在單位換算、價(jià)格精度上做分區(qū)處理。我的實(shí)現(xiàn)方式是在批量轉(zhuǎn)換函數(shù)里增加market_type參數(shù)針對(duì)不同市場(chǎng)的特性做差異化處理而不是把邏輯寫死。第三數(shù)據(jù)安全要做好。雖然轉(zhuǎn)換腳本只是讀取文件不修改原始文件但我還是建議在操作前把整個(gè)vipdoc目錄復(fù)制一份備份。數(shù)據(jù)無價(jià)尤其是在做歷史回測(cè)時(shí)原始數(shù)據(jù)的完整性直接影響策略驗(yàn)證的可靠性。如果后續(xù)打算繼續(xù)深入可以在這個(gè)項(xiàng)目基礎(chǔ)上增加兩個(gè)擴(kuò)展方向一是接入gbbq文件的解析實(shí)現(xiàn)真正的復(fù)權(quán)計(jì)算二是打通“通達(dá)信data文件轉(zhuǎn)CSV”的通道把分鐘級(jí)數(shù)據(jù)也納入轉(zhuǎn)換體系。這些我后面實(shí)際跑通了再單獨(dú)寫一篇文章展開。本文還有配套的精品資源點(diǎn)擊獲取