Compare commits

..

5 Commits

Author SHA1 Message Date
scoobybejesus f117b155cf Improved error handling and verbosity regarding env vars. 2020-10-13 00:27:32 -04:00
scoobybejesus 059e2b2a90 Updated panic/expect message 2020-10-13 00:25:40 -04:00
scoobybejesus 78fa173ceb Updated struct documentation and println's 2020-10-13 00:24:05 -04:00
scoobybejesus 396a084fcd Updated documentation 2020-10-13 00:22:21 -04:00
scoobybejesus 51b021b298 Moved .env.example to examples/ dir 2020-10-13 00:20:27 -04:00
10 changed files with 129 additions and 96 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "cryptools"
version = "0.9.0"
version = "0.9.1"
authors = ["scoobybejesus <scoobybejesus@users.noreply.github.com>"]
edition = "2018"
description = "Command-line utility for processing cryptocurrency transactions into 'lots' and 'movements'."

View File

@ -61,11 +61,11 @@ The rules for successfully preparing and maintaining the input file can generall
1. The first account must be given number `1`, and each additional account must count up sequentially.
2. Margin quote account `ticker`s must be followed by an underscore and the base account ticker (i.e., `BTC_xmr`).
3. `Proceeds` is the value of the transaction, whether spent, received, or exchanged.
3. `Proceeds` is the value of the transaction (measured in the home currency), whether spent, received, or exchanged.
It is **required** in order to properly calculate income/expense/gain/loss.
4. `Proceeds` must have a period as a decimal separator (`1,000.00` not `1.000,00`) and must not contain the ticker or symbol (USD or $).
5. Only home currency accounts can have negative balances. Crypto accounts may not go negative at any time.
(Exception: crypto margin accounts may go negative, of course.)
(Exception: crypto margin accounts may go negative.)
As you can see, most of the rules can generally be ignored.
In fact, the only tricky field is the `proceeds` column, but even that becomes second nature soon enough.
@ -82,18 +82,18 @@ In order to be successfully imported, the CSV input file **must** be in a prescr
| | | |Bank |Exchange|Wallet |Exchange|Simplewallet|
| | | | USD | BTC | BTC | XMR | XMR |
| | | | non | non | non | non | non |
|2/1/16 |0 |Bought | -220| 0.25| | | |
|3/1/16 |250 |Traded | | -0.25| | 180| |
|4/1/16 |0 |Transferred | | | | -90| 90|
|5/1/16 |0 |Transferred | | | | 90| -90|
|5/2/16 |160 |Traded | | 0.3| | -90| |
|6/1/16 |0 |Transferred | | -0.3| 0.3| | |
|7/1/16 |200 |Traded | | 0.7| | -90| |
|8/1/16 |0 |Transferred | | 0.3| -0.3| | |
|9/1/16 |400 |Traded | | -0.5| | 200| |
|10/1/16|900 |Traded | | 1| | -200| |
|11/1/16|0 |Transferred | | -1.5| 1.5| | |
|12/1/16|2000 |Traded* | | | -1.5| | 400|
|2-1-16 |0 |Bought | -220| 0.25| | | |
|3-1-16 |250 |Traded | | -0.25| | 180| |
|4-1-16 |0 |Transferred | | | | -90| 90|
|5-1-16 |0 |Transferred | | | | 90| -90|
|5-2-16 |160 |Traded | | 0.3| | -90| |
|6-1-16 |0 |Transferred | | -0.3| 0.3| | |
|7-1-16 |200 |Traded | | 0.7| | -90| |
|8-1-16 |0 |Transferred | | 0.3| -0.3| | |
|9-1-16 |400 |Traded | | -0.5| | 200| |
|10-1-16|900 |Traded | | 1| | -200| |
|11-1-16|0 |Transferred | | -1.5| 1.5| | |
|12-1-16|2000 |Traded* | | | -1.5| | 400|
---
@ -187,9 +187,9 @@ Until "spot" funds are spent to pay off the margin loans, it's simply an [unreco
##### Columns
* **txDate**: As a default, this parser expects a format of `MM-dd-YY` or `MM-dd-YYYY`.
The ISO 8601 date format (`YYYY-MM-dd` or `YY-MM-dd` both work) may be indicated by passing the `-i` flag.
The ISO 8601 date format (`YYYY-MM-dd` or `YY-MM-dd` both work) may be indicated by setting the environment variable `ISO_DATE` to `1` or `true`.
The hyphen, slash, or period delimiters (`-`, `/`, or `.`) may be indicated
by passing the `-d` option followed by `h`, `s`, or `p`, respectively (hyphen, `-`, is default).
by setting `DATE_SEPARATOR` to `h`, `s`, or `p`, respectively (hyphen, `-`, is default).
* **proceeds**: This is can be any **positive** number that will parse into a floating point 32-bit number,
as long as the **decimal separator** is a **period**.
@ -201,7 +201,7 @@ but be sure not to include the ticker or symbol of the currency
* **memo**: This can be a string of characters of any length, though fewer than 20-30 characters is advised.
Currently, **commas** in the memo field are **not** supported.
* *value*: This is similar to **proceeds**, in that the **decimal separator** must be a **period**,
* *quantity*: This is similar to **proceeds**, in that the **decimal separator** must be a **period**,
and you *cannot* include the ticker or symbol of the currency in that field.
It is different from **proceeds** in that this will be parsed into a 128-bit precision decimal floating point number,
and a negative value can be indicated via a preceding `-`.
@ -226,5 +226,5 @@ Anything aside from those six choices will fail to parse.
* *Transactions*: After the four header rows describing the accounts, the transaction rows follow.
Each row is a separate transaction.
For each transaction, input the **date**, **proceeds**, **memo**, and **values** by which the account balances change.
As mentioned elsewhere, a minimum of one and a maximum of two Accounts can be associated with a single transaction.
For each transaction, input the **date**, **proceeds**, **memo**, and **quantity** by which the account balances change.
As mentioned elsewhere, a minimum of one and a maximum of two **Accounts** can be associated with a single transaction.

View File

@ -4,8 +4,8 @@
###### (The package produces a binary and accompanying library)
This is a command-line tool that calculates income, expenses, realized gains, and realized losses
(and holding period) from cryptocurrency activity and denominates the results in the user's home currency.
This is a command-line tool that calculates income, expenses, realized gains, realized losses,
and holding period from cryptocurrency activity and denominates the results in the user's home currency.
The default home currency is USD, but any currency can be substituted.
This tool is probably most useful for filling out a tax return or making tax planning decisions.
@ -29,7 +29,7 @@ containing the user's entire cryptocurrency transaction history, the software wi
* Two methods each of LIFO or FIFO (compatible w/ the concept of "specific identification")
* Ability to perform like-kind exchange treatment
* Ability to perform like-kind exchange treatment through a particular date
* Compatible with any (single) home currency
@ -63,16 +63,27 @@ This will build `./target/debug/cryptools` (or `./target/release/cryptools` for
## Usage
Run `./target/debug/cryptools` with no arguments (or with `--help`, or `-h`) to see usage.
Alternatively, run `cargo run`, in which case command-line arguments for `cryptools` may be entered following `--`, e.g., `cargo run -- -h`.
Alternatively, run `cargo run`, in which case command-line options for `cryptools` may be entered following `--`, e.g., `cargo run -- -h`.
Running with no options/arguments will lead the user through a wizard.
To skip the wizard, there are three requirements:
* The [CSV input file](https://github.com/scoobybejesus/cryptools/blob/master/InputFile_CSV.md) is a required command line argument.
* The `-a` flag must be passed.
* The configuration settings you require are the same as default, or you set the appropriate environment variables, or you have a `.env` file.
Running with no arguments will lead the user through a wizard; or all required arguments can be passed as command-line flags/options/args.
See `/examples/` directory for further guidance,
or jump directly to the [examples.md](https://github.com/scoobybejesus/cryptools/blob/master/examples/examples.md) file.
###### Note: The import of your [CSV input file](https://github.com/scoobybejesus/cryptools/blob/master/InputFile_CSV.md) may fail or behave undesirably with the default configuration settings.
See [.env.example](https://github.com/scoobybejesus/cryptools/blob/master/examples/.env.example) for those defaults.
If you wish to skip the wizard but require changes to default settings, copy `.env.example` to `.env` and make your changes.
The `.env` file must be placed in the directory from which `cryptools` is run or a parent directory.
Alternatively, the respective environment variables may be set manually.
## Development state
As of summer 2019, the code does not require additional features in order for it to serve the project's founder.
At the same time, there are plenty of bells and whistles, creature comforts, etc. that are desired and may be added.
As of fall 2020, the code does not require additional features in order for it to serve the project's founder.
At the same time, there are still bells and whistles, creature comforts, etc. that are desired and may be added.
Additionally, the code could use factoring or general re-working in several areas.
The software has been tested on Mac, Linux, and FreeBSD.
@ -101,7 +112,7 @@ My first real progress was with Python, but I still didn't manage to fully devel
Luckily, I managed to stumble across a mentor who helped me write 80% of an MVP in strongly-typed Swift.
We coded our way into a corner, but I had learned enough to take the code apart and put it back together correctly and complete it.
I really enjoyed Swift, but I wanted something even more performant (and cross-platform), and Rust seemed to fit the bill.
I rewrote the code in Rust (also with a bit of help from my mentor), and it has turned out to be a great choice.
I rewrote the code in Rust (also with a bit of help), and it has turned out to be a great choice.
## Legal

View File

@ -4,8 +4,8 @@ The sample input files and the resulting reports are in the `/examples/resources
(Note: new reports have been added since the write-up below was written.
Nevertheless, evaluating the reports should mostly be self-explanatory.
Pass a -p flag from the command line to see the full list of available
reports - and select from them - once the import has taken place.)
Pass a -p flag from the command line to be presented a full list of available
reports to select from once the import has taken place.)
## 1. Using the wizard
@ -16,14 +16,14 @@ reports - and select from them - once the import has taken place.)
##### We're going to pass in that file as a command-line argument (no flags are required).
Enter the following:
##### &nbsp;&nbsp;&nbsp;&nbsp;`cargo run -- ./examples/resources/faker1__sample_input.csv`
##### &nbsp;&nbsp;&nbsp;&nbsp;`cargo run -- examples/resources/faker1__sample_input.csv`
&nbsp;&nbsp;&nbsp;&nbsp; (Substitute a Windows-style file path, if necessary.)
Running the command above takes you through the wizard.
&nbsp;&nbsp;&nbsp;&nbsp; (Note: You can simply run **`cargo run`** instead,
in which case after answering yes to "Shall we proceed," you will have to enter the path of the input file.)
in which case after answering 'yes' to "Shall we proceed," you will have to enter the path of the input file.)
##### Type `<Enter>` to accept default responses to the first three prompts, which are:
@ -33,44 +33,51 @@ in which case after answering yes to "Shall we proceed," you will have to enter
##### The final question asks if and where you'd like to save the reports.
The default is the current directory, which is probably undesirable.
The default is the current directory, which may be undesirable.
Type `c` and `<Enter>` to change the directory.
Then tab-complete your way through `/Users/<your-username>/Documents`*, for example, and then `<Enter>`.
Then tab-complete your way through `/Users/<your-username>/Documents`, for example, and then `<Enter>`.
&nbsp;&nbsp;&nbsp;&nbsp;\* This would be different for Windows, of course.
##### Now the program has ended, and you should have reports in the directory you provided.
The reports should generally match those in the `examples/resources` directory.
(Additional reports are created too, but you can generally match the existing reports by title.)
The reports should generally match those in the `examples/resources` directory,
but additional (newer) reports will be generated too.
## 2. Skipping the wizard
Let's run the program again.
This time around, we'll pass command-line parameters to skip the wizard.
Enter the following:
##### Again, preview the input file `faker2__sample_input.csv`.
##### &nbsp;&nbsp;&nbsp;&nbsp;`cargo run -- -a examples/resources/faker1__sample_input.csv`
You'll see it's similar to the README example, except that there is a wider variety of transactions,
&nbsp;&nbsp;&nbsp;&nbsp; (Substitute a Windows-style file path, if necessary.)
This will have quickly and automatically exported all the reports into the current directory.
Go delete them before you forget (or feel free to take a look at them first).
## 3. Using an input file with a slash (`/`) as the date separator
##### Preview the input file `faker2__sample_input.csv`.
You'll see it's similar to the first example, though with a wider variety of transactions,
plus the memos are more descriptive.
More importantly, the date column uses a different format `/` instead of `-`.
##### Run **`cargo run -- --help`** to see descriptions for the parameters we can use, or just enter:
Run:
##### &nbsp;&nbsp;&nbsp;&nbsp;`cargo run -- -ds -a -o ~/Documents ./examples/resources/faker2__sample_input.csv`
##### &nbsp;&nbsp;&nbsp;&nbsp;`DATE_SEPARATOR=s cargo run -- -a -o ~/Documents ./examples/resources/faker2__sample_input.csv`
&nbsp;&nbsp;&nbsp;&nbsp;\* Substitute `~/Documents` with your desired output directory.
Substitute a Windows-style file path, if necessary.
##### Again, the program runs, and you should have reports in the location you provided.
It worked correctly, right?
Note that we set an enviroment variable so the program knew how to parse the date column correctly.
An easier way to do this would be to create a `.env` file and set it there.
This is described in the README (though there isn't much to it).
###### &nbsp;&nbsp;Try once more. This time, after `-a`, type `-p`, to be presented a "print menu" for choosing individual reports.
We were able to bypass the wizard because:
1. The required parameters were passed in.
2. Default values were used for parameters not passed in.
3. The `-a` flag was set which accepts all parameters without asking twice (i.e., skips the wizard).
## To recap:
The only "required" parameter is the input file.
All other parameters have default values.
@ -78,8 +85,8 @@ The default values may not be desirable for your use case, however.
For example, you may want FIFO instead of LIFO,
or you may set your home currency to EUR instead of USD.
Or maybe you may want to apply like-kind exchange treatment through a particular date.
These parameters can all be set via command line options.
See the `--help` screen for all the options.
These parameters can all be set via environment variables (or in a `.env` file).
See the `--help` screen for command line options and .env.example for information on environment variables.
As mentioned above, pass the -p flag to be presented with a list of available reports.

View File

@ -2,15 +2,15 @@ txDate,proceeds,memo,1,2,3,4,5
,,,Bank,Exchange,Wallet,Exchange,Simplewallet
,,,USD,BTC,BTC,XMR,XMR
,,,non,non,non,non,non
2/1/16,0,FIRST,-220,0.25,,,
3/1/16,250,SECOND,,-0.25,,180,
4/1/16,0,THIRD,,,,-90,90
5/1/16,0,FOURTH,,,,90,-90
5/2/16,160,FIFTH,,0.3,,-90,
6/1/16,0,SIXTH,,-0.3,0.3,,
7/1/16,200,SEVENTH,,0.7,,-90,
8/1/16,0,EIGHTH,,0.3,-0.3,,
9/1/16,400,NINTH,,-0.5,,200,
10/1/16,900,TENTH,,1,,-200,
11/1/16,0,ELEVENTH,,-1.5,1.5,,
12/1/16,2000,TWELFTH,,,-1.5,,400
2-1-16,0,FIRST,-220,0.25,,,
3-1-16,250,SECOND,,-0.25,,180,
4-1-16,0,THIRD,,,,-90,90
5-1-16,0,FOURTH,,,,90,-90
5-2-16,160,FIFTH,,0.3,,-90,
6-1-16,0,SIXTH,,-0.3,0.3,,
7-1-16,200,SEVENTH,,0.7,,-90,
8-1-16,0,EIGHTH,,0.3,-0.3,,
9-1-16,400,NINTH,,-0.5,,200,
10-1-16,900,TENTH,,1,,-200,
11-1-16,0,ELEVENTH,,-1.5,1.5,,
12-1-16,2000,TWELFTH,,,-1.5,,400
1 txDate proceeds memo 1 2 3 4 5
2 Bank Exchange Wallet Exchange Simplewallet
3 USD BTC BTC XMR XMR
4 non non non non non
5 2/1/16 2-1-16 0 FIRST -220 0.25
6 3/1/16 3-1-16 250 SECOND -0.25 180
7 4/1/16 4-1-16 0 THIRD -90 90
8 5/1/16 5-1-16 0 FOURTH 90 -90
9 5/2/16 5-2-16 160 FIFTH 0.3 -90
10 6/1/16 6-1-16 0 SIXTH -0.3 0.3
11 7/1/16 7-1-16 200 SEVENTH 0.7 -90
12 8/1/16 8-1-16 0 EIGHTH 0.3 -0.3
13 9/1/16 9-1-16 400 NINTH -0.5 200
14 10/1/16 10-1-16 900 TENTH 1 -200
15 11/1/16 11-1-16 0 ELEVENTH -1.5 1.5
16 12/1/16 12-1-16 2000 TWELFTH -1.5 400

View File

@ -16,7 +16,7 @@ use crate::crptls_lib::create_lots_mvmts;
use crate::crptls_lib::costing_method::InventoryCostingMethod;
/// `ImportProcessParameters` are determined from command-line args and/or wizard input from the user.
/// `ImportProcessParameters` are determined from command-line args, environment variables, and/or wizard input from the user.
/// They are the settings that allow the software to carry out the importing-from-csv of
/// `Account`s and `Transaction`s, creation of `Lot`s and `Movement`s, addition of cost basis and proceeds
/// to `Movement`s, and application of like-kind treatment, in a specific and automated fashion.
@ -26,7 +26,7 @@ pub struct ImportProcessParameters {
pub home_currency: String,
pub costing_method: InventoryCostingMethod,
pub lk_treatment_enabled: bool,
/// NaiveDate either from "1-1-1" (default and not to be used) or the actual date chosen (or passed in via Cli option)
/// NaiveDate either from "1-1-1" (default and not to be used) or the actual date chosen (or passed in via env var)
pub lk_cutoff_date: NaiveDate,
pub lk_basis_date_preserved: bool,
pub should_export: bool,
@ -60,7 +60,8 @@ pub fn import_and_process_final(
&mut transactions_map,
)?;
println!("Successfully imported csv file.");
println!(" Successfully imported csv file.");
println!("Processing the data...");
transactions_map = create_lots_mvmts::create_lots_and_movements(
&settings,
@ -71,7 +72,7 @@ pub fn import_and_process_final(
// &mut lot_map,
)?;
println!(" Successfully created lots and movements.");
println!(" Created lots and movements.");
import_cost_proceeds_etc::add_cost_basis_to_movements(
&settings,
@ -81,7 +82,7 @@ pub fn import_and_process_final(
&transactions_map
)?;
println!(" Successfully added cost basis to movements.");
println!(" Added cost basis to movements.");
import_cost_proceeds_etc::add_proceeds_to_movements(
&raw_account_map,
@ -90,8 +91,7 @@ pub fn import_and_process_final(
&transactions_map
)?;
println!(" Successfully added proceeds to movements.");
println!(" Added proceeds to movements.");
if settings.lk_treatment_enabled {

View File

@ -26,7 +26,7 @@ pub(crate) fn import_from_csv(
transactions_map: &mut HashMap<u32, Transaction>,
) -> Result<(), Box<dyn Error>> {
let file = File::open(import_file_path)?; println!("CSV ledger file opened successfully.\n");
let file = File::open(import_file_path)?; println!("\nCSV ledger file opened successfully.\n");
let mut rdr = csv::ReaderBuilder::new()
.has_headers(true)
@ -240,7 +240,8 @@ fn import_transactions(
let tx_date = NaiveDate::parse_from_str(this_tx_date, &format_yy)
.unwrap_or_else(|_| NaiveDate::parse_from_str(this_tx_date, &format_yyyy)
.expect("
Failed to parse date in input file. Check date separator character (which is a hyphen unless modified via Cli option -d).\n")
Failed to parse date in input file. Check date the separator character, which is expected to be a hyphen \
unless otherwise set via environment variable or .env file. See `.env.example.`\n")
);
let transaction = Transaction {

View File

@ -95,22 +95,22 @@ fn main() -> Result<(), Box<dyn Error>> {
let args = Cli::from_args();
let cfg = setup::get_env()?;
println!(
"
Hello,
"\
Hello!
This software will import your csv file's ledger of cryptocurrency transactions.
It will then process it by creating 'lots' and posting 'movements' to those lots.
Along the way, it will keep track of income, expenses, gains, and losses.
This software will import your csv file's ledger of cryptocurrency transactions.
It will then process it by creating 'lots' and posting 'movements' to those lots.
Along the way, it will keep track of income, expenses, gains, and losses.
See .env.example for environment variables that may be set in a .env file in order to
change default program behavior.
See .env.example for environment variables that may be set in a .env file in order to
change default program behavior.
Note: The software is designed to import a full history. Gains and losses may be incorrect otherwise.
Note: The software is designed to import a full history. Gains and losses may be incorrect otherwise.
");
let cfg = setup::get_env()?;
let (input_file_path, settings) = setup::run_setup(args, cfg)?;
let (

View File

@ -1,7 +1,6 @@
// Copyright (c) 2017-2019, scoobybejesus
// Redistributions must include the license: https://github.com/scoobybejesus/cryptools/blob/master/LEGAL.txt
// use std::ffi::OsString;
use std::path::PathBuf;
use std::error::Error;
use std::process;
@ -20,13 +19,14 @@ use crate::wizard;
pub fn get_env() -> Result<super::Cfg, Box<dyn Error>> {
dotenv::dotenv().expect("Failed to read .env file");
match dotenv::dotenv() {
Ok(_path) => {println!("Setting environment variables from .env file.")},
Err(_e) => println!("Did not find .env file.")
}
let iso_date: bool = match env::var("ISO_DATE") {
Ok(val) => {
let var_lower = val.to_lowercase();
let val_str = var_lower.as_str();
if val_str == "1" || val == "true" {
if val == "1" || val.to_lowercase() == "true" {
true
} else {
false
@ -36,23 +36,37 @@ pub fn get_env() -> Result<super::Cfg, Box<dyn Error>> {
};
let date_separator: String = match env::var("DATE_SEPARATOR") {
Ok(val) => val.to_lowercase(),
Err(_e) => "h".to_string(),
Ok(val) => {
println!(" Found DATE_SEPARATOR env var: {}", val);
val.to_lowercase()},
Err(_e) => {
println!(" Using default date separator (hyphen).");
"h".to_string()},
};
let home_currency = match env::var("HOME_CURRENCY") {
Ok(val) => val.to_uppercase(),
Err(_e) => "USD".to_string(),
Ok(val) => {
println!(" Found HOME_CURRENCY env var: {}", val);
val.to_uppercase()},
Err(_e) => {
println!(" Using default home currency (USD).");
"USD".to_string()},
};
let lk_cutoff_date = match env::var("LK_CUTOFF_DATE") {
Ok(val) => Some(val),
Ok(val) => {
println!(" Found LK_CUTOFF_DATE env var: {}", val);
Some(val)},
Err(_e) => None,
};
let inv_costing_method = match env::var("INV_COSTING_METHOD") {
Ok(val) => val,
Err(_e) => "1".to_string(),
Ok(val) => {
println!(" Found INV_COSTING_METHOD env var: {}", val);
val},
Err(_e) => {
println!(" Using default inventory costing method (LIFO by lot creation date).");
"1".to_string()},
};
let cfg = super::Cfg {
@ -106,7 +120,7 @@ pub (crate) fn run_setup(cmd_args: super::Cli, cfg: super::Cfg) -> Result<(PathB
let like_kind_cutoff_date = if like_kind_election {
NaiveDate::parse_from_str(&like_kind_cutoff_date_string, "%y-%m-%d")
.unwrap_or_else(|_| NaiveDate::parse_from_str(&like_kind_cutoff_date_string, "%Y-%m-%d")
.expect("Command line date (like-kind cutoff option) has an incorrect format. Program must abort."))
.expect("Environment variable for LK_CUTOFF_DATE has an incorrect format. Program must abort. See .env.example."))
} else { NaiveDate::parse_from_str(&"1-1-1", "%y-%m-%d").unwrap() };
let settings = ImportProcessParameters {