[object Object]

← back to Bertha

chore(alshi-dash): update 1 file (.js) [+249/-6]

d107bf66e1da980624ba64f6c22dc22f60cc4c76 · 2026-02-27 15:04:00 +0000 · DW Commit Agent

Files touched

Diff

commit d107bf66e1da980624ba64f6c22dc22f60cc4c76
Author: DW Commit Agent <commit-agent@dw-agents.com>
Date:   Fri Feb 27 15:04:00 2026 +0000

    chore(alshi-dash): update 1 file (.js) [+249/-6]
---
 kalshi-dash/server.js | 255 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 249 insertions(+), 6 deletions(-)

diff --git a/kalshi-dash/server.js b/kalshi-dash/server.js
index b11b5fe..953d056 100644
--- a/kalshi-dash/server.js
+++ b/kalshi-dash/server.js
@@ -1603,6 +1603,61 @@ const routes = {
     }
   },
 
+  // ── Live Markets (all active with snapshots) ──
+  'GET /api/markets': async (req, res) => {
+    try {
+      const { rows } = await kenQ(`
+        SELECT DISTINCT ON (ticker) ticker, event_ticker, title, subtitle, category,
+          yes_bid, yes_ask, no_bid, no_ask, last_price, volume, volume_24h, open_interest, spread,
+          captured_at
+        FROM ken_market_snapshots
+        WHERE captured_at > NOW() - INTERVAL '1 hour'
+        ORDER BY ticker, captured_at DESC
+      `);
+      json(res, { markets: rows, count: rows.length, cached: cachedActiveMarkets.length });
+    } catch (err) {
+      json(res, { error: err.message }, 500);
+    }
+  },
+
+  // ── Energy/LNG Arbitrage Opportunities ──
+  'GET /api/energy-arb': async (req, res) => {
+    try {
+      const arbs = await energyArbitrageScan(cachedActiveMarkets);
+      // Also get recent energy-related signals
+      const { rows: energySignals } = await kenQ(`
+        SELECT ticker, title, signal_type, side, confidence, entry_price, edge_bps, reasoning, sources, created_at
+        FROM ken_predictions
+        WHERE signal_type IN ('energy_arb', 'polymarket_arb')
+           OR reasoning ILIKE '%energy%' OR reasoning ILIKE '%oil%' OR reasoning ILIKE '%gas%' OR reasoning ILIKE '%lng%'
+        ORDER BY created_at DESC LIMIT 50
+      `);
+      json(res, {
+        arbitrage_opportunities: arbs,
+        energy_signals: energySignals,
+        scan_time: new Date().toISOString(),
+        markets_scanned: cachedActiveMarkets.length,
+      });
+    } catch (err) {
+      json(res, { error: err.message }, 500);
+    }
+  },
+
+  // ── All Active Signals (for trading) ──
+  'GET /api/signals': async (req, res) => {
+    try {
+      const { rows } = await kenQ(`
+        SELECT market_id as ticker, direction, expected_edge, confidence, reasoning, sources, risk_approved, created_at
+        FROM ken_strategy_signals
+        WHERE created_at > NOW() - INTERVAL '2 hours'
+        ORDER BY created_at DESC LIMIT 200
+      `);
+      json(res, { signals: rows, count: rows.length });
+    } catch (err) {
+      json(res, { error: err.message }, 500);
+    }
+  },
+
   // ════════════════════════════════════════
   // MEMORY / SYSTEM STATUS
   // ════════════════════════════════════════
@@ -4250,6 +4305,11 @@ const server = http.createServer(async (req, res) => {
     '/api/portfauxlio',
     '/api/portfauxlio/charts',
     '/api/portfauxlio/swot',
+    '/api/markets',
+    '/api/signals',
+    '/api/energy-arb',
+    '/api/predictions',
+    '/api/portfolios',
   ];
   const isPublic = publicPaths.includes(urlPath);
 
@@ -6466,6 +6526,144 @@ async function polymarketCrossRef(kalshiMarkets) {
   }
 }
 
+// ══════════════════════════════════════════════
+// LNG / ENERGY CROSS-MARKET ARBITRAGE
+// Compares energy-related predictions across Kalshi, Polymarket, and Manifold
+// ══════════════════════════════════════════════
+const ENERGY_SEARCH_TERMS = [
+  'oil price', 'crude oil', 'natural gas', 'LNG', 'OPEC',
+  'energy price', 'gas price', 'heating oil', 'petroleum',
+  'electricity price', 'EIA', 'oil production', 'pipeline',
+  'renewable energy', 'solar', 'wind energy', 'nuclear',
+  'carbon price', 'emissions', 'electric vehicle', 'EV',
+  'barrel', 'WTI', 'Brent', 'refinery',
+];
+
+async function energyArbitrageScan(kalshiMarkets) {
+  const arbs = [];
+  try {
+    // 1. Get energy markets from Manifold
+    const manifoldEnergy = await scrapeManifold(ENERGY_SEARCH_TERMS.slice(0, 8));
+    console.log(`[Ken/Energy] Manifold: ${manifoldEnergy.length} energy-related markets`);
+
+    // 2. Get energy events from Polymarket
+    let polyEnergy = [];
+    try {
+      const polyAll = await scrapePolymarketEvents(200);
+      polyEnergy = polyAll.filter(p => {
+        const t = (p.event_title + ' ' + p.question).toLowerCase();
+        return /oil|gas|lng|opec|energy|crude|barrel|petroleum|electric|solar|wind|nuclear|carbon|emission|ev market|renewable/i.test(t);
+      });
+      console.log(`[Ken/Energy] Polymarket: ${polyEnergy.length} energy-related markets`);
+    } catch {}
+
+    // 3. Filter Kalshi markets for energy/commodities
+    const kalshiEnergy = kalshiMarkets.filter(m => {
+      const t = ((m.title || '') + ' ' + (m.subtitle || '') + ' ' + (m.category || '')).toLowerCase();
+      return /oil|gas|lng|opec|energy|crude|barrel|petroleum|electric|solar|wind|nuclear|carbon|emission|ev |renewable|consumption|climate/i.test(t);
+    });
+    console.log(`[Ken/Energy] Kalshi: ${kalshiEnergy.length} energy-related markets`);
+
+    // 4. Cross-reference Manifold vs Kalshi
+    for (const mf of manifoldEnergy) {
+      for (const km of kalshiEnergy) {
+        const score = fuzzyMatchScore(mf.title, (km.title + ' ' + (km.subtitle || '')).toLowerCase());
+        if (score >= 0.25) {
+          const priceDiff = mf.probability - km.yes_ask;
+          if (Math.abs(priceDiff) >= 3) {
+            arbs.push({
+              type: 'manifold_kalshi',
+              kalshi_ticker: km.ticker,
+              event_ticker: km.event_ticker,
+              kalshi_title: km.title,
+              kalshi_price: km.yes_ask,
+              other_title: mf.title,
+              other_price: mf.probability,
+              other_source: 'Manifold',
+              other_url: mf.url,
+              other_volume: mf.volume,
+              price_diff: priceDiff,
+              match_score: Math.round(score * 100),
+              category: 'energy',
+            });
+          }
+        }
+      }
+    }
+
+    // 5. Cross-reference Polymarket vs Kalshi (energy-specific)
+    for (const pm of polyEnergy) {
+      for (const km of kalshiEnergy) {
+        const score = fuzzyMatchScore(pm.event_title + ' ' + pm.question, (km.title + ' ' + (km.subtitle || '')).toLowerCase());
+        if (score >= 0.25) {
+          const priceDiff = pm.price - km.yes_ask;
+          if (Math.abs(priceDiff) >= 3) {
+            arbs.push({
+              type: 'poly_kalshi_energy',
+              kalshi_ticker: km.ticker,
+              event_ticker: km.event_ticker,
+              kalshi_title: km.title,
+              kalshi_price: km.yes_ask,
+              other_title: pm.question,
+              other_price: pm.price,
+              other_source: 'Polymarket',
+              other_url: `https://polymarket.com/event/${pm.slug}`,
+              other_volume: pm.volume,
+              price_diff: priceDiff,
+              match_score: Math.round(score * 100),
+              category: 'energy',
+            });
+          }
+        }
+      }
+    }
+
+    // 6. Cross-reference Manifold vs Polymarket (energy only, no Kalshi needed)
+    for (const mf of manifoldEnergy) {
+      for (const pm of polyEnergy) {
+        const score = fuzzyMatchScore(mf.title, pm.event_title + ' ' + pm.question);
+        if (score >= 0.3) {
+          const priceDiff = mf.probability - pm.price;
+          if (Math.abs(priceDiff) >= 5) {
+            arbs.push({
+              type: 'manifold_poly_energy',
+              kalshi_ticker: null,
+              event_ticker: null,
+              kalshi_title: `${mf.title} (cross-platform)`,
+              kalshi_price: mf.probability,
+              other_title: pm.question,
+              other_price: pm.price,
+              other_source: 'Manifold↔Polymarket',
+              other_url: mf.url,
+              other_volume: Math.max(mf.volume, pm.volume),
+              price_diff: priceDiff,
+              match_score: Math.round(score * 100),
+              category: 'energy',
+            });
+          }
+        }
+      }
+    }
+
+    // Dedupe by best price diff per Kalshi ticker
+    const seen = new Map();
+    for (const arb of arbs) {
+      const key = arb.kalshi_ticker || arb.other_title;
+      if (!seen.has(key) || Math.abs(arb.price_diff) > Math.abs(seen.get(key).price_diff)) {
+        seen.set(key, arb);
+      }
+    }
+    const result = [...seen.values()].sort((a, b) => Math.abs(b.price_diff) - Math.abs(a.price_diff));
+    if (result.length > 0) {
+      console.log(`[Ken/Energy] Found ${result.length} energy arbitrage opportunities (top: ${result[0].kalshi_title} ${result[0].price_diff > 0 ? '+' : ''}${result[0].price_diff}¢)`);
+    }
+    return result;
+  } catch (e) {
+    console.log(`[Ken/Energy] Arbitrage scan error: ${e.message}`);
+    return [];
+  }
+}
+
 // ══════════════════════════════════════════════
 // AUTO-TRADER ($20 budget, risk-managed)
 // ══════════════════════════════════════════════
@@ -6822,7 +7020,7 @@ async function autonomousScan() {
 
     // Sort by 24h volume to find what's hot
     const activeMarkets = allMarkets
-      .filter(m => m.volume_24h > 50 && m.yes_bid > 0 && m.yes_ask > 0 && m.yes_ask < 100)
+      .filter(m => m.yes_bid > 0 && m.yes_ask > 0 && m.yes_ask < 100 && m.last_price > 0)
       .sort((a, b) => b.volume_24h - a.volume_24h);
 
     // Cache for 5-minute signal pipeline
@@ -6880,7 +7078,7 @@ async function autonomousScan() {
       }
     } catch (e) { console.log('[Ken] Price movement alert error:', e.message); }
 
-    for (const m of activeMarkets.slice(0, 75)) {
+    for (const m of activeMarkets.slice(0, 200)) {
       const spread = m.yes_ask - m.yes_bid;
       const noPrice = 100 - m.yes_ask;
       const momentum = momentumMap[m.ticker] || { delta_2h: 0, delta_6h: 0 };
@@ -7003,8 +7201,8 @@ async function autonomousScan() {
 
     logMemory('scan-after-signals');
 
-    // 5. Store market snapshots in DB (batch of 50 to limit memory)
-    for (const m of activeMarkets.slice(0, 50)) {
+    // 5. Store market snapshots in DB (batch of 150 to capture more markets)
+    for (const m of activeMarkets.slice(0, 150)) {
       try {
         await kenQ(`
           INSERT INTO ken_market_snapshots (ticker, event_ticker, title, subtitle, category, yes_bid, yes_ask, no_bid, no_ask, last_price, volume, volume_24h, open_interest, spread)
@@ -7053,6 +7251,14 @@ async function autonomousScan() {
       console.log('[Ken] Polymarket cross-ref failed:', e.message);
     }
 
+    // 7b. Energy/LNG cross-market arbitrage scan
+    let energyArbs = [];
+    try {
+      energyArbs = await energyArbitrageScan(activeMarkets);
+    } catch (e) {
+      console.log('[Ken] Energy arb scan failed:', e.message);
+    }
+
     // 8. Combine ALL signals with confidence scoring
     const buySignals = predictions.filter(p => p.edge_bps > 200);
     const shortSignals = predictions.filter(p => p.edge_bps < -200);
@@ -7120,6 +7326,21 @@ async function autonomousScan() {
         sources: ['polymarket_crossref'],
       });
     }
+    // Energy/LNG cross-market arb signals
+    for (const arb of energyArbs) {
+      if (arb.kalshi_ticker) {
+        allSignals.push({
+          market: arb.kalshi_title, ticker: arb.kalshi_ticker, event_ticker: arb.event_ticker,
+          side: arb.price_diff > 0 ? 'YES' : 'NO',
+          confidence: Math.min(92, 60 + Math.abs(arb.price_diff) * 2),
+          entry_price: arb.kalshi_price,
+          edge_bps: arb.price_diff * 100,
+          signal_type: 'energy_arb',
+          reason: `ENERGY ARB: Kalshi ${arb.kalshi_price}¢ vs ${arb.other_source} ${arb.other_price}¢ (${arb.price_diff > 0 ? '+' : ''}${arb.price_diff}¢ gap) — BUY ${arb.price_diff > 0 ? 'Kalshi' : arb.other_source}, SELL ${arb.price_diff > 0 ? arb.other_source : 'Kalshi'}`,
+          sources: ['energy_crossmarket_arb', arb.other_source.toLowerCase()],
+        });
+      }
+    }
 
     // 10. Store predictions in DB
     for (const sig of allSignals) {
@@ -7142,6 +7363,28 @@ async function autonomousScan() {
       console.log(`[Ken] Auto-trader error: ${e.message}`);
     }
 
+    // 10c. PORTFOLIO TRADER: Feed scan signals into paper portfolios
+    let portfolioTradesPlaced = 0;
+    for (const sig of allSignals.filter(s => s.confidence >= 40 && s.ticker)) {
+      try {
+        await autoPortfolioTrade({
+          marketId: sig.ticker,
+          market_title: sig.market || sig.ticker,
+          direction: sig.side === 'YES' ? 'BUY_YES' : 'BUY_NO',
+          expectedEdge: (sig.edge_bps || 0) / 10000,
+          confidence: (sig.confidence || 50) / 100,
+          reasoning: sig.reason || sig.reasoning || sig.signal_type,
+          signal_type: sig.signal_type,
+          aiEnhanced: (sig.sources || []).includes('gemini'),
+          mcSimulation: { consistent: true, stdDev: 0.05 },
+          num_articles: (sig.sources || []).length,
+          agreement: (sig.confidence || 50) / 100,
+        });
+        portfolioTradesPlaced++;
+      } catch {}
+    }
+    if (portfolioTradesPlaced > 0) console.log(`[Ken] Portfolio trades: ${portfolioTradesPlaced} signals fed to paper portfolios`);
+
     const scanDuration = Date.now() - scanStart;
 
     // 11. Log performance
@@ -7750,12 +7993,12 @@ async function autoPortfolioTrade(signal) {
 
       if (!strategy(sigData)) continue;
 
-      // Skip dupes: don't trade same market twice in same portfolio today
+      // Skip dupes: max 3 trades per market per portfolio per day
       const { rows: dupes } = await kenQ(`
         SELECT id FROM ken_portfolio_trades
         WHERE portfolio_id = $1 AND market_id = $2 AND created_at::date = CURRENT_DATE
       `, [pf.id, signal.marketId || signal.market_id]);
-      if (dupes.length > 0) continue;
+      if (dupes.length >= 3) continue;
 
       // Per-strategy position sizing
       const absEdge = Math.abs(parseFloat(sigData.edge));

← 8e9ef18 chore(alshi-dash): update 1 file (.js) [+16/-13]  ·  back to Bertha  ·  chore(alshi-dash): update 1 file (.js) [+75/-39] fa964a3 →