Maple Finance is the main institutional lending protocol in DeFi.
Maple operates two distinct merchandise. Maple V2 supplies undercollateralised credit score to institutional debtors — hedge funds, market makers, and buying and selling companies — underwritten by skilled pool delegates who assess creditworthiness. Maple Direct affords overcollateralised loans to crypto-native debtors at aggressive charges.
By early 2026, Maple has originated over $3.5B in institutional loans with near-zero defaults post-2022. The protocol has pivoted towards Actual World Property: its syrupUSDC product deploys capital into T-bills and short-duration mounted revenue, making it a yield-bearing steady that competes with Ondo’s OUSG and Ethena’s sUSDe.
MPL is the legacy governance token. SYRUP is the newer staking and revenue-sharing token launched alongside the syrup product suite.
For builders constructing lending analytics bots, the important thing indicators are: MPL and SYRUP worth and momentum, RWA and institutional lending narrative developments, comparability in opposition to competing RWA protocols, and macro regime situations that favour institutional credit score.
On this information, you’ll construct a Maple Finance Lending Analytics Bot with CoinMarketCap API, the place:
- CoinMarketCap API powers the market sign engine
- Maple Finance’s official API and on-chain contracts deal with actual mortgage state, pool utilisation, and yield charges
Structure Clarification
The CoinMarketCap API acts strictly as an off-chain Sign Layer for MPL and SYRUP token worth monitoring, RWA lending narrative development detection, and market regime filtering. It’s not a mortgage state oracle, pool utilisation monitor, or credit score threat engine.
Actual mortgage origination quantity, pool utilisation charges, borrower creditworthiness, default threat, and syrupUSDC yield charges have to be validated immediately by way of Maple Finance’s official API or on-chain contracts.
Mission Setup
import os
import time
import datetime
import requests
CMC_API_KEY = os.getenv(“CMC_API_KEY”)
CMC_BASE_URL = “https://pro-api.coinmarketcap.com”
HEADERS = {
“Settle for”: “utility/json”,
“X-CMC_PRO_API_KEY”: CMC_API_KEY,}
# Maple Finance belongings
MAPLE_ASSETS = [“MPL”, “SYRUP”]
# RWA lending sector for comparability
RWA_ASSETS = [“MPL”, “SYRUP”, “ONDO”, “POLYX”]
# RWA/lending tags for native filtering
RWA_TAGS = {“real-world-assets”, “lending-borrowing”, “maple-ecosystem”, “institutional-defi”}
# MPL DEX config — Ethereum
MPL_NETWORK = “ethereum”
MPL_DEX = “uniswap-v3”
Step 1: Map Property to CoinMarketCap IDs
def map_assets(symbols=”MPL,SYRUP,ONDO,POLYX”):
url = f”{CMC_BASE_URL}/v1/cryptocurrency/map”
params = {“image”: symbols}
r = requests.get(url, headers=HEADERS, params=params)
r.raise_for_status()
return r.json()[“data”]
def resolve_mpl_id(map_data):
for asset in map_data:
if (
asset.get(“image”) == “MPL”
and “maple” in (asset.get(“slug”) or “”).decrease()
):
return asset[“id”]
return subsequent((a[“id”] for a in map_data if a.get(“image”) == “MPL”), None)
def resolve_syrup_id(map_data):
for asset in map_data:
if (
asset.get(“image”) == “SYRUP”
and “maple” in (asset.get(“slug”) or “”).decrease()
):
return asset[“id”]
return subsequent((a[“id”] for a in map_data if a.get(“image”) == “SYRUP”), None)
Each MPL and SYRUP might have image collisions. Filter by slug containing “maple” to isolate the proper entries.
Step 2: Fetch Quotes
def fetch_quotes(ids):
url = f”{CMC_BASE_URL}/v3/cryptocurrency/quotes/newest”
params = {“id”: “,”.be a part of(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):
# quote is a LIST in v3 — use subsequent() to extract USD entry
usd = subsequent(
(q for q in asset.get(“quote”, []) if q.get(“image”) == “USD”),
{}
)
return {
“id”: asset.get(“id”),
“image”: asset.get(“image”),
“worth”: usd.get(“worth”),
“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”),
“tvl_ratio”: asset.get(“tvl_ratio”),
“num_market_pairs”: asset.get(“num_market_pairs”),}
# raw_quotes is a record — construct dict keyed by string ID
quotes = {str(a[“id”]): parse_quote(a) for a in raw_quotes}
tvl and tvl_ratio could also be populated for MPL given Maple’s $3.5B+ in mortgage originations. Parse defensively both means.
Step 3: Rating MPL and SYRUP
def compute_maple_score(quote):
rating = 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: rating += 30
elif pct_24h > 5: rating += 20
elif pct_24h > 2: rating += 10
elif pct_24h < -15: rating -= 25
if pct_7d > 20: rating += 20
elif pct_7d > 10: rating += 10
if pct_1h > 2: rating += 15
elif pct_1h > 0.5: rating += 8
vol = quote.get(“volume_24h”) or 0
if vol > 10_000_000: rating += 20
elif vol > 2_000_000: rating += 10
mcap = quote.get(“market_cap”) or 0
if mcap > 200_000_000: rating += 15
elif mcap > 50_000_000: rating += 8
tvl_ratio = quote.get(“tvl_ratio”) or 0
if 0 < tvl_ratio < 1: rating += 10
return rating
Step 4: Examine RWA Lending Sector
def compare_rwa_sector(quotes, asset_ids):
comparability = []
for image in RWA_ASSETS:
asset_id = asset_ids.get(image)
if not asset_id:
proceed
q = quotes.get(str(asset_id), {})
comparability.append({
“image”: image,
“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,
“tvl_ratio”: q.get(“tvl_ratio”) or 0,})
return sorted(comparability, key=lambda x: -x[“pct_change_24h”])
Step 5: Validate MPL DEX Liquidity
dex_slug is required alongside network_slug. Passing just one returns a 400 error.
def fetch_mpl_pairs(mpl_contract_address):
url = f”{CMC_BASE_URL}/v4/dex/spot-pairs/newest”
params = {“network_slug”: MPL_NETWORK, “dex_slug”: MPL_DEX}
r = requests.get(url, headers=HEADERS, params=params)
r.raise_for_status()
pairs = r.json()[“data”]
return [
p for p in pairs
if mpl_contract_address.lower() in (
(p.get(“base_asset_contract_address”) or “”).lower(),
(p.get(“quote_asset_contract_address”) or “”).lower()
)
]
def fetch_mpl_pools(mpl_contract_address):
url = f”{CMC_BASE_URL}/v1/dex/token/swimming pools”
params = {“deal with”: mpl_contract_address, “platform”: “ethereum”}
r = requests.get(url, headers=HEADERS, params=params)
r.raise_for_status()
return r.json()[“data”]
def get_best_pool(swimming pools, min_liquidity=100_000):
# liqUsd is returned as a string — solid to float earlier than evaluating
legitimate = [
p for p in pools
if float(p.get(“liqUsd”) or 0) >= min_liquidity
]
return max(legitimate, key=lambda p: float(p.get(“liqUsd”) or 0)) if legitimate else None
Step 6: Pool-Stage Quote
def fetch_pool_quote(pool_address, network_slug=”ethereum”):
url = f”{CMC_BASE_URL}/v4/dex/pairs/quotes/newest”
params = {
“network_slug”: network_slug, # required alongside contract_address
“contract_address”: pool_address,}
r = requests.get(url, headers=HEADERS, params=params)
r.raise_for_status()
return r.json()[“data”]
Step 7: Candle Momentum
def fetch_candles(contract_address, interval=”1h”):
url = f”{CMC_BASE_URL}/v1/k-line/candles”
params = {“platform”: “ethereum”, “deal with”: contract_address, “interval”: interval}
r = requests.get(url, headers=HEADERS, params=params)
r.raise_for_status()
return r.json()[“data”]
def parse_candle(c):
return {
“open”: c[0],
“excessive”: c[1],
“low”: c[2],
“shut”: c[3],
“quantity”: c[4],
“timestamp”: c[5],
“merchants”: c[6] or 0,
“datetime”: datetime.datetime.fromtimestamp(c[5] / 1000),}
[5] is UNIX milliseconds — divide by 1000. [6] might be None in stay knowledge.
Step 8: RWA Narrative and Macro
def fetch_rwa_listings():
url = f”{CMC_BASE_URL}/v3/cryptocurrency/listings/newest”
params = {“kind”: “volume_24h”, “sort_dir”: “desc”, “restrict”: 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(belongings):
outcomes = []
for asset in belongings:
tags = set(asset.get(“tags”) or [])
if tags & RWA_TAGS or asset.get(“image”) in RWA_ASSETS:
outcomes.append(asset)
return outcomes
def fetch_macro_regime():
fg_url = f”{CMC_BASE_URL}/v3/fear-and-greed/newest”
as_url = f”{CMC_BASE_URL}/v1/altcoin-season-index/newest”
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(“worth”),
“altcoin_index”: as_idx.get(“altcoin_index”),}
def is_regime_favorable(regime):
# Institutional lending demand rises with risk-on situations
return (regime.get(“fear_greed_value”) or 0) > 55 and (regime.get(“altcoin_index”) or 0) >= 50
Step 9: Finish-to-Finish Stream
def run_maple_lending_bot(asset_ids, mpl_contract_address):
regime = fetch_macro_regime()
raw_quotes = fetch_quotes(record(asset_ids.values()))
quotes = {str(a[“id”]): parse_quote(a) for a in raw_quotes}
mpl_id = asset_ids.get(“MPL”)
syrup_id = asset_ids.get(“SYRUP”)
mpl_q = quotes.get(str(mpl_id), {})
syrup_q = quotes.get(str(syrup_id), {})
mpl_score = compute_maple_score(mpl_q)
syrup_score = compute_maple_score(syrup_q)
if not is_regime_favorable(regime):
mpl_score -= 15
syrup_score -= 15
rwa_comparison = compare_rwa_sector(quotes, asset_ids)
pool_liq = None
if mpl_contract_address:
attempt:
swimming pools = fetch_mpl_pools(mpl_contract_address)
greatest = get_best_pool(swimming pools)
pool_liq = (greatest or {}).get(“liqUsd”)
besides Exception:
go
attempt:
listings = fetch_rwa_listings()
rwa_assets = filter_rwa_assets(listings)
besides 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 {
“mpl_signal”: {
“rating”: mpl_score,
“worth”: mpl_q.get(“worth”),
“pct_24h”: mpl_q.get(“pct_change_24h”),
“volume_24h”: mpl_q.get(“volume_24h”),
“market_cap”: mpl_q.get(“market_cap”),
“dex_pool_liq”: pool_liq,
“regime_favorable”: is_regime_favorable(regime),
},
“syrup_signal”: {
“rating”: syrup_score,
“worth”: syrup_q.get(“worth”),
“pct_24h”: syrup_q.get(“pct_change_24h”),
“volume_24h”: syrup_q.get(“volume_24h”),
“market_cap”: syrup_q.get(“market_cap”),
},
“rwa_comparison”: rwa_comparison,
“rwa_trending”: rwa_trending,
“regime”: regime,
}
Widespread Errors
Not filtering MPL and SYRUP by slug
Each symbols might return a number of entries. Filter by slug containing “maple” to isolate the proper tokens.
Parsing quote as a dict in v3
quote is a record. Use subsequent((q for q in asset.get(“quote”, []) if q.get(“image”) == “USD”), {}).
Not casting liqUsd to float
liqUsd is a string. At all times solid: float(p.get(“liqUsd”) or 0).
Passing solely network_slug to /v4/dex/spot-pairs/newest
dex_slug is required. Omitting it returns a 400 error.
Omitting network_slug from pool quotes
/v4/dex/pairs/quotes/newest requires network_slug alongside contract_address.
Treating CMC as a mortgage state oracle
CMC tracks market costs, not mortgage origination quantity, pool utilisation, or borrower credit score threat. Use Maple’s official API for actual lending knowledge.
Ultimate Ideas
The important thing separation:
- CoinMarketCap identifies market situations and RWA lending narrative momentum
- Maple Finance’s official API validates actual mortgage state and yield charges
Subsequent Steps
- monitor MPL tvl_ratio as a protocol effectivity sign
- evaluate RWA sector rotation throughout MPL, ONDO, POLYX
- combine Maple Finance API for stay pool utilisation and mortgage origination knowledge
- cross-reference CMC regime indicators with TradFi credit score unfold knowledge












