如果你写过一阵子日线因子,可能和我当初一样有个很深的困惑:日线数据里翻来覆去就那几样,量价、均线、波动率,做完之后同质化严重,换到不同市场、不同股票池,IC越来越薄,策略越来越卷。我当时把目光转向逐笔成交数据之后,才真正体会到什么叫“数据里有矿,但矿里也有陷阱”。
逐笔数据记录的是每一笔真实成交的价格、数量、方向,它是市场最底层的颗粒度。基于它构建的高频因子,比如订单流不平衡、主动买卖压力、微观结构错价因子,能够捕捉到日线级别完全看不到的信息。这篇文章我会从逐笔数据的口径讲起,一路走到预处理、因子构建、多股票回测的完整流程,过程中会给出可复现的Python代码和我在实操中踩过的坑。适合已经做过一段时间日频或多因子研究、想往高频微观结构方向深入的研究员和量化开发。
1. 逐笔高频数据:先搞懂要处理的是什么东西
1.1 逐笔成交和逐笔委托的区别,各自能挖什么
很多人一上来就写代码,结果连数据长什么样都没搞清楚。逐笔数据大体分两类,一类是逐笔成交,也就是每一笔真实成交的时间、价格、数量、主买主卖方向;另一类是逐笔委托,是把每一条挂单、撤单、成交回报都记录下来,数据量比逐笔成交大一个量级。
逐笔成交能做的事情已经很多了。最经典的是订单流不平衡类因子,核心逻辑是:主动买盘强的时候,短期价格容易被推高;主动卖盘强的时候,价格容易被压低。这个方向判断依赖数据源给的主买主卖字段,通常是B和S标记,有些数据源还会给到撤单标记、成交编号,这些字段对我们做去重和排序很重要。
逐笔委托则更细,它连挂单在什么价位、挂了多久、是否被撤掉都能看到。基于逐笔委托可以做订单簿失衡因子、撤单率因子、大单挂撤行为因子。但逐笔委托数据处理成本高,对很多个人研究者来说,先把逐笔成交做好已经能跑赢大部分只看日线的策略了。
我给的实操建议是:初学者或数据资源有限的人,先从逐笔成交入手;等你想进一步区分“真实成交推动”和“挂单诱导”的时候,再引入逐笔委托。两者在字段结构、清洗逻辑、因子含义上都有本质差异,不要混在一起做。
1.2 数据口径选择对因子研究的影响
做逐笔因子之前,有一个绕不开的问题:你拿到的时间戳是交易所原始时间还是数据源加工后的时间?是本地时区还是UTC?成交价格是原始价格还是复权价格?这些口径一旦不统一,后面聚合出来的分钟bar就是错的,因子和未来收益之间的关系也会被噪声淹没。
举个例子,我早期处理某数据源拉下来的逐笔数据,ts字段是字符串类型,里面带时区偏移,我直接pd.to_datetime之后没做时区统一,结果聚合出来的分钟成交量出现大量“高低脚”,有些分钟bar甚至出现0成交。后来排查了半天,才发现是时区把9:30的数据偏移到了9:31。
逐笔数据还有一个关键问题:它是非均匀时间序列。普通日线数据每天一根K线,时间上天然对齐,但逐笔数据在开盘和收盘附近密集,中午休息前稀疏,如果直接按固定窗口聚合,不处理缺失分钟,后面做滚动窗口和因子平滑时会出现窗口内数据量不一致的情况,因子值也就会忽大忽小。
我常用的统一策略是:
- 把时间戳统一成交易所本地时间,并且用datetime类型存储。
- 过滤掉集合竞价阶段的数据或单独处理,避免开盘那一笔巨量成交污染分钟聚合。
- 处理停牌股票,没有成交的分钟用前值填充或直接标记为缺失。
- 对价格字段做异常值过滤,比如价格跳变为0、价格高于当日涨停价一定比例等情况。
口径问题看似基础,实际上决定了后续所有因子的稳定性和可解释性。这块偷懒,后面回测再好也是空中楼阁。
2. 环境准备与逐笔数据预处理
2.1 环境、库和数据字段准备
开始之前先把环境准备好。Python版本建议3.9以上,我本地用的是3.10。核心库就是pandas、numpy,回测阶段如果要画净值曲线,matplotlib和seaborn也建议装上。对于大规模逐笔数据,numba可以在滚动计算时帮你节省大量时间,后面因子构建部分我会写一个例子。
逐笔成交数据至少应该包含以下字段:
| 字段名 | 含义 | 示例 |
|---|---|---|
| ts | 成交时间戳 | 2024-05-16 09:45:03.128 |
| symbol | 股票代码 | 600519.SH |
| price | 成交价格 | 1715.50 |
| qty | 成交数量 | 200 |
| side | 主买主卖方向 | B/S |
| trade_index | 成交编号 | 123456789 |
实际从数据源API拉取时,字段名称可能不同,但核心信息离不开这些。拿到数据后,我建议先做一次抽样可视化,把某个股票某天的逐笔成交按时间画出来,看看数据是否连续、方向字段是否有缺失、价格是否有明显跳变。这一步只需要几分钟,但能帮你建立对数据质量的直观感受。
数据量方面,一只成交活跃的股票一天大约产生几万到几十万条逐笔成交记录,全市场几千只股票一天的逐笔数据就可能上亿条。这个量级用pandas直接读进内存,很容易把内存撑爆。我的做法是分股票、分交易日读取,处理完一个股票就释放内存。
2.2 聚合口径、集合竞价与停牌跳空的坑
逐笔数据转分钟bar是整个流程里最基础也最容易出错的一步。你需要决定分钟bar的起止时间是否包含集合竞价,是否要排除开盘前和收盘后的数据,以及如何处理没有成交的分钟。
A股集合竞价阶段会产生一笔或几笔成交量很大的单子,这笔单子的价格是开盘价,但它不代表连续竞价阶段真实的买卖压力。如果把它当成普通逐笔成交去聚合,开盘第一根分钟bar的量会异常放大,订单流不平衡因子在开盘时会产生巨大的伪信号。
我建议的做法是:集合竞价数据单独保留,用于计算开盘跳空因子;在聚合连续竞价分钟bar时,过滤掉9:25之前的成交,只保留9:30及之后的数据。部分数据源会提供period标记字段来区分集合竞价和连续竞价,有的话直接用。
停牌和跳空是另一个场景。股票停牌一天后复牌,开盘价可能直接跳空好几个点,期间没有成交记录。如果直接按时间窗口聚合,开盘那根bar的量价关系和前一个交易日没有任何连续性,订单流类因子会出现极端值。处理方式是在聚合后的分钟数据里,对没有成交的分钟用NaN标记,不强行填充,让后面的因子计算自己处理缺失。
价格校验也很关键。比如某只股票突然出现一笔价格为0的成交记录,或者价格低于1分钱,这类脏数据如果不过滤,在计算收益率和买卖价差时会产生天文数字般的异常值。我一般用两个条件过滤:价格大于0,且价格相对于当日涨跌停价在合理范围内。这招能挡掉大部分数据源偶发返回的脏数据。
2.3 数据质量校验与去重排序
数据校验不是可有可无的步骤。很多逐笔数据源会把同一笔成交重复推送,或者因为网络重传导致ts和trade_index重复。如果不做去重,聚合出来的成交量会虚高,因子值也会失真。
我实际用过的去重方案是:以symbol、ts、trade_index为唯一键,先排序再drop_duplicates。如果数据源没有trade_index,就用symbol、ts、price、qty四个字段联合去重,虽然严格来说可能有误删,但在实际处理中这个概率很低。
完整的数据质量校验流程我总结成下面几步:
- 检查时间戳字段是否可解析为datetime,是否包含NaT。
- 检查price和qty是否为正数,是否为NaN。
- 检查side字段是否只包含B/S两种取值,出现其他值就要警惕。
- 检查一天内数据的最大时间戳是否在收盘之后,如果有明显超出交易时段的数据,多半是数据源异常。
- 检查聚合后分钟bar数量,正常股票一天大约240根1分钟bar,如果数量偏差过大说明有数据缺失。
这些校验写成函数,每次拉完数据跑一遍,能省下大量后面排查问题的时间。数据处理这件事,前期做得越扎实,后期因子和回测出问题的概率越低。
3. 高频因子构建核心:订单流不平衡及其变体
3.1 为什么要用订单流不平衡而不是简单的量比
日线级别的量比只能告诉你今天成交量比过去几天放大还是缩小,但它无法告诉你这些成交量是主动买还是主动卖推动的。订单流不平衡的出发点就是弥补这个缺陷:把成交量拆成主动买和主动卖两个方向,然后比较两者的相对强弱。
主动买对应的是以卖一价或更高价格立即成交的单子,它反映的是买方愿意付出流动性成本去获取股票,这在微观结构里被认为是知情交易者进入市场的信号之一。主动卖则相反。当主动买的量明显超过主动卖时,短期价格更容易上涨,反之更容易下跌。
订单流不平衡最基础的定义是:
OBI = (主动买量 - 主动卖量) / (主动买量 + 主动卖量)
这个值域在-1到1之间,正值代表买方强势,负值代表卖方强势。单纯用这个指标做因子,效果在分钟级别上比较随机,因为它没有累积效应和信息衰减的概念。实际研究中我更常用的是它的变体:
- 累积订单流不平衡:把一段时间内的主动买量-主动卖量做累加,用于捕捉持续性的资金方向。
- 加权订单流不平衡:对近期数据赋予更高权重,使用半衰期衰减或指数加权,让因子对最新信息更敏感。
- 分位数订单流不平衡:把原始OBI值映射到过去一段时间内的分位数,消除量纲和横截面差异。
另一个更进阶的做法是:用分钟收益率对订单流不平衡做滚动回归,取残差作为“未被订单流解释的价格变动”。这个残差因子可以理解为价格变动中超出订单流驱动的部分,在高频领域常被用来识别潜在的错价或信息不对称。回归残差因子构建起来比简单OBI耗时,但在横截面上区分度更好。
3.2 OFI因子的完整实现代码
下面我直接给出一个能够运行的示例,使用模拟的逐笔数据结构来演示从原始成交到分钟因子合成的完整流程。你只需要把数据替换成自己从数据源拉取的真实逐笔成交即可。
import pandas as pd import numpy as np # 模拟逐笔成交数据,真实场景下从数据源API获取 trades = pd.DataFrame({ 'ts': pd.date_range('2024-05-16 09:30:00', periods=10000, freq='100ms'), 'symbol': ['600519.SH'] * 10000, 'price': np.round(1715 + np.random.normal(0, 1, 10000), 2), 'qty': np.random.randint(1, 500, 10000), 'side': np.random.choice(['B', 'S'], 10000) })真实逐笔数据一天可能上几万条,而内存有限,所以我通常先做字段裁剪和时间格式化,再进入聚合流程。下面这段代码把逐笔成交聚合成分钟级别的主动买卖量:
def process_tick_to_minute(trades): df = trades.copy() df['ts'] = pd.to_datetime(df['ts']) # 过滤集合竞价和收盘后的数据 df = df[(df['ts'].dt.time >= pd.Timestamp('09:30:00').time()) & (df['ts'].dt.time <= pd.Timestamp('14:59:00').time())] # 去掉价格或数量异常的数据 df = df[(df['price'] > 0) & (df['qty'] > 0)] # 提取分钟标签 df['minute'] = df['ts'].dt.floor('1min') # 聚合主买主卖量 minute_bars = df.groupby(['symbol', 'minute', 'side']).agg( volume=('qty', 'sum'), vwap=('price', lambda x: np.sum(x * df.loc[x.index, 'qty']) / np.sum(df.loc[x.index, 'qty']))).reset_index() # 转成宽表 pivot = minute_bars.pivot_table(index=['symbol', 'minute'], columns='side', values='volume').reset_index() pivot.columns = ['symbol', 'minute', 'buy_volume', 'sell_volume'] pivot = pivot.fillna(0) return pivot拿到分钟级别的主动买卖量后,就可以构建订单流不平衡因子了:
def calc_obi_factors(minute_df, window=20, smooth_window=5): df = minute_df.copy() # 原始OBI df['obi'] = (df['buy_volume'] - df['sell_volume']) / (df['buy_volume'] + df['sell_volume'] + 1e-9) # 平滑OBI df['obi_smooth'] = df['obi'].rolling(smooth_window).mean() # OBI的移动平均,捕捉累积方向 df['obi_cum'] = (df['buy_volume'] - df['sell_volume']).rolling(window).sum() / \ (df['buy_volume'] + df['sell_volume']).rolling(window).sum() # OBI的Z分数,标准化到横截面可比 df['obi_zscore'] = (df['obi_smooth'] - df['obi_smooth'].rolling(120).mean()) / \ df['obi_smooth'].rolling(120).std() return df这里window=20代表20根1分钟K线,也就是大约20分钟的信息窗口;smooth_window=5则是做一次5分钟简单平均,减少单根bar噪声。两个超参数不是拍脑袋定的,我在真实股票上测过不同参数组合,20分钟窗口在A股主流股票的预测能力上相对稳定,5分钟平滑则能显著降低开盘、尾盘异常成交对因子值的瞬时冲击。
如果你想把因子做得更细化,可以在分钟bar基础上再添加价格变动中位数、交易笔数等指标,比如主动买笔数占比、单均主买金额。这些字段在pivot时一并聚合即可。
3.3 从逐笔到分钟:平滑、分位数、衰减的有效组合方式
一提到高频因子,很多人以为代码越复杂越好,实际上我见过跑得最稳的反而是思路简单的因子。订单流不平衡本身是很有微观结构逻辑的因子,但如果做完之后不做平滑,它的IC表现会非常不稳定。所以我建议至少做两层处理:
第一层是时间平滑。分钟的OBI受单笔大单影响极大,某根bar突然出现一笔超大主动买,OBI直接飙到0.9,但这种单笔冲击很可能在下一分钟就回归。用5到10分钟移动平均可以滤掉一部分噪声,但要注意窗口太长会钝化因子的反应速度,所以通常取5分钟比较合理。
第二层是横截面标准化。多因子研究里,因子的横截面可比性非常重要。一只高价股的OBI波动天然小于低价股,如果不做标准化,选出来的股票很可能集中在你没预期到的价格区间。我一般在每日收盘后,对当天所有股票的因子值做rank或Z-score标准化,再进行选股。
有一种更贴近实战的做法是把OBI和价格收益率放在一起做滚动回归,比如下面这段代码:
def calc_residual_factor(minute_df, window=60): df = minute_df.copy() df['ret'] = df['vwap'].pct_change() df['obi'] = (df['buy_volume'] - df['sell_volume']) / (df['buy_volume'] + df['sell_volume'] + 1e-9) # 滚动回归取残差,简单实现:用numpy的polyfit df['resid'] = np.nan for i in range(window, len(df)): y = df['ret'].iloc[i-window:i].values x = df['obi'].iloc[i-window:i].values if np.std(x) < 1e-9: continue coef = np.polyfit(x, y, 1) df.loc[df.index[i], 'resid'] = y[-1] - (coef[0] * x[-1] + coef[1]) return df这段代码里我用了最后一个样本的残差作为因子值,含义是“在当前订单流不平衡水平下,价格变动中无法被订单流解释的部分”。这个残差因子和单纯的OBI相关性很低,两者合在一起做因子合成的时候,可以把原始OBI的短期方向性和残差的中期错价性都保留下来。
如果你处理的数据量很大,这里建议用numba把for循环加速,具体是在函数上面加@njit,然后把df.values传入。滚动回归虽然逻辑简单,但在全市场几千只股票上跑,纯pandas循环会很痛苦。
4. 回测框架选型与全流程实现
4.1 选一个适合自己场景的回测方式
回测方案的选择取决于你的数据粒度和股票数量。很多初学者上来就套backtrader,但backtrader对分钟级多股票回测的支持并不友好,尤其是逐笔级别数据量很大的时候,内存和性能都会成为瓶颈。
如果你做的是日线级别多股票回测,backtrader确实是一个不错的选型,配置好数据源和经纪人手续费就能跑。但如果你跟我一样,因子是分钟级别生成的,但持仓周期是日级或几日,那我的建议是:自己写一个轻量级事件驱动回测引擎,完全可控,性能也好很多。
轻量级回测的核心逻辑并不复杂,可以拆成下面几部分:
- 数据流:把分钟级因子聚合成每天的因子快照。
- 信号生成:在每天收盘前某个时点,根据当天因子快照生成持仓目标。
- 交易撮合:在次日开盘后按预设价格区间执行买入或卖出。
- 组合记账:记录每日持仓、成交、手续费、净值。
这个流程用pandas就可以实现,代码量不大,但每一部分都能精确控制,不会出现框架里的隐藏逻辑给你造成困惑的情况。
4.2 多股数据准备与训练测试集切分
在回测之前,先要把因子数据处理成“日期 x 股票”的宽表结构,或者长表结构。我习惯用长表,也就是三列:date、symbol、factor。这样做的好处是后续按日期groupby非常方便,也不需要处理不同股票不同交易日导致的行对齐问题。
因子值最终是按天输出的,所以我通常会把分钟级别的因子聚合到日级别,方法很简单:取每天最后一根bar的因子值,或者取全天因子值的加权平均。具体选哪种要看因子本身的逻辑。如果是累积型因子如OBI_cum,我会取盘中最后一个值;如果是短周期因子如OBI_zscore,我会取全天的均值,因为短周期因子一天内波动太大,取尾值容易受收盘前几分钟噪声影响。
训练测试集切分有一个必须遵循的原则:不能随机打乱,必须按时间顺序切分。高频因子存在明显的自相关和季节性,随机K折等于把未来的信息泄漏到训练集里,回测出来的绩效会严重虚高。
train_end = pd.Timestamp('2024-01-01') train = df[df['date'] < train_end] test = df[df['date'] >= train_end]这个切分看起来简单,但我见过有同事为了“更充分地利用数据”用随机抽样切分,最后训练集和测试集里都有同一天不同股票的因子值,导致测试集结果异常好,等实盘就彻底失效。时序数据的回测没有捷径,该牺牲的样本就得牺牲。
4.3 因子信号生成、持仓规则和滚动回测核心代码
下面的代码演示了一个滚动调仓的完整回测流程,包含信号生成、T+1生效、手续费和净值计算:
def run_backtest(factor_df, price_df, rebalance_time='14:30', fee_rate=0.0003): """ factor_df: 长表,包含 date, symbol, factor price_df: 长表,包含 date, symbol, close rebalance_time: 因子快照时间,仅用于说明信号生成时点 """ # 每个交易日选因子值最高的20只股票等权 factor_df = factor_df.sort_values(['date', 'factor'], ascending=False) selected = factor_df.groupby('date').head(20).copy() selected['weight'] = 1.0 / 20 # 合并次日的收益 price_df['next_date'] = price_df.groupby('symbol')['date'].shift(-1) next_ret = price_df.copy() next_ret['ret'] = next_ret.groupby('symbol')['close'].pct_change() next_ret = next_ret[['date', 'symbol', 'ret']] # 信号在T日产生,T+1日生效,所以T日的持仓应该拿T+1的收益 merged = selected.merge(next_ret, left_on=['date', 'symbol'], right_on=['next_date', 'symbol'], how='left') merged['net_ret'] = merged['weight'] * (merged['ret'] - fee_rate) # 每日组合收益 daily_ret = merged.groupby('date')['net_ret'].sum() daily_ret = daily_ret.fillna(0) # 净值曲线 nav = (1 + daily_ret).cumprod() return nav, daily_ret注意这段代码里我特意用next_date去做匹配,就是为了保证T日产生的信号不会用T日当天的收益来计算,这在回测里是最基本的前视偏差规避手段。
实际回测中,rebalance_time这个参数往往会被忽略,但它在A股有特殊意义。因为A股T+1交易,你今天买入的股票明天才能卖出,所以如果你在盘中14:30产生信号,最早也只能在次日开盘附近执行。很多研究报告在做高频因子回测时会选择用当天收盘价成交,这其实已经引入了轻微的前视偏差,因为收盘价包含了尾盘半小时的信息。如果因子就是基于尾盘数据构建的,问题就更大了。
我常用的做法是:当天14:30的因子快照决定次日持仓,成交价用次日开盘后5分钟的VWAP近似。虽然执行成本比用收盘价高,但更贴近真实交易逻辑。
4.4 交易成本、冲击和T+1约束:回测好看不等于实盘能跑
逐笔高频因子有一个天然的特性:它对交易成本极度敏感。因为因子信号换手率高,可能每天都要调仓,如果手续费和滑点设置得过低,回测年化收益可能虚高几十个百分点。
我在回测中至少会设置三档成本:
| 成本类型 | 保守值 | 说明 |
|---|---|---|
| 佣金 | 万2.5双边 | 按实际开户费率设置 |
| 印花税 | 卖出千分之1 | A股卖出时收取 |
| 滑点 | 千分之1到千分之5 | 高频因子对流动性要求高,滑点不可忽略 |
另一个A股特有约束是T+1。日内信号当天买入当天卖出的回测在A股是不现实的,必须保证持仓周期至少跨一个交易日。如果是低频调仓比如一周一次,T+1影响不大;但如果你的因子是日内高频反转类,T+1就基本宣判了策略无效,因为信号产生时你已经无法在当日反向操作。
考虑到滑点影响,我建议对持仓股票做流动性过滤。比如剔除日均成交额低于1亿元的股票,或者剔除涨跌停附近的股票。逐笔因子的超额收益通常集中在中小市值股票上,但这些股票一旦遇到极端行情,冲击成本会吞噬全部alpha。回测里看得见的收益,实盘里往往被成本和冲击吃光,这是高频因子研究最残酷的一点。
5. 高频因子回测的常见问题和排查心得
5.1 常见问题速查表
我在做逐笔因子研究时,遇到过很多问题,有些是数据处理层面的,有些是回测逻辑层面的。这里整理成一张表格,方便你对应排查:
| 现象 | 可能原因 | 解决思路 |
|---|---|---|
| 因子值全是NaN | 股票停牌导致分钟bar缺失,滚动窗口没有足够样本 | 对缺失分钟用前值填充,或直接剔除该股票 |
| 回测年化收益高得离谱 | 信号使用了当天收盘后数据,或收益计算包含了当天信息 | 检查信号产生时间和收益计算区间是否错开至少一个交易日 |
| 同一只股票因子值在相邻两天剧烈跳变 | 主买主卖方向字段口径在不同交易日发生变化 | 检查数据源的side字段是否有异常值,统一数据清洗逻辑 |
| 多股合并回测内存溢出 | 分钟级全市场数据量过大 | 改成分块处理或先聚合到日级别再做回测 |
| OBI因子IC不稳定,时正时负 | 因子窗口参数不适合当前股票池,或主买主卖字段本身不准确 | 对窗口参数做网格搜索,并和量价类因子做合成后再观察 |
| 持仓股票集中在某几个行业 | 因子中包含行业相关偏差,比如高价股或低波动股 | 做行业中性化或市值中性化处理 |
很多问题并不是代码逻辑错了,而是数据源或者市场环境变化了。遇到这类问题,我第一反应永远是去看数据,而不是看因子代码。
5.2 我个人踩过的一些坑
最后分享几个我亲身经历过的坑,希望对你有帮助。
第一个坑是过度依赖数据源给的主买主卖字段。有些数据源的主买主卖是基于成交价和买卖盘口实时判断的,在极快行情下判断可能出错。我自己处理某个月的数据时,发现某只股票连续几天主买占比高达90%以上,怎么想都不合理,后来对比了同期的盘口快照数据才发现是数据源对部分成交的方向标记异常。从那以后,我每次处理新数据源的数据都会做一次统计检验:全市场所有股票的主买占比应该围绕50%波动,如果某只股票长期偏离过大,就要怀疑数据质量。
第二个坑是分钟聚合时的边界处理。我一开始直接用floor('1min')去聚合,然后发现9:30:00到9:30:59的数据被分到9:30,而9:31:00到9:31:59的数据被分到9:31。看起来没问题,但某些数据源的成交时间戳是成交回报时间而非实际成交时间,存在几十毫秒到几百毫秒的延迟,导致部分成交被分配到错误的分钟bar。这个偏差对1分钟级别的因子影响很大,我后来采用的是带容差的匹配逻辑,比如把上报延迟相对稳定的数据源做5秒对齐。
第三个坑是调仓频率过高。我刚做逐笔因子那段时间,总觉得自己拿到了高颗粒度数据,不做日内调仓就浪费了,结果回测手续费和滑点一扣,收益基本归零。后来我把调仓频率降到每日一次,反而因为减少了不必要的交易,净值曲线稳定了很多。逐笔因子的优势在于信号质量,不在于交易频率。
最后再分享一个小技巧:不管你构建什么因子,先做单因子IC检验,IC均值要大于0.03,ICIR大于0.3,才有继续优化的价值。不要一上来就搞复杂的机器学习模型,先把逐笔因子的微观结构逻辑搞清楚,再用统计工具验证。逐笔数据里的每一条记录都有它的意义,但也正因为如此,陷阱也比日线数据多得多。认真做完一遍之后,你会对市场机制的理解比只研究日线的人深一个维度。