How to Build a Centrifuge RWA Portfolio Monitor with CoinMarketCap API
CoinMarketCap API DIY

How to Build a Centrifuge RWA Portfolio Monitor with CoinMarketCap API

Learn how to build a Centrifuge RWA portfolio monitor using CoinMarketCap API for CFG token monitoring and real-world asset narrative discovery.

How to Build a Centrifuge RWA Portfolio Monitor with CoinMarketCap API

İçindekiler

Centrifuge is the infrastructure layer for real-world assets in DeFi.

Where Ondo Finance tokenises T-bills and Maple Finance originates institutional loans, Centrifuge provides the underlying protocol: a set of smart contracts and a financing primitive called Centrifuge Pools that lets any asset originator — invoice financiers, real estate lenders, trade finance providers — turn their off-chain cash flows into on-chain tranched debt positions.

Centrifuge pools are structured as senior and junior tranches. Senior tranche holders receive lower yield with principal protection. Junior tranche holders absorb first losses in exchange for higher returns. This is standard structured finance applied to DeFi.

By early 2026, Centrifuge has tokenised over $500M in real-world assets across dozens of pools including trade finance, real estate, and consumer credit. The protocol runs as a Polkadot parachain (Centrifuge Chain) but its pools are accessible via Ethereum smart contracts through the Centrifuge Connectors bridge.

CFG is the native token of Centrifuge Chain, used for governance, transaction fees, and staking.

For developers building RWA monitors, the key signals are: CFG price and momentum, RWA narrative trends across the broader sector, comparison against competing RWA tokens, and macro regime conditions that favour institutional on-chain credit.

In this guide, you will build a Centrifuge RWA Portfolio Monitor with CoinMarketCap API, where:
  • CoinMarketCap Core API powers the market signal engine
  • Centrifuge's official API and on-chain contracts handle real pool state, NAV, and tranche yields

Why No DEX Endpoints

CFG is the native token of Centrifuge Chain, a Polkadot parachain. It is not an ERC-20 or SPL token. The CMC DEX API indexes EVM, Solana, and BNB Chain AMMs only.

CFG trades primarily on centralised exchanges. Like TIA (Celestia) and STX (Stacks), all price and market signals come from the Core API. CEX volume is the primary liquidity proxy.

Architecture Clarification

The CoinMarketCap API acts strictly as an off-chain Signal Layer for CFG token price monitoring, RWA narrative trend detection, and market regime filtering. It is not a pool NAV oracle, tranche yield feed, or asset originator monitor.

Real pool NAV, tranche yields, asset originator credit risk, and pool utilisation must be validated directly via Centrifuge's official API or Centrifuge Chain RPC.

Project Setup

import os

import time

import requests

CMC_API_KEY  = os.getenv("CMC_API_KEY")

CMC_BASE_URL = "https://pro-api.coinmarketcap.com"

HEADERS = {

"Accept":            "application/json",
"X-CMC_PRO_API_KEY": CMC_API_KEY,

}

# Core assets — MPL is deprecated (is_active=0), use SYRUP instead

RWA_ASSETS = ["CFG", "ONDO", "SYRUP", "POLYX"]

# RWA tags for local filtering — exact CMC tag strings

RWA_TAGS = {"real-world-assets-protocols", "lending-borowing", "polkadot-ecosystem"}

Step 1: Map Assets

def map_assets(symbols="CFG,ONDO,SYRUP,POLYX"):

url = f"{CMC_BASE_URL}/v1/cryptocurrency/map"

params = {"symbol": symbols}

r = requests.get(url, headers=HEADERS, params=params)

r.raise_for_status()

return r.json()["data"]

def resolve_cfg_id(map_data):

for asset in map_data:

if (

asset.get("symbol") == "CFG"

and "centrifuge" in (asset.get("slug") or "").lower()

):

return asset["id"]

return next((a["id"] for a in map_data if a.get("symbol") == "CFG"), None)

CFG is a Polkadot parachain native token. platform will be null — it is not an ERC-20.

Step 2: Fetch Quotes

def fetch_quotes(ids):

url = f"{CMC_BASE_URL}/v3/cryptocurrency/quotes/latest"

params = {"id": ",".join(str(i) for i in ids)}

r = requests.get(url, headers=HEADERS, params=params)

r.raise_for_status()

return r.json()["data"]

def parse_quote(asset):

usd = next(

(q for q in asset.get("quote", []) if q.get("symbol") == "USD"),

{}

)

return {

"id":               asset.get("id"),
"symbol":           asset.get("symbol"),
"price":            usd.get("price"),
"volume_24h":       usd.get("volume_24h"),
"market_cap":       usd.get("market_cap"),
"fdv":              usd.get("fully_diluted_market_cap"),
"pct_change_1h":    usd.get("percent_change_1h"),
"pct_change_24h":   usd.get("percent_change_24h"),
"pct_change_7d":    usd.get("percent_change_7d"),
"tvl":              usd.get("tvl"),          # null for CFG — parachain token, not DeFi protocol
"tvl_ratio":        asset.get("tvl_ratio"),
"num_market_pairs": asset.get("num_market_pairs"),
}

quotes = {str(a["id"]): parse_quote(a) for a in raw_quotes}

Step 3: Score CFG

def compute_cfg_score(quote):

score = 0

pct_1h  = quote.get("pct_change_1h")  or 0

pct_24h = quote.get("pct_change_24h") or 0

pct_7d  = quote.get("pct_change_7d")  or 0

if pct_24h > 10:    score += 30

elif pct_24h > 5:   score += 20

elif pct_24h > 2:   score += 10

elif pct_24h < -15: score -= 25

if pct_7d > 20:     score += 20

elif pct_7d > 10:   score += 10

if pct_1h > 2:      score += 15
elif pct_1h > 0.5:  score += 8

vol = quote.get("volume_24h") or 0

if vol > 10_000_000:  score += 20

elif vol > 2_000_000: score += 10

mcap = quote.get("market_cap") or 0

if mcap > 200_000_000:  score += 15

elif mcap > 50_000_000: score += 8

pairs = quote.get("num_market_pairs") or 0

if pairs > 20:  score += 8

elif pairs > 5: score += 4

return score

Step 4: Compare RWA Sector

def compare_rwa_sector(quotes, asset_ids):

comparison = []

for symbol in RWA_ASSETS:

asset_id = asset_ids.get(symbol)

if not asset_id:

continue

q = quotes.get(str(asset_id), {})

comparison.append({

"symbol":         symbol,
"market_cap":     q.get("market_cap")     or 0,
"volume_24h":     q.get("volume_24h")     or 0,
"pct_change_24h": q.get("pct_change_24h") or 0,
"pct_change_7d":  q.get("pct_change_7d")  or 0,

})

return sorted(comparison, key=lambda x: -x["pct_change_24h"])

Step 5: Track Momentum via Quote Polling

Without DEX candle data, compare successive polls to detect price acceleration:

def compute_momentum_delta(prev_quote, curr_quote):

prev_price = prev_quote.get("price") or 0

curr_price = curr_quote.get("price") or 0

prev_vol   = prev_quote.get("volume_24h") or 0

curr_vol   = curr_quote.get("volume_24h") or 0

if prev_price == 0:

return None

return {

"price_delta_pct": ((curr_price - prev_price) / prev_price) * 100,
"vol_delta_pct":   ((curr_vol   - prev_vol)   / prev_vol)   * 100 if prev_vol else 0,
"accelerating":    curr_price > prev_price and curr_vol > prev_vol,

}

Step 6: RWA Narrative and Macro

def fetch_rwa_listings():

url = f"{CMC_BASE_URL}/v3/cryptocurrency/listings/latest"

params = {"sort": "volume_24h", "sort_dir": "desc", "limit": 200, "volume_24h_min": 500_000}

r = requests.get(url, headers=HEADERS, params=params)

r.raise_for_status()

return r.json()["data"]

def filter_rwa_assets(assets):

results = []

for asset in assets:

tags = set(asset.get("tags") or [])

if tags & RWA_TAGS or asset.get("symbol") in RWA_ASSETS:

results.append(asset)

return results

def fetch_macro_regime():

fg_url = f"{CMC_BASE_URL}/v3/fear-and-greed/latest"

as_url = f"{CMC_BASE_URL}/v1/altcoin-season-index/latest"

fg     = requests.get(fg_url, headers=HEADERS).json()["data"]

as_idx = requests.get(as_url, headers=HEADERS).json()["data"]

return {

"fear_greed_value": fg.get("value"),

"altcoin_index":    as_idx.get("altcoin_index"),

}

def is_regime_favorable(regime):

return (regime.get("fear_greed_value") or 0) > 50 and (regime.get("altcoin_index") or 0) >= 45

Step 7: End-to-End Flow

def run_centrifuge_monitor(asset_ids, prev_cfg_quote=None):

regime = fetch_macro_regime()

raw_quotes = fetch_quotes(list(asset_ids.values()))

quotes = {str(a["id"]): parse_quote(a) for a in raw_quotes}

cfg_id    = asset_ids.get("CFG")

cfg_quote = quotes.get(str(cfg_id), {})

cfg_score = compute_cfg_score(cfg_quote)

if not is_regime_favorable(regime):

cfg_score -= 15

momentum = None

if prev_cfg_quote:

momentum = compute_momentum_delta(prev_cfg_quote, cfg_quote)

rwa_comparison = compare_rwa_sector(quotes, asset_ids)

try:

listings   = fetch_rwa_listings()

rwa_assets = filter_rwa_assets(listings)

except Exception:

rwa_assets = []

rwa_trending = [

{

"symbol":  a.get("symbol"),
"pct_24h": (a.get("quote") or [{}])[0].get("percent_change_24h"),

}

for a in rwa_assets[:10]

]

return {

"cfg_signal": {
"score":            cfg_score,
"price":            cfg_quote.get("price"),
"pct_24h":          cfg_quote.get("pct_change_24h"),
"volume_24h":       cfg_quote.get("volume_24h"),
"market_cap":       cfg_quote.get("market_cap"),
"momentum":         momentum,
"regime_favorable": is_regime_favorable(regime),
},
"rwa_comparison": rwa_comparison,
"rwa_trending":   rwa_trending,
"regime":         regime,
"curr_quote":     cfg_quote,
}

Pass curr_quote as prev_cfg_quote on the next poll cycle.

Common Mistakes

Using DEX endpoints for CFG

CFG is a Polkadot parachain native token. The CMC DEX API covers EVM, Solana, and BNB Chain only. /v4/dex/spot-pairs/latest, /v1/dex/token/pools, and /v1/k-line/candles will not return data for CFG.

Not filtering CFG by slug

symbol=CFG may return multiple entries. Filter by slug containing "centrifuge".

Parsing quote as a dict in v3

quote is a list. Use next((q for q in asset.get("quote", []) if q.get("symbol") == "USD"), {}).

Expecting tvl to be populated for CFG

CFG is a parachain governance token, not a DeFi protocol with on-chain TVL tracked by CMC. Parse defensively.

Treating CMC as a pool NAV oracle

CMC tracks market prices, not Centrifuge pool NAV or tranche yields. Use Centrifuge's official API for real pool data.

Final Thoughts

The key separation:

  • CoinMarketCap identifies market conditions and RWA narrative momentum
  • Centrifuge's official API validates real pool NAV, tranche yields, and asset originator state

Next Steps

  • compare CFG against ONDO, SYRUP, POLYX for RWA sector rotation signals
  • integrate Centrifuge API for live pool NAV and tranche utilisation data
  • track new pool launches as a protocol growth signal
0 people liked this article