Guide

GMX v2 Trading for AI Agents: GLP and GM Pools

Purple Flea Research · March 6, 2026 · 19 min read · Guide

1. GMX v2 Architecture vs v1

GMX launched on Arbitrum in 2021 as one of the first on-chain perpetuals DEXes with deep liquidity, quickly becoming one of the largest DeFi protocols by TVL. The original GLP model (v1) pooled all assets into a single multi-asset liquidity pool that acted as counterparty to all trades. GMX v2 (launched 2023) introduced a fundamentally different architecture with isolated GM pools.

v1 vs v2 at a glance: GMX v1 uses a single GLP pool — all assets share risk. GMX v2 uses isolated GM pools per market — BTC risk is separated from ETH risk. This makes v2 safer for LPs and more composable for agents building LP strategies.

GMX v1 (GLP Model)

In GMX v1, liquidity providers deposit into a single pool (GLP) containing a basket of assets: ETH, BTC, LINK, UNI, USDC, USDT, DAI, FRAX. GLP tokens represent a share of this multi-asset pool. LPs earn trading fees + borrowing fees from traders, but also bear the PnL of all traders as the counterparty. In bull markets where traders profit, GLP holders lose relative to holding the underlying assets; in sideways/bear markets, GLP performs well as traders collectively lose.

GMX v2 (GM Pool Model)

GMX v2 introduced GM (GMX Market) pools — isolated liquidity pools per trading pair. Each market (e.g., BTC/USD, ETH/USD) has its own GM pool with two assets: a long token (e.g., WBTC) and a short token (usually USDC). This isolation means:

$500M+
GMX Total TVL
50+
GM Markets (v2)
100x
Max Leverage
Arbitrum + Avalanche
Supported Chains

2. GM Pools vs GLP: A Deep Dive

Understanding the difference between GLP and GM pools is essential for agents that want to act as LPs on GMX, not just traders.

GLP (v1) LP Mechanics

GLP Pool Composition
  • GLP is a basket token containing BTC, ETH, LINK, UNI, stablecoins (USDC, USDT, DAI, FRAX)
  • Target weights are set by governance; fees incentivize rebalancing toward targets
  • LP yield comes from: 70% of trading fees + 70% of borrow fees, distributed as esGMX and ETH/AVAX
  • Historical APR: 15–30% in high-volume periods, near 0% in low-volume periods
  • Withdrawal fee: 0.1–0.8% depending on pool balance relative to target weights

GM Pool (v2) LP Mechanics

GM Pool Composition
  • Each GM pool holds two assets: long token (e.g., WETH) and short token (USDC)
  • LPs receive GM tokens representing their proportional share of the pool
  • GM LP yield comes from: position fees, borrow fees, funding fees collected from traders
  • Risk: LPs absorb trader PnL for that specific market only (isolated risk)
  • Deposit/withdrawal via 2-minute delayed execution (MEV protection) with a small execution fee
  • Skew incentives: fees are cheaper for deposits that balance the long/short token ratio
python — query GM pool state via Graph API
import requests

# GMX v2 subgraph on Arbitrum
GRAPH_URL = "https://subgraph.satsuma-prod.com/gmx/synthetics-arbitrum-stats/api"

def get_gm_pool_info(market_token: str) -> dict:
    query = """
    query GetMarket($market: String!) {
      marketInfo(id: $market) {
        longTokenAmount
        shortTokenAmount
        longTokenUsd
        shortTokenUsd
        totalBorrowingFees
        netPnl
        marketToken {
          id
          totalSupply
        }
      }
    }
    """
    resp = requests.post(
        GRAPH_URL,
        json={"query": query, "variables": {"market": market_token}}
    )
    return resp.json()["data"]["marketInfo"]

# ETH/USD GM market token address on Arbitrum
ETH_GM_MARKET = "0x70d95587d40a2caf56bd97485ab3eec10bee6336"
pool = get_gm_pool_info(ETH_GM_MARKET)
print(f"ETH GM pool: ${float(pool['longTokenUsd']) + float(pool['shortTokenUsd']):,.0f} TVL")
print(f"Net PnL vs LPs: ${float(pool['netPnl']):,.0f}")

3. Fee Structure for Agents

GMX v2 has a more complex fee structure than pure order-book venues. Understanding all fee components is critical for agents to correctly calculate profitability:

Fee Type Rate Notes
Open/Close position fee0.05–0.07%Based on position size. 0.05% for BTC/ETH, up to 0.07% for smaller markets.
Price impactVariablePositive or negative. Pays you for balancing OI, charges you for skewing it.
Borrowing fee~0.01%/hourPaid by traders holding open positions (per side, per hour)
Funding feeVariableTransfers between longs and shorts based on OI imbalance. Can be positive (you receive) or negative.
Swap fee (LP entry)0.05–0.07%Fee to enter/exit GM LP positions
Execution fee~$0.50Gas cost for 2-min delayed execution on GM deposits
Agent tip — price impact harvesting: GMX v2's price impact mechanism rewards agents that reduce OI imbalance. If longs are heavily dominant, opening a short position earns a positive price impact credit — effectively a subsidy to trade against the crowd. Agents can systematically monitor OI skew and farm these impact credits.

Funding Rate Mechanics

GMX v2 uses a funding rate that transfers between longs and shorts hourly. The rate is determined by the ratio of open interest on each side:

python — compute funding rate
def compute_funding_rate(long_oi: float, short_oi: float,
                           funding_factor: float = 0.0001) -> dict:
    """
    Returns hourly funding rates for longs and shorts.
    Positive rate = you pay. Negative rate = you receive.
    """
    total_oi = long_oi + short_oi
    if total_oi == 0:
        return {"long_rate": 0, "short_rate": 0}

    imbalance = (long_oi - short_oi) / total_oi  # -1 to +1
    funding_rate = imbalance * funding_factor  # hourly rate

    if funding_rate > 0:
        # Longs pay shorts
        return {
            "long_rate": funding_rate,
            "short_rate": -funding_rate * (long_oi / short_oi)
        }
    else:
        # Shorts pay longs
        return {
            "long_rate": funding_rate * (short_oi / long_oi),
            "short_rate": -funding_rate
        }

# Example: ETH market with $50M long OI, $30M short OI
rates = compute_funding_rate(50_000_000, 30_000_000)
print(f"Long hourly rate: {rates['long_rate']*100:.4f}%")   # positive = longs pay
print(f"Short hourly rate: {rates['short_rate']*100:.4f}%")  # negative = shorts receive

4. Opening and Closing Positions

GMX v2 positions are managed via smart contract interactions. Unlike Hyperliquid or dYdX where transactions are fast, GMX uses a 2-step execution pattern: you submit an order request, then an external keeper executes it (typically within 1–3 blocks). This prevents latency-based front-running but means agents must handle the asynchronous execution pattern.

Python Setup with web3.py

bash
pip install web3 eth-abi requests python-dotenv
python — gmx_agent.py
from web3 import Web3
from eth_account import Account
import json, time, requests

# Arbitrum One RPC
w3 = Web3(Web3.HTTPProvider("https://arb1.arbitrum.io/rpc"))
account = Account.from_key("0xYOUR_PRIVATE_KEY")

# GMX v2 ExchangeRouter contract on Arbitrum
EXCHANGE_ROUTER = "0x7C68C7866A64FA2160F78EEaE12217FFbf871fa8"
ORDER_VAULT = "0x31eF83a530Fde1B38EE9A18093A333D8Bbbc40D5"
USDC_ADDRESS = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831"

# Minimal ABI for creating orders
ROUTER_ABI = json.loads("""[
  {
    "inputs": [
      {
        "components": [
          {"name": "receiver", "type": "address"},
          {"name": "callbackContract", "type": "address"},
          {"name": "uiFeeReceiver", "type": "address"},
          {"name": "market", "type": "address"},
          {"name": "initialCollateralToken", "type": "address"},
          {"name": "swapPath", "type": "address[]"},
          {"name": "sizeDeltaUsd", "type": "uint256"},
          {"name": "initialCollateralDeltaAmount", "type": "uint256"},
          {"name": "triggerPrice", "type": "uint256"},
          {"name": "acceptablePrice", "type": "uint256"},
          {"name": "executionFee", "type": "uint256"},
          {"name": "callbackGasLimit", "type": "uint256"},
          {"name": "minOutputAmount", "type": "uint256"},
          {"name": "orderType", "type": "uint8"},
          {"name": "decreasePositionSwapType", "type": "uint8"},
          {"name": "isLong", "type": "bool"},
          {"name": "shouldUnwrapNativeToken", "type": "bool"},
          {"name": "referralCode", "type": "bytes32"}
        ],
        "name": "params",
        "type": "tuple"
      }
    ],
    "name": "createOrder",
    "outputs": [{"name": "", "type": "bytes32"}],
    "type": "function"
  }
]""")

router = w3.eth.contract(address=EXCHANGE_ROUTER, abi=ROUTER_ABI)

def create_market_increase_order(
    market: str,   # GM market token address
    is_long: bool,
    collateral_usdc: float,  # USDC collateral amount
    size_usd: float,         # position size in USD
    current_price: float,
    slippage_bps: float = 50  # 0.5% slippage tolerance
) -> str:

    collateral_wei = int(collateral_usdc * 1e6)  # USDC has 6 decimals
    size_wei = int(size_usd * 1e30)              # GMX uses 1e30 for USD
    execution_fee = w3.to_wei(0.0003, "ether")    # ~$0.50 ETH execution fee

    # Acceptable price: add slippage for longs (buy), subtract for shorts (sell)
    slippage_mult = 1 + (slippage_bps / 10000) if is_long else 1 - (slippage_bps / 10000)
    acceptable_price = int(current_price * slippage_mult * 1e30)

    order_params = (
        account.address,      # receiver
        "0x0000000000000000000000000000000000000000",  # callbackContract
        "0x0000000000000000000000000000000000000000",  # uiFeeReceiver
        market,               # market token address
        USDC_ADDRESS,         # initialCollateralToken
        [],                   # swapPath (empty)
        size_wei,             # sizeDeltaUsd
        collateral_wei,       # initialCollateralDeltaAmount
        0,                    # triggerPrice (0 = market order)
        acceptable_price,     # acceptablePrice
        execution_fee,        # executionFee
        0,                    # callbackGasLimit
        0,                    # minOutputAmount
        2,                    # orderType: MarketIncrease = 2
        0,                    # decreasePositionSwapType
        is_long,
        False,               # shouldUnwrapNativeToken
        b"\x00" * 32           # referralCode
    )

    tx = router.functions.createOrder(order_params).build_transaction({
        "from": account.address,
        "value": execution_fee,
        "gas": 500000,
        "gasPrice": w3.eth.gas_price,
        "nonce": w3.eth.get_transaction_count(account.address)
    })

    signed = account.sign_transaction(tx)
    tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction)
    print(f"Order submitted: {tx_hash.hex()}")
    return tx_hash.hex()

5. Liquidation Risk Management

GMX v2 liquidation mechanics differ from traditional perps. Rather than a sudden liquidation at a fixed price, GMX uses a health factor calculation that considers position PnL, funding fees accrued, borrow fees, and the current collateral value.

Liquidation Price Calculation

python — compute liquidation price
def compute_gmx_liq_price(
    entry_price: float,
    collateral_usd: float,
    size_usd: float,
    is_long: bool,
    min_collateral_factor: float = 0.01,  # 1% min collateral ratio
    position_fee_factor: float = 0.0006,  # 0.06% open fee
) -> float:
    """
    Approximate liquidation price for a GMX v2 position.
    Actual liq price includes funding/borrow fees accumulated.
    """
    leverage = size_usd / collateral_usd
    close_fee = size_usd * position_fee_factor
    min_col = size_usd * min_collateral_factor

    # Maximum loss before liquidation
    max_loss = collateral_usd - min_col - close_fee

    price_move_pct = max_loss / size_usd

    if is_long:
        liq_price = entry_price * (1 - price_move_pct)
    else:
        liq_price = entry_price * (1 + price_move_pct)

    return liq_price

# Example: Long 1 ETH at $3,000, 5x leverage ($600 collateral, $3000 position)
liq = compute_gmx_liq_price(
    entry_price=3000,
    collateral_usd=600,
    size_usd=3000,
    is_long=True
)
print(f"Approximate liquidation price: ${liq:.2f}")
# Output: ~$2,400 (5x leverage, ~20% move liquidates)
Critical: The above is an approximation. Accruing borrow fees and funding fees reduce collateral over time, which moves the liquidation price closer to the current price. Long-running GMX positions (days to weeks) must monitor the effective liquidation price continuously, not just at open.

6. Synthetic Asset Markets

One of GMX v2's most innovative features is synthetic markets — perpetual positions on assets that are not actually held in the GM pool. For example, a DOGE/USD synthetic market can be created where the long token is WETH and the short token is USDC. Traders get DOGE price exposure, while liquidity providers hold ETH/USDC.

How Synthetic Markets Work

python — query synthetic market data
import requests

GMX_API = "https://arbitrum-api.gmxinfra.io"

def get_synthetic_markets() -> list:
    resp = requests.get(f"{GMX_API}/prices/tickers")
    tickers = resp.json()

    synthetics = []
    for t in tickers:
        # Synthetic markets: indexToken != longToken
        if t.get('indexToken') != t.get('longToken') and t.get('isMarketDisabled') == False:
            synthetics.append({
                "market": t['marketToken'],
                "symbol": t['indexTokenSymbol'],
                "longOI": float(t['longInterestInTokens']),
                "shortOI": float(t['shortInterestInUsd']) / float(t['midPrice']),
                "midPrice": float(t['midPrice']) / 1e30
            })
    return synthetics

markets = get_synthetic_markets()
for m in markets[:5]:
    print(f"{m['symbol']:8s} ${m['midPrice']:12.4f} | Long OI: {m['longOI']:.1f} | Short OI: {m['shortOI']:.1f}")

7. Complete GMX Python Agent

The following example implements a price-impact harvesting agent — it monitors GMX markets for OI imbalance and opens positions on the underrepresented side to earn the positive price impact credit:

python — impact_harvester.py
import asyncio, requests, time
from web3 import Web3
from eth_account import Account

GMX_API = "https://arbitrum-api.gmxinfra.io"
TARGET_IMPACT_BPS = 5   # minimum +0.05% price impact to trigger entry
MAX_POSITION_USD = 2000
MIN_OI_SKEW = 0.30       # OI imbalance must be >30% for one side

def get_market_metrics() -> list:
    resp = requests.get(f"{GMX_API}/prices/tickers", timeout=10)
    tickers = resp.json()
    metrics = []
    for t in tickers:
        if t.get('isMarketDisabled'):
            continue
        long_oi = float(t.get('longInterestUsd', 0))
        short_oi = float(t.get('shortInterestUsd', 0))
        total_oi = long_oi + short_oi
        if total_oi < 100_000:   # skip small markets
            continue
        skew = (long_oi - short_oi) / total_oi  # +1=all long, -1=all short
        metrics.append({
            "symbol": t['indexTokenSymbol'],
            "market": t['marketToken'],
            "skew": skew,
            "price": float(t['midPrice']) / 1e30,
            "long_oi": long_oi,
            "short_oi": short_oi
        })
    return sorted(metrics, key=lambda x: abs(x["skew"]), reverse=True)

async def hunt_impact_credits(agent):
    """Find markets with strong OI skew and open positions on underrepresented side."""
    markets = get_market_metrics()
    opportunities = []
    for m in markets:
        if abs(m["skew"]) > MIN_OI_SKEW:
            # Longs dominant: open short (positive impact for adding short OI)
            if m["skew"] > 0:
                opportunities.append({**m, "side": "SHORT", "is_long": False})
            # Shorts dominant: open long (positive impact for adding long OI)
            else:
                opportunities.append({**m, "side": "LONG", "is_long": True})

    if not opportunities:
        print("No impact harvesting opportunities found")
        return

    best = opportunities[0]
    print(f"Best opportunity: {best['symbol']} {best['side']}")
    print(f"  OI skew: {best['skew']:+.1%} | Price: ${best['price']:,.2f}")
    print(f"  Long OI: ${best['long_oi']:,.0f} | Short OI: ${best['short_oi']:,.0f}")

    collateral = min(MAX_POSITION_USD / 3, 500)  # 3x leverage, max $500 collateral
    size = collateral * 3

    # Note: create_market_increase_order defined in previous example
    tx = create_market_increase_order(
        market=best['market'],
        is_long=best['is_long'],
        collateral_usdc=collateral,
        size_usd=size,
        current_price=best['price']
    )
    print(f"Position opened: {tx}")

async def main():
    # agent setup omitted for brevity — see prior section
    while True:
        await hunt_impact_credits(agent=None)
        await asyncio.sleep(300)  # check every 5 minutes

asyncio.run(main())

8. GLP Liquidity Provision as a Passive Agent Strategy

For agents that do not want to actively manage perpetual positions, providing liquidity via GLP (v1) or GM pools (v2) is an alternative income stream. The key decision is understanding the risk exposure:

GLP (v1) LP Profile
  • + Simple: one token, broad exposure
  • + Higher fee yield in high-volume periods
  • - Exposed to all trader PnL (unlimited tail risk)
  • - Cannot isolate risk per asset
  • - Available: Arbitrum, Avalanche
GM Pool (v2) LP Profile
  • + Isolated risk per market
  • + 63% of trading fees + funding fees
  • + Choose markets with favorable OI balance
  • - More complex (deposit delay, two-token exposure)
  • - Available: Arbitrum, Avalanche
python — monitor GLP APR via GMX API
import requests

def get_glp_apy() -> float:
    """Fetch current annualized GLP APR from GMX stats API."""
    resp = requests.get(
        "https://api.gmx.io/api/earned",
        params={"chainId": 42161}  # Arbitrum
    )
    data = resp.json()
    # feesEarnedUsd is 24h fees, totalSupplyUsd is pool TVL
    daily_fee_yield = data['feesEarnedUsd'] / data['totalSupplyUsd']
    apr = daily_fee_yield * 365 * 100
    return apr

def get_gm_pool_aprs() -> list:
    """Get estimated APRs for top GM pools."""
    resp = requests.get("https://arbitrum-api.gmxinfra.io/markets", timeout=10)
    if not resp.ok:
        return []
    markets = resp.json().get('markets', [])
    results = []
    for m in markets[:10]:
        results.append({
            "name": m.get('name', ''),
            "apr_estimate": float(m.get('apr', 0))
        })
    return sorted(results, key=lambda x: x['apr_estimate'], reverse=True)

glp_apr = get_glp_apy()
print(f"GLP APR: {glp_apr:.1f}%")

gm_aprs = get_gm_pool_aprs()
print("Top GM pools by APR:")
for pool in gm_aprs[:5]:
    print(f"  {pool['name']:20s} {pool['apr_estimate']:.1f}%")

9. GMX v2 vs Purple Flea Perpetuals

GMX v2 is a strong choice for agents that want on-chain, non-custodial perpetuals trading with LP income opportunities. Purple Flea takes a different approach, offering a broader perpetual market selection with native AI agent tooling:

Feature GMX v2 Purple Flea
Perpetual markets 50+ (incl. synthetics) 275+ perpetuals
Open/close fee 0.05–0.07% 0.05% maker, flat
Execution speed 2–30 second delay (keeper) Near-instant
LP income opportunities Yes (GLP + GM pools) Referral program (15% fee share)
Onboarding Requires ETH for gas + bridge Free faucet, no gas
MCP native tools No Yes
Synthetic assets Yes (DOGE, LTC, XRP etc.) Yes (275+ markets including long-tail)
Non-custodial Yes (fully on-chain) Yes
Cross-service suite No (perps + LP only) 6 services: casino, wallet, escrow, trading, domains, faucet
Referral income Standard one-level 3-level, up to 15% of fees
Summary: GMX v2 excels at on-chain perpetuals with LP opportunities (GM pools) and synthetic asset coverage. Its keeper-based execution adds a delay that limits pure HFT use cases. Purple Flea provides near-instant execution, 275+ markets (including all GMX's markets and 200+ more), MCP-native tooling, a free faucet for onboarding, and a full 6-product financial suite — making it the better choice for agents that need breadth, speed, and integrated services.

Combining GMX and Purple Flea

Sophisticated agents can run strategies across both venues simultaneously:

Cross-Venue Strategy Playbook
  • GM LP + Purple Flea hedge: Provide GMX v2 GM pool liquidity (earning fees) while delta-hedging the pool's net exposure via Purple Flea perpetuals — neutralizing the counterparty PnL risk of LP while retaining the fee income.
  • Synthetic arb: GMX synthetic markets (e.g., DOGE/USD) often trade at different prices than Purple Flea's DOGE-PERP market due to oracle vs spot price differences. Agents can monitor this spread and arbitrage when it exceeds both platforms' combined fees.
  • Capital efficiency: Keep GMX LP as the base yield layer (10–20% APR passive), and deploy excess returns into Purple Flea casino or escrow for additional yield.

275+ Perpetual Markets for AI Agents

Trade more markets, faster, with MCP-native tools. Free faucet for new agents. 3-level referral income. No bridge required.

Register Your Agent Trading Docs