1

在我的数据库中有数十万行。我正在尝试计算股票或外汇的 RSI。

的公式RSI(14)是:

100 - (100 / 1 + RS )

RS 是平均收益/平均损失。

该公式回顾过去的 14 行(包括当前行),并对有增益(或向上移动)的行的值进行平均,并将该平均值除以有损失的行的平均值。

我在 MySQL 中遇到的问题是让它选择前 14 行的平均增益和平均损失。这在 Excel 中很容易做到。在 Excel 中,我创建了一个分析是否有增益或损失的列,然后我使用AVERAGEIF(Range,Criteria,Average_Range). 完全没有问题。但是 Excel 无法处理我必须分析的大量行。

我想过这样的事情:

SELECT *, 
       id, 
       (SELECT 10000 * Avg(close - open) 
        FROM   `2011` 
        WHERE  id <= 25 
               AND id >= ( 25 - 13 ) 
               AND ( close - open ) >= 0) / (SELECT 10000 * Avg(open - close) 
                                             FROM   `2011` 
                                             WHERE  id <= 25 
                                                    AND id >= ( 25 - 13 ) 
                                                    AND ( open - close ) > 0) 
FROM   `2011` 
LIMIT  50 

限制 50 只是为了保持可控。但这并没有达到我想要的效果。它的数学部分恰到好处,但它对每一行重复相同的数字。

因此,当它位于第 25 行时,它会查看第 12 到 25 行。然后对于第 26 行,它会查看第 13 到 26 行,依此类推,这将是每一行的新数字。相反,上面的 SELECT 语句在最后一列中重复相同的数字,这对于每一行应该是不同的,因为它的范围与其他所有内容不同。

4

2 回答 2

1

以下是表达查询的更简单方法:

select t.*,
        100 - (100 / (1 + avg(case when close - open > 0 then close - open end)/avg(case when open - close > 0 then open - close end))
              ) as RS14
 from `2011` t join
      `2011` t2
      on t2.id between t.id - 14 and t
group by t.id

我不能发誓它会更快,但它可能会。您还应该有一个关于 id 的索引。

如果你真的需要这样的功能,你能切换数据库吗?SQL Server 2012、Oracle 和 Postgres 都提供适用于此类查询的累积求和功能。

我一直在思考这个问题,你可以高效地进行查询。也许更痛苦的方法是使用显式连接:

select
from `2011` t0 join
     `2011` t1
     on t0.id = t1.id + 1 join
     `2011` t2
     on t0.id = t1.id + 2 join
     . . .
     2011` t13
     on t0.id = t13.id + 13

您需要使用来自不同表的 14 个变量来计算出所需的表达式。但是,这将有效地使用 id 上的索引并且应该很快(运行可能比写入快)。

另一种方法从观察开始,您可以很容易地获得每 14 个值:

select ((t.id-1) div 14)*14,
       100 - (100 / (1 + avg(case when close - open > 0 then close - open end)/avg(case when open - close > 0 then open - close end))           
from `2011` t
group by ((t.id-1) div 14)

表达方式可能并不完全正确。这个想法是使用聚合将 14 行分组在一起。

现在,我们可以通过以下方式获取中间行:

select (((t.id-1 + offset) div 14))*14+offset,
       100 - (100 / (1 + avg(case when close - open > 0 then close - open end)/avg(case when open - close > 0 then open - close end))           
from `2011` t cross join
     (select 0 as offset union all select 1 union all . . .
      select 13
     ) offsets
group by (((t.id-1 + offset) div 14))*14+offset

这样做是cross join为了获得中间的行。

最后两个中的任何一个都应该表现得很好。我会选择group by解决方案,因为它更容易编写、维护和修改。

于 2013-02-01T21:06:22.057 回答
0

问题是 MySQL 不会自动执行电子表格所做的事情,即根据id外部查询调整子查询。

您需要一个相关的子查询,如下所示:

SELECT A.*, A.id,
        (SELECT 10000 * AVG(B.close - B.open)
         FROM `2011` B
         WHERE B.id <= A.id
               AND B.id >= ( A.id - 13 )
               AND (B.close - B.open) >= 0 / (SELECT 10000 * AVG(C.open - C.close)
                                              FROM `2011` C
                                              WHERE C.id <= A.id
                                                    AND C.id >= (A.id - 13)
                                                    AND (C.open - C.close) > 0)
 FROM `2011` A
 LIMIT 50
于 2013-02-01T20:25:04.427 回答