buy.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. from __future__ import annotations
  2. import collections
  3. import concurrent.futures
  4. import dataclasses
  5. import typing
  6. import cache
  7. from config import config
  8. import supply
  9. if typing.TYPE_CHECKING:
  10. import market
  11. def main() -> None:
  12. with concurrent.futures.ThreadPoolExecutor() as executor:
  13. futures = [
  14. executor.submit(get_raw_prices),
  15. executor.submit(get_total_buy), # what we need to buy
  16. executor.submit(supply.warehouse_inventory), # what we have
  17. executor.submit(get_planet_exports),
  18. executor.submit(get_bids), # what we already are bidding for
  19. ]
  20. raw_prices, buy, warehouse, exports, (bids, orders) = (f.result() for f in futures)
  21. executor.shutdown()
  22. # what's left to buy
  23. materials: list[Material] = []
  24. for mat, amount in buy.items():
  25. remaining = max(amount - bids[mat] - warehouse.get(mat, 0) - exports.get(mat, 0), 0)
  26. price = raw_prices[mat]
  27. if price['Bid'] is None or price['Ask'] is None:
  28. print(mat, 'has no bid/ask')
  29. continue
  30. spread = price['Ask'] - price['Bid']
  31. materials.append(Material(mat, amount=amount, bids=bids[mat], have=warehouse.get(mat, 0) + exports.get(mat, 0),
  32. spread=spread, savings=spread * remaining))
  33. materials.sort(reverse=True)
  34. print('mat want bids have buy savings')
  35. for m in materials:
  36. buy = max(m.amount - m.bids - m.have, 0)
  37. print(f'{m.ticker:4} {m.amount:>5} {m.bids:>5} {m.have:>5} {buy:>5} {m.savings:8.0f}')
  38. # deposits of current bids
  39. orders.sort(key=lambda order: order['Limit'] * order['Amount'], reverse=True)
  40. print('\ncurrent bid deposits:')
  41. for order in orders:
  42. print(f"{order['MaterialTicker']:4} {order['Limit'] * order['Amount']:7,.0f}")
  43. def get_raw_prices() -> typing.Mapping[str, market.RawPrice]:
  44. return {p['MaterialTicker']: p
  45. for p in cache.get('https://refined-prun.github.io/refined-prices/all.json') if p['ExchangeCode'] == 'IC1'}
  46. def get_total_buy() -> typing.Mapping[str, int]:
  47. planets = [supply.Planet(fio_burn) for fio_burn in cache.get('https://rest.fnar.net/fioweb/burn/user/' + config.username,
  48. headers={'Authorization': config.fio_api_key})]
  49. buy: dict[str, int] = collections.defaultdict(int)
  50. for planet in planets:
  51. for mat, amount in planet.supply_for_days(7).items():
  52. buy[mat] += amount
  53. return buy
  54. def get_planet_exports() -> typing.Mapping[str, int]:
  55. fio_burn: typing.Sequence[supply.FIOBurn] = cache.get('https://rest.fnar.net/fioweb/burn/user/' + config.username,
  56. headers={'Authorization': config.fio_api_key})
  57. avail = collections.defaultdict(int)
  58. for burn in fio_burn:
  59. planet = supply.Planet(burn)
  60. for mat in planet.exporting:
  61. avail[mat] += planet.inventory.get(mat, 0)
  62. return avail
  63. def get_bids() -> tuple[typing.Mapping[str, int], list[market.ExchangeOrder]]:
  64. orders: typing.Sequence[market.ExchangeOrder] = cache.get('https://rest.fnar.net/cxos/' + config.username,
  65. headers={'Authorization': config.fio_api_key})
  66. orders = [order for order in orders
  67. if order['OrderType'] == 'BUYING' and order['Status'] != 'FILLED' and order['ExchangeCode'] == 'IC1']
  68. bids = collections.defaultdict(int)
  69. for order in orders:
  70. bids[order['MaterialTicker']] += order['Amount']
  71. return bids, orders
  72. @dataclasses.dataclass(eq=False, slots=True)
  73. class Material:
  74. ticker: str
  75. amount: int
  76. bids: int
  77. have: int
  78. spread: float
  79. savings: float
  80. def __lt__(self, o: Material) -> bool:
  81. return self.savings< o.savings
  82. if __name__ == '__main__':
  83. main()