Passer au contenu principal
Les bots sont des citoyens de premier rang sur counsel. Un bot est un client XRPL ordinaire : il lit les marchés en direct, récupère un Payment de pari non signé, le signe avec sa propre clé, puis le soumet au ledger. counsel ne détient jamais de clé. Chaque pari porte le SourceTag de counsel, de sorte que le volume on-chain d’un bot compte dans l’attribution au classement de la même manière qu’un pari manuel. Comme les marchés counsel sont parimutuels, il n’y a pas de carnet d’ordres : les bots apportent de la valeur en corrigeant les erreurs de prix, en équilibrant les pools ou en arbitrant entre plateformes. Chacune de ces actions est un Payment natif taggé, qui génère du volume on-chain direct.

Ce qu’un bot peut faire

  • Value-betting : estimez la probabilité réelle de chaque issue et misez lorsque l’implied_prob du marché s’écarte de votre modèle d’une marge significative. Cela pousse la cote vers le prix juste, au bénéfice de tous les participants.
  • Équilibrage de pool : les cotes parimutuelles dérivent continuellement jusqu’au bet_cutoff. Un bot peut repérer un pool déséquilibré proche de la clôture et miser sur le côté sous-pondéré, capturant de meilleures cotes tout en corrigeant le déséquilibre.
  • Arbitrage entre plateformes : prenez une position sur counsel et couvrez-la sur le même événement sur une autre plateforme, verrouillant un écart lorsque les probabilités implicites divergent.

Démarrage rapide : installation et configuration

Installez le SDK depuis le repo et configurez l’URL de base :
import { Counsel } from "./counsel";

const counsel = new Counsel({ baseUrl: "https://api.counsel.markets" });
Définissez COUNSEL_URL sur https://api.counsel.markets et référencez-le avec process.env.COUNSEL_URL pour garder l’URL de base hors de votre code source.

Lire les marchés en direct

Récupérez tous les marchés publics et inspectez l’état actuel de leur pool ainsi que les cotes indicatives :
const { markets } = await counsel.markets();
// Each market has: id, question, phase, status, outcomes[]
// Each outcome: index, label, implied_prob, payout_per_unit, pool_xrp
Filtrez sur les marchés qui acceptent actuellement des paris avant toute autre chose :
const open = (markets as any[]).filter(
  (m) => m.status === "open" && m.phase === "open"
);

Récupérer une intention de pari

Avant de vous engager, récupérez une transaction non signée pour le pari précis que vous souhaitez placer. Cela renvoie également l’impact projeté de votre mise sur la cote :
const intent = await counsel.betIntent(marketId, walletAddress, outcomeIndex, amountXrp);
// intent.tx = unsigned XRPL Payment ready to sign
// intent.projected_implied_odds_after = { implied_prob, payout_per_unit }
betIntent correspond à :
GET /api/v1/markets/:id/bet-intent
  ?account=WALLET_ADDRESS
  &outcome=OUTCOME_INDEX
  &amount=AMOUNT_XRP
Le tx renvoyé est un objet XRPL Payment entièrement formé. Il ne reste plus qu’à compléter automatiquement les champs sequence/fee, à signer et à soumettre.

Signer et soumettre

placeBet gère tout le cycle de vie : il appelle betIntent en interne, signe avec la seed fournie via xrpl.js, soumet, puis attend la validation par le ledger. Il renvoie le hash de la transaction confirmée.
const hash = await counsel.placeBet(process.env.BOT_SEED!, marketId, outcomeIndex, amountXrp);
console.log("tx:", hash);
Utilisez uniquement des seeds de testnet. Ne committez jamais de seeds dans le contrôle de version. Chargez BOT_SEED exclusivement depuis des variables d’environnement ou un gestionnaire de secrets.
En interne, placeBet se connecte au nœud WebSocket XRPL spécifié dans CounselOptions.wss (par défaut wss://s.altnet.rippletest.net:51233 pour le testnet), complète automatiquement la transaction, la signe avec Wallet.fromSeed(seed), la soumet via submitAndWait et vérifie tesSUCCESS avant de renvoyer. La connexion WebSocket est fermée après chaque appel.

Exemple complet de value-betting

Le bot suivant récupère tous les marchés ouverts, fait passer chaque issue dans un modèle de probabilité, puis mise lorsqu’il trouve un avantage suffisant. Il vérifie aussi les cotes projetées après la mise avant de s’engager, afin d’éviter de parier alors que la cote a déjà dépassé le prix juste :
import { Counsel } from "./counsel";

const counsel = new Counsel({ baseUrl: process.env.COUNSEL_URL! });
const SEED = process.env.BOT_SEED!; // testnet only

// Your model: the probability you believe each outcome has.
function myModel(question: string): number[] {
  // ...return [pYes, pNo]; here a naive 50/50
  return [0.5, 0.5];
}

const { markets } = await counsel.markets();
for (const m of markets as any[]) {
  if (m.status !== "open" || m.phase !== "open") continue;
  const fair = myModel(m.question);
  for (const o of m.outcomes) {
    // Bet when the market underprices an outcome vs your model, with edge to spare.
    if (fair[o.index] - o.implied_prob > 0.08) {
      const intent = await counsel.betIntent(m.id, walletAddress, o.index, 5);
      // Check the projected odds AFTER your own stake before committing.
      if (intent.projected_implied_odds_after.implied_prob < fair[o.index]) {
        await counsel.placeBet(SEED, m.id, o.index, 5);
      }
    }
  }
}
La vérification interne sur projected_implied_odds_after.implied_prob est importante : dans un petit pool, votre propre mise de 5 XRP peut déplacer la cote suffisamment pour éliminer l’avantage que vous aviez détecté à partir de l’implied_prob périmé figurant dans la liste des marchés.

Anti-slippage

Les cotes parimutuelles ne sont pas fixes : chaque pari modifie le pool, et donc chaque payout ultérieur. Lorsque vous lisez l’implied_prob depuis GET /markets, ce chiffre reflète déjà tous les paris soumis avant votre lecture. Au moment où vous récupérez l’intention de pari, d’autres paris peuvent être arrivés. Utilisez toujours projected_implied_odds_after comme critère de décision, et non les cotes de la liste des marchés :
const intent = await counsel.betIntent(marketId, walletAddress, outcomeIndex, amountXrp);

if (intent.projected_implied_odds_after.implied_prob < myFairProbability) {
  // Market still offers value after your stake is factored in, proceed.
  await counsel.placeBet(SEED, marketId, outcomeIndex, amountXrp);
} else {
  // Your stake alone moves the line past fair value, skip.
}
Pour des mises importantes par rapport à la taille du pool, envisagez de découper un seul pari en plusieurs paris séquentiels plus petits et de revérifier l’intention à chaque fois.

Limites de débit

L’API publique ne nécessite aucune authentification. Aucune limite de débit n’est imposée, mais l’API est une ressource partagée. Interrogez GET /markets à une cadence raisonnable : 5 à 10 secondes convient à la plupart des stratégies. Une interrogation à haute fréquence n’apporte aucun avantage sur les marchés parimutuels : les cotes changent quand les paris arrivent, pas selon un rythme régulier, et les payouts finaux sont déterminés à la clôture, quel que soit le moment où vous avez placé votre pari.

Référence des méthodes du SDK

MéthodeEndpoint
markets()GET /api/v1/markets
market(id)GET /api/v1/markets/:id
betIntent(id, account, outcome, amount)GET /api/v1/markets/:id/bet-intent
positions(address)GET /api/v1/accounts/:address/positions
leaderboard()GET /api/v1/leaderboard