ƒxyzƒxyz Docs
Algorithms

Routing Algorithms

Bellman-Ford optimal FX routing, line-graph transformation, and cyclic arbitrage detection

ƒxyz Network routes FX conversions across a weighted directed graph where nodes are currencies and edges are exchange rate pairs. Three algorithms work in concert: Bellman-Ford finds optimal paths, the line-graph transformation improves routing on dense networks, and negative-cycle detection identifies arbitrage opportunities.

Implementation: packages/neo4j-network/src/services/routing-engine.ts GraphQL queries: findRoute, detectArbitrage, routingGraphInfo Paper references: C6, D6


Bellman-Ford Routing

Overview

Bellman-Ford finds the shortest (cheapest) path between any two currencies in the FX exchange graph. Unlike Dijkstra, Bellman-Ford handles negative edge weights - which is essential for arbitrage detection.

The routing engine converts exchange rates to log-space edge weights so that multiplicative rate chains become additive path weights:

edge weight w(u,v) = -log(rate(u → v))

A cheaper path (lower total weight) corresponds to a more favorable exchange rate sequence.

Algorithm

Initialize: d[source] = 0, d[v] = +inf for all other v
Repeat |V|-1 times:
  For each edge (u, v, w):
    if d[u] + w < d[v]:
      d[v] = d[u] + w
      predecessor[v] = u

Arbitrage check (iteration |V|):
  For each edge (u, v, w):
    if d[u] + w < d[v]:
      negative cycle detected → arbitrage opportunity

Complexity: O(|V| * |E|)

Mathematical Formulation

For an FX path through currencies c1 → c2 → ... → cn:

Total log-cost = sum(-log(rate(ci → ci+1)))
               = -log(product(rates along path))

Optimal path = argmin over all paths of total log-cost
             = argmax over all paths of product(exchange rates)

Implementation

// bellmanFord() in packages/neo4j-network/src/services/routing-engine.ts
bellmanFord(
  graph: Map<string, Map<string, number>>,
  source: string
): { distances: Map<string, number>; predecessors: Map<string, string> }

The function returns distances and predecessors for path reconstruction. The routing graph is built from live ECB FX rate data and crypto prices via buildRoutingGraph().

Status: WIRED - backend + GraphQL resolvers complete. The findRoute GraphQL query is available but has no UI consumer yet. Target UI: /markets/trade Route Finder.


Line-Graph Transformation

Overview

The standard Bellman-Ford treats each currency node as the unit of routing. This ignores a real cost in FX trading: switching between currency pairs at an intermediate currency incurs spread costs that depend on which pair you came from and which pair you're entering.

The line-graph transformation (Paper D6) solves this by converting the FX graph into a line graph where edges become nodes. This allows edge-switching costs to be expressed as ordinary node weights, making the full bid-ask spread model compatible with standard shortest-path algorithms.

Transformation

Given the original FX graph G = (V, E):

Line graph L(G):
  Vertices of L(G) = edges of G
  Two vertices of L(G) are adjacent iff the corresponding
  edges of G share an endpoint (i.e., a currency hop)

For FX routing, this means:

  • Each currency pair (e.g., EUR/USD) becomes a node in L(G)
  • Adjacent nodes in L(G) share a common currency
  • Edge weights in L(G) represent the cost of transitioning through that common currency (bid-ask spread, liquidity penalty)

Performance

Paper D6 reports that line-graph routing on dense FX networks achieves over 50% improvement in effective rate versus naive Bellman-Ford, because it properly accounts for spread accumulation at each currency hop.

Implementation

// buildLineGraph() in packages/neo4j-network/src/services/routing-engine.ts
buildLineGraph(edges: RouteEdge[]): {
  nodes: Map<string, LineGraphNode>;
  edges: LineGraphEdge[];
}

// Activated via useLineGraph parameter on findRoute query:
findRoute(from: "EUR", to: "JPY", useLineGraph: true)

Status: WIRED - buildLineGraph() is implemented. Routing via line-graph is available through the findRoute query's useLineGraph option.


Arbitrage Detection

Overview

In a correctly priced FX market, converting EUR → USD → JPY → EUR should return exactly the starting amount (minus spreads). When the product of exchange rates along a cycle exceeds 1.0, the cycle is profitable - a textbook arbitrage opportunity.

In the log-rate graph, this corresponds to a negative-weight cycle: a cycle where the sum of log-costs is negative.

Mathematical Formulation

For a cycle of currencies c1 → c2 → ... → cn → c1:

Profit = product(rate(ci → ci+1)) - 1
       = exp(-sum(log-weights along cycle)) - 1

Profitable cycle: sum(log-weights) < 0, which means product(rates) > 1.

After the standard |V|-1 Bellman-Ford relaxations, a |V|-th pass that still finds a shorter path indicates a negative cycle exists.

Algorithm

Run Bellman-Ford for |V|-1 iterations (standard shortest path)

On iteration |V|:
  For each edge (u, v, w):
    if d[u] + w < d[v]:
      v is in or reachable from a negative cycle
      → trace predecessor chain to extract the cycle
      → compute cycle profit
      → emit as arbitrage opportunity

Cycle Profit Calculation

// findNegativeCycles() in packages/neo4j-network/src/services/routing-engine.ts
findNegativeCycles(
  graph: Map<string, Map<string, number>>
): ArbitrageCycle[]

interface ArbitrageCycle {
  path: string[];          // e.g. ["EUR", "USD", "JPY", "EUR"]
  profitPercent: number;   // e.g. 0.12 for 0.12% profit
  rateProduct: number;     // product of exchange rates along cycle
}

Spreads matter: Raw rate arbitrage often disappears once bid-ask spreads are included. The platform applies configurable spread models before reporting actionable arbitrage cycles. Cycles below the minimum profit threshold are filtered.

Status: WIRED - detectArbitrage GraphQL query is complete. Target UI: /markets/trade Arbitrage Scanner panel.


Dijkstra Shortest Path

Overview

Dijkstra's algorithm finds the shortest path in a graph with non-negative edge weights by greedily expanding the lowest-cost frontier using a priority queue. In the currency routing demo visualization, Dijkstra provides the baseline shortest-path computation.

Limitation: Dijkstra cannot handle negative edge weights. Since the FX log-rate graph can produce negative weights (required for arbitrage detection), production routing uses Bellman-Ford. Dijkstra is used in the demo visualization and as a fast path for graphs known to have non-negative weights.

Algorithm

Initialize: d[source] = 0, d[v] = +inf for all other v
Priority queue Q with all vertices

While Q is not empty:
  u = extract-min(Q)
  For each neighbor v of u:
    if d[u] + w(u,v) < d[v]:
      d[v] = d[u] + w(u,v)
      predecessor[v] = u
      decrease-key(Q, v)

Complexity: O(|E| + |V| log |V|) with a Fibonacci heap.

Status: LIVE - Used in the currency-routing demo visualization. Registered in the algorithm registry as dijkstra.


A* Pathfinding

Overview

A* extends Dijkstra with a heuristic function h(v) that estimates the cost-to-goal, allowing the search to focus on promising directions. In the currency routing operational system, A* uses a route-scoring heuristic that accounts for fees, speed, and reliability.

Formula

f(v) = g(v) + h(v)

Where:
  g(v) = known cost from source to v
  h(v) = heuristic estimate from v to goal (must be admissible: h(v) <= true cost)
  f(v) = estimated total cost through v

The search expands the node with the lowest f(v) first. When h is admissible, A* is guaranteed to find the optimal path.

Status: WIRED - Used in the currency routing operational system with a multi-factor route-scoring heuristic. Registered in the algorithm registry as a-star.


Triangular Arbitrage Detection

Overview

Triangular arbitrage is a special case of cyclic arbitrage restricted to 3-node cycles in the FX graph. A triangular arbitrage occurs when converting A to B to C to A yields more than the starting amount. This is detectable as a length-3 negative cycle in the log-rate graph.

Formula

Spread = r(A->B) * r(B->C) * r(C->A) - 1

In log-space:
  sum = -log(r(A->B)) + -log(r(B->C)) + -log(r(C->A))
  Profitable if sum < 0 (i.e., product of rates > 1)

Triangular arbitrage is the most commonly observed form in FX markets because 3-hop cycles execute fastest and face the least slippage.

Status: WIRED - Extends the findNegativeCycles() infrastructure with a 3-hop filter. Registered in the algorithm registry as triangular-arbitrage.


Planned Extensions

STAP: Standardized Total Arbitrage Profit (Paper D6b, arXiv:2508.03217)

STAP extends the line-graph DEX routing framework by defining a standardized metric for total arbitrage profit across all possible cycles. STAP-maximizing execution provably eliminates all arbitrage opportunities in the graph:

STAP = sum over all negative cycles C: |profit(C)|

STAP provides a single scalar that quantifies the total inefficiency in the exchange graph. A STAP of zero means the market is in no-arbitrage equilibrium. Planned as an extension to routing-engine.ts.

Tropical Algebra Scanner (Paper C6)

Tropical (min-plus) algebra provides a unified algebraic framework for computing all shortest paths simultaneously. Planned tropicalArbitrageScan() in tropical-arbitrage.ts.

Genetic Algorithm Optimization (PaymentsCanada paper)

For large FX graphs (>100 currencies), exact cycle enumeration becomes expensive. Planned GA-based cycle optimizer in ga-arbitrage.ts uses evolutionary search to find high-profit cycles efficiently.


Paper References

PaperTitleRelevance
C6FX Arbitrage via Graph TheoryBellman-Ford on FX networks
D6Line-Graph DEX RoutingLine-graph transformation, >50% improvement
D6bSTAP DEX Efficiency (arXiv:2508.03217)Standardized Total Arbitrage Profit metric (planned)
D5AMM Pool Simulator (CPMM)Pool-aware arbitrage sizing
S4-cyclicCyclic Arbitrage SizingStatistical no-arbitrage bounds
Dijkstra 1959Two problems in connexion with graphsBaseline shortest path algorithm
Hart et al. 1968Heuristic Minimum Cost PathsA* pathfinding

On this page