[object Object]

← back to Bertha

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

fa964a3a1d2f65337be56a658bd306b0896c9751 · 2026-02-27 15:34:00 +0000 · DW Commit Agent

Files touched

Diff

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

    chore(alshi-dash): update 1 file (.js) [+75/-39]
---
 kalshi-dash/server.js | 114 +++++++++++++++++++++++++++++++++-----------------
 1 file changed, 75 insertions(+), 39 deletions(-)

diff --git a/kalshi-dash/server.js b/kalshi-dash/server.js
index 953d056..6cb3c67 100644
--- a/kalshi-dash/server.js
+++ b/kalshi-dash/server.js
@@ -1416,37 +1416,50 @@ const routes = {
         json(res, { success: true, order: paperOrder, message: 'Paper order filled' });
 
       } else if (body.action === 'get_positions') {
-        // Aggregate paper trades into positions
-        const positionMap = {};
-        for (const trade of paperTrades) {
-          const key = `${trade.ticker}_${trade.side}`;
-          if (!positionMap[key]) {
-            positionMap[key] = {
-              ticker: trade.ticker,
-              side: trade.side,
-              total_contracts: 0,
-              avg_price: 0,
-              total_cost: 0,
-              trades: 0,
-            };
-          }
-          const pos = positionMap[key];
-          if (trade.action === 'buy') {
-            pos.total_cost += trade.price * trade.count;
-            pos.total_contracts += trade.count;
-          } else {
-            pos.total_cost -= trade.price * trade.count;
-            pos.total_contracts -= trade.count;
+        // Aggregate from ken_portfolio_trades (live hyper-trading data)
+        try {
+          const { rows } = await kenQ(`
+            SELECT pt.market_id as ticker, pt.market_title, pt.direction,
+              SUM(pt.contracts) as total_contracts,
+              SUM(pt.cost_cents) as position_cost,
+              ROUND(AVG(pt.entry_price_cents)) as avg_price,
+              COUNT(*) as trades,
+              SUM(CASE WHEN pt.status != 'open' THEN pt.pnl_cents ELSE 0 END) as realized_pnl
+            FROM ken_portfolio_trades pt
+            WHERE pt.created_at > NOW() - INTERVAL '24 hours'
+            GROUP BY pt.market_id, pt.market_title, pt.direction
+            ORDER BY SUM(pt.cost_cents) DESC
+          `);
+          const positions = rows.map(r => ({
+            ticker: r.ticker,
+            market_ticker: r.ticker,
+            title: r.market_title,
+            side: r.direction === 'BUY_YES' ? 'yes' : 'no',
+            market_exposure: r.direction === 'BUY_YES' ? parseInt(r.total_contracts) : -parseInt(r.total_contracts),
+            position: r.direction === 'BUY_YES' ? parseInt(r.total_contracts) : -parseInt(r.total_contracts),
+            position_cost: parseInt(r.position_cost) * 100,
+            avg_price_cents: parseInt(r.avg_price),
+            realized_pnl: parseInt(r.realized_pnl || 0) * 100,
+            fees_paid: 0,
+            trades: parseInt(r.trades),
+          }));
+          json(res, { success: true, positions, is_paper: true });
+        } catch (e) {
+          // Fallback to old paper trades
+          const positionMap = {};
+          for (const trade of paperTrades) {
+            const key = `${trade.ticker}_${trade.side}`;
+            if (!positionMap[key]) positionMap[key] = { ticker: trade.ticker, side: trade.side, total_contracts: 0, avg_price: 0, total_cost: 0, trades: 0 };
+            const pos = positionMap[key];
+            if (trade.action === 'buy') { pos.total_cost += trade.price * trade.count; pos.total_contracts += trade.count; }
+            else { pos.total_cost -= trade.price * trade.count; pos.total_contracts -= trade.count; }
+            pos.trades++;
+            pos.avg_price = pos.total_contracts > 0 ? Math.round(pos.total_cost / pos.total_contracts) : 0;
           }
-          pos.trades++;
-          pos.avg_price = pos.total_contracts > 0
-            ? Math.round(pos.total_cost / pos.total_contracts)
-            : 0;
+          const positions = Object.values(positionMap).filter(p => p.total_contracts !== 0);
+          json(res, { success: true, positions, is_paper: true });
         }
 
-        const positions = Object.values(positionMap).filter(p => p.total_contracts !== 0);
-        json(res, { success: true, positions, is_paper: true });
-
       } else if (body.action === 'get_fills') {
         // Return paper trade history
         const limit = body.limit || 50;
@@ -7993,12 +8006,12 @@ async function autoPortfolioTrade(signal) {
 
       if (!strategy(sigData)) continue;
 
-      // Skip dupes: max 3 trades per market per portfolio per day
+      // Hyper-trade: max 10 OPEN positions per market per portfolio (no daily cap)
       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
+        WHERE portfolio_id = $1 AND market_id = $2 AND status = 'open'
       `, [pf.id, signal.marketId || signal.market_id]);
-      if (dupes.length >= 3) continue;
+      if (dupes.length >= 10) continue;
 
       // Per-strategy position sizing
       const absEdge = Math.abs(parseFloat(sigData.edge));
@@ -8065,15 +8078,17 @@ async function capturePortfolioSnapshots() {
   }
 }
 
-// Resolve portfolio trades — check if markets settled
+// Resolve portfolio trades — HYPER MODE: quick take-profit/stop-loss + aging out
 async function resolvePortfolioTrades() {
   try {
+    // Check ALL open trades (no minimum age for hyper-trading)
     const { rows: openTrades } = await kenQ(`
-      SELECT pt.*, p.name as portfolio_name
+      SELECT pt.*, p.name as portfolio_name, p.strategy as portfolio_strategy
       FROM ken_portfolio_trades pt
       JOIN ken_portfolios p ON p.id = pt.portfolio_id
-      WHERE pt.status = 'open' AND pt.created_at < NOW() - INTERVAL '4 hours'
-      LIMIT 100
+      WHERE pt.status = 'open'
+      ORDER BY pt.created_at ASC
+      LIMIT 500
     `);
 
     for (const trade of openTrades) {
@@ -8085,17 +8100,33 @@ async function resolvePortfolioTrades() {
 
       if (snaps.length === 0) continue;
       const currentPrice = parseInt(snaps[0].last_price);
+      const ageMinutes = (Date.now() - new Date(trade.created_at).getTime()) / 60000;
 
       let pnl = 0;
-      let status = 'expired';
       if (trade.direction === 'BUY_YES') {
         pnl = (currentPrice - trade.entry_price_cents) * trade.contracts;
-        status = pnl > 0 ? 'won' : pnl < 0 ? 'lost' : 'expired';
       } else {
         pnl = (trade.entry_price_cents - currentPrice) * trade.contracts;
+      }
+
+      // Hyper-trade exit rules:
+      // 1. Take profit: +3¢ per contract (minimum 5 min hold)
+      // 2. Stop loss: -5¢ per contract (minimum 5 min hold)
+      // 3. Age out: close after 2 hours regardless
+      let status = null;
+      const profitPerContract = pnl / Math.max(1, trade.contracts);
+
+      if (ageMinutes >= 5 && profitPerContract >= 3) {
+        status = 'won'; // Take profit
+      } else if (ageMinutes >= 5 && profitPerContract <= -5) {
+        status = 'lost'; // Stop loss
+      } else if (ageMinutes >= 120) {
+        // Age out after 2 hours — mark based on current P&L
         status = pnl > 0 ? 'won' : pnl < 0 ? 'lost' : 'expired';
       }
 
+      if (!status) continue; // Hold position
+
       await kenQ(`
         UPDATE ken_portfolio_trades SET status = $1, exit_price_cents = $2, pnl_cents = $3, resolved_at = NOW()
         WHERE id = $4
@@ -8516,12 +8547,17 @@ server.listen(PORT, '0.0.0.0', () => {
     }
   }, SIGNAL_PIPELINE_MS);
 
-  // Resolve old predictions + portfolio trades every hour
+  // Resolve old predictions every hour
   setInterval(() => {
     resolveOldPredictions().catch(e => console.error('[Ken] Resolution error:', e.message));
-    resolvePortfolioTrades().catch(e => console.error('[Ken] Portfolio resolution error:', e.message));
   }, 60 * 60 * 1000);
 
+  // HYPER-TRADE: Resolve portfolio trades every 5 minutes (take-profit/stop-loss)
+  setInterval(() => {
+    resolvePortfolioTrades().catch(e => console.error('[Ken] Portfolio resolution error:', e.message));
+  }, 5 * 60 * 1000);
+  setTimeout(() => resolvePortfolioTrades().catch(() => {}), 30000); // Initial run after startup
+
   // Portfolio NAV snapshots every 30 minutes for charting
   setInterval(() => capturePortfolioSnapshots().catch(e => console.error('[Ken/Charts] Error:', e.message)), 30 * 60 * 1000);
   setTimeout(() => capturePortfolioSnapshots().catch(() => {}), 25000); // Initial after startup

← d107bf6 chore(alshi-dash): update 1 file (.js) [+249/-6]  ·  back to Bertha  ·  chore(alshi-dash,kalshi-dash): update 8 files (.js) [+583/-1 2acd899 →