Browse Source

sell: decide what to sell vs keep

raylu 1 month ago
parent
commit
a7d7a146eb
2 changed files with 107 additions and 46 deletions
  1. 41 46
      sell.py
  2. 66 0
      sell_liquidity.py

+ 41 - 46
sell.py

@@ -1,65 +1,60 @@
 from __future__ import annotations
 
 import collections
-import dataclasses
+import math
+import sys
 import typing
 
 import cache
 from config import config
 import market
+import supply
 
 if typing.TYPE_CHECKING:
 	import supply
 
 def main() -> None:
+	(ship,) = sys.argv[1:]
+	ship_inventory = get_ship_storage(ship)
+	warehouse_inventory = supply.warehouse_inventory()
 	raw_prices: typing.Mapping[str, market.RawPrice] = {p['MaterialTicker']: p
 			for p in cache.get('https://refined-prun.github.io/refined-prices/all.json') if p['ExchangeCode'] == 'IC1'}
-
-	planets: typing.Sequence[supply.FIOBurn] = cache.get('https://rest.fnar.net/fioweb/burn/user/' + config.username,
-			headers={'Authorization': config.fio_api_key})
-	producing = frozenset(mat['MaterialTicker'] for planet in planets for mat in planet['OrderProduction']
-			if mat['MaterialTicker'] not in (c['MaterialTicker'] for c in planet['OrderConsumption']))
-	materials: list[Material] = []
-	for mat in producing:
-		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']) / price['Ask']
-		chart_analysis = market.analyze_price_chart(mat + '.IC1', (price['Bid'] + price['Ask']) / 2)
-		ask_fill_ratio = chart_analysis.asks_filled / (chart_analysis.bids_filled + chart_analysis.asks_filled)
-		materials.append(Material(mat, spread=spread, bid=price['Bid'], ask=price['Ask'], score=spread * ask_fill_ratio))
-	materials.sort()
-
-	all_orders: typing.Sequence[market.ExchangeOrder] = cache.get('https://rest.fnar.net/cxos/' + config.username,
+	consumption = get_consumption()
+
+	for item in ship_inventory:
+		ticker = item['MaterialTicker']
+		print(f'{item["MaterialAmount"]:5} {ticker:3}:', end=' ')
+		have = item['MaterialAmount'] + warehouse_inventory.get(ticker, 0)
+		need = math.ceil(consumption.get(ticker, 0) * 14)
+		sell = min(have - need, item['MaterialAmount'])
+		keep = item['MaterialAmount'] - sell
+		if sell > 0:
+			print('sell', sell, end=' ')
+		if keep > 0:
+			print('keep', keep, end= ' ')
+		print()
+
+def get_ship_storage(ship_name: str) -> typing.Sequence[market.StorageItem]:
+	ship_name = ship_name.casefold()
+	stores: typing.Sequence[market.Storage] = cache.get('https://rest.fnar.net/storage/' + config.username,
 			headers={'Authorization': config.fio_api_key})
-	orders: dict[str, int] = collections.defaultdict(int)
-	for order in all_orders:
-		if order['OrderType'] != 'SELLING' or order['Status'] == 'FILLED' or order['ExchangeCode'] != 'IC1':
-			continue
-		orders[order['MaterialTicker']] += order['Amount']
-
-	print(f'{"mat":^4} spread  asks  instant cash  loss')
-	for m in materials:
-		print(f'{m.ticker:4} {m.spread*100:5.1f}%', end='')
-		if asks := orders.pop(m.ticker, None):
-			print(f' {asks:5} {asks * m.bid:13} {asks * (m.ask - m.bid):5}')
-		else:
-			print()
-	print('\nother listings:')
-	for mat, asks in orders.items():
-		print(f'{mat:4} {asks:5}')
-
-@dataclasses.dataclass(eq=False, slots=True)
-class Material:
-	ticker: str
-	spread: float
-	bid: float
-	ask: float
-	score: float
-
-	def __lt__(self, o) -> bool:
-		return self.score < o.score
+	for store in stores:
+		if store['Type'] == 'SHIP_STORE' and store['Name'].casefold() == ship_name:
+			return store['StorageItems']
+	raise Exception(f'ship storage {ship_name} not found')
+
+def get_consumption() -> typing.Mapping[str, float]:
+	'''get consumption on planets that consume each mat'''
+	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})]
+
+	consumption: dict[str, float] = collections.defaultdict(float)
+	for planet in planets:
+		supply_config = config.supply_config(planet.name)
+		for mat, amount in planet.net_consumption.items():
+			if mat not in supply_config.ignore_materials and amount > 0:
+				consumption[mat] += amount
+	return consumption
 
 
 if __name__ == '__main__':

+ 66 - 0
sell_liquidity.py

@@ -0,0 +1,66 @@
+from __future__ import annotations
+
+import collections
+import dataclasses
+import typing
+
+import cache
+from config import config
+import market
+
+if typing.TYPE_CHECKING:
+	import supply
+
+def main() -> None:
+	raw_prices: typing.Mapping[str, market.RawPrice] = {p['MaterialTicker']: p
+			for p in cache.get('https://refined-prun.github.io/refined-prices/all.json') if p['ExchangeCode'] == 'IC1'}
+
+	planets: typing.Sequence[supply.FIOBurn] = cache.get('https://rest.fnar.net/fioweb/burn/user/' + config.username,
+			headers={'Authorization': config.fio_api_key})
+	producing = frozenset(mat['MaterialTicker'] for planet in planets for mat in planet['OrderProduction']
+			if mat['MaterialTicker'] not in (c['MaterialTicker'] for c in planet['OrderConsumption']))
+	materials: list[Material] = []
+	for mat in producing:
+		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']) / price['Ask']
+		chart_analysis = market.analyze_price_chart(mat + '.IC1', (price['Bid'] + price['Ask']) / 2)
+		ask_fill_ratio = chart_analysis.asks_filled / (chart_analysis.bids_filled + chart_analysis.asks_filled)
+		materials.append(Material(mat, spread=spread, bid=price['Bid'], ask=price['Ask'], score=spread * ask_fill_ratio))
+	materials.sort()
+
+	all_orders: typing.Sequence[market.ExchangeOrder] = cache.get('https://rest.fnar.net/cxos/' + config.username,
+			headers={'Authorization': config.fio_api_key})
+	orders: dict[str, int] = collections.defaultdict(int)
+	for order in all_orders:
+		if order['OrderType'] != 'SELLING' or order['Status'] == 'FILLED' or order['ExchangeCode'] != 'IC1':
+			continue
+		orders[order['MaterialTicker']] += order['Amount']
+
+	print(f'{"mat":^4} spread  asks  instant cash  loss')
+	for m in materials:
+		print(f'{m.ticker:4} {m.spread*100:5.1f}%', end='')
+		if asks := orders.pop(m.ticker, None):
+			print(f' {asks:5} {asks * m.bid:13} {asks * (m.ask - m.bid):5}')
+		else:
+			print()
+	print('\nother listings:')
+	for mat, asks in orders.items():
+		print(f'{mat:4} {asks:5}')
+
+@dataclasses.dataclass(eq=False, slots=True)
+class Material:
+	ticker: str
+	spread: float
+	bid: float
+	ask: float
+	score: float
+
+	def __lt__(self, o) -> bool:
+		return self.score < o.score
+
+
+if __name__ == '__main__':
+	main()