# Costflow 语法规则 v0.2

Costflow 是一个提升复式记账效率的工具,通过连接聊天工具与云存储的方式来解决现有复式记账记录麻烦和移动设备无法记录的问题。更多 Costflow 的介绍可以查看这篇文章。Costflow 语法则是其中解析文本到记账工具格式的规则。

例如发送下方内容给 Costflow 机器人:

Dinner #trip 200 bofa > trip

根据语法规则会被解析成下方内容:

2019-06-25 * "Dinner" #trip #costflow
  Assets:US:BofA                              -200.00 USD
  Expenses:Trip                               +200.00 USD

Costflow 语法和对应的解析器代码已经开源在 https://github.com/costflow/syntaxhttps://github.com/costflow/parser 。欢迎大家来贡献想法和代码,一起来让这个语法更好用。

# Costflow Syntax v0.2

以下内容基于 Costflow Syntax v0.2,并且输出格式仅支持 Beancount

# 语法特点

  • 日期可省略,默认为今天;
  • 货币代码可省略;
  • 账户名替换,例如用 bofa 代替 Assets:US:BofA:Checking;
  • 自动插入 tag;
  • 自动获取当前货币汇率、加密货币和股票的价格;
  • 极简交易语法;
  • 自定义缩进长度以及每行对齐长度;
  • v0.2 新增:自动插入 link;
  • v0.2 新增:插入交易时间信息;
  • v0.2 新增:简化日期输入的语法;

# 语法指令符

指令符是文本开头的第一个符号或者单词(除日期外),指令符兼容记账工具的部分 directive,并进行了扩展。以下为可用的指令符。

指令符 说明
* 已确认交易,可省略。
! 未确认交易。
; 注释。会同步存储到文件中。
// 注释。只记录在 Costflow 中,不会同步到文件中。
open 对应 Open Directive.
close 对应 Close Directive.
commodity 对应 Commodity Directive.
option 对应 Option Directive.
note 对应 Note Directive.
balance 对应 Balance Directive.
pad 对应 Pad Directive.
price 对应 Price Directive.
$ 查询法定货币、加密货币的汇率,以及股票的最新价格。不会同步到文件中。
event 对应 Event Directive.
在未匹配到任何指令符的情况下:
如果语句中含有数字,则认为指令符为 *;
如果语句中没有数字,则认为指令符为 //。

# 通用规则

为了使语法更简单易用,在使用前会要求用户预先设置以下内容:

  • 解析格式,此版本只支持 Beancount;
  • 用户所在时区,使用 IANA 格式
  • 默认货币代码,使用 ISO 4217 格式
  • 缩进长度;
  • 每行对齐长度;
  • 可选:账户名称对应的缩写。
  • 可选:自动插入的 tag;
  • 可选:自动插入的 link;
  • 可选:插入交易时间的位置;
设置项 是否必须 类型 说明 示例
mode 字符串 输出格式,当前版本只支持 beancount。 beancount
currency 字符串 默认货币符号,采用 ISO 4217 格式 CNY
timezone 字符串 所在时区,采用 IANA 格式 Asia/Hong_Kong
indent 数字 缩进长度。用于交易指令类型。 2
lineLength 数字 交易类型时货币符号前(包含)的对齐长度。用于交易指令类型。 80
tag 字符串 交易类型时自动插入的 tag,需要以 # 开头,如有多个以空格分隔。用于交易指令类型。 #trip #food
link 字符串 交易类型时自动插入的 link,需要以 ^ 开头,如有多个以空格分隔。用于交易指令类型。 ^project-x
insertTime 字符串 可在交易类型的结果中自动插入时间(以服务器接收到的时间为准)。此项是设置时间插入的位置,目前可选值只能是 metadata。用于交易指令类型。 metadata
alphavantage 字符串 playground 等处使用 pricing 或 $ 命令时需要使用自己的 Alpha Vantage API token,可以在此处申请。使用 Costflow Hub 产品无需配置。 TRBZ2Y1D7TN0ZHFO

此版本语法有如下通用规则:

  • 日期可采用 YYYY-MM-DD 格式,可省略,默认为当前你所在时区的今天(以下示例用 2019-07-01 代替),放在消息的最开始。为更方便地记录日期,v0.2 开始除了支持 YYYY-MM-DD 格式的日期外,新增了以下日期格式:

    • 日期简写: Jul 25 / July 10 / Aug 2,其中月份可为三个字母的简写或者是全称,第一个字母要求大写。
    • 常用日期简写:
      • 昨天:yesterday / ytd;
      • 前天:dby;
      • 明天:tomorrow / tmr,在记录 balance 时很有用;
      • 后天:dat;
  • 货币代码可省略,默认为你预设的货币(以下示例用 USD 代替);

  • 不支持 metadata;

  • 数字、货币代码、交易描述之间都需要有空格;

  • commodity 全部为大写英文字母;

  • 文字缩写不能全部是大写字母或者是数字;

  • 以 2019-07-01 * "McDonald's" "🍔" 为例,如果 insertTime 为 metadata,则会自动在下一行插入一条值为 time 的 metadata:

    2019-07-01 *  "McDonald's" "🍔"
      time: "11:22:33"
      ...
    

以下是后面示例中用到的配置:

{
    "mode": "beancount",
    "currency": "CNY",
    "timezone": "Asia/Hong_Kong",
    "tag": "#costflow",
    "link": "",
    "indent": 2,
    "lineLength": 60,
    "insertTime": "",
    "replacement": {
        "eob": "Equity:Opening-Balances",
        "bofa": "Assets:US:BofA:Checking",
        "rx": "Assets:Receivables:X",
        "ry": "Assets:Receivables:Y",
        "boc": "Assets:CN:BOC",
        "cmb": "Liabilities:CreditCard:CMB",
        "food": "Expenses:Food",
        "phone": "Expenses:Home:Phone",
        "rent": "Expenses:Home:Rent"
    }
}
  

# 交易 Transaction

交易是最常见的记账类型。Costflow 支持以下两种交易格式。

# 第一种:>

第一种使用大于号来代表交易的流向,意思是交易从符号左侧的账户流向符号右侧的账户。语法规则如下:

[YYYY-MM-DD] [Flag] ["Payee"] ["Narration"] [#tag] [^link] AMOUNT [COMMODITY] FROM_ACCOUNT > [AMOUNT] [COMMODITY] TO_ACCOUNT

方括号中的内容代表可以被省略。具体规则如下:

  • 日期可以省略,默认为当前你所在时区的今天;
  • Flag 代码交易是否确认, ! 代表未确认,* 代表已确认,如果是 * 可以省略;
  • Payee 和 Narration 使用起来会很简单,但语法规则稍微有些复杂:
    • 兼容 Beancount 现有的双引号规则(但不支持 | 来分割):只有一个双引号时代表 Narration,如果有两个则第一个代表 Payee,第二个代表 Narration;
    • 双引号在 Costflow 中可以省略:
      • 如果是 Narration 可以直接跳过双引号输入文字,但此规则 Narration 中不能有数字出现,例如 「去 711 买饮料」是不符合规则的。
      • 如果是 Payee 可以使用 @Payee 的格式,但 Payee 中间不可以有空格,可以使用 _ 或者 - 来连接。例如 @KFC 和 @Burger_King 都是符合规则的 Payee;
      • 如果只想记录 Payee,则直接 @Payee 即可,Narration 完全不用在意。
  • Tag:以 # 开始的字符会被认为是 tag,可为多个;
  • Link:以 ^ 开始的字符会被认为是 link,可为多个;
  • 第一种交易语法中,AMOUNT / COMMODITY / ACCOUNT 要按照这个顺序来书写;
    • AMOUNT 为交易金额,因为 > 代表流向,所以 > 左侧金额默认为负数,负号可以省略;
    • COMMODITY 可省略,默认为预先设置的货币代码;
    • ACCOUNT 为账户名称,可为账户全名,或者是预先设置缩写;
  • > 右侧的规则和左侧基本一致,除了金额默认为正数;
  • 如果需要记录 Costs 和 Prices,用法与 Beancount 一致,@ 和 @@ 两端都需要有空格;
  • 如果涉及多个账户,则使用 + 来代表,左右两侧均可出现。但请注意, > 只能出现一次;
  • > 右侧的金额可以省略,默认根据左侧对的金额来计算,如果右侧有多个账户,则平分左侧金额(AA)。

下面来看几个示例:

// 示例一:传统格式输入
2017-01-05 "RiverBank Properties" "Paying the rent" 2400 Assets:US:BofA:Checking > 2400  Expenses:Home:Rent
// 示例一输出
2017-01-05 * "RiverBank Properties" "Paying the rent"
  Assets:US:BofA:Checking                        -2400.00 USD
  Expenses:Home:Rent                             +2400.00 USD


// 示例二:省略日期、货币代码,并使用非双引号格式的 Payee
@Verizon 59.61 Assets:US:BofA:Checking > Expenses:Home:Phone
// 示例二输出
2019-07-01 * "Verizon" ""
  Assets:US:BofA:Checking                          -59.61 USD
  Expenses:Home:Phone                              +59.61 USD


// 示例三:在示例二的基础上,按照文字替换进行缩写
@Verizon 59.61 bofa > phone
// 示例三输出与示例二完全一致


// 示例四:从多个账户支出,一种用途
Rent 750 cmb + 750 boc > rent
// 示例四输出
2019-07-01 * "Rent"
  Liabilities:CreditCard:CMB                     -750.00 USD
  Assets:CN:BOC                                  -750.00 USD
  Expenses:Home:Rent                            +1500.00 USD


// 示例五:从一个账户支出,多种用途。场景为朋友 AA 聚餐,你使用信用卡来结帐(为 X 和 Y 垫付)。
Dinner 180 CNY bofa > rx + ry + food
// 示例五输出
2019-07-01 * "Dinner"
  Assets:Receivables:X                            +60.00 CNY
  Assets:Receivables:Y                            +60.00 CNY
  Expenses:Food                                   +60.00 CNY
  Liabilities:CreditCard:CMB                     -180.00 CNY


// 示例六:记录 Cost/Price
Transfer to account in US 5000 CNY @@ 726.81 USD boc > 726.81 bofa
// 示例六输出
2019-07-01 * "Transfer to account in US"
  Assets:CN:BOC                                 -5000.00 CNY @@ 726.81 USD
  Assets:US:BofA:Checking                        +726.81 USD

# 第二种:|

第二种方式与第一种语法大部分相同,除了账户部分不使用 > 表示流向,而是使用管道符号(U+007C)来代表换行,换行出现的时机与 Beancount 一致。第二种规则如下:

[YYYY-MM-DD] [Flag] ["Payee"] ["Narration"] [#tag] [^link] | ACCOUNT_A [COMMODITY] AMOUNT | ACCOUNT_B [COMMODITY] AMOUNT

可以看出,其中日期、Flag、Payee、Narration、Tag、Link 用法与第一种完全相同。两者不同点是:

  • 第二种需要在每个账户出现前输入一个 |;
  • 第二种语法中, ACCOUNT / COMMODITY / AMOUNT 的顺序与 Beancount 一致;
  • 第二种因为没有流向,所以无法对金额进行计算。所以 Amount 如果是负值,负号不能省略。每个区间内 AMOUNT 值也不能省略。

我们将上面的示例用第二种方式进行改写:

// 示例一:传统格式输入
2017-01-05 "RiverBank Properties" "Paying the rent" | Assets:US:BofA:Checking -2400 | Expenses:Home:Rent 2400
// 示例一输出
2017-01-05 * "RiverBank Properties" "Paying the rent"
  Assets:US:BofA:Checking                        -2400.00 USD
  Expenses:Home:Rent                             +2400.00 USD


// 示例二:省略日期、货币代码,并使用非双引号格式的 Payee
@Verizon | Assets:US:BofA:Checking -59.61 | Expenses:Home:Phone 59.61
// 示例二输出
2019-07-01 * "Verizon" ""
  Assets:US:BofA:Checking                          -59.61 USD
  Expenses:Home:Phone                              +59.61 USD


// 示例三:在示例二的基础上,按照文字替换进行缩写
@Verizon | bofa -59.61 | phone 59.61
// 示例三输出与示例二完全一致


// 示例四:从多个账户支出,一种用途
Rent | cmb -750 | boc -750 | rent 1500
// 示例四输出
2019-07-01 * "Rent"
  Liabilities:CreditCard:CMB                     -750.00 USD
  Assets:CN:BOC                                  -750.00 USD
  Expenses:Home:Rent                            +1500.00 USD


// 示例五:从一个账户支出,多种用途。场景为朋友 AA 聚餐,你使用信用卡来结帐(为 X 和 Y 垫付)。
Dinner | bofa 180 CNY | rx -60 | ry -60 | food -60
// 示例五输出
2019-07-01 * "Dinner"
  Assets:Receivables:X                            +60.00 CNY
  Assets:Receivables:Y                            +60.00 CNY
  Expenses:Food                                   +60.00 CNY
  Liabilities:CreditCard:CMB                     -180.00 CNY


// 示例六:记录 Cost/Price
Transfer to account in US | boc -5000 CNY @@ 726.81 USD  | bofa +726.81
// 示例六输出
2019-07-01 * "Transfer to account in US"
  Assets:CN:BOC                                 -5000.00 CNY @@ 726.81 USD
  Assets:US:BofA:Checking                        +726.81 USD


# 注释 Comment

; 与 // 对的用法都非常简单,直接在符号后面输入任何内容即可。例如

; I paid and left the taxi, forgot to take change, it was cold.
// to do: cancel Netflix subscription

两者不同的是,使用 ; 的注释会先存储到 Costflow 服务器(其它类型同样,可以登录 Costflow 后台查看),然后同步注释到云存储文件中;而 // 注释只会存储到 Costflow 服务器。// 注释适用于提醒自己做一些账户操作。

# Open

语法与 Beancount 一致。此版本暂不支持 Metadata 的设置。

[YYYY-MM-DD] open ACCOUNT

示例:

// 输入
open Assets:US:BofA
// 输出
2019-07-01 open Assets:US:BofA

# Close

语法与 Beancount 一致。

[YYYY-MM-DD] close ACCOUNT

示例:

// 输入
close Assets:US:BofA
// 输出
2019-07-01 close Assets:US:BofA

# Commodity

语法与 Beancount 一致。此版本暂不支持 Metadata 的设置。

[YYYY-MM-DD] commodity SYMBOL

示例:

// 输入
commodity BTC
// 输出
2019-07-01 commodity BTC

# Option

语法:

option [NAME] VALUE

NAME 和 VALUE 一般需要放在双引号中,但在以下几种情况下除外:

  • 如果 Name 为 title,此时 Name 可以省略,直接使用 option Value 的格式,Value 也无需使用双引号;
  • 如果 Value 值为 ISO 4217 中的货币代码,此时 Name 可以省略(会自动解析成 operating_currency),Value 也无需使用双引号。

示例:

// 输入
option Example Costflow file
// 输出
option "title" "Example Costflow file"

// 输入
option CNY
// 输出
option "operating_currency" "CNY"

// 输入
option "conversion_currency" "NOTHING"
// 输出
option "conversion_currency" "NOTHING"

# Note

语法:

[YYYY-MM-DD] note ACCOUNT DESCRIPTION

说明:

  • ACCOUNT 可以是账户全称也可以是预先设置的缩写;
  • DESCRIPTION 为任意文字,无需使用双引号。

示例:

// 输入
note bofa Called about fraudulent card.
// 输出
2019-07-01 note Assets:US:BofA:Checking "Called about fraudulent card."

# Balance

语法:

[YYYY-MM-DD] balance ACCOUNT AMOUNT [COMMODITY]

说明:

  • ACCOUNT 可以是账户全称也可以是预先设置的缩写;
  • COMMODITY 可以省略,默认为预先设置的货币代码。

示例:

// 输入
balance bofa 360
// 输出
2019-07-01 balance Assets:US:BofA:Checking 360 USD

// 输入,使用日期简写。因为 Beancount 会默认 Balance 执行时间为当日零点,所以日期通常为后一天。 https://docs.google.com/document/d/1wAMVrKIA2qtRGmoVDSUBJGmYZSygUaR0uOMW1GV3YE0/edit#heading=h.l0pvgeniwvq8
tmr balance bofa 360
// 输出
2019-07-02 balance Assets:US:BofA:Checking 360 USD

# Pad

语法:

[YYYY-MM-DD] pad ACCOUNT ACCOUNT_PAD

说明:

  • ACCOUNT 和 ACCOUNT_PAD 可以是账户全称也可以是预先设置的缩写;
  • Pad 操作请谨慎使用,详情可参考 Beancount 文档

示例:

// 输入
pad bofa eob
// 输出
2019-07-01 pad Assets:US:BofA:Checking Equity:Opening-Balances

# Price

语法:

[YYYY-MM-DD] price COMMODITY_A [PRICE] [COMMODITY_B]

说明:

  • 如果 COMMODITY_B 省略,则默认为 COMMODITY_B 是你预设的货币代码;

  • 可以手动输入 PRICE,即 PRICE 为阿拉伯数字,此时用法与 Beancount 一致;

  • 如果没有输入 PRICE,Costflow 将会自动在货币之间机型转换(数据源为 Alpha Vantage),转换优先级为:

    1. 首先判断 COMMODITY_A 是否是 ISO 4217 中的货币代码,如果是则进行汇率之间的转换;
    2. 其次判断 COMMODITY_A 是否是 Alpha Vantage 支持的加密货币代码(支持列表),如果是则进行转换;
    3. 最后将 COMMODITY_A 作为股票代码进行当前价格的查询,目前只支持美股(NASDAQ 和 NYSE)。注意此时不能指定 COMMODITY_B,而是以股票所在市场的货币为准。

示例:

// 输入
2017-01-17 price USD 1.08 CAD
// 输出
2017-01-17 price USD 1.08 CAD

// 输入,自动查询当前汇率。为了方便理解可以使用 to 链接两个货币代码,可以省略
price CAD to USD
// 输出
2019-07-01 price 

// 输入,查询当前比特币价格
price BTC
// 输出
2019-07-01 price BTC 11946.64 USD

// 输入,查询苹果股票价格
price AAPL
// 输出
2019-07-01 price AAPL 199.8 USD

# $

$ 作为指令符时用于查询法定货币、加密货币的汇率,以及股票的最新价格。查询结果将直接返回,不会同步到文件中。 $ 的语法与 price 有些类似:

$ [AMOUNT] COMMODITY_A [to] COMMODITY_B

说明:

  • 如果 COMMODITY_B 省略,则默认为 COMMODITY_B 是你预设的货币代码;
  • 如果不输入 AMOUNT,则默认 AMOUNT 为 1;
  • to 只是为了方便语义通顺,可以省略;
  • 转换优先级与 price 相同:
    1. 首先判断 COMMODITY_A 是否是 ISO 4217 中的货币代码,如果是则进行汇率之间的转换;
    2. 其次判断 COMMODITY_A 是否是 Alpha Vantage 支持的加密货币代码(支持列表),如果是则进行转换;
    3. 最后将 COMMODITY_A 作为股票代码进行当前价格的查询,目前只支持美股(NASDAQ 和 NYSE)。注意此时不能指定 COMMODITY_B,而是以股票所在市场的货币为准。

示例:

// 输入,自动查询当前汇率。为了方便理解可以使用 to 链接两个货币代码,可以省略
$ CAD to USD
// 输出
1 CAD = 0.7637 USD

// 输入,查询当前比特币价格,可以加入数量
$ 10 BTC
// 输出
10 BTC = 119466.4 USD

// 输入,查询苹果股票价格
$ AAPL
// 输出
AAPL 199.8 USD (-0.030%)

# Event

语法:

[YYYY-MM-DD] event NAME VALUE

说明:

  • NAME 和 VALUE 可以使用双引号的形式;
  • 如果语句中没有双引号出现,则视语句中(除日期外)第一个单词为 NAME,剩余部分为 VALUE。

示例:

// 输入:传统方式
2017-01-02 event "location" "Paris, France"
// 输出
2017-01-02 event "location" "Paris, France"

// 输入:省略日期与双引号
event location Paris, Francce
// 输出
2017-01-02 event "location" "Paris, France"

# 相关链接