Przeglądaj źródła

roi: handle thinly traded scientist consumables

raylu 1 miesiąc temu
rodzic
commit
d20d4b9f48
1 zmienionych plików z 17 dodań i 11 usunięć
  1. 17 11
      roi.py

+ 17 - 11
roi.py

@@ -12,8 +12,8 @@ def main() -> None:
 	materials: dict[str, Material] = {m['Ticker']: m for m in cache.get('https://api.prunplanner.org/data/materials')}
 	raw_prices: list[RawPrice] = cache.get('https://refined-prun.github.io/refined-prices/all.json')
 	prices: dict[str, Price] = {
-		p['MaterialTicker']: Price(p['VWAP7D'], p['AverageTraded7D']) for p in raw_prices # pyright: ignore[reportArgumentType]
-		if p['ExchangeCode'] == 'IC1' and p['VWAP7D'] is not None
+		p['MaterialTicker']: Price(p['VWAP7D'], p['AverageTraded7D'], p['VWAP30D']) for p in raw_prices # pyright: ignore[reportArgumentType]
+		if p['ExchangeCode'] == 'IC1'
 	}
 	habitation: typing.Mapping[Worker, str] = {
 		'Pioneers': 'HB1',
@@ -43,12 +43,15 @@ def calc_profit(recipe: Recipe, buildings: typing.Mapping[str, Building], hab_ar
 		(output,) = recipe['Outputs']
 	except ValueError: # skip recipes that don't have exactly 1 output
 		return
-	try:
-		output_price = prices[output['Ticker']]
-		cost = sum(prices[input['Ticker']].vwap * input['Amount'] for input in recipe['Inputs'])
-	except KeyError: # skip recipes with thinly traded materials
+	output_price = prices[output['Ticker']]
+	if output_price.vwap_7d is None or output_price.average_traded_7d is None: # skip recipes with thinly traded output
 		return
-	revenue = output_price.vwap * output['Amount']
+	cost = 0
+	for input in recipe['Inputs']:
+		if (input_cost := prices[input['Ticker']].vwap_7d) is None:
+			return # skip recipes with thinly traded inputs
+		cost += input_cost * input['Amount']
+	revenue = output_price.vwap_7d * output['Amount']
 	building = buildings[recipe['BuildingTicker']]
 	area = building['AreaCost'] + sum(hab_area_cost[worker] * building[worker] for worker in hab_area_cost)
 	capex = building_construction_cost(building, prices) + \
@@ -77,7 +80,7 @@ def calc_profit(recipe: Recipe, buildings: typing.Mapping[str, Building], hab_ar
 			average_traded_7d=output_price.average_traded_7d)
 
 def building_construction_cost(building: Building, prices: typing.Mapping[str, Price]) -> float:
-	return sum(bc['Amount'] * prices[bc['CommodityTicker']].vwap for bc in building['BuildingCosts'])
+	return sum(bc['Amount'] * prices[bc['CommodityTicker']].vwap_7d for bc in building['BuildingCosts'])
 
 def building_daily_cost(building: Building, prices: typing.Mapping[str, Price]) -> float:
 	consumption = {
@@ -91,7 +94,8 @@ def building_daily_cost(building: Building, prices: typing.Mapping[str, Price])
 	for worker, mats in consumption.items():
 		workers = building[worker]
 		for mat, per_100 in mats:
-			cost += prices[mat].vwap * workers * per_100 / 100
+			mat_price = prices[mat]
+			cost += (mat_price.vwap_7d or mat_price.vwap_30d) * workers * per_100 / 100
 	return cost
 
 Worker = typing.Literal['Pioneers', 'Settlers', 'Technicians', 'Engineers', 'Scientists']
@@ -133,11 +137,13 @@ class RawPrice(typing.TypedDict):
 	PriceAverage: int
 	VWAP7D: float | None # volume-weighted average price over last 7 days
 	AverageTraded7D: float | None # averaged daily traded volume over last 7 days
+	VWAP30D: float | None
 
 @dataclasses.dataclass(eq=False, frozen=True, slots=True)
 class Price:
-	vwap: float
-	average_traded_7d: float
+	vwap_7d: float | None
+	average_traded_7d: float | None
+	vwap_30d: float | None
 
 @dataclasses.dataclass(eq=False, frozen=True, slots=True)
 class Profit: