1
0

Improved price parsing
Some checks failed
Python Ruff Code Quality / ruff (push) Failing after 23s
Run Python tests (through Pytest) / Test (push) Failing after 24s
Verify Python project can be installed, loaded and have version checked / Test (push) Successful in 21s

This commit is contained in:
Jon Michael Aanes 2025-05-29 18:42:13 +02:00
parent 5c29ea63da
commit 79b9edc5d6
2 changed files with 38 additions and 20 deletions

View File

@ -214,16 +214,17 @@ class FiatCurrency(Currency):
@staticmethod
def from_currency_symbol(symbol: str) -> 'Currency | None':
for currency, currency_symbol in CURRENCY_SYMBOLS.items():
if currency_symbol == symbol:
for currency, currency_display in ASSET_DISPLAY.items():
if currency_display.symbol == symbol:
return currency
return None
def to_currency_symbol(self) -> str:
return CURRENCY_SYMBOLS[self]
return ASSET_DISPLAY[self].symbol
FiatCurrency.DKK = FiatCurrency('DKK')
FiatCurrency.SEK = FiatCurrency('SEK')
FiatCurrency.NOK = FiatCurrency('NOK')
FiatCurrency.USD = FiatCurrency('USD')
FiatCurrency.EUR = FiatCurrency('EUR')
@ -353,6 +354,7 @@ CURRENCY_CODES = {
'CHF',
'ZAR',
'CZK',
'SEK',
]
}
@ -364,15 +366,25 @@ WELL_KNOWN_SYMBOLS = (
| {'SPX500': SPX, 'SP500': SPX, 'Nasdaq 100': NDX}
)
CURRENCY_SYMBOLS: dict[Currency, str] = {
USD: '$',
EUR: '',
FiatCurrency.GBP: '£',
BTC: '',
FiatCurrency.JPY: '¥',
}
ASSET_PREFIX: dict[Asset, str] = CURRENCY_SYMBOLS # TODO: Remove at some point.
@dataclasses.dataclass(frozen=True)
class AssetDisplay:
symbol: str | None
prefix: str | None
suffix: str | None
UNKNOWN_ASSET_DISPLAY = AssetDisplay(None,None,None)
ASSET_DISPLAY: dict[Asset, AssetDisplay] = {
USD: AssetDisplay('$','$', None),
EUR: AssetDisplay('','', None),
FiatCurrency.GBP: AssetDisplay('£','£', None),
BTC: AssetDisplay('','',None),
FiatCurrency.JPY: AssetDisplay('¥','¥', None),
FiatCurrency.DKK: AssetDisplay(None,None, 'kr.'),
FiatCurrency.SEK: AssetDisplay(None,None, 'kr.'),
FiatCurrency.NOK: AssetDisplay(None,None, 'kr.'),
}
EXCHANGES = [
NYSE,
@ -501,10 +513,11 @@ class AssetAmount:
elif abs_amount >= THREE_DECIMALS_UNDER_THIS_AMOUNT:
specificity = '2'
prefix = ASSET_PREFIX.get(self.asset, '')
return ('{sign}{prefix}{amount:.' + specificity + 'f} {name}').format(
display = ASSET_DISPLAY.get(self.asset, UNKNOWN_ASSET_DISPLAY)
return ('{sign}{prefix}{amount:.' + specificity + 'f}{suffix} {name}').format(
sign='-' if (self.amount == self.amount and self.amount < 0) else '',
prefix=prefix,
prefix=display.prefix or '',
suffix=f' {display.suffix}' if display.suffix else '',
amount=abs_amount,
name=self.asset.raw_short_name(),
)

View File

@ -4,7 +4,7 @@ from decimal import Decimal
from .data import (
CURRENCY_CODES,
CURRENCY_SYMBOLS,
ASSET_DISPLAY,
Asset,
AssetAmount,
FiatCurrency,
@ -30,7 +30,7 @@ def parse_amount(price: str) -> Decimal:
RE_CURRENCY_CODES = '(?P<code>' + '|'.join(re.escape(c) for c in CURRENCY_CODES) + ')'
RE_CURRENCY_SYMBOLS = (
r'(?P<sym>[' + ''.join(re.escape(c) for c in CURRENCY_SYMBOLS.values()) + '])'
r'(?P<sym>[' + ''.join(re.escape(c.symbol) for c in ASSET_DISPLAY.values() if c.symbol) + '])'
)
RE_SYM_AMOUNT_CODE = re.compile(
@ -77,6 +77,8 @@ RE_AMOUNT_SUFFIX_KR = re.compile(
VARIANTS_OF_FREE = frozenset(['free', 'gratis', 'gives væk'])
KRONER_USING_ASSETS = {k for k,c in ASSET_DISPLAY.items() if c.suffix == 'kr.'}
def parse_price(text: str, default_currency: Asset) -> AssetAmount | None:
"""
@ -94,6 +96,7 @@ def parse_price(text: str, default_currency: Asset) -> AssetAmount | None:
return AssetAmount(default_currency, Decimal(0))
code, sym, amount_text = None, None, None
currency = None
if m := RE_SYM_AMOUNT_CODE.fullmatch(text):
code, sym, amount_text = m.group('code'), m.group('sym'), m.group('amount')
@ -104,11 +107,13 @@ def parse_price(text: str, default_currency: Asset) -> AssetAmount | None:
elif m := RE_AMOUNT_CODE.fullmatch(text):
code, amount_text = m.group('code'), m.group('amount')
elif m := (RE_KR_AMOUNT.fullmatch(text) or RE_AMOUNT_SUFFIX_KR.fullmatch(text)):
code, amount_text = 'DKK', m.group('amount')
amount_text = m.group('amount')
currency = default_currency if default_currency in KRONER_USING_ASSETS else FiatCurrency.DKK
else:
logger.debug('Unknown format: %s', text)
return None
if currency is None:
currency = (
CURRENCY_CODES[code.upper()] if code else FiatCurrency.from_currency_symbol(sym)
)