Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pit38/domain/crypto/profit_calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import List
from loguru import logger

from pit38.domain.currency_exchange_service.currencies import FiatValue
from pit38.domain.currency_exchange_service.currencies import Currency, FiatValue
from pit38.domain.currency_exchange_service.exchanger import Exchanger
from pit38.domain.tax_service.profit_per_year import ProfitPerYear
from pit38.domain.transactions import Transaction, Action
Expand All @@ -22,7 +22,7 @@ def profit_per_year(self, transactions: List[Transaction]) -> ProfitPerYear:

def _sum_transactions_per_year(self, transactions: List[Transaction], transaction_type: Action) \
-> defaultdict[int, FiatValue]:
transactions_sum_per_year: defaultdict[int, FiatValue] = defaultdict(lambda: FiatValue(0))
transactions_sum_per_year: defaultdict[int, FiatValue] = defaultdict(lambda: FiatValue.zero(Currency.ZLOTY))
for transaction in transactions:
if transaction.action != transaction_type:
continue
Expand Down
6 changes: 5 additions & 1 deletion pit38/domain/currency_exchange_service/currencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,14 @@ def parse_currency(currency: str) -> Currency:


class FiatValue:
def __init__(self, amount: float = 0, currency: Currency = Currency.ZLOTY):
def __init__(self, amount: float, currency: Currency):
self.amount = amount
self.currency = currency

@classmethod
def zero(cls, currency: Currency):
return cls(0, currency)

def __add__(self, other):
if self.currency != other.currency:
raise InvalidCurrencyException(f"Cannot add different currencies: {self.currency} and {other.currency}")
Expand Down
4 changes: 2 additions & 2 deletions pit38/domain/stock/profit/per_stock_calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from loguru import logger

from pit38.domain.currency_exchange_service.currencies import FiatValue
from pit38.domain.currency_exchange_service.currencies import Currency, FiatValue
from pit38.domain.currency_exchange_service.exchanger import Exchanger
from pit38.domain.tax_service.profit_per_year import ProfitPerYear
from pit38.domain.stock.queue import Queue
Expand Down Expand Up @@ -51,7 +51,7 @@ def _get_company_name(self, transaction: List[Transaction]) -> str:

def _calculate_cost_for_sell(self, buy_queue: Queue, transaction: Transaction) -> FiatValue:
stock_amount_to_account = transaction.asset.amount
cost = FiatValue(0)
cost = FiatValue.zero(Currency.ZLOTY)

while stock_amount_to_account > self.EPSILON:
try:
Expand Down
12 changes: 6 additions & 6 deletions pit38/domain/tax_service/crypto_tax_calculator.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from pit38.domain.currency_exchange_service.currencies import FiatValue
from pit38.domain.currency_exchange_service.currencies import Currency, FiatValue
from pit38.domain.tax_service.profit_per_year import ProfitPerYear
from pit38.domain.tax_service.tax_year_result import TaxYearResult

Expand All @@ -17,12 +17,12 @@ def calculate_tax_per_year(
deductible_loss: float = -1,
) -> TaxYearResult:
if deductible_loss != -1:
loss = FiatValue(deductible_loss)
loss = FiatValue(deductible_loss, Currency.ZLOTY)
else:
loss = self._deductible_loss(profit_per_year, tax_year)

profit_in_tax_year = profit_per_year.get_profit(tax_year)
if loss > FiatValue(0):
if loss > FiatValue.zero(Currency.ZLOTY):
profit_in_tax_year = profit_in_tax_year - loss

return TaxYearResult(
Expand All @@ -37,15 +37,15 @@ def calculate_tax_per_year(
def _deductible_loss(
self, profit_per_year: ProfitPerYear, tax_year: int
) -> FiatValue:
accumulated_loss = FiatValue(0)
accumulated_loss = FiatValue.zero(Currency.ZLOTY)
for year in sorted(y for y in profit_per_year.all_years() if y < tax_year):
profit_this_year = profit_per_year.get_profit(year)
if profit_this_year >= accumulated_loss:
accumulated_loss = FiatValue(0)
accumulated_loss = FiatValue.zero(Currency.ZLOTY)
continue
accumulated_loss = accumulated_loss - profit_this_year
return accumulated_loss

def _calculate_tax(self, profit: FiatValue) -> FiatValue:
zero = FiatValue(0)
zero = FiatValue.zero(Currency.ZLOTY)
return profit * self.tax_rate if profit > zero else zero
10 changes: 5 additions & 5 deletions pit38/domain/tax_service/loss_deduction.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
from dataclasses import dataclass, field
from typing import List

from pit38.domain.currency_exchange_service.currencies import FiatValue
from pit38.domain.currency_exchange_service.currencies import Currency, FiatValue
from pit38.domain.tax_service.profit_per_year import ProfitPerYear

FIVE_MILLION = FiatValue(5_000_000)
ZERO = FiatValue(0)
FIVE_MILLION = FiatValue(5_000_000, Currency.ZLOTY)
ZERO = FiatValue.zero(Currency.ZLOTY)


@dataclass
class LossRecord:
year: int
original_amount: FiatValue
already_deducted: FiatValue = field(default_factory=FiatValue)
already_deducted: FiatValue = field(default_factory=lambda: FiatValue.zero(Currency.ZLOTY))

@property
def remaining(self) -> FiatValue:
Expand Down Expand Up @@ -44,7 +44,7 @@ def calculate_deductible_loss(
if profit <= ZERO:
return ZERO

total_deducted = FiatValue(0)
total_deducted = FiatValue.zero(Currency.ZLOTY)
remaining_profit = profit

for loss in losses:
Expand Down
6 changes: 3 additions & 3 deletions pit38/domain/tax_service/profit_per_year.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from collections import defaultdict

from pit38.domain.currency_exchange_service.currencies import FiatValue
from pit38.domain.currency_exchange_service.currencies import Currency, FiatValue


class ProfitPerYear:
def __init__(self, income: defaultdict[int, FiatValue] = None, cost: defaultdict[int, FiatValue] = None):
self.income = income if income is not None else defaultdict(FiatValue)
self.cost = cost if cost is not None else defaultdict(FiatValue)
self.income = income if income is not None else defaultdict(lambda: FiatValue.zero(Currency.ZLOTY))
self.cost = cost if cost is not None else defaultdict(lambda: FiatValue.zero(Currency.ZLOTY))

def add_income(self, year: int, value: FiatValue):
self.income[year] += value
Expand Down
8 changes: 4 additions & 4 deletions pit38/domain/tax_service/stock_tax_calculator.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from pit38.domain.currency_exchange_service.currencies import FiatValue
from pit38.domain.currency_exchange_service.currencies import Currency, FiatValue
from pit38.domain.tax_service.loss_deduction import StockLossDeductionStrategy
from pit38.domain.tax_service.profit_per_year import ProfitPerYear
from pit38.domain.tax_service.tax_year_result import TaxYearResult
Expand All @@ -16,14 +16,14 @@ def calculate_tax_per_year(
deductible_loss: float = -1,
) -> TaxYearResult:
if deductible_loss != -1:
loss = FiatValue(deductible_loss)
loss = FiatValue(deductible_loss, Currency.ZLOTY)
else:
loss = self._loss_strategy.calculate_deductible_loss(
profit_per_year, tax_year
)

profit_in_tax_year = profit_per_year.get_profit(tax_year)
if loss > FiatValue(0):
if loss > FiatValue.zero(Currency.ZLOTY):
profit_in_tax_year = profit_in_tax_year - loss

return TaxYearResult(
Expand All @@ -36,5 +36,5 @@ def calculate_tax_per_year(
)

def _calculate_tax(self, profit: FiatValue) -> FiatValue:
zero = FiatValue(0)
zero = FiatValue.zero(Currency.ZLOTY)
return profit * self.tax_rate if profit > zero else zero
3 changes: 3 additions & 0 deletions tests/test_currencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ def test_mul_by_non_numeric(self):
with self.assertRaises(ValueError):
a * "a"

def test_zero(self):
self.assertEqual(FiatValue.zero(Currency.DOLLAR), FiatValue(0, Currency.DOLLAR))

def test_gt(self):
a = usd(100)
b = usd(200)
Expand Down
Loading