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 ' ) )
2024-12-27 21:38:28 +00:00
BORDER_TOP = openpyxl . styles . Border ( top = openpyxl . styles . Side ( border_style = ' thin ' , color = ' FF000000 ' ) )
2024-12-27 21:14:39 +00:00
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
2024-12-28 00:02:37 +00:00
def set_number_format ( sheet , range , format = " 0.00 " ) :
2024-12-27 21:14:39 +00:00
for row in sheet [ range ] :
for cell in row :
2024-12-28 00:02:37 +00:00
cell . number_format = format
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-28 00:02:37 +00:00
column_headers = [ ' Værdipapir ' , ' Mængde ' , ' Kurs ' , ' Sum (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-28 00:02:37 +00:00
exchange_rate_asset_to_dkk or ' Unknown ' ,
2024-12-27 21:14:39 +00:00
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:38:28 +00:00
sheet . column_dimensions [ ' A ' ] . width = 14
sheet . column_dimensions [ ' B ' ] . width = 12
sheet . column_dimensions [ ' C ' ] . width = 12
2024-12-27 21:14:39 +00:00
sheet . column_dimensions [ ' D ' ] . width = 15
2024-12-27 21:38:28 +00:00
sheet . column_dimensions [ ' E ' ] . width = 20
2024-12-27 21:14:39 +00:00
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
' '
2024-12-28 00:02:37 +00:00
] + [ a . raw_short_name ( ) for a in assets ] + [ ' ' , ' Kommentarer ' ]
2024-12-27 14:58:38 +00:00
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:38:28 +00:00
]
for a_idx , a in enumerate ( assets ) :
above_cell = f ' { get_column_letter ( 7 + a_idx ) } { row_idx - 1 } '
if row_idx == 2 :
above_cell = 0
if ledger_entry . amount . asset == a :
row . append ( f ' = { above_cell } + D { row_idx } ' )
else :
row . append ( f ' = { above_cell } ' )
del a
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:38:28 +00:00
for row in sheet [ f ' A { row_idx } :J { row_idx } ' ] :
for cell in row :
cell . border = BORDER_TOP
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 ' ,
2024-12-28 00:02:37 +00:00
' Købssum (DKK) ' ,
' Salgssum (DKK) ' ,
' Gevinst (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 ,
2024-12-28 00:02:37 +00:00
exchange_rate_dkk_bought or ' Unknown ' ,
exchange_rate_dkk_sold or ' Unknown ' ,
f ' =D { row_idx } *E { row_idx } ' ,
f ' =D { row_idx } *F { row_idx } ' ,
2024-12-27 21:14:39 +00:00
f ' =max(0, H { row_idx } - G { row_idx } ) ' ,
2024-12-28 00:02:37 +00:00
f ' =max(0, G { row_idx } - H { 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
2024-12-27 21:38:28 +00:00
sheet . column_dimensions [ ' E ' ] . width = 18
2024-12-27 21:14:39 +00:00
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 '
2024-12-28 00:02:37 +00:00
sheet [ ' B10 ' ] = ' Beregner gevinst og tab for stablecoins ved FIFO-pricippet. '
2024-12-27 21:14:39 +00:00
sheet [ ' A11 ' ] = ' FIFO Kryptovaluta '
2024-12-28 00:02:37 +00:00
sheet [ ' B11 ' ] = ' Beregner gevinst og tab for kryptovaluta ved FIFO-pricippet. '
2024-12-27 21:14:39 +00:00
sheet [ ' A12 ' ] = ' FIFO Fiat '
2024-12-28 00:02:37 +00:00
sheet [ ' B12 ' ] = ' Beregner gevinst og tab for udenlandsk valuta ved FIFO-pricippet. '
2024-12-27 21:14:39 +00:00
# Beregnet kolonner
2024-12-28 00:02:37 +00:00
sheet [ ' D6 ' ] = ' Beregnet Skatte Rubrikker ' # TODO
2024-12-27 21:14:39 +00:00
sheet . merge_cells ( ' D6:E6 ' )
sheet [ ' A4 ' ] . alignment = ALIGN_CENTER
2024-12-28 00:02:37 +00:00
sheet [ ' D7 ' ] = ' Rubrik ' # TODO
2024-12-27 21:14:39 +00:00
sheet [ ' E7 ' ] = ' Værdi '
2024-12-28 00:02:37 +00:00
sheet [ ' D8 ' ] = ' Rubrik 20 '
2024-12-27 21:14:39 +00:00
sheet [ ' E8 ' ] = ' =$ \' FIFO Kryptovaluta \' . ' + sums [ ' Kryptovaluta ' ] [ 0 ]
2024-12-28 00:02:37 +00:00
sheet [ ' F8 ' ] = ' (Gevinst ved kryptospekulation) '
sheet [ ' G8 ' ] = ' Kilde: https://skat.dk/borger/aktier-og-andre-vaerdipapirer/skat-paa-krypto-kend-reglerne-saa-du-undgaar-et-skattesmaek/beregn-og-oplys-gevinst-og-tab-paa-krypto '
sheet [ ' D9 ' ] = ' Rubrik 58 '
2024-12-27 21:14:39 +00:00
sheet [ ' E9 ' ] = ' =$ \' FIFO Kryptovaluta \' . ' + sums [ ' Kryptovaluta ' ] [ 1 ]
2024-12-28 00:02:37 +00:00
sheet [ ' F9 ' ] = ' (Tab ved kryptospekulation) '
sheet [ ' G10 ' ] = ' Kilde: https://skat.dk/borger/aktier-og-andre-vaerdipapirer/skat-paa-krypto-kend-reglerne-saa-du-undgaar-et-skattesmaek/beregn-og-oplys-gevinst-og-tab-paa-krypto '
sheet [ ' D10 ' ] = ' Rubrik 346 '
sheet [ ' E10 ' ] = f " =$ ' FIFO Stable Coin ' . { sums [ ' Stable Coin ' ] [ 0 ] } ' -$ ' FIFO Stable Coin ' . { sums [ ' Stable Coin ' ] [ 1 ] } ' "
sheet [ ' F10 ' ] = ' (Gevinst og Tab ved Stable Coin / Financiel Kontrakt) '
sheet [ ' G10 ' ] = ' Kilde: https://skat.dk/borger/aktier-og-andre-vaerdipapirer/skat-paa-krypto-kend-reglerne-saa-du-undgaar-et-skattesmaek/stablecoins-finansielle-kontrakter '
2024-12-27 21:14:39 +00:00
# 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-28 00:02:37 +00:00
sheet . column_dimensions [ ' D ' ] . width = 20
sheet . column_dimensions [ ' E ' ] . width = 20
sheet . column_dimensions [ ' F ' ] . width = 50
sheet . column_dimensions [ ' G ' ] . width = 80
2024-12-27 21:14:39 +00:00
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 )