如何计算最后一个值与 X 时间前的值之间的百分比?
How to calculate the percentage between the last value and the value from X time ago?
今天我试着玩了一下货币,给了 PostgreSQL 一个机会来帮助我。
我在 PostgreSQL 数据库中有一个 table,它有三个字段:
CREATE TABLE IF NOT EXISTS binance (
date TIMESTAMP,
symbol VARCHAR(20),
price REAL
)
此 table 更新时间为 10 秒到 10 秒,约 250 行。间隔之间的符号始终相同。例如数据:
+----------------------------+--------+-------+
| date | symbol | price |
+----------------------------+--------+-------+
| 2018-01-18 00:00:00.000000 | x | 12 |
| 2018-01-18 00:00:00.000120 | y | 15 |
| 2018-01-18 00:00:00.000200 | z | 19 |
| 2018-01-18 00:00:10.080000 | x | 14 |
| 2018-01-18 00:00:10.123000 | y | 16 |
| 2018-01-18 00:00:10.130000 | z | 20 |
+----------------------------+--------+-------+
现在,我想要做的是获取每个符号在过去 5 分钟内增长了多少(百分比)。
我们以符号为例(ETHBTC
)。该交易品种在过去 5 分钟内的数据如下所示:
+----------------------------+--------+----------+
| date | symbol | price |
+----------------------------+--------+----------+
| 2018-01-19 22:59:10.000000 | ETHBTC | 0.09082 |
| 2018-01-19 22:58:59.000000 | ETHBTC | 0.0907 |
| 2018-01-19 22:58:47.000000 | ETHBTC | 0.090693 |
| 2018-01-19 22:58:35.000000 | ETHBTC | 0.090697 |
| 2018-01-19 22:58:24.000000 | ETHBTC | 0.090712 |
| 2018-01-19 22:58:11.000000 | ETHBTC | 0.090682 |
| 2018-01-19 22:57:59.000000 | ETHBTC | 0.090774 |
| 2018-01-19 22:57:48.000000 | ETHBTC | 0.090672 |
| 2018-01-19 22:57:35.000000 | ETHBTC | 0.09075 |
| 2018-01-19 22:57:24.000000 | ETHBTC | 0.090727 |
| 2018-01-19 22:57:12.000000 | ETHBTC | 0.090705 |
| 2018-01-19 22:57:00.000000 | ETHBTC | 0.090707 |
| 2018-01-19 22:56:49.000000 | ETHBTC | 0.090646 |
| 2018-01-19 22:56:37.000000 | ETHBTC | 0.090645 |
| 2018-01-19 22:56:25.000000 | ETHBTC | 0.090636 |
| 2018-01-19 22:56:13.000000 | ETHBTC | 0.090696 |
| 2018-01-19 22:56:00.000000 | ETHBTC | 0.090698 |
| 2018-01-19 22:55:48.000000 | ETHBTC | 0.090693 |
| 2018-01-19 22:55:37.000000 | ETHBTC | 0.090698 |
| 2018-01-19 22:55:25.000000 | ETHBTC | 0.090601 |
| 2018-01-19 22:55:13.000000 | ETHBTC | 0.090644 |
| 2018-01-19 22:55:01.000000 | ETHBTC | 0.0906 |
| 2018-01-19 22:54:49.000000 | ETHBTC | 0.0906 |
| 2018-01-19 22:54:37.000000 | ETHBTC | 0.09062 |
| 2018-01-19 22:54:25.000000 | ETHBTC | 0.090693 |
+----------------------------+--------+----------+
为了 select 此数据,我使用以下查询:
SELECT *
FROM binance
WHERE date >= NOW() AT TIME ZONE 'EET' - INTERVAL '5 minutes'
AND symbol = 'ETHBTC'
ORDER BY date DESC;
我想做的是找出每个符号:
- 最后一个值与 10 秒前的值之间的百分比
- 最后一个值与 1 分钟前的值之间的百分比
- 最后一个值与 5 分钟前的值之间的百分比
现在,我对这样的查询应该是什么样子感到困惑。更重要的是,IDK 是否重要,但查询是 Python 中的 运行,所以我可能无法利用完整的 PostgreSQL 功能。
排名百分比:
此查询给出查询中行的 0 到 1 的百分比,第一行为 0,最后一行为 1。
例如:
date |symbol |price | percentage
-----------+--------+------+-------------
2017-01-05 | 1 | 0.5 | 1
2017-01-04 | 1 | 1.5 | 0.5
2017-01-03 | 1 | 1 | 0
2017-01-05 | 2 | 1 | 1
2017-01-04 | 2 | 3 | 0.5
2017-01-03 | 2 | 2 | 0
这是查询:
SELECT *,
-- this makes a column with the percentage per row
percent_rank() OVER (PARTITION BY symbol ORDER BY date) AS percent
FROM binance
WHERE date >= NOW() AT TIME ZONE 'EET' - INTERVAL '5 minutes'
ORDER BY symbol,date DESC;
相对百分比:
此查询显示数据集价格的最旧值的百分比。
例如:
date | symbol |price | percentage
-----------+--------+------------
2017-01-05 | 1 | 0.5 | 50
2017-01-04 | 1 | 1.5 | 150
2017-01-03 | 1 | 1 | 100
2017-01-05 | 2 | 1 | 50
2017-01-04 | 2 | 3 | 150
2017-01-03 | 2 | 2 | 100
查询是:
SELECT *,
-- Formula to get the percentage taking the price from the oldest date:
100*price/last_value(price) OVER (PARTITION BY symbol ORDER BY date DESC rows between unbounded preceding and unbounded following) AS percentage
FROM binance
WHERE date >= NOW() AT TIME ZONE 'EET' - INTERVAL '5 minutes'
ORDER BY symbol,date DESC;
要获得连续三个不同时间的相对百分比,您必须每次都加入每个案例,在本例中为 10 秒/1 分钟/5 分钟。
这是查询,注意 JOIN
是 ON
id。您需要主键或唯一值才能使此 JOIN
正常工作:
-- Overall SELECT, '*' includes 5min
SELECT a.*,b."1min",c."10sec"
FROM
-- First we select the group with most rows, that are <=5min
(SELECT *,
-- Formula for the percentage
100*price/last_value(price)
OVER (PARTITION BY symbol
ORDER BY date DESC rows between unbounded preceding and
unbounded following) AS "5min"
FROM test
WHERE date >= NOW() AT TIME ZONE 'EET' - INTERVAL '5 minutes'
ORDER BY symbol,date DESC)a
LEFT JOIN
-- Join with 1 minute query
(SELECT *,
-- Formula for the percentage
100*price/last_value(price)
OVER (PARTITION BY symbol
ORDER BY date DESC rows between unbounded preceding and
unbounded following) AS "1min"
FROM test
WHERE date >= NOW() AT TIME ZONE 'EET' - INTERVAL '1 minutes'
ORDER BY symbol,date DESC)b
-- join with id (primary or unique)
ON a.id = b.id
-- Join with 30 seconds query
LEFT JOIN
(SELECT *,
-- Formula for the percentage
100*price/last_value(price)
OVER (PARTITION BY symbol
ORDER BY date DESC rows between unbounded preceding and
unbounded following) AS "10sec"
FROM test
WHERE date >= NOW() AT TIME ZONE 'EET' - INTERVAL '30 seconds'
ORDER BY symbol,date DESC)c
-- join with id (primary or unique)
ON a.id=c.id
在此查询中,您可以根据需要更改百分比和时间的公式。如果您希望百分比相对于另一个值,如 主价格 ,则必须将其包含在每个查询中并添加到公式中,而不是 last_value(price) OVER...
。请记住,实际公式获取的是相对于查询中最旧行的百分比。
演示
Rextester 在线演示:http://rextester.com/QNVGU31219
SQL
下面是SQL最新价格与1分钟前的价格对比:
WITH cte AS
(SELECT price,
ABS(EXTRACT(EPOCH FROM (
SELECT date - (SELECT MAX(date) - INTERVAL '1 minute' FROM binance))))
AS secs_from_prev_timestamp
FROM binance
WHERE symbol = 'ETHBTC')
SELECT price /
(SELECT price FROM binance
WHERE symbol = 'ETHBTC' AND date = (SELECT MAX(date) FROM binance))
* 100.0 AS percentage_difference
FROM cte
WHERE secs_from_prev_timestamp = (SELECT MIN(secs_from_prev_timestamp) FROM cte);
可以简单地更改以上内容以与不同时间间隔之前的价格进行比较,例如通过更改为 INTERVAL '5 minutes'
而不是 INTERVAL '1 minute'
,或者通过将对 'ETHBTC'
的两个引用更改为不同的符号来给出不同符号的结果。
说明
棘手的一点是获取以前的价格。这是通过使用常见的 table 表达式 (CTE) 来完成的,该表达式列出了所有价格和距离所需时间戳的秒数。使用了绝对值函数(ABS
)所以无论是大于还是小于目标时间戳,都会找到最近的一个。
结果
在上面的一个例子中,查询给出了 99.848...%
的结果。这是根据 0.090682 / 0.09082 * 100.0
制定的,其中 0.09082
是最新价格,0.090682
是一分钟前的价格。
以上是基于对 "percentage difference" 含义的假设,但也有可以计算的替代百分比 - 例如0.09082
比 0.090682
高 0.152%
。 (如果我对百分比差异的解释不是您想要的,请在评论中回复,我会相应地更新答案。)
更新 - "do it all" 查询
在阅读了您对 Dan 的回答的评论后,您希望使用单个查询获得所有这些结果,我在下面发布了一个应该满足要求的结果。 Rextester 演示在这里:http://rextester.com/QDUN45907
WITH cte2 AS
(WITH cte1 AS
(SELECT symbol,
price,
ABS(EXTRACT(EPOCH FROM (
SELECT date - (SELECT MAX(date) - INTERVAL '10 seconds' FROM binance))))
AS secs_from_latest_minus_10,
ABS(EXTRACT(EPOCH FROM (
SELECT date - (SELECT MAX(date) - INTERVAL '1 minute' FROM binance))))
AS secs_from_latest_minus_60,
ABS(EXTRACT(EPOCH FROM (
SELECT date - (SELECT MAX(date) - INTERVAL '5 minutes' FROM binance))))
AS secs_from_latest_minus_300
FROM binance)
SELECT symbol,
(SELECT price AS latest_price
FROM binance b2
WHERE b2.symbol = b.symbol AND date = (SELECT MAX(date) FROM binance)),
(SELECT price AS price_latest_minus_10
FROM cte1
WHERE cte1.symbol = b.symbol AND secs_from_latest_minus_10 =
(SELECT MIN(secs_from_latest_minus_10) FROM cte1)),
(SELECT price AS price_latest_minus_60
FROM cte1
WHERE cte1.symbol = b.symbol AND secs_from_latest_minus_60 =
(SELECT MIN(secs_from_latest_minus_60) FROM cte1)),
(SELECT price AS price_latest_minus_500
FROM cte1
WHERE cte1.symbol = b.symbol AND secs_from_latest_minus_60 =
(SELECT MIN(secs_from_latest_minus_60) FROM cte1))
FROM binance b
GROUP BY symbol)
SELECT symbol,
price_latest_minus_10 / latest_price * 100.0 AS percentage_diff_10_secs_ago,
price_latest_minus_60 / latest_price * 100.0 AS percentage_diff_1_minute_ago,
price_latest_minus_500 / latest_price * 100.0 AS percentage_diff_5_minutes_ago
FROM cte2;
今天我试着玩了一下货币,给了 PostgreSQL 一个机会来帮助我。
我在 PostgreSQL 数据库中有一个 table,它有三个字段:
CREATE TABLE IF NOT EXISTS binance (
date TIMESTAMP,
symbol VARCHAR(20),
price REAL
)
此 table 更新时间为 10 秒到 10 秒,约 250 行。间隔之间的符号始终相同。例如数据:
+----------------------------+--------+-------+
| date | symbol | price |
+----------------------------+--------+-------+
| 2018-01-18 00:00:00.000000 | x | 12 |
| 2018-01-18 00:00:00.000120 | y | 15 |
| 2018-01-18 00:00:00.000200 | z | 19 |
| 2018-01-18 00:00:10.080000 | x | 14 |
| 2018-01-18 00:00:10.123000 | y | 16 |
| 2018-01-18 00:00:10.130000 | z | 20 |
+----------------------------+--------+-------+
现在,我想要做的是获取每个符号在过去 5 分钟内增长了多少(百分比)。
我们以符号为例(ETHBTC
)。该交易品种在过去 5 分钟内的数据如下所示:
+----------------------------+--------+----------+
| date | symbol | price |
+----------------------------+--------+----------+
| 2018-01-19 22:59:10.000000 | ETHBTC | 0.09082 |
| 2018-01-19 22:58:59.000000 | ETHBTC | 0.0907 |
| 2018-01-19 22:58:47.000000 | ETHBTC | 0.090693 |
| 2018-01-19 22:58:35.000000 | ETHBTC | 0.090697 |
| 2018-01-19 22:58:24.000000 | ETHBTC | 0.090712 |
| 2018-01-19 22:58:11.000000 | ETHBTC | 0.090682 |
| 2018-01-19 22:57:59.000000 | ETHBTC | 0.090774 |
| 2018-01-19 22:57:48.000000 | ETHBTC | 0.090672 |
| 2018-01-19 22:57:35.000000 | ETHBTC | 0.09075 |
| 2018-01-19 22:57:24.000000 | ETHBTC | 0.090727 |
| 2018-01-19 22:57:12.000000 | ETHBTC | 0.090705 |
| 2018-01-19 22:57:00.000000 | ETHBTC | 0.090707 |
| 2018-01-19 22:56:49.000000 | ETHBTC | 0.090646 |
| 2018-01-19 22:56:37.000000 | ETHBTC | 0.090645 |
| 2018-01-19 22:56:25.000000 | ETHBTC | 0.090636 |
| 2018-01-19 22:56:13.000000 | ETHBTC | 0.090696 |
| 2018-01-19 22:56:00.000000 | ETHBTC | 0.090698 |
| 2018-01-19 22:55:48.000000 | ETHBTC | 0.090693 |
| 2018-01-19 22:55:37.000000 | ETHBTC | 0.090698 |
| 2018-01-19 22:55:25.000000 | ETHBTC | 0.090601 |
| 2018-01-19 22:55:13.000000 | ETHBTC | 0.090644 |
| 2018-01-19 22:55:01.000000 | ETHBTC | 0.0906 |
| 2018-01-19 22:54:49.000000 | ETHBTC | 0.0906 |
| 2018-01-19 22:54:37.000000 | ETHBTC | 0.09062 |
| 2018-01-19 22:54:25.000000 | ETHBTC | 0.090693 |
+----------------------------+--------+----------+
为了 select 此数据,我使用以下查询:
SELECT *
FROM binance
WHERE date >= NOW() AT TIME ZONE 'EET' - INTERVAL '5 minutes'
AND symbol = 'ETHBTC'
ORDER BY date DESC;
我想做的是找出每个符号:
- 最后一个值与 10 秒前的值之间的百分比
- 最后一个值与 1 分钟前的值之间的百分比
- 最后一个值与 5 分钟前的值之间的百分比
现在,我对这样的查询应该是什么样子感到困惑。更重要的是,IDK 是否重要,但查询是 Python 中的 运行,所以我可能无法利用完整的 PostgreSQL 功能。
排名百分比:
此查询给出查询中行的 0 到 1 的百分比,第一行为 0,最后一行为 1。
例如:
date |symbol |price | percentage
-----------+--------+------+-------------
2017-01-05 | 1 | 0.5 | 1
2017-01-04 | 1 | 1.5 | 0.5
2017-01-03 | 1 | 1 | 0
2017-01-05 | 2 | 1 | 1
2017-01-04 | 2 | 3 | 0.5
2017-01-03 | 2 | 2 | 0
这是查询:
SELECT *,
-- this makes a column with the percentage per row
percent_rank() OVER (PARTITION BY symbol ORDER BY date) AS percent
FROM binance
WHERE date >= NOW() AT TIME ZONE 'EET' - INTERVAL '5 minutes'
ORDER BY symbol,date DESC;
相对百分比:
此查询显示数据集价格的最旧值的百分比。
例如:
date | symbol |price | percentage
-----------+--------+------------
2017-01-05 | 1 | 0.5 | 50
2017-01-04 | 1 | 1.5 | 150
2017-01-03 | 1 | 1 | 100
2017-01-05 | 2 | 1 | 50
2017-01-04 | 2 | 3 | 150
2017-01-03 | 2 | 2 | 100
查询是:
SELECT *,
-- Formula to get the percentage taking the price from the oldest date:
100*price/last_value(price) OVER (PARTITION BY symbol ORDER BY date DESC rows between unbounded preceding and unbounded following) AS percentage
FROM binance
WHERE date >= NOW() AT TIME ZONE 'EET' - INTERVAL '5 minutes'
ORDER BY symbol,date DESC;
要获得连续三个不同时间的相对百分比,您必须每次都加入每个案例,在本例中为 10 秒/1 分钟/5 分钟。
这是查询,注意 JOIN
是 ON
id。您需要主键或唯一值才能使此 JOIN
正常工作:
-- Overall SELECT, '*' includes 5min
SELECT a.*,b."1min",c."10sec"
FROM
-- First we select the group with most rows, that are <=5min
(SELECT *,
-- Formula for the percentage
100*price/last_value(price)
OVER (PARTITION BY symbol
ORDER BY date DESC rows between unbounded preceding and
unbounded following) AS "5min"
FROM test
WHERE date >= NOW() AT TIME ZONE 'EET' - INTERVAL '5 minutes'
ORDER BY symbol,date DESC)a
LEFT JOIN
-- Join with 1 minute query
(SELECT *,
-- Formula for the percentage
100*price/last_value(price)
OVER (PARTITION BY symbol
ORDER BY date DESC rows between unbounded preceding and
unbounded following) AS "1min"
FROM test
WHERE date >= NOW() AT TIME ZONE 'EET' - INTERVAL '1 minutes'
ORDER BY symbol,date DESC)b
-- join with id (primary or unique)
ON a.id = b.id
-- Join with 30 seconds query
LEFT JOIN
(SELECT *,
-- Formula for the percentage
100*price/last_value(price)
OVER (PARTITION BY symbol
ORDER BY date DESC rows between unbounded preceding and
unbounded following) AS "10sec"
FROM test
WHERE date >= NOW() AT TIME ZONE 'EET' - INTERVAL '30 seconds'
ORDER BY symbol,date DESC)c
-- join with id (primary or unique)
ON a.id=c.id
在此查询中,您可以根据需要更改百分比和时间的公式。如果您希望百分比相对于另一个值,如 主价格 ,则必须将其包含在每个查询中并添加到公式中,而不是 last_value(price) OVER...
。请记住,实际公式获取的是相对于查询中最旧行的百分比。
演示
Rextester 在线演示:http://rextester.com/QNVGU31219
SQL
下面是SQL最新价格与1分钟前的价格对比:
WITH cte AS
(SELECT price,
ABS(EXTRACT(EPOCH FROM (
SELECT date - (SELECT MAX(date) - INTERVAL '1 minute' FROM binance))))
AS secs_from_prev_timestamp
FROM binance
WHERE symbol = 'ETHBTC')
SELECT price /
(SELECT price FROM binance
WHERE symbol = 'ETHBTC' AND date = (SELECT MAX(date) FROM binance))
* 100.0 AS percentage_difference
FROM cte
WHERE secs_from_prev_timestamp = (SELECT MIN(secs_from_prev_timestamp) FROM cte);
可以简单地更改以上内容以与不同时间间隔之前的价格进行比较,例如通过更改为 INTERVAL '5 minutes'
而不是 INTERVAL '1 minute'
,或者通过将对 'ETHBTC'
的两个引用更改为不同的符号来给出不同符号的结果。
说明
棘手的一点是获取以前的价格。这是通过使用常见的 table 表达式 (CTE) 来完成的,该表达式列出了所有价格和距离所需时间戳的秒数。使用了绝对值函数(ABS
)所以无论是大于还是小于目标时间戳,都会找到最近的一个。
结果
在上面的一个例子中,查询给出了 99.848...%
的结果。这是根据 0.090682 / 0.09082 * 100.0
制定的,其中 0.09082
是最新价格,0.090682
是一分钟前的价格。
以上是基于对 "percentage difference" 含义的假设,但也有可以计算的替代百分比 - 例如0.09082
比 0.090682
高 0.152%
。 (如果我对百分比差异的解释不是您想要的,请在评论中回复,我会相应地更新答案。)
更新 - "do it all" 查询
在阅读了您对 Dan 的回答的评论后,您希望使用单个查询获得所有这些结果,我在下面发布了一个应该满足要求的结果。 Rextester 演示在这里:http://rextester.com/QDUN45907
WITH cte2 AS
(WITH cte1 AS
(SELECT symbol,
price,
ABS(EXTRACT(EPOCH FROM (
SELECT date - (SELECT MAX(date) - INTERVAL '10 seconds' FROM binance))))
AS secs_from_latest_minus_10,
ABS(EXTRACT(EPOCH FROM (
SELECT date - (SELECT MAX(date) - INTERVAL '1 minute' FROM binance))))
AS secs_from_latest_minus_60,
ABS(EXTRACT(EPOCH FROM (
SELECT date - (SELECT MAX(date) - INTERVAL '5 minutes' FROM binance))))
AS secs_from_latest_minus_300
FROM binance)
SELECT symbol,
(SELECT price AS latest_price
FROM binance b2
WHERE b2.symbol = b.symbol AND date = (SELECT MAX(date) FROM binance)),
(SELECT price AS price_latest_minus_10
FROM cte1
WHERE cte1.symbol = b.symbol AND secs_from_latest_minus_10 =
(SELECT MIN(secs_from_latest_minus_10) FROM cte1)),
(SELECT price AS price_latest_minus_60
FROM cte1
WHERE cte1.symbol = b.symbol AND secs_from_latest_minus_60 =
(SELECT MIN(secs_from_latest_minus_60) FROM cte1)),
(SELECT price AS price_latest_minus_500
FROM cte1
WHERE cte1.symbol = b.symbol AND secs_from_latest_minus_60 =
(SELECT MIN(secs_from_latest_minus_60) FROM cte1))
FROM binance b
GROUP BY symbol)
SELECT symbol,
price_latest_minus_10 / latest_price * 100.0 AS percentage_diff_10_secs_ago,
price_latest_minus_60 / latest_price * 100.0 AS percentage_diff_1_minute_ago,
price_latest_minus_500 / latest_price * 100.0 AS percentage_diff_5_minutes_ago
FROM cte2;