mirror of
https://github.com/theodore-song/polymarket-analyst.git
synced 2026-08-24 13:08:10 +00:00
Realize binary complement merges immediately
This commit is contained in:
+53
-8
@@ -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<totalCost)return null;
|
||||
p.cash=+(Number(p.cash||0)+proceeds).toFixed(2);
|
||||
let allocated=0;
|
||||
group.forEach((pos,index)=>{
|
||||
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||totalCost<BUNDLE_MIN_NOTIONAL)return null;
|
||||
p.cash=+(p.cash-totalCost).toFixed(2);p.positions.push(...positions);
|
||||
p.history.push({date:logDay(),action:"OPEN",question:s.question,side:bundleSide,
|
||||
detail:`${decision?decision.mode+" mode — ":""}Opened complete ${legs.length}-leg ${bundleSide} bundle for ${fmtUSD(totalCost)} · ${units} bundle units · modeled settlement profit ${fmtUSD(units*Number(s.bundle_net_profit_per_unit))} after estimated costs · live prices required`});
|
||||
return {cost:totalCost,marketIds:positions.map(pos=>pos.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=`<div class="empty">Run a cycle to see top picks.</div>`;return;}
|
||||
root.innerHTML=top.map(s=>`<div class="pos"><div>
|
||||
<div class="pq">${s.url?`<a class="market-title" href="${esc(s.url)}" target="_blank" rel="noopener">${esc(s.question.slice(0,58))}${s.question.length>58?"…":""}</a>`:`${esc(s.question.slice(0,58))}${s.question.length>58?"…":""}`}</div>
|
||||
<div class="sub"><span class="cat-badge" style="background:${catColor(s.category)}22;color:${catColor(s.category)}">${esc(s.category)}</span> ${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)}¢`}</div>
|
||||
<div class="sub"><span class="cat-badge" style="background:${catColor(s.category)}22;color:${catColor(s.category)}">${esc(s.category)}</span> ${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)}¢`}</div>
|
||||
</div><div class="right"><div style="font-family:'Space Grotesk';font-weight:700">${Math.round(s.conviction)}</div><div class="sub">conviction</div></div></div>`).join("");
|
||||
}
|
||||
function renderEmailAlerts(){
|
||||
@@ -5137,7 +5166,7 @@ function renderSuggestions(){
|
||||
<div class="q">${s.url?`<a class="market-title" href="${esc(s.url)}" target="_blank" rel="noopener">${esc(s.question)}</a>`:esc(s.question)}</div>
|
||||
<div class="event">${esc(s.event||"")}</div>
|
||||
<div class="sug-row">
|
||||
<span class="pill ${s.side}">${s.quality==="bundle-arb"?`COMPLETE ${s.bundle_legs.length}-LEG ${esc(s.bundle_side||s.side)} BUNDLE`:`${s.trade_ready?"BUY":"WATCH"} ${s.side}`}</span>
|
||||
<span class="pill ${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}`}</span>
|
||||
<span class="muted small">${esc(String(s.quality||"watch").toUpperCase())}</span>
|
||||
<span class="muted small">@ ${Math.round(s.entry_price*100)}¢</span>
|
||||
<div class="conv-bar"><span style="width:${s.conviction}%"></span></div>
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user