{"id":13442556,"url":"https://github.com/ledger/cl-ledger","last_synced_at":"2026-03-03T11:39:38.273Z","repository":{"id":492954,"uuid":"119424","full_name":"ledger/cl-ledger","owner":"ledger","description":"Port of the Ledger accounting system (see project \"ledger\") to Common Lisp","archived":false,"fork":false,"pushed_at":"2025-08-28T08:03:46.000Z","size":1680,"stargazers_count":104,"open_issues_count":2,"forks_count":24,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-08-28T15:24:18.127Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://www.newartisans.com","language":"Common Lisp","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ledger.png","metadata":{"files":{"readme":"README.org","changelog":null,"contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2009-02-01T21:18:48.000Z","updated_at":"2025-08-28T08:03:50.000Z","dependencies_parsed_at":"2025-11-20T11:01:20.857Z","dependency_job_id":null,"html_url":"https://github.com/ledger/cl-ledger","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ledger/cl-ledger","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ledger%2Fcl-ledger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ledger%2Fcl-ledger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ledger%2Fcl-ledger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ledger%2Fcl-ledger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ledger","download_url":"https://codeload.github.com/ledger/cl-ledger/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ledger%2Fcl-ledger/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":285422071,"owners_count":27168929,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-20T02:00:05.334Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T03:01:47.216Z","updated_at":"2025-11-20T11:03:00.038Z","avatar_url":"https://github.com/ledger.png","language":"Common Lisp","readme":"#+TITLE: CL-Ledger\n\n*CL-Ledger* is a Common Lisp port of the [[http://ledger-cli.org/][Ledger]] double-entry\naccounting system.\n\n* Installation\n\nThe easiest way to install *CL-Ledger* is using [[https://www.quicklisp.org][Quicklisp]]:\n\n#+BEGIN_SRC lisp\n(quicklisp:quickload \"cl-ledger\")\n#+END_SRC\n\nIf you want to work with the latest source code and need to install it\nmanually, you will need the following libraries:\n\n - alexandria\n - cl-containers\n - cl-ppcre\n - local-time\n - series\n\nThey can be installed easily using *Quicklisp*:\n\n#+BEGIN_SRC lisp\n(quicklisp:quickload '(\"alexandria\" \"cl-containers\" \"cl-ppcre\" \"local-time\" \"series\"))\n#+END_SRC\n\nNow you need to get the source code and pull all of the projects\nrelating to *CL-Ledger*:\n\n#+BEGIN_SRC shell\ngit clone https://github.com/ledger/cl-ledger.git\ncd cl-ledger\ngit submodule init\ngit submodule update\n#+END_SRC\n\nThe next step is to indicate your Common Lisp system where to load\n*CL-Ledger* from.\n\nIf you are using *Quicklisp*, you can add links to the\n/local-projects/ directory:\n\n#+BEGIN_SRC shell\ncd /path/to/quicklisp/\ncd local-projects\nln -s /path/to/cl-ledger .\nln -s /path/to/cl-ledger/cambl .\nln -s /path/to/cl-ledger/periods .\n#+END_SRC\n\nIf you don't use *Quicklisp*, you can add the following contents to\nthe initialization file of your Common Lisp implementation\n(e.g. ~/.sbclrc for *SBCL*):\n\n#+BEGIN_SRC lisp\n(push \"/path/to/cl-ledger/\" asdf:*central-registry*)\n\n(dolist (project '(\"cambl/\" \"periods/\"))\n  (push (merge-pathnames project \"/path/to/cl-ledger/\")\n        asdf:*central-registry*))\n#+END_SRC\n\nMake sure you change ~/path/to/cl-ledger/~ to be the directory where\n*CL-Ledger* lives. Also, be sure this pathname ends with a slash! In\nCL, directory names always end with /.\n\nNow you can run *CL-Ledger* at the REPL like this:\n\n#+BEGIN_SRC lisp\n(asdf:load-system \"cl-ledger\")\n#+END_SRC\n\nThis compiles and loads the *CL-Ledger* core, and also the textual\nparser package, for parsing standard Ledger text files.\n\n* Basic commands\n\nOnce in the REPL, try out this command:\n\n#+BEGIN_SRC lisp\n(ledger:register-report \"/path/to/cl-ledger/doc/sample.dat\")\n#+END_SRC\n\nYou should see a register printed representing the contents of\n/sample.dat/.\nYou can constrain this report using keyword modifiers:\n\n#+BEGIN_SRC lisp\n(ledger:register-report \"/path/to/cl-ledger/doc/sample.dat\"\n                        :account \"books\")\n#+END_SRC\n\n*CL-Ledger* only reads the file on the first run, and if it changes,\nso feel free to repeat the same command several times even for large\njournal files.\n\nThe following reports are supported:\n\n#+BEGIN_SRC lisp\n(ledger:register-report \"/path/to/file\" [OPTIONS])\n(ledger:balance-report \"/path/to/file\" [OPTIONS])\n(ledger:print-report \"/path/to/file\" [OPTIONS])\n(ledger:equity-report \"/path/to/file\" [OPTIONS])\n(ledger:sexp-report \"/path/to/file\" [OPTIONS])\n(ledger:csv-report \"/path/to/file\" [OPTIONS])\n(ledger:derive-entry \"/path/to/file\" [OPTIONS])\n#+END_SRC\n\nAs for /OPTIONS/, any of the following keyword pairs is allowed. There\nare some extra options allowed for ~derive-entry~, for which please\nsee below.\n\n|-----------------------------+----------------------------------------------|\n| Keyword                     | Description                                  |\n|-----------------------------+----------------------------------------------|\n| :account \"REGEXP\"           |                                              |\n|-----------------------------+----------------------------------------------|\n| :not-account \"REGEXP\"       |                                              |\n|-----------------------------+----------------------------------------------|\n| :payee \"REGEXP\"             |                                              |\n|-----------------------------+----------------------------------------------|\n| :not-payee \"REGEXP\"         |                                              |\n|-----------------------------+----------------------------------------------|\n| :note \"REGEXP\"              |                                              |\n|-----------------------------+----------------------------------------------|\n| :not-note \"REGEXP\"          |                                              |\n|-----------------------------+----------------------------------------------|\n| :begin \"YYYY/MM/DD\"         |                                              |\n|-----------------------------+----------------------------------------------|\n| :end \"YYYY/MM/DD\"           |                                              |\n|-----------------------------+----------------------------------------------|\n| :range \"RANGE EXPRESSION\"   | a range expression, like \"this month\"        |\n|-----------------------------+----------------------------------------------|\n| :period \"PERIOD EXPRESSION\" | like \"every month this year\"                 |\n|-----------------------------+----------------------------------------------|\n| :expr \"VALUE-EXPR\"          | most Ledger 2.x value exprs allowed          |\n|-----------------------------+----------------------------------------------|\n| :limit \"VALUE-EXPR\"         | the same as 2.x's --limit or -l              |\n|                             | this is a convenience alias for :expr        |\n|-----------------------------+----------------------------------------------|\n| :only \"VALUE-EXPR\"          | the same as 2.x's --only                     |\n|-----------------------------+----------------------------------------------|\n| :display \"VALUE-EXPR\"       | the same as 2.x's -d or --display            |\n|-----------------------------+----------------------------------------------|\n| :status KEYWORD             | only report transactions whose status        |\n|                             | is :CLEARED, :PENDING or :UNCLEARED          |\n|-----------------------------+----------------------------------------------|\n| :sort \"VALUE-EXPR\"          | sort based on VALUE-EXPR calculation         |\n|-----------------------------+----------------------------------------------|\n| :no-total BOOL              | don't show totals                            |\n|-----------------------------+----------------------------------------------|\n| :collapse BOOL              | collapse multiline entries                   |\n|-----------------------------+----------------------------------------------|\n| :subtotal BOOL              | group all transactions by account            |\n|-----------------------------+----------------------------------------------|\n| :invert BOOL                | negate all transaction values                |\n|                             | (same as saying :amount \"-a\")                |\n|-----------------------------+----------------------------------------------|\n| :related BOOL               | show \"other\" transactions of each entry      |\n|-----------------------------+----------------------------------------------|\n| :lots BOOL                  | show all commodity lot information           |\n|-----------------------------+----------------------------------------------|\n| :lot-prices BOOL            | show commodity lot prices                    |\n|-----------------------------+----------------------------------------------|\n| :lot-dates BOOL             | show commodity lot dates                     |\n|-----------------------------+----------------------------------------------|\n| :lot-tags BOOL              | show commodity lot tags                      |\n|-----------------------------+----------------------------------------------|\n| :amount \"VALUE-EXPR\"        | use EXPR to display transaction amounts      |\n|-----------------------------+----------------------------------------------|\n| :total \"VALUE-EXPR\"         | use EXPR to display the running total        |\n|-----------------------------+----------------------------------------------|\n| :set-amount \"VALUE-EXPR\"    | instead of :amount, actually represent       |\n|                             | the amount using EXPR (this is rarely        |\n|                             | something you want to do)                    |\n|-----------------------------+----------------------------------------------|\n| :set-total \"VALUE-EXPR\"     | same for the running total                   |\n|-----------------------------+----------------------------------------------|\n| :bridge-totals BOOL         | if the running totals are not contiguous     |\n|                             | create revaluation entries to fill gaps      |\n|-----------------------------+----------------------------------------------|\n| :show OUTPUT-MODE           | show amounts and totals using the given mode |\n|-----------------------------+----------------------------------------------|\n| :show :market               | .. in terms of their market value            |\n|-----------------------------+----------------------------------------------|\n| :show :basis                | .. in terms of their basis cost              |\n|-----------------------------+----------------------------------------------|\n\n\nHere's a quick table for translating *Ledger* 2.6.1 options into their\ncorresponding *CL-Ledger* keywords:\n\n|-------------------+--------------------+-------------------|\n| Short option      | Long option        | CL-Ledger keyword |\n|-------------------+--------------------+-------------------|\n| -b ARG            | --begin ARG        | :begin ARG        |\n|-------------------+--------------------+-------------------|\n| -e ARG            | --end ARG          | :end ARG          |\n|-------------------+--------------------+-------------------|\n| -p ARG            | --period ARG       | :period ARG       |\n|-------------------+--------------------+-------------------|\n| -l ARG            | --limit ARG        | :limit ARG        |\n|-------------------+--------------------+-------------------|\n|                   | --only ARG         | :only ARG         |\n|-------------------+--------------------+-------------------|\n| -d ARG            | --display ARG      | :display ARG      |\n|-------------------+--------------------+-------------------|\n| -n (for balances) |                    | :no-total t       |\n|-------------------+--------------------+-------------------|\n| -n                | --collapse         | :collapse t       |\n|-------------------+--------------------+-------------------|\n| -r                | --related          | :related t        |\n|-------------------+--------------------+-------------------|\n| -s                | --subtotal         | :subtotal t       |\n|-------------------+--------------------+-------------------|\n| -S EXPR           | --sort ARG         | :sort ARG         |\n|-------------------+--------------------+-------------------|\n| -t EXPR           | --sort-entries ARG | :sort-entries ARG |\n|-------------------+--------------------+-------------------|\n\n\nHere are a few examples, using /sample.dat/ as a reference:\n\n#+BEGIN_SRC lisp\n(ledger:balance-report \"doc/sample.dat\")\n  =\u003e\n         $1,980.00\n           50 AAPL  Assets\n         $1,980.00    Bank:Checking\n           50 AAPL    Brokerage\n        $-2,500.00  Equity:Opening Balances\n            $40.00  Expenses:Books\n        $-1,000.00  Income:Salary\n           $-20.00  Liabilities:MasterCard\n  -----------------------------------------------------\n        $-1,500.00\n           50 AAPL\n\n(ledger:balance-report \"doc/sample.dat\" :account \"Assets\")\n  =\u003e\n         $1,980.00\n           50 AAPL  Assets\n         $1,980.00    Bank:Checking\n           50 AAPL    Brokerage\n  -----------------------------------------------------\n         $1,980.00\n           50 AAPL\n\n(ledger:register-report \"doc/sample.dat\" :begin \"2004/05/10\" :end \"2004/05/28\")\n  =\u003e\n  2004/05/14 Pay day              Assets:Bank:Checking      $1,000.00    $1,000.00\n                                  Income:Salary            $-1,000.00        $0.00\n  2004/05/27 Book Store           Expenses:Books               $20.00       $20.00\n                                  Liabilities:MasterCard      $-20.00        $0.00\n  2004/05/27 Credit card company  Liabilities:MasterCard       $20.00       $20.00\n                                  Assets:Bank:Checking        $-20.00        $0.00\n\n(ledger:register-report \"doc/sample.dat\" :limit \"commodity=(1 AAPL)\" :total \"V\")\n  =\u003e\n  2004/05/01 Investment balance   Assets:Brokerage            50 AAPL    $1,500.00\n#+END_SRC\n\n* Options to /derive-entry/\n\nThe reporting command ~derive-entry~ takes some special options.\n\nThe /derive-entry/ report uses *CL-Ledger* to intelligently create a new\nentry for you. The possible keywords arguments are:\n\n - ~:date~\n - ~:payee~\n - ~:account~\n - ~:balance-account~\n - ~:amount~\n - ~:append~\n\nExcept for ~:payee~, all of these keyword arguments are optional. Here\nis what they mean:\n\n|---------------------------+--------------------------------------------------|\n| Keyword                   | Description                                      |\n|---------------------------+--------------------------------------------------|\n| :payee \"REGEXP\"           | Find the most recent entry whose payee matches   |\n|                           | REGEXP, and base the new entry derivation on     |\n|                           | its details. If no matching entry can be found,  |\n|                           | the payee of the newly created entry will        |\n|                           | exactly match REGEXP.                            |\n|---------------------------+--------------------------------------------------|\n| :date \"DATE-STRING\"       | The date of the new entry will be DATE-STRING,   |\n|                           | otherwise it is today.                           |\n|---------------------------+--------------------------------------------------|\n| :account \"REGEXP\"         | Set the first account line in the new entry to   |\n|                           | be the most recently used account which matches  |\n|                           | REGEXP. If no such account can be found, an      |\n|                           | account named REGEXP is used. If no account is   |\n|                           | specified, the account \"Expenses:Unknown\" is     |\n|                           | used.                                            |\n|---------------------------+--------------------------------------------------|\n| :balance-account \"REGEXP\" | Like :ACCOUNT, except this refers to the         |\n|                           | account used for the second transaction in the   |\n|                           | newly derived entry. If not specified, a         |\n|                           | calculated \"balance account\" is looked for in    |\n|                           | the matching entry; if this does not apply, the  |\n|                           | journal's default account is used; if this does  |\n|                           | not apply, the account \"Assets:Unknown\" is used. |\n|---------------------------+--------------------------------------------------|\n| :amount \"VALUE-STRING\"    | The amount of the first transaction. If it has   |\n|                           | no commodity, the correlated commodity from the  |\n|                           | discovered entry is used.                        |\n|---------------------------+--------------------------------------------------|\n| :append BOOL              | If non-NIL, the new entry is written to the same |\n|                           | journal where the matching entry was found (for  |\n|                           | a binder that references many journals, this is  |\n|                           | whichever file the discovered entry was in).     |\n|---------------------------+--------------------------------------------------|\n\nHere are a few examples, using /sample.dat/ as a reference:\n\n#+BEGIN_SRC lisp\n(ledger:derive-entry \"doc/sample.dat\" :payee \"book\")\n  =\u003e\n  2007/12/04 Book Store\n      Expenses:Books                            $20.00\n      Liabilities:MasterCard\n\n(ledger:derive-entry :payee \"book\" :amount \"$125\")\n  =\u003e\n  2007/12/04 Book Store\n      Expenses:Books                           $125.00\n      Liabilities:MasterCard\n\n(ledger:derive-entry :payee \"Hello World\")\n  =\u003e\n  2007/12/04 Hello World\n      Expenses:Unknown\n      Assets:Unknown\n\n(ledger:derive-entry :date \"2004/01/01\" :payee \"Hello World\")\n  =\u003e\n  2004/01/01 Hello World\n      Expenses:Unknown\n      Assets:Unknown\n\n(ledger:derive-entry :payee \"book\" :account \"equ\")\n  =\u003e\n  2007/12/04 Book Store\n      Equity:Opening Balances                   $20.00\n      Liabilities:MasterCard\n\n(ledger:derive-entry :payee \"book\" :account \"Who Knows\")\n  =\u003e\n  2007/12/04 Book Store\n      Who Knows                                 $20.00\n      Liabilities:MasterCard\n\n(ledger:derive-entry :payee \"book\" :balance-account \"bank\")\n  =\u003e\n  2007/12/04 Book Store\n      Expenses:Books                            $20.00\n      Assets:Bank:Checking\n\n(ledger:derive-entry :payee \"book\" :account \"liab\"\n                     :balance-account \"bank\")\n  =\u003e\n  2007/12/04 Book Store\n      Liabilities:MasterCard                   $-20.00\n      Assets:Bank:Checking\n\n(ledger:derive-entry :payee \"book\" :account \"bank\" :amount \"50\")\n  =\u003e\n  2007/12/04 Book Store\n      Assets:Bank:Checking                      $50.00\n      Liabilities:MasterCard\n\n(ledger:derive-entry :payee \"book\" :account \"bank\" :amount \"$125\")\n  =\u003e\n  2007/12/04 Book Store\n      Assets:Bank:Checking                     $125.00\n      Liabilities:MasterCard\n#+END_SRC\n\n* Date format\n\nThe date format used in your journal file can be specified either\nusing the ~*input-time-format*~ variable (by default: \"%Y/%m/%d%| %H:%M:%S\"),\nor by writing a ~F~ command directive at the beginning of the journal:\n\n#+BEGIN_SRC\nF %Y-%m-%d\n\n2017-09-01 * Some payee\n  Some account  45,18 EUR\n  Some other account\n#+END_SRC\n\nThe date format used in reports can be specified using the\n~*output-time-format*~ variable (by default: \"%Y/%m/%d\").\n\n* Binder caching\n\nAfter the call to ~binder~, the variable ~*last-binder*~ contains\nthe contents of what was read. From that point forward, if no binder\nor string is passed to the reporting function, they will assume you\nwish to report on the contents of ~*last-binder*~.\n\n* Implementations status\n\nHere is how *CL-Ledger* stands up against current Lisp\nimplementations:\n\n|----------------+---------------+--------------------------------|\n| Implementation |       Version | Status                         |\n|----------------+---------------+--------------------------------|\n| SBCL           |        1.3.21 | WORKS                          |\n| LispWorks      | 5.02 Personal | WORKS                          |\n| Allegro CL     |  10.0 Express | WORKS                          |\n| Clozure CL     |          1.11 | WORKS                          |\n| OpenMCL        |    2007-07-22 | Fails to compile LOCAL-TIME    |\n| ECL            |    2007-12-07 | WORKS                          |\n| ABCL           |         1.5.0 | Fails to compile CL-LEDGER     |\n| CLISP          |          2.49 | Fails to compile CL-CONTAINERS |\n| CMUCL          | 19d (2007-11) | Fails to compile PERIODS       |\n| GCL            |         2.6.7 | \u003cunable to build so far\u003e       |\n|----------------+---------------+--------------------------------|\n\n* Series\n\nFor fans of the *Series* library, you can apply ~scan-transactions~ or\n~scan-entries~ to a binder/account/journal/entry in order to produce\na /series/ of the corresponding type. Example:\n\n#+BEGIN_SRC lisp\n(collect (ledger:scan-transactions\n          (ledger:read-journal \"doc/sample.dat\")))\n  =\u003e [a list of all transactions, in sequence, within sample.dat]\n#+END_SRC\n\n* Command line\n\nYou can build a standalone /cl-ledger/ binary using the /Makefile/:\n\n#+BEGIN_SRC shell\nmake LISP=sbcl\ncl-ledger -f doc/sample.dat balance\n#+END_SRC\n\n\nYou can also use the [[https://github.com/roswell/roswell][Roswell]] script /cl-ledger.ros/ in the /roswell/\ndirectory to use *CL-Ledger* from the command line:\n\n#+BEGIN_SRC shell\ncl-ledger.ros -f doc/sample.dat balance\n#+END_SRC\n","funding_links":[],"categories":["Common Lisp","Applications"],"sub_categories":["Utilities"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fledger%2Fcl-ledger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fledger%2Fcl-ledger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fledger%2Fcl-ledger/lists"}