DEV Community

Cover image for How I Built a Multi-Chain DEX Trading Bot with Hermes Agent as My Trading Partner
semi
semi

Posted on

How I Built a Multi-Chain DEX Trading Bot with Hermes Agent as My Trading Partner

Hermes Agent Challenge Submission: Build With Hermes Agent

This is a submission for the Hermes Agent Challenge: Build With Hermes Agent

What I Built

IYOP Trading Bot is an AI-powered multi-chain DEX trading bot that executes trades across Solana (Jupiter), Ethereum (Uniswap V3), and BSC (PancakeSwap V2). It uses MiMo v2 for market analysis, generates signals from multiple technical indicators, and manages risk with position sizing and drawdown protection.

The problem I was solving: decentralized trading is fragmented. Each chain has its own DEX, its own quirks, its own liquidity pools. Monitoring them manually is impossible. Executing trades across chains requires different SDKs, different gas management, different wallet handling.

I needed a unified layer that could:

  • Scan multiple DEXs simultaneously
  • Generate signals from technical indicators and AI analysis
  • Execute trades with proper risk management
  • Track positions across chains
  • Handle stop-loss and take-profit automatically

And I needed Hermes Agent to make the development process fast enough to actually ship.

Demo

Live Demo: iyop-trading-terminal.vercel.app

Source: github.com/iyop666/iyop-trading-bot

The bot runs as a FastAPI backend with a web dashboard. Key features:

  • Multi-chain DEX scanning (Jupiter, Uniswap V3, PancakeSwap)
  • AI-powered market analysis via IYOP v2 API
  • Signal generation with confidence scoring
  • Risk management with position sizing and drawdown limits
  • Trade execution with stop-loss/take-profit
  • Real-time WebSocket price feeds

Code

Here are the core modules that Hermes Agent helped build:

AI Market Analyzer

The analyzer connects to IYOP v2 for fast market analysis, signal generation, and sentiment analysis:

import httpx
from dataclasses import dataclass
from enum import Enum

MIMO_API_URL = "http://127.0.0.1:19911/v1/chat/completions"
MIMO_MODEL = "gitlawb/mimo-v2-flash"

class Signal(Enum):
    BUY = "buy"
    SELL = "sell"
    HOLD = "hold"

@dataclass
class MarketAnalysis:
    signal: Signal
    confidence: float
    reasoning: str
    risk_level: str

async def analyze_market(pair: str, timeframe: str) -> MarketAnalysis:
    """Send market data to MiMo v2 for analysis."""
    async with httpx.AsyncClient() as client:
        response = await client.post(
            MIMO_API_URL,
            json={
                "model": MIMO_MODEL,
                "messages": [
                    {"role": "system", "content": "Analyze this trading pair..."},
                    {"role": "user", "content": f"Pair: {pair}, Timeframe: {timeframe}"}
                ],
                "temperature": 0.1
            },
            timeout=30
        )
        result = response.json()
        return parse_analysis(result)
Enter fullscreen mode Exit fullscreen mode

Signal Generation

Combines multiple technical indicators with weighted scoring:

from dataclasses import dataclass
from enum import Enum

class SignalType(Enum):
    STRONG_BUY = "strong_buy"
    BUY = "buy"
    WEAK_BUY = "weak_buy"
    HOLD = "hold"
    WEAK_SELL = "weak_sell"
    SELL = "sell"
    STRONG_SELL = "strong_sell"

@dataclass
class TradeSignal:
    signal_type: SignalType
    confidence: float  # 0.0 to 1.0
    pair: str
    price: float
    indicators: dict
    reasoning: str

def generate_signal(indicators: dict, ai_analysis: dict) -> TradeSignal:
    """Combine technical indicators with AI analysis."""
    weights = {
        "rsi": 0.25,
        "macd": 0.20,
        "bollinger": 0.15,
        "volume": 0.15,
        "ai_analysis": 0.25
    }

    score = 0.0
    for indicator, weight in weights.items():
        score += normalize(indicators.get(indicator, 0)) * weight

    signal_type = classify_signal(score)
    return TradeSignal(
        signal_type=signal_type,
        confidence=abs(score),
        pair=indicators["pair"],
        price=indicators["price"],
        indicators=indicators,
        reasoning=build_reasoning(indicators, ai_analysis)
    )
Enter fullscreen mode Exit fullscreen mode

Multi-Chain DEX Integration

Unified interface for Jupiter, Uniswap V3, and PancakeSwap:

from enum import Enum
import httpx

class DEX(Enum):
    JUPITER = "jupiter"      # Solana
    UNISWAP = "uniswap"      # Ethereum
    PANCAKESWAP = "pancakeswap"  # BSC

class Chain(Enum):
    SOLANA = "solana"
    ETHEREUM = "ethereum"
    BSC = "bsc"

@dataclass
class SwapResult:
    success: bool
    tx_hash: str
    input_amount: float
    output_amount: float
    chain: Chain
    dex: DEX
    gas_used: float

async def execute_swap(
    dex: DEX,
    chain: Chain,
    token_in: str,
    token_out: str,
    amount: float,
    slippage: float = 0.5
) -> SwapResult:
    """Execute a swap on the specified DEX."""
    if dex == DEX.JUPITER:
        return await jupiter_swap(token_in, token_out, amount, slippage)
    elif dex == DEX.UNISWAP:
        return await uniswap_swap(token_in, token_out, amount, slippage)
    elif dex == DEX.PANCAKESWAP:
        return await pancakeswap_swap(token_in, token_out, amount, slippage)
Enter fullscreen mode Exit fullscreen mode

Risk Management

Position sizing and drawdown protection:

@dataclass
class RiskManager:
    max_position_pct: float = 0.1  # 10% max per position
    max_daily_loss: float = 0.05   # 5% max daily loss
    max_drawdown: float = 0.15     # 15% max drawdown
    current_drawdown: float = 0.0
    daily_pnl: float = 0.0

    def can_open_position(self, portfolio_value: float, position_size: float) -> bool:
        """Check if a new position is within risk limits."""
        # Check position size limit
        if position_size / portfolio_value > self.max_position_pct:
            return False

        # Check daily loss limit
        if self.daily_pnl < -(portfolio_value * self.max_daily_loss):
            return False

        # Check drawdown limit
        if self.current_drawdown > self.max_drawdown:
            return False

        return True

    def calculate_position_size(
        self,
        portfolio_value: float,
        entry_price: float,
        stop_loss: float
    ) -> float:
        """Calculate optimal position size based on risk parameters."""
        risk_per_trade = portfolio_value * self.max_position_pct
        price_risk = abs(entry_price - stop_loss) / entry_price
        return risk_per_trade / price_risk if price_risk > 0 else 0
Enter fullscreen mode Exit fullscreen mode

Trade Execution Engine

Handles order lifecycle with stop-loss and take-profit:

class Side(str, Enum):
    BUY = "buy"
    SELL = "sell"

class OrderStatus(str, Enum):
    PENDING = "pending"
    FILLED = "filled"
    CANCELLED = "cancelled"
    SL_HIT = "stop_loss_hit"
    TP_HIT = "take_profit_hit"

@dataclass
class Position:
    id: str
    pair: str
    side: Side
    entry_price: float
    current_price: float
    size: float
    stop_loss: float
    take_profit: float
    pnl: float
    chain: Chain
    dex: DEX

class Trader:
    def __init__(self, risk_manager: RiskManager):
        self.positions: dict[str, Position] = {}
        self.risk_manager = risk_manager
        self.trade_history: list[dict] = []

    async def open_position(
        self,
        pair: str,
        side: Side,
        size: float,
        stop_loss: float,
        take_profit: float,
        chain: Chain,
        dex: DEX
    ) -> Position:
        """Open a new position with risk checks."""
        if not self.risk_manager.can_open_position(self.portfolio_value, size):
            raise RiskLimitExceeded("Position exceeds risk limits")

        # Execute the swap
        result = await execute_swap(dex, chain, pair, size)

        position = Position(
            id=str(uuid.uuid4()),
            pair=pair,
            side=side,
            entry_price=result.price,
            current_price=result.price,
            size=size,
            stop_loss=stop_loss,
            take_profit=take_profit,
            pnl=0.0,
            chain=chain,
            dex=dex
        )

        self.positions[position.id] = position
        return position
Enter fullscreen mode Exit fullscreen mode

My Tech Stack

  • Backend: Python, FastAPI, Pydantic
  • AI Engine: MiMo v2 (local API)
  • DEX Integrations: Jupiter (Solana), Uniswap V3 (Ethereum), PancakeSwap V2 (BSC)
  • Data: WebSocket price feeds, httpx for async HTTP
  • Frontend: HTML dashboard
  • AI Assistant: Hermes Agent (for development, debugging, and deployment)

How I Used Hermes Agent

1. Architecture Design

When I started building the trading bot, I described the requirements to Hermes:

"I need a multi-chain DEX trading bot with AI analysis, signal generation, risk management, and trade execution. It should support Jupiter, Uniswap V3, and PancakeSwap."

Hermes designed the module structure:

  • backend/ai/ for AI analysis and signal generation
  • backend/core/ for DEX integration, risk management, and trade execution
  • Separation of concerns between analysis and execution

That architecture saved me hours of refactoring later.

2. DEX Integration Code

Each DEX has different APIs, different transaction formats, different gas estimation. Instead of reading docs for each one, I told Hermes:

"Write async swap functions for Jupiter, Uniswap V3, and PancakeSwap. Use httpx, handle errors, return transaction hashes."

Hermes generated the integration code for all three DEXs. I reviewed the error handling, adjusted the slippage parameters, and tested. What would have taken a full day of reading docs and writing boilerplate took 30 minutes.

3. Risk Management Logic

The risk manager needed to handle position sizing, drawdown limits, and daily loss caps. I described the rules:

"Max 10% per position, max 5% daily loss, max 15% drawdown. Calculate position size based on stop-loss distance."

Hermes implemented the logic with proper edge cases. When I tested it with extreme values (100% loss scenarios), it correctly rejected the trades.

4. Signal Weighting System

Combining multiple indicators into a single signal required a weighting system. I asked Hermes:

"Create a signal generator that weights RSI (25%), MACD (20%), Bollinger (15%), Volume (15%), and AI analysis (25%). Normalize each to -1 to +1 range."

Hermes built the weighted scoring system with configurable weights. I later adjusted the AI analysis weight to 30% after testing showed it was the most predictive.

5. Testing and Debugging

When the bot was not executing trades correctly, I asked Hermes to trace the issue:

"Trades are not executing. Check the swap function, the risk manager, and the trade executor. Find the bottleneck."

Hermes read through the code, found that the risk manager was rejecting trades because the portfolio value was not being updated after each trade. One line fix, but finding it manually would have taken an hour of debugging.

Why Hermes Agent Was Essential

Building a trading bot requires:

  • Fast iteration (markets move, code needs to keep up)
  • Cross-module awareness (risk manager affects trader, trader affects portfolio)
  • Async programming patterns (all DEX calls are async)
  • Error handling edge cases (network failures, insufficient gas, slippage)

Hermes handled all of these because it maintained context across the entire codebase. When I changed the risk manager, it automatically suggested updates to the trader. When I added a new DEX, it updated the signal generator to include the new chain.

That cross-module awareness is what made the difference between a prototype and a working bot.


Project Stats:

  • ~2,000 lines of Python
  • 3 DEX integrations (Jupiter, Uniswap V3, PancakeSwap)
  • 5 trading strategies (momentum, mean reversion, AI-driven, scalping, hybrid)
  • Risk management with 3 safety layers
  • Real-time WebSocket price feeds
  • AI-powered market analysis via MiMo v2

Links:

Top comments (0)