概述

内容

Ledger 是一个命令行会计工具,提供基于文本日记的复式记账。它没有花哨的功能,带用户回到用户界面还未在他们父亲的 CRT 显示器中闪现的时代。

Ledger 是一种会计工具,具有生存的勇气。它没有任何花哨的功能,将用户带回到用户界面甚至还未在他们父亲的 CRT 显示器上闪现的日子。

它所提供的是一个双重记账的会计日记,具有现代同类工具的所有灵活性和强大功能,而没有任何多余的部分。可以把它看作是会计工具中的全麦松饼。

要使用它,您需要开始记账。这是所有会计的基础,如果您还没有开始,现在是学习的时机。随您的支票簿附带的小册子就是一本日记,因此我们将以此来描述复式记账。

支票簿日记记录借方(减少或取款)和贷方(增加或存款),并参考单一账户:支票账户。资金来源和去向在收款人字段中描述,您可以在此写下个人或公司的名称。保持支票簿日记的最终目的是了解可支配的资金有多少。这实际上是所有日记的目的。

计算机所增加的是浏览这些帖子并告诉你关于消费习惯的信息的能力;让你制定预算并控制开支;在不需要实际移动资金的情况下,将钱存入虚拟储蓄账户;等等。当你保持日记时,你在记录关于你的生活和习惯的信息,有时这些信息会开始告诉你一些你未曾意识到的事情。这就是所有优秀会计工具的目标。

从支票簿日记本向上迈出的下一步是一个记录所有账户的日记本,而不仅仅是支票账户。在这样的日记本中,您不仅记录谁收到了付款——在借记的情况下——还记录钱的来源。在支票簿日记本中,假设所有的钱都来自您的支票账户。但在一般日记本中,您在两行中写入记录:源账户和目标账户。每个账户的每次贷记必须至少有一个账户的借记。这就是所谓的“双重记账”会计:日记本必须始终平衡为零,借记和贷记的数量相等。

例如,假设您有一个支票账户和一个经纪账户,并且您可以从这两个账户中开支票。与其保留两个支票簿,不如决定使用一个日记本来记录这两个账户。在这个总日记中,您需要记录向太平洋贝尔支付的每月电话费,以及从您的经纪账户转账(通过支票)到您的支票账户。假设太平洋贝尔的账单是23.00美元,您想从支票账户支付。在总日记中,您需要说明资金的来源,以及资金的去向。这些交易可能看起来像这样:

9/29 太平洋贝尔 $23.00 $23.00 支票 $-23.00 0 9/30 支票 $100.00 $100.00 (123) 经纪公司 $-100.00 0

该记录必须平衡为 $0:$23 转入太平洋贝尔,$23 来自支票账户。下一条记录显示支票号码 123 是从您的经纪账户开出的,转账到您的支票账户。由于资金在这两种情况下只是从一个账户转移到另一个账户,因此没有多余的资金需要核算。这就是复式记账的基础:资金从未凭空产生或消失;它始终是从一个账户到另一个账户的记录。

保持一个普通日记本就像保持两个独立的日记本:一个用于太平洋贝尔,一个用于支票。在这种情况下,每次在一个日记本中写入付款时,你都要在另一个日记本中写入相应的取款。这使得写入“实时余额”变得更容易,因为你不必回头查看上次引用账户的时间——但这也意味着如果你处理多个账户,就需要很多日记本。

这里是一个关于“账户”一词使用的旁注的好地方。大多数私人认为账户是指在某个机构为他们存放资金的东西。Ledger 对这个词的定义更为广泛。账户是指资金可以去的任何地方。其他财务程序使用“类别”,而 Ledger 使用账户。因此,例如,如果你在 Trader Joe’s 买了一些杂货,然后在 Whole Food Market 买了更多杂货,你可能会像这样分配这些交易。

2011/03/15 Trader Joe's 费用:杂货 $100.00 资产:支票 2011/03/15 Whole Food Market 费用:杂货 $75.00 资产:支票

在这两种情况下,钱都进入了“杂货”账户,即使收款人不同。您可以根据自己的选择设置账户。

进入计算机化会计的美丽。Ledger程序的目的是通过为您跟踪余额,使总账会计变得简单。您唯一的工作是输入记账。如果单个记账不平衡,Ledger会显示错误并指示不正确的记账。1

总之,Ledger 使用有两个方面:更新日记数据文件,以及使用 Ledger 工具查看您交易的汇总结果。

为了举例说明——作为那些想要全心投入的人的起点——以下是上面的日记交易,按照Ledger程序希望看到的格式呈现:

2004/09/29 太平洋贝尔 费用:太平洋贝尔 $23.00 资产:支票

如果将此文件中的账户余额和登记保存为 ledger.dat,可以使用以下方式报告:

$ ledger -f ledger.dat 余额

         $-23.00  资产:支票账户
          $23.00  费用:太平洋贝尔

               0

$ ledger -f ledger.dat 注册 支票

04-九月-29 太平洋贝尔 资产:支票账户 $-23.00 $-23.00

甚至:

$ ledger -f ledger.dat 注册 Bell

04-九月-29 太平洋贝尔 费用:太平洋贝尔 $23.00 $23.00

Ledger与其他财务软件之间一个重要的区别是,Ledger永远不会更改您的输入文件。您可以以任何您喜欢的方式创建和编辑该文件,但Ledger仅用于分析数据,而不是用于更改数据。

Ledger 是用 ANSI C++ 编写的,应该可以在任何 Unix 平台上编译。构建和安装 ledger 的最简单方法是使用准备好的 acprep 脚本,它完成了很多基础工作:

# 安装缺失的依赖
./acprep dependencies
# 构建账本
./acprep update
# 运行实际安装
make install

请参阅‘acprep‘的‘help‘子命令,它解释了许多选项。您可以运行‘make check‘来确认结果,并运行‘make install‘进行安装。如果这些说明对您无效,您可以查看源目录中的‘INSTALL.md‘以获取最新的构建说明。

Ledger 拥有一个基于 GNU Info 的完整在线帮助系统。可以通过命令行直接搜索本手册,使用 info ledger,这将在您的 TTY 中显示整个手册。或者,可以通过命令行访问更简短的手册页,使用 man ledgerledger --help

如果您需要有关如何使用 Ledger 的帮助,或遇到问题,您可以加入 Ledger 邮件列表,访问 http://groups.google.com/group/ledger-cli

您还可以在 IRC 服务器 irc.libera.chat#ledger 频道中找到帮助。

有很多人使用 Ledger 进行会计应用。一些人记录了他们如何利用 Ledger 的功能来解决他们的会计问题。

一个专门为寻求使用 Ledger 的非营利慈善机构设计的教程可以在 https://k.sfconservancy.org/NPO-Accounting/npo-ledger-cli 找到(在 GitHub 上也有副本,地址为 https://github.com/conservancy/npo-ledger-cli/)。如果您正在寻找有关如何使用 Ledger 的标签系统来处理发票、按项目目标跟踪费用以及其他类似概念的信息,您可能会发现这个教程很有用。(与上述 Ledger 设置相关的一些审计报告脚本可以在 Ledger 自己的源代码库中的 contrib/non-profit-audit-reports/ 找到。)

日记是您财务交易的记录,将是使用 Ledger 的核心。现在我们只想体验一下 Ledger 能做什么。示例日记文件随源代码分发一起提供,名为 drewr3.dat(见 示例日记文件)。将其复制到一个方便的地方,并在该目录中打开一个终端窗口。

如果你更愿意立即开始自己的日记,请参见 保持日记

请注意,作为一个命令行程序,Ledger 是通过你的 shell 控制的。有几种不同的命令 shell,它们在某些特殊字符的处理上略有不同。特别是,“bash” shell 会以不同于 ledger 的方式解释‘$’符号,必须进行转义才能到达实际程序。另一个例子是“zsh”,它会以不同于 ledger 预期的方式解释‘^’。在接下来的所有情况下,你在输入给定的命令行参数时应考虑到这一点。不同 shell 之间的变体太多,无法为每种情况提供具体示例。

显示所有交易和累计总额:

$ ledger -f drewr3.dat 注册

分类账将生成:

10-Dec-01 检查余额 资产:检查 $ 1,000.00 $ 1,000.00 权益:期初余额 $ -1,000.00 0 10-Dec-20 有机合作社 费用:食品:杂货 $ 37.50 $ 37.50 费用:食品:杂货 $ 37.50 $ 75.00 费用:食品:杂货 $ 37.50 $ 112.50 费用:食品:杂货 $ 37.50 $ 150.00 费用:食品:杂货 $ 37.50 $ 187.50 费用:食品:杂货 $ 37.50 $ 225.00 资产:检查 $ -225.00 0 10-Dec-28 阿克梅抵押 负债:抵押:本金 $ 200.00 $ 200.00 费用:利息:抵押 $ 500.00 $ 700.00 费用:托管 $ 300.00 $ 1,000.00 资产:检查 $ -1,000.00 0 11-Jan-02 杂货店 费用:食品:杂货 $ 65.00 $ 65.00 资产:检查 $ -65.00 0 11-Jan-05 雇主 资产:检查 $ 2,000.00 $ 2,000.00 收入:工资 $ -2,000.00 0 (负债:十分之一) $ -240.00 $ -240.00 11-Jan-14 银行 资产:储蓄 $ 300.00 $ 60.00 资产:检查 $ -300.00 $ -240.00 11-Jan-19 杂货店 费用:食品:杂货 $ 44.00 $ -196.00 资产:检查 $ -44.00 $ -240.00 11-Jan-25 银行 资产:检查 $ 5,500.00 $ 5,260.00 资产:储蓄 $ -5,500.00 $ -240.00 11-Jan-25 汤姆的二手车 费用:汽车 $ 5,500.00 $ 5,260.00 资产:检查 $ -5,500.00 $ -240.00 11-Jan-27 书店 费用:书籍 $ 20.00 $ -220.00 负债:万事达卡 $ -20.00 $ -240.00 11-Dec-01 销售 资产:检查:业务 $ 30.00 $ -210.00 收入:销售 $ -30.00 $ -240.00 (负债:十分之一) $ -3.60 $ -243.60

要将其限制为更有用的子集,只需添加您感兴趣查看交易的账户:

$ ledger -f drewr3.dat 登记 食品杂货

10-Dec-20 有机合作社 费用:食品:杂货 $ 37.50 $ 37.50 费用:食品:杂货 $ 37.50 $ 75.00 费用:食品:杂货 $ 37.50 $ 112.50 费用:食品:杂货 $ 37.50 $ 150.00 费用:食品:杂货 $ 37.50 $ 187.50 费用:食品:杂货 $ 37.50 $ 225.00 11-Jan-02 杂货店 费用:食品:杂货 $ 65.00 $ 290.00 11-Jan-19 杂货店 费用:食品:杂货 $ 44.00 $ 334.00

与‘杂货’账户报告的余额相符:

$ ledger -f drewr3.dat 余额 食品杂货

        $ 334.00  费用:食品:杂货

如果您想仅查找特定收款人的交易,请使用‘payee’或‘@’:

$ ledger -f drewr3.dat register payee "有机"

10-Dec-20 有机合作社 费用:食品:杂货 $ 37.50 $ 37.50 费用:食品:杂货 $ 37.50 $ 75.00 费用:食品:杂货 $ 37.50 $ 112.50 费用:食品:杂货 $ 37.50 $ 150.00 费用:食品:杂货 $ 37.50 $ 187.50 费用:食品:杂货 $ 37.50 $ 225.00 资产:支票 $ -225.00 0

会计就是简单地跟踪你的资金。它可以从什么都不做,仅仅等待自动透支保护启动,或者不启动,到一个完整的复式记账系统。Ledger 实现了后者。使用 Ledger,你可以处理个人财务或企业财务。复式记账具有可扩展性。

会计师会谈论“贷方”和“借方”,但其含义往往与外行的理解不同。为了避免混淆,Ledger 仅使用减法和加法,尽管其基本意图与标准会计原则相同。

请记住,每个记录将涉及两个或多个账户。资金从一个或多个账户转移到一个或多个其他账户。为了记录该记录,从源账户中 减去 一定金额,并 添加 到目标账户。

为了正确地写入账本交易,您必须确定资金的来源和去向。例如,当您收到工资时,您必须将钱添加到您的银行账户,并从收入账户中扣除它。

9/29 我的雇主 资产:支票账户 $500.00 收入:工资 $-500.00

为什么收入是一个负数?当你查看账本的余额总额时,你可能会惊讶地发现支出是一个正数,而收入是一个负数。这可能需要一些时间来适应,但要正确使用总账,你必须从资金流动的角度来思考。与其让总账“修正”负号,不如理解它们存在的原因。

当你赚到钱时,这笔钱必须来自某个地方。我们称这个地方为“社会”。为了让社会给你收入,你必须从社会中提取钱(取款),然后将其存入你的银行。当你花费这笔钱时,它会从你的银行账户中流出(取款),并返回到社会(支付)。这就是为什么收入会显得为负——它反映了你从社会中提取的钱——而支出会显得为正——这是你返还的金额。这些加法和减法最终总会相互抵消,因为你没有创造新钱的能力:它必须总是来自某个地方,并且最终必须总是流出。这是经济学的开始,之后的解释会变得非常困难。

根据这个解释,这里有另一种看待您的余额报告的方法:每一个负数意味着该账户、个人或地点现在的钱比您开始记账时少;而每一个正数意味着该账户、个人或地点现在的钱比您开始记账时多。明白了吗?

资产是你拥有的钱,而负债是你欠的钱。“负债”只是债务的一个更广泛的名称。

资产通常通过从收入账户转移资金来增加,例如当你获得薪水时。以下是一个典型的交易:

2004/09/29 我的雇主 资产:支票 $500.00 收入:工资

钱,来自于属于‘我的雇主’的收入账户,并转入你的支票账户。现在这笔钱是你的,这使它成为一种资产。

负债跟踪欠他人的钱。这可能发生在你借钱购买某物时,或者如果你欠某人钱时。以下是通过使用万事达卡消费来增加负债的一个例子:

2004/09/30 餐厅 费用:餐饮 $25.00 负债:万事达卡

餐饮账户余额现在显示已在餐饮上花费了25美元,并且在万事达卡上相应欠款25美元——因此显示为$-25.00。万事达卡负债显示为负数,因为它抵消了您资产的价值。

您资产和负债的总和就是您的净资产。因此,要查看您当前的净资产,请使用此命令:

$ 账本余额 ^资产 ^负债

         $500.00  资产:支票账户
         $-25.00  负债:信用卡

         $475.00

类似地,您的收入账户显示为负数,因为它们是从一个账户转移资金以增加您的资产。您的支出显示为正数,因为那是资金流向的地方。收入和支出的总和就是您的现金流。正现金流意味着您花费的超过您所赚取的,因为收入始终是一个负数。要查看您当前的现金流,请使用此命令:

$ 账本余额 ^收入 ^支出

          $25.00  费用:餐饮
        $-500.00  收入:工资

        $-475.00

另一个常见的问题是:我每个月在X上花了多少钱?Ledger提供了一种简单的方法来显示任何账户的月总额。以下是一个总结您每月汽车开支的示例:

$ ledger -M register -f drewr3.dat 费用:自动

11-Jan-01 - 11-Jan-31 费用:汽车 $ 5,500.00 $ 5,500.00

这当然假设你使用像‘Expenses:Auto:Gas’和‘Expenses:Auto:Repair’这样的账户名称。

有时你会想要代表其他人花钱,这最终会得到偿还。由于这笔钱仍然是_你的_,它实际上是一项资产。由于支出是为了其他人,你不希望它污染你的费用报告。你需要保持一个账户来跟踪报销。

在账本中,这相对容易做到。当花费资金时,将其花费到你的资产:报销,使用不同的账户为每个你花钱的人或企业。例如:

2004/09/29 Circuit City 资产:报销:公司 XYZ $100.00 负债:万事达卡

这显示了在Circuit City用MasterCard消费的$100.00,该费用是代表公司XYZ产生的。后来,当公司XYZ偿还该金额时,资金将从该报销账户转回到常规资产账户:

2004/09/29 公司 XYZ 资产:支票 $100.00 资产:报销:公司 XYZ

这将公司XYZ所欠的款项存入一个支票账户,推测是因为他们用支票偿还了该金额。

但是如果你经营自己的生意,并且想要跟踪自己所花费的费用,同时又想在一个单一的账本文件中记录所有内容,该怎么办呢?这就更复杂了,因为你需要跟踪两个独立的事项:1)这笔钱应该报销给你,2)费用账户是什么,以便你可以稍后确定你的公司在哪里花费了钱。

这种记录最好在两个不同的文件中进行镜像记录,一个用于个人账户,一个用于公司账户。但将它们保存在一个文件中涉及相同类型的记录,因此这里展示的就是这些。首先是个人交易,显示了报销的需求:

2004/09/29 Circuit City 资产:报销:公司 XYZ $100.00 负债:万事达卡

这与上面相同,唯一的区别是你拥有公司 XYZ,并且在同一个账本文件中跟踪其费用。此交易后应立即跟随一笔等值交易,显示费用的种类,并注明现在应支付给你的金额为 $100.00:

2004/09/29 Circuit City 公司 XYZ:费用:计算机:软件 $100.00 公司 XYZ:应付账款:您的名字

这第二笔交易显示公司 XYZ 刚刚花费了 $100.00 购买软件,而这 $100.00 来自您的名字,必须偿还。

这两笔交易也可以合并,以使事情更清晰。请注意,现在必须指定所有金额:

2004/09/29 Circuit City 资产:报销:公司 XYZ $100.00 负债:万事达卡 $-100.00 公司 XYZ:费用:计算机:软件 $100.00 公司 XYZ:应付账款:您的名字 $-100.00

要“偿还”报销,只需将所有内容的顺序反转,除了这次从公司资产中提取资金,支付给应付账款,然后再从报销账户中提取资金,支付到你的个人资产账户。说起来容易,做起来难:

2004/10/15 公司 XYZ 资产:支票账户 $100.00 资产:报销:公司 XYZ $-100.00 公司 XYZ:应付账款:您的名字 $100.00 公司 XYZ:资产:支票账户 $-100.00

现在,报销账户已结清,应付账款已结清,$100.00 已有效地从公司的支票账户转入您的个人支票账户。这笔钱只是“等待”——在‘资产:报销:公司 XYZ’和‘公司 XYZ:应付账款:您的名字’中——直到可以结清为止。

从两方面跟踪费用的价值在于,您不会将代表他人产生的费用污染您的个人费用报告,同时也使得生成公司支出准确报告成为可能。这比仅仅用个人资产支付费用更冗长,但它为您提供了非常准确的信息记录。

将这些双重交易保持在一起的好处是它们始终保持同步。将它们分开的好处是它澄清了转移的观点。要将记录保存在单独的文件中,只需分开上面连接的两个交易。例如,对于上面显示的费用和偿还,将创建以下四个交易。在您的个人分类账文件中:

2004/09/29 Circuit City 资产:报销:公司 XYZ $100.00 负债:万事达卡 $-100.00

2004/10/15 公司 XYZ 资产:支票 $100.00 资产:报销:公司 XYZ $-100.00

并且在您的公司账本文件中有两个:

申请账户 公司 XYZ

2004/09/29 Circuit City 费用:计算机:软件 $100.00 应付账款:您的名字 $-100.00

2004/10/15 公司 XYZ 应付账款:您的名字 $100.00 资产:支票 $-100.00

结束申请账户

(注意:上面的 apply account 意味着文件中提到的所有账户都是该账户的子账户。在这种情况下,这意味着文件中的所有活动都与公司 XYZ 相关。)

在创建这些交易后,您将始终知道 $100.00 是通过您的 MasterCard 代表公司 XYZ 支出的,并且公司 XYZ 将这笔钱用于计算机软件,并在大约两周后偿还。

$ 账本余额 --无总计

         $100.00  资产:检查
               0  公司 XYZ
        $-100.00    资产:检查
         $100.00    费用:计算机:软件
        $-100.00  负债:万事达卡

Ledger 不对您使用的商品做出任何假设;它只要求您指定一个商品。商品可以是任何不包含句点、逗号、斜杠或 @ 符号的非数字字符串。它可以出现在金额之前或之后,尽管假设出现在金额之前的符号指的是货币,而出现在金额之后的非连接符号指的是商品。以下是一些有效的货币和商品说明符:

$20.00 ; 货币: 二十美元 40 AAPL ; 商品: 40 股苹果股票 60 DM ; 货币: 60 德国马克 £50 ; 货币: 50 英镑 50 EUR ; 货币: 50 欧元(或使用适当符号)

Ledger 将检查任何商品的首次使用,以确定该商品在报告上的打印方式。它关注商品名称是否与金额分开,商品名称是在金额之前还是之后,指定金额时使用的精度,是否使用了千位分隔符等。这是为了确保商品的打印方式与您使用它的方式看起来相同。

一个账户可能包含多种商品,在这种情况下,它将为每种商品有单独的总额。例如,如果您的经纪账户同时包含现金、黄金和几种股票数量,则余额可能如下所示:

$200.00 100.00 澳元 AAPL 40 BORL 100 FEQTX 50 资产:经纪公司

此余额报告显示您在经纪账户中每种商品的数量。

有时,您会想知道您余额的当前街头价值,而不是商品总额。为了实现这一点,您必须指定每种商品的当前价格。价格可以是任何商品,在这种情况下,余额将以该商品为单位进行计算。指定价格的通常方法是使用价格历史文件,可能看起来像这样:

P 2004/06/21 02:18:01 FEQTX $22.49 P 2004/06/21 02:18:01 BORL $6.20 P 2004/06/21 02:18:02 AAPL $32.91 P 2004/06/21 02:18:02 AU $400.00

指定要与 --price-db FILE 选项一起使用的价格历史,使用 --market (-V) 选项以当前市场价值报告:

$ ledger --price-db prices.db -V 余额 经纪公司

您的经纪账户余额将以美元报告,因为价格数据库使用该货币。

$40880.00 资产:经纪公司

您可以将任何商品转换为任何其他商品。假设您在支票账户中有5000美元,无论出于什么原因,您想知道这可以购买多少盎司黄金,按照当前黄金价格计算:

$ ledger -X AU 余额检查

此命令的结果可能是:

有时,一种商品有几种形式,这些形式都是等价的。一个例子就是时间。无论是以分钟、小时还是天来计算,都应该能够在各种形式之间进行转换。做到这一点需要使用商品等价性。

例如,您可能有以下两个记录,一个将一个小时的时间转入“可计费”账户,另一个将同一账户减少十分钟。生成的报告将显示剩余五十分钟:

2005/10/01 为公司完成的工作 可计费:客户 1小时 项目:XYZ

2005/10/02 返回项目十分钟 项目:XYZ 10m 可计费:客户

报告此账本文件的余额产生:

$ ledger --no-total balance 可计费项目

           50.0m  可计费:客户
          -50.0m  项目:XYZ

这个例子之所以有效,是因为账本已经知道如何处理秒、分钟和小时,作为其时间跟踪支持的一部分。定义其他等价关系很简单。以下是一个创建数据等价关系的例子,有助于跟踪字节、千字节、兆字节等:

C 1.00 Kb = 1024 b C 1.00 Mb = 1024 Kb C 1.00 Gb = 1024 Mb C 1.00 Tb = 1024 Gb

这些定义将一种商品(例如‘Kb’)和默认精度与另一种商品的某个数量相关联。在上述示例中,千字节以两位小数的精度报告,每千字节等于1024字节。

等价链可以根据需要延长。每当商品以小数金额(少于‘1.00’)报告时,将使用下一个最小的商品。如果某个商品可以用更高的商品来报告而不导致部分分数,则使用更大的商品。

由于Ledger的账户和商品系统非常灵活,您可以拥有实际上并不存在的账户,并使用其他人不承认的商品。例如,假设您在EverQuest中买卖各种物品,并希望使用账本来跟踪它们。只需将您希望的任意数量的物品添加到您的EverQuest账户中:

9/29 在旅馆获取一些物品 地点:黑氏酒馆 -3 个苹果 地点:黑氏酒馆 -5 块牛排 无尽的任务:库存

现在你的 EverQuest:Inventory 中有 3 个苹果和 5 块牛排。数量为负,因为你是从 Black’s Tavern 取出以添加到你的 Inventory 账户。请注意,你不必使用 ‘Places:Black's Tavern’ 作为源账户。你可以使用 ‘EverQuest:System’ 来表示你是在线获取它们的。选择某种源账户而不是另一种的唯一目的,是为了在后面生成更有信息量的报告。你知道的越多,你能进行的分析就越好。

如果你后来将这些物品中的一些出售给其他玩家,交易将看起来像:

10/2 风暴亮刃 无尽的任务:库存 -2 块牛排 无尽的任务:库存 15 黄金

现在你把2块牛排变成了15金币,感谢你的顾客,斯图姆·布莱特布雷德。

$ 账本余额 EverQuest

        3 苹果
         15 黄金
        3 牛排  EverQuest:Inventory

任何账本中最令人困惑的交易将是你的股权账户——因为期初余额不可能凭空而来。

当你第一次开始使用你的账本时,你可能已经在某些账户中有钱。假设你的支票账户中有100美元;然后在你的账本中添加一笔交易以反映这个金额。这笔钱来自哪里?答案是:你的权益。

10/2 开始余额 资产:支票账户 $100.00 权益:开始余额

但什么是权益?当人们谈论房屋抵押贷款时,你可能听说过权益,指的是“你拥有的房屋部分”。基本上,权益就像某物的价值。如果你拥有一辆价值5000美元的汽车,那么你在这辆车上的权益就是5000美元。为了将这辆车(商品)转变为现金流,或是存入你银行账户的信用,你需要通过出售它来借记这部分权益。

当你开始一个账本时,你可能已经有了净资产。你的净资产就是你当前的权益。通过将账本中的资金从你的权益转移到你的银行账户,你是在根据你之前的权益对账本账户进行记账。这就是为什么,当你查看余额报告时,你会看到权益的一个大负数,这个数字从未改变:因为那是你在资金开始流动之前的价值(你为了开始账本而从自己那里借出的金额)。如果你资产的总正值大于你起始权益的绝对值,这意味着你正在赚钱。

清晰如泥?继续思考。直到你弄明白为止,在你的余额命令末尾加上 not Equity,以从总数中去除令人困惑的数字。

许多人不愿意记账的原因之一是跟踪小额现金支出是非常麻烦的。它们很少产生收据,而且通常有很多小额记录,而不是像支票那样只有少数几笔大额记录。

一个解决方案是:别费心。把你的消费转到借记卡上,但一般来说忽略现金。一旦你从ATM取出现金,就将其标记为已花费到‘费用:现金’类别:

2004/03/15 ATM 费用:现金 $100.00 资产:支票

如果在某个时刻你产生了一笔想要追踪的大额现金支出,只需将支出的金额从‘Expenses:Cash’转入目标账户:

2004/03/20 某人 费用: 食物 $65.00 费用: 现金

通过这种方式,您仍然可以跟踪大额现金支出,同时忽略所有较小的支出。

有些情况下,您跟踪的账户在您的客户和资金存放的金融机构之间是不同的。一个例子是作为宗教机构的财务主管。从世俗的角度来看,您可能正在处理三个不同的账户:

  • 支票账户
  • 储蓄账户
  • 信用卡

从宗教的角度来看,社区期望将其资源分成多个“基金”,从中进行购买或为以后保留资源:

  • 学校基金
  • 建筑基金
  • 社区基金

这种设置的问题在于,当你花钱时,它同时来自两个或多个地方:账户和基金。然而,基金和账户之间的金额相关性很少是一对一的。如果学校基金有‘$500.00’,但其中‘$400.00’来自支票账户,而‘$100.00’来自储蓄账户,那该怎么办?

传统金融方案要求资金只能存在一个地方。但实际上数据有两种“视角”:从账户的角度和从资金的角度——然而这两组数据应该反映相同的整体费用和现金流。只是资金存在的地方不同。

这种情况可以通过两种方式处理。第一种是使用虚拟记录来表示资金同时在两种账户之间流动的事实:

2004/03/20 贡献 资产:支票账户 $500.00 收入:捐款

2004/03/25 捐款分配 [资金:学校] $300.00 [资金:建筑] $200.00 [资产:支票] $-500.00

在第二笔交易中使用方括号确保虚拟记录的余额为零。现在可以在从实体账户提取资金的同时,直接从一个基金中支出资金:

2004/03/25 书籍付款(从支票账户支付) 费用:书籍 $100.00 资产:支票账户 $-100.00 (资金:学校) $-100.00

使用圆括号创建了一个虚拟的过账,而没有确保余额为零。当生成报告时,默认情况下它们将以资金的形式出现。在这种情况下,您可能希望隐藏您的“资产”账户,因为否则余额将没有太大意义:

$ ledger --no-total bal not ^资产

         $100.00  费用:书籍
         $400.00  资金
         $200.00    建筑
         $200.00    学校
        $-500.00  收入:捐赠

如果使用 "--real" 选项,报告将以实际账户为准:

$ ledger --real --no-total bal

         $400.00  资产:检查
         $100.00  费用:书籍
        $-500.00  收入:捐赠

如果需要更多的资产账户作为过账的来源,只需像往常一样列出它们,例如:

2004/03/25 书籍付款(从支票账户支付) 费用:书籍 $100.00 资产:支票账户 $-50.00 负债:信用卡 $-50.00 (资金:学校) $-100.00

跟踪资金的第二种方法是使用交易代码。在这方面,代码就像虚拟账户,涵盖了整个记账集。基本上,我们通过设置代码将交易与资金关联起来。以下是两笔交易,分别向‘Funds:School’基金存入资金和从中支出资金:

2004/03/25 (资金:学校) 捐款 资产:支票 $100.00 收入:捐款

2004/03/25 (资金:建筑) 捐款 资产:支票 $20.00 收入:捐款

2004/04/25 (资金:学校) 书籍付款 费用:书籍 $50.00 资产:支票

请注意,账户现在仅与实际账户相关,任何余额或登记报告将反映这一点。与特定基金相关的交易仅在代码中保留。

这如何变成一份基金报告?通过使用 --payee=code 选项,您可以生成一份登记报告,其中每个记录的收款人显示代码。单独来看,这并不是特别有趣;但当与 --by-payee (-P) 选项结合使用时,您将看到与特定基金相关的任何记录的账户小计。因此,要查看所有基金的当前货币余额,命令将是:

$ ledger --payee=code -P reg ^资产

04-Mar-25 资金:建筑 资产:支票 $20.00 $20.00 04-Mar-25 资金:学校 资产:支票 $50.00 $70.00

或者查看特定基金的费用,以本例中的‘学校’基金为例:

$ ledger --payee=code -P reg ^费用 和 code 学校

04-Apr-25 资金:学校 支出:书籍 $50.00 $50.00

这两种方法提供了不同类型的灵活性,具体取决于您更喜欢如何看待您的资金:作为虚拟账户,还是作为与特定交易相关的标签。您自己的喜好将决定哪种方法最适合您的情况。

会计中最重要的部分是保持良好的日记。如果你有一个好的日记,可以编写工具来处理你需要的任何数学技巧,以更好地理解你的消费模式。没有一个好的日记,任何工具,无论多么智能,都无法帮助你。

Ledger 程序旨在使日记交易尽可能简单。由于它是一个命令行工具,因此不提供用于保持日记的用户界面。如果您需要一个用户界面来维护日记交易,GnuCash 是一个不错的替代方案。

如果您不是在使用 GnuCash,而是使用文本编辑器来维护您的日记,请继续阅读。Ledger 的设计旨在使数据交易尽可能简单,通过保持日记格式的简易性,并且通过自动确定尽可能多的信息,基于您交易的性质。

例如,您不需要告诉 Ledger 您使用的账户。每当 Ledger 看到涉及一个它不知道的账户的记录时,它会创建该账户2。如果您使用了 Ledger 新的商品,它会创建该商品,并根据您在记录中使用该商品的方式来确定其显示特性(符号在金额前或后,显示精度等)。

这里是上面提到的太平洋贝尔的例子,作为一个分类账条目,附加了支票号码:

9/29 (1023) 太平洋贝尔 费用:公用事业:电话 $23.00 资产:支票 $-23.00

正如你所看到的,它与纸上写的内容非常相似,减去了计算的余额总额,并添加了更符合Ledger方案的账户名称。事实上,由于Ledger在许多方面都很智能,如果余额与第一行相同,你不需要指定余额金额:

9/29 (1023) 太平洋贝尔 费用:公用事业:电话 $23.00 资产:支票

对于这笔交易,Ledger 将会计算出 $-23.00 必须来自 ‘资产:支票’ 以平衡交易。

还要注意账户条目的结构。通过用冒号分隔建立了一个隐含的层次结构(见 结构化您的账户)。

格式非常灵活,不必完全按照示例中的缩进和空格来排版。唯一的要求是交易的开始(通常是日期)位于交易的第一行开头,账户至少要缩进一个空格。如果您在账户行中省略了前导空格,Ledger 将会生成错误。金额和账户之间必须至少有两个空格或一个制表符。如果金额和账户之间的分隔不够,Ledger 将会报错并停止计算。

除非你最近从另一个星球到达,否则你已经有了一个财务状况。你需要记录下这个财务状况,以便账本有一个起点。

在某个方便的时间点,你知道你所有财务账户的余额和未偿还义务。这些金额构成了账本的开盘条目。例如,如果你选择2011年初作为开始使用账本跟踪财务的日期,你的开盘余额条目可能看起来像这样:

2011/01/01 * 期初余额 资产:联合支票 $800.14 资产:其他支票 $63.44 资产:储蓄 $2805.54 资产:投资:401K:递延 100.0000 VIFSX @ $80.5227 资产:投资:401K:匹配 50.0000 VIFSX @ $83.7015 资产:投资:IRA 250.0000 VTHRX @ $20.5324 负债:抵押贷款 $-175634.88 负债:汽车贷款 $-3494.26 负债:信用卡 -$1762.44 权益:期初余额

“期初余额”这个名称并没有什么特别之处,作为账户名称的收款人,任何你理解的方便名称都可以使用。

实际上,对你如何做这件事并没有要求,但为了保持你的理智,我们建议你的会计系统有一些非常基本的结构。

在最高层次上,你有五种账户类型:

  1. 费用:钱的去向,
  2. 资产:钱的存放,
  3. 收入:钱的来源,
  4. 负债:你所欠的钱,
  5. 股权:你财产的真实价值。

以这种方式开始结构将使您更容易获得关于您财务的真正需要询问的问题的答案。

在这些顶级账户下,您可以拥有任何您想要的详细程度。例如,如果您想具体跟踪您在汉堡和薯条上的消费,您可以拥有以下内容:

费用: 食物: 汉堡和薯条

Ledger 对于您如何评估账户是中立的。美元、欧元、英镑、法郎、股票等都只是“商品”。股票、债券、共同基金和其他金融工具的持有可以使用对您方便的任何标签(建议使用股票代码符号来标记公开交易的资产)。3

在本手册的其余部分,我们将仅在提及交易价值的单位时使用“商品”一词。

这在根本上与许多常见的会计软件不同,这些软件假设您所有账户中使用相同的货币。这意味着如果您通常使用欧元,但前往美国并产生一些费用,您必须在将条目录入财务系统之前进行货币转换。使用 ledger 这并不是必需的。在同一本日记中,您可以有任何或所有您实际持有的商品的条目。您可以使用报告功能将所有商品转换为单一商品以便于报告,而无需更改基础条目。

例如,以下条目反映了从美国前往欧洲的商务旅行所进行的交易:

2011/09/23 在慕尼黑的现金 资产:现金 €50.00 资产:支票账户 $-66.00

2011/09/24 慕尼黑晚餐 费用:商务:旅行 €35.00 资产:现金

这表示 $66.00 从支票账户中取出,变成了 50 欧元。隐含的汇率是 $1.32。然后在慕尼黑花费了 35.00 欧元用于晚餐。

运行账本余额报告显示:

$ ledger -f example.dat bal

         $-66.00
          €15.00  资产
          €15.00    现金
         $-66.00    支票
          €35.00  费用:业务:旅行

         $-66.00
          €50.00

前两行显示我的当前资产为支票账户 $-66.00(在这个非常简短的例子中,我没有为支票账户设定初始余额)和 €15.00。晚餐消费后,我的钱包里有 €15.00。底线平衡为零,但显示为两行,因为我们还没有告诉账本转换商品。

购买股票是一个典型的例子,许多人会用到,它涉及在同一交易中多种商品。股票的类型(AAPL代表苹果公司)和您购买时所用货币单位的股票购买价格(AAPL的$)。是的,典型的约定如下:

2004/05/01 股票购买 资产:经纪人 50 AAPL @ $30.00 费用:经纪人:佣金 $19.95 资产:经纪人 $-1,519.95

这假设您拥有一个能够管理流动资产和商品资产的经纪账户。现在,在销售的当天:

2005/08/01 股票出售 资产:经纪人 -50 AAPL {$30.00} @ $50.00 费用:经纪人:佣金 $19.95 收入:资本利得 $-1,000.00 资产:经纪人 $2,480.05

当然,你可以省略最后一条记录的金额。这样做是为了清晰。

‘{$30.00}’ 是一个批次价格。您还可以使用批次日期,‘[2004/05/01]’,或者两者都使用,以防您有多个相同价格/日期的批次,并且您的税收模型是基于最久持有优先。

您保留以便于稍后出售的商品具有随市场价格波动的可变价值。您消费的商品不应波动其价值,而应保持在购买时的批发价格。作为“批发定价”的延伸,您可以固定商品的单价。

例如,假设你以1.20美元的价格购买了10加仑汽油。在未来的“价值”报告中,你不希望这些加仑以今天的价格报告,而是以你购买时的价格报告。同时,你也希望其他类型的商品——比如股票——以今天的价格报告。

这支持如下:

2009/01/01 Shell 费用:汽油 11 加仑 {=$2.299} 资产:支票账户

这笔交易实际上引入了一种新商品,‘GAL {=$2.29}’,其市场价值不考虑未来汽油价格的任何变化。

如果您不想进行价格固定,您可以通过两种方式中的一种来指定此交易,两者等效(请注意与上述交易相比缺少等号):

2009/01/01 Shell 费用:汽油 11 加仑 {$2.299} 资产:支票

2009/01/01 Shell 费用:汽油 11 加仑 @ $2.299 资产:支票

这两种形式在意义上没有区别。你问为什么两者都存在?为了支持这样的事情:

2009/01/01 Shell 费用:汽油 11 加仑 {=$2.299} @ $2.30 资产:支票账户

此交易表示您以每加仑 $2.299 的价格购买了 11 加仑,您的 成本 为每加仑 $2.30。在这种情况下,账本自动生成一条余额记录到权益:资本损失,以反映 1.1 美分的差额,然后通过资产:支票账户进行平衡,因为其金额为零。

Ledger 允许您对商品的估值进行非常详细的控制。您可以使用 \--market 或 \--exchange COMMODITY 选项来微调给出的结果。现在有几个拦截点;您可以指定估值方法:

  1. 在商品本身上,
  2. 在一个发布上,通过元数据(效果与 #1 大致相同),
  3. 在一个交易上,这将适用于该交易中的所有发布,
  4. 在任何发布上通过自动化交易,
  5. 按账户基础,
  6. 按商品基础,
  7. 通过更改 market 的期刊默认值。

固定定价(例如‘{=$20}’)在这个方案中仍然发挥着作用。就估值而言,它是写作‘((s,d,t -> market($20,d,t)))’的简写。

一个估值函数接收三个参数:

一个标识所询问价格的商品的字符串(例如:‘EUR’)。

日期

价格应相对的参考日期。

目标

一个字符串,用于标识“目标”商品,或者返回的价格应该以该商品为单位。如果使用了 --market 而不是 --exchange COMMODITY,则此参数为 null。

估值函数应返回一个金额。如果您在 Python 中编写了函数,可以返回类似于 ‘Amount("$100")’ 的内容。如果函数返回一个明确的值,则该值始终被使用,无论商品、日期或期望的目标商品是什么。例如,

定义 myfunc t even(s, d, t) = 7 欧元

为了指定一个固定价格,但仍然将该价格评估到目标商品中,可以使用类似这样的内容:

定义 myfunc\_five(s, d, t) = market(5 欧元, d, t)

value 指令设置用于数据流其余部分中所有商品的估值。如果没有找到更具体的内容,这是后备选项。

您可以为每种商品设置特定的估值函数。您也可以传递一个 lambda,而不是定义一个函数。

商品 $ 价值 s, d, t -> 6 欧元

每个账户还可以为转移到该账户的任何商品提供默认估值函数。

账户 费用: 食物5 值 myfunc_five

元数据字段‘值’,如果找到,将覆盖交易范围或每个记账的估值函数。

= @XACT 和食品 ; 价值:: 8 欧元 (股权) $1

= @POST 和餐饮 (费用: 食物9) $1 ; 价值:: 9 欧元

最后,您可以使用‘(( ))’商品注释为任何特定金额指定估值函数/值。

2012-03-02 肯德基 费用:食品2 $1 ((2 欧元)) 资产:现金2

2012-03-03 KFC 费用:食物3 $1 ; 价值:: 3 欧元 资产:现金3

2012-03-04 肯德基 ; 价值:: 4 欧元 费用:食品4 $1 资产:现金4

2012-03-05 肯德基 费用: 食物5 $1 资产: 现金5

2012-03-06 肯德基 费用: 食物6 $1 资产: 现金6

2012-03-07 肯德基 费用: 食物7 1 加元 资产: 现金7

2012-03-08 XACT 费用:食品8 $1 资产:现金8

2012-03-09 POST 费用:餐饮9 $1 资产:现金9

12-Mar-02 KFC 费用:食品2 2 欧元 2 欧元 12-Mar-03 KFC 费用:食品3 3 欧元 5 欧元 12-Mar-04 KFC 费用:食品4 4 欧元 9 欧元 12-Mar-05 KFC 费用:食品5 $1 $1 9 欧元 12-Mar-06 KFC 费用:食品6 $1 $2 9 欧元 12-Mar-07 KFC 费用:食品7 1 加元 $2 1 加元 9 欧元 12-Mar-08 XACT 费用:食品8 $1 $3 1 加元 9 欧元

有时,Ledger 的灵活性可能会导致困难。使用自由格式的文本编辑器输入交易使得数据的保存变得简单,但也容易导致账户或收款人输入不一致或拼写错误。

为了应对不一致性,您可以定义允许的账户和收款人。为了简单起见,创建一个单独的文本文件,并定义账户和收款人,如

账户费用 账户费用:公用事业

使用 \--strict 选项将导致 Ledger 抱怨如果任何账户未被事先定义:

$ ledger bal --strict 警告: "FinanceData/Master.dat", 第 6 行: 未知账户 'Liabilities:Tithe Owed' 警告: "FinanceData/Master.dat", 第 8 行: 未知账户 'Liabilities:Tithe Owed' 警告: "FinanceData/Master.dat", 第 15 行: 未知账户 'Allocation:Equities:Domestic'

如果您已经创建了一个大的账本注册,请使用 accounts 命令开始:

$ 账本账户 >> Accounts.dat

您需要编辑此文件,在每一行前添加 account 指令。

账本文件格式相当简单,但也非常灵活。它支持许多选项,尽管通常用户可以忽略其中大部分。它们在下面进行了总结。

有许多工具可以帮助将各种格式转换为 Ledger 文件。大多数银行会生成一个逗号分隔值文件,可以使用这些工具中的一个轻松解析为 Ledger 格式。一些最受欢迎的工具包括:

直接从银行提取信息超出了Ledger的功能范围。

过一段时间后,你的日记可能会变得相当庞大。虽然这不会减慢 Ledger 的速度——它被设计为能够非常快速地处理日记——但事情可能会开始感觉“杂乱”;而且人们普遍抱怨,当财务感觉杂乱时,他们会避免处理它们。

因此,将前几年的数据归档到它们自己的文件中可以提供一种完成感,以及摆脱过去的自由。但是如何最好地使用账本程序来实现这一点呢?有两个命令可以使其变得非常简单:printequity

让我们以一个示例文件为例,数据范围从2000年到2004年。我们想将2000年和2001年的数据归档到它们自己的文件中,将2002年至2004年的数据保留在当前文件中。因此,使用 print 将所有早期的交易输出到一个名为 ledger-old.dat 的文件中:

$ ledger -f ledger.dat -b 2000 -e 2002 print > ledger-old.dat

请注意 -e 限制输出为指定日期 之前 的交易。

要从当前账本文件中删除旧数据,请再次使用 print,这次指定 2002 年作为起始日期:

$ ledger -f ledger.dat -b 2002 打印 > x $ mv x ledger.dat

然而,现在的文件仅包含2002年以后的记录,这将无法得出准确的现今余额,因为之前年份的净收入不再被计算。为了弥补这一点,我们必须在新账本的开头附加一份旧账本的权益报告:

$ ledger -f ledger-old.dat 权益 > equity.dat $ cat equity.dat ledger.dat > x $ mv x ledger.dat $ rm equity.dat

现在从 ledger.dat 报告的余额与数据拆分前的余额完全相同。

你应该多久拆分一次账本?如果你不想的话,实际上不需要拆分。即使是八十年的数据也不会显著减慢账本的速度,而这仅仅是使用现代硬件的结果!或者,你可以将前一年和当前年份放在一个文件中,而将之前的每一年放在各自的文件中。这完全取决于你,以及你想如何组织你的财务。对于那些也保持准确纸质记录的人来说,将较旧的年份归档到自己的文件中,然后将这些文件刻录到CD上以便与纸质记录一起保存,可能会很有用——以及在这一年中收到的任何电子对账单。在组织方面,只需记住这个格言:做任何能让你继续进行的事情。

最基本的交易形式是:

2012-03-10 肯德基 费用: 食物 $20.00 资产: 现金 $-20.00

此交易具有日期、收款人或描述、目标账户(第一笔记账)和来源账户(第二笔记账)。每笔记账指定与该账户相关的操作。

一笔交易可以有任意数量的分录:

2012-03-10 KFC 费用:食品 $20.00 资产:现金 $-10.00 负债:信用 $-10.00

您可以做的第一件事是省略金额。也就是说,如果恰好有一条记录没有指定金额,分类账将推断出其他记录金额的相反值:

2012-03-10 肯德基 费用: 食物 $20.00 资产: 现金 $-10.00 负债: 信用 ; 同样指定为 $-10

如果其他分录使用多种商品,分类账将复制空分录 N 次,并填写各种商品的负值:

2012-03-10 KFC 费用:食品 $20.00 费用:小费 $2.00 资产:现金 EUR -10.00 资产:现金 GBP -10.00 负债:信用

此交易与写入相同:

2012-03-10 KFC 费用:食品 $20.00 费用:小费 $2.00 资产:现金 EUR -10.00 资产:现金 GBP -10.00 负债:信用 $-22.00 负债:信用 EUR 10.00 负债:信用 GBP 10.00

一个交易可以有一个文本“代码”。这没有意义,仅由打印命令显示。支票账户通常使用像 DEP、XFER 等代码,以及支票号码。这是为了给你一个放置这些代码的地方:

2012-03-10 (#100) KFC 费用: 食物 $20.00 资产: 支票

一笔交易可以有一个“状态”:已清算、待处理或未清算。默认状态是未清算。要标记一笔交易为已清算,请在日期后、代码或收款人之前加上一个星号‘*’:

2012-03-10

  • KFC 费用: 食物 $20.00 资产: 现金

要标记为待处理,请使用‘!’:

2012-03-10 ! 肯德基 费用: 食物 $20.00 资产: 现金

这些意味着什么完全取决于你。 "--cleared" 选项仅限于已清除的项目,而 "--uncleared" 显示未清除和待处理的项目,"--pending" 仅显示待处理的项目。

我使用 "已清算" 来表示我已与我的银行对账单核对过交易,而 "待处理" 则表示我正在进行核对。

当你清除一笔交易时,这实际上只是清除其所有记录的简写。也就是说:

2012-03-10

  • KFC 费用: 食物 $20.00 资产: 现金

与写作相同:

2012-03-10 肯德基 * 费用: 食物 $20.00 * 资产: 现金

您可以将单个帖子标记为已清除或待处理,以防交易的一方已清除,但另一方尚未清除:

2012-03-10 肯德基 负债:信用 $100.00 * 资产:支票

在收款人之后,以及至少一个制表符或两个空格(或一个空格和一个制表符)之后,Ledger称之为“硬分隔符”,您可以使用‘;’字符引入有关交易的注释:

2012-03-10

  • KFC ; yum, chicken... 费用:食品 $20.00 资产:现金

注释也可以出现在下一行,只要该行以空白字符开头:

2012-03-10

  • KFC ; yum, chicken... ; and more notes... Expenses:Food $20.00 Assets:Cash

2012-03-10

  • KFC ; 仅这些备注... 费用: 食物 $20.00 资产: 现金

交易的备注由所有的记账共享。当查询元数据时,这变得很重要(见下文)。要指定备注仅属于一个记账,请将其放在金额后面的硬分隔符之后,或单独放在前面有空格的行上:

2012-03-10

  • KFC 费用: 食物 $20.00 ; 发布 #1 注释 资产: 现金 ; 发布 #2 注释,额外缩进是可选的

通常,交易中所有记账的金额必须平衡为零。这是不可谈判的。这就是复式记账的全部意义!但在账本中还有一些小技巧...

您可以使用虚拟账户悄悄地将金额转移到一个账户,绕过平衡要求。诀窍在于这些记录不被视为“真实”,并且可以使用 --real 从所有报告中删除。

要指定一个虚拟账户,请用括号括住账户名称:

2012-03-10

  • KFC 费用: 食物 $20.00 资产: 现金 (预算: 食物) $-20.00

如果你愿意,可以声明虚拟发布 应该 通过使用方括号(看起来“更难”)而不是圆括号来与一个或多个其他虚拟发布进行平衡:

2012-03-10 * KFC 费用: 食物 $20.00 资产: 现金

[预算: 食物]               $-20.00
[权益: 预算]             $20.00

金额是与商品相关的数字,但它也可以是任何价值表达。为了表示这一点,请用括号将金额表达包围起来:

2012-03-10

  • KFC 费用: 食物 ($10.00 + $20.00) ; 分类账为您计算总额 资产: 现金

如果在一条记录的金额末尾(如果有费用,也包括费用)有一个等号,那么 Ledger 将验证该记录时该账户的总值是否与指定的金额匹配。请参见 --permissive 选项以放宽余额断言检查。

此功能有两种形式:余额断言和余额分配。请注意,这两者在解析给定的账本文件时都会被处理。因此,它们的评估顺序与它们在账本文件中出现的顺序相同。包含余额断言或余额分配的交易和过账的日期或生效日期对于余额断言和余额分配的评估而言是无关紧要的。这可能会让那些更直观地理解日期顺序的人感到困惑。

当你将商品从一个账户转移到另一个账户时,有时在交易过程中会发生转变。这种情况发生在你花钱购买燃料时,例如,这将美元转化为加仑汽油,或将美元转化为公司的股票。

在这些情况下,Ledger 会为您记住该交易的“成本”,并可以在报告中以各种方式使用它。以下是股票购买的一个例子:

2012-03-10 我的经纪人 资产:经纪账户 10 AAPL 资产:经纪账户:现金 $-500.00

这与将10股AAPL股票从一个账户转移到另一个账户不同,在这种情况下,您是在_交换_一种商品为另一种商品。结果的发布成本为每股50.00美元。

您可以在金额或金额表达式后使用‘@’符号使任何帖子费用明确:

2012-03-10 我的经纪人 资产:经纪账户 10 AAPL @ $50.00 资产:经纪账户:现金 $-500.00

当你这样做时,由于 Ledger 现在可以从第一次发布的成本中计算出平衡金额,你可以省略其他金额:

2012-03-10 我的经纪人 资产:经纪账户 10 AAPL @ $50.00 资产:经纪账户:现金

正如你可以有金额表达式,你也可以有过账表达式:

2012-03-10 我的经纪人 资产:经纪账户 10 AAPL @ ($500.00 / 10) 资产:经纪账户:现金

你甚至可以同时拥有两者:

2012-03-10 我的经纪人 资产:经纪账户 (5 AAPL * 2) @ ($500.00 / 10) 资产:经纪账户:现金

‘@’字符后面的成本数字指定了所转移商品的_单价_。如果您想指定总成本,请使用‘@@’:

2012-03-10 我的经纪人 资产:经纪账户 10 AAPL @@ $500.00 资产:经纪账户:现金

分类账将其视为您已写的内容:

2012-03-10 我的经纪人 资产:经纪账户 10 AAPL @ ($500.00 / 10) 资产:经纪账户:现金

通常,每当发生这样的商品交易时,交易的价格(例如上面的每股 AAPL 50 美元)会记录在 Ledger 的内部价格历史数据库中。为了防止在特殊交易的情况下发生这种情况,请用括号括住‘@’或‘@@’:

2012-03-10 我的兄弟 资产:经纪账户 1000 AAPL (@) $1 收入:收到的礼物

当发生一笔交易,将一种商品交换为另一种商品时,Ledger 不仅在其内部价格数据库中记录该商品价格,还将其附加到商品本身。通常,这一事实对用户来说是不可见的,除非您开启 --lot-prices 以显示这些隐藏的价格数据。

例如,考虑上述股票销售:

2012-03-10 我的经纪人 资产:经纪账户 10 AAPL @ $50.00 资产:经纪账户:现金

转入‘资产:经纪’的商品实际上不是10 AAPL,而是10 AAPL {$50.00}。金额后面的括号中的数字称为“批次价格”。这是Ledger记住该商品是通过交易所转移的方式,以及$50.00是该交易的价格。

如果你后来再次出售该商品,这就变得重要。例如,你可能会写下这个:

2012-04-10 我的经纪人 资产:经纪账户:现金 资产:经纪账户 -10 AAPL @ $75.00

这完全没问题,但你如何追踪销售的资本收益呢?这可以通过虚拟发布来完成:

2012-04-10 我的经纪人 资产:经纪账户:现金 资产:经纪账户 -10 AAPL @ $75.00 (收入:资本利得) $-250.00

但这变得复杂,因为资本利得收入是非常真实的,并不太适合虚拟发布。

相反,如果你引用那个相同的隐藏价格注释,Ledger 将会计算出你所出售的股票的价格,以及你出售它们的成本,并且这两者不平衡:

2012-04-10 我的经纪人 资产:经纪账户:现金 $750.00 资产:经纪账户 -10 AAPL {$50.00} @ $75.00

此交易将失败,因为您购买这些股票时的价格与您出售时的价格之间的 $250.00 差额不匹配。批次价格还标识了您在之前日期购买的股票。

作为一种简写,您可以在双大括号中指定总价格,而不是每股价格。这与总成本相辅相成,但并不要求与它们一起使用:

2012-04-10 我的经纪人 资产:经纪账户:现金 $750.00 资产:经纪账户 -10 AAPL {{$500.00}} @@ $750.00 收入:资本利得 $-250.00

需要注意的是,这仅仅是针对您买卖整批商品的便利。{{$500.00}} 不是 商品的属性,而{$50.00}是。实际上,当您写{{$500.00}}时,Ledger只是将该值除以10,并看到{$50.00}。因此,如果您使用打印命令查看此交易,您将在输出中看到单括号形式。双括号价格形式仅是一种简写。

此外,它也带来了危险。这很好用:

2012-04-10 我的经纪人 资产:经纪账户 10 AAPL @ $50.00 资产:经纪账户:现金 $-500.00

2012-04-10 我的经纪人 资产:经纪账户:现金 $375.00 资产:经纪账户 -5 AAPL {$50.00} @@ $375.00 收入:资本利得 $-125.00

2012-04-10 我的经纪人 资产:经纪账户:现金 $375.00 资产:经纪账户 -5 AAPL {$50.00} @@ $375.00 收入:资本利得 $-125.00

但这并没有达到你可能期望的效果:

2012-04-10 我的经纪人 资产:经纪账户 10 AAPL @ $50.00 资产:经纪账户:现金 $-500.00

2012-04-10 我的经纪人 资产:经纪账户:现金 $375.00 资产:经纪账户 -5 AAPL {{$500.00}} @@ $375.00 收入:资本利得 $-125.00

2012-04-10 我的经纪人 资产:经纪账户:现金 $375.00 资产:经纪账户 -5 AAPL {{$500.00}} @@ $375.00 收入:资本利得 $-125.00

在金额无法整除且必须四舍五入的情况下,资本利得数字可能会偏差一分。请谨慎使用。

因为批次定价提供了足够的信息来推断成本,以下两个交易是等价的:

2012-04-10 我的经纪人 资产:经纪账户 10 AAPL @ $50.00 资产:经纪账户:现金 $-500.00

2012-04-10 我的经纪人 资产:经纪账户 10 AAPL {$50.00} 资产:经纪账户:现金 $-500.00

然而,请注意,您在某些报告中看到的内容可能会有所不同,例如在打印报告中。然而,从功能上讲,没有区别,登记报告和余额报告对这种差异都不敏感。

如果你去年买了一只股票,并询问今天的价值,Ledger 将查询其价格数据库,以查看该股票的最新价格。你可以通过在交易时“固定”价格来绕过这个查找。这是通过使用 ‘{=AMOUNT}’ 完成的:

2012-04-10 我的经纪人 资产:经纪账户 10 AAPL {=$50.00} 资产:经纪账户:现金 $-500.00

这10股AAPL现在将始终被报告为价值50美元,无论在此期间股票发生什么其他情况。

固定价格是使用批次估值表达式(见下文)来固定商品批次价值的一种特殊情况。

由于价格注释和成本在很大程度上是可以互换的,并且是个人偏好的问题,因此通过成本有一种等效的语法来指定固定价格:

2012-04-10 我的经纪人 资产:经纪账户 10 AAPL @ =$50.00 资产:经纪账户:现金 $-500.00

这与之前的交易相同,具有在价格与成本中发现的相同警告。

您还可以在括号中关联任意备注以供自己记录,并通过 --lot-notes 显示它们。一个警告是,备注不能以‘@’字符开头,因为那将表示一个虚拟成本:

2012-04-10 我的经纪人 资产:经纪账户:现金 $375.00 资产:经纪账户 -5 AAPL {$50.00} [2012-04-10] (哦,我的天!) @@ $375.00 收入:资本利得 $-125.00

您可以以任何顺序指定任何组合的批次价格、日期或备注。它们都是可选的。

要在报告中显示所有批次信息,请使用 --lots.

通常,当你要求 Ledger 显示所持商品的价值时,它会使用一种称为“市场”的价值表达式来确定其价格数据库中最新的价值——甚至在指定了 --download (-Q) 并且在你的系统上找到合适的 getquote 脚本时,从互联网下载价格。

然而,您可以通过提供一个双括号中的商品估值表达式来覆盖此估值逻辑。该表达式必须产生两个值之一:要么是始终用作该商品每股价格的金额;要么是一个接受三个参数的函数,该函数被调用以确定该价格。

如果你使用函数形式,你可以指定一个函数名称,或者一个 lambda 表达式。这里有一个函数,它返回所请求商品的价格为 $10:

定义 ten t_dollars(s, date, t) = market($10, date, t)

我现在可以在许多值表达式中使用它,如下所示:

2012-04-10 我的经纪人 资产:经纪账户:现金 $375.00 资产:经纪账户 -5 AAPL {$50.00} ((十 ext{美元})) @@ $375.00 收入:资本利得 $-125.00

或者,我可以通过使用一个接受三个参数的 lambda 表达式来做同样的事情,而不预先定义一个函数:

2012-04-10 我的经纪人 A:B:现金 $375.00 A:B -5 AAPL {$50.00} ((s, d, t -> 市场($10, 日期, t))) @@ $375.00 收入:资本利得 $-125.00

传递给这些函数的参数具有以下含义:

  • source 源商品字符串,或一个金额对象。如果它是一个字符串,返回值必须是一个表示由该字符串标识的商品价格的金额(示例:‘$’)。如果它是一个金额,返回该金额的值作为一个新的金额(通常计算为商品价格乘以源金额)。
  • date 用于确定价值的日期。如果为null,意味着没有指定日期,这可以意味着你想要的任何含义。
  • target 如果不为null,一个表示所需目标商品的字符串,该商品价格或重新定价金额应以此为价值。请注意,这个字符串可以是一个以逗号分隔的列表,并且该列表中的某些或所有商品可能会以感叹号结尾,以指示所需的内容。

在大多数情况下,最简单的方法是直接在你的估值表达式中使用明确的数额,或者在修改它们以适应你的需求后,将参数传递给‘market’。

自动化交易是一种特殊类型的交易,它会在其他交易的某个记录与其谓词匹配时,将其记录添加到其他交易中。谓词使用与 Ledger 命令行相同的查询语法。

考虑这个帖子:

2012-03-10 肯德基 费用: 食物 $20.00 资产: 现金

如果我在文件中写下这个自动化交易在它之前:

= expr true Foo $50.00 Bar $-50.00

然后第一笔交易将在解析过程中被修改,就像我写的这样:

2012-03-10 KFC 费用:食品 $20.00 Foo $50.00 Bar $-50.00 资产:现金 $-20.00 Foo $50.00 Bar $-50.00

尽管有这种华丽的逻辑,自动化交易本身遵循的大多数规则与常规交易相同:它们的记账必须平衡(除非你使用虚拟记账),你可以有元数据等。

然而,有一件事你不能做,那就是在自动化交易中省略金额。

如果您在自动交易的过账中使用金额表达式,该表达式可以访问匹配过账的所有详细信息。例如,您可以使用“amount”值表达式变量引用该过账的金额:

= expr true (Foo) (amount * 2) ; 在这种情况下与仅仅 "2" 相同

2012-03-10 肯德基 费用: 食物 $20.00 资产: 现金

这变成:

2012-03-10 KFC 费用:食品 $20.00 (Foo) $40.00 资产:现金 $-20.00 (Foo) $-40.00

有时您想要引用在自动交易中以某种方式匹配的账户。这是通过在自动发布的账户部分中的任何位置使用字符串‘$account’来完成的:

= 食物 (预算:$account) 10

2012-03-10 肯德基 费用: 食物 $20.00 资产: 现金

变为:

2012-03-10 肯德基 费用:食品 $20.00 (预算:费用:食品) $200.00 资产:现金 $-20.00

可以使用 VEXPR 引用发布中的信息。请注意,使用 VEXPR 的语法是 "%(VEXPR)"。

= ^收入 负债:税:%(tag(/税/)) (20/120) $账户 (-20/120)

2024-07-04

  • 销售 ; 税: 一般 资产 10 美元 收入:客户 A

变为:

2024/07/04 * 销售 ; 税: 一般 资产 10 美元 收入:客户 A -10 美元 负债:税:一般 -2 美元 收入:客户 A 2 美元

请记住,如果您使用 \--strict 或 \--pedantic,您必须明确定义一个账户以避免错误。当使用 ‘$account’ 时,可以这样进行定义:

在现实世界中,交易并不是瞬间完成的。购买可能需要几天才能到账到银行账户。而且你可能会提前支付某些费用,以便分摊成本。使用Ledger,你可以控制交易时间的每一个方面。

假设你在做生意。如果你给客户开账单,你可以输入类似于

2008/01/01=2008/01/14 客户发票 ; 预计付款日期 资产: 应收账款 $100.00 收入: 客户名称

然后,当你收到付款时,你将其更改为

2008/01/01=2008/01/15 客户发票 ; 实际收到款项日期 资产:应收账款 $100.00 收入: 客户名称

并添加类似的内容

2008/01/15 客户付款 资产:支票账户 $100.00 资产:应收账款

现在

$ ledger --begin 2008/01/01 --end 2008/01/14 bal 收入

在年初的前两周给你累积的收入,和

$ ledger --effective --begin 2008/01/01 --end 2008/01/14 bal 收入

在同样的两周内给你现金基础收入。

另一个用途是将成本分摊到时间上。举个例子,假设你刚刚预付了一个当地蔬菜合作社的费用,这个合作社让你在冬天有足够的食物。加入该项目的费用是225美元,所以你写了一张支票。然而,你不想因为提前购买食物而让你的十月杂货预算受到影响。你真正想要的是将这笔钱在接下来的六个月内均匀分配,这样你的每月预算就会逐渐受到影响,以便你从合作社提取的蔬菜,尽管你已经为它们支付了费用。

2008/10/16 \* (2090) 丰盛的祝福农场 费用:食品:杂货 $ 37.50 ; [=2008/10/01] 费用:食品:杂货 $ 37.50 ; [=2008/11/01] 费用:食品:杂货 $ 37.50 ; [=2008/12/01] 费用:食品:杂货 $ 37.50 ; [=2009/01/01] 费用:食品:杂货 $ 37.50 ; [=2009/02/01] 费用:食品:杂货 $ 37.50 ; [=2009/03/01] 资产:支票

此条目实现了这一点。每个月你会看到一个自动的 $37.50 赤字,就像你应该看到的那样,而你的支票账户实际上知道本月扣除了 $225。

并且使用 \--effective (或 \--aux-date) 选项,初始日期将被有效日期覆盖。

$ ledger --有效登记 食品杂货

08-Oct-01 丰盛的祝福.. 费用:食品:杂货 $ 37.50 $ 37.50 08-Nov-01 丰盛的祝福.. 费用:食品:杂货 $ 37.50 $ 75.00 08-Dec-01 丰盛的祝福.. 费用:食品:杂货 $ 37.50 $ 112.50 09-Jan-01 丰盛的祝福.. 费用:食品:杂货 $ 37.50 $ 150.00 09-Feb-01 丰盛的祝福.. 费用:食品:杂货 $ 37.50 $ 187.50 09-Mar-01 丰盛的祝福.. 费用:食品:杂货 $ 37.50 $ 225.00

请注意,--aux-date 选项是 --effective 的别名;有关辅助日期的简要说明,请参见 辅助日期

Ledger 的强大来自于其报告命令的惊人灵活性,以及格式化命令的结合。一些选项控制计算中包含的内容,而格式化控制其显示方式。组合是无限的。本章将向您展示组合各种选项和命令的基础知识。在接下来的章节中,您将找到有关具体命令和选项的详细信息。

余额报告是最常用的报告。最简单的调用是:

$ 账本余额 -f drewr3.dat

这将打印您日记中每个账户的余额。

     $ -3,804.00  资产
      $ 1,396.00    支票
         $ 30.00      商业
     $ -5,200.00    储蓄
     $ -1,000.00  权益:期初余额
      $ 6,654.00  费用
      $ 5,500.00    汽车
         $ 20.00    书籍
        $ 300.00    托管
        $ 334.00    食品:杂货
        $ 500.00    利息:抵押贷款
     $ -2,030.00  收入
     $ -2,000.00    工资
        $ -30.00    销售
        $ -63.60  负债
        $ -20.00    万事达卡
        $ 200.00    抵押贷款:本金
       $ -243.60    十分之一

       $ -243.60

大多数时候,这比你想要的要多。将结果限制为特定账户只需在命令后输入账户名称即可:

$ 账本余额 -f drewr3.dat 自动万事达卡

      $ 5,500.00  支出:汽车
        $ -20.00  负债:万事达卡

      $ 5,480.00

注意‘自动’和‘万事达卡’之间的隐含逻辑或。

如果您想要您账户树某个分支的全部内容,请使用该分支中的最高公共名称:

$ 账本余额 -f drewr3.dat 收入

     $ -2,030.00  收入
     $ -2,000.00    工资
        $ -30.00    销售

     $ -2,030.00

您可以在 Ledger 需要字符串的几乎任何地方使用通用正则表达式 (PCRE):

$ 账本余额 -f drewr3.dat ^Bo

这个第一个例子查找任何以‘Bo’开头的账户,但没有找到。

$ 账本余额 -f drewr3.dat Bo

这个第二个例子查找任何包含‘Bo’的账户,即‘费用:书籍’。

如果您想确切知道在特定账户上对特定收款人花费了多少,以下是等价的:

$ 账本余额 费用:汽车:燃料 和 @Chevron

$ ledger balance --limit 'account=~/费用:汽车:燃料/ and payee=~/雪佛龙/''

将向您展示在雪佛龙加油的支出金额。第二个示例是可用于塑造报告的非常强大的表达语言的第一个示例。第一个示例可能更容易记住,但学习使用第二个示例将打开更多的可能性。

如果您想从报告中排除特定账户,可以使用括号排除多个账户:

$ ledger bal 费用 并且不是 (费用:饮料 或 费用:糖果 或 费用:礼物)

以下查询显示自去年十月以来的所有费用,按总额排序:

$ ledger -b "last oct" -S T bal ^支出

从左到右,这些选项的意思是:显示自去年十月以来的交易;按总额的绝对值排序;并报告所有以“费用”开头的账户的余额。

以下查询使得查看每月开支变得简单,每个月的开支按金额排序:

$ ledger -M --period-sort "(amount)" reg ^开支

现在,你可能会想,这些东西的费用来自哪里。要查看该报告,请添加 --related (-r),这将显示“相关账户”的记录:

$ ledger -M --period-sort "(amount)" -r reg ^开支

但也许这打印的信息太多了。你可能只想看看你用万事达卡花了多少钱。这种查询需要使用显示谓词,因为计算出的记录必须匹配‘^expenses’,而显示的记录必须匹配‘mastercard’。命令是:

$ ledger -M -r --display 'account=~/mastercard/' reg ^费用

此查询表示:报告每月小计;报告“相关账户”记录;仅显示账户匹配‘mastercard’的相关记录,并基于匹配‘^expenses’的记录进行计算。

这同样适用于报告总体总数:

$ ledger -s -r --display "account=~/mastercard/" reg ^expenses

--subtotal (-s) 选项对所有记录进行小计,就像 --monthly (-M) 按月进行小计一样。然而,在这两种情况下,运行总计都是不正确的,因为正在使用显示表达式。

一种非常流行的投资组合管理方法是通过某些类别控制资产的百分比分配。类别的组合和应用于它们的权重因投资哲学而异,但大多数遵循类似的模式。在账本中跟踪资产配置并不困难,但确实需要一些额外的努力来描述您拥有的各种资产如何贡献于您想要跟踪的资产类别。

在我们的简单示例中,我们假设您想将资产分配到国内和国际股票(股票)的一般类别,以及债券和现金的综合类别。为了说明,我们将使用几只来自先锋(Vanguard)的公开可用共同基金。我们将跟踪的三只基金是先锋500 IDX FD信号(VIFSX)、先锋目标退休2030(VTHRX)和先锋短期联邦基金(VSGBX)。这些基金将资产分配到投资领域的不同类别,并以不同的比例进行分配。当您购买一份VTHRX时,该份额部分投资于股票,部分投资于债券和现金。以下是上述每种工具的资产配置:

国内

全球

符号

股权

股权

债券/现金

VIFSX

100%

VTHRX

24.0%

56.3%

19.7%

VSGBX

100%

这些数字可以从任何公开可用的共同基金的招募说明书中获得。当然,单一股票发行是100%的股权,而单一债券发行是100%的债券。

我们使用该投资的符号作为其商品来跟踪特定投资的购买。我们如何告诉Ledger一股VTHRX是24%的国内股票?输入自动交易和虚拟账户。

在我们账本的顶部,我们输入自动交易,以向账本描述这些比例。在同一条记录中,我们设置虚拟账户,以便将这些抽象计算与我们的实际余额分开。

对于上述列出的三种工具,这些自动交易看起来像是:

= expr ( commodity == 'VIFSX' ) (分配:股票:国内) 1.000

= expr ( commodity == 'VTHRX' ) (分配:股票:全球) 0.240 (分配:股票:国内) 0.563 (分配:债券/现金) 0.197

= expr ( commodity == 'VBMFX') (Allocation:债券/现金) 1.000

2015-01-01 购买 VIFSX 资产:经纪人 100 VIFSX 资产:现金 $-10000

2015-01-01 购买 VTHRX 资产:经纪人 10 VTHRX 资产:现金 $-10000

2015-01-01 购买 VBMFX 资产:经纪人 1 VBMFX 资产:现金 $-10000

这些是如何工作的?首先,行首的‘=’符号告诉账本这是一个自动交易,当‘=’后面的条件为真时将被应用。在‘=’符号后是一个值表达式(见 值表达式),只要一个记账包含感兴趣的商品,它就会返回真。

以下行给出了属于每个资产类别的每种商品单位的比例(不是百分比)。每当账本看到某种商品的买入或卖出时,它将根据移动的股份数量的比例对这些虚拟账户进行记贷。

现在 Ledger 理解了如何在各种资产类别之间分配商品,我们如何获得一份报告来告诉我们当前的分配情况?使用 balance 命令和一些巧妙的格式!

账本余额分配 --当前 --格式 "
%-17((深度_间隔)+(部分_账户))
%10(百分比(市场(显示_总计), 市场(父级.总计)))
%16(市场(显示_总计))\n%/"

产生:

    分配                  100.00%                  $30000
      债券/现金                 39.90%                  $11970
      股票                   60.10%                  $18030
        国内                 86.69%                  $15630
        全球                   13.31%                   $2400

让我们更仔细地看看 Ledger 调用。上面的命令为了清晰而分成了几行。第一行是非常基础的 Ledger,询问“分配”树中账户的当前余额,使用了一种特殊的格式化器。

魔法在于格式化器。第二行简单地告诉 Ledger 按照树中的深度缩进打印部分账户名称。第三行是我们计算和显示百分比的地方。display_total 命令给出这一行中账户计算的总值。parent.total 命令给出树中上一级的总值。percent 将它们的比率格式化为百分比。第四行告诉 Ledger 显示该行的当前市场价值。最后两个字符 ‘%/’ 告诉 Ledger 对最后一行该怎么做,在这种情况下,不做任何操作。

如果您安装了“Gnuplot”程序,您可以绘制上述任何注册报告。执行此操作的脚本包含在账本分发中,名为 contrib/report。将 report 安装在您的 PATH 中的任何位置,然后在进行注册报告时使用 report 而不是 ledger。唯一需要记住的是,您必须指定 --amount-data (-j) 或 --total-data (-J) 来指示“Gnuplot”应该绘制金额还是运行总计。例如,此命令绘制您在 MasterCard 上的每月总支出。

$ report -j -M -r --display "账户 =~ /mastercard/" reg ^expenses

该报告脚本是一个非常简单的 Bourne shell 脚本,它将一组脚本命令传递给 “Gnuplot”。请随意修改脚本以符合您的喜好,例如,您可能更喜欢直方图而不是折线图。

这里有一些有用的图表:

report -j -M reg ^expenses # 每月支出 report -J reg checking # 支票账户余额 report -J reg ^income ^expenses # 现金流报告

净资产报告,忽略非$的帖子

报告 -J -l "Ua>={$0.01}" 注册 ^资产 ^负债

净资产报告从去年二月开始。需要使用显示

谓词 (-d),否则余额将从

零开始,因此 y 轴将无法反映真实余额

报告 -J -l "Ua>={$0.01}" -d "d>=[上个月的二月]" reg ^资产 ^负债

最后的报告使用了计算谓词 --limit EXPR (-l) 和显示谓词 --display EXPR (-d)。计算谓词将报告限制为金额大于或等于 $0.01 的记录(这只有在记录金额以美元为单位时才会发生)。显示谓词将 显示 的交易限制为自去年二月以来的交易,尽管之前的交易将作为余额的一部分进行计算。

register 命令逐行显示单个账户中的所有帖子。账户正则表达式必须作为此命令的唯一参数指定。如果在所需的账户名称之后出现任何正则表达式,注册将仅包含匹配的帖子,这使得它在寻找特定帖子时非常有用。

register 的输出与典型的支票簿或单账户分类账非常相似。它还显示了一个持续的余额。任何登记册的最终持续余额应始终与该账户的当前余额相同。

如果您安装了“Gnuplot”,您可以通过使用包含在Ledger发行版中的脚本report来绘制任何注册的金额或运行总额。唯一的要求是您在register命令中添加"--amount-data (-j)"或"--total-data (-J)",以分别绘制金额或总列。

convert 命令解析逗号分隔值 (csv) 文件并打印 Ledger 交易。许多银行提供 csv 文件下载。不幸的是,除了逗号之外,文件格式各不相同。ledger convert 命令尽可能提供帮助。

您银行的csv文件中的字段顺序与其他银行不同,因此必须有一种方法告诉Ledger该期待什么。在csv文件的开头插入一行,描述Ledger所需的字段。

例如,这是从美国一家信用合作社下载的csv文件的一部分:

账户名称: VALUFIRST CHECKING 账户号码: 71 日期范围: 11/13/2011 - 12/13/2011

交易编号,日期,描述,备忘录,借方金额,贷方金额,余额,支票号码,费用 767406,12/13/2011,"存款","现金存款",,45.00,00001646.89,, 767718,12/13/2011,"取款","ACE HARDWARE 16335 S HOUGHTON RD",8.80,,00001640.04,, 767406,12/13/2011,"取款","ACE HARDWARE 16335 S HOUGHTON RD",1.03,,00001648.84,, 683342,12/13/2011,"Visa 支票","NetFlix 日期 12/12/11 000326585896 5968",21.85,,00001649.87,, 639668,12/13/2011,"取款","ID: 1741472662 CO: XXAA.COM 付款",236.65,,00001671.72,, 1113648,12/12/2011,"取款","Tuscan IT #00037657",29.73,,00001908.37,,

不幸的是,Ledger 目前无法读取它,但你可以。Ledger 期望第一行包含文件中每行字段的描述。Ledger 可以识别的字段包含这些不区分大小写的字符串 datepostedcodepayeedescdescriptionamountcreditdebitcosttotalnote

删除顶部的账户描述行,并用上面的数据中的第一行替换为:

,日期,收款人,备注,借方,贷方,,代码,

然后像这样执行账本:

$ ledger convert download.csv --input-date-format "%m/%d/%Y"

其中 \--input-date-format DATE_FORMAT 选项告诉账本如何解释日期。

导入csv文件是一项繁重的工作,但非常适合脚本处理。

如果您的 csv 只有一列金额,且贷方和借方的符号相反,这也是支持的。例如,上述账户的前几行也可以采用以下格式:

,日期,收款人,备注,信用,,代码, 767406,12/13/2011,"存款","现金存款",45.00,00001646.89,, 767718,12/13/2011,"取款","ACE HARDWARE 16335 S HOUGHTON RD",-8.80,00001640.04,,

如果在银行数据中有您希望保留在账本数据中的列,除了上述描述的主要字段外,您可以在字段描述符列表中命名它们,如果账本无法识别字段名称,它将把它们作为元数据包含在交易中。例如,如果您想捕获银行交易编号,并且它出现在数据的第一列,请使用:

交易ID,日期,收款人,备注,借记,贷记,,代码,

分类账将包括‘; transid: 767718’在上述文件的第一笔交易中。

convert 命令接受四个选项。它们是 \--invert,它反转金额, \--auto-match,它自动匹配每个 CSV 行的 Ledger 日志中的账户, \--account STR,您可以使用它来指定要平衡的账户,以及 \--rich-data,它存储额外的标签/值对。

使用上述csv文件的前两行,

,日期,收款人,备注,借方,贷方,余额,代码, 767406,12/13/2011,"存款","现金存款",,45.00,00001646.89,, 767718,12/13/2011,"取款","ACE HARDWARE 16335 S HOUGHTON RD",8.80,,00001640.04,,

并执行以下命令,

$ ledger convert download.csv --input-date-format "%m/%d/%Y"
--invert --account Assets:MyBank --rich-data
--file sample.dat --now=2012/01/13

你将得到结果:

2011/12/13 * 存款 ;现金存款 ; 余额: 00001646.89 ; CSV: 767406,12/13/2011,"存款","现金存款",,45.00,00001646.89,, ; 导入时间: 2012/01/13 ; UUID: ce0b7d42b02ce5eaf0d828c3b1028041fd09494c 支出:未知 -45 资产:我的银行

2011/12/13 * 提款 ;ACE HARDWARE 16335 S HOUGHTON RD ; 余额: 00001640.04 ; CSV: 767718,12/13/2011,"提款","ACE HARDWARE 16335 S HOUGHTON RD",8.80,,00001640.04,, ; 导入时间: 2012/01/13 ; UUID: 0aaf85911adc447ea2d5377ff6a60d6b2940047f 支出:未知 8.8 资产:我的银行

添加的三个元数据是:‘CSV’作为来自csv文件的原始行,‘Imported’作为csv文件导入到Ledger的日期,以及‘UUID’作为原始csv行的校验和。

如果一个带有相同‘UUID’标签的条目已经包含在正常的账本文件中(通过 --file FILE (-f) 指定或通过环境变量 LEDGER_FILE 指定),则该条目将不会再次打印。

在上面的输出中,账户为‘Expenses:Unknown’用于CSV行。您可以使用--auto-match选项自动匹配您Ledger日记中的账户。

您还可以使用 convertpayeeaccount 指令。首先,您可以使用 payeealias 指令根据某些规则重写 payee 字段。然后,您可以使用账户及其 payee 指令来指定账户。我这样使用,例如:

收款人 Aldi 别名 ^ALDI SUED SAGT DANKE 账户 Aufwand:Einkauf:Lebensmittel 收款人 ^(Aldi|Alnatura|Kaufland|REWE)$

请注意,如果您想要匹配新的收款人字段,可能需要将‘ledger convert’的输出再次通过ledger print处理。在ledger convert运行期间,似乎仅使用了csv数据中指定的原始收款人名称。

Org 模式有一个名为 Babel 的子系统,它允许进行文献编程。这使您可以在同一文档中混合文本和代码,并自动执行代码,这可能会生成结果,然后出现在文本中。

Babel 支持的语言之一是 Ledger,这样您可以在文本文件中嵌入 ledger 命令,并且 ledger 命令的输出也会出现在文本文件中。每当添加新的 ledger 条目时,输出可以更新。

例如,以下 Org 模式文本文档片段展示了 Babel 系统一个非常简单但仍然有用的应用:

* 在 org 文件中对账本的简单测试 以下是一些条目,我已请求运行账本以生成账户的余额。我本可以请求一个登记簿,或者实际上请求账本通过命令行选项可以做的任何事情。

#+begin_src ledger :cmdline bal :results value 2010/01/01 * 开始余额 assets:bank:savings £1300.00 income:starting balances 2010/07/22 * 收到工资 assets:bank:chequing £1000.00 income:salary 2010/07/23 租金 expenses:rent £500.00 assets:bank:chequing #+end_src

#+results: : £1800.00 资产:银行 : £500.00 支票账户 : £1300.00 储蓄 : £500.00 支出:租金 : £-2300.00 收入 : £-1000.00 工资 : £-1300.00 起始余额

在“分类账源代码块”中任何地方输入 C-c C-c 将会对该块的内容调用分类账,并生成一个“结果”块。结果块可以出现在文件的任何地方,但默认情况下,会出现在源代码块的正下方。

您可以在执行账本之前组合多个源代码块,并使用 Babel(和 Org 模式)做各种其他奇妙的事情。

默认情况下,Ledger 使用人类可读的数据格式,并以适合在屏幕上阅读的方式显示其报告。然而,为了编写使用 Ledger 的工具,可以使用 XML 读取和显示数据。本节记录了该格式。

用于分类账数据的一般格式是:

<?xml version="1.0"?> <ledger> <xact>...</xact> <xact>...</xact> <xact>...</xact>... </ledger>

数据流被封装在一个 ledger 标签中,该标签包含一系列一个或多个交易。每个 xact 描述一个交易,并包含一系列一个或多个记录:

<xact> <en:date>2004/03/01</en:date> <en:cleared/> <en:code>100</en:code> <en:payee>约翰·维格利</en:payee> <en:postings> <posting>...</posting> <posting>...</posting> <posting>...</posting>... </en:postings> </xact>

en:date 的日期格式始终为 YYYY/MM/DDen:cleared 标签是可选的,表示该条目是否已被清除。还有一个 en:pending 标签,用于标记待处理的条目。en:codeen:payee 标签都包含用户希望输入的任何文本。

在初始交易数据之后,必须跟随一组标记为 en:postings 的记录。通常这些记录会相互平衡,但如果不平衡,它们将自动平衡到一个名为‘Unknown’的账户中。

en:postings 标签内是一系列一个或多个 posting,其形式如下:

<posting> <tr:account>费用:计算机:硬件</tr:account> <tr:amount> <value type="amount"> <amount> <commodity flags="PT">$</commodity> <quantity>90.00</quantity> </amount> </value> </tr:amount> </posting>

这是一条基本的帖子。它也可以以 tr:virtual 和/或 tr:generated 标签开始,以指示虚拟和自动生成的帖子。接下来是 tr:account 标签,其中包含与该帖子相关的账户的全名。冒号用于分隔账户名称中的父级和子级。

最后是发布的金额,由 tr:amount 指示。在此标签内有一个 value 标签,分为四种不同类型,每种都有其自己的格式:

  1. 布尔值,
  2. 整数,
  3. 金额,
  4. 余额.

布尔值的格式是 truefalse,并被 boolean 标签包围,例如:

整数值的格式是被 integer 标签包围的数值,例如:

金额的格式包含两个成员,商品和数量。商品可以有一组标志,指示如何显示它。这些标志的含义(所有标志都是可选的)是:

P

商品被前缀到价值上。

S

商品与价值之间隔着一个空间。

T

成千上万的标记用于显示数量。

E

金额的格式为欧洲格式,使用句点作为千位分隔符,使用逗号作为小数点。

一个金额的实际数量是任意大小的整数。Ledger 使用 GNU 多重精度算术库来处理这些值。XML 格式假设读者同样具备这种能力。以下是一个金额的示例:

<value type="amount"> <amount> <commodity flags="PT">$</commodity> <quantity>90.00</quantity> </amount> </value>

最后,余额值包含一系列金额,每个金额对应不同的商品。与名称不同,这样的值确实需要平衡。之所以称为余额,是因为它汇总了多个金额。例如:

<value type="balance"> <balance> <amount> <commodity flags="PT">$</commodity> <quantity>90.00</quantity> </amount> <amount> <commodity flags="TE">DM</commodity> <quantity>200.00</quantity> </amount> </balance> </value>

这就是 Ledger 使用的 XML 数据格式的范围。如果使用 xml 命令,它将输出这样的数据,并且可以读取相同的数据。

本章描述了Ledger的功能和选项。您可能希望先浏览一下,以便在深入了解Ledger教程和后续的更详细示例之前获得一个概述。

Ledger 有一个非常简单的命令行界面,名为——足够诱人——ledger。它支持一些报告命令,以及大量选项来细化这些命令的输出。任何 ledger 命令的基本语法是:

$ ledger [选项...\] 命令 [参数...\]

在命令词之后可能会出现任意数量的参数。对于大多数命令,这些参数是正则表达式,导致输出仅与匹配这些正则表达式的帖子相关。对于 xact 命令,参数具有特殊含义,如下所述。

正则表达式参数始终匹配发布所指的账户名称。要改为匹配交易的收款人,请在正则表达式前加上‘payee’或‘@’。例如,以下余额命令报告租金、食品和电影的账户总额,但仅限于收款人匹配Freddie的那些。

$ 账本 余额 租金 食物 电影 收款人 freddie

$ 账本 余额 租金 食物 电影 @freddie

在 ledger 程序中有许多命令选项可用,掌握它们需要一些时间。然而,使用基本报告命令并不需要任何这些选项。

这些选项影响商品价值的显示方式:

--价格-db 文件

设置用于记录下载商品价格的文件。它在启动时始终被读取,以确定历史价格。其他设置可以手动放置在此文件中,以防止下载特定商品的报价,例如。这可以通过添加如下行来完成:

; 不要下载美元的报价或时间日志值 N $ N h

注意:Ledger 从不将输出写入文件。您有责任更新 price-db 文件。最好的方法是让您的价格下载脚本维护此文件。

文件的格式可以通过告诉账本使用你定义的 --pricedb-format FORMAT_STRING 来更改。

--价格经验 INT

--leeway INT

-Z 整数

设置价格报价的预期新鲜度,以 INT 分钟为单位。也就是说,如果任何商品的最后已知报价比这个值旧,并且如果正在使用 --download,则将再次咨询互联网以获取更新的价格。否则,旧价格仍然被认为是足够新鲜的。

--下载

-Q

通过运行名为 getquote 的脚本,自动下载所需的报价,并期望该脚本返回 ledger 理解的值。分发中提供了一个用 Perl 实现的 getquote 脚本的示例实现。下载的报价价格随后被附加到价格数据库,通常使用环境变量 LEDGER_PRICE_DB 指定。

账本可以以几种不同的方式报告其显示的总计。调整它们的最灵活方法是使用值表达式,以及 \--amount EXPR (-t) 和 \--total VEXPR (-T) 选项。然而,还有几种“默认”报告,可以满足大多数用户的基本报告需求:

--quantity

-O

报告商品总数(这是默认设置)。

--basis

-B

报告所有记录的成本基础。

--市场

-V

使用商品的最后已知值来计算最终值。

--gain

-G

报告所有在报告中具有价格历史的商品的净收益/损失。

通常,您会更关心您所有持有资产的总价值,以您偏好的货币计算。知道您持有10,000股PENNY可能很好,但您更关心的是这是否值$1000.00或$10,000.00。然而,商品的当前日价值对不同的人可能意味着不同的事情,这取决于相关账户、商品、交易的性质等。

当您指定 --market (-V) 或 --exchange COMMODITY (-X) 时,您请求将某些或所有商品按今天的价值进行评估(或按 --now DATE 设置的日期进行评估)。但这样的评估意味着什么呢?这个意义由一个 VALUE 元数据属性的存在来决定,其内容是用于计算该价值的表达式。

如果未指定 VALUE 属性,则假定每个帖子都有一个默认值,就像您指定了一个全局的自动交易,如下所示:

= expr true ; VALUE:: market(amount, date, exchange)

此定义模拟了当前 \--market (-V) 和 \--exchange COMMODITY (-X) 的行为(在‘\-X’的情况下,请求的商品通过上面的字符串‘exchange’传递)。

许多人想做的一件事是,在某个日期之后,将旧欧洲货币的估值固定为欧元:

= expr commodity == "DM" ; VALUE:: date < [2008年6月] ? market(amount, date, exchange) : 1.44 欧元

这句话的意思是:如果 --now DATE 是某个旧日期,则使用当时的市场价格;但如果 --now DATE 在2008年6月之后,则使用固定价格将德国马克转换为欧元。

或者如何从不重新评估用于费用的商品,因为它们不可能有不同的未来价值:

= /^费用:/ ; VALUE:: 市场(金额, 发布.日期, 交换)

这表示未来的估值与发布时的估值相同。 post.date 等于发布的日期,而仅仅是 ’date’ 是 "--now DATE 的值(默认为今天)。

或者如何根据特定时间段内的报销率来评估里程:

= expr commodity == "miles" and date >= [2007] and date < [2008] ; VALUE:: market($1.05, date, exchange)

在这种情况下,2007年行驶的英里将始终以每英里1.05美元的价格计算。如果您使用‘-X EUR’明确请求所有金额以欧元表示,Ledger将通过适当的方式将1.05美元转换为欧元。

请注意,您可以通过使用特定的元数据覆盖这些一般默认值,从而拥有特定于某个发布或交易的估值表达式:

2010-12-26 示例 费用: 食物 $20 ; 只是为了搞笑,总是将*这些* $20 估值为 30 DM,无论用户用 -V 或 -X 要求什么 ; 价值:: 30 DM 资产: 现金

这个例子演示了你的值表达式应该尽可能具有象征性,使用像’amount’和’date’这样的术语,而不是具体的金额和日期。此外,你应该将金额传递给函数’market’,以便在用户要求特定货币时可以进一步重新评估。

或者,如果这更适合你的会计,你可以少一些象征性,这样你可以在使用‘-X EUR’时报告大部分内容为欧元,除了某些账户或记录,这些账户或记录应始终以另一种货币进行估值。例如:

= /^资产:经纪:加元$/ ; 始终以现今美元的价值报告此账户中的商品价值,尽管命令行中要求的是 ; VALUE:: 市场(金额, 日期, ‘$’)

Ledger 目前没有处理 FIFO 和 LIFO 这样的事务的方式。

如果您指定一个未修饰的商品名称,比如 AAPL,它将与自身平衡。如果 --lots 没有被显示,那么它将看起来与任何 AAPL 的批次平衡。

如果您指定了一个装饰商品,比如 AAPL {$10.00},它也会与自身平衡,并且如果未指定 --lots,则会与任何 AAPL 平衡。但如果您确实指定了 --lot-prices,例如,它将与 AAPL 的特定价格平衡。

通常,当您使用 --exchange COMMODITY (-X) 请求以特定商品报告金额时,Ledger 使用以下值:

  • 注册报告 对于 register 报告,使用报告日期时该商品的价值,如果今天的价值与上次发布的价值不同,则在末尾添加一个‘<Revalued>’发布。
  • 余额报告 对于 balance 报告,使用截至今天该商品的价值。

您现在可以指定 --historical (-H) 来要求所有金额的估值相对于遇到该金额的日期进行。

您现在还可以将 --exchange COMMODITY (-X) (和 --historical (-H))与 --basis (-B) 和 --price (-I) 结合使用,以查看仅包含您的基础成本或批次价格的估值报告。

最后,有时您可能只想以另一种商品的形式报告一个(或某些子集)商品。在这种情况下,您可以使用语法 ,--exchange COMMODITY1:COMMODITY2 来请求账本始终以 COMMODITY2 的形式显示 COMMODITY1,但您希望没有其他商品在没有额外 ,--exchange 选项的情况下自动以 COMMODITY2 的形式显示。例如,如果您想以 USD 的形式报告 EUR 和 BTC,但以不转换为 USD 的形式报告所有其他商品,您可以使用: ,--exchange EUR:USD ,--exchange BTC:USD.

一个周期表达式表示一段时间,或一个报告间隔,或两者兼而有之。账本的结束日期总是排除在外,想象一下日期后面跟着 00:00:00 的时间。它们是时间的瞬间,而不是整天。完整的语法是:

可选的 INTERVAL 部分可以是以下任意一个:

每天 每周 每月 每季度 每年 每 N 天 # N 是任何整数 每 N 周 每 N 月 每 N 季度 每 N 年 每日 每周 每两周 每月 每两个月 每季度 每年

在间隔之后,可以指定开始时间、结束时间、两者或都不指定。至于开始时间,可以是以下任意一个:

结束时间可以是以下任意一个:

其中 SPEC 可以是以下任意内容:

2004 2004/10 2004/10/1 10/1 十月 oct 本周 # 或者天、月、季度、年 下周 上周

开始和结束可以同时给出,如果它跨越一个单一的时间段。在这种情况下,只需单独使用SPEC。比如,‘oct’这个时间段将涵盖十月的所有天数。可能的形式有:

区间从一周的开始、一个月的第一天、一个季度或一年开始。这可以通过指定 --align-intervals 来覆盖,如果指定了开始时间,则将使用该开始时间。

以下是一些时间表达的例子:

每月 2004年的每月 十月开始每周 上个月开始每周 九月到十月 从10/1到10/5 直到2005年的每月 从2005/04/06开始每月 从四月开始 直到十一月 去年十月 去年八月每周

制定预算可以让你更密切地关注你的收入和支出,通过报告你的实际财务活动与预期之间的差距。

要开始制定预算,请在您的账本文件顶部放置一些定期交易(见 定期交易)。定期交易与常规交易几乎相同,唯一的区别是它以波浪号开头,并且在收款人位置有一个周期表达式。例如:

~ 每月 费用:租金 $500.00 费用:食品 $450.00 费用:汽车:燃油 $120.00 费用:保险 $150.00 费用:电话 $125.00 费用:公用事业 $100.00 费用:电影 $50.00 费用 $200.00 ; 所有其他费用 资产

~ 每年 费用:汽车:维修 $500.00 资产

这两个定期交易提供了通常的月度开支,以及一个典型的年度开支。要帮助您找出任何类别的平均月度开支,可以使用类似于以下的命令:

$ ledger -p "今年" --monthly --average register ^expenses

报告的总数是每个账户当前年的平均值。

一旦这些周期性交易被定义,创建预算报告就像在命令行中添加 --budget 一样简单。例如,一个典型的月度支出报告将是:

$ ledger --monthly register ^费用

要查看与您的预算相平衡的相同报告,请使用:

$ ledger --budget --monthly register ^开支

预算报告仅包括出现在预算中的账户。要查看所有与预算相平衡的支出,请使用 --add-budget。您甚至可以仅查看未预算的支出,使用 --unbudgeted:

$ ledger --unbudgeted --monthly register ^支出

您还可以将这些标志与 balance 命令一起使用。

有时,了解未来的财务状况是有用的,例如确定何时账户将达到零。Ledger 使这变得简单,使用与预算相同的定期交易。可以生成一个示例预测报告,内容如下:

$ ledger --file drewr3.dat --forecast "T>{$-500.00}" register ^资产 ^负债

本报告将继续输出记录,直到运行总计大于 $-500.00。最后的记录始终会显示,以告知您之后的总计将是多少。

预测也可以与 balance 报告一起使用,但仅按日期,而不是与运行总计进行比较:

$ ledger --forecast "d<[2010]" bal ^资产 ^负债

Ledger 直接支持 “timelog” 条目,其形式为:

i 2013/03/28 22:13:00 账户[ 收款人] o 2013/03/29 03:39:00

这记录了对给定账户的签到和签出。如果您愿意,可以同时签到多个账户,并且它们可以跨越多天(使用 --day-break 在报告中将它们分开)。签到和签出之间的秒数将累积为该账户的时间。如果签出使用大写的‘O’,则该交易标记为“已清除”。您可以使用可选的收款人,赋予其您喜欢的任何含义。

现在,有几种方法可以生成这些信息。您可以使用 timeclock.el 包,这是 Emacs 的一部分。或者您可以用您喜欢的任何语言编写一个简单的脚本来发出类似的信息。或者您可以使用 Org 模式的时间记录功能和 John Wiegley 开发的 org2tc 脚本。

这些时间记录条目可以出现在单独的文件中,或直接在您的主账本文件中。最初的‘i’和‘o’字符被视为账本“指令”,并在普通交易有效的任何地方被接受。

Ledger 使用值表达式进行多种不同目的的计算:

  1. 报告中显示的值。
  2. 对于谓词(真值为任何非零值),确定计算哪些帖子(选项 --limit EXPR (-l))或显示哪些帖子(选项 --display EXPR (-d))。
  3. 对于排序标准,生成排序键。
  4. 在自动发布使用的匹配标准中。

值表达式支持大多数简单的数学和逻辑运算符,以及一组函数和变量。

显示谓词在注册报告中也非常方便,可以限制打印哪些交易。例如,以下命令仅显示当前月份开始的交易,同时仍然根据所有交易计算运行余额:

$ ledger -d "d>[这个月]" register checking

此命令复杂性的优点在于它以注册中的所有交易的形式打印运行总计。以下更简单的命令类似,但仅对显示的过账进行总计:

$ ledger -b "这个月" register checking

以下是任何值表达式中可用的单字母变量。对于 registerprint 命令,这些变量与单个记录相关,有时与受记录影响的账户相关。对于 balance 命令,这些变量与账户相关,通常在含义上有细微的差别。每个变量在两者中的使用已被指定。

t

这映射到用户通过 --amount EXPR (-t) 指定的内容。在 register 报告中,--amount EXPR (-t) 更改值列;在 balance 报告中,默认情况下没有意义。如果未指定 --amount EXPR (-t),则使用当前报告样式的值表达式。

T

这映射到用户指定的 \--total VEXPR (-T)。在寄存器报告中, \--total VEXPR (-T) 更改总计列;在余额报告中,这是为每个账户提供的值。如果未指定 \--total VEXPR (-T),则使用当前报告样式的值表达式。

m

这始终是现在的时刻/日期。

d

日期

发布的日期,以自纪元以来的秒数表示。对于一个账户来说,这始终是“今天”。

aux_date

一个发布的辅助日期

a

金额

发布的金额;账户的余额,不考虑子账户。

b

发布的成本;一个账户的成本,不包括其子账户。

v

一个帖子或账户的市场价值,不包括其子项。

g

一个发布或账户的净收益(市场价值减去成本基础),不包括其子项。它与“v-b”相同。

深度

账户的深度(“级别”)。如果一个账户有一个父账户,则其深度为一。

n

一个帖子的索引,或影响账户的帖子数量。

X

清除

‘1’ 如果一个发布的交易已被清除,‘0’ 否则。

未清算

‘1’ 如果一个发布的交易状态是未清算,‘0’ 否则。

待处理

‘1’ 如果发布的交易状态为待处理,‘0’ 否则。

R

‘1’ 如果发布不是虚拟的,‘0’ 否则。

Z

‘1’ 如果发布不是自动化的,‘0’ 否则。

可用的一字母函数有:

-

否定论点。

U

参数的绝对(无符号)值。

S

从参数中剥离商品。

P

该论点的当前市场价值。支持语法‘P(x,d)’,该语法在时间‘d’上产生市场价值。如果未给出日期,则使用当前时刻。

格式字符串可用于更改报告的输出格式。它们通过将格式字符串传递给 --format FORMAT\_STRING (-F) 选项来指定。在该字符串中,允许使用构造,使得可以以自定义方式显示帐户或帖子各个部分。

还有几个额外的标志,允许您为特定报告定义格式。这些在您的配置文件中定义是很有用的,并且将允许您从命令行运行账本报告,而无需为每个命令输入新的格式。

  • --余额格式 FORMAT_STRING
  • --预算格式 FORMAT_STRING
  • --已清格式 FORMAT_STRING
  • --csv格式 FORMAT_STRING
  • --图表金额格式 FORMAT_STRING
  • --图表总计格式 FORMAT_STRING
  • --价格数据库格式 FORMAT_STRING
  • --价格格式 FORMAT_STRING
  • --注册格式 FORMAT_STRING

在格式字符串中,使用百分号‘%’字符指定替换。所有替换的基本格式是:

%[-][最小宽度][最大宽度](VALEXPR)

如果可选的负号‘-’跟在百分号‘%’后面,替代的内容将左对齐。默认情况下是右对齐。如果接下来给定了最小宽度,替代的文本将至少达到该宽度,可能更宽。如果给定了一个小数点和最大宽度,替代的文本将永远不会超过这个宽度,并将被截断以适应。以下是一些示例:

%-20P

交易的收款人,左对齐并填充到20个字符宽。

%20P

相同,右对齐,宽度至少为 20 个字符。

%.20P

相同,宽度不超过20个字符。

遵循格式约束的表达式可以是一个单独的字母,或者是一个用括号或方括号括起来的表达式。

出于演示目的,使用了来自 expr.dat 的期刊数据。允许的表达式有:

%

插入一个百分号。

$ ledger -f expr.dat --format "%%\n" reg 资产

t

插入由 --amount EXPR (-t) 指定的值表达式的结果。如果未指定 --amount EXPR (-t),则使用当前报告样式的值表达式。

T

插入由 --total VEXPR (-T) 指定的值表达式的结果。如果未指定 --total VEXPR (-T),则使用当前报告样式的值表达式。

(表达式)

插入括号中给定的值表达式所产生的金额。例如,要插入账户总值的五倍,可以说‘%12(5*O)’。注意:在该表达式中将五放在前面是很重要的,以便商品不会从总值中剥离。

$ ledger -f expr.dat --format "%12(5*O)\n" reg assets

[日期格式]

插入使用日期格式字符串格式化的帖子日期的结果,完全像strftime (3)支持的那样。例如:‘% [%Y/%m/%d %H:%M:%S]’.

S

插入读取交易数据的文件的路径名。仅在 register 报告中有意义。

$ ledger -f ~/journal.dat --format "%S\n" reg 资产

/home/jwiegley/journal.dat

B

插入该事务在文件中的起始字符位置。

$ ledger -f expr.dat --format "%B\n" reg 资产

b

在文件中插入该事务的开始行。

$ ledger -f expr.dat --format "%b\n" reg 资产

E

在文件中插入该事务的结束字符位置。

$ ledger -f expr.dat --format "%E\n" reg 资产

e

在文件中插入该事务的结束行。

$ ledger -f expr.dat --format "%e\n" reg 资产

D

返回根据默认格式的日期。

d

返回根据默认格式的日期。如果交易有生效日期,它将打印 ACTUAL_DATE=EFFECTIVE_DATE

X

如果一个帖子已被清除,则返回1,否则返回0。

Y

这与‘%X’相同,除了只有在所有成员发布的状态相同的情况下才显示状态字符。

C

插入交易代码。这是交易第一行括号内指定的值。

$ ledger -f expr.dat --format "%C\n" reg 资产

P

插入与过账相关的收款人。

$ ledger -f expr.dat --format "%P\n" reg 资产

A

插入账户的全名。

$ ledger -f expr.dat --format "%A\n" reg

资产:现金 费用:办公用品

N

插入与发布相关的备注(如果存在的话)。

$ ledger -f expr.dat --format "%N\n" reg 资产

/

‘%/’ 结构是特殊的。它将格式字符串分隔为交易的第一次记录所打印的内容和所有后续记录所打印的内容。如果不使用,则对所有记录使用相同的格式字符串。

$ ledger -f expr.dat --format "%P\n%/%A\n" reg

储蓄罐 费用:办公用品

作为 \--format FORMAT_STRING 字符串灵活性的一个例子,默认的余额格式如下所示(各种函数将在后面描述):

"%(justify(scrub(display_total), 20, -1, true, color))" " %(!options.flat ? depth_spacer : "")" "%-(ansify_if(partial_account(options.flat), blue if color))\n%/" "%$1\n%/" "--------------------\n"

Python 可以用来扩展您的 Ledger 体验。但首先,必须提到 Ledger 的数据模型,以便后面的内容能够理解。

与 Ledger 的每次交互都发生在一个会话的上下文中。即使您没有手动创建会话,顶层接口函数也会为您创建一个会话。会话是对象存在的地方,例如金额所指的商品。

要使会话有用,您必须使用函数‘read_journal‘将日记读入其中。这将从给定文件中读取分类账数据,在当前会话中填充一个日记对象,并返回对该日记对象的引用。

在期刊中,包含所有与您的数据相关的交易、发布和其他对象。还有自动交易和定期交易等。

以下是如何遍历数据文件中的所有帖子:

导入账本

对于 xact 在 ledger.read tjournal("sample.dat").xacts(): 对于 post 在 xact.posts(): 打印 "转账 %s 到/从 %s" % (post.amount, post.account)

账本数据存在两种形式:原始和处理过的。原始对象是你从上面的遍历中获得的,准确代表了数据文件中看到的内容。考虑这个日志:

= true (资产:现金) $100

2012-03-01 KFC 费用: 食物 $100 资产: 信用

在这种情况下,此文件中的 raw 常规交易是:

2012-03-01 肯德基 费用: 食物 $100 资产: 信用

cooked 形式为:

2012-03-01 KFC 费用: 食物 $100 资产: 信用 $-100 (资产: 现金) $100

所以,关于原始数据与加工数据的简单理解是,原始数据是未处理的数据,而加工数据则是经过所有考虑后的数据。

当你通过迭代其事务来遍历一个日志时,你通常是在查看原始数据。为了查看处理过的数据,你必须通过查询日志生成某种报告:

对于 post 在 ledger.read tjournal("sample.dat").query("food") 中: 打印 "正在转移 %s 到/从 %s" % (post.amount, post.account)

查询遍历文档而不是事务的原因是,查询通常只返回它们所应用的事务的一个“切片”。您可以通过查看其 xact 成员始终获取匹配文档的事务:

last_xact = None for post in ledger.read_journal("sample.dat").query(""): if post.xact != last_xact: for post in post.xact.posts(): print "Transferring %s to/from %s" % (post.amount, post.account) last_xact = post.xact

此查询最终报告了期刊中每个已处理的发布,但它是按交易进行的。它依赖于一个事实,即未排序的报告以从期刊文件中解析的确切顺序返回发布。

Journal.query() 方法接受您可以在命令行中指定的每个参数,包括 --options.

由于一个查询会“处理”它所应用的期刊,因此在任何给定时间内,该期刊只能有一个活动查询。一旦查询对象消失(在 for 循环之后),数据就会恢复到其原始状态。

您可以使用 'python' 指令将 Python 嵌入到您的数据文件中:

python import os def check_path(path_value): print "%s => %s" % (str(path_value), os.path.isfile(str(path_value))) return os.path.isfile(str(path_value))

标签路径 断言检查路径(值)

2012-02-29 KFC ; PATH: somebogusfile.dat 费用: 食物 $20 资产: 现金

您以这种方式定义的任何 Python 函数都会立即作为 valexpr 函数可用。

当数字来自账本时,例如 post.amount,值的类型是金额。它可以像普通数字一样使用,除了加法和减法仅限于相同商品的金额。如果您需要创建多个商品的总和,请使用余额。例如:

total = Balance() for post in ledger.read_journal("sample.dat").query(""): total += post.amount print total

Ledger 被开发为一套分层的功能,其中较低层级对较高层级一无所知。实际上,在开发过程中构建了多个库,并将单元测试链接到这些库,因此对于较低层级来说,违反这种模块化将导致链接错误。

这些层级是:

  • 实用代码

在Ledger中,有很多通用的功能用于时间解析、使用Boost.Regex、错误处理等。这一切都是以可以在其他项目中根据需要重用的方式完成的。

  • 商品化金额 (amount_t, commodity_t 和朋友们)

一个将多精度有理数(通过GMP)与商品结合的数值抽象。这些结构可以像常规数字一样在C++或Python中操作(作为Amount对象)。

  • 商品池

商品均由商品池所有,因此未来对金额的解析可以链接到相同的商品,并建立一致的价格历史和格式细节记录。

  • 余额
增加了多个不同商品的金额概念。支持简单的算术运算,以及与非商品化值的乘法和除法。
  • 价格历史

金额有价格,这些价格保存在一个数据图中,而金额代码本身对此仅有模糊的了解(有三个访问点,以便金额可以查询其在特定日期的重新评估价格)。

通常,Ledger 中的较高层并不关心某个东西是金额还是余额,他们只想将东西添加到其中或打印出来。为此,我创建了一个类型擦除类,value

t/Value,可以将许多东西放入其中并进行操作。它们可以包含金额、余额、日期、字符串等。如果你尝试对两个没有意义的值进行操作(例如将金额除以余额),将在运行时发生错误,而不是在编译时(如果你真的尝试将 amount_t 除以 balance_t,就会发生这种情况)。

这是值表达式语言的核心数据类型。

  • 值表达式

下一层增加了围绕值概念的函数和运算符。这使得您可以在运行时对值应用变换和测试,而无需将其嵌入到 C++ 中。可用的函数集由 Ledger 中的每个对象类型(帖子、账户、交易等)定义,尽管核心引擎对此一无所知。它的基础只知道如何对值应用运算符,以及如何将它们传递给函数并从函数接收它们。

  • 查询表达式

表达式在命令行中输入可能很繁琐,因此有一种称为“查询表达式”的简写方式。这些表达式本身不增加任何功能,而是纯粹从输入字符串转换为相应的值表达式,例如输入字符串‘cash’被转换为‘(account =~ /cash/)’。这是一种便利层。

  • 格式字符串

格式字符串允许你将值表达式插入到字符串中,要求任何插入的值必须具有字符串表示。实际上,这只是计算当前报告上下文中的值表达式,调用结果值的 to_string() 方法,并将结果放入输出字符串中。它还提供类似 printf 的行为,例如最小/最大宽度、右/左对齐等。

  • 日志项目

接下来是一个被任何可以出现在日志中的内容共享的基本类型:item t。它包含所有这些解析实体的共同细节,比如它是在什么文件和行上找到的等。

  • 日志帖子

在期刊中发现的数量最多的对象,帖子是一种包含账户、金额、成本和元数据的项目。还有一些其他复杂情况,比如账户可以标记为虚拟,金额可以是一个表达式等。

  • 日志交易
帖子始终由交易拥有。这个`item_t`的子类知道日期、收款人等信息。如果从一个帖子请求日期或元数据标签,而它没有这些信息,则会查询交易以查看它是否可以提供这些信息。
  • 日志账户

帖子也由账户共享,尽管实际的内存由事务管理。每个账户都知道其内部的所有帖子,但包含的信息相对较少。

  • 日志对象

最后,所有交易及其记录,以及所有账户,都由一个 journal_t 对象拥有。这是查询和报告您数据的首选对象。

  • 文本期刊解析器
有一个文本解析器,完全包含在 textual.cc 中,它知道如何将文本解析为日记对象,然后这些对象被“最终确定”并添加到日记中。最终确定是强制执行双重记账保证的步骤。
  • 迭代器

每个日志对象都是“可迭代的”,这些迭代器在 iterators.h 和 iterators.cc 中定义。为了模块化,这种迭代逻辑被保留在基本日志对象之外。

  • 比较器

另一个隔离到其自身层次的抽象,这个类封装了期刊对象的比较,基于用户传递给 --sort VEXPR 的任何值表达式。

  • 临时

许多报告创建伪日志对象,例如在“总计”账户中报告总数的帖子。这些对象由 temporaries_t 对象创建和管理,该对象在报告过滤器的许多地方被使用。

  • 选项处理
有一个选项处理子系统被许多更下层的模块使用。这使我相对容易添加新选项,并使这些选项设置可以立即被值表达式访问。
  • 会话对象

每个期刊对象都由一个会话拥有,该会话为该对象提供支持。在图形用户界面术语中,这是期刊数据对象的控制器对象,其中每个文档窗口都是一个单独的会话。它们都由全局范围拥有。

  • 报告对象

每次您创建任何报告输出时,都会创建一个报告对象来确定您想要看到的内容。在 Ledger REPL 中,每次执行命令时都会创建一个新的报告对象。在 CLI 模式下,只会生成一个报告对象,因为 Ledger 在显示结果后会立即退出。

  • 报告过滤器
Ledger 生成数据的方式是这样的:它向会话请求当前的日志,然后创建一个应用于该日志的迭代器。迭代器的类型取决于报告的类型。
然后遍历这个迭代器,迭代器产生的每个对象都被传递给一个“项处理器”,其类型与迭代器的类型直接相关。
有很多很多的项目处理器,可以串联在一起。每个处理器接收一个项目(帖子、账户、交易等),对其执行某些操作,然后将其传递给链中的下一个处理器。有一些过滤器计算运行总计;有一些在以新顺序播放之前对所有输入项目进行排队和排序;还有一些过滤掉不符合谓词的项目等。Ledger 中几乎每个报告功能都与一个或多个过滤器相关。查看 filters.h,目前定义了超过 25 个过滤器。
  • 过滤器链

过滤器如何连接,以及连接的顺序,是一个复杂的过程,基于用户指定的各种选项。这是链逻辑的工作,完全在 chain.cc 中实现。花了很长时间才将这个逻辑调整得完全正确,这就是为什么我还没有将这一层暴露给 Python 桥接。

  • 输出模块

虽然过滤器很好,但最终你想看到的是内容。这是特殊的“叶子”过滤器,称为输出模块的工作。它们的实现方式与常规过滤器相同,但它们没有“下一个”过滤器来传递数据。相反,它们是终点,必须对项目做一些处理,以便用户在屏幕上或文件中看到某些内容。

  • 选择查询

选择查询对所有事物都了解很多,尽管它们通过将用户的查询以所有其他呈现的特性来实现其逻辑。选择查询没有自己的功能,它们只是一个简写,通过更清晰、更一致的语法提供对大部分分类账功能的访问。

  • 全球范围
有一个主对象,它拥有所有其他对象,这就是 Ledger 的全局范围。它创建其他对象,为命令行工具提供 REPL 行为等。在 GUI 术语中,这就是应用程序对象。
  • 主要驱动程序

这创建了全局作用域对象,执行错误报告,并处理必须在全局作用域创建之前的命令行选项,例如 --debug CODE.

这就是Ledger的概述。其余的都是细节,例如每个日记条目暴露了哪些值表达式,当前存在多少个过滤器,报告和会话范围定义了哪些选项等等。

本章提供了期刊数据格式的完整描述,适合其他语言的实现者参考。对于用户来说,关于保持日记的章节虽然不那么详尽,但更符合常见用法(见 保持日记)。

数据以 交易 的形式收集,这些交易发生在一个或多个 日志文件 中。每个交易又由一个或多个 分录 组成,这些分录描述了 金额 如何从一个 账户 流向另一个账户。以下是最简单的日志文件的示例:

2010/05/31 只是一个例子 费用:某个:账户 $100.00 收入:另一个:账户

在这个例子中,有一个交易日期、一个收款人或交易描述,以及两个记账。记账显示从收入层级内的一个账户转出一百美元到指定的支出账户。这些账户的名称和含义是任意的,没有暗示任何偏好,尽管你会发现遵循标准会计实践是有用的(见 会计原则与分类账)。

由于第二个记账中缺少一个金额,因此假设它是第一个的相反数。这保证了复式记账的基本规则:每笔交易的总和必须平衡为零,否则就是错误的。每当账本在交易中遇到 null posting 时,它会用它来平衡剩余部分。

通常,尽管没有强制要求,人们会将第一次发布视为目的地,而将最后一次发布视为来源。因此,第一次发布的金额通常是正数。考虑:

2010/05/31 一笔收入交易 资产:支票账户 $1,000.00 收入:工资

2010/05/31 一笔费用交易 费用:餐饮 $100.00 资产:支票

日记的核心是它记录的金额,这一事实反映在允许的金额表达的多样性上。所有这些在这里都有涉及,尽管必须说,有时有多种方式可以实现所需的结果。

注意: 重要的是要注意,账户结束和金额开始之间必须至少有两个空格(包括商品标识符)。

最简单的形式,接受裸小数:

2010/05/31 一笔收入交易 资产:支票账户 1000.00 收入:工资

这样的金额只能使用可选的句点作为小数点。这些被称为 整数金额非商品化金额。在大多数方面,它们与 商品化金额 相似,但有一个显著的区别:它们在报告中始终以 完整精度 显示。稍后会详细介绍这一点。现在,必须提到的是 Ledger 如何存储数字。

Ledger 解析的每个数字都内部存储为无限精度的有理值。永远不使用浮点数学,因为它无法被信任以保持值的精度。因此,在上述‘1000.00’的情况下,内部值为‘100000/100’。

虽然有理数在不失去精度方面表现出色,但问题随之而来:它们应该如何显示?像‘100000/100’这样的数字没有问题,因为它代表一个干净的小数分数。但当数字‘1/1’被三除时呢?应该如何打印‘1/3’,一个无限循环的小数?

Ledger 通过在最后可能的时刻将有理数转换为十进制来解决这个问题,并且仅用于显示。因此,有时必须进行一些舍入。如果这种舍入会影响运行总计的计算,则会生成特殊的调整记录,以使您意识到它已经发生。在实践中,这种情况很少发生,但即便如此,它也不反映 内部金额 的调整,仅反映显示的金额。

仍然没有回答的是 Ledger 如何进行数值四舍五入。‘1/3’ 应该打印为 ‘0.33’ 还是 ‘0.33333’?对于商品化的金额,小数位数是通过观察每种商品的使用情况来决定的;但在整数金额的情况下,必须选择一个任意的因子。最初,这个因子是六。因此,‘1/3’ 被打印回 ‘0.333333’。此外,这个四舍五入因子与每个特定值相关联,并在数学运算中传递。例如,如果该特定数字与自身相乘,结果的小数精度将是十二。加法和减法不会影响精度。

由于每个整数金额保留其自身的显示精度,这被称为 全精度,与商品化金额相对,后者总是依赖其商品来确定应四舍五入到什么精度,因此使用 商品精度

一个 商品化金额 是一个与某种商品相关的整数金额。该商品可以出现在金额之前或之后,并且可能与金额之间有或没有空格。商品名称中允许大多数字符,除了以下字符:

  • 任何类型的空白字符
  • 数字
  • 标点符号: .,;:?!
  • 数学和逻辑运算符: -+*/^&|=
  • 括号字符: <>[](){}
  • @符号: @

然而,如果这些词被双引号包围,它们中的任何一个都可能出现在商品名称中,例如:

如果发现一个 报价商品,它也会以引号显示,以避免对哪个部分是金额,哪个部分是商品产生任何混淆。

另一个商品化金额的特点是,它们以解析后的相同形式报告。如果您使用‘$100’指定美元金额,它们将以相同的方式打印;同样适用于‘100 $’或‘$100.000’。您甚至可以使用小数逗号,例如‘$100,00’,或千位分隔符,如‘$10,000.00’。

这些显示特征与商品相关联,结果是所有相同商品的数量都被一致报告。最明显的地方是 显示精度,它由给定商品所见的最精确值决定——在大多数情况下。

Ledger 区分 观察到的金额 和未观察到的金额。观察到的金额由 Ledger 进行评估,以确定使用该商品的金额应如何显示;未观察到的金额仅在其价值上具有重要性——无论它们如何被指定,都不会改变该商品中其他金额的显示方式。

一个例子是在接下来的成本表达式中找到的。

您已经看到如何为一个发布指定商品化金额或整数金额。但是,如果您为某样东西支付的金额是一个商品,而收到的金额是另一个商品呢?有两种主要方式来表达这一点:

2010/05/31 农民市场 资产:我的储藏室 100 个苹果 资产:支票账户 -$20.00

在这个例子中,您为一百个苹果支付了二十美元。您每个苹果的成本是二十美分,Ledger 为您计算了这个隐含成本。您还可以使用 cost amount 使成本明确:

2010/05/31 农民市场 资产:我的储藏室 100 个苹果 @ $0.200000 资产:支票账户

这里的 每单位成本 以成本金额的形式明确给出;由于成本金额是 未观察到的,因此使用六位小数对最终报告中美元金额的显示没有影响。您还可以指定 总成本

2010/05/31 农夫市场 资产:我的储藏室 100 个苹果 @@ $20 资产:支票账户

这三种形式具有相同的含义。在大多数情况下,首选第一种,但当涉及到两个以上的帖子时,第二种和第三种是必要的。

2010/05/31 农民市场 资产:我的储藏室 100 个苹果 @ $0.200000 资产:我的储藏室 100 个菠萝 @ $0.33 资产:我的储藏室 100 个 "蟹苹果" @ $0.04 资产:支票账户

这里隐含的成本是‘$57.00’,它会自动输入到空白记录中,以便交易平衡。

在每个涉及多个商品的交易中,总有一个是 主要商品。这个商品应该被视为交换商品,或用于购买和销售其他商品单位的商品。在上面的水果示例中,美元是主要商品。这是由账本根据商品在交易中的位置决定的:

2010/05/31 示例交易 费用 100 次要 资产 -50 主要

2010/05/31 示例交易 费用 100 次要 @ 0.5 主要 资产

2010/05/31 示例交易 费用 100 次要 @@ 50 主要 资产

唯一涉及初级与次级知识的情况是在使用 \--market (-V) 或 \--basis (-B) 选项的报告中。在这些情况下,仅显示初级商品。

如果一笔交易只使用一种商品,则该商品也被视为主要商品。实际上,当账本确保所有交易的余额为零时,它只会对主要商品提出这一要求。

以下内容已从 Ledger 3.0 中移除:

  • OFX 支持。
  • GnuCash 文件导入。
  • 选项 --performance (-g)。
  • 余额报告现在默认显示所有相关账户。这与 2.x 相反。也就是说,3.0 中的 bal 做了 2.x 中 ‘-s bal’ 的事情。要查看 2.6 的行为,请在 3.0 中使用 --collapse (-n) 选项,如 ‘bal -n’。--subtotal (-s) 选项不再对余额报告产生任何影响。

在 Ledger 3.0 中,以下内容已被弃用:

  • 单字符值表达式已被弃用,应更改为 3.0 中可用的新值表达式
  • 以下环境变量在 Ledger 3.0 中已被重命名:
`账本`
现在是 `LEDGER_FILE`, 
`账本初始化`
现在是 `LEDGER_INIT_FILE`, 
`价格历史`
现在是 `LEDGER_PRICE_DB`, 
`价格_EXP`
现在是 `LEDGER_PRICE_EXP`.
总结
Ledger 是一款基于文本日记的命令行会计工具,提供双重记账功能。它没有复杂的用户界面,回归了早期计算机时代的简约风格。用户需要记录日记,这是会计的基础。通过记录借方和贷方,Ledger 允许用户跟踪所有账户的资金流动,确保每笔交易的借贷平衡。使用 Ledger,用户可以轻松管理个人财务,分析消费习惯,制定预算。Ledger 的设计旨在简化会计过程,用户只需输入交易记录,系统会自动跟踪余额。与其他财务软件不同,Ledger 不会修改用户的输入文件,只用于数据分析。它支持在 Unix 平台上编译,并提供在线帮助和社区支持,适合各种会计应用,包括非营利组织的财务管理。