diff --git a/README.md b/README.md index ce9379a..9595338 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,13 @@ three-hour shock window count as one learner event. Each Build 121 cycle also scans the 1,000 most-active Polymarket events for complete negative-risk bundles and logically nested threshold or deadline pairs, plus same-market YES/NO complements whose equal shares have a fixed -$1 redemption value. Gamma's market-specific fee flag replaces the old blanket 0.5-cent fee +$1 redemption value. Polymarket's documented complete-set merge converts equal +YES and NO amounts directly back into collateral, so a verified binary complement +is merged and realized in the same paper cycle instead of waiting for resolution. +Gasless CTF operations include merge transactions; see +[Positions & Tokens](https://docs.polymarket.com/concepts/positions-tokens) and +[Gasless Transactions](https://docs.polymarket.com/trading/gasless). +Gamma's market-specific fee flag replaces the old blanket 0.5-cent fee reserve for markets declared fee-free. The scanner keeps the existing 60 independent-first general structure checks and reserves 20 additional checks for same-market complements. Structures are ranked by locked-capital efficiency, with diff --git a/api/state.js b/api/state.js index 441f296..56bdf1a 100644 --- a/api/state.js +++ b/api/state.js @@ -288,7 +288,8 @@ export function compactSuggestion(s) { adaptive_probation: s.adaptive_probation, probation_exit_hours: s.probation_exit_hours, promoted_for_agents: s.promoted_for_agents, watch_only: s.watch_only, jump_risk: s.jump_risk, requires_live: s.requires_live, - bundle_id: s.bundle_id, bundle_event_id: s.bundle_event_id, bundle_side: s.bundle_side, bundle_logic: s.bundle_logic, bundle_cost_per_unit: s.bundle_cost_per_unit, + bundle_id: s.bundle_id, bundle_event_id: s.bundle_event_id, bundle_side: s.bundle_side, bundle_logic: s.bundle_logic, + bundle_immediate_merge: s.bundle_immediate_merge, bundle_cost_per_unit: s.bundle_cost_per_unit, bundle_payout_per_unit: s.bundle_payout_per_unit, bundle_net_profit_per_unit: s.bundle_net_profit_per_unit, bundle_capital_efficiency: s.bundle_capital_efficiency, bundle_legs: s.bundle_legs, depth_verified: s.depth_verified, fees_verified: s.fees_verified, diff --git a/index.html b/index.html index 9aaccf4..c642bdc 100644 --- a/index.html +++ b/index.html @@ -872,7 +872,7 @@ async function refreshTradeEmailStatus(){ Five core and five aggressive strategies share the same live suggestion pool. */ const AGENTS = [ {id:"value", name:"Value Hunter", emoji:"🎯", color:"#7c8cff", kind:"strategy", - blurb:"Runs the arena's non-directional book: complete negative-risk bundles plus paired resting YES/NO bids in calm, reward-scoring markets. Verified bundles are prioritized by return per day of locked capital and capped by underlying event, so one threshold family cannot crowd out independent opportunities. A paired quote earns nothing until live prices cross each leg; two fills below $1 lock settlement value, while unmatched inventory is unwound after 24 hours.", + blurb:"Runs the arena's non-directional book: mergeable same-market YES/NO complete sets, complete negative-risk bundles, and paired resting bids in calm reward-scoring markets. Equal YES/NO fills below $1 are merged immediately back to collateral, while longer-lived verified bundles are prioritized by return per locked day and capped by underlying event. Unmatched maker inventory is unwound after 24 hours.", rank:(s)=>[...s].sort((a,b)=>{ const bundleOrder=Number(b.quality==="bundle-arb")-Number(a.quality==="bundle-arb"); if(bundleOrder)return bundleOrder; @@ -1291,7 +1291,7 @@ function binaryComplementBundleSuggestions(event,{includeUnprofitable=false}={}) evidence_source_count:0,quality:"bundle-arb",conviction:+clamp(80+Math.max(0,netReturn)*1200,80,96).toFixed(1),volume:toNum(raw.volumeNum||raw.volume), volume_24hr:toNum(raw.volume24hr),liquidity,spread:yesAsk-yesBid,price_change_1h:0,price_change_1d:0,price_change_1w:0,momentum_strength:0, signal_strength:1,signal_confidence:1,signal_type:"bundle-arb",trade_ready:actionable,entry_candidate:actionable,audited_observation_only:!actionable, - adaptive_promotion:false,watch_only:!actionable,jump_risk:false,requires_live:true,opportunity_actionable:actionable, + adaptive_promotion:false,watch_only:!actionable,jump_risk:false,requires_live:true,opportunity_actionable:actionable,bundle_immediate_merge:true, days_to_resolution:daysUntil(raw.endDate||event.endDate),bundle_cost_per_unit:+cost.toFixed(4),bundle_payout_per_unit:1, bundle_net_profit_per_unit:+profit.toFixed(4),bundle_legs:legs,depth_verified:false,fees_verified:false,execution_model:"top-of-book-estimate", drivers:["same-market YES plus NO complement","fixed one-dollar redemption value","live executable asks required","positive margin after exact fees"], @@ -1350,7 +1350,7 @@ function bundleEventKey(item={}){ } function bundleCapitalEfficiency(item={}){ const netReturn=Math.max(0,Number(item.net_edge)||0),days=Number(item.days_to_resolution); - const lockDays=Number.isFinite(days)&&days>0?Math.max(1,days):365; + const lockDays=item.bundle_immediate_merge?1:(Number.isFinite(days)&&days>0?Math.max(1,days):365); return netReturn/lockDays; } function compareBundleOpportunities(a,b){ @@ -2087,7 +2087,8 @@ function compactSuggestionForSync(s){ trade_ready:s.trade_ready,entry_candidate:s.entry_candidate,audited_observation_only:s.audited_observation_only, adaptive_promotion:s.adaptive_promotion,adaptive_probation:s.adaptive_probation, probation_exit_hours:s.probation_exit_hours,promoted_for_agents:s.promoted_for_agents,watch_only:s.watch_only,jump_risk:s.jump_risk, - requires_live:s.requires_live,bundle_id:s.bundle_id,bundle_event_id:s.bundle_event_id,bundle_side:s.bundle_side,bundle_logic:s.bundle_logic,bundle_cost_per_unit:s.bundle_cost_per_unit, + requires_live:s.requires_live,bundle_id:s.bundle_id,bundle_event_id:s.bundle_event_id,bundle_side:s.bundle_side,bundle_logic:s.bundle_logic, + bundle_immediate_merge:s.bundle_immediate_merge,bundle_cost_per_unit:s.bundle_cost_per_unit, bundle_payout_per_unit:s.bundle_payout_per_unit,bundle_net_profit_per_unit:s.bundle_net_profit_per_unit,bundle_legs:s.bundle_legs, bundle_capital_efficiency:s.bundle_capital_efficiency, depth_verified:s.depth_verified,fees_verified:s.fees_verified,fee_model:s.fee_model,execution_model:s.execution_model, @@ -2521,6 +2522,32 @@ function completeBundleGroups(p){ return expected>=2&&group.length===expected&&shares.every(value=>value>0&&Math.abs(value-shares[0])<=0.02); }); } +function mergeableBinaryBundleGroup(group){ + return Array.isArray(group)&&group.length===2&&new Set(group.map(pos=>String(pos.market_id||""))).size===1 + &&new Set(group.map(pos=>String(pos.side||""))).size===2&&group.some(pos=>pos.side==="YES")&&group.some(pos=>pos.side==="NO") + &&group.every(pos=>pos.bundle_immediate_merge&&Number(pos.shares)>0); +} +function mergeCompleteBinaryBundle(p,bundleId){ + const group=completeBundleGroups(p).find(rows=>String(rows[0]&&rows[0].bundle_id||"")===String(bundleId||"")); + if(!mergeableBinaryBundleGroup(group))return null; + const units=Math.min(...group.map(pos=>Number(pos.shares||0))),payoutPerUnit=Math.min(...group.map(pos=>Number(pos.bundle_payout_per_unit||0))); + const proceeds=+(units*payoutPerUnit).toFixed(2),totalCost=+group.reduce((sum,pos)=>sum+Number(pos.cost||0),0).toFixed(2); + if(!(units>0)||!(payoutPerUnit>0)||proceeds+0.005{ + const legProceeds=index===group.length-1?+(proceeds-allocated).toFixed(2):+(proceeds*Number(pos.cost||0)/Math.max(0.01,totalCost)).toFixed(2); + allocated=+(allocated+legProceeds).toFixed(2);pos.current_price=+(legProceeds/Math.max(0.01,Number(pos.shares))).toFixed(4); + pos.exit_price=pos.current_price;pos.value=legProceeds;pos.unrealized_pnl=0;pos.closed_at=cycleIso(); + pos.close_reason="Immediate complete-set merge";pos.close_action="CLOSE";pos.bundle_merged_at=pos.closed_at; + pos.realized_pnl=+(legProceeds-Number(pos.cost||0)+Number(pos.partial_realized_pnl||0)).toFixed(2);p.closed.push(pos); + }); + const merged=new Set(group);p.positions=(p.positions||[]).filter(pos=>!merged.has(pos)); + const realized=+(proceeds-totalCost).toFixed(2); + p.history.push({date:logDay(),action:"CLOSE",question:group[0].bundle_name||group[0].question,side:"MIXED", + detail:`Merged equal YES/NO shares immediately for ${fmtUSD(proceeds)} collateral (realized ${fmtUSD(realized)} after exact modeled execution costs)`}); + return {units,proceeds,cost:totalCost,realized}; +} function completeBundleAccountingFloor(group){ const cost=group.reduce((sum,pos)=>sum+Number(pos.cost||0),0); const payoutPerUnit=Math.min(...group.map(pos=>Number(pos.bundle_payout_per_unit||0))); @@ -4171,6 +4198,7 @@ function openNegativeRiskBundle(p,cfg,s,stake,decision){ momentum_strength:0,signal_strength:1,signal_confidence:1,signal_type:"bundle-arb",price_change_1d:0,price_change_1w:0, days_to_resolution:s.days_to_resolution,jump_risk:false,bundle_id:s.bundle_id,bundle_event_id:bundleEventKey(s),bundle_name:s.event||s.question,bundle_leg_count:legs.length, bundle_payout_per_unit:s.bundle_payout_per_unit,bundle_net_profit_per_unit:s.bundle_net_profit_per_unit,requires_complete_bundle:true, + bundle_logic:s.bundle_logic||null,bundle_immediate_merge:Boolean(s.bundle_immediate_merge), learning_score:0,learning_confidence:0,market_learning_score:0,market_learning_confidence:0,learning_state:"priced-bundle",market_learning_state:"priced-bundle", historical_prior_score:0,historical_prior_confidence:0,historical_prior_features:[],historical_requires_promotion:false, learning_multiplier:1,learning_exploration:false,risk_budget_pct:+(tradeLossBudgetPct(cfg,s)*100).toFixed(2), @@ -4180,8 +4208,9 @@ function openNegativeRiskBundle(p,cfg,s,stake,decision){ if(totalCost>p.cash||totalCostpos.market_id)}; + detail:`${decision?decision.mode+" mode — ":""}Opened complete ${legs.length}-leg ${bundleSide} bundle for ${fmtUSD(totalCost)} · ${units} bundle units · modeled ${s.bundle_immediate_merge?"immediate merge":"settlement"} profit ${fmtUSD(units*Number(s.bundle_net_profit_per_unit))} after estimated costs · live prices required`}); + const merge=s.bundle_immediate_merge?mergeCompleteBinaryBundle(p,s.bundle_id):null; + return {cost:totalCost,marketIds:positions.map(pos=>pos.market_id),merge}; } function openPositions(p,cfg,rankedSugs,focus,decision,avoidMarketIds,peerStats=null){ const d=decision||{minConv:Math.max(cfg.minConv||0,cfg.aggressive?60:64),maxNew:Math.min(cfg.maxNew||4,cfg.aggressive?4:3),maxFrac:Math.min(cfg.maxFrac||0.05,cfg.aggressive?0.07:0.045),reserve:cfg.aggressive?0.10:0.18,targetExposure:cfg.targetExposure??(cfg.aggressive?0.75:0.62),minExposure:0}; @@ -4798,7 +4827,7 @@ function renderTopPicks(){ if(!top.length){root.innerHTML=`
Run a cycle to see top picks.
`;return;} root.innerHTML=top.map(s=>`
${s.url?`${esc(s.question.slice(0,58))}${s.question.length>58?"…":""}`:`${esc(s.question.slice(0,58))}${s.question.length>58?"…":""}`}
-
${esc(s.category)} ${s.quality==="bundle-arb"?`COMPLETE ${s.bundle_legs.length}-LEG ${esc(s.bundle_side||s.side)} BUNDLE · ${(s.bundle_net_profit_per_unit*100).toFixed(1)}c modeled profit/unit`:`BUY ${s.side} @ ${Math.round(s.entry_price*100)}¢`}
+
${esc(s.category)} ${s.quality==="bundle-arb"?`${s.bundle_immediate_merge?"MERGEABLE":"COMPLETE"} ${s.bundle_legs.length}-LEG ${esc(s.bundle_side||s.side)} BUNDLE · ${(s.bundle_net_profit_per_unit*100).toFixed(1)}c modeled profit/unit`:`BUY ${s.side} @ ${Math.round(s.entry_price*100)}¢`}
${Math.round(s.conviction)}
conviction
`).join(""); } function renderEmailAlerts(){ @@ -5137,7 +5166,7 @@ function renderSuggestions(){
${s.url?`${esc(s.question)}`:esc(s.question)}
${esc(s.event||"")}
- ${s.quality==="bundle-arb"?`COMPLETE ${s.bundle_legs.length}-LEG ${esc(s.bundle_side||s.side)} BUNDLE`:`${s.trade_ready?"BUY":"WATCH"} ${s.side}`} + ${s.quality==="bundle-arb"?`${s.bundle_immediate_merge?"MERGEABLE":"COMPLETE"} ${s.bundle_legs.length}-LEG ${esc(s.bundle_side||s.side)} BUNDLE`:`${s.trade_ready?"BUY":"WATCH"} ${s.side}`} ${esc(String(s.quality||"watch").toUpperCase())} @ ${Math.round(s.entry_price*100)}¢
@@ -7211,6 +7240,15 @@ function runEngineSelfTest(){ const binaryComplementQuote=bundleQuoteAtUnits(binaryComplementSuggestion,new Map([ ["binary-yes",{asks:[{price:"0.49",size:"100"}]}],["binary-no",{asks:[{price:"0.49",size:"100"}]}], ]),100); + const binaryExecutableSuggestion=Object.assign({},binaryComplementSuggestion,{bundle_legs:binaryComplementQuote.legs, + bundle_cost_per_unit:+binaryComplementQuote.cost.toFixed(6),bundle_net_profit_per_unit:+binaryComplementQuote.profit.toFixed(6), + net_edge:+binaryComplementQuote.net_return.toFixed(6),edge:+binaryComplementQuote.net_return.toFixed(6), + bundle_capital_efficiency:+bundleCapitalEfficiency(Object.assign({},binaryComplementSuggestion,{net_edge:binaryComplementQuote.net_return})).toFixed(8), + depth_verified:true,fees_verified:true,trade_ready:true,entry_candidate:true,watch_only:false,opportunity_actionable:true, + verification_status:"executable",execution_units:100,execution_notional:+binaryComplementQuote.notional.toFixed(2)}); + const binaryMergeBook=defaultPortfolio(),binaryMergeOpen=openNegativeRiskBundle(binaryMergeBook,AGENTS.find(agent=>agent.id==="value"), + binaryExecutableSuggestion,binaryComplementQuote.notional,{mode:"Fixture"}); + const compactedBinarySuggestion=compactSuggestionForSync(binaryExecutableSuggestion); const yesBundleSuggestion=negativeRiskBundleSuggestion({id:"bundle-yes-test",title:"Three-way YES result",slug:"bundle-yes-test",negRisk:true,enableNegRisk:true,markets:[ {id:"yes-bundle-a",question:"A wins",outcomes:'["Yes","No"]',outcomePrices:'["0.295","0.705"]',clobTokenIds:'["ya-yes","ya-no"]',bestBid:0.29,bestAsk:0.30,liquidityNum:30000,volumeNum:100000,volume24hr:10000,acceptingOrders:true}, {id:"yes-bundle-b",question:"B wins",outcomes:'["Yes","No"]',outcomePrices:'["0.305","0.695"]',clobTokenIds:'["yb-yes","yb-no"]',bestBid:0.30,bestAsk:0.31,liquidityNum:28000,volumeNum:100000,volume24hr:10000,acceptingOrders:true}, @@ -7812,6 +7850,13 @@ function runEngineSelfTest(){ bundleArbitrage:{ discoversExecutableBinaryComplements:Boolean(binaryComplementSuggestion&&binaryComplementSuggestion.bundle_logic==="binary-complement" &&binaryComplementQuote.depth_verified&&bundleQuoteIsActionable(binaryComplementQuote)&&Math.abs(binaryComplementQuote.profit-0.02)<0.000001), + immediatelyMergesBinaryCompleteSet:Boolean(binaryMergeOpen&&binaryMergeOpen.merge&&binaryMergeBook.positions.length===0 + &&binaryMergeBook.closed.length===2&&binaryMergeBook.cash===10002&&binaryMergeOpen.merge.realized===2), + rejectsCrossMarketBinaryMerge:!mergeableBinaryBundleGroup([ + {market_id:"market-a",side:"YES",shares:10,bundle_immediate_merge:true}, + {market_id:"market-b",side:"NO",shares:10,bundle_immediate_merge:true}, + ]), + immediateMergeMetadataSurvivesSync:compactedBinarySuggestion.bundle_immediate_merge===true, reservesBinaryComplementDepthChecks:binaryReserveOrder.length===BUNDLE_DEPTH_CANDIDATE_LIMIT &&binaryReserveOrder.filter(row=>row.bundle_logic==="binary-complement").length===BINARY_COMPLEMENT_DEPTH_RESERVE, prioritizesReturnPerLockedDay:capitalEfficiencyOrder[0].bundle_event_id==="fast-event", diff --git a/scripts/test-autonomous-runtime.mjs b/scripts/test-autonomous-runtime.mjs index 53d1f33..3d3d8ff 100644 --- a/scripts/test-autonomous-runtime.mjs +++ b/scripts/test-autonomous-runtime.mjs @@ -83,6 +83,10 @@ assert.match(index, /const BUNDLE_MIN_DAILY_RETURN=0\.0002;/); assert.match(index, /function compareBundleOpportunities\(a,b\)/); assert.match(index, /function binaryComplementBundleSuggestions\(event,\{includeUnprofitable=false\}=\{\}\)/); assert.match(index, /discoversExecutableBinaryComplements:/); +assert.match(index, /immediatelyMergesBinaryCompleteSet:/); +assert.match(index, /rejectsCrossMarketBinaryMerge:/); +assert.match(index, /immediateMergeMetadataSurvivesSync:/); +assert.match(api, /bundle_immediate_merge: s\.bundle_immediate_merge/); assert.match(index, /reservesBinaryComplementDepthChecks:/); assert.match(index, /function bundleVerificationShortlist\(candidates\)/); assert.match(index, /function prioritizeIndependentBundles\(candidates,limit=Infinity\)/);