Back to Blog

Developer / C03

SDK Python de MERX: Cero Dependencias, Potencia Total

El SDK Python de MERX proporciona una interfaz completa al intercambio de energía TRON de MERX utilizando solo la biblioteca estándar de Python - sin requests, sin httpx, sin aiohttp. Este artículo cubre los cuatro módulos del SDK (precios, órdenes, saldo, webhooks), todos los métodos con ejemplos funcionales, el sistema de tipos dataclass, manejo de errores y una comparación con el SDK de JavaScript para equipos que usan ambos lenguajes.

Por Qué Cero Dependencias

La mayoría de los clientes HTTP de Python traen consigo un árbol de dependencias. La librería requests por sí sola trae urllib3, certifi, charset-normalizer e idna. Para un SDK ligero que realiza unos pocos llamados a API, ese sobrecargo es innecesario.

El SDK Python de MERX utiliza solo urllib.request de la biblioteca estándar. Esto significa:

El compromiso es la ausencia de soporte asincrónico. El SDK utiliza llamadas HTTP sincrónicas. Si necesitas async, la API REST es lo suficientemente simple para llamar directamente con aiohttp o httpx usando los patrones mostrados en este artículo.

Instalación

pip install merx-sdk

Eso es todo. No se instalarán dependencias junto a él.

Inicio Rápido

from merx import MerxClient

client = MerxClient(api_key="sk_live_your_key_here")

# Verificar precios actuales
prices = client.prices.list()
for p in prices:
    if p.energy_prices:
        print(f"{p.provider}: {p.energy_prices[0].price_sun} SUN/energy")

# Comprar energía
order = client.orders.create(
    resource_type="ENERGY",
    amount=65000,
    target_address="TYourTargetAddressHere",
    duration_sec=3600,
)
print(f"Order {order.id}: {order.status}")

El cliente se inicializa con tu clave API del panel de MERX en merx.exchange. El parámetro opcional base_url tiene como valor predeterminado https://merx.exchange.

client = MerxClient(
    api_key="sk_live_your_key_here",
    base_url="https://merx.exchange",  # Opcional
)

El Sistema de Tipos

Todas las respuestas de API se analizan en un dataclass de Python. Sin diccionarios sin procesar, sin datos sin tipo. Tu IDE obtiene autocompletado completo y tu verificador de tipos detecta errores antes del tiempo de ejecución.

El SDK exporta 16 tipos de dataclass:

from merx import (
    Balance,
    DepositInfo,
    Fill,
    HistoryEntry,
    HistorySummary,
    Order,
    OrderPreview,
    OrderWithFills,
    PreviewMatch,
    PriceHistoryEntry,
    PricePoint,
    PriceStats,
    ProviderPrice,
    Webhook,
    Withdrawal,
    MerxError,
)

Definiciones de Tipos Clave

@dataclass
class PricePoint:
    duration_sec: int
    price_sun: int

@dataclass
class ProviderPrice:
    provider: str
    is_market: bool
    energy_prices: list[PricePoint]
    bandwidth_prices: list[PricePoint]
    available_energy: int
    available_bandwidth: int
    fetched_at: int

@dataclass
class Order:
    id: str
    resource_type: str       # "ENERGY" o "BANDWIDTH"
    order_type: str          # "MARKET", "LIMIT", "PERIODIC", "BROADCAST"
    status: str              # "PENDING", "EXECUTING", "FILLED", "FAILED", "CANCELLED"
    amount: int
    target_address: str
    duration_sec: int
    total_cost_sun: Optional[int] = None
    total_fee_sun: Optional[int] = None
    created_at: str = ""
    filled_at: Optional[str] = None
    expires_at: Optional[str] = None

@dataclass
class Fill:
    provider: str
    amount: int
    price_sun: int
    cost_sun: int
    tx_id: Optional[str] = None
    status: str = ""
    delegation_tx: Optional[str] = None
    verified: bool = False
    tronscan_url: Optional[str] = None

Todos los dataclasses utilizan indicaciones de tipo de Python estándar. Los campos con valores predeterminados son campos de respuesta API opcionales que pueden no estar presentes en cada respuesta.

Módulo 1: Precios

El módulo de precios proporciona cinco métodos para consultar datos de precios en tiempo real e históricos. Todos los puntos finales de precios son públicos y no requieren autenticación, pero el SDK siempre envía el encabezado de clave API para mantener coherencia.

prices.list()

Devuelve precios actuales de todos los proveedores activos.

prices = client.prices.list()

for p in prices:
    print(f"\n{p.provider} (market={p.is_market}):")
    print(f"  Available energy: {p.available_energy:,}")
    for tier in p.energy_prices:
        hours = tier.duration_sec // 3600
        print(f"  {hours}h: {tier.price_sun} SUN/unit")

Devuelve list[ProviderPrice]. Cada entrada incluye el nombre del proveedor, si admite órdenes de mercado con duraciones flexibles, niveles de precios para energía y bandwidth, y capacidad disponible actual.

prices.best(resource, amount=None)

Devuelve el precio disponible más barato para un tipo de recurso.

best = client.prices.best("ENERGY")
print(f"Cheapest: {best.price_sun} SUN from {best.provider}")

# Solo proveedores con al menos 200,000 disponibles
best_large = client.prices.best("ENERGY", amount=200000)

Devuelve PriceHistoryEntry. El parámetro amount filtra proveedores que carecen de capacidad suficiente.

prices.history(provider=None, resource=None, period="24h")

Devuelve instantáneas de precios históricos para gráficos y análisis.

history = client.prices.history(
    provider="sohu",
    resource="ENERGY",
    period="7d",
)

for entry in history:
    print(f"{entry.polled_at}: {entry.price_sun} SUN ({entry.available:,} available)")

Devuelve list[PriceHistoryEntry]. Períodos disponibles: "1h", "6h", "24h", "7d", "30d".

prices.stats()

Devuelve estadísticas agregadas del mercado.

stats = client.prices.stats()
print(f"Best price: {stats.best_price_sun} SUN")
print(f"Average price: {stats.avg_price_sun} SUN")
print(f"Providers online: {stats.total_providers}")
print(f"Cheapest-provider changes (24h): {stats.cheapest_changes_24h}")

Devuelve PriceStats.

prices.preview(resource, amount, duration, max_price_sun=None)

Obtiene una vista previa de lo que costaría una orden antes de colocarla.

preview = client.prices.preview(
    resource="ENERGY",
    amount=100000,
    duration=86400,       # 24 horas
    max_price_sun=35,     # Techo opcional
)

if preview.best:
    print(f"Best: {preview.best.provider}")
    print(f"Cost: {preview.best.cost_trx} TRX")
    print(f"Price: {preview.best.price_sun} SUN/unit")

for fb in preview.fallbacks:
    print(f"Alternative: {fb.provider} at {fb.cost_trx} TRX")

if preview.no_providers:
    print("No providers available for these parameters")

Devuelve OrderPreview con una coincidencia best (o None), una lista de fallbacks, y un booleano no_providers.

Módulo 2: Órdenes

orders.create(...)

Crea una orden de energía o bandwidth.

order = client.orders.create(
    resource_type="ENERGY",
    amount=65000,
    target_address="TYourTargetAddress",
    duration_sec=3600,
    order_type="MARKET",           # Predeterminado
    max_price_sun=None,            # Requerido para órdenes LIMIT
    idempotency_key="unique-id",   # Previene órdenes duplicadas
)

print(f"Order {order.id}: {order.status}")

Devuelve Order. Nota que a diferencia del SDK de JavaScript donde los parámetros se pasan como un diccionario, el SDK de Python utiliza argumentos de palabra clave explícitos para mayor claridad.

El parámetro idempotency_key es crítico para sistemas en producción. Si un error de red causa un reintento, la API devuelve la orden original en lugar de crear una duplicada.

orders.list(limit=30, offset=0, status=None)

Lista órdenes con paginación.

orders, total = client.orders.list(limit=10, offset=0, status="FILLED")
print(f"{total} filled orders total")

for o in orders:
    cost_trx = (o.total_cost_sun or 0) / 1_000_000
    print(f"  {o.id}: {o.amount:,} {o.resource_type}, {cost_trx:.3f} TRX")

Devuelve tuple[list[Order], int] - la lista de órdenes y el recuento total para paginación.

orders.get(order_id)

Devuelve una sola orden con su desglose de rellenos.

order = client.orders.get("ord_abc123")

print(f"Status: {order.status}")
print(f"Total cost: {(order.total_cost_sun or 0) / 1_000_000:.3f} TRX")

for fill in order.fills:
    print(f"  {fill.provider}: {fill.amount:,} at {fill.price_sun} SUN")
    print(f"  Verified: {fill.verified}")
    if fill.tronscan_url:
        print(f"  TX: {fill.tronscan_url}")

Devuelve OrderWithFills, que extiende Order con una lista fills de objetos Fill.

Módulo 3: Saldo

balance.get()

Devuelve los saldos actuales de la cuenta.

bal = client.balance.get()
print(f"TRX: {bal.trx}")
print(f"USDT: {bal.usdt}")
print(f"Locked: {bal.trx_locked}")

Devuelve Balance. El campo trx_locked muestra TRX reservado para órdenes pendientes.

balance.deposit_info()

Devuelve la dirección de depósito y el memo para financiar tu cuenta.

info = client.balance.deposit_info()
print(f"Send TRX to: {info.address}")
print(f"Memo: {info.memo}")
print(f"Minimum: {info.min_amount_trx} TRX / {info.min_amount_usdt} USDT")

Devuelve DepositInfo. Siempre incluye el memo en tu transacción de depósito para acreditación automática.

balance.withdraw(address, amount, currency="TRX", idempotency_key=None)

Retira fondos a una dirección TRON externa.

withdrawal = client.balance.withdraw(
    address="TExternalAddress",
    amount=100,
    currency="TRX",
    idempotency_key="withdraw-unique-id",
)
print(f"Withdrawal {withdrawal.id}: {withdrawal.status}")

Devuelve Withdrawal.

balance.history(period="30D") y balance.summary()

history = client.balance.history("7D")
print(f"{len(history)} transactions in last 7 days")

summary = client.balance.summary()
print(f"Total orders: {summary.total_orders}")
print(f"Total energy: {summary.total_energy:,}")
print(f"Average price: {summary.avg_price_sun} SUN")
print(f"Total spent: {summary.total_spent_sun / 1_000_000:.3f} TRX")

Módulo 4: Webhooks

webhooks.create(url, events)

Crea una suscripción a webhook.

webhook = client.webhooks.create(
    url="https://your-server.com/merx-webhook",
    events=["order.filled", "order.failed", "deposit.received"],
)
print(f"Webhook ID: {webhook.id}")
print(f"Secret: {webhook.secret}")  # Se muestra solo una vez

Devuelve Webhook. El campo secret solo se incluye en la respuesta de creación y se usa para verificación de firma HMAC-SHA256.

Tipos de eventos disponibles: order.filled, order.failed, deposit.received, withdrawal.completed.

webhooks.list() y webhooks.delete(webhook_id)

webhooks = client.webhooks.list()
active = [w for w in webhooks if w.is_active]
print(f"{len(active)} active webhooks")

deleted = client.webhooks.delete("wh_abc123")
print(f"Deleted: {deleted}")  # True o False

Manejo de Errores

Todos los errores de API lanzan MerxError con un atributo code y un mensaje legible por humanos.

from merx import MerxClient, MerxError

client = MerxClient(api_key="sk_live_your_key_here")

try:
    order = client.orders.create(
        resource_type="ENERGY",
        amount=65000,
        target_address="TInvalidAddress",
        duration_sec=3600,
    )
except MerxError as e:
    print(f"Error [{e.code}]: {e}")
    # Output: Error [INVALID_ADDRESS]: Target address is not a valid TRON address

La clase MerxError extiende Exception, así que se integra naturalmente con el manejo de excepciones de Python. El atributo code proporciona clasificación legible por máquina:

Data table
CódigoSignificado
UNAUTHORIZEDClave API inválida o faltante
INSUFFICIENT_FUNDSSaldo de cuenta insuficiente
INVALID_ADDRESSNo es una dirección TRON válida
ORDER_NOT_FOUNDEl ID de orden no existe
DUPLICATE_REQUESTClave de idempotencia ya utilizada
RATE_LIMITEDDemasiadas solicitudes, retroceder
PROVIDER_UNAVAILABLENingún proveedor puede cumplir la solicitud
VALIDATION_ERRORCuerpo de solicitud o parámetros no válidos

Comparación con el SDK de JavaScript

Ambos SDKs cubren los mismos cuatro módulos con los mismos métodos. Las diferencias clave están en los idiomas de programación:

Data table
AspectoSDK PythonSDK JavaScript
Cliente HTTPurllib.request (stdlib)Native fetch
DependenciasCeroCero
Soporte asyncNo (solo sincrónico)Sí (todos los métodos devuelven Promises)
TiposDataclassesInterfaces TypeScript
Creación de ordenArgumentos de palabra claveParámetro objeto
Retorno de lista de órdenestuple[list, int]{ orders: [], total: number }
Eliminación de webhookDevuelve boolDevuelve { deleted: boolean }
Tiempo de ejecución mínimoPython 3.10+Node.js 18+
Sistema de módulosImportaciones estándarSolo ESM

Los dos SDKs están diseñados para sentirse naturales en sus respectivos lenguajes mientras mantienen paridad funcional. Un equipo que usa Python y JavaScript puede esperar un comportamiento idéntico en cada uno.

Patrón de Producción: Colocación de Órdenes Consciente de Precios

Aquí hay un flujo de producción completo que verifica precios actuales, valida el costo y crea una orden con idempotencia:

import uuid
from merx import MerxClient, MerxError

client = MerxClient(api_key="sk_live_your_key_here")

def buy_energy(target: str, amount: int = 65000, max_cost_trx: float = 5.0):
    """Compra energía con validación de costo e idempotencia."""

    # Obtener vista previa del costo primero
    preview = client.prices.preview(
        resource="ENERGY",
        amount=amount,
        duration=3600,
    )

    if preview.no_providers:
        raise RuntimeError("No energy providers available")

    cost = float(preview.best.cost_trx)
    if cost > max_cost_trx:
        raise RuntimeError(
            f"Cost {cost:.3f} TRX exceeds limit {max_cost_trx:.3f} TRX"
        )

    # Colocar la orden
    order = client.orders.create(
        resource_type="ENERGY",
        amount=amount,
        target_address=target,
        duration_sec=3600,
        idempotency_key=str(uuid.uuid4()),
    )

    print(f"Order {order.id} created at {preview.best.provider}")
    print(f"Expected cost: {cost:.3f} TRX")
    return order


try:
    order = buy_energy("TYourTargetAddress", amount=65000, max_cost_trx=3.0)
except MerxError as e:
    print(f"API error: [{e.code}] {e}")
except RuntimeError as e:
    print(f"Business logic error: {e}")

Verificación de Instalación

Después de instalar, verifica que el SDK esté funcionando:

from merx import MerxClient, __version__

print(f"merx-sdk version: {__version__}")

# Punto final público, no requiere autenticación
client = MerxClient(api_key="test")
try:
    prices = client.prices.list()
    print(f"Providers online: {len(prices)}")
    for p in prices:
        if p.energy_prices:
            print(f"  {p.provider}: {p.energy_prices[0].price_sun} SUN")
except Exception as e:
    print(f"Connection error: {e}")

El punto final prices.list() es público, por lo que incluso una clave API de marcador de posición funcionará para probar conectividad.

Recursos

Pruébalo Ahora con IA

Añade MERX a Claude Desktop o a cualquier cliente compatible con MCP -- sin instalación, sin necesidad de clave API para herramientas de solo lectura:

{
  "mcpServers": {
    "merx": {
      "url": "https://merx.exchange/mcp/sse"
    }
  }
}

Pregunta a tu agente IA: "¿Cuál es la energía TRON más barata en este momento?" y obtén precios en vivo de todos los proveedores conectados.

Documentación completa de MCP: merx.exchange/docs/tools/mcp-server


All Articles