fx.py 1.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. from __future__ import annotations
  2. import dataclasses
  3. import sys
  4. import typing
  5. import cache
  6. import roi
  7. def main() -> None:
  8. from_cx, to_cx = sys.argv[1:]
  9. raw_prices: list[RawPrice] = cache.get('https://refined-prun.github.io/refined-prices/all.json')
  10. materials: dict[str, roi.Material] = {m['Ticker']: m for m in cache.get('https://api.prunplanner.org/data/materials')}
  11. bids: dict[str, float] = {}
  12. for price in raw_prices:
  13. if price['ExchangeCode'] != to_cx or price['MMBuy'] is None:
  14. continue
  15. assert price['Bid'] is not None
  16. bids[price['MaterialTicker']] = price['Bid']
  17. rates: list[FX] = []
  18. for price in raw_prices:
  19. if price['ExchangeCode'] != from_cx or price['Ask'] is None or (to_price := bids.get(price['MaterialTicker'])) is None:
  20. continue
  21. rates.append(FX(price['MaterialTicker'], to_price, (price['Ask'] - to_price) / to_price))
  22. rates.sort()
  23. for rate in rates:
  24. print(f'{rate.ticker:4} {rate.rate:8.5f}', end=' ')
  25. mat = materials[rate.ticker]
  26. if mat['Weight'] >= mat['Volume']:
  27. print(f'{rate.to_price / mat["Weight"]:9,.0f}/t')
  28. else:
  29. print(f'{rate.to_price / mat["Volume"]:9,.0f}/m³')
  30. class RawPrice(typing.TypedDict):
  31. MaterialTicker: str
  32. ExchangeCode: str
  33. Ask: float | None
  34. Bid: float | None
  35. MMBuy: float | None
  36. @dataclasses.dataclass(eq=False, frozen=True, slots=True)
  37. class FX:
  38. ticker: str
  39. to_price: float
  40. rate: float
  41. def __lt__(self, other: FX) -> bool:
  42. return self.rate < other.rate
  43. if __name__ == '__main__':
  44. main()