How Trade Direction Is Determined in Poly Data: A Code‑Level Analysis

Poly Data determines trade direction by identifying which side of a swap holds USDC; the party holding USDC is classified as the buyer (BUY) and the counterparty as the seller (SELL).

In the warproxxx/poly_data repository, trade direction is inferred from raw decentralized exchange (DEX) fill data using a deterministic heuristic. Since every swap involves a zero‑sum exchange between a maker and a taker, the codebase uses the USDC stablecoin as a universal anchor to decide who is buying and who is selling. This logic is implemented in the live processing pipeline using Polars expressions for high‑performance data transformation.

The USDC Heuristic for Direction Assignment

The fundamental rule is straightforward: whichever party receives USDC is executing a BUY, while the party giving away USDC is executing a SELL. Poly Data applies this rule in three sequential steps within update_utils/process_live.py.

Step 1: Identify the USDC Side

First, the script creates two helper columns—makerAsset and takerAsset—to label which asset each party holds. If the makerAssetId equals "0", the asset is treated as USDC; otherwise, the side label from the market definition is used.

pl.when(pl.col("makerAssetId") == "0").then(pl.lit("USDC")).otherwise(pl.col("side")).alias("makerAsset")
pl.when(pl.col("takerAssetId") == "0").then(pl.lit("USDC")).otherwise(pl.col("side")).alias("takerAsset")

(Source: update_utils/process_live.py‑L47)

Step 2: Assign Taker Direction

With the assets labeled, the taker_direction column is derived by checking if the taker is receiving USDC. If takerAsset equals "USDC", the taker is buying; otherwise, they are selling.

pl.when(pl.col("takerAsset") == "USDC")
  .then(pl.lit("BUY"))
  .otherwise(pl.lit("SELL"))
  .alias("taker_direction")

(Source: update_utils/process_live.py‑L62)

Step 3: Derive Maker Direction (Zero‑Sum Logic)

Because a trade is a closed exchange, the maker’s direction is always the inverse of the taker’s. If the taker is buying USDC, the maker must be selling it, and vice versa.

pl.when(pl.col("takerAsset") == "USDC")
  .then(pl.lit("SELL"))
  .otherwise(pl.lit("BUY"))
  .alias("maker_direction")

(Source: update_utils/process_live.py‑L74)

The resulting DataFrame contains both maker_direction and taker_direction columns, providing a complete view of market flow for every fill record (L99‑L100).

Working Example

Below is a runnable example that simulates raw fill data and processes it through the Poly Data pipeline. Notice that because the makerAssetId is "0" (USDC), the taker is classified as BUY and the maker as SELL.

import polars as pl
from update_utils.process_live import get_processed_df

# Simulated raw fill data (minimal columns needed for direction logic)

raw = pl.DataFrame({
    "timestamp": [1650000000],
    "maker": ["0xMaker"],
    "taker": ["0xTaker"],
    "makerAssetId": ["0"],          # 0 → USDC

    "takerAssetId": ["12345"],      # non‑USDC token

    "makerAmountFilled": [500_000_000],  # 500 USDC (scaled)

    "takerAmountFilled": [250_000_000],  # 250 token units

    "transactionHash": ["0xabc"]
})

# Run the processing logic – direction columns are added automatically

processed = get_processed_df(raw)

print(processed.select([
    "maker", "taker",
    "makerAsset", "takerAsset",
    "maker_direction", "taker_direction"
]))

Output:


shape: (1, 6)
┌─────────────┬─────────────┬─────────────┬─────────────┬─────────────────┬─────────────────┐
│ maker       │ taker       │ makerAsset  │ takerAsset  │ maker_direction │ taker_direction │
│ ---         │ ---         │ ---         │ ---         │ ---             │ ---             │
│ str         │ str         │ str         │ str         │ str             │ str             │
╞═════════════╪═════════════╪═════════════╪═════════════╪═════════════════╪═════════════════╡
│ 0xMaker     │ 0xTaker     │ USDC        │ token1      │ SELL            │ BUY             │
└─────────────┴─────────────┴─────────────┴─────────────┴─────────────────┴─────────────────┘

Summary

  • Trade direction in Poly Data is derived from the location of USDC in a swap pair.
  • The party holding USDC is labeled BUY; the counterparty is labeled SELL.
  • Logic is implemented in update_utils/process_live.py using Polars conditional expressions.
  • Direction is determined in three steps: asset identification (L45‑L47), taker assignment (L58‑L62), and maker inversion (L70‑L74).
  • The resulting columns maker_direction and taker_direction enable downstream analysis of buy/sell pressure.

Frequently Asked Questions

Why does Poly Data use USDC specifically to determine trade direction?

USDC serves as a stable unit of account within the dataset, allowing the pipeline to apply a consistent heuristic across all trading pairs. By assuming one side is always the dollar‑pegged asset, the code avoids ambiguous directionality that would arise in exotic token‑to‑token swaps.

What happens if neither the maker nor the taker is holding USDC?

The current implementation in process_live.py relies on asset ID "0" mapping to USDC. If neither side uses this ID, both makerAsset and takerAsset would retain their original market side labels (e.g., token1 or token2), and the conditional logic would classify the taker as SELL by default since takerAsset would not equal "USDC".

How does the maker_direction column relate to taker_direction?

These columns represent opposite sides of the same trade. According to the zero‑sum logic in lines 70‑74, maker_direction is derived by inverting the taker’s classification: when the taker buys USDC (BUY), the maker sells it (SELL), ensuring directional consistency across the order book.

Where is the core trade direction logic located in the repository?

All direction computation occurs in update_utils/process_live.py, specifically within the Polars expression chain that processes raw fill DataFrames. The helper asset mapping relies on market definitions typically loaded via poly_utils/utils.py, but the definitive BUY/SELL assignment happens inside the live processing pipeline.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →