A self-contained Java command-line program that downloads official exchange-rate history from the Bank of Russia and calculates arithmetic averages for requested day, week, month or year periods.
The result is expressed as Russian roubles per one unit of the selected currency. Only rates actually published by the CBR are averaged; weekends and other dates without a newly published rate are not filled in.
Downloaded rates are cached in SQLite at
~/.cache/rouble-rate-calculator/rates.db. Set the ROUBLE_RATE_DB environment
variable to use another database file. The cache records both rates and fetched
date coverage, so weekends and holidays do not look like missing data. When a
request has several gaps, the calculator makes one CBR request covering the
first through last gap. Historical coverage is reused permanently; today and
future dates remain refreshable.
- CLI: JDK 25 or newer and Maven 3.8 or newer
- Android: JDK 21, Android SDK 36, and Android Build Tools 36.0.0
- Internet access to
www.cbr.ru
The Maven CLI and Android builds currently require different active JDKs. The CLI is compiled with Java 25. The Android build uses Gradle 8.13 and AGP 8.13, which require JDK 17 or newer; this project standardizes its Android builds and app bytecode on Java 21. The current Android toolchain cannot run on Java 25.
Set JAVA_HOME per command, or switch JDKs with your preferred version manager:
JAVA_HOME=/path/to/jdk-25 mvn test
JAVA_HOME=/path/to/jdk-21 ./gradlew test assembleDebugIn Android Studio, set the Gradle JDK to 21 under the Gradle settings. This is independent of the JDK selected in a terminal for Maven or GraalVM.
mvn package
java -jar rate-cli/target/rouble-rate-calculator-1.0.0.jarAll parameters are named and optional. The defaults are USD, yesterday's date in Moscow, and a three-month period:
java -jar rate-cli/target/rouble-rate-calculator-1.0.0.jar --currency EUR --end-date 2026-07-31
java -jar rate-cli/target/rouble-rate-calculator-1.0.0.jar --periods 3m,7d,1w,1y
java -jar rate-cli/target/rouble-rate-calculator-1.0.0.jar -c EUR -e 2026-07-31 -p 3m,7d,1w,1y
java -jar rate-cli/target/rouble-rate-calculator-1.0.0.jar -c EUR --today
java -jar rate-cli/target/rouble-rate-calculator-1.0.0.jar -c EUR --start-date 2026-01-01
java -jar rate-cli/target/rouble-rate-calculator-1.0.0.jar -s 2026-01-01 -e 2026-06-30
java -jar rate-cli/target/rouble-rate-calculator-1.0.0.jar -s 2026-01-01 -p 3mBoth --name value and --name=value forms are accepted. Period units are d
for days, w for weeks, m for months and y for years. Short forms are -c
for currency, -e for end date, and -p for periods. Argument parsing is
provided by Picocli.
Use -t or --today to print only the currently effective official rate. The
output includes the CBR effective date, which may differ around weekends and
holidays. Today's mode cannot be combined with date or period options.
Use -s or --start-date for an explicit interval:
- Start only: from the start date through yesterday in Moscow.
- Start and end: the inclusive interval between those dates.
- Start and one period: from the start date through start plus that period.
- Start, end, and period together: rejected as ambiguous.
Run with --help to see the command syntax.
CLI output and help are available in English and Russian. By default the app
uses the system language when it is Russian and falls back to English for other
locales. Select a language explicitly with -l en, --language ru, or the
corresponding --language=... form. Option names, currency codes, ISO input
dates, and period syntax remain language-independent.
Run the unit tests with:
mvn testInstall GraalVM Community Edition 25 with the native-image tool, make it the
active JDK, and run:
mvn -Pnative clean package
./rate-cli/target/rouble-rate-calculatorThe native executable accepts the same named options:
./rate-cli/target/rouble-rate-calculator --currency EUR --periods 3m,7d,1w,1yData source: the Bank of Russia XML_daily.asp and XML_dynamic.asp endpoints.
The Android application lives in android-app and consumes the same rate-core
and rate-cbr sources through the Gradle multi-project build. Its Compose screen
accepts a three-letter currency, a start date, and an optional end date, then
loads the interval average from CBR using OkHttp. When the end date is omitted,
the interval runs through yesterday. The screen can also show the currently
effective official rate. Dates use YYYY-MM-DD. The defaults are USD and three
months through yesterday. Normalized rates and downloaded
coverage are persisted in a Room database; historical cache coverage has the
same semantics as the CLI's SQLite cache. Date fields support both ISO text
entry and a calendar picker. The last successful inputs and result persist
between launches, allowing the app to reopen directly on the previous result
and refresh it with one tap. Android saved state also preserves in-progress UI
state across activity or process recreation.
The Android UI follows the device language and includes complete English and
Russian resources, localized result dates and numbers, and pluralized rate
counts.
Open the repository root in Android Studio, or build from a terminal with:
./gradlew test assembleDebugWith an emulator or device connected, run the Room and Compose instrumentation tests with:
./gradlew connectedDebugAndroidTestThe GitHub Actions workflow runs the CLI tests with JDK 25, builds and smoke-tests the GraalVM native executable in English and Russian, and runs the Android unit tests plus debug and optimized release builds with JDK 21. Successful runs publish the Linux native executable, debug APK, and unsigned release AAB as workflow artifacts. Device and emulator instrumentation tests remain part of the local verification command shown above.
The debug APK is written to
android-app/build/outputs/apk/debug/android-app-debug.apk. The app compiles
and targets Android 16/API 36, uses Build Tools 36.0.0 and Java 21, and retains
Android 8/API 26 as its minimum supported version.
Build an optimized, resource-shrunk Android App Bundle with JDK 21:
./gradlew :android-app:bundleReleaseWithout signing variables this produces an unsigned bundle for verification at
android-app/build/outputs/bundle/release/android-app-release.aab. CI builds
and publishes this unsigned bundle as an artifact.
For a distributable bundle, first create and securely back up an upload keystore using Android Studio's Build > Generate Signed Bundle/APK flow. Then provide all four signing values as environment variables:
export ROUBLE_RATE_KEYSTORE=/secure/path/rouble-rate-upload.jks
export ROUBLE_RATE_STORE_PASSWORD='...'
export ROUBLE_RATE_KEY_ALIAS='rouble-rate-upload'
export ROUBLE_RATE_KEY_PASSWORD='...'
./gradlew :android-app:bundleReleaseThe build fails when only some signing variables are present, preventing an
accidentally misconfigured release. Keystores and keystore.properties are
ignored by Git and must never be committed. For Google Play distribution, use
this as the upload key and enroll the application in Play App Signing.
rate-coreis a standalone Java 21, JDK-only library containing the data model, calculations, use cases, and theExchangeRateSourceandExchangeRateStorecontracts. It is intended to be shared with Android.rate-cbris a Java 21 library containing the shared CBR XML parser and its raw transport interface.rate-clicontains Picocli presentation, Java HTTP, the SQLite store, and GraalVM native-image configuration.android-appcontains Kotlin/Compose UI, OkHttp transport, and Room storage.