Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Increase decimal precision. #305

Merged
merged 3 commits into from
Jan 22, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
25 changes: 12 additions & 13 deletions cgt_calc/parsers/schwab_equity_award_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,19 @@

from cgt_calc.exceptions import ParsingError
from cgt_calc.model import ActionType, BrokerTransaction
from cgt_calc.util import round_decimal

# Delay between a (sale) trade, and when it is settled.
SETTLEMENT_DELAY = 2 * CustomBusinessDay(calendar=USFederalHolidayCalendar())

# We want enough decimals to cover what Schwab gives us (up to 4 decimals)
# divided by the share-split factor (20), so we keep 6 decimals.
# We don't want more decimals than necessary or we risk converting
# the float number format approximations into Decimals
# (e.g. a number 1.0001 in JSON may become 1.00010001 when parsed
# into float, but we want to get Decimal('1.0001'))
ROUND_DIGITS = 6

JsonRowType = Any # type: ignore


Expand Down Expand Up @@ -84,22 +93,12 @@ def action_from_str(label: str) -> ActionType:
raise ParsingError("schwab transactions", f"Unknown action: {label}")


def _round_decimal(num: Decimal) -> Decimal:
# We want enough decimals to cover what Schwab gives us (2 decimals)
# divided by the share-split factor (20), so we keep 4 decimals.
# We don't want more decimals than necessary or we risk converting
# the float number format approximations into Decimals
# (e.g. a number 1.0001 in JSON may become 1.00010001 when parsed
# into float, but we want to get Decimal('1.0001'))
return num.quantize(Decimal(".0001")).normalize()


def _get_decimal_or_default(
row: JsonRowType, key: str, default: Decimal | None = None
) -> Decimal | None:
if key in row and row[key]:
if isinstance(row[key], float):
return _round_decimal(Decimal.from_float(row[key]))
return round_decimal(Decimal.from_float(row[key]), ROUND_DIGITS)

return Decimal(row[key])

Expand Down Expand Up @@ -214,8 +213,8 @@ def _normalize_split(self) -> None:
and self.price > 175
and self.quantity
):
self.price = _round_decimal(self.price / split_factor)
self.quantity = _round_decimal(self.quantity * split_factor)
self.price = round_decimal(self.price / split_factor, ROUND_DIGITS)
self.quantity = round_decimal(self.quantity * split_factor, ROUND_DIGITS)


def read_schwab_equity_award_json_transactions(
Expand Down
2 changes: 1 addition & 1 deletion tests/test_schwab_equity_award_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def test_schwab_transaction() -> None:

assert transactions[1].date == datetime.date(2022, 6, 10)
assert transactions[1].action == ActionType.SELL
assert transactions[1].quantity == Decimal("62.6015")
assert transactions[1].quantity == Decimal("62.601495")
assert transactions[1].price == Decimal("113.75")
assert transactions[1].fees == Decimal("0.17")

Expand Down