跳到主要内容

Beancount.io 介绍

· 阅读需 9 分钟

为什么要记账?

每个人都有关于如何管理资金的建议。在谷歌搜索“管理资金”,你会找到超过 1,690,000,000 个链接。你会发现大量的生活黑客或自助文章和书籍。你会找到专业教练或收费的课程。你会找到金融和投资服务。随意尝试你感兴趣的内容,通过试错来增加你的资产。

我认为最重要的是要记住,问钱的问题源于恐惧和自我怀疑。我们都害怕变化。我们都怀疑自己能否赚更多的钱。

与其花时间担心和怀疑,不如专注于相反的事情——你的信心。如果你在玩扑克,筹码很少,你只能小额下注,赢得少量资金。当你有很多筹码时,你可以大额下注并赢得大奖。你有更多的空间去冒险。你可以尝试那些在筹码较少时无法尝试的事情。

这里的魔力在于——通过更多地了解你的财务状况,你获得了信心!拥有更多的信心,我们可以做出更好的判断,并愿意下注最佳金额以获得更大的成功,从而赢得更多。

Expenses

了解你的支出并规划下一次消费

赢得更多的担忧的尽头在哪里?人们常常谈论财务自由这个流行词。然而,空谈无益,而记账恰恰回答了这个问题。

四个主要财务报表概述财务状况

不幸的是,在我们现代生活中,记账并不容易。我们正处于一个丰盈的新纪元。我们有很多账户——现金、银行账户、支付应用、信用卡、股票或加密货币经纪账户、折扣卡……我们拥有房屋、汽车、黄金、珠宝等资产……更糟糕的是,我们中的一些人可能生活在不同的国家,必须处理不同的货币。我们如何绘制出准确的财务生活地图,并在未来的不确定性中导航?

我所说的“准确的财务生活地图”是指这四个主要财务报表:

  1. 损益表:它显示我们在特定期间内赚取了多少收入。这个报表通常被认为是财务报表中最重要的,因为它反映了经营结果。
  2. 资产负债表:它回答我们拥有多少资产、负债和权益。这个报表是第二重要的,因为它报告了我们资产的流动性和资本化情况。
  3. 现金流量表:它报告我们的现金流入和流出,并回答我们是否产生了现金。我们需要足够的现金来支付费用和购买资产。
  4. 权益表:这对你的个人会计没有帮助。然而,对于公司而言,这个报表报告它如何在利益相关者之间分配权益。

Income Statement

Balance Sheet

使用 beancount.io,你可以快速生成上述报表。但是等等……如何为这些报表准备数据?

双重记账确保准确性

为了确保准确性并将错误检测内化到系统中,双重记账要求每个账户的每个条目至少有一个对应的条目到另一个账户。一笔交易涉及至少两个账户和两个操作——借记(+)和贷记(-)。

1970-01-01 open Income:BeancountCorp
1970-01-01 open Assets:Cash
1970-01-01 open Expenses:Food
1970-01-01 open Assets:Receivables:Alice
1970-01-01 open Assets:Receivables:Bob
1970-01-01 open Assets:Receivables:Charlie
1970-01-01 open Liabilities:CreditCard

2019-05-31 * "BeancountCorp" "5月15日至5月31日的工资"
Income:BeancountCorp -888 USD
Assets:Cash 888 USD

2019-07-12 * "Popeyes 鸡肉三明治" "与 Alice、Bob 和 Charlie 的晚餐"
Expenses:Food 20 USD
Assets:Receivables:Alice 20 USD
Assets:Receivables:Bob 20 USD
Assets:Receivables:Charlie 20 USD
Liabilities:CreditCard -80 USD

如上面的两个例子所示,每笔交易必须满足会计等式。

资产 = 负债 + 权益(即净资产)

我们使用了 Martin Blais 的 Beancount 语法和 Jakob Schnitzer 的网络项目 Fava 来构建这个网站。如果任何交易的各方不相加为零,它将提醒你。

Error Alert

现在你明白我们如何强制确保账本的正确性。但是你可能会问,那些“账户”是什么?

账户作为水的桶

将你的资产视为水在不同的桶中流入和流出,而“账户”就是那些装着你资金的桶。通过双重记账,资金如何在不同账户之间流动变得显而易见,就像水在不同桶之间流动一样。

Beancount.io 介绍了五种类型的账户。

  1. 收入——其金额总是负数或借记。这是因为你在赚钱,然后资金从“收入”账户借记到你的“资产”中。
  2. 支出——其金额总是正数或贷记。这是因为你在花钱,资金从“资产”或“负债”流向“支出”。
  3. 负债——其金额为正数或零。你的信用卡负债就是一个很好的例子,它会周期性地上升和下降。
  4. 资产——其金额为正数或零。你的现金或房屋总是有一定的价值。
  5. 权益——你的净资产。系统会自动为你计算。权益 = 资产 - 负债,它反映了你的财富状况。

现在你可以使用上述关键词打开自定义账户:

1970-01-01 open Assets:Cash
1970-01-01 open Assets:Stock:Robinhood
1970-01-01 open Assets:Crypto:Coinbase
1970-01-01 open Expenses:Transportation:Taxi
1970-01-01 open Equity:OpeningBalance

商品:跟踪你的投资

是的,你可以使用 beancount.io 跟踪你的投资。例如,我们在 2014 年以每个 100 美元的价格购买了 10 个比特币:

2014-08-08 * "购买 10 个比特币"
Assets:Trade:Cash -1000.00 USD
Assets:Trade:Positions 10 BTC {100.00 USD}

然后三年后,你以每个 10,000 美元 的价格出售它们(原始成本为每个 100 美元,标注为 {100.00 USD})。

2017-12-12 * "出售 2 个比特币"
Assets:Trade:Positions -2 BTC {100.00 USD} @ 10,000.00 USD
Assets:Trade:Cash 20,000.00 USD
Income:Trade:PnL -19,800.00 USD

或者同样的交易用 @@ 20,000.00 USD 表示总价为 20,000 美元

2017-12-12 * "出售 2 个比特币"
Assets:Trade:Positions -2 BTC {100.00 USD} @@ 20,000.00 USD
Assets:Trade:Cash 20,000.00 USD
Income:Trade:PnL -19,800.00 USD

交易的所有各方的总和,包括 -2 BTC {100.00 USD},仍然如往常一样为零。

成本 {100.00 USD} 标签很重要,因为你可能以不同的成本购买了相同的商品。

100 BTC {10.00 USD, 2012-08-08}
10 BTC {100.00 USD, 2014-08-08}

如果你想简化这个过程,可以在开始时设置账户为 FIFO 或 LIFO。FIFO 代表先进先出,而 LIFO 代表后进先出。在美国,国税局使用 FIFO 来计算你的盈亏和税收。

1970-01-01 open Assets:Trade:Positions "FIFO"

然后当你以简写方式出售时,如 -2 BTC {},beancount 将自动应用 FIFO 策略,出售最旧的商品。

Beancount.io

Beancount.io 是一个云服务,用于在文本文件中记录你的财务交易,将其可视化为财务报表(损益表、资产负债表、试算表等),并帮助你过上更好的财务生活。 立即注册 - 目前处于促销期且免费!

公司的技术和市场象限图和引力方向

· 阅读需 4 分钟

技术

技术

市场

市场

领导者

领导者

创新者的窘境

创新者的窘境

尝试者

尝试者

屠龙者

屠龙者

  • 技术的引力方向是平均水平:随着技术的通胀,优秀的技术会趋向于平庸,平庸的公司会采纳优秀的技术。
  • 市场的引力方向是马太效应:有市场的市场会更大,没市场的市场会更小。

设计度量系统

· 阅读需 14 分钟

需求

日志与度量:日志是发生的事件,而度量是对系统健康状况的测量。

我们假设该系统的目的是提供度量——即计数器、转化率、计时器等,用于监控系统性能和健康。如果转化率大幅下降,系统应警告值班人员。

  1. 监控商业指标,如注册漏斗的转化率
  2. 支持各种查询,如在不同平台上(IE/Chrome/Safari,iOS/Android/桌面等)
  3. 数据可视化
  4. 可扩展性和可用性

架构

构建系统的两种方式:

  1. 推送模型:Influx/Telegraf/Grafana
  2. 拉取模型:Prometheus/Grafana

拉取模型更具可扩展性,因为它减少了进入度量数据库的请求数量——没有热路径和并发问题。

服务器农场

服务器农场

写入

写入

Telegraf

Telegraf

InfluxDB

InfluxDB

REST API

REST API

Grafana

Grafana

InfluxDB 推送模型

InfluxDB 推送模型

Prometheus 拉取模型

Prometheus 拉取模型

应用程序

应用程序

导出器

导出器

客户端库

客户端库

第三方


应用程序

第三方<br>应用程序

拉取

拉取

Prometheus

Prometheus

检索

检索

服务发现

服务发现

存储

存储

PromQL

PromQL

告警管理器

告警管理器

Web UI / Grafana / API 客户端

Web UI / Grafana / API 客户端

PagerDuty

PagerDuty

邮件

邮件

特性与组件

测量注册漏斗

以移动应用的四步注册为例

输入手机号码 -> 验证短信代码 -> 输入姓名 -> 输入密码

每一步都有 IMPRESSIONPOST_VERIFICATION 阶段。并发出如下度量:

{
"sign_up_session_id": "uuid",
"step": "VERIFY_SMS_CODE",
"os": "iOS",
"phase": "POST_VERIFICATION",
"status": "SUCCESS",
// ... ts, contexts, ...
}

因此,我们可以查询 iOSVERIFY_SMS_CODE 步骤的整体转化率,如下所示:

(counts of step=VERIFY_SMS_CODE, os=iOS, status: SUCCESS, phase: POST_VERIFICATION) / (counts of step=VERIFY_SMS_CODE, os=iOS, phase: IMPRESSION)

数据可视化

Grafana 在数据可视化工作中已经相当成熟。如果您不想暴露整个网站,可以使用 嵌入面板与 iframe

设计 Square Cash 或 PayPal 转账系统

· 阅读需 21 分钟

澄清需求

设计一个类似于 Square Cash(以下称为 Cash App)或 PayPal 的转账后端系统,以实现:

  1. 从银行存款和支付
  2. 账户之间转账
  3. 高扩展性和可用性
  4. 国际化:语言、时区、货币兑换
  5. 非幂等 API 和至少一次交付的去重
  6. 跨多个数据源的一致性

架构

AWS CloudHSM

AWS CloudHSM

演示层

演示层

SDK/文档

SDK/文档

移动仪表板

移动仪表板

网页仪表板

网页仪表板

仪表板客户端

仪表板客户端

移动钱包

移动钱包

网页钱包

网页钱包

钱包客户端

钱包客户端

商户 


用户

商户 <br>用户

最终用户

最终用户

网页 Chrome 扩展

网页 Chrome 扩展

操作员

操作员

支付

支付

任务队列

任务队列

财务报告

财务报告

支付网关

支付网关

银行 / 


供应商

[不支持的查看器]

副作用制造者

副作用制造者

帮助服务门户

帮助服务门户

用户


配置文件


AuthDB


[不支持的查看器]

API 网关


单体


API 网关<br>单体<br>

支付


数据库


支付<br>数据库<br>

Aurora

Aurora

风险控制

风险控制

风险控制

风险控制

事件
队列

[不支持的查看器]

功能和组件

支付服务

支付数据模型本质上是“复式记账”。每个账户的每一笔入账都需要在另一个账户中有相应的对立入账。所有借方和贷方的总和等于零。

存款和支付

交易:新用户 Jane Doe 从银行向 Cash App 存入 100 美元。这一笔交易涉及以下数据库条目:

记账表(用于历史记录)

+ 借方, 美元, 100, CashAppAccountNumber, txId
- 贷方, 美元, 100, RoutingNumber:AccountNumber, txId

交易表

txId, 时间戳, 状态(待处理/已确认), [记账条目], 叙述

一旦银行确认交易,更新上述待处理状态和以下资产负债表,均在一笔交易中完成。

资产负债表

CashAppAccountNumber, 美元, 100

在 Cash App 内部账户之间转账

与上述情况类似,但没有待处理状态,因为我们不需要慢速外部系统来更改其状态。所有记账表、交易表和资产负债表的更改都在一笔交易中完成。

国际化

我们在三个维度上解决国际化问题。

  1. 语言:所有文本,如文案、推送通知、电子邮件,均根据 accept-language 头部进行选择。
  2. 时区:所有服务器时区均为 UTC。我们在客户端将时间戳转换为本地时区。
  3. 货币:所有用户转账交易必须使用相同货币。如果他们想要跨货币转移,必须先以对 Cash App 有利的汇率兑换货币。

例如,Jane Doe 想以 0.2 的汇率将 1 美元兑换为 6.8 人民币。

记账表

- 贷方, 美元, 1, CashAppAccountNumber, txId
+ 借方, 人民币, 6.8, CashAppAccountNumber, txId, @7.55 人民币/美元
+ 借方, 美元, 0.1, ExpensesOfExchangeAccountNumber, txId

交易表、资产负债表等与存款和支付中讨论的交易类似。主要区别在于银行或供应商提供兑换服务。

如何在交易表和外部银行及供应商之间同步?

  • 使用幂等性重试以提高外部调用的成功率并确保没有重复订单
  • 检查待处理订单是否已完成或失败的两种方法。
    1. 轮询:定时作业(SWF、Airflow、Cadence 等)轮询待处理订单的状态。
    2. 回调:为外部供应商提供回调 API。
  • 优雅关闭。银行网关调用可能需要数十秒才能完成,重启服务器可能会从数据库恢复未完成的交易。该过程可能会创建过多连接。为减少连接,在关闭之前停止接受新请求,并等待现有的外发请求完成。

去重

为什么去重是一个问题?

  1. 不是所有端点都是幂等的
  2. 事件队列可能是至少一次的。

不是所有端点都是幂等的:如果外部系统不是幂等的怎么办?

对于上述 轮询 情况,如果外部网关不支持幂等 API,为了不淹没重复条目,我们必须记录外部系统给我们的订单 ID 或参考 ID,并通过订单 ID 查询 GET,而不是一直使用 POST

对于 回调 情况,我们可以确保实现幂等 API,并且无论如何我们将 待处理 更改为 已确认

事件队列可能是至少一次的

  • 对于事件队列,我们可以使用一个完全一次的 Kafka,生产者吞吐量仅下降 3%。
  • 在数据库层,我们可以使用 幂等性密钥或去重密钥
  • 在服务层,我们可以使用 Redis 键值存储。

可用性和扩展性

设计支付 webhook

· 阅读需 5 分钟

1. 澄清需求

  1. 一旦支付成功,webhook 将回调商家。
    1. 商家开发者向我们注册 webhook 信息。
    2. 可靠且安全地向 webhook 发起 POST HTTP 请求。
  2. 高可用性、错误处理和故障恢复。
    1. 异步设计。假设商家的服务器分布在全球,可能会有高达 15 秒的延迟。
    2. 至少一次交付。幂等密钥。
    3. 顺序无关。
    4. 强大且可预测的重试和短路机制。
  3. 安全性、可观察性和可扩展性。
    1. 防伪造。
    2. 当商家的接收器出现故障时通知商家。
    3. 易于扩展和规模化。

2. 概述高层设计

异步设计 + 重试 + 排队 + 可观察性 + 安全性

3. 功能和组件

核心功能

  1. 用户访问仪表板前端向我们注册 webhook 信息 - 例如要调用的 URL、他们希望订阅的事件范围,然后从我们这里获取 API 密钥。
  2. 当有新事件时,将其发布到队列中,然后被调用者消费。调用者获取注册信息并向外部服务发起 HTTP 调用。

webhook 调用者

  1. 订阅由支付状态机或其他服务发布的支付成功事件队列。

  2. 一旦调用者接受事件,从用户设置服务获取 webhook URI、密钥和设置。根据这些设置准备请求。为了安全...

  • 所有来自用户设置的 webhook 必须使用 HTTPS。

  • 如果负载很大,预期延迟很高,并且我们希望确保目标接收器是活跃的,我们可以通过携带挑战的 ping 验证其存在。例如,Dropbox 通过发送带有“challenge”参数(一个随机字符串)的 GET 请求来验证 webhook 端点,您的端点需要将其回显作为响应。

  • 所有回调请求都带有头部 x-webhook-signature。这样接收者可以验证请求。

    • 对于对称签名,我们可以使用 HMAC/SHA256 签名。其值为 HMAC(webhook secret, raw request payload); Telegram 使用此方法。
    • 对于非对称签名,我们可以使用 RSA/SHA256 签名。其值为 RSA(webhook private key, raw request payload); Stripe 使用此方法。
    • 如果是敏感信息,我们还可以考虑对负载进行加密,而不仅仅是签名。
  1. 向外部商家的端点发起带有事件负载和安全头的 HTTP POST 请求。

API 定义

// POST https://example.com/webhook/
{
"id": 1,
"scheduled_for": "2017-01-31T20:50:02Z",
"event": {
"id": "24934862-d980-46cb-9402-43c81b0cdba6",
"resource": "event",
"type": "charge:created",
"api_version": "2018-03-22",
"created_at": "2017-01-31T20:49:02Z",
"data": {
"code": "66BEOV2A", // 或用户需要履行的订单 ID
"name": "主权个体",
"description": "掌握信息时代的过渡",
"hosted_url": "https://commerce.coinbase.com/charges/66BEOV2A",
"created_at": "2017-01-31T20:49:02Z",
"expires_at": "2017-01-31T21:49:02Z",
"metadata": {},
"pricing_type": "CNY",
"payments": [
// ...
],
"addresses": {
// ...
}
}
}
}

商家服务器应以 200 HTTP 状态码响应以确认收到 webhook。

错误处理

如果没有收到确认,我们将使用幂等性密钥和指数退避重试,最长可达三天。最大重试间隔为 1 小时。 如果达到某个限制,则短路/标记为损坏。向商家发送电子邮件。

指标

Webhook 调用者服务将状态发射到时序数据库以获取指标。

使用 Statsd + Influx DB 还是 Prometheus?

  • InfluxDB:应用程序将数据推送到 InfluxDB。它有一个用于指标和索引的单体数据库。
  • Prometheus:Prometheus 服务器定期从运行的应用程序中拉取指标值。它使用 LevelDB 进行索引,但每个指标存储在自己的文件中。

或者如果您有宽裕的预算,可以使用昂贵的 DataDog 或其他 APM 服务。

小谈的艺术

· 阅读需 4 分钟
  • 在西方世界,除了公众演讲,最大的社交恐惧是与陌生人开始对话。为了克服被拒绝的恐惧,意识到在大多数情况下,当你努力与他们交谈时,人们会对此表示赞赏,这很有帮助。
  • 在某些情况下,不说话可能会让你显得傲慢或冷漠。开始对话可以很简单:首先,对某人微笑;其次,建立眼神接触;第三,主动自我介绍。
  • 接近一群人:1,远处表现出你对这个小组的兴趣,注意发言者。2,群体会注意到并为你腾出空间。3,在你表达任何强烈观点之前,让小组对你有好感。
  • 引导对话会唤起积极的情感,使人们愿意与你合作或社交。承担这一责任的一个简单方法是表现得像一个主人,问:“你叫什么名字?”强调“你”,让他们感到被重视。
  • 改善对话的最佳方法是提出开放式问题,这表明你真心关心他们想说的话。
  • 对话不可避免地会有尴尬的沉默。你可以通过询问与当前情境相关的开放式问题或使用FORM首字母缩略词(家庭、职业、休闲和其他)来恢复舒适的交流。
  • 成为一个积极的倾听者,肢体语言很重要:避免交叉双臂、耸肩或摆弄衣物、头发或珠宝。相反,向前倾身、点头、微笑并保持眼神接触。
  • 成为一个积极的倾听者,语音提示也很重要:通过询问关于细节的后续问题来参与,热情回应以表达你的兴趣,或对发言者所说的内容进行释义以澄清。
  • 优雅地结束对话的第一件事是回到你讨论的重点。如果你真心想在稍后继续讨论,交换联系方式,说明你接下来要做什么(如果是熟人)握手并道别。
  • 重要的是要遵循你所说的接下来要做的事情。否则,另一方可能会认为你根本不享受与他们的相处,这会伤害他们的感情。
  • 以介绍你的对话伙伴给新朋友的方式结束对话是一种礼貌的方式,这将扩大他的社交网络,并确保他不会觉得你在抛弃他。或者反过来,你可以请他们介绍你给其他人。

移动分析指标

· 阅读需 3 分钟
名称定义评论
DAU(每日活跃用户)每日独立用户数量下载数据具有误导性,因为 80% 到 90% 的下载用户从未返回
MAU(每月活跃用户)每月独立用户数量下载数据具有误导性
粘性(DAU / MAU ) x 100粘性越高 = 投资回报率越高,移动粘性是移动/桌面的 20 倍
留存率((期末客户数量 – 期内获得客户数量) / 期初客户数量 ) x 100高留存率几乎总是好事。
流失率1- 客户留存率
CPA(每次获取成本)总营销成本 / 总用户获取数量越低越好
每 DAU 平均每日会话数用户每天登录应用的频率。并不总是好事。
LTV(生命周期价值)转化的平均价值 x 一段时间内的平均转化数量 x 平均客户价值亏损 = 布尔值(LTV < CPA)
ARPU(每用户平均收入)应用的生命周期收入 / 生命周期用户数量ARPU 说明你应该在每个用户身上赚取更多的收入
ARPPU(每付费用户平均收入)应用的生命周期收入 / 生命周期付费用户数量
ROI(投资回报率)回报 / 投资保持一致以衡量年度相对进展
应用加载时间应该 ≤ 2 秒
用户满意度通过 CSAT 和 NPS 测量更好的用户满意度 = 更多的用户留存 + 更多的 LTV
CSAT(客户满意度评分)(满意客户数量 / 调查响应者数量) x 100请客户在 1 到 5 的范围内评分他们的满意度。4 或 5 表示满意
NPS(净推荐值)((推荐者数量 – 贬低者数量) / 调查响应者数量) x 100请客户在 1 到 10 的范围内评分他们的满意度。回复 0 到 6 的用户是贬低者。回复 8 到 10 的用户是推荐者。
目标达成每个会话中实现目标的用户百分比目标可以是购买、注册、分享等。
营销获取%, $, 和来自顶级营销渠道的访客的美元价值百分比

Stratechery: 为什么亚马逊收购了全食超市?

· 阅读需 6 分钟

答案是:亚马逊想要买客户给自己的食品杂货服务。

背景

  • 亚马逊收购全食超市 = 苹果公司的 iPhone 击败了 Palm

    • 不要混淆目标(goals)、战略(strategies)和战术(tactics)—— 苹果公司的战略:
      • 不是要制造手机而是要生产个人电脑
      • 不是要为手机添加功能而是要将传统手机的功能压缩成一个app
      • 不是复制运营商的工作,而是利用其与客户之间的联系
    • iPhone 是有史以来最成功的产品 = 亚马逊是有史以来最具统治力的公司
  • 亚马逊的目标

    1. 起初,Amazon.com 的目标是成为基于信息化产品与服务的一流零售商,并且从卖书开始。
    2. 然后,亚马逊宣布“我们的愿景是成为地球上最以客户为中心的公司,要让每个人都能在我们的线上网站找到任何他们想要买的东西。”
    3. ==亚马逊的目标是从所有的经济活动中分一杯羹==。
  • 亚马逊的战略

    • 面向企业:AWS。假设所有商业交易不久之后都能使用互联网完成
    • 面向客户:Prime。假设高成本和多样的选择是不可持续的。有了 prime 客户就不会考虑其他替代方案。
      • 然而
        • 食品杂货业是最大的零售业类别
        • 食品杂货业最能够持续提醒消费者:除了亚马逊其实还有其他的选择
  • 亚马逊的战术:发展食品杂货服务

为什么亚马逊没有得出正确的战术?

书籍杂货
高存货单位=选品丰富低存货单位(30k - 50k)
标准化的良莠不齐的
不会腐烂容易腐烂

亚马逊生鲜的成本劣势

  1. 一旦规模不足,商品腐烂将会产生很高的成本
  2. 而规模取决于每个城市的具体情况

为什么收购全食超市(而非其他)可以解决商业规模问题?

==商业基础元件模型 + 两个基本点 1) 高固定成本 2) 高回报==

  • 将基础设施解构成最小可售单元(MSUs)
  • 这些业务本身就是这些最小可售单元的第一和最佳的客户
  • 将最小可售单元转售出去

AWS的三层架构

服务基础元件S3, EC2, RDS, SNS, ...
平台AWS高额的固定成本 + 规模回报
基础设施模块化组件数据中心,服务器,存储,交换机,带宽
  • MSUs 属于 S3, EC2, RDS, SNS等
  • 第一和最佳的客户是 amazon.com
  • 转售 MSUs 给非亚马逊开发者

Amazon.com 的三层架构

服务包裹FDA, Amazon Pay, ...
平台物流中心高额的固定成本 + 规模回报
基础设施模块化供应商制造商,第三方供应商等
  • MSUs 属于 FDA, Amazon Pay 等
  • 第一和最佳的客户是亚马逊第一方电子商务
  • 将 MSUs 转售给第三方供应商

这里的洞见是亚马逊现有的食品杂货并不存在第一和最佳供应商。

完美的客户

把全食超市放进这个版图中,我们可以看到, ==亚马逊所做的不仅仅是买到了一个零售商,更是买到了自己原有业务的一个客户==。

Amazon.com 的三层架构 + 客户

客户所有品类的食品,快递,餐厅
服务杂货肉,水果,蔬菜,干粮等
平台物流中心高额的固定成本 + 规模回报
基础设施模块化供应商商店品牌,名牌,本地供应商,区域供应商等

如今,亚马逊食品杂货可以服务于亚马逊生鲜和全食超市,未来,这个基础平台还可以为餐厅等需要食物的地方提供服务。

设计股票价格变动的智能通知

· 阅读需 16 分钟

需求

  • 300 万用户
  • 5000 支股票 + 250 支全球股票
  • 用户在以下情况下会收到价格变动通知:
    1. 订阅该股票
    2. 股票价格变动达到 5% 或 10%
    3. 自 a) 上周或 b) 昨天以来
  • 可扩展性。可能支持其他类型的通知,如突发新闻、财报电话会议等。

构建架构草图

上下文:

  • 什么是清算?清算是金融交易结算的程序——即资金及时准确地转移给卖方,证券转移给买方。通常,清算由一个称为清算所的专业组织作为中介。
  • 什么是证券交易所?一个股票经纪人和交易者可以买卖证券的场所。

苹果推送通知服务


(APNs)

苹果推送通知服务<br>(APNs)

Google Firebase 云消息传递


(FCM)

Google Firebase 云消息传递<br>(FCM)

电子邮件服务


AWS SES /sendgrid/etc

电子邮件服务<br>AWS SES /sendgrid/etc

通知者

通知者

外部供应商



市场价格

[不支持查看器]

Robinhood 应用

Robinhood 应用

API 网关

API 网关

反向代理

反向代理

批量写入

批量写入

价格


监测器

[不支持查看器]

时间序列数据库


influx 或 prometheus

时间序列数据库<br>influx 或 prometheus

每 5 分钟一次

[不支持查看器]

定期读取

定期读取

价格


监测器

价格<br>监测器

用户设置

用户设置

通知队列

通知队列

限流缓存

限流缓存

cronjob

cronjob

这些组件是什么,它们如何相互作用?

  • 价格监测器
    • 数据获取策略
      • 选项 1 初步:每 5 分钟获取一次数据,并批量写入时间序列数据库。
      • 选项 2 高级:如今外部系统通常直接推送数据,因此我们不必一直拉取。
    • 每次请求或每次价格变动约 6000 个点。
    • 数据保留 1 周,因为这只是 lambda 架构的加速层。
  • 价格观察者
    • 读取过去一周或过去 24 小时内每只股票的数据。
    • 计算波动是否超过 5% 或 10% 在这两个时间段内。我们得到的元组如 (股票, 上涨 5%, 1 周)。
      • 边缘情况:我们是否应该规范化价格数据?例如,某人错误地将 UBER 以 1 美元的价格出售。
    • 速率限制(因为 5% 或 10% 的变化可能在一天内发生多次),然后发出事件 PRICE_CHANGE(STOCK_CODE, timeSpan, percentage) 到通知队列。
  • 定期触发器是 cron 作业,例如 Airflow、Cadence。
  • 通知队列
    • 在用户和股票数量较少时,可能不一定会引入。
    • 可能接受通用消息事件,如 PRICE_CHANGEEARNINGS_CALLBREAKING_NEWS 等。
  • 通知者
    • 订阅通知队列以获取事件
    • 然后从用户设置服务中获取通知对象
    • 最后根据用户设置,通过 APNs、FCM 或 AWS SES 发送消息。

设计股票交易所

· 阅读需 17 分钟

需求

  • 买入卖出 订单的订单匹配系统。订单类型:
    • 市场订单
    • 限价订单
    • 止损订单
    • 完全成交或取消订单
    • 订单有效期
  • 为数百万用户提供高可用性和低延迟
    • 异步设计 - 广泛使用消息队列(顺便提一下,副作用:工程师在一个服务上发布到队列,不知道下游服务具体在哪里,因此无法做坏事。)

架构

反向代理

反向代理

API 网关

API 网关

订单匹配

订单匹配

用户存储

用户存储

结算

结算

订单

订单

股票元数据

股票元数据

认证

认证

缓存

缓存

余额与记账

余额与记账

外部定价

外部定价

清算


清算<br>所

银行,ACH,Visa 等

银行,ACH,Visa 等

支付

支付

审计与报告

审计与报告

组件及其相互作用

订单匹配系统

  • 按股票代码分片
  • 订单的基本数据模型(其他元数据省略):Order(id, stock, side, time, qty, price)
  • 订单簿的核心抽象是匹配算法。有许多匹配算法(参考 stackoverflow参考 medium
  • 示例 1:价格-时间 FIFO - 一种将二维向量投射或展平为一维向量的方式
    • x 轴是价格
    • y 轴是订单。价格/时间优先队列,FIFO。
      • 买方:按价格升序,按时间降序。
      • 卖方:按价格升序,按时间升序。
    • 换句话说
      • 买方:价格越高,订单越早,越接近匹配中心。
      • 卖方:价格越低,订单越早,越接近匹配中心。

x 轴

价格线

y 轴投射到 x 轴上

Id   Side    Time   Qty   Price   Qty    Time   Side
---+------+-------+-----+-------+-----+-------+------
#3 20.30 200 09:05 卖出
#1 20.30 100 09:01 卖出
#2 20.25 100 09:03 卖出
#5 买入 09:08 200 20.20
#4 买入 09:06 100 20.15
#6 买入 09:09 200 20.15

来自 Coinbase Pro 的订单簿

单一股票交易所模拟器

  • 示例 2:按比例分配

纯按比例分配

如何实现价格-时间 FIFO 匹配算法?

  • 按股票分片,CP 优于 AP:一股一个分区
  • 有状态的内存树图
    • 定期迭代树图以匹配订单
  • 使用 Cassandra 进行数据持久化
  • 订单匹配服务的进出请求通过消息队列进行
  • 故障转移
    • 内存树图快照到数据库
    • 在错误情况下,从快照恢复并与缓存去重

如何实时将订单簿数据传输到客户端?

  • websocket

如何支持不同类型的订单?

  • 在树图中使用相同的 卖出或买入:数量 @ 价格,但具有不同的创建设置和匹配条件
    • 市场订单:以最后市场价格下单。
    • 限价订单:以特定价格下单。
    • 止损订单:以特定价格下单,并在特定条件下匹配。
    • 完全成交或取消订单:以特定价格下单,但仅匹配一次。
    • 订单有效期:以特定价格下单,但仅在给定时间范围内匹配。

订单服务

  • 保留所有活动订单和订单历史。
  • 接收到新订单时写入订单匹配。
  • 接收匹配的订单并与外部清算所结算(异步外部网关调用 + 定时任务同步数据库)

参考文献