30 秒看懂
Jane Street 工程师 Ian Henry 与 Ron Minsky 讨论他从消费应用转向低延迟类交易所系统和期权交易员工具的经历。他们探讨了专家界面设计、紧密的用户反馈、共享 OCaml 类型和 Bonsai 界面框架。 Henry 还介绍了基于符号距离函数的三维创作工具 Bauble,以及他从 Janet 和个人项目中获得的经验。最后,两人实际讨论了 expect 测试及其在交互式开发中的用途。
- 1期权交易员界面团队构建工具来探索市场,并在大量期权中引导用户关注相关事件。
- 2紧密的用户反馈循环工程师坐在交易员附近,可以观察工具的实际使用,但必须学习足够的领域知识才能设计有用的功能。
- 3全栈使用同一种语言共享的 OCaml 类型和生成的协议使数据与计算更容易在客户端和服务器之间转移。
- 4增量界面计算Bonsai 显式建模不断变化的计算,使团队能控制市场数据变化时重复执行的工作量。
- 5Bauble 与三维形状Henry 的创意编程环境使用符号距离函数,将高层形状描述编译成 GPU 着色器。
- 6用 expect 测试构建反馈循环测试将可观察的输出与代码保存在一起,并在以后运行时比较,便于检查行为及其变化。
要点建议
- 面向专家的工具可以优先考虑信息密度和键盘操作速度,同时投入培训和文档。
- 与用户紧密合作能加快反馈,但构建有用的工具也需要理解用户的专业领域。
- 内部软件生态中的共享类型和生成的协议能减少客户端与服务器之间的重复工作。
- Bonsai 使用显式的增量计算来管理频繁变化的界面状态。
- 符号距离函数能高效组合和渲染三维形状,但一些变换只能得到近似结果。
- Expect 测试使程序输出可检查、可重复,前提是开发者选择清晰且有意义的观察结果。
按章节阅读整理后的全文
从消费应用到低延迟系统
Henry 讲述他从 Trello 和函数式编程来到 Jane Street 的经历。他与 Minsky 解释类交易所系统和 OCaml 支持的多种编程风格。
Ron Minsky 介绍 Jane Street 软件工程师 Ian Henry。Henry 曾在 Trello 开发 iOS 应用和 Web 前端,也曾在 Warby Parker 开发 iOS,于 2016 年加入 Jane Street。他先做内部交易所软件,后来转向为期权交易台的交易员开发工具。
Henry 说,Jane Street 在函数式编程方面的名声最初吸引了他。大约在 2013 年,ReactiveCocoa 和函数响应式编程让复杂交互式 iOS 界面的构建变得更容易。后来他通过音乐创意编程环境 Overtone 探索了 Clojure。
音乐软件接入编程语言后,Henry 可以把 MIDI 键盘连接到网络请求,也可以让 Slack 机器人触发合成器。这些探索让他接触了 Haskell,并开始思考什么是 monad。他原本没有想到自己会在 Jane Street 工作,因为公司的名声让他觉得望而生畏。
Minsky 说,Jane Street 每个人都需要博士学位的说法其实从未真正成立,虽然一些研究 OCaml 和类型理论的人有博士学位。Henry 补充,公司也喜欢招聘工程师。谈到音乐时,他说自己对工具和声音合成理论的兴趣超过了实际创作音乐。
Henry 在 Jane Street 的第一份工作与 iOS 截然不同:低延迟软件以微秒而不是毫秒计时。若延迟达到一毫秒,就意味着出了非常严重的问题。这个系统看起来像交易所,实际上是单一交易商平台。
交易对手可以使用类似交易所的协议下单,但他们是与 Jane Street 交易,而不是彼此交易。软件仍然有订单簿、撮合引擎和公开报告已完成交易的方式,因此很多实现类似交易所。
虽然 Henry 是冲着函数式编程来的,性能关键的核心部分却高度命令式。他说,OCaml 类型系统的功能有助于让这种风格保持安全。围绕这个紧密循环,团队可以用同一种语言和类型编写更高层的测试及辅助代码,而不必跨到单独的 C 接口。
Minsky 称 OCaml 是一门适用范围很广的语言:它在统一的生态中支持低延迟代码、测试、Web 编程和硬件综合。Henry 随后转到 Trader Tools,帮助手动交易期权的交易员了解市场、配置自动化系统并发送订单。
为期权交易员设计工具
团队为市场探索、提醒和工作流构建工具。与专家用户密切接触能带来快速反馈,同时也要求工程师具备领域知识。
期权带来大量信息需要处理。关注 Nvidia 股票的交易员可能只需要跟踪一个代码,而期权交易员需要考虑数百种行权价与到期日组合。期权赋予持有人按照条款以指定价格买入或卖出的权利;到期日决定这一权利何时终止。
Minsky 解释,尽管存在许多单独的衍生品,交易员关心的是波动率和未来价格分布等基础属性。Henry 说,以这些宏观概念思考,与通过许多单独的订单簿表达观点之间存在落差,软件有助于弥合这一差距。
多年来,交易台大量依赖 Excel、Bash 和定制的 curses 应用等工具。较新的界面大致分两类:探索工具聚合数据并绘制市场走势;注意力管理工具则提醒交易员关注有趣的事件或机会。
把合适的事件发送给合适的人也是工作流问题:不同个人和团队负责不同事务。Henry 说,这并不是光鲜的技术问题,却出乎意料地困难,而且非常重要。
Minsky 问,这与为大众打造 Trello 有什么不同。Henry 说,交易员就坐在附近,工程师可以观察他们使用工具、讨论需求、发布修改,然后第二天就看到实际使用情况。这种紧密循环很有价值,但用户群虽小,需求却高度专业化。
工程师不是交易员,必须学习足够的领域知识,才能理解用户想完成什么。Minsky 指出,用户请求的功能可能源于近期一个狭窄的问题,或者基于对实现难度的假设。设计者必须找到更简单、能满足更广泛需求的方案。
Henry 说,交易员很有耐心,也希望工程师理解他们的问题。内部教育资源能提供帮助,兼具交易经验和设计直觉的资深人士也很重要。这种关系是合作,而不是把需求简单地扔过一道墙。
为专家设计会改变一般的界面选择。交易员可能要求只有 6 像素高的文字,以便屏幕容纳更多信息;他们也偏好键盘操作,而不是伸手拿鼠标。如此密集的消费应用可能难以阅读,但这里的用户整天使用这些工具。
键盘优先的界面可能让功能更难被发现。Henry 说,人们往往从同事和帮助文档中学习,而不是依靠教程。Minsky 指出,Web 界面可以加入提示和指向可编辑维基页面的链接;随着组织扩大,让工具本身教会用户就更有价值。
不同的内部软件生态
内部应用不必处理消费产品的某些问题,客户端和服务器可以共享 OCaml 类型及生成的协议。两位讲者讨论了这一生态的取舍。
内部软件也省去了许多消费互联网产品需要处理的工作:注册、忘记密码、支付和公开分发。Henry 说,团队可以专注于应用逻辑,快速的内部连接也让较大的 JavaScript 下载不那么成问题。
由于支持性基础设施随手可用,团队可以创建许多独立的 Web 应用,而不必把所有功能塞进一个产品。Henry 也不再像以前那样花很多时间追赶依赖更新。Minsky 指出取舍:Jane Street 建立了独特的内部生态,与外部软件包的集成相对较少。
Minsky 说,公司确实会引入有用的库,但希望对稳定性和安全性保有更强的控制,而不是不加选择地导入软件包。Henry 描述了一个全栈 OCaml 环境:客户端和服务器共享类型,生成的序列化器在两者之间建立稳定协议。
工程师不必定义 JSON API、另写解析器,再祈祷两边的类型始终兼容,而是可以复用相同的类型和逻辑。他们可以把计算从服务器移到客户端,分散给用户设备,也可以在服务器资源更合适时反向迁移。
团队将 OCaml RPC 生态带进了浏览器。这些应用不依赖 REST 或 GraphQL,而是通过包括 WebSocket 在内的流式 RPC 连接运行生成的协议。这加快了内部功能开发,不过 Henry 说,支持外部惯例会带来不同的挑战。
版本兼容性仍然重要,但控制内部客户端意味着团队可以在必要时要求用户刷新浏览器。Minsky 指出,HTTP 并不是唯一可用的网络协议。Henry 补充,类交易所系统使用 UDP 多播,说明合适的传输方式取决于具体任务。
使用 OCaml 和 Bonsai 开发前端
和类型与模式匹配使应用状态更加明确。Bonsai 使用增量计算和带类型的依赖来管理变化中的界面。
前端还使用 Jane Street 自己的框架 Bonsai。Minsky 说,界面工作有时被轻视,尽管它既重要又非常困难。管理状态、拖放等交互以及不断变化的 DOM,都需要仔细建模。
Henry 指出,团队可以避开公共前端开发中一些棘手问题:他们只需面向受控的浏览器版本,而不必支持所有浏览器和布局差异。即便如此,还没有框架彻底解决界面设计问题,Bonsai 体现的是团队自己的方法。
OCaml 的一个优势是类型系统。前端的值可能正在加载、已经可用或加载失败;和类型可以明确表示这些选项。模式匹配随后帮助程序员考虑每一种情况,而不是悄悄假定值一定存在。
Minsky 说,分类讨论遍布软件开发,穷尽式模式匹配有助于避免遗漏或混淆情况。Henry 举了一个实际例子:字符串加载失败时,OCaml 会让开发者决定显示什么,而不是忘记失败路径。
Minsky 将其视为明确程度上的取舍。许多语言隐式允许 null,开发者必须记住它何时可能出现。更明确的类型系统要求他们写出并处理这种可能性,额外的步骤可以预防某些类别的错误。
Henry 说,熟悉 React 的人会觉得 Bonsai 有些相似,因为它使用虚拟 DOM 和差异比较。但其底层模型是更通用的带状态增量计算图。虚拟 DOM 节点是图中的值,框架将它们连接到浏览器更新。
Minsky 说,市场数据不断变化,因此团队不仅需要避免重新计算整个页面,还要避免重新执行其背后昂贵的转换。Bonsai 让开发者表达各部分依赖哪些输入,并选择重新计算和渲染的粒度。
Henry 提醒,他使用 React 已是很久以前,所以比较可能忽略了细节。据他描述,React hooks 通过协调过程将状态与组件关联,并对何时可以调用 hooks 设有规则。Bonsai 则在类型中明确表示变化的值及其依赖。
Bonsai 计算会声明自己的输入;如果没有注册某个输入,就不能直接在另一侧使用它。出错时会得到类型错误。Minsky 将这种明确的约束比作 Rust 的内存管理方式,但 Henry 说,这个类比不应让人以为两者对表达能力的限制相同。
Henry 说,Bonsai 要求编写更多明确的代码,但团队可以表达自己需要的内容。例如,渲染动态集合时必须提供键,而不是等程序员忘记后仅给出警告。类型系统强制执行了其他框架开发者同样需要遵守的约束。
学习领域知识与改进提醒
Henry 解释了期权工具需要多广泛的知识,以及团队如何在错过机会与提醒疲劳之间取得平衡。配置必须让整个交易台都能管理。
Minsky 再次谈到工程师与交易员之间的知识差距。Henry 说学习期权让人意识到自己的不足:内部教育很强,但工具团队需要广泛理解许多领域,而不是像定价或隐含波动率团队那样深入专精。
Henry 说,他能充分理解各个部分来构建工具,但遇到具体的提前行权决策时,仍需要仔细推导数学。广泛的知识有助于连接交易台的许多环节,但并不会让他成为期权交易员。
最近一项改进涉及有趣市场事件的提醒。团队既要避免错过机会,也要避免不断打断用户。他们可以加入基于时间的规则、提高重复事件的提醒门槛,或考虑此前已经报告过的内容。
改进提醒是全栈工作:工程师可能需要新的市场数据和启发式方法,估计事件是否异常,然后请交易员试用结果。交易员可以调整参数,但目标不是让他们自己解决整个提醒问题。
Minsky 提到提醒疲劳:交易员承受的不便可能比他们抱怨的多,即便这已经影响工作效率。Henry 说,可配置的控制项在特别繁忙、正常阈值不再合适的日子里很有帮助,让人们可以当天调整。
一些系统根据告知交易员某个事件的预估价值来排序。交易台可以提高总体门槛,也可以针对某个资产类别调整。设置通常会逐渐稳定,再通过相对调整应对当前情况。
每个用户的设置过多又会造成另一个问题:同事实际上可能用着不同的工具,新人也可能继承不佳的默认值。Henry 说,团队正在减少个人调节项,转而采用更多交易台级配置,因为一个交易员关心的问题通常也是其他人的共同关切。
Bauble 与符号距离函数
Henry 介绍用于渲染三维形状的创意编程环境。可组合的距离函数能生成有趣的形状,但近似计算和光线步进有其限制。
话题转向 Henry 的个人项目 Bauble,一个用于三维图像和动画的实时编程环境。一段短程序会被编译成 OpenGL 着色器,也就是对图像像素求值的函数。GPU 可以并行执行这些计算,交互式地渲染复杂图像。
直接编写 GLSL 比较底层,因此 Henry 想要一种简洁的语言,让自己能够描述一个球体,把它与立方体组合,然后得到渲染结果。Bauble 将这些高层表达式编译成大得多的着色器,并使用符号距离函数建模形状。
符号距离函数接收三维空间中的一个点,返回它到表面的距离,符号表示该点位于表面的哪一侧。对于球体,基本计算是点到球心的距离减去半径。
这些函数可以组合。在同一点对两个形状求值并组合其结果,可以生成中间形状或平滑融合效果。变换输入坐标可以扭曲一个立方体。Henry 将其与三角网格对比:网格要平滑连接表面需要额外的几何结构。
Minsky 观察到,网格要求较早选择分辨率,而且在交叉处可能产生瑕疵。函数则在采样之前表示理想化的球体。Henry 说,如果需要用于 3D 打印或导出,之后可以将它转换成三角网格。
Minsky 质疑任意变换的结果是否仍是真正的符号距离函数。Henry 同意,许多有趣的组合只能近似表示距离。这个名称有助于查找相关文献,但这一不变量并不能在每种操作中保持。
距离信息能提高光线步进的效率。如果最近的表面距离 60 个单位,光线就可以一步跳过这么多空白空间。采用近似值时,一步可能跨过表面或进入形状,因此创作者必须考虑渲染瑕疵,或使用更慢的缓解方法。
近似值也可能产生有趣的视觉效果。沿 Bézier 曲线挤出一个形状,单独渲染可能很好,但再与其他操作组合时就可能不准确。Henry 说,了解哪些组合有效,是玩这种媒介的一部分。
Minsky 称赞 Bauble 的界面很精致,并问 Henry 如何找到时间构建它。Henry 感谢了其他人开发的技术,包括 Inigo Quilez 和 ShaderToy 社区的工作,以及用于编辑器的 CodeMirror。他自己在编译器和交互控制上投入了大量精力。
例如,用户可以在编辑器中拖动数字,看着图像变化,从而更容易选择颜色或位置。Henry 估计总共投入了约五个月的认真工作,其中很多是在育儿假期间趁孩子午睡完成的。
乐器、Janet 和个人项目
Henry 谈到自制乐器,以及他为 Bauble 选择 Janet 的原因。宏和基于镜像的执行方式拓宽了他对编程的理解。
Henry 还添加了供 3D 打印使用的网格导出功能,并制作了电子乐器和机械键盘。他喜欢连接微控制器和按钮,制作不同寻常的 MIDI 乐器,尽管他实际上不怎么演奏。其中一个实验用按键组合来产生音符。
电子乐器允许采用非常规控制方式,例如用一个按钮将音符升高半音,而不是选择固定音高。Henry 开玩笑说,对他而言,设计乐器以及设计 Bauble 本身,往往比演奏音乐或用它们创作艺术更有趣。
Bauble 使用小型 Lisp 语言 Janet。Henry 希望用简洁的记法和可变参数函数来组合形状。他还增加了一些非传统语法,包括后缀和中缀形式,以保留类似 GLSL 向量运算的便利。
Janet 的宏让 Henry 无须编写单独的解析器就能塑造语言。它也很容易编译成 WebAssembly,因此他可以用表达设计所用的同一种语言编写 Bauble 及其编译器,而不是用 JavaScript 或 OCaml 来解释这些记法。
将 Janet 嵌入浏览器后使用很愉快,但刚开始做很困难。Henry 写了一本书来帮助其他人,并让更多人了解这门语言。有人以书中的例子为基础,开发了名为 Trane 的现场音乐编程环境,他对此非常高兴。
Henry 做个人项目并不是为了职业规划,但出于兴趣获得的知识多次在工作中派上用场。接触不同语言会改变他对问题的建模方式。例如,Haskell 和不可变状态让他用新的方式处理熟悉的应用。
Janet 也让他第一次持续接触强大的宏系统:代码可以在编译期间执行,并为运行时生成代码。它可以生成包含程序状态的镜像,保存后再恢复,其中包括复杂对象图和暂停的计算。
Minsky 担心,在基于镜像的环境中,长期存在且不断变化的代码与数据集合可能难以检查或复现。Henry 说自己的用法更有约束:让程序运行到固定点,保存镜像,并始终从该可复现状态重新启动,前提是编译过程具有确定性。
Minsky 重视 Lisp 的宏和多阶段编程思想,同时观察到 OCaml 和 Haskell 往往更注重类型。他说,Jane Street 正在 OCaml 中探索范围更有限的运行时代码生成。这并不等同于冻结和重启整个程序,但仍可能很有用。
将 expect 测试用作开发工具
Expect 测试将输出与代码一起保存,使变化可见。两位讲者讨论了有用的表示方式、审查、起源和工具。
被问到从工作带入个人项目的经验时,Henry 提到了 expect 测试。测试运行代码,将输出捕获到同一个源文件中,并在后续运行时进行比较。开发时,他可以检查结果、修复代码,并在输出符合预期后接受它。
Minsky 将这一体验比作纯文本笔记本,但有一个重要区别:笔记本单元格可能按任意顺序执行,因此显示的结果未必与一次干净运行一致。Expect 测试则从头到尾重新运行,标记差异,让开发者决定是否接受更新后的输出。
Henry 说,探索式使用不能替代对精确性质的审慎测试。好的 expect 测试也可以捕获程序状态的有用视图。例如,在棋盘游戏中打印一系列走法后的棋盘,既能显示发生了变化,也能展示新的状态和预期状态。
Expect 测试可以与传统断言和属性测试共存。属性测试工具可以把发现的失败案例插入测试源代码,让以后运行时持续检查这一回归。共同的思想是方便地观察并保留值得关注的行为。
Minsky 比较了紧凑的源代码和电子表格:前者隐藏了流经代码的数据,后者的数据可见,但程序散落在各个单元格中。Expect 测试可以将紧凑代码与选定的执行轨迹放在一起。审查提交时,可以同时看到代码变化和由此产生的行为差异。
两人都提醒,倾倒大量输出是一种失败模式。有用的测试会选择相关状态并整理格式,让人能够读懂。构建清晰可视化的工作也让之后的修改更容易,给工程师一个为了自己受益而写测试的理由。
Henry 非常喜欢这种工作流,于是为 Janet 实现了一个修改源代码的宏。Minsky 将他们采用的方法追溯到 Mercurial 的统一测试和 CRAM 工具;Jane Street 并没有发明这个想法,但帮助推广了它。这种技术不依赖特定语言,虽然良好的编辑器集成可能很难构建。
Minsky 说,教孩子编程让他发现测试工具有多差,特别是在 C 和 C++ 中。Jane Street 的 OCaml 与 Emacs 集成会在保存文件时显示差异,并提供快速接受新输出的方法。Henry 同意,工具对于让这种工作流用起来愉快很重要。
同频道的其他文章
15:33EN → 简体中文
1:48:29EN → 简体中文
1:36:37EN → 简体中文
1:05:45EN → 简体中文
1:25:00EN → 简体中文
1:16:29EN → 简体中文