我正在尝试在postgres上实现指数移动平均线(EMA),但是当我检查文档并考虑它时,我尝试的越多,我就越困惑。
EMA(x)
的公式为:
EMA(x1) = x1 EMA(xn) = α * xn + (1 - α) * EMA(xn-1)
对聚合器来说似乎是完美的,保持最后计算元素的结果正是这里必须完成的。但是聚合器会产生一个结果(缩减或折叠),这里我们需要一个结果列表(一列)(如map)。我一直在检查程序和功能是如何工作的,但是AFAIK它们产生一个单独的输出而不是列。我已经看到了很多程序和函数,但我无法弄清楚它是如何与关系代数交互的,尤其是在做这样的事情时,EMA。
到目前为止,我没有运气搜索互联网。但是EMA的定义非常简单,我希望有可能将这个定义翻译成postgres中的一个简单而有效的东西,因为在我的上下文中迁移到NoSQL会过度。
谢谢。
PD:在这里你可以看到一个例子:
https://docs.google.com/spreadsheet/ccc?key=0AvfclSzBscS6dDJCNWlrT3NYdDJxbkh3cGJ2S2V0cVE
答案 0 :(得分:19)
您可以定义自己的聚合函数,然后将其与窗口规范一起使用,以获取每个阶段的聚合输出,而不是单个值。
因此聚合是一个状态,一个转换函数用于修改每一行的状态,还有一个用于将状态转换为输出值的终结函数。对于像这样的简单情况,只需一个转换函数即可。
create function ema_func(numeric, numeric) returns numeric
language plpgsql as $$
declare
alpha numeric := 0.5;
begin
-- uncomment the following line to see what the parameters mean
-- raise info 'ema_func: % %', $1, $2;
return case
when $1 is null then $2
else alpha * $2 + (1 - alpha) * $1
end;
end
$$;
create aggregate ema(basetype = numeric, sfunc = ema_func, stype = numeric);
给了我:
steve@steve@[local] =# select x, ema(x, 0.1) over(w), ema(x, 0.2) over(w) from data window w as (order by n asc) limit 5;
x | ema | ema
-----------+---------------+---------------
44.988564 | 44.988564 | 44.988564
39.5634 | 44.4460476 | 43.9035312
38.605724 | 43.86201524 | 42.84396976
38.209646 | 43.296778316 | 41.917105008
44.541264 | 43.4212268844 | 42.4419368064
这些数字似乎与您添加到问题中的电子表格相符。
此外,您可以定义函数以将alpha作为参数从语句中传递:
create or replace function ema_func(state numeric, inval numeric, alpha numeric)
returns numeric
language plpgsql as $$
begin
return case
when state is null then inval
else alpha * inval + (1-alpha) * state
end;
end
$$;
create aggregate ema(numeric, numeric) (sfunc = ema_func, stype = numeric);
select x, ema(x, 0.5 /* alpha */) over (order by n asc) from data
此外,这个函数实际上非常简单,根本不需要在plpgsql中,但它可以只是一个sql函数,尽管你不能在其中一个中按名称引用参数:
create or replace function ema_func(state numeric, inval numeric, alpha numeric)
returns numeric
language sql as $$
select case
when $1 is null then $2
else $3 * $2 + (1-$3) * $1
end
$$;
答案 1 :(得分:1)
这种类型的查询可以通过递归CTE解决 - 尝试:
with recursive cte as (
select n, x ema from my_table where n = 1
union all
select m.n, alpha * m.x + (1 - alpha) * cte.ema
from cte
join my_table m on cte.n = m.n - 1
cross join (select ? alpha) a)
select * from cte;
答案 2 :(得分:1)
--$1 Stock code
--$2 exponential;
create or replace function fn_ema(text,numeric)
returns numeric as
$body$
declare
alpha numeric := 0.5;
var_r record;
result numeric:=0;
n int;
p1 numeric;
begin
alpha=2/(1+$2);
n=0;
for var_r in(select *
from stock_old_invest
where code=$1 order by stock_time desc)
loop
if n>0 then
result=result+(1-alpha)^n*var_r.price_now;
else
p1=var_r.price_now;
end if;
n=n+1;
end loop;
result=alpha*(result+p1);
return result;
end
$body$
language plpgsql volatile
cost 100;
alter function fn_ema(text,numeric)
owner to postgres;