dot 快速的未来正在您所在的城市举行活动。

加入我们在 Redis 发布会

在 RedisTimeSeries 上构建您的金融应用程序

总的来说,世界上有两种类型的投资者基本面投资者在决定是否投资一家公司时,会关注基本面指标,例如公司的商业模式、收入、收益、现金流、当前估值、风险和增长前景。沃伦·巴菲特通过他的基本面研究发现了被低估的公司,从而成名并成为世界上最富有的人之一。

另一方面,技术投资者很少或根本不关注公司的基本面,而是将注意力集中在从数百个技术指标中推导出买入、卖出和持有信号。这些技术投资者每天、每周或每月进行交易。

我并不是要为任何一种投资风格辩护或反对。但基本面投资者和技术投资者都会研究、分析并根据数百甚至数千个数据源做出决策。

基本面投资者研究全球数千家公司和数百个行业的资产负债表、利润表、现金流量表、人口趋势、主题趋势和社会趋势。他们在这些数据的基础上构建财务模型或分析,以更好地了解公司的轨迹和公司的增长前景。

技术投资者每天或每小时都会查看数百个技术指标,以推导出投资信号。依赖技术指标的投资者必须处理跨多个时间框架的大量数据,以构建他们的交易策略。他们可能需要查询大量指标,并需要几乎即时的答案。他们可能需要快速调整他们的算法和交易策略,以适应快速变化的市场。Redis Enterprise 可以解决所有这些挑战。

最近,我写了一篇关于为什么Redis Enterprise 是金融行业必不可少的工具。在这篇博文中,我将展示如何使用RedisTimeSeries来存储、聚合和查询股票价格和技术指标。我将概述的原则也可以用于存储和查询来自公司的财务信息或基本面投资者使用的任何其他时间序列数据。

RedisTimeSeries 模块可以摄取和查询数百万个事件、样本和定价数据。RedisTimeSeries 最适合存储相关的时点值对,以帮助发现从物联网到医疗保健到金融等各种用例中的趋势。RedisTimeSeries 提供平均值、总和、最小值、最大值、标准差、范围等聚合,可帮助您轻松分析数据并做出决策。

使用 RedisTimeSeries 追踪股票价格

在这篇文章中,我将演示一个使用 RedisTimeSeries 来存储股票价格和技术指标的模型。我将介绍如何创建用于价格和指标的各种时间序列,展示如何在原始时间序列的基础上创建聚合,并演示如何使用各种 RedisTimeSeries 命令轻松摄取和查询大量时间序列。我还提供了 Python 示例代码,您可以将其用作您自己的用例的起点。

无论对错,许多人认为道琼斯工业平均指数 (DJIA) 是美国经济的风向标。我希望追踪所有 30 支道琼斯指数成分股的技术指标,以及这些股票的价格和交易量。我首先追踪道琼斯成分股高盛的股票价格和该股票的一个技术指标。我最喜欢的技术指标之一是相对强弱指数 (RSI)。RSI 是一个动量指标,用于衡量股票可能处于超买或超卖区域。当 RSI 接近或低于 30 时,股票可能被认为是超卖的,可能是一个买入机会。同样,当 RSI 上升到 70 以上时,它可能进入超买区域,预示着卖出时机。

高盛集团的相对强弱指数

(来源:SeekingAlpha.com)

在交易时间内,RSI 与任何其他技术指标一样,会随着基于高盛股票需求和供应的交易执行而变化。我们可以使用 RedisTimeSeries 模块来帮助回答一些关键问题:

  • 在每个指定的时间间隔内,技术指标的最小值和最大值是多少?
    • 这将使您能够快速发现交易的进场点或出场点。
  • 在指定的时间间隔内,股票价格的范围和标准差是多少?
    • 范围和标准差是基础股票价格波动性的指标。
  • 对于该股票的交易来说,哪些可能是有利可图的进场价和出场价?
  • 在指定的时间间隔内,所有股票的技术指标值是多少?

我们可以使用 RedisTimeSeries 查询以编程方式识别指定时间段内的最小 RSI 值和最大 RSI 值以及股票价格。例如,如果我想找到交易日每个 15 分钟期间的最小和最大 RSI 值?当 RSI 处于或接近 30 时,我可能希望生成一个警报,执行交易或启动另一个复杂的交易工作流来分析其他技术指标,然后再买入或卖出。您想做的任何事情都可以轻松地建模到 RedisTimeSeries 数据库中。

股票价格和技术指标的数据模型

获得 RedisTimeSeries 实践经验的最简单方法是运行RedisTimeSeries 的 Docker 镜像

执行以下命令以拉取并运行 Docker 镜像

docker run -p 6379:6379 -it --rm redis/redistimeseries

Redis 团队对 RedisTimeSeries 模块做出了一项深思熟虑的设计决策,即在每个时间序列中只包含一个指标。数据模型的这种简单性使得数据的插入和检索速度极快。您可以根据需要添加新的时间序列,而无需担心破坏现有应用程序。这意味着您可以轻松地添加新的数据源,而无需担心破坏数据库模式或您的应用程序。

一旦您启动并运行了 RedisTimeSeries 容器,您就可以使用 Python 连接到服务器(确保您拥有正确的 IP 地址或主机名),如下所示

from redistimeseries.client import Client 
rts = Client(host='127.0.0.1', port=6379)

根据在每个时间序列中只包含一个指标的设计原则,我们可以将每只道琼斯指数成分股的盘中股票价格和 RSI 存储在各自的时间序列中。下面,我创建了高盛集团的时间序列。我将高盛的盘中 RSI 命名为‘DAILYRSI:GS’,并且我在每个时间序列上应用了各种标签——对时间序列进行标签可以让您使用标签跨所有键进行查询。

rts.create('DAILYRSI:GS', 
            labels={ 'SYMBOL': 'GS'
                   , 'DESC':'RELATIVE_STRENGTH_INDEX'
                   , 'INDEX' :'DJIA'
                   , 'TIMEFRAME': '1_DAY'
                   , 'INDICATOR':'RSI'
                   , 'COMPANYNAME': 'GOLDMAN_SACHS_GROUP'})

在这里,我创建了高盛盘中股票价格的时间序列,命名为‘INTRADAYPRICES:GS’

rts.create('INTRADAYPRICES:GS', 
            labels={ 'SYMBOL': 'GS'
                   , 'DESC':'SHARE_PRICE'
                   , 'INDEX' :'DJIA'
                   , 'PRICETYPE':'INTRADAY'
                   , 'COMPANYNAME': 'GOLDMAN_SACHS_GROUP'})

接下来,我在 15 分钟的特定时间范围内创建了 RSI 数据的各种聚合。(我们可以根据我们的交易需求创建更长或更短的时间范围的聚合。)这些聚合将使我们能够查看 RSI 的第一个、最后一个、最小、最大和范围值。为了创建一个聚合,我们首先创建一个时间序列来存储聚合,然后创建一个规则来使用聚合值填充时间序列。在本例中,我们创建了一个名为‘DAILYRSI15MINRNG:GS’ 的时间序列来存储 15 分钟时间段内 RSI 的范围。‘createrule’ 对原始数据 (‘DAILYRSI:GS’) 在每个 15 分钟的时间范围内应用范围函数,并将这些数据聚合到‘DAILYRSI15MINRNG:GS’ 中。

rts.create('DAILYRSI15MINRNG:GS', 
            labels={ 'SYMBOL': 'GS' 
                   , 'DESC':'RELATIVE_STRENGTH_INDEX' 
                   , 'INDEX' :'DJIA' 
                   , 'TIMEFRAME': '15_MINUTES' 
                   , 'AGGREGATION': 'RANGE' 
                   , 'INDICATOR':'RSI' 
                   , 'COMPANYNAME': 'GOLDMAN_SACHS_GROUP'}) 
rts.createrule('DAILYRSI:GS', 'DAILYRSI15MINRNG:GS', 'range', 900)

在这里,我创建了几个规则,用于计算高盛股票价格在交易日每个 15 分钟间隔内的范围和标准差

rts.create('INTRADAYPRICES15MINRNG:GS'
          , labels={ 'SYMBOL': 'GS'
                   , 'DESC':'SHARE_PRICE'
                   , 'INDEX' :'DJIA'
                   , 'PRICETYPE':'RANGE'
                   , 'AGGREGATION': 'RANGE'
                   , 'DURATION':'15_MINUTES'
                   , 'COMPANYNAME': 'GOLDMAN_SACHS_GROUP'})

rts.createrule('INTRADAYPRICES:GS’
              ,'INTRADAYPRICES15MINRNG:GS'
              ,'range', 900)

rts.create('INTRADAYPRICES15MINSTDP:GS'
          , labels={ 'SYMBOL': 'GS'
                   , 'DESC':'SHARE_PRICE'
                   , 'INDEX' :'DJIA'
                   , 'PRICETYPE':'STDDEV'
                   , 'AGGREGATION': 'STDDEV'
                   , 'DURATION':'15_MINUTES'
                   , 'COMPANYNAME': 'GOLDMAN_SACHS_GROUP'})

rts.createrule( 'INTRADAYPRICES:GS'
              , 'INTRADAYPRICES15MINSTDP:GS'
              , 'std.p', 900)

您可以以类似的方式为道琼斯工业平均指数 (DJIA) 中的所有 30 支股票创建时间序列。

数据摄取方法

有两种方法可以将数据摄取到 RedisTimeSeries 中。您可以使用TS.ADD 命令将每个股票价格或技术指标添加到时间序列中。但是,由于来自金融市场的数据几乎是连续产生的,您需要将多个样本添加到 RedisTimeSeries,因此最好使用TS.MADD 方法。MADD 函数将元组列表作为参数。每个元组都包含时间序列键的名称、时间戳和值

您可以使用以下 Python 命令将数据插入时间序列键中

rts.madd(<RSIIndicatorList>)

查询 RedisTimeSeries

与 RedisTimeSeries 中的大多数事情一样,查询数据库也很容易。股票价格或技术指标的范围和标准差是波动性的一个指标。以下查询将使您能够查看高盛股票价格在每个 15 分钟间隔内的价格范围和标准差

rts.range( 'INTRADAYPRICES15MINRNG:GS' 
         , from_time = 1603704600 
         , to_time   = 1603713600)

此范围聚合查询将为您提供以下结果集

[(1603704600, 1.75999999999999), (1603705500, 0.775000000000006), (1603706400, 0.730000000000018), (1603707300, 0.449999999999989), (1603708200, 0.370000000000005), (1603709100, 1.01000000000002), (1603710000, 0.490000000000009), (1603710900, 0.89500000000001), (1603711800, 0.629999999999995), (1603712700, 0.490000000000009), (1603713600, 0.27000000000001)]

如该查询结果所示,在 2020 年 10 月 26 日美国东部时间上午 9:30(整数时间戳 = 1603704600)交易日开始时,高盛股票波动很大,交易范围为 1.75 美元。现在将此与接下来的 15 分钟进行比较,该时间段从 2020 年 10 月 26 日上午 9:45 开始(整数时间戳 = 1603705500),波动范围降至 0.77 美元。高盛股票的价格范围在随后的 15 分钟间隔内继续下降,波动性从未恢复到开盘 15 分钟间隔期间达到的水平。   

这些来自 RedisTimeSeries 的数据可以在仪表盘和图表中轻松可视化。下面的图表显示了高盛股票在 15 分钟间隔内的价格范围

下面的图表以折线图格式显示相同的数据,表明高盛股票交易的价格范围随着时间推移到一天中变得更窄(此图表在 x 轴上显示整数时间戳)

您可以使用另一种波动性度量(标准差)作为聚合函数来执行类似的查询

rts.range( 'INTRADAYPRICES15MINSTDP:GS' 
         , from_time = 1603704600 
         , to_time   = 1603713600)
[(1603704600, 0.54657783830434), (1603705500, 0.23201149395202), (1603706400, 0.196072381345986), (1603707300, 0.160267138157647), (1603708200, 0.116990621700049), (1603709100, 0.28043101744222), (1603710000, 0.144379900126934), (1603710900, 0.327611558618967), (1603711800, 0.163118915546951), (1603712700, 0.151417199675549), (1603713600, 0.0963889432843203)]

这打开了许多有趣的机会。假设您想知道高盛股票在日内 RSI 值介于 30 到 40 之间时的价格。您可以查询 RSI 的时间序列和股票价格的时间序列,以识别交易的有利可图的切入点。   

在这里,我正在查询 2020 年 11 月 13 日美国东部时间上午 9:35(1605260100)到 2020 年 11 月 13 日美国东部时间上午 9:49(1605260940)之间的时间范围内高盛的 RSI 值。

dailyGSRSIValue = rts.range( 'DAILYRSI:GS'
                            , from_time = 1605260100
                            , to_time   = 1605260940)

查询发现,在 1605260820(美国东部时间上午 9:47)时,RSI 值为 34.2996427544861。

[(1605260100, 75.0305441024708), (1605260160, 81.6673948350152), (1605260220, 83.8852225932517), (1605260280, 85.9469082344746), (1605260340, 94.3803586011592), (1605260400, 92.2412262502652), (1605260460, 85.6867329371465), (1605260520, 87.9557361513823), (1605260580, 89.9407873066781), (1605260640, 57.1512452602676), (1605260700, 50.5638232111769), (1605260760, 35.2804436894564), (1605260820, 34.2996427544861), (1605260880, 54.5486275202972), (1605260940, 64.7593307385219)]

现在,我可以查询与用于 RSI 查询的相同间隔的高盛日内价格,或者以编程方式更改查询以反映 RSI 值为 34.29 的时间。以下是一个使用与 RSI 查询相同时间范围的示例。

dailyGSPrice = rts.range( 'INTRADAYPRICES:GS'
                         , from_time = 1605260100
                         , to_time   = 1605260940)

该查询返回指定范围内的高盛股票价格:在 1605260820(2020 年 11 月 13 日美国东部时间上午 9:47)时,价格为 217.18 美元。   

[(1605260100, 216.57), (1605260160, 216.73), (1605260220, 217.08), (1605260280, 217.17), (1605260340, 217.87), (1605260400, 218.05), (1605260460, 217.91), (1605260520, 218.0), (1605260580, 218.11), (1605260640, 218.02), (1605260700, 217.72), (1605260760, 217.22), (1605260820, 217.18), (1605260880, 217.46), (1605260940, 217.61)]

RedisTimeSeries 提供了一种强大的方法,可以同时跨多个时间序列进行查询。 TS.MGET 命令允许您使用过滤器跨多个时间序列进行查询。我们已经创建了各种时间序列并向其附加了标签。现在,这些标签可以充当跨时间序列查询的过滤器。 

以下 Python 代码基于两个标签应用了两个过滤器:“DESC”和“TIMEFRAME”。参数 “with_labels=False” 允许结果集在不包含每个值的标签的情况下返回

allRSIValues = rts.mget(filters=['DESC=RELATIVE_STRENGTH_INDEX','TIMEFRAME=1_DAY'], with_labels=False)

此查询将呈现类似于此处显示的结果,该结果返回许多股票的最后 RSI 值

[{'DAILYRSI:BA': [{}, 1605261060, 62.2048922111768]}, {'DAILYRSI:CAT': [{}, 1605261060, 68.3834400302296]}, {'DAILYRSI:CRM': [{}, 1605261060, 59.2107333830133]}, {'DAILYRSI:CSCO': [{}, 1605261060, 52.7011052724688]}, {'DAILYRSI:CVX': [{}, 1605261060, 62.9890368832232]}, {'DAILYRSI:DOW': [{}, 1605261060, 73.597680480764]}, {'DAILYRSI:GS': [{}, 1605283140, 41.182852552541]}, {'DAILYRSI:IBM': [{}, 1605261060, 65.3742140862697]}, {'DAILYRSI:JPM': [{}, 1605261060, 77.7760292843745]}, {'DAILYRSI:KO': [{}, 1605261060, 26.8638381005608]}, {'DAILYRSI:MMM': [{}, 1605261060, 65.7852833683174]}, {'DAILYRSI:MRK': [{}, 1605261060, 38.9991886598036]}, {'DAILYRSI:UNH': [{}, 1605261060, 74.2672428885775]}, {'DAILYRSI:VZ': [{}, 1605261060, 33.177554436462]}, {'DAILYRSI:WBA': [{}, 1605261060, 47.3877762365391]}]

如果我设置了 “with_labels=True”,那么结果将包含每个时间序列上的所有标签,如这里所示

[{'DAILYRSI:BA': [{'SYMBOL': 'BA', 'DESC': 'RELATIVE_STRENGTH_INDEX', 'INDEX': 'DJIA', 'TIMEFRAME': '1_DAY', 'INDICATOR': 'RSI', 'COMPANYNAME': 'BOEING'}, 1605261060, 62.2048922111768]}, {'DAILYRSI:CAT': [{'SYMBOL': 'CAT', 'DESC': 'RELATIVE_STRENGTH_INDEX', 'INDEX': 'DJIA', 'TIMEFRAME': '1_DAY', 'INDICATOR': 'RSI', 'COMPANYNAME': 'CATERPILLAR'}, 1605261060, 68.3834400302296]}, {'DAILYRSI:CRM': [{'SYMBOL': 'CRM', 'DESC': 'RELATIVE_STRENGTH_INDEX', 'INDEX': 'DJIA', 'TIMEFRAME': '1_DAY', 'INDICATOR': 'RSI', 'COMPANYNAME': 'SALESFORCE'}, 1605261060, 59.2107333830133]}]

结论 

这篇博文说明了 RedisTimeSeries 的许多命令中的一些,这些命令可以灵活地构建一个依赖于时间序列数据的金融应用程序。例如,股票交易者需要能够根据数十个变量实时做出高风险决策。RedisTimeSeries 无模式,这意味着您可以在不定义模式的情况下加载数据,随时添加新字段,或者在业务情况发生变化时更改数据模型。它的实时性能和简单的开发人员体验使其在处理时间序列数据时充满乐趣!

您可以在 GitHub 上找到这篇博文的示例代码。  您还可以在此处了解更多有关 RedisTimeSeries 的信息