Git Product home page Git Product logo

eprbell / rp2 Goto Github PK

View Code? Open in Web Editor NEW
245.0 10.0 38.0 14.25 MB

Privacy-focused, free, open-source cryptocurrency tax calculator for multiple countries: it handles multiple coins/exchanges and computes long/short-term capital gains, cost bases, in/out lot relationships/fractioning, and account balances. It supports FIFO, LIFO, HIFO and it outputs in form 8949 format. It has a programmable plugin architecture

Home Page: https://pypi.org/project/rp2/

License: Apache License 2.0

Python 99.64% Makefile 0.36%
tax crypto bitcoin ethereum cryptocurrency 8949 privacy altcoin accounting capital-gains

rp2's Introduction

RP2 Logo

RP2 v1.5.1

Privacy-focused, free, powerful crypto tax calculator

Static Analysis / Main Branch Documentation Check / Main Branch Unix Unit Tests / Main Branch Windows Unit Tests / Main Branch CodeQL/Main Branch

Tweet

Table of Contents

Introduction

RP2 is a privacy-focused, free, non-commercial, open-source, community-driven cryptocurrency tax calculator for multiple countries (currently US, Japan and Spain are supported). Preparing crypto taxes can be a daunting and error-prone task, especially if multiple transactions, coins, exchanges and wallets are involved. This task could be delegated to a crypto tax preparation service, but many crypto users value their privacy and prefer not to send their transaction information to third parties unnecessarily. Additionally, many of these services cost money. RP2 solves all of these problems:

  • it manages the complexity related to coin flows and tax calculation and it generates reports that accountants can understand (in the format of form 8949, for the US case), even if they are not cryptocurrency experts;
  • it prioritizes user privacy by storing crypto transactions and tax results on the user's computer and not sending them anywhere else;
  • it's 100% free, open-source and non-commercial.

This means that with RP2 there are no transaction limits, no premium versions, no payment requests, no personal data sent to a server (at risk of being hacked), no account creation, no unauditable source code.

Another unique advantage of RP2 is transparent computation: RP2 generates full computation details for every lot fraction, so that it's possible to:

  • verify step-by-step how RP2 reaches the final result;
  • track down every lot fraction and its accounting details, in case of an audit.

RP2 currently supports the following accounting methods:

  • US: FIFO, LIFO and HIFO. Note that these methods use universal application (not per-wallet application), as explained here;
  • Spain: FIFO;
  • Japan: Total Average Method.

RP2 reads a configuration file and an input spreadsheet containing crypto transactions. These input files can be generated either manually or automatically using DaLI, a RP2 data loader and input generator (which is also privacy-focused, free, non-commercial, open-source and community-driven). After parsing the input, RP2 uses high-precision math to calculate long/short term capital gains, cost bases, balances, average price, in/out lot relationships/fractions, and finally it generates output files.

RP2 has a programmable plugin architecture for output generators, accounting methods and countries. The builtin output generator plugins are in part generic and in part country-specific, but RP2's architecture makes it possible to contribute additional generators for different countries or for different country-based cases. The builtin output generator plugins are:

  • tax_report_us: generates a US-specific tax report meant to be read by tax preparers (in the format of form 8949);
  • tax_report_jp: generates a Japan-specific tax report meant to be read by tax preparers;
  • rp2_full_report: generates a comprehensive report (valid for any country), with complete transaction history, lot relationships/fractions and computation details;
  • open_positions: geterates a report (valid for any country) on assets with non-zero crypto balance: unrealized gains / losses, portfolio weighting, and more.

RP2 has extensive unit test coverage to reduce the risk of regression.

IMPORTANT DISCLAIMERS:

  • RP2 offers no guarantee of correctness (read the license): always verify results with the help of a tax professional.
  • The author of RP2 is not a tax professional, but has used RP2 personally for a few years.

How RP2 Operates

RP2 has been designed to have expressive primitives that can be used as building blocks for most tax scenarios: complex tax events can be described with patterns, built on top of these primitives (see the FAQ list for examples).

For the US case, RP2 treats virtual currency as property for tax purposes, as per IRS Virtual Currency Guidance.

RP2 supports various accounting methods (e.g. FIFO, LIFO, HIFO): however, in and out lots typically don't have matching amounts, so RP2 fractions them, maps in/out lot fractions and computes the resulting cost basis and capital gains for each lot fraction.

RP2 groups lot fractions into the following taxable event categories, each of which has a specific tax treatment:

  • AIRDROP: gains from airdrops;
  • DONATE: donations to charitable organizations;
  • FEE: fee-only transaction, used in some DeFi scenarios (e.g. gas fee for running certain smart contracts);
  • GIFT: gifts to parties who are not charitable organizations (not tax-deductible).
  • HARDFORK: gains from hard forks;
  • INCOME: gains from miscellaneous income (e.g. Coinbase Earn);
  • INTEREST: gains from interest;
  • MINING: gains from mining;
  • MOVE: the fee for moving currency between two accounts controlled by the same owner;
  • SELL: specifically, sale and conversion of a cryptocurrency to another. RP2 splits them in two subcategories:
    • long-term capital gains (if supported by the country plugin: e.g. in the US it's 1 year or more), or
    • short-term capital gains otherwise;
  • STAKING: gains from staking;
  • WAGES: income from crypto wages.

For each of these categories RP2 generates an output spreadsheet with transaction details and computed gains/losses (see Input and Output Files for more details). Users can give this output to their tax preparer with the rest of their tax documentation (see also FAQ on which tax forms to file). Note that buying cryptocurrency using fiat currency is not a taxable event.

NOTE ON NFTs: Read the FAQ on NFTs to learn about how RP2 treats NFTs.

License

RP2 is released under the terms of Apache License Version 2.0. For more information see LICENSE or http://www.apache.org/licenses/LICENSE-2.0.

Download

The latest version of RP2 can be downloaded at: https://pypi.org/project/rp2/

Installation

RP2 has been tested on Ubuntu Linux, macOS and Windows 10 but it should work on all systems that have Python version 3.7.0 or greater.

Installation on Ubuntu Linux

Open a terminal window and enter the following commands:

sudo apt-get update
sudo apt-get install python3 python3-pip

Then install RP2:

pip install rp2

Installation on macOS

First make sure Homebrew is installed, then open a terminal window and enter the following commands:

brew update
brew install python3

Then install RP2:

pip install rp2

Installation on Windows 10

First make sure Python 3.7 or greater is installed (in the Python installer window be sure to click on "Add Python to PATH"), then open a PowerShell window and enter the following:

pip install rp2

Installation on Other Unix-like Systems

  • install python 3.7 or greater
  • install pip3

Then install RP2:

pip install rp2

Running

RP2 requires two files as input:

  • an ODS-format spreadsheet, containing crypto transactions (ODS-format files can be opened and edited with LibreOffice and many other spreadsheet applications);
  • a config file, describing the format of the spreadsheet file: what value each column corresponds to (e.g. timestamp, amount, exchange, fee, etc.) and which cryptocurrencies and exchanges to expect.

The two input files can either:

  • be generated automatically using DaLI, the data loader and input generator for RP2, or
  • be prepared manually by the user.

The formats of these files are described in detail in the Input Files section of the documentation.

Examples of an input spreadsheet and its respective config file:

After reading the input files, RP2 computes taxes and generates output files, which contain information on long/short capital gains, cost bases, balances, average price, in/out lot relationships and fractions, etc. They are described in detail in the Output Files section of the documentation.

To try RP2 with example files, download crypto_example.ods and crypto_example.ini. Let's call <download_directory> the location of the downloaded files.

The RP2 executable is country-dependent: rp2_<country_code>, where country code is a ISO 3166-1 alpha-2, 2-letter identifier (e.g. rp2_us, rp2_jp, etc).

To generate US tax output for the example files open a terminal window (or PowerShell if on Windows) and enter the following commands:

cd <download_directory>
rp2_us -m fifo -o output -p crypto_example_ crypto_example.ini crypto_example.ods

Results are generated in the output directory and logs are stored in the log directory.

The -m option is particularly important, because is selects the accounting method: rp2_us supports FIFO, LIFO and HIFO (if -m is not specified it defaults to FIFO).

To print full command usage information for the rp2_us command:

rp2_us --help

Input and Output Files

Read the input files and output files documentation.

RP2 Ecosystem

This is a call for coders: come and help us expand RP2's functionality!

RP2 is the first component of what could be a powerful, community-driven suite of open-source, crypto tax software. It is intended as the core of a larger project ecosystem, maintained by the community. These projects can extend RP2's capability, usefulness and ease of use in new ways. For example:

  • DaLI data loader plugins: add support for more exchanges and wallets (via REST API and/or CSV files). Dali, the RP2 data loader, uses them to generate an input ODS file and a config file that can be fed directly to RP2;
  • RP2 plugins: RP2 can be expanded via its programmable plugin architecture, which enables support for new output generators, countries and accounting methods;
  • RP2 GUI: make RP2 more user-friendly and accessible to people who are not familiar with the CLI;
  • RP2 high-level interface: RP2 captures complex tax events using a few powerful, low-level primitives, aggregated in patterns. A higher level tool, might abstract out these patterns and present them to the user in a friendlier way (for example it may capture a complex concept like DeFi bridging as a primitive, rather than a pattern);
  • more...

Important note: any RP2 ecosystem project must make user privacy its first priority.

If you'd like to start an ecosystem project, please open an issue to let the RP2 community know.

List of Ecosystem Projects

Here's the current list of projects in the RP2 ecosystem:

Reporting Bugs

Read the Contributing document.

Contributing

Read the Contributing document.

Developer Documentation

Read the developer documentation.

Frequently Asked Questions

Read the user FAQ list and the developer FAQ list.

Change Log

Read the Change Log document.

rp2's People

Contributors

antonok-edm avatar debben avatar eprbell avatar iloveitaly avatar jayr0d avatar kianmeng avatar larision avatar macanudo527 avatar mdavid217 avatar ninideol avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rp2's Issues

Where to put buying crypto with cash or using swapping site?

First of all, great project! This seems to be exactly what I need. I would like to contribute as well.

I have a few questions/suggestions:

  1. How to account for buying crypto with cash from a random person on the street?
  2. How to account for using swapping sites like simpleswap.io / changenow.io
  3. Correct me if I am wrong, but where do we get crypto fee value? I think inside the exhange this can easily be done, but outside the exchange, where do we get the transaction fee value? Sorry this must be a stupid question
  4. Where to get spot prices the best? I guess inside the exchange this can be done easily, but outside the exchange, we just have to rely on coinmarketcap.com?
  5. What date-time is best representing a transaction? Is it the date time transaction is initiated or when the date time crypto is received/sent? Because an exchange might say the transaction run at a certain time, but then we see on the chain it is a different time?
  6. I'm thinking maybe we could provide a column metadata that keeps track of transaction hash/addresses, maybe will be useful for audit?

Sorry for the long questions. Awesome project again!

How to enter the swap fee without double counting the deduction?

I have the following 3-step scenario:

  1. Buy Coin1
  2. Swap Coin1 for Coin2, paying a fee in Coin1 currency
  3. Sell Coin2 for USD.

To enter this scenario in rp2, I split the swap into two separate transactions at the exact same timestamp with each transaction truthfully reporting the fee for the swap. The transactions are OUT(Coin1) and IN(Coin2). When I run rp2 on this scenario, it correctly reports a final position size of zero for both Coin1 and Coin2. However, this approach is double counting the fee -- it reduces the proceeds for the sale of Coin1 and it also increases the cost basis of Coin2. I think this is not allowed.

Here is what I want to accomplish:

  1. Apply the swap fee towards the cost basis of the purchased asset (Coin2).
  2. Do not double-count the fee deduction, meaning the fee should not be used to reduce the proceeds from the sold asset (Coin1).
  3. Accurately reduce the position size of the sold asset (Coin1) by the total transaction cost including the fee.

What is the right way to enter these transactions in the input spreadsheet?

Add Support for Japan

I recently added support for per-country accounting methods: this allows country plugins to define which accounting methods are accepted in the country.

After we add average method accounting (or whatever other method is supported in Japan) we need to update the following methods in the JP plugin:

  • get_default_accounting_method();
  • get_accounting_methods().

CC: @macanudo527

Add Support for Sweden

RP2 and DaLI have a programmable plugin architecture for countries, accounting methods, report generators and more. Adding support for a new country is a high-impact activity because it allows many new potential users to get access to RP2 and DaLI.

Here's how to add support for a new country:

  1. add a new country plugin to RP2;
  2. if the country requires accounting methods that aren't already supported in RP2, add the necessary accounting method plugins;
  3. ensure any newly added accounting methods are reflected in the country plugin's get_default_accounting_method() and get_accounting_methods() methods;
  4. optionally, new country-specific report generators can be added. The built-in report generators (i.e. rp2_full_report and open_positions) are automatically supported;
  5. only for non-English speaking countries: translations for the built-in report generators and for any new report generator can be added;
  6. ensure any newly added report generators are reflected in the country plugin's get_report_generators() method;
  7. add the same country plugin to DaLI. The implementation on the DaLI side is trivial: it's just an entry point instantiating the RP2 country plugin and passing it to the main function;
  8. update the documentation of RP2 and DaLI to reflect the new country, accounting methods (if any) and report generators (if any).

If you notice the country-specific infrastructure is missing a feature that is required to express taxes for the new country, open an issue to let the RP2 community know.

Add Support for Spain

RP2 and DaLI have a programmable plugin architecture for countries, accounting methods, report generators and more. Adding support for a new country is a high-impact activity because it allows many new potential users to get access to RP2 and DaLI.

Here's how to add support for a new country:

  1. add a new country plugin to RP2;
  2. if the country requires accounting methods that aren't already supported in RP2, add the necessary accounting method plugins;
  3. ensure any newly added accounting methods are reflected in the country plugin's get_default_accounting_method() and get_accounting_methods() methods;
  4. optionally, new country-specific report generators can be added. The built-in report generators (i.e. rp2_full_report and open_positions) are automatically supported;
  5. only for non-English speaking countries: translations for the built-in report generators and for any new report generator can be added;
  6. ensure any newly added report generators are reflected in the country plugin's get_report_generators() method;
  7. add the same country plugin to DaLI. The implementation on the DaLI side is trivial: it's just an entry point instantiating the RP2 country plugin and passing it to the main function;
  8. update the documentation of RP2 and DaLI to reflect the new country, accounting methods (if any) and report generators (if any).

If you notice the country-specific infrastructure is missing a feature that is required to express taxes for the new country, open an issue to let the RP2 community know.

Data element provenance

I'm manually entering transaction data from multiple sources. It's not unusual for a single transaction to have data provided by multiple exchanges, quote lookup sources, or blockchain viewers. Sometimes after being away from the data for a few days, I look at the spreadsheet and find myself wondering what was the source of a particular data element.

I've added new columns in the spreadsheet for this purpose. For example:
Timestamp, Timestamp Source, Spot Price, Spot Price Source, retc.

Maybe there is a better way to track this information. Any suggestions on a good way to keep track of the sources of the data elements?

Document the release steps

Hi @eprbell, may I ask, could you document the step-by-step process for releasing a new distribution? For now, this is something entirely under your control, but maybe some time in the future, other community members may need to do it. I can see you are using bumpversion, there is a change log, tags in the repo, releases on GitHub, the distribution on PyPi, and maybe other artifacts I'm missing. The exact sequence of producing the artifacts is unclear.

RP2ValueError("Total in-transaction value < total taxable entries") from None

RP2

Version: RP2 0.9.26 (https://pypi.org/project/rp2/)
Command: rp2_us -m fifo -o output/ -p rp2_ output/crypto_data.config output/crypto_data.ods

Ran DaLI with Coinbase REST API for output ODS file and config file. Logs show:

2022-04-03 07:34:43,384/rp2/INFO: Country: us
2022-04-03 07:34:43,384/rp2/INFO: Accounting Method: fifo
2022-04-03 07:34:43,396/rp2/INFO: Configuration file: output/crypto_data.config
2022-04-03 07:34:43,396/rp2/INFO: Input file: output/crypto_data.ods
2022-04-03 07:34:43,513/rp2/INFO: Processing ALGO
2022-04-03 07:34:43,649/rp2/INFO: Processing AMP
2022-04-03 07:34:43,713/rp2/INFO: Processing ANKR
2022-04-03 07:34:43,778/rp2/INFO: Processing AUCTION
2022-04-03 07:34:43,843/rp2/INFO: Processing BOND
2022-04-03 07:34:43,908/rp2/INFO: Processing BTC
2022-04-03 07:34:43,975/rp2/INFO: Processing CGLD
2022-04-03 07:34:44,039/rp2/INFO: Processing CHZ
2022-04-03 07:34:44,103/rp2/INFO: Processing CLV
2022-04-03 07:34:44,168/rp2/INFO: Processing COMP
2022-04-03 07:34:44,233/rp2/INFO: Processing CTSI
2022-04-03 07:34:44,297/rp2/INFO: Processing DAI
2022-04-03 07:34:44,431/rp2/INFO: Processing ETH
2022-04-03 07:34:44,493/rp2/INFO: Processing ETH2
2022-04-03 07:34:44,556/rp2/INFO: Processing FET
2022-04-03 07:34:44,621/rp2/INFO: Processing FORTH
2022-04-03 07:34:44,685/rp2/INFO: Processing GRT
2022-04-03 07:34:44,749/rp2/INFO: Processing IOTX
2022-04-03 07:34:44,813/rp2/INFO: Processing LRC
2022-04-03 07:34:44,877/rp2/ERROR: Fatal exception occurred:
Traceback (most recent call last):
  File "/home/james/.local/lib/python3.10/site-packages/rp2/rp2_main.py", line 96, in _rp2_main_internal
    computed_data: ComputedData = compute_tax(configuration=configuration, accounting_method=accounting_method, input_data=input_data)
  File "/home/james/.local/lib/python3.10/site-packages/rp2/tax_engine.py", line 44, in compute_tax
    unfiltered_gain_loss_set: GainLossSet = _create_unfiltered_gain_and_loss_set(configuration, accounting_method, input_data, unfiltered_taxable_event_set)
  File "/home/james/.local/lib/python3.10/site-packages/rp2/tax_engine.py", line 155, in _create_unfiltered_gain_and_loss_set
    raise RP2ValueError("Total in-transaction value < total taxable entries") from None
rp2.rp2_error.RP2ValueError: Total in-transaction value < total taxable entries
2022-04-03 07:34:44,877/rp2/INFO: Log file: ./log/rp2_2022_04_03_07_34_43_362639.log
2022-04-03 07:34:44,877/rp2/INFO: Generated output directory: output/
2022-04-03 07:34:44,877/rp2/INFO: Done

Looking at LRC table:

IN ย  ย  ย  ย  ย  ย  ย  ย  ย  ย  ย  ย  ย 
Timestamp Asset Exchange Holder Transaction Type ย  Spot Price Crypto In Crypto Fee USD In No Fee USD In With Fee USD Fee Unique ID Notes
2021-11-04 00:49:31 +0000 LRC Coinbase (name) Income ย  $1.15 0.43489605 ย  $0.50 $0.50 $0.00 XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX Coinbase EARN; Received Loopring
2021-11-04 00:50:20 +0000 LRC Coinbase (name) Income ย  $1.15 0.43378302 ย  $0.50 $0.50 $0.00 XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX Coinbase EARN; Received Loopring
2021-11-04 00:50:50 +0000 LRC Coinbase (name) Income ย  $1.15 0.43449923 ย  $0.50 $0.50 $0.00 XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX Coinbase EARN; Received Loopring
TABLE END ย  ย  ย  ย  ย  ย  ย  ย  ย  ย  ย  ย  ย 
ย  ย  ย  ย  ย  ย  ย  ย  ย  ย  ย  ย  ย  ย 
OUT ย  ย  ย  ย  ย  ย  ย  ย  ย  ย  ย  ย  ย 
Timestamp Asset Exchange Holder Transaction Type ย  Spot Price Crypto Out No Fee Crypto Fee Crypto Out With Fee USD Out No Fee USD Fee Unique ID Notes
2021-11-04 00:51:21 +0000 LRC Coinbase (name) Sell ย  $1.15 1.30317832 0.00000000 1.30317832 $1.50 $0.00 XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX Sell side of conversion of 1.30317832 LRC
TABLE END ย  ย  ย  ย  ย  ย  ย  ย  ย  ย  ย  ย  ย 

From what I see, it's $1.50 in and $1.50 out. I'm not sure if there's something else I should be looking at.

JSONDecodeError: Unexpected UTF-8 BOM (decode using utf-8-sig)

Hi. I'm not sure what's wrong with my JSON file, but I keep getting this error when trying to run the program. (I'm new to python stuff, so it's probably just me making a dumb error. I'm not even sure if this is the correct place to ask you about it.)

INFO: Country: us
INFO: Accounting Method: fifo
ERROR: Fatal exception occurred:
Traceback (most recent call last):
File "c:\dev\2021-taxes\venv\lib\site-packages\rp2\rp2_main.py", line 73,
2021_tax.txt
in rp2_main_internal
configuration: Configuration = Configuration(
File "c:\dev\2021-taxes\venv\lib\site-packages\rp2\configuration.py", line 98, in init
json_configuration: Any = json.load(configuration_file)
File "C:\Program Files\Python38\lib\json_init
.py", line 293, in load
return loads(fp.read(),
File "C:\Program Files\Python38\lib\json_init_.py", line 337, in loads
raise JSONDecodeError("Unexpected UTF-8 BOM (decode using utf-8-sig)",
json.decoder.JSONDecodeError: Unexpected UTF-8 BOM (decode using utf-8-sig): line 1 column 1 (char 0)
INFO: Log file: ./log/rp2_2022_04_15_12_49_50_163788.log
INFO: Generated output directory: output
INFO: Done

My JSON file looks like this:

{
"in_header": {
"timestamp": 1,
"exchange": 3,
"transaction_type": 5,
"crypto_in": 6,
"asset": 7,
"spot_price": 8,
โ€œcrypto_feeโ€: 9,
"fiat_fee": 10,
"unique_id": 11,
"notes": 12,
"holder": 13
},

"out_header": {
    "timestamp": 1,
    "exchange": 3,
    "transaction_type": 5,
    "crypto_out_no_fee": 6,
    "asset": 7,
    "spot_price": 8,
    โ€œcrypto_feeโ€: 9,
    "fiat_fee": 10,
    "unique_id": 11,
    "notes": 12,
    "holder": 13
},

"intra_header": {
    "timestamp": 1,
    "from_exchange": 3,
    "to_exchange": 4,
    "crypto_sent": 5,
    "crypto_received": 6,
    "asset": 7,
    "spot_price": 8,
    "unique_id": 11,
    "notes": 12,
    "from_holder": 13,
    "to_holder": 14
},

"assets": [
    "ADA",
    "ALGO",
    "AVAX",
    "CRO",
    "stETH",
    "ETH",
    "SHIB",
    "xSHIB"
],
"exchanges": [
    "Bank",
    "Binance.US",
    "Coinbase",
    "Coinbase Pro",
    "Trust Wallet (BSC Smart Chain)",
    "Trust Wallet (BNB BEP-2)",
    "Trust Wallet (ETH & ERC-20)",
    "Trust Wallet (ALGO)"
],
"holders": [
    "Me"
]

}

What am I doing wrong? Any help would be greatly appreciated. Thank you.

Adding HIFO

I was wondering the possibility of adding HIFO to the existing FIFO and LIFO cost basis structures (https://www.investopedia.com/terms/h/hifo.asp). It is available to use in the USA and would potentially help save a lot on taxes such that you're always selling the highest priced lot.

Unfortunately, my knowledge of python is more in the scripting/engineering realm, so I'm not sure how much help I would be on the coding aspect...

Thanks!

Documentation: input_files.md crypto_out_no_fee crypto_out_with_fee

input_files.md
OUT section
The description for both of these data elements says "excluding fees". If I'm understanding it properly, crypto_out_with_fee is the total amount subtracted from the account including the fees.

crypto_out_no_fee: how much of the given cryptocurrency was sold or sent with the transaction (excluding fees).

crypto_out_with_fee (optional): how much of the given cryptocurrency was sold or sent with the transaction (excluding fees). If not provided, RP2 will compute this value automatically.

Deducting sell fees in proceeds

First off, this is an amazing project, and a big bravo to you for creating this as a DIY alternative. After wrangling the tool for a little bit, one thing I believe I noticed is that fees are not being deducted from proceeds. Source for fee deduction logic is this article.

As an example, consider some fake currency XYZ.

I buy 10 XYZ @ $10 with a $1 fee. Total cost of $101, making my cost basis for each XYZ $10.10, as the fee is distributed across all the assets.

I then sell all my XYZ @ $20 with a $2 fee, netting me $198. Each XYZ sold for $19.8, for a total capital gain of $9.7 per coin = $97.

However, when running this example case, rp2 returns that the proceed of the sale was $200, for a capital gain of $99. Apologies if I'm missing something!

As an aside, an option to mark sell fees as fiat instead of crypto would be useful, I think, as some exchanges charge USD off the top.

Again, thanks so much for putting so much time and energy into an open-source and free solution.
Screen Shot 2022-01-30 at 11 07 14 PM

Implement JPN country plugin

I read through the country plugin documentation and it seems like the main thing to define is long_term_capital_gain_period(). However, Japan has no long term capital gains benefits. Everything crypto is taxed as short term. To be more specific, it is taxed as regular income.

So, how should I define this?

Processing ALGO transactions with Coinbase Pro REST API. ValueError: Unknown format code 'f' for object of type 'str'

Hi, I successfully used DaLI in preparation for RP2. I'm getting this error when I run rp2.

rp2_us -m fifo -o output/ -p rp2_ output/test_crypto_data.config output/test_crypto_data.ods
INFO: Country: us
INFO: Accounting Method: fifo
INFO: Configuration file: output/test_crypto_data.config
INFO: Input file: output/test_crypto_data.ods
INFO: Processing ADA
INFO: Processing ALGO
ERROR: Fatal exception occurred:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/rp2/ods_parser.py", line 258, in _process_constructor_argument_pack
argument_pack[numeric_parameter] = RP2Decimal(f"{value:.11f}") if value is not None else None
ValueError: Unknown format code 'f' for object of type 'str'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/rp2/rp2_main.py", line 93, in _rp2_main_internal
input_data: InputData = parse_ods(configuration=configuration, asset=asset, input_file_handle=input_file_handle)
File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/rp2/ods_parser.py", line 128, in parse_ods
_create_and_process_transaction(
File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/rp2/ods_parser.py", line 168, in _create_and_process_transaction
transaction: AbstractTransaction = _create_transaction(configuration, current_table_type, internal_id, row_values)
File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/rp2/ods_parser.py", line 284, in _create_transaction
argument_pack = _process_constructor_argument_pack(configuration, argument_pack, internal_id, "IntraTransaction")
File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/rp2/ods_parser.py", line 260, in _process_constructor_argument_pack
raise RP2ValueError(f"Argument '{numeric_parameter}' has non-numeric value: {value}") from exc
rp2.rp2_error.RP2ValueError: Argument 'crypto_received' has non-numeric value: __unknown
INFO: Log file: ./log/rp2_2022_04_02_16_28_00_882266.log
INFO: Generated output directory: output/
INFO: Done

INstallation issue

Just tried installing rp2 but I get the following errors on the powershell page:
PS C:\WINDOWS\system32> pip install rp2
WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ProxyError('Cannot connect to proxy.', NewConnectionError('<pip._vendor.urllib3.connection.HTTPSConnection object at 0x00000191A1813DF0>: Failed to establish a new connection: [WinError 10061] No connection could be made because the target machine actively refused it'))': /simple/rp2/
WARNING: Retrying (Retry(total=3, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ProxyError('Cannot connect to proxy.', NewConnectionError('<pip._vendor.urllib3.connection.HTTPSConnection object at 0x00000191A1813BB0>: Failed to establish a new connection: [WinError 10061] No connection could be made because the target machine actively refused it'))': /simple/rp2/
WARNING: Retrying (Retry(total=2, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ProxyError('Cannot connect to proxy.', NewConnectionError('<pip._vendor.urllib3.connection.HTTPSConnection object at 0x00000191A1840400>: Failed to establish a new connection: [WinError 10061] No connection could be made because the target machine actively refused it'))': /simple/rp2/
WARNING: Retrying (Retry(total=1, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ProxyError('Cannot connect to proxy.', NewConnectionError('<pip._vendor.urllib3.connection.HTTPSConnection object at 0x00000191A1840580>: Failed to establish a new connection: [WinError 10061] No connection could be made because the target machine actively refused it'))': /simple/rp2/
WARNING: Retrying (Retry(total=0, connect=None, read=None, redirect=None, status=None)) after connection broken by 'ProxyError('Cannot connect to proxy.', NewConnectionError('<pip._vendor.urllib3.connection.HTTPSConnection object at 0x00000191A1840700>: Failed to establish a new connection: [WinError 10061] No connection could be made because the target machine actively refused it'))': /simple/rp2/
ERROR: Could not find a version that satisfies the requirement rp2 (from versions: none)
ERROR: No matching distribution found for rp2

THought maybe there was a networking issue due to me trying to install it on my work laptop and being on a work network. I detached the network cable and got on a normal network off of the work network and got the same errors. Any ideas?

Add Support for Italy

RP2 and DaLI have a programmable plugin architecture for countries, accounting methods, report generators and more. Adding support for a new country is a high-impact activity because it allows many new potential users to get access to RP2 and DaLI.

Here's how to add support for a new country:

  1. add a new country plugin to RP2;
  2. if the country requires accounting methods that aren't already supported in RP2, add the necessary accounting method plugins;
  3. ensure any newly added accounting methods are reflected in the country plugin's get_default_accounting_method() and get_accounting_methods() methods;
  4. optionally, new country-specific report generators can be added. The built-in report generators (i.e. rp2_full_report and open_positions) are automatically supported;
  5. only for non-English speaking countries: translations for the built-in report generators and for any new report generator can be added;
  6. ensure any newly added report generators are reflected in the country plugin's get_report_generators() method;
  7. add the same country plugin to DaLI. The implementation on the DaLI side is trivial: it's just an entry point instantiating the RP2 country plugin and passing it to the main function;
  8. update the documentation of RP2 and DaLI to reflect the new country, accounting methods (if any) and report generators (if any).

If you notice the country-specific infrastructure is missing a feature that is required to express taxes for the new country, open an issue to let the RP2 community know.

Some issues, some solutions, and general thoughts

So far this year I've spent about 250 hours researching everything crypto tax related and running through various software packages (both commercial and open-source), and it was a nightmare. However, I learned a lot that I would like to share, and I would be interested in supporting this project and helping it grow. Since this is a going to be a really long post, the moral of the story is that here are a bunch of comments and suggestions; let me know how I can help contribute. I'm not a python expert, but I do a lot of work with Excel VBA and can probably write pseudo code fairly well.

First off, thank you so much for this software, as it was the only thing that was able to process all of my data with enough functionality to get it done CORRECTLY and then provide the right output (US tax forms). I ended up having around 103,000 crypto disposals between 14 wallets, exchanges, and services, crossing 7 blockchains, so doing these calculations manually was completely out of the question. However, while this software worked, I had to use several workarounds that I think can and should be integrated into the code, which I'll describe in a bit. First, I have thoughts about implementation, since I think there are two distinct pieces of the puzzle that need to work in tandem.

As mentioned above, this software is focused on primitive IN/OUT transactions, which is totally fine and works well for the tax calculation side of things. The downside to doing it this way is that huge challenge of accurately getting data into this form so that the code can do its job. This challenge is partially because it's a semi-manual process at best, but also because it requires extensive knowledge of the crypto world AND comprehensive tax knowledge. Both of these are difficult because the information on them is still lacking. As such, I think the biggest thing that this project needs is a data pre-processor/converter that prepares the data CORRECTLY for the tax calculation. I see that DaLI has been created, but it still seems pretty sparse and under-documented. Iโ€™ll discuss my thoughts on DaLI at the end of this. The primitive code, as it stands, also needs some tweaking to be able to more intelligently handle complex transactions. Here are things that need to be addressed:

FEES

  1. The current code needs to be modified to handle DeFi situations, but most of this comes down to fees. Regarding "fee only" transactions, you guys have already come up with the two obvious solutions (100% Sell with $0 in proceeds vs. 0% Sell with 100% Fees). The problem is that the current software can't handle either of these. It doesn't allow you to put zero as the amount sold, nor does it allow you to put zero for the coin price. Was this done as a data integrity check only, or are these numbers (amount sold and coin price) used later in the denominator of an equation, which might lead to a "divide by zero" error? One solution could be to create a different type of transaction, such as "FEE" or "COST" (mentioned above), which allows either the sell amount or the coin price to be zero. This way the data can still be validated by checking that the right numbers exist in the right place, but also ensures that these numbers aren't used later in calculations that would cause an error. By the way, the current workaround I used here is listing the coin price of "fee only" transactions as 1e-10, which is messy, but it works.

  2. IN, OUT, and INTRA transactions all need to be able to handle fees differently. In general, the most correct way to handle fees is to include them in the transaction by either adding them to the cost basis or removing them from the proceeds. This helps reduce the number of extra "disposals" by limiting the number of "fee only" transactions. This should be handled on two fronts. First, the current code should be modified such that "BUY" fees are in whatever currency is being traded, not simply in fiat. This brings up another issue with fiat currency fees that I'll address shortly. Anyway, the "BUY" fee currency should match that of the asset being bought. It comes up often that transaction fees are paid in the bought currency. The other piece that goes along with this is that the data pre-processor needs to keep the fee for each transaction with either the BUY or SELL half if the fee asset matches the BUY or SELL asset. For INTRA trades, the current code is fine, since adjusting the AMOUNT SENT and AMOUNT RECEIVED numbers sets the fee. This also needs to be part of the data pre-processor, though, since generally, the amount moved between accounts needs to be the same number in order to identify matching transactions. In other words, say you have 1 BTC in you account in the Cash App, and you send it all to Coinbase, which has a transaction fee of .005 BTC. In the ledger, this would look like a 0.995 BTC withdrawal from Cash, a 0.995 BTC Deposit to Coinbase, and a 0.005 BTC fee on the Cash App account. This is necessary so that internal movements of funds can be identified based on their date, asset, and amount. However, this can be converted to an INTRA transaction of 1 BTC send, 0.995 BTC received, if that's the smartest way to handle this in the future version of your software.

I'll take a moment to pause here with a sidenote on fiat currency fees. Am I correct in assuming that this software was developed to ignore USD as its own "crypto asset" for calculations? I realize that by including a USD tab to be processed by the software that it creates gains/losses on USD, which is a bit weird, but letting the software calculate stuff on USD is useful, since it provides a better picture of what's happening in your USD holdings. Including USD would probably be necessary, however, if the BUY fee asset is changed from USD to whatever the bought currency is. However, this could be managed by just logging USD fees in their own tab and modifying the cost basis/proceeds/expenses of transactions after the fact. Regardless, I bring this up because I included USD in my calculations and I got an error because the software didn't recognize that my excel tab for USD was not the same USD as the FIAT FEE column. So when I listed USD fees for transactions, these dollars weren't removed from my USD holdings. This is another reason why the BUY fees should be set as whatever the BUY asset is.

  1. All fees that arenโ€™t included with either the BUY or SELL half of the transaction need their own transaction, which is already discussed in the conversation about how to list โ€œfee onlyโ€ transactions. The challenge here is that it would be great if the fiat value of these fees could be added to the cost basis or removed from the proceeds of whatever transaction they came from. On the other hand, the software still needs โ€œfee onlyโ€ transaction capability because โ€œfee onlyโ€ transactions actually exist when trading in DeFi. For example, if you go to swap BTC to ETH using 1inch.io on Binance Smart Chain, you must first approve the assets being traded. These are smart contracts that you sign, which tells 1inch.io that you have checked the coin addresses and confirm that you are trading the correct coins. Such a transaction, often called a contract approval, generates a fee (gas fee), but does not buy, sell, or trade any coins. For taxes, itโ€™s a bit unclear how these should be categorized, but I would think they should be expenses, similar to fees incurred when moving assets between accounts. In fact, any fee that cannot be tied to the cost basis/proceeds of a transaction should probably be categorized as an expense.

LIQUIDITY MINING
Interacting with smart contracts can be a tricky business. There are so many ways that these can complicate things, and it probably makes sense to add functionality for them as situations arise, but again, this should be part of the data pre-processor. One example of a very common situation is with liquidity mining. The concept is that you add liquidity to an exchange by adding two assets together, which can be used by people to buy and sell those assets. In return for offering these coins to be traded, you usually get some of the proceeds (trading fees) incurred by the person using the exchange. The way these transactions work is the following. You want to add BUSD/USDC liquidity to PancakeSwap. To do this, you add 1000 BUSD 1000 USDC, and in exchange, you get 2000 Cake-LP tokens as a receipt for the coins youโ€™ve added to the liquidity pool. This transaction has 4 assets involved, in 3 parts. First, you had a withdrawal for 1000 BUSD and a withdrawal for 1000 USDC. Next, you received (a deposit) for 2000 Cake-LP. Lastly, you paid gas in the blockchainโ€™s native token, say 0.001 BNB. In my opinion, the best way to handle this transaction is to split it. So the first transaction would be trading 1000 BUSD for 1000 Cake-LP with a transaction fee of 0.0005 BNB. The second would be trading 1000 USDC for 1000 Cake-LP with a transaction fee of 0.0005 BNB. Also note that the opposite of this situation happens when liquidity is removed from the liquidity pool.

STAKING
First, I think itโ€™s dumb that the IRS, or whoever, decided that rewards from staking assets should be classified as โ€œStakingโ€. This makes things very confusing for people who are also using โ€œStakingโ€ and โ€œUnstakingโ€ to describe doing those actions. Aside from this stupidity, the actions of staking and unstaking need to be handled appropriately. The current reigning theory in staking assets is that this act is non-taxable, because you still own the assets that you are staking, even though theyโ€™re being held by someone else. As such, you could probably exclude the movement of funds into and out of staking pools, except there are a couple problems with this. First, excluding these movements means that the transaction fees (expenses) from these movements wouldnโ€™t be captured. Second, there are some instances where the staking pool charges a fee to add your coins. For example, there are staking pools on brand new โ€œsh**coinโ€ exchanges that charge 4% of your staked assets upon depositing, since the APR offered is incredibly high. In these cases, the coin price always drops like a rock, and youโ€™re lucky to recoup the fees, but it can be done for a decent profit. In either case mentioned here, itโ€™s important to be able to log and account for the fees incurred. The best way Iโ€™ve found to do this is to create a separate virtual wallet that stores all of my staked coins. By doing this, you can show your activity of staking and unstaking coins where the only taxable part of the transaction is the fee (which is what we want).

DATA PRE-PROCESSING
As mentioned throughout this post, the need for data pre-processing is huge here. I think this is one of the things that DaLI is supposed to be doing, but it seems to be more focused on parsing data files from common exchanges rather than cleaning, organizing, and preparing an ODS file that can be used for RP2. I suggest that the nearest-term project should be on creating a converter that takes a .csv file (or whatever) and turns it into an input file for RP2. The input data format for this should be predetermined (names of columns, categories, general conventions, etc), and information contained within it should dictate how the data gets transformed into the RP2 input file. As an example, say you label a transaction (a row in the data file) as โ€œFEEโ€. The converter would then know that this line needs to be processed to make it a fee-only transaction for RP2. Similarly, if a set of 3 lines (all with the same transaction hash) are labeled โ€œADD LIQUIDITYโ€, then the converter will know to split the transaction, distribute the LP token and fee to each half, and then process these into IN/OUT lines for RP2.
Once the converter is made and is functional, then time should be spent on parsing and importing common data files from exchanges and wallets (DaLI). Note that these tools should extract data from the downloaded exchange files, for example, and put it into the converter file structure with proper labels and such, so that the converter will be useful for all data. The problem with DaLI currently is that each plugin must be managed separately, but the rules for converting the information to an RP2 input should be centralized. Please consider building a converter first, then using DaLI plugins to populate the input file for the converter. This is because a universal converter will be extremely useful right away, whereas DaLI is only useful for whichever plugins are written and written correctly. Plus, changes to how they convert data for RP2 would have to be managed in each of them individually instead of letting the converter do the hard work.

OUTPUT REPORTS
Another thing that would be useful, since the IRS wants this info, is a script that exports summary statements within the crypto tax report. Information should include:

  1. Summary of Capital Gains with number of disposals, total proceeds from sales, total acquisition costs, total realized gains, total realized losses, net capital gains

  2. Capital Gains Report showing realized gains, realized losses, and net gain/loss grouped by cryptocurrency

  3. Year end holdings with the following information for each coin: quantity, cost basis for that amount, net value at year end (12/31), average cost per coin, and unrealized gains/losses

  4. Income summary showing how much income was received from each type (airdrop, staking, interest, wages, etc.)

  5. Expense summary whosing the total expenses incurred by category (transfer fees, contract approvals, etc.)

  6. The settings and assumptions used by this software. For example, that transfers between accounts are not taxes, but the fees for moving them are included in the expense report and are based on fair market value at the time of the transaction. That gains/losses from crypto-to-crypto trades are included in the capital gains report. Etc...

ADDITIONAL THOUGHTS
The one thing this project is missing is the ability to automatically find and download historical coin prices to be added to the data. This feature would be killer, but the challenge seems to be finding a free crypto price API and interacting with it. Let me know if you want to see what Iโ€™ve accomplished on this front.

Add Support for Canada

RP2 and DaLI have a programmable plugin architecture for countries, accounting methods, report generators and more. Adding support for a new country is a high-impact activity because it allows many new potential users to get access to RP2 and DaLI.

Here's how to add support for a new country:

  1. add a new country plugin to RP2;
  2. if the country requires accounting methods that aren't already supported in RP2, add the necessary accounting method plugins;
  3. ensure any newly added accounting methods are reflected in the country plugin's get_default_accounting_method() and get_accounting_methods() methods;
  4. optionally, new country-specific report generators can be added. The built-in report generators (i.e. rp2_full_report and open_positions) are automatically supported;
  5. only for non-English speaking countries: translations for the built-in report generators and for any new report generator can be added;
  6. ensure any newly added report generators are reflected in the country plugin's get_report_generators() method;
  7. add the same country plugin to DaLI. The implementation on the DaLI side is trivial: it's just an entry point instantiating the RP2 country plugin and passing it to the main function;
  8. update the documentation of RP2 and DaLI to reflect the new country, accounting methods (if any) and report generators (if any).

If you notice the country-specific infrastructure is missing a feature that is required to express taxes for the new country, open an issue to let the RP2 community know.

Example files do not work

Hi, I'm trying to run the example files:

  • crypto_example.config
  • crypto_example.ods

But upon running, get these errors

INFO: Country: us
INFO: Accounting Method: fifo
INFO: Configuration file: crypto_example.config
INFO: Input file: crypto_example.ods
INFO: Processing BTC
INFO: Processing ETH
ERROR: Fatal exception occurred:
Traceback (most recent call last):
  File "/Users/swissarmybox/Desktop/cry/rp2stuff/venv/lib/python3.8/site-packages/rp2/rp2_main.py", line 91, in _rp2_main_internal
    input_data: InputData = parse_ods(configuration=configuration, asset=asset, input_file_handle=input_file_handle)
  File "/Users/swissarmybox/Desktop/cry/rp2stuff/venv/lib/python3.8/site-packages/rp2/ods_parser.py", line 123, in parse_ods
    transaction = _create_transaction(configuration, current_table_type, i + 1, row_values)
  File "/Users/swissarmybox/Desktop/cry/rp2stuff/venv/lib/python3.8/site-packages/rp2/ods_parser.py", line 190, in _create_transaction
    argument_pack: Dict[str, Any] = configuration.get_in_table_constructor_argument_pack(row_values)
  File "/Users/swissarmybox/Desktop/cry/rp2stuff/venv/lib/python3.8/site-packages/rp2/configuration.py", line 163, in get_in_table_constructor_argument_pack
    return self.__get_table_constructor_argument_pack(data, "in", self.__in_header)
  File "/Users/swissarmybox/Desktop/cry/rp2stuff/venv/lib/python3.8/site-packages/rp2/configuration.py", line 154, in __get_table_constructor_argument_pack
    raise RP2ValueError(
rp2.rp2_error.RP2ValueError: Parameter 'data' has length 13, but required minimum from in-table headers in crypto_example.config is 14: ['2020-06-03T11:23Z', 'Coinbase', 'Bob', 'brown', 'fox', 'Buy', 'ETH', 10.0, 244.0, 2440.0, 2465.0, 25.0, None]
INFO: Log file: ./log/rp2_2022_02_26_09_58_40_020721.log
INFO: Generated output directory: output
INFO: Done

Use default timezone when none found

I'm using a Google spreadsheet where I enter date formatted values and it seems it doesn't provide a timezone.

What about allowing to pass in a default timezone (CLI flag) that will be used when it cannot find one?

Add Support for India

RP2 and DaLI have a programmable plugin architecture for countries, accounting methods, report generators and more. Adding support for a new country is a high-impact activity because it allows many new potential users to get access to RP2 and DaLI.

Here's how to add support for a new country:

  1. add a new country plugin to RP2;
  2. if the country requires accounting methods that aren't already supported in RP2, add the necessary accounting method plugins;
  3. ensure any newly added accounting methods are reflected in the country plugin's get_default_accounting_method() and get_accounting_methods() methods;
  4. optionally, new country-specific report generators can be added. The built-in report generators (i.e. rp2_full_report and open_positions) are automatically supported;
  5. only for non-English speaking countries: translations for the built-in report generators and for any new report generator can be added;
  6. ensure any newly added report generators are reflected in the country plugin's get_report_generators() method;
  7. add the same country plugin to DaLI. The implementation on the DaLI side is trivial: it's just an entry point instantiating the RP2 country plugin and passing it to the main function;
  8. update the documentation of RP2 and DaLI to reflect the new country, accounting methods (if any) and report generators (if any).

If you notice the country-specific infrastructure is missing a feature that is required to express taxes for the new country, open an issue to let the RP2 community know.

Question on 0.00001 SAT Precision Data

Is there a way to increase the precision of the asset amounts especially for higher value cryptos (like BTC)?

For example, I have the following in IN table

BTC | 0.000214345960800000
BTC | 0.001076080000000000
BTC | 0.000000135094000000

The total value of BTC should be 0.003331371054800000, but the BTC Tax tab in the generated full report shows an outstanding balance of 0.00333137105. The BTC In-Out tab also doesn't show all the decimal places.

Final Balance in fifo_rp2_full_report.ods

The Final Balance in fifo_rp2_full_report.ods (Tax tab) does not match my expected balance -- it is slightly too high.
The difference is exactly the amount of the last "crypto_fee" in the OUT section of the input file.

My input file has 3 OUT transactions. The results looks as if the crypto fee for the first two transactions is correctly subtracted from the final balance, but the fee for the 3rd transaction is not subtracted.

Add Support for New Zealand

RP2 and DaLI have a programmable plugin architecture for countries, accounting methods, report generators and more. Adding support for a new country is a high-impact activity because it allows many new potential users to get access to RP2 and DaLI.

Here's how to add support for a new country:

  1. add a new country plugin to RP2;
  2. if the country requires accounting methods that aren't already supported in RP2, add the necessary accounting method plugins;
  3. ensure any newly added accounting methods are reflected in the country plugin's get_default_accounting_method() and get_accounting_methods() methods;
  4. optionally, new country-specific report generators can be added. The built-in report generators (i.e. rp2_full_report and open_positions) are automatically supported;
  5. only for non-English speaking countries: translations for the built-in report generators and for any new report generator can be added;
  6. ensure any newly added report generators are reflected in the country plugin's get_report_generators() method;
  7. add the same country plugin to DaLI. The implementation on the DaLI side is trivial: it's just an entry point instantiating the RP2 country plugin and passing it to the main function;
  8. update the documentation of RP2 and DaLI to reflect the new country, accounting methods (if any) and report generators (if any).

If you notice the country-specific infrastructure is missing a feature that is required to express taxes for the new country, open an issue to let the RP2 community know.

Switch to Keyword Argument Calling Style

Currently RP2 uses keyword argument calling style inconsistently (mostly of the inconsitencies are in unit tests but there are a few in src as well). This issue has the purpose of switching all call sites with non-trivial number of arguments to keyword argument calling style.

The work on this issue will be incremental: you can open a PR for a single fix (or a few) and point it to this issue.

Some examples of fixes follow.

This RP2 src snippet:

          balance = Balance(
               configuration,
               self.__asset,
               account.exchange,
               account.holder,
               final_balance,
               acquired_balances.get(account, ZERO),
               sent_balances.get(account, ZERO),
               received_balances.get(account, ZERO),
           )

should become:

            balance = Balance(
                configuration=configuration,
                asset=self.__asset,
                exchange=account.exchange,
                account=account.holder,
                final_balance=final_balance,
                acquired_balance=acquired_balances.get(account, ZERO),
                sent_balance=sent_balances.get(account, ZERO),
                received_balance=received_balances.get(account, ZERO),
            )

And this unit test snippet:

        self._in_buy = InTransaction(
            self._configuration,
            "2020-01-02T08:42:43.882Z",
            "BTC",
            "Coinbase Pro",
            "Bob",
            "Buy",
            RP2Decimal("10000"),
            RP2Decimal("2.0002"),
            fiat_fee=RP2Decimal("20"),
            fiat_in_no_fee=RP2Decimal("20002"),
            fiat_in_with_fee=RP2Decimal("20022"),
            internal_id=10,
        )

should become:

        self._in_buy = InTransaction(
            configuration=self._configuration,
            timestamp="2020-01-02T08:42:43.882Z",
            asset="BTC",
            exchange="Coinbase Pro",
            holder="Bob",
            transaction_type="Buy",
            spot_price=RP2Decimal("10000"),
            crypto_in=RP2Decimal("2.0002"),
            fiat_fee=RP2Decimal("20"),
            fiat_in_no_fee=RP2Decimal("20002"),
            fiat_in_with_fee=RP2Decimal("20022"),
            internal_id=10,
        )

RecursionError: maximum recursion depth exceeded in comparison for Decimal.quantize()

I'm getting an endless loop which seems to be triggered by Decimal.quantize() calling RP2Decimal.__ne__ which then calls RP2Decimal.__eq__ which then calls Decimal.quantize() again.

I presume there is something wrong with my numbers, which I have yet to investigate.

...
  File "/home/rndstr/venv3/lib/python3.10/site-packages/rp2/rp2_decimal.py", line 40, in __eq__
    return (self - other).quantize(CRYPTO_DECIMAL_MASK).__eq__(ZERO)
  File "/usr/lib/python3.10/_pydecimal.py", line 2588, in quantize
    if ans != self:
  File "/home/rndstr/venv3/lib/python3.10/site-packages/rp2/rp2_decimal.py", line 43, in __ne__
    return not self.__eq__(other)
  File "/home/rndstr/venv3/lib/python3.10/site-packages/rp2/rp2_decimal.py", line 40, in __eq__
    return (self - other).quantize(CRYPTO_DECIMAL_MASK).__eq__(ZERO)
  File "/usr/lib/python3.10/_pydecimal.py", line 2588, in quantize
    if ans != self:
  File "/home/rndstr/venv3/lib/python3.10/site-packages/rp2/rp2_decimal.py", line 43, in __ne__
    return not self.__eq__(other)
  File "/home/rndstr/venv3/lib/python3.10/site-packages/rp2/rp2_decimal.py", line 40, in __eq__
    return (self - other).quantize(CRYPTO_DECIMAL_MASK).__eq__(ZERO)
  File "/home/rndstr/venv3/lib/python3.10/site-packages/rp2/rp2_decimal.py", line 69, in __sub__
    return RP2Decimal(Decimal.__sub__(self, other))
  File "/usr/lib/python3.10/_pydecimal.py", line 1257, in __sub__
    return self.__add__(other.copy_negate(), context=context)
  File "/home/rndstr/venv3/lib/python3.10/site-packages/rp2/rp2_decimal.py", line 64, in __add__
    return RP2Decimal(Decimal.__add__(self, other))
  File "/usr/lib/python3.10/_pydecimal.py", line 1202, in __add__
    ans = self._rescale(exp, context.rounding)
  File "/usr/lib/python3.10/_pydecimal.py", line 2625, in _rescale
    if not self:
  File "/usr/lib/python3.10/_pydecimal.py", line 815, in __bool__
    return self._is_special or self._int != '0'
RecursionError: maximum recursion depth exceeded in comparison

Remainder of acquired lot does not seem to be carried over

I have some small BTC transactions:

BUY 0.00032667 crypto_in
SELL 0.00032600 crypto_out_with_fee
--- Wallet contains 0.00000067 BTC
BUY 0.00147452 crypto_in
--- Wallet now contains 0.00147519 BTC
SELL 0.00147500 crypto_out_with_fee
BUY
0.00162737 <- RP2 stops here and complains that the taxable event (previous Sell) is earlier than this acquired lot.

It appears that RP2 doesn't carry over the remainder. RP2 supports splitting lots right? Maybe I'm misunderstanding tax law.

RP2 Ecosystem Brainstorming

Hi.

I could use something like this, but of course I use different exchanges and each has a different CSV output format for trades and orders.

I'd like to write a separate project rp2-conv that converts CSV and outputs the .ods and .json config file. I'll probably use Boilerplate but tack on each importer in the exchanges section.

At first I was hoping to add it to this project, but I think it's better if I keep it as a separate project. I plan to support Kraken, Bitmart, Binance.US, Coinbase and Coinbase Pro to start. I may try to support public wallet addresses for TrustWallet / PancakeSwap.

I'm wondering if you'd like to link it in your readme once things are going? Either way I'm interested in your project and hope to use it to help with my taxes this year.

I'm not sure how you write .ods, I see ods_parser.py though. I'd like to have it output the .ods but in the early versions it may just output a CSV that can be loaded into OpenOffice/LibreOffice and exported manually.

Add Support for South Korea

RP2 and DaLI have a programmable plugin architecture for countries, accounting methods, report generators and more. Adding support for a new country is a high-impact activity because it allows many new potential users to get access to RP2 and DaLI.

Here's how to add support for a new country:

  1. add a new country plugin to RP2;
  2. if the country requires accounting methods that aren't already supported in RP2, add the necessary accounting method plugins;
  3. ensure any newly added accounting methods are reflected in the country plugin's get_default_accounting_method() and get_accounting_methods() methods;
  4. optionally, new country-specific report generators can be added. The built-in report generators (i.e. rp2_full_report and open_positions) are automatically supported;
  5. only for non-English speaking countries: translations for the built-in report generators and for any new report generator can be added;
  6. ensure any newly added report generators are reflected in the country plugin's get_report_generators() method;
  7. add the same country plugin to DaLI. The implementation on the DaLI side is trivial: it's just an entry point instantiating the RP2 country plugin and passing it to the main function;
  8. update the documentation of RP2 and DaLI to reflect the new country, accounting methods (if any) and report generators (if any).

If you notice the country-specific infrastructure is missing a feature that is required to express taxes for the new country, open an issue to let the RP2 community know.

Add Support for Australia

RP2 and DaLI have a programmable plugin architecture for countries, accounting methods, report generators and more. Adding support for a new country is a high-impact activity because it allows many new potential users to get access to RP2 and DaLI.

Here's how to add support for a new country:

  1. add a new country plugin to RP2;
  2. if the country requires accounting methods that aren't already supported in RP2, add the necessary accounting method plugins;
  3. ensure any newly added accounting methods are reflected in the country plugin's get_default_accounting_method() and get_accounting_methods() methods;
  4. optionally, new country-specific report generators can be added. The built-in report generators (i.e. rp2_full_report and open_positions) are automatically supported;
  5. only for non-English speaking countries: translations for the built-in report generators and for any new report generator can be added;
  6. ensure any newly added report generators are reflected in the country plugin's get_report_generators() method;
  7. add the same country plugin to DaLI. The implementation on the DaLI side is trivial: it's just an entry point instantiating the RP2 country plugin and passing it to the main function;
  8. update the documentation of RP2 and DaLI to reflect the new country, accounting methods (if any) and report generators (if any).

If you notice the country-specific infrastructure is missing a feature that is required to express taxes for the new country, open an issue to let the RP2 community know.

Investment Expenses Tab

Hello,

I just have a quick question for you.

In the tax-friendly output file: Are the transactions in the "Investment Expenses" tab already factored into the "Capital Gains" tab?
Do "Investment Expenses" get their own tab simply for easy record-keeping, or, do I need to manually go through my "Capital Gains" transactions and adjust the cost basis myself, based upon what I see in the "Investment Expenses" tab?

Thank you!

DaLi generated output results in RP2TypeError: Parameter 'unique_id' has non-string value

Not sure if this is a problem with DaLi, RP2, or something I'm doing wrong.

DaLi input is a manual in_csv_file with a unique id consisting of all digits, 61891576412, no decimal point.
DaLi runs successfully and generates the crypto_data.ods output file.
When RP2 runs it raises this error:
rp2.rp2_error.RP2TypeError: Parameter 'unique_id' has non-string value 61891576412.0

DeFi Brainstorming

This is a really interesting and useful project, nice work! But unless I am not understanding something correctly, there is a problem here: this code must be used from the very first transaction across all years of trading crypto. What if I already have a detailed snapshot of my crypto portfolio at the start of 2021, including coins/tokens, dates of purchase and cost basis for coins/tokens purchased on that date. I would like to use just my transactions from 2021 and that portfolio snapshot to compute my taxes for 2021. Is there any way to do this? If not, where would I start in the code to update the code to handle this?

Cannot process earlier transaction?

I have transactions in IN:

  • 2021-01-03T23:40:42Z, Buy, 1.0
  • 2021-11-06T17:42:38Z, Buy, 0.05
  • 2021-11-06T17:53:04Z, Buy, 0.05

and transactions in OUT:

  • 2021-01-13T23:25:01Z, Sell, 1.0

Upon generation, I see a log being raised
RP2ValueError9 TImestamp (rp2.rp2_error.RP2ValueError: Timestamp 2021-01-13 23:25:01+00:00 of taxable_event is earlier than timestamp 2021-11-06 17:42:38+00:00 of acquired_lot: GainLoss:)

Seems it gets confused on the dates

Add Support for UK

RP2 and DaLI have a programmable plugin architecture for countries, accounting methods, report generators and more. Adding support for a new country is a high-impact activity because it allows many new potential users to get access to RP2 and DaLI.

Here's how to add support for a new country:

  1. add a new country plugin to RP2;
  2. if the country requires accounting methods that aren't already supported in RP2, add the necessary accounting method plugins;
  3. ensure any newly added accounting methods are reflected in the country plugin's get_default_accounting_method() and get_accounting_methods() methods;
  4. optionally, new country-specific report generators can be added. The built-in report generators (i.e. rp2_full_report and open_positions) are automatically supported;
  5. only for non-English speaking countries: translations for the built-in report generators and for any new report generator can be added;
  6. ensure any newly added report generators are reflected in the country plugin's get_report_generators() method;
  7. add the same country plugin to DaLI. The implementation on the DaLI side is trivial: it's just an entry point instantiating the RP2 country plugin and passing it to the main function;
  8. update the documentation of RP2 and DaLI to reflect the new country, accounting methods (if any) and report generators (if any).

If you notice the country-specific infrastructure is missing a feature that is required to express taxes for the new country, open an issue to let the RP2 community know.

Does this take crypto intra transfer fees into account?

Hi, sorry if this is described somewhere. Does this library calculate crypto intra transfer fees into account? Say I do these:

  • Buy 1 ETH from an exchange for $100
  • Transfer to my wallet, transfer to another wallet, transfer back to my wallet, then transfer back to exchange the leftover ETH, 0.9 ETH
  • Sell 0.9 ETH in an exchange for $110 (short term gain)

Would the cost basis would be 0.9 ETH for $100 or 1 ETH for $100?

Margin trading

Does RP2 handle margin trading? Leveraged trades. Shorting. Selling borrowed assets that are bought later to repay the loan.

Entering Fee only transactions when fees are paid in crypto rather than fiat

I am not able to add fee only transactions when I have transactions with Binance and the fees are paid in BNB.

If I do something similar as explained here, I get the following error
rp2.rp2_error.RP2ValueError: Parameter 'crypto_out_no_fee' has zero value.

Example:
SELL 0.01 ETH with 0.001 BNB fee

OUT
ETH crypto_out_no_fee=0.01, crypto_fee=0
BNB crypto_out_no_fee=0, crypto_fee=0.001

Adding liquidity pools

Hey, great project. Was thinking of taking a stab at trying this out but I need to brush up.

I had a question about liquidity pools (LPs). Has there any thought about how they would be handled? There's a few details here.

  1. LPs are usually created with a 50/50 token split. However, they don't have to always be 50/50. There could be 2+ token assets that create a pool such as a 3 asset pool with an 80/20/20 split.
  2. When you create an LP, you will know the value in. However, when you withdraw from an LP, pool rebalancing due to price fluctuations may have happened which means you wouldn't get the exact ratio of your tokens out. IE: LP-IN (2 token-A & 4 token-B) -> LP-OUT (1 token-A & 3 token-B).
  3. You can partially withdraw from an LP
  4. You can withdraw from the LP with just a single asset IE: LP-IN ($5 token-A & $6 token-b) -> LP-OUT ($11 token-A)
  5. LPs still have a quantity attached to it as they are a token representation of your share of the pool.

I'm thinking of ways to handle this which could include

  1. Consider an LP a token or separate "wallet" whose value is calculated based on USD value at time of deposit
  2. On withdraw, deduct the LP quantity and calculate the USD withdraw value
  3. Use the LP deposit USD value as the cost basis

It seems like it would work for tax purposes, but if a frontend were to ever be built, it could get tricky to display.

Add Support for Germany

RP2 and DaLI have a programmable plugin architecture for countries, accounting methods, report generators and more. Adding support for a new country is a high-impact activity because it allows many new potential users to get access to RP2 and DaLI.

Here's how to add support for a new country:

  1. add a new country plugin to RP2;
  2. if the country requires accounting methods that aren't already supported in RP2, add the necessary accounting method plugins;
  3. ensure any newly added accounting methods are reflected in the country plugin's get_default_accounting_method() and get_accounting_methods() methods;
  4. optionally, new country-specific report generators can be added. The built-in report generators (i.e. rp2_full_report and open_positions) are automatically supported;
  5. only for non-English speaking countries: translations for the built-in report generators and for any new report generator can be added;
  6. ensure any newly added report generators are reflected in the country plugin's get_report_generators() method;
  7. add the same country plugin to DaLI. The implementation on the DaLI side is trivial: it's just an entry point instantiating the RP2 country plugin and passing it to the main function;
  8. update the documentation of RP2 and DaLI to reflect the new country, accounting methods (if any) and report generators (if any).

If you notice the country-specific infrastructure is missing a feature that is required to express taxes for the new country, open an issue to let the RP2 community know.

Multiple account balance records in fifo_rp2_full_report.ods

Not sure if this is a bug or a feature. In the Tax-tab, I was expecting a single row containing the account balance at each exchange. Instead, I see something like this (contents changed a bit to preserve privacy):

image

For some reason, I'm seeing two entries for the same token at exchangeBB. The actual balance of exchangeBB is the sum of the two entries.

Add Support for Ireland

RP2 and DaLI have a programmable plugin architecture for countries, accounting methods, report generators and more. Adding support for a new country is a high-impact activity because it allows many new potential users to get access to RP2 and DaLI.

Here's how to add support for a new country:

  1. add a new country plugin to RP2;
  2. if the country requires accounting methods that aren't already supported in RP2, add the necessary accounting method plugins;
  3. ensure any newly added accounting methods are reflected in the country plugin's get_default_accounting_method() and get_accounting_methods() methods;
  4. optionally, new country-specific report generators can be added. The built-in report generators (i.e. rp2_full_report and open_positions) are automatically supported;
  5. only for non-English speaking countries: translations for the built-in report generators and for any new report generator can be added;
  6. ensure any newly added report generators are reflected in the country plugin's get_report_generators() method;
  7. add the same country plugin to DaLI. The implementation on the DaLI side is trivial: it's just an entry point instantiating the RP2 country plugin and passing it to the main function;
  8. update the documentation of RP2 and DaLI to reflect the new country, accounting methods (if any) and report generators (if any).

If you notice the country-specific infrastructure is missing a feature that is required to express taxes for the new country, open an issue to let the RP2 community know.

Python 3.10: TypeError: __add__() got an unexpected keyword argument 'context'

Running the example i'm getting this error (tested with Python3.9 and Python3.10)

 $ rp2_us -m fifo -o output -p crypto_example_ crypto_example.config crypto_example.ods
INFO: Country: us
INFO: Accounting Method: fifo
INFO: Configuration file: crypto_example.config
INFO: Input file: crypto_example.ods
INFO: Processing BTC
ERROR: Fatal exception occurred:
Traceback (most recent call last):
  File "/home/rndstr/venv/lib/python3.9/site-packages/rp2/rp2_main.py", line 93, in _rp2_main_internal
    input_data: InputData = parse_ods(configuration=configuration, asset=asset, input_file_handle=input_file_handle)
  File "/home/rndstr/venv/lib/python3.9/site-packages/rp2/ods_parser.py", line 128, in parse_ods
    _create_and_process_transaction(
  File "/home/rndstr/venv/lib/python3.9/site-packages/rp2/ods_parser.py", line 168, in _create_and_process_transaction
    transaction: AbstractTransaction = _create_transaction(configuration, current_table_type, internal_id, row_values)
  File "/home/rndstr/venv/lib/python3.9/site-packages/rp2/ods_parser.py", line 277, in _create_transaction
    transaction = InTransaction(**argument_pack)
  File "/home/rndstr/venv/lib/python3.9/site-packages/rp2/in_transaction.py", line 52, in __init__
    super().__init__(configuration, timestamp, asset, transaction_type, spot_price, internal_id, unique_id, notes)
  File "/home/rndstr/venv/lib/python3.9/site-packages/rp2/abstract_transaction.py", line 41, in __init__
    self.__spot_price: RP2Decimal = configuration.type_check_positive_decimal("spot_price", spot_price)
  File "/home/rndstr/venv/lib/python3.9/site-packages/rp2/configuration.py", line 276, in type_check_positive_decimal
    if result < ZERO:
  File "/home/rndstr/venv/lib/python3.9/site-packages/rp2/rp2_decimal.py", line 59, in __lt__
    return not self.__ge__(other)
  File "/home/rndstr/venv/lib/python3.9/site-packages/rp2/rp2_decimal.py", line 48, in __ge__
    return (self - other).quantize(CRYPTO_DECIMAL_MASK).__ge__(ZERO)
  File "/home/rndstr/venv/lib/python3.9/site-packages/rp2/rp2_decimal.py", line 69, in __sub__
    return RP2Decimal(Decimal.__sub__(self, other))
  File "/usr/lib/python3.9/_pydecimal.py", line 1257, in __sub__
    return self.__add__(other.copy_negate(), context=context)
TypeError: __add__() got an unexpected keyword argument 'context'
INFO: Log file: ./log/rp2_2022_04_10_15_52_55_045185.log
INFO: Generated output directory: output
INFO: Done

Add Support for France

RP2 and DaLI have a programmable plugin architecture for countries, accounting methods, report generators and more. Adding support for a new country is a high-impact activity because it allows many new potential users to get access to RP2 and DaLI.

Here's how to add support for a new country:

  1. add a new country plugin to RP2;
  2. if the country requires accounting methods that aren't already supported in RP2, add the necessary accounting method plugins;
  3. ensure any newly added accounting methods are reflected in the country plugin's get_default_accounting_method() and get_accounting_methods() methods;
  4. optionally, new country-specific report generators can be added. The built-in report generators (i.e. rp2_full_report and open_positions) are automatically supported;
  5. only for non-English speaking countries: translations for the built-in report generators and for any new report generator can be added;
  6. ensure any newly added report generators are reflected in the country plugin's get_report_generators() method;
  7. add the same country plugin to DaLI. The implementation on the DaLI side is trivial: it's just an entry point instantiating the RP2 country plugin and passing it to the main function;
  8. update the documentation of RP2 and DaLI to reflect the new country, accounting methods (if any) and report generators (if any).

If you notice the country-specific infrastructure is missing a feature that is required to express taxes for the new country, open an issue to let the RP2 community know.

Add Support for Netherlands

RP2 and DaLI have a programmable plugin architecture for countries, accounting methods, report generators and more. Adding support for a new country is a high-impact activity because it allows many new potential users to get access to RP2 and DaLI.

Here's how to add support for a new country:

  1. add a new country plugin to RP2;
  2. if the country requires accounting methods that aren't already supported in RP2, add the necessary accounting method plugins;
  3. ensure any newly added accounting methods are reflected in the country plugin's get_default_accounting_method() and get_accounting_methods() methods;
  4. optionally, new country-specific report generators can be added. The built-in report generators (i.e. rp2_full_report and open_positions) are automatically supported;
  5. only for non-English speaking countries: translations for the built-in report generators and for any new report generator can be added;
  6. ensure any newly added report generators are reflected in the country plugin's get_report_generators() method;
  7. add the same country plugin to DaLI. The implementation on the DaLI side is trivial: it's just an entry point instantiating the RP2 country plugin and passing it to the main function;
  8. update the documentation of RP2 and DaLI to reflect the new country, accounting methods (if any) and report generators (if any).

If you notice the country-specific infrastructure is missing a feature that is required to express taxes for the new country, open an issue to let the RP2 community know.

Feature Request - Open Positions / Unrealized Gains

It'd be useful for me to see a table of open positions for all cryptos in a single table. Possible placement could be the summary tab or its own tab.

The following fields alone would be quite handy: Asset, Exchange, Balance, Cost Basis of Position

It would be especially awesome if there was a field off to the side for "Current Spot Price" designated for the user to enter the current price of the asset, and additional calc fields near that for "Current Value", "Net Gain/Loss", "Pct Gain/Loss" which had the calculations already in place where once the spot price was entered, the corresponding values would be displayed.

There could be some finagling such that the user didn't have to enter the current spot value multiple times e.g. for the same asset held in multiple places, but that would be a minor inconvenience for the value provided.

Staking Documentation Requirements Unclear & 'fiat_in_no_fee' has zero value

I have (truncated) data (in my IN) that looks like this:

Transaction Type Asset Crypto In Spot Price USD In (No Fee) USD In (With Fee) USD Fee
Buy XTZ 3.443759 5.37 18.51 20 1.49
Buy XTZ 61.7067 3.19 197.01 200 2.99
Buy XTZ 18.45087 2.6 48.01 50 1.99
Staking XTZ 0.001164 3.53 0 0 0
Staking XTZ 0.001157 3.45 0 0 0
Staking XTZ 0.001049 3.09 0 0 0
Staking XTZ 0.001047 3.33 0 0 0
Staking XTZ 0.00119 3.29 0 0 0

Which appears to be causing the error:
rp2.rp2_error.RP2ValueError: Parameter 'fiat_in_no_fee' has zero value

The config is aligned, but it doesn't like my staking rows - what is supposed to be here?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.