diff options
| author | kboz <kutayboz@gmail.com> | 2026-04-24 00:55:26 +0200 |
|---|---|---|
| committer | kboz <kutayboz@gmail.com> | 2026-04-24 00:55:26 +0200 |
| commit | e43c98997b0de6a8da989f68d97f1d4f7fb7bb6f (patch) | |
| tree | 1b9c8073acdef60af2ca3e19ae0c4a1b2c55410f /backend/src/pathfinder.py | |
| parent | acbf6d6bd6d63ab0e9f7c4e119827f76e0382aac (diff) | |
| download | haul-e43c98997b0de6a8da989f68d97f1d4f7fb7bb6f.tar.gz | |
feat(core): implement wallet trade tracking and sqlite market caching
- Transitioned primary ESI networking and dataset initialization to `asyncio` and `aiohttp` for non-blocking I/O.
- Introduced `aiosqlite` utilizing an in-memory database to significantly speed up arbitrage querying.
- Implemented character wallet tracking (added `esi-wallet.read_character_wallet.v1` scope) to automatically deduct filled market volumes internally instead of waiting for full region polling.
- Exposed `/settings` endpoint allowing dynamic tweaking of threshold variables (cargo size, limits, tax rate, ship speeds) directly from the React UI.
- Decoupled auto-waypoint submission during route planning; users now push routes selectively via a new "Set in Game" frontend action and `/set_waypoints` backend logic.
- Optimized order evaluations and triplet generation limits through `LegMetrics` to lower CPU footprint drastically on large dataset passes.
- Migrated React frontend completely to Server-Sent Events (SSE), cleanly ditching HTTP polling.
Diffstat (limited to 'backend/src/pathfinder.py')
| -rw-r--r-- | backend/src/pathfinder.py | 61 |
1 files changed, 15 insertions, 46 deletions
diff --git a/backend/src/pathfinder.py b/backend/src/pathfinder.py index 4ce2df3..a08d47c 100644 --- a/backend/src/pathfinder.py +++ b/backend/src/pathfinder.py @@ -1,13 +1,17 @@ import logging from dataclasses import dataclass +from typing import Optional + from tqdm import tqdm from arbitrage import Trade -from graph import NavigationGraph as Graph +from graph import NavigationGraph as Graph, LegMetrics logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) +LegMetricLookup = dict[tuple[int, int], Optional[LegMetrics]] + @dataclass class Ship: location: int @@ -19,7 +23,7 @@ class Ship: risk_cost: float -def pathfinder(order_matches: list[Trade], graph: Graph, ship: Ship) -> list[Trade]: +def pathfinder(order_matches: list[Trade], graph: Graph, ship: Ship, leg_metrics: Optional[LegMetricLookup] = None) -> list[Trade]: """ Processes order matches by filtering and calculating net profit. @@ -33,11 +37,11 @@ def pathfinder(order_matches: list[Trade], graph: Graph, ship: Ship) -> list[Tra """ order_matches = filter_order_matches_not_in_graph(order_matches, graph) logger.info("After filtering order matches not in graph: %d", len(order_matches)) - - order_matches = filter_order_matches_same_region(order_matches, graph) - logger.info("After filtering order matches to same region: %d", len(order_matches)) - order_matches = calculate_net_profit(order_matches, graph, ship) + if leg_metrics is None: + leg_metrics = graph.build_trade_leg_metrics(ship["location"], order_matches) + + order_matches = calculate_net_profit(order_matches, ship["location"], leg_metrics) order_matches = filter_order_matches_low_profit(order_matches) logger.info("After filtering low profit order matches: %d", len(order_matches)) @@ -60,42 +64,7 @@ def filter_order_matches_not_in_graph(order_matches: list[Trade], graph: Graph) if order_match.from_station in graph.graph.nodes and order_match.to_station in graph.graph.nodes ] -def filter_order_matches_same_region(order_matches: list[Trade], graph: Graph) -> list[Trade]: - """ - Filters out order matches where the from_region and to_region are different. - Keeps only order matches within the same region. - - Args: - order_matches: List of trade opportunities. - graph: Navigation graph. - - Returns: - Filtered list of trade opportunities where both stations are in the same region. - """ - filtered_order_matches = [] - - for order_match in order_matches: - from_station = order_match.from_station - to_station = order_match.to_station - - from_station_data = graph.stations.get(from_station) - to_station_data = graph.stations.get(to_station) - - if not from_station_data or not to_station_data: - continue # Skip if station data is missing - - from_solar_system_id = from_station_data["solar_system_id"] - to_solar_system_id = to_station_data["solar_system_id"] - - from_region = graph.solar_systems[from_solar_system_id]["region_id"] - to_region = graph.solar_systems[to_solar_system_id]["region_id"] - - if from_region == to_region: - filtered_order_matches.append(order_match) - - return filtered_order_matches - -def calculate_net_profit(order_matches: list[Trade], graph: Graph, ship: Ship) -> list[Trade]: +def calculate_net_profit(order_matches: list[Trade], ship_location: int, leg_metrics: LegMetricLookup) -> list[Trade]: """ Calculates the net profit for each order match by subtracting transport cost from gross profit. @@ -108,12 +77,12 @@ def calculate_net_profit(order_matches: list[Trade], graph: Graph, ship: Ship) - List of trade opportunities with net_profit calculated. """ logger.info("Calculating estimated profits") - for order_match in tqdm(order_matches): + for order_match in tqdm(order_matches, desc="Scoring trades"): try: # Calculate transport cost from ship location to from_station - cost_to_source = graph.shortest_path_length(ship["location"], order_match.from_station) + cost_to_source = leg_metrics.get((ship_location, order_match.from_station)) # Calculate transport cost from from_station to to_station - cost_to_destination = graph.shortest_path_length(order_match.from_station, order_match.to_station) + cost_to_destination = leg_metrics.get((order_match.from_station, order_match.to_station)) # Check if paths exist if cost_to_source is None or cost_to_destination is None: @@ -121,7 +90,7 @@ def calculate_net_profit(order_matches: list[Trade], graph: Graph, ship: Ship) - order_match.net_profit = None continue - total_transport_cost = cost_to_source + cost_to_destination + total_transport_cost = cost_to_source.weight + cost_to_destination.weight order_match.net_profit = order_match.gross_profit - total_transport_cost except Exception as e: logger.error(f"Error calculating net profit for order match {order_match}: {e}") |
