def execute_sync(config_file_path: Optional[str], league: str): try: # Load user config config_file_path = UserConfig.get_file_path(config_file_path) user_config = UserConfig.from_file(config_file_path, False) except Exception as ex: logging.error(f"Error: {ex.args[0]}") exit(1) if user_config.account_name == None: raise Exception("Missing accountName in config file") if user_config.poe_session_id == None: raise Exception("Missing POESESSID in config file") logging.info( f"Starting sync for account {user_config.account_name} in league {league}" ) session = requests.Session() session.headers.update({ "Cookie": "POESESSID={}".format(user_config.poe_session_id), "User-Agent": "curl/7.76.1" }) stash_tabs = fetch_stash_tabs(session, league, user_config.account_name) public_tab_indices = find_public_stashes(stash_tabs) logging.info("Found {} public stash tabs".format(len(public_tab_indices))) if len(public_tab_indices) == 0: logging.warn("No public stash tabs to sync with were found") return items: Dict[str, int] = dict() for tab_idx in tqdm(public_tab_indices): stash_items = fetch_stash_tab_items(session, league, user_config.account_name, tab_idx) aggregate(items, stash_items) time.sleep(1) for item_name, stack_size in items.items(): user_config.set_asset_quantity(item_name, stack_size) user_config.save(config_file_path) logging.info(f"Successfully synced {len(items)} items")
def test_load_user_confg_with_correct_defaults(self): raw = {"version": 1, "trading": {"Chaos Orb": {}}, "assets": {}} raw = json.dumps(raw) user_config = UserConfig.from_raw(raw) self.assert_is_user_config(user_config) x = user_config.trading["Chaos Orb"] assert (x.minimum_stock == 0) assert (x.maximum_stock != 0) assert (type(x.sell_for) is dict)
def find_paths(graph: Dict[str, Dict[str, List[Offer]]], have: str, want: str, user_config: UserConfig, max_length: int = 3) -> List[List[Offer]]: """ Returns a list of all possible paths from `want` to `have` for a given graph. A path is simply a list of transactions between two currency nodes. """ paths: deque = deque() correct_paths: List[List[Offer]] = [] # If there are no paths between the specified currencies, simply abort if have not in graph: return [] for currency in graph[have]: for offer in graph[have][currency]: paths.append([offer]) while len(paths) > 0: next: List[Offer] = paths.pop() path_length = calculate_path_length(next) if path_length > max_length: continue # If a path contains an edge with a stock outside of the user-specified boundaries, prune it for edge in next: (minimum, maximum) = user_config.get_stock_boundaries(edge.have, edge.want) if edge.stock < minimum or edge.stock > maximum: continue # We have arrived at the target currency if next[-1].want == want: if is_profitable(next): correct_paths.append(next) continue next_currency = next[-1].want seen_currencies = [edge.have for edge in next] # If there are no paths between the specified currencies, simply skip if next_currency in graph: for currency in graph[next_currency]: if max_length == len(next) + 1 and currency != have: continue if currency not in seen_currencies[1:]: for offer in graph[next_currency][currency]: paths.append(next + [offer]) return correct_paths
def equalize_stock_differences(path: List[Offer], user_config: UserConfig) -> List[Edge]: """ Finds the maximum flow for a found path and alters the conversion edges accordingly. Also rounds up the trading values to trade for full pieces of currency. """ edges: List[Edge] = [Edge(offer, 1, 1) for offer in path] # Limit the first transaction's volume based on maximum trading volume first_edge = edges[0] sell_trading_cap = user_config.get_maximum_trade_volume_for_item( first_edge.have) buy_trading_cap = math.floor(first_edge.conversion_rate * sell_trading_cap) first_edge.stock = min(first_edge.stock, buy_trading_cap) # add some precalculated values for idx, edge in enumerate(edges): edge.paid = math.floor(edge.stock / edge.conversion_rate) edge.received = math.floor(edge.paid * edge.conversion_rate) # need this double loop to make sure that all stock quantity differences # per transaction pair are equalized. The worst case for this (starting # at the front of the path) is that the limiting transaction is the last one. # Therefore, we have to run the inner loop n times to propagate the stock # quantity fix towards the front of the path for k in range(0, len(edges)): for i in range(1, len(edges)): left = edges[i - 1] right = edges[i] if (left.received == 0 or left.paid == 0 or right.paid == 0 or right.received == 0): return [] if left.received > right.paid: factor = left.received / right.paid left.paid = math.ceil(left.paid / factor) left.received = right.paid if left.received < right.paid: factor = right.paid / left.received right.received = math.floor(right.received / factor) right.paid = left.received return edges
def run(self): item_list = ItemList.load_from_file() user_config = UserConfig.from_file() # By default, default_backend = PoeTrade(item_list) params = parse_args() item_pairs = user_config.get_item_pairs( ) if params.use_filter else item_list.get_item_list_for_backend( default_backend, {"fullbulk": params.fullbulk}) p = PathFinder(params.league, item_pairs, user_config) p.run(2) filename = "{}/{}".format(params.path, gen_filename()) with open(filename, "w") as f: data = p.prepickle() f.write(data)
def test_deserialize_user_config_with_correct_defaults(self): raw = { "version": 1, "trading": { "Chaos Orb": {} }, "assets": {}, } raw = json.dumps(raw) user_config = UserConfig.from_raw(raw) self.assert_is_user_config(user_config) x = user_config.trading["Chaos Orb"] assert (x.minimum_stock == 0) assert (x.maximum_stock != 0) assert (type(x.sell_for) is dict) self.assertEqual(user_config.poe_session_id, None) self.assertEqual(user_config.account_name, None)
def test_deserialize_user_config(self): raw = { "version": 1, "trading": { "Chaos Orb": {} }, "assets": {}, "POESESSID": "123", "accountName": "herpderp" } raw = json.dumps(raw) user_config = UserConfig.from_raw(raw) self.assert_is_user_config(user_config) x = user_config.trading["Chaos Orb"] assert (x.minimum_stock == 0) assert (x.maximum_stock != 0) assert (type(x.sell_for) is dict) self.assertEqual(user_config.poe_session_id, "123") self.assertEqual(user_config.account_name, "herpderp")
def test_load_default_user_confg_from_fs(self): user_config: UserConfig = UserConfig.from_file() self.assert_is_user_config(user_config)
init_logger(arguments.debug) item_list = ItemList.load_from_file() backend = PoeOfficial(item_list) league = arguments.league currency = arguments.currency limit = arguments.limit fullbulk = arguments.fullbulk no_filter = arguments.nofilter config = {"fullbulk": fullbulk} # Load excluded trader list excluded_traders = load_excluded_traders() # Load user config user_config = UserConfig.from_file() # Load item pairs item_pairs = item_list.get_item_list_for_backend( backend, config) if no_filter else user_config.get_item_pairs() p = PathFinder(league, item_pairs, user_config, excluded_traders) p.run(2) try: logging.info("\n") if currency == "all": for c in p.graph.keys(): log_conversions(p.results, c, limit) else: log_conversions(p.results, currency, limit)
# global arguments command: str = arguments.command league: str = arguments.league config_file_path: Union[str, None] = arguments.config if command == "pathfinding": # arguments related to pathfinding currency: Union[str, None] = arguments.currency limit: Union[int, None] = arguments.limit fullbulk: bool = arguments.fullbulk no_filter: bool = arguments.nofilter config = {"fullbulk": fullbulk} try: # Load user config user_config = UserConfig.from_file(config_file_path, True) except Exception as ex: logging.error(f"Error: {ex.args[0]}") exit(1) # Load excluded trader list excluded_traders = load_excluded_traders() # Load item pairs item_list = ItemList.load_from_file() backend = PoeOfficial(item_list) item_pairs = item_list.get_item_list_for_backend( backend, config) if no_filter else user_config.get_item_pairs() execute_pathfinding(currency, league, limit, item_pairs, user_config, excluded_traders)
league=LEAGUE, stock=1576), paid=96, received=66), Edge(offer=Offer(contact_ign="MVP_Kefir", want="Chaos", have="Chromatic", conversion_rate=0.087, league=LEAGUE, stock=20), paid=66, received=5), ], } user_config = UserConfig.from_file(DEFAULT_CONFIG_DEFAULT_FILE_PATH) class GraphTest(unittest.TestCase): def test_build_graph(self): graph = build_graph(test_offers) self.assertDictEqual(graph, expected_graph) def test_find_paths(self): paths_small_same_currency = find_paths(expected_graph_small, "Chaos", "Chaos", user_config) self.assertListEqual(expected_profitable_paths_small_same_currency(), paths_small_same_currency) def test_is_profitable(self): path = expected_paths_small_same_currency()[0]
def test_load_default_user_config_from_file(self): user_config = UserConfig.from_file(DEFAULT_CONFIG_DEFAULT_FILE_PATH) self.assert_is_user_config(user_config)