共计 7306 个字符,预计需要花费 19 分钟才能阅读完成。
本文是一个由多局部组成的系列文章的第二篇,该系列文章展现了 FlinkSQL 利用于市场数据的性能和可表白性。万一您错过了它,第一局部从计算流 VWAP 的简略状况开始。该系列的代码和数据可在 github 上取得。
速度在金融市场上至关重要。无论指标是最大化 alpha 还是最大水平地缩小危险,金融技术人员都会投入大量资金,以获取无关市场情况以及行情的最新见解。事件驱动和流式解决体系结构可在事件产生时对事件进行简单的解决,使其很天然地适宜金融市场利用。
Flink SQL 是一种数据处理语言,可用于事件驱动和流应用程序的疾速原型设计和开发。Flink SQL 将 SQL 的简略性和可拜访性与 Apache Flink(一种风行的分布式流媒体平台)的性能和可伸缩性联合在一起。借助 Flink SQL,业务剖析人员、开发人员和量化人员都能够疾速建设流传输管道,以实时执行简单的数据分析。
在本文中,咱们将应用 Simudyne 开发的基于代理的模型(ABM)生成的综合市场数据。ABM 并不是自上而下的办法,而是在简单零碎中对自主参与者(或代理)进行建模,例如,金融市场中的各种买卖双方。能够捕捉这些交互,并能够针对许多应用程序剖析生成的综合数据集,例如用于检测紧急欺诈行为的培训模型,或摸索风险管理的“假如”场景。ABM 生成的综合数据在历史数据有余或不可用的状况下很有用。
1 盘中 VaR
危险价值(VaR)是风险管理中宽泛应用的指标。它有助于辨认危险敞口,为交易前的决策提供根据,并报告给监管机构进行压力测试。在给定的置信度和工夫范畴内,VaR 将危险示意为货币金额,批示资产将来可能产生的最坏损失。例如,AAPL 的 1 天 10 美元的 99%VaR 示意 100 的 99 倍,第二天 AAPL 的损失不会超过 10 美元。
计算股票的 VaR 的一种常见办法是获取历史日末收益(例如最近 500 个交易日的每日收盘价变动)并将其视为可能的将来收益的散布。VaR 是第 99 个百分位数(或 500 天中第 5 个最差回报率)的最差每日收益乘以以后资产值。假如收益率遵从正态分布,则计算 VaR 的另一种办法是将标准偏差乘以与所需置信区间绝对应的 z 分数,在本例中,均值的 99%置信区间为 -2.58。将后果数字加到均匀收益上,而后乘以以后资产价值即可得出 VaR。
图片起源:https://spot.pcc.edu/~evega/ConfidenceIntervals.html
在大多数市场危险利用中,VaR 公式是基于日末定价并每天分批计算的。自从 JP Morgan 在 1980 年代创造 VaR 以来,这种做法在风险管理中很广泛。从那时起,钻研人员提出了用于计算日内 VaR 的办法[1],该办法受古代市场一直倒退的构造和动静驱动:
在过来的几年中,交易速度始终在一直进步。即日交易,当初是场内交易者的专属区域,当初所有投资者都能够应用。“高频金融对冲基金”曾经成为对冲基金的一个胜利的新类别。因而,风险管理当初必须与市场放弃同步。对于日间交易者,做市商或市场上其余沉闷的经纪人,应以短于每日的工夫距离评估危险,因为他们的投资期限通常少于一天。
本文中,咱们探讨了如何应用流式 SQL 从实时报价数据流中计算日内 VaR(IVaR)。具体来说,咱们将依据前 5 分钟的定价数据,每秒计算出 99%的 IVaR。在本练习中,咱们将应用 Simudyne 生成的综合市场数据。他们为咱们提供了 CSV 格局的 1 级刻度数据,以实现虚构安全性(“SIMUl”):
time,sym,best_bid_prc,best_bid_vol,tot_bid_vol,num,sym,best_ask_prc,best_ask_vol,tot_ask_vol,num
2020-10-22 08:00:00.000,SIMUl,149.34,2501,17180,1,SIMUl,150.26,2501,17026,1
2020-10-22 08:00:01.020,SIMUl,149.34,2901,17580,2,SIMUl,150.26,2501,17026,1
2020-10-22 08:00:02.980,SIMUl,149.36,3981,21561,1,SIMUl,150.26,2501,17026,1
2020-10-22 08:00:05.000,SIMUl,149.36,3981,21561,1,SIMUl,149.86,2300,19326,1
2020-10-22 08:00:05.460,SIMUl,149.36,3981,21561,1,SIMUl,149.86,6279,23305,2
2020-10-22 08:00:05.580,SIMUl,149.36,3981,21561,1,SIMUl,149.86,6279,23305,2
2020-10-22 08:00:06.680,SIMUl,149.36,3981,21561,1,SIMUl,149.86,6279,23305,2
2020-10-22 08:00:07.140,SIMUl,149.74,582,22143,1,SIMUl,149.86,6279,23305,2
2020-10-22 08:00:07.600,SIMUl,149.74,582,22143,1,SIMUl,149.86,2044,19070,1
2020-10-22 08:00:08.540,SIMUl,149.74,582,22143,1,SIMUl,149.86,2044,19070,1
级别 1 的报价数据在给定的即时工夫内传播了证券交易簿中的最佳买入价和最佳卖出价。咱们次要关注交易种类和工夫戳,以及市场中间价,咱们能够通过均匀最佳买入价和要价来获取中间价。为了使 Flink SQL 解决此数据,咱们首先通过以下语句申明一个流表:
event_time TIMESTAMP(3),
symbol STRING,
best_bid_prc DOUBLE,
best_bid_vol INT,
tot_bid_vol INT,
num INT,
sym2 STRING,
best_ask_prc DOUBLE,
best_ask_vol INT,
tot_ask_vol INT,
num2 INT,
WATERMARK FOR event_time AS event_time - INTERVAL '5' SECONDS
) WITH (
'connector' = 'filesystem',
'path' = '/path/to/varstream/data/l1_raw',
'format' = 'csv'
) ;
2 Flink SQL 中的工夫序列采样
为了计算 IVaR,咱们须要在过来 5 分钟内调配每秒回报(两头价格与前一秒的变动百分比)。如果咱们将 L1 数据视为一个工夫序列,则须要每秒采样一次两头价格。实现此目标的一种办法是向前填充:每秒采样的两头价格是该秒之前或该秒之前的最初察看到的两头价格。
本能地,咱们能够尝试应用翻滚窗口来执行此操作,就像咱们在第一局部中计算 VWAP 所做的那样。然而,此办法将不起作用。思考上面的滚动窗口查问:
SELECT
symbol,
TUMBLE_START (event_time, INTERVAL '1' SECOND) AS start_time,
TUMBLE_ROWTIME (event_time, INTERVAL '1' SECOND) AS row_time,
LAST_VALUE (best_bid_prc) AS best_bid_prc,
LAST_VALUE (best_ask_prc) AS best_ask_prc
FROM
l1
GROUP BY
TUMBLE (event_time, INTERVAL '1' SECOND), symbol
LIMIT 20
滚动窗口可能会导致间隙,如下所示。
该查问在 8 : 00: 03,8 : 00 :04 和 8:00:13 没有产生任何记录。这是因为在源 L1 数据中,在第二个工夫距离内没有事件。潜在地,能够通过应用跳跃窗口来解决此问题,并具备足够的回溯期以确保在此期间察看到一个事件:
SELECT
symbol,
HOP_START (event_time, INTERVAL '1' SECOND, INTERVAL '120' SECONDS) AS start_time,
HOP_ROWTIME (event_time, INTERVAL '1' SECOND, INTERVAL '120' SECONDS) AS row_time,
LAST_VALUE (best_bid_prc) AS best_bid_prc,
LAST_VALUE (best_ask_prc) AS best_ask_prc
FROM
l1
GROUP BY
HOP (event_time, INTERVAL '1' SECOND, INTERVAL '120' SECONDS), symbol
LIMIT 20
可怜的是,上述查问无奈运行,因为在编写本文时,LAST_VALUE 函数不适用于跳跃窗口。Flink 社区正在致力于修复(FLINK-20110)。同时,咱们提出了一种不依赖于跳变窗口或回溯期的解决办法。
首先,咱们得出每行的无效工夫范畴(开始和完结工夫):
CREATE VIEW l1_times AS
SELECT
symbol,
MIN (event_time) OVER w AS start_time,
CAST (event_time AS TIMESTAMP) AS end_time,
FIRST
FIRST_VALUE (best_ask_prc) OVER w AS ask_price
FROM l1
WINDOW w AS (
PARTITION BY symbol
ORDER BY event_time
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW
)
;
* 请留神应用 MIN(event_time)而不是 FIRST_VALUE(event_time)- 以后,FIRST_VALUE 函数不反对 TIMESTAMP 类型。
该视图在保留前一行的同时流式传输数据,并收回前一行的字段值以及以后行的 event_time 作为无效完结工夫。针对该视图的查问将产生以下内容,该结果显示每行(第一行除外)当初具备蕴含的开始工夫和排除的完结工夫。
为了每秒收回一行,咱们编写了一组用户定义的表函数(UDTF)。您能够在此处查看代码。该我的项目提供了无关如何构建二进制文件(.jar 文件)以及如何将其与 Flink SQL 一起应用的简要阐明。您须要收回 CREATE FUNCTION 语句来注册每个 UDTF,而后能力在查问中应用它们:
CREATE FUNCTION fill_sample_per_day AS 'varstream.FillSample$PerDayFunction' LANGUAGE JAVA ;
CREATE FUNCTION fill_sample_per_hour AS 'varstream.FillSample$PerHourFunction' LANGUAGE JAVA ;
CREATE FUNCTION fill_sample_per_minute AS 'varstream.FillSample$PerMinuteFunction' LANGUAGE JAVA ;
CREATE FUNCTION fill_sample_per_second AS 'varstream.FillSample$PerSecondFunction' LANGUAGE JAVA ;
在查问中,UDTF 具备以下语法:
fill_sample_by_timeunit (start_time, end_time, frequency)
工夫单位能够是日、小时、分钟或秒。开始工夫和排他性完结工夫标记每行的无效工夫,频率批示给定的天、小时、分钟或秒采样次数。因而,频率为 6 的 fill_sample_by_hour 将每 10 分钟采样一次(:00,:10,:20,:30,:40 和:50)。调用 fill_sample_by_minute 具备 60 的频率是性能上雷同 fill_sample_by_second 一个的频率。然而,因为 UDTF 实现的外部起因,by_second 变体的性能会更好。
当初,咱们能够创立一个每秒对流进行采样的视图。留神应用的 INNER JOIN LATERAL TABLE,这确保了所发射的即将由 UDTF 输入进行管制:
CREATE VIEW l1_sample AS
SELECT
symbol,
start_time,
end_time,
sample_time,
bid_price,
ask_price,
(bid_price + ask_price) / 2 AS mid_price
FROM l1_times AS l1
INNER JOIN LATERAL TABLE (fill_sample_per_minute (l1.start_time, l1.end_time, 60))
AS T(sample_time) ON TRUE
;
SELECT symbol, start_time, end_time, sample_time, mid_price FROM l1_sample ;
查问此视图将产生以下后果。
3 计算流内盘中 VaR
当初咱们有一个以秒为两头值采样的工夫序列,咱们能够开始计算流 IVaR 了。首先,咱们须要计算每秒的回报,这就是以后价格减去之前的价格。为了得出先前的价格,咱们再次应用 OVER WINDOW 语法:
CREATE VIEW l1_sample_prev AS
SELECT
symbol,
start_time,
sample_time,
mid_price,
FIRST_VALUE (mid_price) OVER w AS prev_price
FROM l1_sample
WINDOW w AS (
PARTITION BY symbol
ORDER BY start_time
ROWS BETWEEN 1 PRECEDING AND CURRENT ROW
)
;
为了取得第 99 个百分位数的回报,咱们计算了过来 300 行的回溯窗口中的回报(以百分比示意),这是因为咱们每秒采样的工夫为 5 分钟。咱们还计算了同一窗口的均匀收益率和标准差。
CREATE VIEW l1_stddev AS
SELECT
symbol,
start_time,
sample_time,
mid_price,
(mid_price - prev_price) / prev_price AS pct_return,
AVG (mid_price) OVER lookback AS avg_price,
AVG ((mid_price - prev_price) / prev_price) OVER lookback AS avg_return,
STDDEV_POP ((mid_price - prev_price) / prev_price) OVER lookback AS stddev_return
FROM l1_sample_prev
WINDOW lookback AS (
PARTITION BY symbol
ORDER BY start_time
ROWS BETWEEN 300 PRECEDING AND CURRENT ROW
)
;
有了这些信息,咱们能够通过将标准偏差与 -2.58 的 Z 得分相乘并将该数字加到均匀收益中来得出第 99 个百分位数的最差收益。这在上面显示为 var99_return。处于危险中的理论价值是以后两头价格乘以 var99_return。在上面的查问中,咱们心愿显示该资产的 99%可能的最差将来价格,因而咱们将以后价格(mid_price)乘以 1 + var99_return。
CREATE VIEW l1_var99 AS
SELECT
*,
avg_return - 2.58 * stddev_return AS var99_return,
mid_price * (1 + (avg_return - 2.58 * stddev_return)) AS var99_price
FROM
l1_stddev
;
SELECT symbol, sample_time, mid_price, var99_price FROM l1_var99
4 论断
随着高频交易和小型交易解体变得越来越广泛 [2],理解盘中市场危险可能会像了解一个人的盘中危险一样无益,尤其是在应用高频算法时。侥幸的是,借助像 Flink 这样的古代流媒体平台,以及像 Flink SQL 这样的易于应用的流编程语言,咱们能够疾速构建强壮的管道,以在市场数据实时达到时计算日内危险度量。
咱们心愿本系列文章能激励您尝试将 Flink SQL 用于流式市场数据应用程序。在下一部分中,咱们将向您展现如何应用行将公布的 Cloudera SQL Stream Builder 版本(Cloudera Streaming Analytics 1.4 版的一部分)尝试这些示例。
感激 Tim Spann,Felicity Liu,Jiyan Babaie-Harmon,Roger Teoh,Justin Lyon 和 Richard Harmon 对这项工作的奉献。
5 引文
[1] Dionne,Georges 和 Duchesne,Pierre 和 Pacurar,Maria,应用逐笔交易数据并将其利用于多伦多证券交易所,其当日危险值(Ivar)(2005 年 12 月 13 日)。在 SSRN 上可用:https ://ssrn.com/abstract=868594 或 http://dx.doi.org/10.2139/ssr…
[2] Bayraktar,Erhan 和 Munk,Alexander,Mini-Flash Crash,模型危险和最佳执行力(2017 年 5 月 27 日)。在 SSRN 上可用:https ://ssrn.com/abstract=2975769 或 http://dx.doi.org/10.2139/ssr…
原文作者:Patrick Angeles& Krishnen Vytelingum
原文链接:https://blog.cloudera.com/streaming-market-data-with-flink-sql-part-ii-intraday-value-at-risk/
关注微信公共号理解更多信息:
本文由 mdnice 多平台公布