| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- from __future__ import annotations
- import collections
- import concurrent.futures
- import dataclasses
- import json
- import math
- import sys
- import typing
- import cache
- from config import config
- import supply
- if typing.TYPE_CHECKING:
- import market
- def main() -> None:
- days = int(sys.argv[1])
- with concurrent.futures.ThreadPoolExecutor() as executor:
- futures = [
- executor.submit(get_raw_prices),
- executor.submit(get_total_buy, days), # what we need to buy
- executor.submit(supply.warehouse_inventory), # what we have
- executor.submit(get_planet_exports_and_ship_storage),
- executor.submit(get_bids), # what we already are bidding for
- ]
- raw_prices, buy, warehouse, exports, (bids, orders) = (f.result() for f in futures)
- executor.shutdown()
- # what's left to buy
- materials: list[Material] = []
- price_limits: dict[str, int] = {}
- for mat, amount in buy.items():
- remaining = max(amount - bids[mat] - warehouse.get(mat, 0) - exports.get(mat, 0), 0)
- price = raw_prices[mat]
- if price['Bid'] is None or price['Ask'] is None:
- print(mat, 'has no bid/ask')
- continue
- spread = price['Ask'] - price['Bid']
- materials.append(Material(mat, amount=amount, bids=bids[mat], have=warehouse.get(mat, 0) + exports.get(mat, 0),
- spread=spread, savings=spread * remaining))
- epsilon = 10 ** (int(math.log10(price['Bid'])) - 2)
- price_limits[mat] = price['Bid'] + 2 * epsilon
- materials.sort(reverse=True)
- to_buy: dict[str, int] = {}
- print('mat want bids have buy savings')
- for m in materials:
- buy = max(m.amount - m.bids - m.have, 0)
- if m.bids == 0 and buy > 0:
- bids = f'\033[91m{m.bids:5}\033[0m'
- else:
- bids = str(m.bids).rjust(5)
- print(f'{m.ticker:4} {m.amount:>5} {bids} {m.have:>5} {buy:>5} {m.savings:8.0f}')
- if buy > 0:
- to_buy[m.ticker] = buy
- print('\n' + json.dumps({
- 'actions': [
- {'name': 'BuyItems', 'type': 'CX Buy', 'group': 'A1', 'exchange': 'IC1',
- 'priceLimits': price_limits, 'buyPartial': True, 'allowUnfilled': True, 'useCXInv': False},
- ],
- 'global': {'name': f'buy orders for {days} days'},
- 'groups': [{'type': 'Manual', 'name': 'A1', 'materials': to_buy}],
- }))
- # deposits of current bids
- orders.sort(key=lambda order: order['Limit'] * order['Amount'], reverse=True)
- print('\ncurrent bid deposits:')
- for order in orders:
- print(f"{order['MaterialTicker']:4} {order['Limit'] * order['Amount']:7,.0f}")
- def get_raw_prices() -> typing.Mapping[str, market.RawPrice]:
- return {p['MaterialTicker']: p
- for p in cache.get('https://refined-prun.github.io/refined-prices/all.json') if p['ExchangeCode'] == 'IC1'}
- def get_total_buy(days: int) -> typing.Mapping[str, int]:
- planets = [supply.Planet(fio_burn) for fio_burn in cache.get('https://rest.fnar.net/fioweb/burn/user/' + config.username,
- headers={'Authorization': config.fio_api_key})]
- buy: dict[str, int] = collections.defaultdict(int)
- for planet in planets:
- for mat, amount in planet.supply_for_days(days).items():
- buy[mat] += amount
- return buy
- def get_planet_exports_and_ship_storage() -> typing.Mapping[str, int]:
- '''materials in base storage that aren't being consumed and materials in ship storage'''
- avail = collections.defaultdict(int)
- fio_burn: typing.Sequence[supply.FIOBurn] = cache.get('https://rest.fnar.net/fioweb/burn/user/' + config.username,
- headers={'Authorization': config.fio_api_key})
- for burn in fio_burn:
- planet = supply.Planet(burn)
- for mat in planet.exporting:
- avail[mat] += planet.inventory.get(mat, 0)
-
- stores: typing.Sequence[market.Storage] = cache.get('https://rest.fnar.net/storage/' + config.username,
- headers={'Authorization': config.fio_api_key})
- for store in stores:
- if store['Type'] != 'SHIP_STORE':
- continue
- for item in store['StorageItems']:
- avail[item['MaterialTicker']] += item['MaterialAmount']
- return avail
- def get_bids() -> tuple[typing.Mapping[str, int], list[market.ExchangeOrder]]:
- orders: typing.Sequence[market.ExchangeOrder] = cache.get('https://rest.fnar.net/cxos/' + config.username,
- headers={'Authorization': config.fio_api_key})
- orders = [order for order in orders
- if order['OrderType'] == 'BUYING' and order['Status'] != 'FILLED' and order['ExchangeCode'] == 'IC1']
- bids = collections.defaultdict(int)
- for order in orders:
- bids[order['MaterialTicker']] += order['Amount']
- return bids, orders
- @dataclasses.dataclass(eq=False, frozen=True, slots=True)
- class Material:
- ticker: str
- amount: int
- bids: int
- have: int
- spread: float
- savings: float
- def __lt__(self, o: Material) -> bool:
- return self.savings< o.savings
- if __name__ == '__main__':
- main()
|