| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109 |
- from __future__ import annotations
- import collections
- import typing
- import cache
- import roi
- BUY = frozenset([
- # definitely buy
- 'C', 'FLX', 'H', 'H2O', 'HAL', 'HCP', 'HE', 'LST', 'MG', 'N', 'NA', 'NCS', 'NS', 'O', 'PE', 'PG', 'S', 'TCL',
- # maybe buy
- 'AU', 'BRM', 'CU', 'FE', 'LI', 'RG', 'ROM', 'SI', 'TI',
- ])
- def main() -> None:
- blueprint = {
- 'FFC': 1,
- 'FSE': 1,
- 'LFE': 2,
- 'MFE': 2,
- 'QCR': 1,
- 'SFE': 1,
- 'LCB': 1,
- 'MFL': 1,
- 'MSL': 1,
- 'LHP': 94,
- 'SSC': 128,
- 'BR1': 1,
- 'CQM': 1,
- }
- prices: typing.Mapping[str, RawPrice] = {p['MaterialTicker']: p
- for p in cache.get('https://refined-prun.github.io/refined-prices/all.json') if p['ExchangeCode'] == 'IC1'}
- recipes = recipe_for_mats()
- production: dict[str, dict[str, float]] = collections.defaultdict(lambda: collections.defaultdict(float))
- cost = 0.0
- for mat, amount in blueprint.items():
- cost += analyze_mat(0, mat, amount, production, prices, recipes)
- print()
- print(f'total cost: {cost:,}')
- buildings: typing.Sequence[roi.Building] = cache.get('https://api.prunplanner.org/data/buildings/', expiry=cache.ONE_DAY)
- expertise = collections.defaultdict(list)
- for building in buildings:
- if building['building_ticker'] in production:
- expertise[building['expertise']].append(building['building_ticker'])
- for expertise, buildings in expertise.items():
- print(expertise)
- for building in buildings:
- print(f'\t{building:3}:', end='')
- for mat, amount in production[building].items():
- traded = prices[mat]['AverageTraded30D'] or 0
- if traded > amount * 2:
- print(f' \033[32m{mat}\033[0m', end='')
- else:
- print(f' \033[31m{mat}\033[0m', end='')
- print()
- def recipe_for_mats() -> dict[str, roi.Recipe]:
- all_recipes: list[roi.Recipe] = cache.get('https://api.prunplanner.org/data/recipes/', expiry=cache.ONE_DAY)
- mat_recipes = collections.defaultdict(list) # all ways to make a mat
- for recipe in all_recipes:
- for output in recipe['outputs']:
- mat_recipes[output['material_ticker']].append(recipe)
- mat_recipe: dict[str, roi.Recipe] = {} # mats for which there's only one recipe to make
- for mat, recipes in mat_recipes.items():
- if len(recipes) == 1:
- mat_recipe[mat] = recipes[0]
- return mat_recipe
- def analyze_mat(level: int, mat: str, amount: float, production: dict[str, dict[str, float]],
- prices: typing.Mapping[str, RawPrice], recipes: dict[str, roi.Recipe]) -> float:
- price = prices[mat]
- traded = price['AverageTraded30D'] or 0
- if mat in BUY:
- print('\t' * level + f'{amount:g}×{mat} buy: {price["Ask"]}, daily traded {traded:5.1f}')
- assert price['Ask'] is not None
- return price['Ask'] * amount
- else:
- if (recipe := recipes.get(mat)) is None:
- print('\t' * level + f'{amount:g}×{mat} make (unknown recipe)')
- return 0
- else:
- building = recipe['building_ticker']
- production[building][mat] += amount
- liquid = '\033[31mnot liquid\033[0m'
- if traded > amount * 2:
- liquid = '\033[32mliquid\033[0m'
- print('\t' * level + f'{amount:g}×{mat} make ({building}, {liquid})')
- total_cost = 0.0
- for input_mat in recipe['inputs']:
- input_amount = input_mat['material_amount'] * amount / recipe['outputs'][0]['material_amount']
- total_cost += analyze_mat(level + 1, input_mat['material_ticker'], input_amount, production, prices, recipes)
- print('\t' * level + f'\tcost: {total_cost:9,.2f}')
- return total_cost
- class RawPrice(typing.TypedDict):
- MaterialTicker: str
- ExchangeCode: str
- Ask: float | None
- AverageTraded30D: float | None # averaged daily traded volume over last 30 days
- Supply: int
- if __name__ == '__main__':
- main()
|