diff --git a/crypto_tax/__init__.py b/crypto_tax/__init__.py index 65ca5e7..1a9afe8 100644 --- a/crypto_tax/__init__.py +++ b/crypto_tax/__init__.py @@ -26,26 +26,13 @@ from fin_depo.data import * import datetime import dataclasses import logging +import fin_data + +from .data import TaxReport, BoughtAndSold, BoughtAndNotYetSold logger = logging.getLogger(__name__) -@dataclasses.dataclass -class BoughtAndSold: - amount: AssetAmount - time_bought: datetime.datetime - time_sold: datetime.datetime - -@dataclasses.dataclass -class BoughtAndNotYetSold: - amount: AssetAmount - time_bought: datetime.datetime - - -@dataclasses.dataclass -class TaxReport: - bought_and_sold_for: list[BoughtAndSold] - bought_and_spent_for: list[BoughtAndSold] - current_assets: dict[Asset, deque[BoughtAndNotYetSold]] +FIN_DB = fin_data.FinancialDatabase(enable_kucoin=True, enable_nationalbanken_dk=True) def compute_tax(transfers: list) -> TaxReport: @@ -117,8 +104,17 @@ def compute_tax(transfers: list) -> TaxReport: spend(fee, transfer.executed_time) del transfer, fee + def exchange_rate_at_time(a, b, t): + try: + sample = FIN_DB.get_exchange_rate_at_time(a, b, t) + return sample.average + except Exception: + logger.exception('Error when fetching exchange rate (%s,%s) at %s', a, b, t) + return None + return TaxReport( bought_and_sold_for=bought_and_sold_for, bought_and_spent_for=bought_and_spent_for, current_assets=prices_bought_for, + exchange_rate_at_time=exchange_rate_at_time, ) diff --git a/crypto_tax/__main__.py b/crypto_tax/__main__.py index d4f0fe0..3e90b62 100644 --- a/crypto_tax/__main__.py +++ b/crypto_tax/__main__.py @@ -9,8 +9,9 @@ from fin_depo.data import * import datetime import dataclasses import logging +from pathlib import Path -from . import compute_tax +from . import compute_tax, output_excel KUCOIN_CLIENT = fin_depo.defi_kucoin.KucoinDepoFetcher( secrets.KUCOIN_KEY, @@ -52,6 +53,10 @@ def main(): for bas in tax_report.bought_and_sold_for: sys.stdout.write(f'{bas.amount} ({bas.time_bought} ----> {bas.time_sold})\n') + output_path = Path('./output/report.xlsx') + output_excel.produce_excel_report(tax_report, output_path) + + if __name__ == '__main__': main() diff --git a/crypto_tax/data.py b/crypto_tax/data.py new file mode 100644 index 0000000..7354142 --- /dev/null +++ b/crypto_tax/data.py @@ -0,0 +1,34 @@ +import sys +import requests +import fin_depo +from . import secrets +from fin_defs import CryptoCurrency, AssetAmount, MPC, Asset, USDT +from decimal import Decimal +from collections import deque +from collections.abc import Callable +from fin_depo.data import * +import datetime +import dataclasses +import logging + +@dataclasses.dataclass +class BoughtAndSold: + amount: AssetAmount + time_bought: datetime.datetime + time_sold: datetime.datetime + +@dataclasses.dataclass +class BoughtAndNotYetSold: + amount: AssetAmount + time_bought: datetime.datetime + + +@dataclasses.dataclass +class TaxReport: + bought_and_sold_for: list[BoughtAndSold] + bought_and_spent_for: list[BoughtAndSold] + current_assets: dict[Asset, deque[BoughtAndNotYetSold]] + exchange_rate_at_time: Callable[[Asset,Asset, datetime.datetime ], Decimal] + + + diff --git a/crypto_tax/output_excel.py b/crypto_tax/output_excel.py new file mode 100644 index 0000000..2cbf678 --- /dev/null +++ b/crypto_tax/output_excel.py @@ -0,0 +1,93 @@ +import sys +import requests +import fin_depo +from . import secrets +import fin_defs +from decimal import Decimal +from collections import deque +from fin_depo.data import * +import datetime +import dataclasses +import logging + +from .data import TaxReport, BoughtAndSold, BoughtAndNotYetSold +from pathlib import Path + +import openpyxl + +TAX_TYPES: dict[fin_defs.Asset, str] = { + fin_defs.DKK: 'Fiat', + fin_defs.EUR: 'Fiat', + fin_defs.BTC: 'Krypto-valuta', + fin_defs.MPC: 'Krypto-valuta', + fin_defs.WELL_KNOWN_SYMBOLS['MATIC']: 'Krypto-valuta', + fin_defs.USDT: 'Stable Coin (Financiel Kontrakt)', +} + +NOW = datetime.datetime.now(tz=datetime.UTC) + +def mult_a(a, b): + if a is None or b is None: + return 'Unknown' + return a * b + +def write_current_assets(sheet, tax_report: TaxReport): + column_headers = ['Værdipapir', 'Mængde', 'Estimeret værdi (DKK)', + 'Beskatningstype'] + sheet.append(column_headers) + for asset, positions in tax_report.current_assets.items(): + total_amount: Decimal = sum((p.amount.amount for p in positions), start=Decimal(0)) + exchange_rate_asset_to_dkk = tax_report.exchange_rate_at_time(asset, fin_defs.DKK, NOW) + assert exchange_rate_asset_to_dkk is not None, asset + row = [ + asset.raw_short_name(), + total_amount, + mult_a(total_amount, exchange_rate_asset_to_dkk), + TAX_TYPES[asset], + ] + sheet.append(row) + del asset, positions, row, total_amount + + +def write_fifo_sheet(sheet, tax_report: TaxReport): + column_headers = [ + 'Værdipapir', 'Mængde', 'Anskaffelsesværdi (DKK)', 'Købsdato (UTC)', + 'Salgsværdi (DKK)', 'Salgsdato (UTC)', 'Profit (DKK)', 'Tab (DKK)', + 'Beskatningstype', + ] + sheet.append(column_headers) + row_idx = 2 + for fifo_entry in tax_report.bought_and_sold_for: + print(fifo_entry) + asset = fifo_entry.amount.asset + + exchange_rate_dkk_bought = tax_report.exchange_rate_at_time(asset, fin_defs.DKK, fifo_entry.time_bought) + #assert exchange_rate_dkk_bought is not None, asset + exchange_rate_dkk_sold = tax_report.exchange_rate_at_time(asset, fin_defs.DKK, fifo_entry.time_sold) + #assert exchange_rate_dkk_bought is not None, asset + + row = [ + asset.raw_short_name(), + fifo_entry.amount.amount, + mult_a(fifo_entry.amount.amount , exchange_rate_dkk_bought), + fifo_entry.time_bought.replace(tzinfo=None), + mult_a(fifo_entry.amount.amount , exchange_rate_dkk_sold), + fifo_entry.time_sold.replace(tzinfo=None), + f'=max(0, E{row_idx} - C{row_idx})', + f'=max(0, C{row_idx} - E{row_idx})', + TAX_TYPES[asset], + ] + sheet.append(row) + row_idx+= 1 + del fifo_entry, asset, row + +def produce_excel_report(report: TaxReport, output_path: Path): + workbook = openpyxl.Workbook() + + workbook.active.title = 'Current Assets' + write_current_assets(workbook.active, report) + + sheet_buy_sales = workbook.create_sheet('FIFO') + write_fifo_sheet(sheet_buy_sales, report) + + workbook.save(output_path)