ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

股小仙(一):数据底座——两种流水格式、公式陷阱与查询起点

股小仙(一):数据底座——两种流水格式、公式陷阱与查询起点 股小仙系列第一篇。分析系统的地基是数据而个人投资者的数据源只有两处券商/平台导出的文件和平台接口的分页拉取。这篇讲数据底座的三个实战问题一份文件两种格式的兼容读取、Excel 导出的...公式陷阱、以及查历史流水该从哪天开始这个看似简单实则坑深的问题。一、一份文件两种格式券商导出的对账单看起来是个 Excel实际到手可能是两种东西真 Excelxlsx 二进制——pandas 一行读完披着 .xls 后缀的 GBK 文本tab 分隔——read_excel直接炸。兼容读取的写法try:dfpd.read_excel(file_path)if成交日期notindf.columns:raiseValueError(not excel)exceptException:withopen(file_path,rb)asf:textf.read().decode(gbk)cleaned[re.sub(r([^]*),r\1,line)forlineintext.strip().split(\n)]dfpd.read_csv(io.StringIO(\n.join(cleaned)),sep\t,dtypestr)三个细节按内容判断而不是按后缀先读、验列名成交日期不在就认定不是 Excel。后缀会说谎列名不会GBK 解码又是它A4 篇行情接口、B3 篇流水入库GBK 三连dtypestr先全按字符串读代码列000001按 Python 默认规则会变成1整数前导零没了股票代码就废了。先 str 后逐列转数值顺序不能反。二、...公式陷阱注意清洗正则cleaned[re.sub(r([^]*),r\1,line)forlineinlines]它处理的是 Excel 导出文本时的一个阴招日期和代码字段会被包成2026-01-15这种公式形式本意是防 Excel 自动转换格式。不剥掉这层壳读进来的日期是字符串2026-01-15解析必炸。正则([^]*)→\1把公式壳剥掉留下裸值。这个坑的教训是导出文件里的每个字段都可能穿着给 Excel 看的衣裳给程序读之前要先脱。常见的三件衣裳公式壳、前导零杀手数字格式、千分位逗号金额列1,234.56直接to_numeric会得到 NaN需要先删逗号——本例金额列没这个问题但换一家券商就可能有。三、datetime 的拼装与排序df[datetime]pd.to_datetime(df[成交日期].astype(str) df[成交时间],errorscoerce)dfdf.sort_values(datetime).reset_index(dropTrue)日期列和时间列拼成 datetime 再全局排序——这步是后面匹配引擎A2 篇的前置条件匹配的时间序约束依赖流水的严格时序。errorscoerce让坏行变 NaT 而不是炸掉配合后续筛选把脏数据挡在引擎外面。A2 篇讲过秒级时间戳为零的坑这里补一句它的源头成交时间只到分同一分钟内的顺序只能靠导出文件的原始顺序——而上面reset_index后原始顺序就藏在 index 里这就是券商导出通常是时序的这个假设的全部依靠。四、接口侧的分页拉取平台接口的历史流水是分页的拉全量的循环结构page,count1,30whileTrue:params{page:str(page),count:str(count),...,query_list:json.dumps([{code:code,market:market}])}ex_dataapi_post(fund_key,/pc/account/v2/get_money_history,params)recordsex_data.get(list,[])max_pageex_data.get(max_page,1)ifnotrecords:breakall_records.extend(records)ifpagemax_page:breakpage1all_records.sort(keylambdax:x.get(entry_date,))两个防御性设计双终止条件空列表提前退 到达 max_page 退出——只信 max_page 的话服务端分页数算错的瞬间就是死循环拉完重排序按entry_date——不信任接口的分页顺序时序自己在客户端保证。加sort_type参数接口也支持但客户端再排一次是免费的保险。五、最微妙的问题从哪天开始查拉历史流水不能无脑从开户日查到今天——接口按时间范围查询范围越大越慢而且很多平台对久远历史有限制。看这段决策代码defdetermine_start_date(position,cleared_list):codeposition[code]hold_daysint(position.get(hold_days,0))stock_cleared[cforcincleared_listifc[code]code]ifstock_cleared:last_closemax(c[close_date]forcinstock_cleared)startlast_closetimedelta(days1)# 有清仓记录清仓次日开始returnstart,Trueelse:lookbackmax(hold_days*3,365)# 无清仓回溯 max(持有天数×3, 一年)returnnow-timedelta(dayslookback),False逻辑分两支依据是这只股票有没有清仓记录有清仓记录当前持仓是清仓后重新建仓的流水从最近一次清仓的次日查起——之前的旧周期流水与当前持仓无关拉了也是噪音无清仓记录当前仓位可能从很远以前一路持有回溯max(持有天数 × 3, 365)天。为什么是持有天数的三倍因为持仓 N 天是当前这笔的账龄而这只股票可能经历过多轮买卖——3 倍是对此前最多再有一两轮完整周期的经验估计365 天下兜底保证至少覆盖一年。这个函数返回的第二个值bool标记是否找到了精确起点调用方据此决定要不要在日志里提示流水可能不完整——启发式规则要把自己的启发式性质暴露给下游这条原则和 A4 篇行情降级的零值标记一脉相承。六、两条数据通道一套数据底座文件导出券商对账单和接口拉取平台历史各有所长文件全而慢人工导出、接口快而可能有范围限制。本系统的取舍是接口为主自动化文件兜底校验——两路数据落到同一张trade_record表B3 篇的幂等键天然支持双路写入去重互为对账依据。数据底座的完成度直接决定上层分析的天花板A2 的匹配、A3 的健康度、A4 的估值全部站在这一篇的三个决定上——按内容识别格式、剥掉 Excel 的衣裳、想清楚从哪天开始查。系列导航A1本篇数据底座——两种格式、公式陷阱与查询起点A2 买卖匹配引擎两轮匹配、贪心排序与幂等键的坑A3 网格层健康检测与波段加仓计划A4 免费行情接入GBK、波浪号与 68 个字段
返回列表