2024-12-22 22:25:24 +00:00
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
2024-12-27 14:58:38 +00:00
import openpyxl . styles
2024-12-27 21:14:39 +00:00
import openpyxl . worksheet . table
from openpyxl . utils import get_column_letter
2024-12-22 22:25:24 +00:00
TAX_TYPES : dict [ fin_defs . Asset , str ] = {
fin_defs . DKK : ' Fiat ' ,
fin_defs . EUR : ' Fiat ' ,
2024-12-27 21:14:39 +00:00
fin_defs . BTC : ' Kryptovaluta ' ,
fin_defs . MPC : ' Kryptovaluta ' ,
fin_defs . WELL_KNOWN_SYMBOLS [ ' MATIC ' ] : ' Kryptovaluta ' ,
fin_defs . USDT : ' Stable Coin ' ,
2024-12-22 22:25:24 +00:00
}
NOW = datetime . datetime . now ( tz = datetime . UTC )
def mult_a ( a , b ) :
2024-12-22 23:08:24 +00:00
if a == 0 or b == 0 :
return 0
2024-12-22 22:25:24 +00:00
if a is None or b is None :
return ' Unknown '
return a * b
2024-12-27 21:14:39 +00:00
FONT_BOLD = openpyxl . styles . Font ( bold = True )
FONT_HEADER = openpyxl . styles . Font ( size = 20 )
2024-12-27 14:58:38 +00:00
ALIGN_CENTER = openpyxl . styles . Alignment ( horizontal = " center " , vertical = " center " )
2024-12-27 21:14:39 +00:00
ALIGN_WRAP = openpyxl . styles . Alignment ( wrap_text = True )
BORDER_BOTTOM = openpyxl . styles . Border ( bottom = openpyxl . styles . Side ( border_style = ' medium ' , color = ' FF000000 ' ) )
BORDER_SUM = openpyxl . styles . Border ( top = openpyxl . styles . Side ( border_style = ' thin ' , color = ' FF000000 ' ) ,
bottom = openpyxl . styles . Side ( border_style = ' double ' , color = ' FF000000 ' ) )
2024-12-27 14:58:38 +00:00
def add_headers ( sheet , column_headers ) :
sheet . append ( column_headers )
2024-12-27 21:14:39 +00:00
end_column = get_column_letter ( len ( column_headers ) )
sheet . row_dimensions [ 1 ] . height = 30
for row in sheet [ f ' A1: { end_column } 1 ' ] :
2024-12-27 14:58:38 +00:00
for cell in row :
2024-12-27 21:14:39 +00:00
cell . font = FONT_BOLD
cell . alignment = ALIGN_CENTER
cell . border = BORDER_BOTTOM
def set_number_format ( sheet , range ) :
for row in sheet [ range ] :
for cell in row :
cell . number_format = " 0.00 "
2024-12-27 14:58:38 +00:00
2024-12-22 22:25:24 +00:00
def write_current_assets ( sheet , tax_report : TaxReport ) :
2024-12-27 21:14:39 +00:00
column_headers = [ ' Værdipapir ' , ' Mængde ' , ' Kurs ' , ' Værdi (DKK) ' ,
2024-12-22 22:25:24 +00:00
' Beskatningstype ' ]
2024-12-27 14:58:38 +00:00
add_headers ( sheet , column_headers )
2024-12-27 21:14:39 +00:00
row_idx = 2
2024-12-22 22:25:24 +00:00
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 )
2024-12-22 23:08:24 +00:00
#assert exchange_rate_asset_to_dkk is not None, asset
2024-12-22 22:25:24 +00:00
row = [
asset . raw_short_name ( ) ,
total_amount ,
2024-12-27 21:14:39 +00:00
exchange_rate_asset_to_dkk ,
f ' =B { row_idx } *C { row_idx } ' ,
2024-12-22 22:25:24 +00:00
TAX_TYPES [ asset ] ,
]
sheet . append ( row )
2024-12-27 21:14:39 +00:00
row_idx + = 1
2024-12-22 22:25:24 +00:00
del asset , positions , row , total_amount
2024-12-27 21:14:39 +00:00
sheet . column_dimensions [ ' A ' ] . width = 16
sheet . column_dimensions [ ' B ' ] . width = 16
sheet . column_dimensions [ ' C ' ] . width = 15
sheet . column_dimensions [ ' D ' ] . width = 15
sheet . column_dimensions [ ' E ' ] . width = 15
set_number_format ( sheet , ' C1:D6 ' )
2024-12-24 19:12:50 +00:00
def write_ledger_sheet ( sheet , tax_report : TaxReport ) :
2024-12-27 14:58:38 +00:00
assets = list ( { e . amount . asset for e in tax_report . ledger_entries } )
2024-12-24 19:12:50 +00:00
column_headers = [
' Tidspunkt (UTC) ' ,
' Type ' ,
' Værdipapir ' ,
' Mængde ' ,
' Kontoudbyder ' ,
2024-12-27 14:58:38 +00:00
' '
] + [ a . raw_short_name ( ) for a in assets ]
add_headers ( sheet , column_headers )
2024-12-24 19:12:50 +00:00
row_idx = 2
2024-12-27 14:58:38 +00:00
prev_entry_time = None
start_of_same_entry = 2
2024-12-24 19:12:50 +00:00
for ledger_entry in tax_report . ledger_entries :
2024-12-27 14:58:38 +00:00
entry_time = ledger_entry . time . replace ( tzinfo = None , microsecond = 0 , fold = 0 )
2024-12-24 19:12:50 +00:00
row = [
2024-12-27 14:58:38 +00:00
entry_time ,
2024-12-24 19:12:50 +00:00
ledger_entry . type ,
ledger_entry . amount . asset . raw_short_name ( ) ,
ledger_entry . amount . amount ,
ledger_entry . account_provider ,
2024-12-27 14:58:38 +00:00
' ' ,
2024-12-27 21:14:39 +00:00
] + [ ( ledger_entry . balance . amount if ledger_entry . amount . asset == a else ' ' ) for a in assets ]
2024-12-24 19:12:50 +00:00
sheet . append ( row )
2024-12-27 14:58:38 +00:00
2024-12-27 21:14:39 +00:00
if entry_time != prev_entry_time and prev_entry_time is not None :
2024-12-27 14:58:38 +00:00
# TODO:
sheet . merge_cells ( f ' A { start_of_same_entry } :A { row_idx - 1 } ' )
sheet [ f ' A { start_of_same_entry } ' ] . alignment = ALIGN_CENTER
start_of_same_entry = row_idx
2024-12-27 21:14:39 +00:00
prev_entry_time = entry_time
row_idx + = 1
2024-12-24 19:12:50 +00:00
del ledger_entry , row
2024-12-27 21:14:39 +00:00
# Styling
sheet . column_dimensions [ ' A ' ] . width = 20
sheet . column_dimensions [ ' B ' ] . width = 12
sheet . column_dimensions [ ' C ' ] . width = 13
sheet . column_dimensions [ ' D ' ] . width = 12
sheet . column_dimensions [ ' E ' ] . width = 12
sheet . column_dimensions [ ' F ' ] . width = 16
2024-12-27 14:58:38 +00:00
2024-12-27 21:14:39 +00:00
def write_fifo_sheet ( sheet , bought_and_sold_for : list , exchange_rate_at_time ) - > tuple [ str , str ] :
2024-12-24 19:12:50 +00:00
# TODO: Account for transfers
2024-12-22 22:25:24 +00:00
column_headers = [
2024-12-27 21:14:39 +00:00
' Købsdato (UTC) ' ,
' Salgsdato (UTC) ' ,
' Værdipapir ' ,
' Mængde ' ,
' Anskaffelseskurs ' ,
' Salgskurs ' ,
' Anskaffelsesværdi (DKK) ' ,
' Salgsværdi (DKK) ' ,
' Profit (DKK) ' , ' Tab (DKK) ' ,
2024-12-22 22:25:24 +00:00
]
2024-12-27 14:58:38 +00:00
add_headers ( sheet , column_headers )
2024-12-22 22:25:24 +00:00
row_idx = 2
2024-12-27 21:14:39 +00:00
for fifo_entry in bought_and_sold_for :
2024-12-22 22:25:24 +00:00
asset = fifo_entry . amount . asset
2024-12-27 21:14:39 +00:00
exchange_rate_dkk_bought = exchange_rate_at_time ( asset , fin_defs . DKK , fifo_entry . time_bought )
2024-12-22 22:25:24 +00:00
#assert exchange_rate_dkk_bought is not None, asset
2024-12-27 21:14:39 +00:00
exchange_rate_dkk_sold = exchange_rate_at_time ( asset , fin_defs . DKK , fifo_entry . time_sold )
2024-12-22 22:25:24 +00:00
#assert exchange_rate_dkk_bought is not None, asset
row = [
fifo_entry . time_bought . replace ( tzinfo = None ) ,
fifo_entry . time_sold . replace ( tzinfo = None ) ,
2024-12-27 21:14:39 +00:00
asset . raw_short_name ( ) ,
fifo_entry . amount . amount ,
exchange_rate_dkk_bought ,
exchange_rate_dkk_sold ,
f ' =F { row_idx } *D { row_idx } ' ,
f ' =E { row_idx } *D { row_idx } ' ,
f ' =max(0, G { row_idx } - H { row_idx } ) ' ,
f ' =max(0, H { row_idx } - G { row_idx } ) ' ,
2024-12-22 22:25:24 +00:00
]
sheet . append ( row )
row_idx + = 1
del fifo_entry , asset , row
2024-12-27 21:14:39 +00:00
# Sums
sum_profit = f ' I { row_idx } '
sum_loss = f ' J { row_idx } '
sheet . append ( [
' ' ,
' ' ,
' ' ,
' ' ,
' ' ,
' ' ,
' ' ,
' ' ,
f ' =sum(I2:I { row_idx - 1 } ) ' ,
f ' =sum(J2:J { row_idx - 1 } ) '
] )
for row in sheet [ f ' A { row_idx } : { sum_loss } ' ] :
for cell in row :
cell . font = FONT_BOLD
cell . border = BORDER_SUM
# Styling
sheet . column_dimensions [ ' A ' ] . width = 20
sheet . column_dimensions [ ' B ' ] . width = 20
sheet . column_dimensions [ ' C ' ] . width = 16
sheet . column_dimensions [ ' D ' ] . width = 10
sheet . column_dimensions [ ' E ' ] . width = 15
sheet . column_dimensions [ ' F ' ] . width = 15
sheet . column_dimensions [ ' G ' ] . width = 26
sheet . column_dimensions [ ' H ' ] . width = 20
sheet . column_dimensions [ ' I ' ] . width = 15
sheet . column_dimensions [ ' J ' ] . width = 15
set_number_format ( sheet , f ' G1:J { row_idx } ' )
return ( sum_profit , sum_loss )
def write_overview ( sheet , tax_report : TaxReport , sums ) :
sheet [ ' A1 ' ] = ' Krypto-Skatterapport 2024 '
sheet . merge_cells ( ' A1:B1 ' )
sheet [ ' A1 ' ] . font = FONT_HEADER
sheet [ ' A1 ' ] . alignment = ALIGN_CENTER
sheet [ ' A2 ' ] = ' Jon Michael Aanes '
sheet . merge_cells ( ' A2:B2 ' )
sheet [ ' A2 ' ] . font = FONT_HEADER
sheet [ ' A2 ' ] . alignment = ALIGN_CENTER
# Forklaring
sheet [ ' A4 ' ] = ' Denne rapport afdækker aktiviteten på en række krypto-børser, med henblik på at give en komplet oversigt af handler og nuværende beholdning. '
sheet . merge_cells ( ' A4:B4 ' )
sheet [ ' A4 ' ] . alignment = ALIGN_WRAP
sheet [ ' A6 ' ] = ' Rapporten er splittet op I følgende ark: '
sheet . merge_cells ( ' A6:B6 ' )
sheet [ ' A7 ' ] = ' Overview '
sheet [ ' B7 ' ] = ' Denne oversigt '
sheet [ ' A8 ' ] = ' Current Assets '
sheet [ ' B8 ' ] = ' Afdækker nuværende beholdning. '
sheet [ ' A9 ' ] = ' Ledger '
sheet [ ' B9 ' ] = ' Afdækker alle handler og overførsler. '
sheet [ ' A10 ' ] = ' FIFO Stablecoin '
sheet [ ' B10 ' ] = ' Beregner profit og tab for stablecoins ved FIFO-pricippet. '
sheet [ ' A11 ' ] = ' FIFO Kryptovaluta '
sheet [ ' B11 ' ] = ' Beregner profit og tab for kryptovaluta ved FIFO-pricippet. '
sheet [ ' A12 ' ] = ' FIFO Fiat '
sheet [ ' B12 ' ] = ' Beregner profit og tab for udenlandsk valuta ved FIFO-pricippet. '
# Beregnet kolonner
sheet [ ' D6 ' ] = ' Beregnet Skatte Kolonnner ' # TODO
sheet . merge_cells ( ' D6:E6 ' )
sheet [ ' A4 ' ] . alignment = ALIGN_CENTER
sheet [ ' D7 ' ] = ' Kolonne ' # TODO
sheet [ ' E7 ' ] = ' Værdi '
sheet [ ' D8 ' ] = ' Profit Krypto '
sheet [ ' E8 ' ] = ' =$ \' FIFO Kryptovaluta \' . ' + sums [ ' Kryptovaluta ' ] [ 0 ]
sheet [ ' D9 ' ] = ' Tab Krypto '
sheet [ ' E9 ' ] = ' =$ \' FIFO Kryptovaluta \' . ' + sums [ ' Kryptovaluta ' ] [ 1 ]
sheet [ ' D10 ' ] = ' Profit Stable Coin '
sheet [ ' E10 ' ] = ' =$ \' FIFO Stable Coin \' . ' + sums [ ' Stable Coin ' ] [ 0 ]
sheet [ ' D11 ' ] = ' Profit Stable Coin '
sheet [ ' E11 ' ] = ' =$ \' FIFO Stable Coin \' . ' + sums [ ' Stable Coin ' ] [ 1 ]
# Disclaimer
#sheet['A13'] = 'Bemærk at rapporten kan være ufyldtesgørende, af forskellige årsager, inklusiv: Fejl i data fra krypto-børser'
# Styling
sheet . column_dimensions [ ' A ' ] . width = 20
sheet . column_dimensions [ ' B ' ] . width = 70
sheet . row_dimensions [ 4 ] . height = 60
2024-12-22 22:25:24 +00:00
def produce_excel_report ( report : TaxReport , output_path : Path ) :
workbook = openpyxl . Workbook ( )
2024-12-27 21:14:39 +00:00
# Oversigt
workbook . active . title = ' Oversigt '
workbook . active . page_setup . fitToWidth = 1
# Current assets
sheet_ledger = workbook . create_sheet ( ' Current Assets ' )
workbook . active . page_setup . fitToWidth = 1
write_current_assets ( sheet_ledger , report )
2024-12-22 22:25:24 +00:00
2024-12-27 21:14:39 +00:00
# Ledger
2024-12-24 19:12:50 +00:00
sheet_ledger = workbook . create_sheet ( ' Ledger ' )
2024-12-27 21:14:39 +00:00
sheet_ledger . page_setup . fitToWidth = 1
2024-12-24 19:12:50 +00:00
write_ledger_sheet ( sheet_ledger , report )
2024-12-27 21:14:39 +00:00
# FIFO
sums = { }
for tax_type in set ( TAX_TYPES . values ( ) ) :
sheet_fifo = workbook . create_sheet ( ' FIFO ' + tax_type )
sheet_fifo . page_setup . fitToWidth = 1
bought_and_sold_for = [ entry for entry in report . bought_and_sold_for if TAX_TYPES [ entry . amount . asset ] == tax_type ]
sums [ tax_type ] = write_fifo_sheet ( sheet_fifo , bought_and_sold_for , report . exchange_rate_at_time )
del tax_type , sheet_fifo
write_overview ( workbook . active , report , sums )
2024-12-22 22:25:24 +00:00
workbook . save ( output_path )