
Complete migration of a widely-used v3.2.3 double-entry accounting system: parses and processes all ledger files, full query engine, 18 built-in plugins, CLI suite, up to 2.2× faster.
Kotlin Multiplatform implementation of Beancount v3.2.3, compatible with JVM, Android, iOS, and Kotlin.
Beancount JVM is a complete migration of Python Beancount accounting system to the JVM/Android ecosystem using Kotlin Multiplatform (KMP). It maintains semantic compatibility with Python Beancount 3.2.3 while delivering up to 2.2x performance improvement on large ledgers.
@JvmStatic
Published to Maven Central and discoverable on klibs.io.
For KMP projects (commonMain):
// build.gradle.kts (KMP project)
commonMain.dependencies {
implementation("io.github.tonyzhye:beancount-core:3.2.5")
}For JVM-only projects:
// build.gradle.kts (JVM project)
dependencies {
implementation("io.github.tonyzhye:beancount-core:3.2.5")
}For Android projects:
// build.gradle.kts (Android project)
dependencies {
implementation("io.github.tonyzhye:beancount-core:3.2.5")
}For Maven (JVM-only):
<dependency>
<groupId>io.github.tonyzhye</groupId>
<artifactId>beancount-core-jvm</artifactId>
<version>3.2.5</version>
</dependency>import io.github.tonyzhye.beancount.api.Beancount as bn
// Load a beancount file
val result = bn.loadFile("ledger.beancount")
// Check for errors
if (result.errors.isNotEmpty()) {
println(bn.formatErrors(result.errors))
return
}
// Query all transactions
val transactions = bn.getTransactions(result.entries)
// Get all accounts
val accounts = bn.getAccounts(result.entries)
// Build price map
val priceMap = bn.buildPriceMap(result.entries)
// Format entries back to beancount syntax
println(bn.formatEntries(result.entries))import io.github.tonyzhye.beancount.api.Beancount;
import io.github.tonyzhye.beancount.core.LoadResult;
// Load a beancount file
LoadResult result = Beancount.loadFile("ledger.beancount");
// Check for errors
if (!result.getErrors().isEmpty()) {
System.out.println(Beancount.formatErrors(result.getErrors()));
return;
}
// Get all accounts
var accounts = Beancount.getAccounts(result.getEntries());
System.out.println("Accounts: " + accounts);# Check a ledger file
beancount check ledger.beancount
# With verbose output
beancount check ledger.beancount --verbose
# Format/beautify a file
beancount format ledger.beancount -o formatted.beancount
# Run BQL queries
beancount query ledger.beancount "SELECT date, narration, position WHERE account ~ 'Expenses'"
# Show file dependencies
beancount deps ledger.beancount
# Get help for any command
beancount check --help┌─────────────────────────────────────────────────────────────┐
│ CLI Layer │
│ bean-check | bean-doctor | bean-format | beanquery │
├─────────────────────────────────────────────────────────────┤
│ API Layer │
│ Beancount singleton (60+ methods) │
├─────────────────────────────────────────────────────────────┤
│ Query Engine │
│ BQL Parser → Compiler → Executor (60+ functions) │
├─────────────────────────────────────────────────────────────┤
│ Loader Layer │
│ Parse → Sort → Booking → Transformations → Validation │
├──────────────┬──────────────┬───────────────────────────────┤
│ Parser │ Plugins │ Core │
│ Lexer │ 18 Built-in │ 12 Directive Types │
│ Parser │ Plugins │ Amount / Cost / Posting │
│ Booking │ │ Inventory / Validation │
└──────────────┴──────────────┴───────────────────────────────┘
core (foundation)
↑
parser (depends on core)
↑
loader (depends on parser, core)
↑
api (depends on loader, parser, query, core, plugin-api)
↑
cli (depends on api, loader, query)
All 18 built-in plugins and the complete BQL query engine are implemented.
Compared against Python Beancount 3.2.3:
Full comparison report: CONSISTENCY_REPORT.md
Compared to Python Beancount 3.2.3 (load + book + validate):
| File Size | Python (ms) | Kotlin (ms) | Speedup |
|---|---|---|---|
| Example (340 KB) | 87 | 77 | 1.1x |
| 1 MB | 161 | 154 | 1.0x |
| 5 MB | 1,467 | 781 | 1.9x |
| 10 MB | 3,423 | 1,543 | 2.2x |
Kotlin scales better as file size grows — from roughly equivalent on small files to 2.2x faster on 10 MB ledgers.
Full benchmark report: PERFORMANCE_REPORT.md
# Build all modules (JVM + Android)
./gradlew build
# Run tests
./gradlew test
# Run Android unit tests
./gradlew testDebugUnitTest
# Generate coverage report
./gradlew koverHtmlReport
# Build CLI distribution
./gradlew :cli:distZip
# Publish to local Maven
./gradlew publishToMavenLocalRecommended: IntelliJ IDEA with Kotlin plugin
settings.gradle.kts is)./gradlew test
# All tests (JVM + Android unit tests)
./gradlew test
# JVM tests only
./gradlew jvmTest
# Specific module
./gradlew :core:jvmTest
./gradlew :parser:jvmTest
./gradlew :loader:jvmTest
./gradlew :query:jvmTest
# Coverage verification
./gradlew koverVerify
# Compatibility tests (requires Python + beancount 3.2.3)
./gradlew :loader:jvmTest --tests "*CompatTest"Coverage Status:
| Module | Coverage |
|---|---|
| core | 81.3% |
| parser | 81.6% |
| loader | 80.1% |
| query | 81.6% |
| api | 94.3% |
| plugin-api | 96.2% |
Contributions are welcome! Please read our Contributing Guide (if available) and ensure your changes:
./gradlew test)This project is licensed under the GNU General Public License v2.0 only (GPL-2.0-only).
Copyright (C) 2026 Tony Ye
Based on Beancount by Martin Blais, licensed under GPL-2.0-only.
This is an independent project, not affiliated with the official Beancount project.
Kotlin Multiplatform implementation of Beancount v3.2.3, compatible with JVM, Android, iOS, and Kotlin.
Beancount JVM is a complete migration of Python Beancount accounting system to the JVM/Android ecosystem using Kotlin Multiplatform (KMP). It maintains semantic compatibility with Python Beancount 3.2.3 while delivering up to 2.2x performance improvement on large ledgers.
@JvmStatic
Published to Maven Central and discoverable on klibs.io.
For KMP projects (commonMain):
// build.gradle.kts (KMP project)
commonMain.dependencies {
implementation("io.github.tonyzhye:beancount-core:3.2.5")
}For JVM-only projects:
// build.gradle.kts (JVM project)
dependencies {
implementation("io.github.tonyzhye:beancount-core:3.2.5")
}For Android projects:
// build.gradle.kts (Android project)
dependencies {
implementation("io.github.tonyzhye:beancount-core:3.2.5")
}For Maven (JVM-only):
<dependency>
<groupId>io.github.tonyzhye</groupId>
<artifactId>beancount-core-jvm</artifactId>
<version>3.2.5</version>
</dependency>import io.github.tonyzhye.beancount.api.Beancount as bn
// Load a beancount file
val result = bn.loadFile("ledger.beancount")
// Check for errors
if (result.errors.isNotEmpty()) {
println(bn.formatErrors(result.errors))
return
}
// Query all transactions
val transactions = bn.getTransactions(result.entries)
// Get all accounts
val accounts = bn.getAccounts(result.entries)
// Build price map
val priceMap = bn.buildPriceMap(result.entries)
// Format entries back to beancount syntax
println(bn.formatEntries(result.entries))import io.github.tonyzhye.beancount.api.Beancount;
import io.github.tonyzhye.beancount.core.LoadResult;
// Load a beancount file
LoadResult result = Beancount.loadFile("ledger.beancount");
// Check for errors
if (!result.getErrors().isEmpty()) {
System.out.println(Beancount.formatErrors(result.getErrors()));
return;
}
// Get all accounts
var accounts = Beancount.getAccounts(result.getEntries());
System.out.println("Accounts: " + accounts);# Check a ledger file
beancount check ledger.beancount
# With verbose output
beancount check ledger.beancount --verbose
# Format/beautify a file
beancount format ledger.beancount -o formatted.beancount
# Run BQL queries
beancount query ledger.beancount "SELECT date, narration, position WHERE account ~ 'Expenses'"
# Show file dependencies
beancount deps ledger.beancount
# Get help for any command
beancount check --help┌─────────────────────────────────────────────────────────────┐
│ CLI Layer │
│ bean-check | bean-doctor | bean-format | beanquery │
├─────────────────────────────────────────────────────────────┤
│ API Layer │
│ Beancount singleton (60+ methods) │
├─────────────────────────────────────────────────────────────┤
│ Query Engine │
│ BQL Parser → Compiler → Executor (60+ functions) │
├─────────────────────────────────────────────────────────────┤
│ Loader Layer │
│ Parse → Sort → Booking → Transformations → Validation │
├──────────────┬──────────────┬───────────────────────────────┤
│ Parser │ Plugins │ Core │
│ Lexer │ 18 Built-in │ 12 Directive Types │
│ Parser │ Plugins │ Amount / Cost / Posting │
│ Booking │ │ Inventory / Validation │
└──────────────┴──────────────┴───────────────────────────────┘
core (foundation)
↑
parser (depends on core)
↑
loader (depends on parser, core)
↑
api (depends on loader, parser, query, core, plugin-api)
↑
cli (depends on api, loader, query)
All 18 built-in plugins and the complete BQL query engine are implemented.
Compared against Python Beancount 3.2.3:
Full comparison report: CONSISTENCY_REPORT.md
Compared to Python Beancount 3.2.3 (load + book + validate):
| File Size | Python (ms) | Kotlin (ms) | Speedup |
|---|---|---|---|
| Example (340 KB) | 87 | 77 | 1.1x |
| 1 MB | 161 | 154 | 1.0x |
| 5 MB | 1,467 | 781 | 1.9x |
| 10 MB | 3,423 | 1,543 | 2.2x |
Kotlin scales better as file size grows — from roughly equivalent on small files to 2.2x faster on 10 MB ledgers.
Full benchmark report: PERFORMANCE_REPORT.md
# Build all modules (JVM + Android)
./gradlew build
# Run tests
./gradlew test
# Run Android unit tests
./gradlew testDebugUnitTest
# Generate coverage report
./gradlew koverHtmlReport
# Build CLI distribution
./gradlew :cli:distZip
# Publish to local Maven
./gradlew publishToMavenLocalRecommended: IntelliJ IDEA with Kotlin plugin
settings.gradle.kts is)./gradlew test
# All tests (JVM + Android unit tests)
./gradlew test
# JVM tests only
./gradlew jvmTest
# Specific module
./gradlew :core:jvmTest
./gradlew :parser:jvmTest
./gradlew :loader:jvmTest
./gradlew :query:jvmTest
# Coverage verification
./gradlew koverVerify
# Compatibility tests (requires Python + beancount 3.2.3)
./gradlew :loader:jvmTest --tests "*CompatTest"Coverage Status:
| Module | Coverage |
|---|---|
| core | 81.3% |
| parser | 81.6% |
| loader | 80.1% |
| query | 81.6% |
| api | 94.3% |
| plugin-api | 96.2% |
Contributions are welcome! Please read our Contributing Guide (if available) and ensure your changes:
./gradlew test)This project is licensed under the GNU General Public License v2.0 only (GPL-2.0-only).
Copyright (C) 2026 Tony Ye
Based on Beancount by Martin Blais, licensed under GPL-2.0-only.
This is an independent project, not affiliated with the official Beancount project.