AgentSkillsCN

trading-edge

用于天气预测市场边缘计算逻辑与交易策略的技能。优化分析与评分算法。

SKILL.md
--- frontmatter
name: trading-edge
description: Skill pour la logique de calcul d'edge et la stratégie de trading sur les marchés de prédiction météo. Optimisation des algorithmes d'analyse et de scoring.

Trading Edge - Stratégie & Algorithmes

Maximiser l'avantage statistique sur les marchés météo.

Objectif

Ce skill documente la logique métier du calcul d'edge, les formules de confiance, et les stratégies de trading optimales pour les marchés de prédiction météo Polymarket.


Fondamentaux du Trading Météo

Le Principe de l'Edge

code
EDGE = Probabilité Réelle - Prix du Marché

Exemple :

  • Open-Meteo prédit 35.2°F avec 89% de confiance
  • Le bracket "34-35°F" est pricé à 41¢ (= 41% de probabilité implicite)
  • Edge = 89% - 41% = +48%

Un edge positif signifie que le marché sous-évalue cette issue.

Pourquoi l'Edge Existe

  1. Asymétrie d'information : Les traders retail n'ont pas accès aux meilleurs modèles météo
  2. Biais psychologiques : Préférence pour les outcomes "round numbers"
  3. Liquidité limitée : Peu d'arbitrageurs sur ce marché niche
  4. Timing : Les modèles météo se mettent à jour plus vite que le marché

Calcul de la Confiance Météo

Confiance par Horizon Temporel

La précision météo décroît avec le temps. Voici les coefficients empiriques :

javascript
const BASE_CONFIDENCE = {
  0: 0.95,  // Aujourd'hui (J+0)
  1: 0.90,  // Demain (J+1)
  2: 0.82,  // J+2
  3: 0.74,  // J+3
  4: 0.65,  // J+4
  5: 0.55,  // J+5
  6: 0.45,  // J+6
  7: 0.35,  // J+7+
};

Confiance Multi-Modèle (Recommandé)

Quand plusieurs modèles météo sont disponibles, utiliser leurs statistiques :

javascript
function calculateMultiModelConfidence(models, bracket, daysAhead) {
  // 1. Calculer la distribution des prévisions
  const predictions = models.map(m => m.tempMax);
  const mean = average(predictions);
  const median = percentile(predictions, 50);
  const stdDev = standardDeviation(predictions);
  
  // 2. Calculer la probabilité que la température tombe dans le bracket
  // Utiliser la CDF normale
  const zMin = (bracket.min - mean) / Math.max(stdDev, 0.5);
  const zMax = (bracket.max - mean) / Math.max(stdDev, 0.5);
  const probability = normalCDF(zMax) - normalCDF(zMin);
  
  // 3. Ajuster par l'accord entre modèles
  const cv = stdDev / Math.abs(mean);  // Coefficient de variation
  const agreement = Math.max(0, Math.min(1, 1 - cv));
  
  // 4. Facteur d'accord (0.7 à 1.0)
  const agreementFactor = 0.7 + (agreement * 0.3);
  
  // 5. Pénalité temporelle
  const daysPenalty = Math.max(0, (daysAhead - 1) * 0.015);
  
  // 6. Confiance finale
  let confidence = probability * agreementFactor * (1 - daysPenalty);
  
  // 7. Plafond basé sur le nombre de modèles
  const modelCap = Math.min(1, 0.6 + (models.length * 0.05));
  
  return Math.min(confidence, modelCap);
}

Pénalité de Bord de Bracket

Si la température prédite est proche du bord d'un bracket, réduire la confiance :

javascript
function applyEdgePenalty(confidence, predictedTemp, bracket) {
  const bracketWidth = bracket.max - bracket.min;
  const center = (bracket.max + bracket.min) / 2;
  
  // Distance relative au centre (0 = centre, 1 = bord)
  const distanceFromCenter = Math.abs(predictedTemp - center) / (bracketWidth / 2);
  
  // Pénalité max 15% si exactement au bord
  const penalty = Math.min(distanceFromCenter * 0.15, 0.15);
  
  return confidence * (1 - penalty);
}

Seuils de Signal

Configuration Recommandée

javascript
const SIGNAL_THRESHOLDS = {
  STRONG_BUY: 0.20,  // Edge > 20%
  BUY: 0.10,         // Edge > 10%
  WATCH: 0.05,       // Edge > 5%
  PASS: 0            // Edge < 5%
};

const SIGNALS = {
  STRONG_BUY: {
    label: "ACHAT FORT",
    emoji: "🔥",
    action: "Entrée agressive",
    sizing: "2-3% du bankroll"
  },
  BUY: {
    label: "ACHAT",
    emoji: "✅",
    action: "Entrée standard",
    sizing: "1-2% du bankroll"
  },
  WATCH: {
    label: "SURVEILLER",
    emoji: "⚠️",
    action: "Attendre confirmation",
    sizing: "Pas d'action immédiate"
  },
  PASS: {
    label: "PASSER",
    emoji: "➖",
    action: "Pas d'edge",
    sizing: "N/A"
  }
};

Signaux Additionnels

javascript
const ADVANCED_SIGNALS = {
  VALUE_BET: {
    condition: (edge, agreement) => edge > 0.25 && agreement > 0.9,
    label: "VALUE BET",
    description: "Tous les modèles d'accord, marché en désaccord majeur"
  },
  FADING: {
    condition: (edge, prevEdge) => edge < prevEdge - 0.05,
    label: "EDGE EN BAISSE",
    description: "Le marché corrige vers les prévisions"
  },
  CONTRARIAN: {
    condition: (edge, volumeTrend) => edge > 0.15 && volumeTrend < 0,
    label: "CONTRARIAN",
    description: "Edge élevé malgré volume en baisse"
  }
};

Stratégie de Sizing (Kelly Criterion)

Kelly Fraction

javascript
function kellyFraction(edge, marketPrice) {
  // f* = (p*b - q) / b
  // où p = notre probabilité, b = odds, q = 1-p
  
  const impliedOdds = (1 / marketPrice) - 1;  // Si price=0.4, odds=1.5
  const ourProbability = marketPrice + edge;   // Notre estimation
  const lossProbability = 1 - ourProbability;
  
  const kelly = (ourProbability * impliedOdds - lossProbability) / impliedOdds;
  
  // Ne jamais dépasser 25% Kelly (prudence)
  return Math.max(0, Math.min(kelly * 0.25, 0.10));
}

Position Sizing Recommandé

SignalKellySizing MaxNote
STRONG BUYFull (25%)3% bankrollVolume > $5k
BUYHalf (12.5%)2% bankrollVolume > $2k
WATCHQuarter (6%)1% bankrollConfirmation requise
PASS00Pas d'action

Parsing des Brackets Polymarket

Patterns Supportés

javascript
function parseBracket(bracket) {
  const b = bracket.trim();
  
  // Pattern 1: "34-35°F" ou "34-35°C"
  const rangeMatch = b.match(/(-?\d+(?:\.\d+)?)\s*[-–]\s*(-?\d+(?:\.\d+)?)/);
  if (rangeMatch) {
    return {
      min: parseFloat(rangeMatch[1]),
      max: parseFloat(rangeMatch[2]),
      type: 'range'
    };
  }
  
  // Pattern 2: "38°F or more" / "38+ F" / "38°F and above"
  const orMoreMatch = b.match(/(-?\d+(?:\.\d+)?)\s*(?:°[FC])?\s*(?:or more|\+|and above|or higher)/i);
  if (orMoreMatch) {
    const base = parseFloat(orMoreMatch[1]);
    return {
      min: base,
      max: base + 15,  // Extension arbitraire
      type: 'open_high'
    };
  }
  
  // Pattern 3: "30°F or less" / "under 30" / "30°F and below"
  const orLessMatch = b.match(/(-?\d+(?:\.\d+)?)\s*(?:°[FC])?\s*(?:or less|or lower|and below|or under)/i);
  if (orLessMatch) {
    const base = parseFloat(orLessMatch[1]);
    return {
      min: base - 15,
      max: base,
      type: 'open_low'
    };
  }
  
  // Pattern 4: "under 30" / "below 30"
  const underMatch = b.match(/(?:under|below)\s*(-?\d+(?:\.\d+)?)/i);
  if (underMatch) {
    const base = parseFloat(underMatch[1]);
    return { min: base - 15, max: base, type: 'open_low' };
  }
  
  // Pattern 5: "over 38" / "above 38"
  const overMatch = b.match(/(?:over|above)\s*(-?\d+(?:\.\d+)?)/i);
  if (overMatch) {
    const base = parseFloat(overMatch[1]);
    return { min: base, max: base + 15, type: 'open_high' };
  }
  
  // Pattern 6: Valeur unique "35°F" (bracket exact)
  const singleMatch = b.match(/^(-?\d+(?:\.\d+)?)\s*°[FC]?$/);
  if (singleMatch) {
    const val = parseFloat(singleMatch[1]);
    return { min: val - 0.5, max: val + 0.5, type: 'exact' };
  }
  
  return null;
}

Correspondance Prévision → Bracket

javascript
function findMatchingBracket(outcomes, predictedTemp) {
  // 1. Chercher correspondance exacte
  for (let i = 0; i < outcomes.length; i++) {
    const bracket = parseBracket(outcomes[i]);
    if (!bracket) continue;
    
    if (predictedTemp >= bracket.min && predictedTemp <= bracket.max) {
      return {
        index: i,
        bracket: outcomes[i],
        ...bracket,
        mid: (bracket.min + bracket.max) / 2,
        matchType: 'exact'
      };
    }
  }
  
  // 2. Chercher le bracket le plus proche (max 3° de tolérance)
  const MAX_TOLERANCE = 3;
  let closest = null;
  let minDistance = Infinity;
  
  for (let i = 0; i < outcomes.length; i++) {
    const bracket = parseBracket(outcomes[i]);
    if (!bracket) continue;
    
    const distance = Math.min(
      Math.abs(predictedTemp - bracket.min),
      Math.abs(predictedTemp - bracket.max)
    );
    
    if (distance < minDistance && distance <= MAX_TOLERANCE) {
      minDistance = distance;
      closest = {
        index: i,
        bracket: outcomes[i],
        ...bracket,
        mid: (bracket.min + bracket.max) / 2,
        matchType: 'closest',
        distance
      };
    }
  }
  
  return closest;
}

Calcul du ROI Potentiel

javascript
function calculatePotentialROI(marketPrice, edge) {
  if (edge <= 0 || marketPrice <= 0) return 0;
  
  // ROI brut = (1 / prix) - 1 si le bet gagne
  const grossROI = (1 / marketPrice) - 1;
  
  // Ajuster pour les frais Polymarket (~2%)
  const POLYMARKET_FEE = 0.02;
  const netROI = grossROI - POLYMARKET_FEE;
  
  return Math.max(0, netROI * 100);  // En pourcentage
}

Gestion des Risques

Règles de Position

javascript
const RISK_RULES = {
  // Max par position
  maxPositionSize: 0.03,  // 3% du bankroll
  
  // Max exposition totale
  maxTotalExposure: 0.15,  // 15% du bankroll
  
  // Corrélation
  maxCorrelatedPositions: 3,  // Max 3 positions même ville
  
  // Stop-loss implicite
  // (sur Polymarket, pas de stop-loss, mais on peut vendre)
  exitOnEdgeLoss: -0.05,  // Sortir si edge passe négatif > 5%
  
  // Volume minimum
  minVolume: 1000,  // Ignorer marchés < $1000
  
  // Horizon maximum
  maxDaysAhead: 7,  // Pas de trade > 7 jours
};

Checklist Avant Trade

javascript
const PRE_TRADE_CHECKLIST = [
  (market) => market.volume >= RISK_RULES.minVolume,
  (market) => market.daysAhead <= RISK_RULES.maxDaysAhead,
  (market) => market.analysis.edge >= SIGNAL_THRESHOLDS.BUY,
  (market) => market.analysis.modelAgreement >= 0.6,
  (market) => !market.closed,
  (market) => market.outcomes.length >= 3,  // Assez de brackets
];

function validateTrade(market) {
  return PRE_TRADE_CHECKLIST.every(check => check(market));
}

Métriques de Performance

KPIs à Tracker

javascript
const PERFORMANCE_METRICS = {
  // Win rate
  winRate: (wins, total) => wins / total,
  
  // Expected Value
  expectedValue: (edge, avgROI) => edge * avgROI,
  
  // Sharpe Ratio (adapté)
  sharpe: (avgReturn, stdReturn) => avgReturn / stdReturn,
  
  // Max Drawdown
  maxDrawdown: (peakValue, troughValue) => (peakValue - troughValue) / peakValue,
  
  // Profit Factor
  profitFactor: (grossProfit, grossLoss) => grossProfit / Math.abs(grossLoss),
  
  // Brier Score (précision des probabilités)
  brierScore: (predictions, outcomes) => {
    return predictions.reduce((sum, p, i) => {
      const outcome = outcomes[i] ? 1 : 0;
      return sum + Math.pow(p - outcome, 2);
    }, 0) / predictions.length;
  }
};

Commandes

  • /trading-edge:analyze [market_id] — Analyse détaillée d'un marché
  • /trading-edge:portfolio — État du portefeuille simulé
  • /trading-edge:backtest [days] — Backtester la stratégie
  • /trading-edge:optimize — Suggérer des améliorations d'algo