Ava*_*lla 4 sql postgresql recursion weighted-average
EMA的一般公式:
EMA(x n ) = α * x n + (1 - α) * EMA(x n-1 )
在哪里:
x n = 价格 α = 0.5 -- 给定 3 天 SMA
下面的递归 CTE 可以完成这项工作:
WITH recursive
ewma_3 (DATE, PRICE, EMA_3, rn)
AS (
-- Anchor
-- Feed SMA_3 to recursive CTE
SELECT rows."DATE", rows."PRICE", sma.sma AS ewma, rows.rn
FROM (
SELECT "DATE", "PRICE", ROW_NUMBER() OVER(ORDER BY "DATE") rn
FROM PRICE_TBL
) rows
JOIN (
SELECT "DATE",
ROUND(AVG("PRICE"::numeric)
OVER(ORDER BY "DATE" ROWS BETWEEN 2 PRECEDING AND CURRENT ROW), 6) AS sma
FROM PRICE_TBL
) sma ON sma."DATE" = rows."DATE"
WHERE rows.rn = 3
UNION ALL
-- Recursive Member
SELECT rows."DATE", rows."PRICE"
-- Calculate EMA_3 below
,ROUND(((rows."PRICE"::numeric * 0.5) +
(ewma.EMA_3 * (1 - 0.5))), 6) AS ewma
, rows.rn
FROM ewma_3 ewma
JOIN (
SELECT "DATE", "PRICE", ROW_NUMBER() OVER(ORDER BY "DATE") rn
FROM PRICE_TBL
) rows ON ewma.rn + 1 = rows.rn
WHERE rows.rn > 3
)
SELECT ewma_3.rn AS "ID", DATE, PRICE, EMA_3
FROM ewma_3
;
Run Code Online (Sandbox Code Playgroud)
这更多的是效率和速度的问题。11 s 631 ms完成一组包含 9852 行的示例。
我读到聚合器保留最后计算元素的结果,如果是这样:
我也愿意接受任何改进 CTE 的建议,但是,我相信aggregates会更快。我也知道这是一个较旧的主题,但我是个新手,posgres因此非常感谢任何帮助。谢谢!
更新
样本数据:
我的 7 天期间 CTE 回报(不含DATE):
ID PRICE EMA_7
--+----------+-----------
7 0.529018 0.4888393
8 0.551339 0.5044642
9 0.580357 0.5234374
10 0.633929 0.5510603
11 0.642857 0.5740095
12 0.627232 0.5873151
Run Code Online (Sandbox Code Playgroud)
尽管 @GordonLinoff 提供的递归 CTE 快了一瞬间,但聚合器(聚合函数)对于速度来说是最佳的。我尝试过这个但得到:
错误:函数 ema(numeric, numeric) 不存在
显然,没有函数与给定的名称和参数类型匹配。显式类型转换?无能
我会将递归 CTE 写为:
with recursive p as (
select p.*, row_number() over (order by date) as seqnum
from price_tbl p
),
cte as (
select seqnum, date, price, price * 1.0 as exp_avg
from p
where seqnum = 1
union all
select p.seqnum, p.date, p.price, (cte.exp_avg * 0.5 + p.price * 0.5)
from cte join
p
on p.seqnum = cte.seqnum + 1
)
select *
from cte;
Run Code Online (Sandbox Code Playgroud)
确实0.5是0.5和1 - 0.5。您可以轻松地针对不同的 alpha 进行调整。
您还可以使用窗口函数来执行此操作:
select p.*,
(sum(power((1 / 0.5), seqnum) * price) over (order by seqnum) +
first_value(price) over (order by seqnum)
) / power((1 / 0.5), seqnum + 1)
from (select p.*,
row_number() over (order by date) - 1 as seqnum
from price_tbl p
) p;
Run Code Online (Sandbox Code Playgroud)
这first_value()是因为计算的一个怪癖。第一个和第二个值实际上计算相同的数量,因此第一个数量需要“加回”。
也就是说,如果您的序列甚至有几十行长,则很容易出现溢出和除零错误。
这是一个 db<>fiddle。
| 归档时间: |
|
| 查看次数: |
1907 次 |
| 最近记录: |