如何为股票市场Cube数据构建每日简单移动平均(Simple Moving Average)度量
嘿,这个移动平均的需求我在Cube里实现过,咱们来拆解一下怎么搞定它!
核心思路
你猜的没错,确实可以用tt.agg.mean()结合scope参数来实现,但关键是动态获取每个日期对应的X个交易日窗口——因为要跳过周末、节假日这些非交易日,不能直接用自然日的区间。Cube的时间维度成员是按实际交易日排序的,所以我们可以利用tt.range()函数来精准定位窗口范围。
具体实现步骤
假设你的Cube里,时间维度叫date(对应数据库里的trade_date字段),收盘价字段是close_price,我们以X=5为例写代码:
cube(`StockMarket`, { sql: `SELECT * FROM your_stock_table`, measures: { // 定义5日简单移动平均度量 SMA5: { type: `number`, sql: `close_price`, aggregator: tt.agg.mean({ // 动态生成每个日期对应的窗口范围 scope: (members) => { // 获取当前行的日期成员 const currentTradeDate = members['date']; // 生成从当前日期往前数4个交易日(含当前)的范围,共5个交易日 // 这里的-4是因为X=5,所以需要-(X-1) return tt.range(currentTradeDate, -4, 0); } }) } }, dimensions: { date: { sql: `trade_date`, type: `time`, // 确保维度只包含交易日(如果你的原始数据有非交易日的话) filters: [{ sql: `EXTRACT(ISODOW FROM trade_date) NOT IN (6,7)` }] }, // 其他维度(比如股票代码) ticker: { sql: `ticker`, type: `string` } } });
关键细节解释
tt.range()的用法:tt.range(currentMember, startOffset, endOffset)会返回从currentMember + startOffset到currentMember + endOffset的所有维度成员。这里0代表当前日期,-4代表往前数第4个交易日,加起来正好是5个交易日的窗口。- 非交易日过滤:如果你的原始数据里包含周末或节假日的空数据,一定要在
date维度里加上过滤条件,确保时间维度的成员都是有效的交易日,这样tt.range()才会正确跳过非交易日。 - 可配置窗口大小:如果想让用户自由选择窗口大小(比如5日、10日、20日),可以把窗口数做成Cube参数:
parameters: { smaWindow: { type: `number`, default: 5, description: "简单移动平均的窗口大小" } }, measures: { SMA: { type: `number`, sql: `close_price`, aggregator: tt.agg.mean({ scope: (members) => { const currentDate = members['date']; const windowSize = cube.parameters.smaWindow; return tt.range(currentDate, -(windowSize - 1), 0); } }) } }
边界情况处理
当遇到数据开头的几个交易日(比如前4个交易日,对应X=5),tt.range()会自动只取现有存在的交易日成员,这时候移动平均会计算现有天数的平均值(比如第一个交易日的SMA5就是当天收盘价),这符合常规的移动平均逻辑。
内容的提问来源于stack exchange,提问作者Alex
相关产品推荐
相关产品推荐

