diff --git a/.github/workflows/release-build-120.yml b/.github/workflows/release-build-121.yml
similarity index 58%
rename from .github/workflows/release-build-120.yml
rename to .github/workflows/release-build-121.yml
index 190a740..2bc4f36 100644
--- a/.github/workflows/release-build-120.yml
+++ b/.github/workflows/release-build-121.yml
@@ -1,4 +1,4 @@
-name: Release Build 120 after Vercel quota reset
+name: Release Build 121 after Vercel quota reset
on:
schedule:
@@ -9,7 +9,7 @@ permissions:
contents: write
concurrency:
- group: release-build-120
+ group: release-build-121
cancel-in-progress: false
jobs:
@@ -25,36 +25,36 @@ jobs:
env:
ARENA_URL: https://polymarket-site-eta.vercel.app
run: |
- if curl --fail --silent --show-error "${ARENA_URL}/?build_120_preflight=${GITHUB_RUN_ID}" | grep -q 'const BUILD_VERSION = 120;'; then
- echo "Build 120 is already live."
+ if curl --fail --silent --show-error "${ARENA_URL}/?build_121_preflight=${GITHUB_RUN_ID}" | grep -q 'const BUILD_VERSION = 121;'; then
+ echo "Build 121 is already live."
exit 0
fi
- printf 'Build 120 deployment retry from run %s at %s\n' "$GITHUB_RUN_ID" "$(date -u +%FT%TZ)" > .build-120-release-trigger
+ printf 'Build 121 deployment retry from run %s at %s\n' "$GITHUB_RUN_ID" "$(date -u +%FT%TZ)" > .build-121-release-trigger
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- git add .build-120-release-trigger
- git commit -m "Retry Build 120 production deployment"
+ git add .build-121-release-trigger
+ git commit -m "Retry Build 121 production deployment"
git push origin HEAD:main
- - name: Wait for Build 120 in production
+ - name: Wait for Build 121 in production
env:
ARENA_URL: https://polymarket-site-eta.vercel.app
run: |
for attempt in $(seq 1 40); do
- if curl --fail --silent --show-error "${ARENA_URL}/?build_120_wait=${GITHUB_RUN_ID}-${attempt}" | grep -q 'const BUILD_VERSION = 120;'; then
- echo "Build 120 is live."
+ if curl --fail --silent --show-error "${ARENA_URL}/?build_121_wait=${GITHUB_RUN_ID}-${attempt}" | grep -q 'const BUILD_VERSION = 121;'; then
+ echo "Build 121 is live."
exit 0
fi
sleep 30
done
- echo "Build 120 did not become live; Build 118 remains the autonomous runtime target."
+ echo "Build 121 did not become live; Build 118 remains the autonomous runtime target."
exit 1
- - name: Activate the Build 120 autonomous runner
+ - name: Activate the Build 121 autonomous runner
run: |
git pull --ff-only origin main
- perl -0pi -e 's/EXPECTED_BUILD: "118"/EXPECTED_BUILD: "120"/' .github/workflows/autonomous-cycle.yml
- perl -0pi -e 's/assert\.match\(workflow, \/EXPECTED_BUILD: "118"\/\);/assert.match(workflow, \/EXPECTED_BUILD: "120"\/);/' scripts/test-autonomous-runtime.mjs
- rm -f .build-120-release-trigger
- git rm .github/workflows/release-build-120.yml
+ perl -0pi -e 's/EXPECTED_BUILD: "118"/EXPECTED_BUILD: "121"/' .github/workflows/autonomous-cycle.yml
+ perl -0pi -e 's/assert\.match\(workflow, \/EXPECTED_BUILD: "118"\/\);/assert.match(workflow, \/EXPECTED_BUILD: "121"\/);/' scripts/test-autonomous-runtime.mjs
+ rm -f .build-121-release-trigger
+ git rm .github/workflows/release-build-121.yml
git add .github/workflows/autonomous-cycle.yml scripts/test-autonomous-runtime.mjs
- git commit -m "Activate Build 120 autonomous cycles"
+ git commit -m "Activate Build 121 autonomous cycles"
git push origin HEAD:main
diff --git a/README.md b/README.md
index 6c31a20..bffd11f 100644
--- a/README.md
+++ b/README.md
@@ -20,7 +20,7 @@ market snapshots. During an outage, cycles continue locally; cached entries are
allowed for 90 minutes, older snapshots become mark-only, and all cached data
expires after 24 hours.
-Build 120 targets five-minute slots with a serialized, self-chained GitHub Actions runtime.
+Build 121 targets five-minute slots with a serialized, self-chained GitHub Actions runtime.
It continues from the previous agent snapshot and runs the next due paper cycle
even when no browser is open. After each successful cycle, one repo-scoped
workflow dispatch waits for the next five-minute boundary; one concurrency group
@@ -60,7 +60,7 @@ Polymarket event key. Related Ethereum or Bitcoin contracts cannot create
several simultaneous copies of one move, and outcomes from the same underlying
three-hour shock window count as one learner event.
-Each Build 120 cycle also scans the 1,000 most-active Polymarket events for
+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. Gamma's market-specific fee flag replaces the old blanket 0.5-cent fee
reserve for markets declared fee-free. The closest 60 structures are then
@@ -69,7 +69,11 @@ paper order up to a $400 verified-notional ceiling. The scanner applies each mar
consumed ask level and checks the CLOB fee-rate endpoint for a matching enabled
or fee-free state. Any opened position is capped to the exact equal-unit size
of the largest fill that stays profitable and passes this depth test; portfolio
-cash, reserve, and 4% limits can reduce it further. Existing paper bundles are
+cash, reserve, and 4% limits can reduce it further. Strategy 64 ranks verified
+structures by net return per expected locked day before raw edge, and caps active
+cost from one underlying event at 12% of Value Hunter equity. This prevents
+several related threshold pairs from monopolizing the non-directional book while
+preserving every already-open complete bundle until settlement. Existing paper bundles are
never retroactively enlarged against depth their original simulated fill would
already have consumed. A bundle can enter Value Discipline only when that fee check
passes and the resulting worst-case payout clears both the three-tenths-cent
@@ -79,7 +83,7 @@ The Suggestions view stores scan, depth, fee, actionable, and closest
executable-margin counts so an empty lane is evidence rather than an ambiguous
failure.
-Build 120 retains the directional learner's exact-fee policy, which replaced the blanket half-cent cost with
+Build 121 retains the directional learner's exact-fee policy, which replaced the blanket half-cent cost with
the market's Gamma fee schedule at both the entry and future checkpoint, plus a
separate half-cent round-trip slippage allowance. Fee-free markets pay only the
slippage allowance; an unavailable fee schedule gets a conservative four-cent
@@ -90,7 +94,7 @@ available at reduced weight. The exact-fee replay covered the top 1,000
active markets and 208 independent events: broad trends, reversals, and
favorite trends all had 90% upper bounds below zero at 12 hours. A separate
3,000-recently-closed-market study tested 297 one-decision-per-event settlement
-rules and found zero robust positive rule. Strategy 63 retains online
+rules and found zero robust positive rule. Strategy 64 retains online
directional evidence under fee policy 2. A separate probation gate can now use
capital after at least 12 current-policy independent events produce a net-of-cost
six-hour lower confidence bound above 1% for every required cohort feature. It
@@ -100,7 +104,7 @@ Every probation position exits at the matching six-hour executable bid and keeps
the 18% stop policy active. Normal directional sizing remains locked until the
same cohort independently passes both the 24-hour and 72-hour promotion gates.
-Build 120 retains Build 100's retirement of the old 3-6 day resolution-window
+Build 121 retains Build 100's retirement of the old 3-6 day resolution-window
capital permission. That audit clustered confidence by event but still averaged
several correlated contracts inside each event, while production could choose
only one. The corrected replay chooses the highest-volume eligible contract per event and
@@ -220,9 +224,9 @@ evidence proves an edge.
Run `npm run evaluate:sports-favorites` for the retired pregame favorite audit.
Its 12-hour, 60%-75% cohort had positive point estimates but did not establish a
-reliable confidence bound, so Build 120 no longer allocates capital to that rule.
+reliable confidence bound, so Build 121 no longer allocates capital to that rule.
-Strategy 63 also uses the exact-fee settlement calibration's three-day Sports
+Strategy 64 also uses the exact-fee settlement calibration's three-day Sports
NO cohort as a bounded paper exploration lane. The corrected 5,000-market run
groups every prop with the same dated contest slug, anchors the decision to the
published game start, and keeps only the highest-priced eligible NO contract per
@@ -348,7 +352,7 @@ Strategy 54 gives previously opened Politics trend positions that 72-hour observ
ordinary signal exits. Stops, profit locks, settlement handling, and risk-budget
reductions remain immediate.
-Strategy 63 retains exact Gamma entry and exit taker fees plus a half-cent
+Strategy 64 retains exact Gamma entry and exit taker fees plus a half-cent
slippage allowance when grading each live walk-forward signal. Confidence uses the largest independent matching bucket,
not the sum of five overlapping feature buckets, and evidence from older engine
versions is down-weighted. This prevents a handful of duplicated observations
diff --git a/api/state.js b/api/state.js
index e2d60ed..441f296 100644
--- a/api/state.js
+++ b/api/state.js
@@ -288,8 +288,9 @@ 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_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_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,
fee_model: s.fee_model, execution_model: s.execution_model, execution_units: s.execution_units,
execution_notional: s.execution_notional, verification_status: s.verification_status,
diff --git a/index.html b/index.html
index 184bcad..6bb6199 100644
--- a/index.html
+++ b/index.html
@@ -363,7 +363,7 @@ footer{margin-top:34px;padding-top:22px;border-top:1px solid var(--border);color
Personal research mode. This copy is for your own analysis, paper tracking, and manual trade research only. It does not pool money, onboard investors, custody funds, bypass eligibility rules, or place orders without your manual approval.
-
Build 120 active: Strategy 63 protects a dedicated evidence budget in every cloud snapshot, so pending observations and net-of-cost outcomes survive runner restarts instead of being displaced by repeated display text. Independently positive six-hour cohorts can enter bounded probation after at least 12 events and a lower confidence bound above 1%, using at most 0.5% per position and 1% per-agent total capital with a matching executable exit. Full directional sizing still requires positive 24-hour and 72-hour evidence, while one mature negative cohort can veto risk. Chart snapshots retain bounded time coverage, the public agents continue serialized five-minute cycles and restart-safe offline operation, and intact complete bundles remain depth-and-fee verified. Phones and computers display the same read-only autonomous state. Profits are not guaranteed.
+
Build 121 active: Strategy 64 protects a dedicated evidence budget in every cloud snapshot, so pending observations and net-of-cost outcomes survive runner restarts instead of being displaced by repeated display text. Independently positive six-hour cohorts can enter bounded probation after at least 12 events and a lower confidence bound above 1%, using at most 0.5% per position and 1% per-agent total capital with a matching executable exit. Full directional sizing still requires positive 24-hour and 72-hour evidence, while one mature negative cohort can veto risk. Depth-and-fee-verified complete bundles are ranked by return per locked day and capped at 12% of Value Hunter equity per underlying event, preventing repeated threshold pairs from crowding out independent opportunities. Phones and computers display the same read-only autonomous state. Profits are not guaranteed.
@@ -745,7 +745,7 @@ footer{margin-top:34px;padding-top:22px;border-top:1px solid var(--border);color
@@ -774,17 +774,17 @@ const POLITICS_TREND_MIN_HOLD_HOURS = 72;
const EXIT_CONFIRM_HOURS = 6;
const AGENTS_KEY = "pma_agents_v2";
const SUG_KEY = "pma_suggestions_v5";
-const BUILD_VERSION = 120;
+const BUILD_VERSION = 121;
const AGENT_LEARNING_VERSION = 3;
-const SUGGESTION_ENGINE_VERSION = 63;
+const SUGGESTION_ENGINE_VERSION = 64;
const MAKER_STRATEGY_VERSION = 3;
const SHOCK_FADE_STRATEGY_VERSION = 4;
const RESOLUTION_WEEK_NO_STRATEGY_VERSION = 3;
const SPORTS_CONTEST_NO_STRATEGY_VERSION = 1;
-const PREVIOUS_STRATEGY_VERSION = 62;
+const PREVIOUS_STRATEGY_VERSION = 63;
const DIRECTIONAL_SIGNAL_POLICY_VERSION = 2;
const DIRECTIONAL_SIGNAL_COMPATIBLE_STRATEGY_MIN = 51;
-const DIRECTIONAL_SIGNAL_COMPATIBLE_STRATEGY_MAX = 63;
+const DIRECTIONAL_SIGNAL_COMPATIBLE_STRATEGY_MAX = 64;
const LEGACY_BUILD_STRATEGY_LINEAGE = Object.freeze({40:40,41:40});
function normalizedStrategyVersion(value){
const version=Number(value||0);
@@ -872,8 +872,13 @@ 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. 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.",
- rank:(s)=>[...s].sort((a,b)=>(Math.abs(b.net_edge||b.edge||0)*120+b.conviction+Number(b.evidence_score||0)*18)-(Math.abs(a.net_edge||a.edge||0)*120+a.conviction+Number(a.evidence_score||0)*18)),
+ 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.",
+ rank:(s)=>[...s].sort((a,b)=>{
+ const bundleOrder=Number(b.quality==="bundle-arb")-Number(a.quality==="bundle-arb");
+ if(bundleOrder)return bundleOrder;
+ if(a.quality==="bundle-arb")return compareBundleOpportunities(a,b);
+ return (Math.abs(b.net_edge||b.edge||0)*120+b.conviction+Number(b.evidence_score||0)*18)-(Math.abs(a.net_edge||a.edge||0)*120+a.conviction+Number(a.evidence_score||0)*18);
+ }),
maxNew:4, maxFrac:0.055, minConv:58, kelly:0.24, targetExposure:0.62, drawdownExposure:0.42},
{id:"momentum", name:"Momentum Chaser", emoji:"🚀", color:"#34d399", kind:"strategy",
blurb:"Chases markets where attention and price are accelerating together. It prioritizes true positive-edge entries, then uses tightly sized trend probes when one-day and one-week price direction agree, spreads are narrow, and enough time remains before settlement.",
@@ -1187,7 +1192,7 @@ function dominanceBundleSuggestions(event,{includeUnprofitable=false}={}){
:`${superset.question} logically contains ${subset.question}`;
const strategy=deadline?"Buying NO on the earlier deadline and YES on the later deadline":"Buying YES on the superset and NO on the subset";
const quoteA=pair[0].quote,quoteB=pair[1].quote;
- candidates.push({market_id:bundleId,bundle_id:bundleId,bundle_side:"MIXED",bundle_logic:bundleLogic,
+ candidates.push({market_id:bundleId,bundle_id:bundleId,bundle_event_id:String(event.id||""),bundle_side:"MIXED",bundle_logic:bundleLogic,
question:`Dominance pair: ${event.title||(deadline?lower.stem.replace("{deadline}","___"):superset.stem.replace("{threshold}","___"))}`,event:event.title||"",url:legs[0].url,
category:classifyCategory(eventTags),tags:eventTags.map(tag=>tag.label).filter(Boolean).slice(0,4),side:"MIXED",
entry_price:+(cost/2).toFixed(4),yes_price:+(cost/2).toFixed(4),no_price:+(cost/2).toFixed(4),fair_value:+(payout/2).toFixed(4),
@@ -1203,7 +1208,7 @@ function dominanceBundleSuggestions(event,{includeUnprofitable=false}={}){
rationale:`${deadline?"Deadline":"Threshold"} dominance: ${relation}. ${strategy} costs ${cost.toFixed(3)} per pair against a guaranteed minimum payout of $1.00 when both use the same event terms. The preliminary margin is ${profit.toFixed(3)} (${(netReturn*100).toFixed(2)}%); a live depth and market-specific fee check is required before entry. Both live legs must open together and remain paired through settlement.`});
}
}
- return candidates.sort((a,b)=>b.net_edge-a.net_edge);
+ return candidates.sort(compareBundleOpportunities);
}
function negativeRiskBundleSuggestion(event,{includeUnprofitable=false}={}){
if(!event||!event.negRisk||event.enableNegRisk===false)return null;
@@ -1239,7 +1244,7 @@ function negativeRiskBundleSuggestion(event,{includeUnprofitable=false}={}){
if(!candidate)return null;
const {side,legs,cost,payout,profit,netReturn}=candidate,minLiquidity=Math.min(...legs.map(leg=>leg.liquidity));
const actionable=profit>=NEG_RISK_MIN_NET_PROFIT&&netReturn>=NEG_RISK_MIN_NET_RETURN;
- return {market_id:`bundle:${event.id}:${side.toLowerCase()}`,bundle_id:`bundle:${event.id}:${side.toLowerCase()}`,bundle_side:side,
+ return {market_id:`bundle:${event.id}:${side.toLowerCase()}`,bundle_id:`bundle:${event.id}:${side.toLowerCase()}`,bundle_event_id:String(event.id||""),bundle_side:side,
question:`Complete ${side} bundle: ${event.title||"multi-outcome event"}`,event:event.title||"",
url:event.slug?`https://polymarket.com/event/${event.slug}`:"",category,tags:eventTags.map(t=>t.label).filter(Boolean).slice(0,4),
side,entry_price:+(cost/legs.length).toFixed(4),yes_price:side==="YES"?+(cost/legs.length).toFixed(4):+(1-cost/legs.length).toFixed(4),
@@ -1300,6 +1305,26 @@ function bundleQuoteIsActionable(quote){
&"e.profit>=NEG_RISK_MIN_NET_PROFIT&"e.net_return>=NEG_RISK_MIN_NET_RETURN
&"e.notional<=BUNDLE_MAX_VERIFIED_NOTIONAL+0.01);
}
+function bundleEventKey(item={}){
+ const explicit=String(item.bundle_event_id||"").trim();if(explicit)return explicit;
+ const id=String(item.bundle_id||"");
+ const match=id.match(/^(?:bundle|dominance):([^:]+)/);return match?match[1]:id;
+}
+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;
+ return netReturn/lockDays;
+}
+function compareBundleOpportunities(a,b){
+ return bundleCapitalEfficiency(b)-bundleCapitalEfficiency(a)
+ ||Number(b.net_edge||-Infinity)-Number(a.net_edge||-Infinity)
+ ||Number(b.bundle_net_profit_per_unit||-Infinity)-Number(a.bundle_net_profit_per_unit||-Infinity);
+}
+function bundleEventExposure(portfolio,eventKey){
+ const key=String(eventKey||"");if(!key)return 0;
+ return (portfolio&&portfolio.positions||[]).filter(pos=>pos.requires_complete_bundle&&bundleEventKey(pos)===key)
+ .reduce((sum,pos)=>sum+Math.max(0,Number(pos.cost)||0),0);
+}
function maximizeBundleExecution(candidate,books,minimumUnits){
let best=bundleQuoteAtUnits(candidate,books,minimumUnits);if(!best.depth_verified)return best;
const repricedMinimum=bundleMinimumExecutionUnits(best.cost,minimumUnits);
@@ -1342,7 +1367,7 @@ async function fetchBundleFeeRates(tokenIds){
await Promise.all(workers);return results;
}
async function verifyExecutableBundles(candidates){
- const shortlist=(candidates||[]).slice().sort((a,b)=>Number(b.net_edge||-Infinity)-Number(a.net_edge||-Infinity)).slice(0,BUNDLE_DEPTH_CANDIDATE_LIMIT);
+ const shortlist=(candidates||[]).slice().sort(compareBundleOpportunities).slice(0,BUNDLE_DEPTH_CANDIDATE_LIMIT);
const tokenIds=shortlist.flatMap(candidate=>(candidate.bundle_legs||[]).map(leg=>leg.token_id));
const books=await fetchBundleBooks(tokenIds),depthChecked=shortlist.map(candidate=>{
const estimatedCost=Number(candidate.bundle_cost_per_unit),requiredUnits=bundleMinimumExecutionUnits(Math.max(estimatedCost,0.01));
@@ -1370,10 +1395,11 @@ async function verifyExecutableBundles(candidates){
return Object.assign({},candidate,{fees_verified:feesVerified,fee_model:feeState,trade_ready:actionable,entry_candidate:actionable,
opportunity_actionable:actionable,watch_only:!actionable,audited_observation_only:!actionable,
verification_status:actionable?"executable":(candidate.depth_verified?feeState:candidate.verification_status),
+ bundle_capital_efficiency:+bundleCapitalEfficiency(Object.assign({},candidate,{net_edge:netReturn})).toFixed(8),
friction:+friction.toFixed(6),drivers:[...(candidate.drivers||[]).filter(driver=>driver!=="positive margin after estimated costs"),
"live order-book VWAP",feesVerified?"CLOB-verified fee schedule":"fee check blocks entry"],
rationale:`${candidate.rationale} Live verification modeled ${Number(candidate.execution_units||0).toFixed(2)} equal units (${candidate.execution_notional==null?"unavailable":fmtUSD(candidate.execution_notional)}) across every leg and applied the taker fee curve at each consumed ask. ${actionable?`All legs had sufficient depth and their CLOB fee state matched Gamma; executable worst-case profit is ${Number(candidate.bundle_net_profit_per_unit).toFixed(4)} per unit after ${friction.toFixed(4)} in fees per unit.`:`Entry is blocked: ${candidate.depth_verified?feeState:"one or more legs lacked sufficient ask depth"}.`}`});
- }).sort((a,b)=>Number(b.net_edge||-Infinity)-Number(a.net_edge||-Infinity));
+ }).sort(compareBundleOpportunities);
}
async function fetchNegativeRiskBundles(limit=NEG_RISK_EVENT_SCAN_LIMIT){
const events=[],pageSize=100;
@@ -1387,7 +1413,7 @@ async function fetchNegativeRiskBundles(limit=NEG_RISK_EVENT_SCAN_LIMIT){
const audited=events.flatMap(event=>{
const complete=negativeRiskBundleSuggestion(event,{includeUnprofitable:true});
return [...(complete?[complete]:[]),...dominanceBundleSuggestions(event,{includeUnprofitable:true})];
- }).sort((a,b)=>b.net_edge-a.net_edge);
+ }).sort(compareBundleOpportunities);
const executableAudit=await verifyExecutableBundles(audited);
const candidates=executableAudit.filter(candidate=>candidate.opportunity_actionable);
const seen=new Set();
@@ -1455,6 +1481,7 @@ const BUNDLE_FEE_CONCURRENCY=8;
const BUNDLE_MIN_NOTIONAL=50;
const BUNDLE_MIN_UNITS=5;
const BUNDLE_MAX_VERIFIED_NOTIONAL=400;
+const BUNDLE_EVENT_CAP_PCT=0.12;
const SPORTS_FAVORITE_MIN_ENTRY=0.03;
const SPORTS_FAVORITE_MAX_ENTRY=0.97;
const SPORTS_FAVORITE_TARGET_LEAD_HOURS=72;
@@ -1995,8 +2022,9 @@ 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_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_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,
execution_units:s.execution_units,execution_notional:s.execution_notional,verification_status:s.verification_status,
days_to_resolution:s.days_to_resolution,event_key:s.event_key,game_start:s.game_start,hours_to_start:s.hours_to_start,
@@ -4075,7 +4103,7 @@ function openNegativeRiskBundle(p,cfg,s,stake,decision){
category:s.category,opened_at:openedAt,url:leg.url||s.url||"",peer_note:"",entry_reason:s.rationale||"",net_edge:s.net_edge,evidence_score:1,evidence_source_count:0,
friction:s.friction,chase_penalty:0,quality:"bundle-arb",strategy_version:SUGGESTION_ENGINE_VERSION,build_version:BUILD_VERSION,
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_name:s.event||s.question,bundle_leg_count:legs.length,
+ 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,
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,
@@ -4147,6 +4175,7 @@ function openPositions(p,cfg,rankedSugs,focus,decision,avoidMarketIds,peerStats=
if(!Array.isArray(s.bundle_legs)||s.bundle_legs.length<2||!(Number(s.bundle_net_profit_per_unit)>0))return reject("bundle_integrity");
if((p.positions||[]).length+s.bundle_legs.length>maxPositions)return reject("portfolio_limit");
if((p.positions||[]).some(pos=>pos.bundle_id===s.bundle_id))return reject("already_held");
+ if(bundleEventExposure(p,bundleEventKey(s))>=eqBefore*BUNDLE_EVENT_CAP_PCT-BUNDLE_MIN_NOTIONAL)return reject("bundle_event_cap");
return true;
}
if(s.signal_type==="sports-contest-no-pilot"){
@@ -4219,7 +4248,9 @@ function openPositions(p,cfg,rankedSugs,focus,decision,avoidMarketIds,peerStats=
const investable=Math.min(p.cash-eq*d.reserve,exposureGap,cycleBudgetRemaining);
if(investable<50)break;
if(s.quality==="bundle-arb"){
- const bundleStake=boundedStakeForRisk(eq,Math.min(eq*Math.min(d.maxFrac,0.04),investable),cfg,s);
+ const eventCapacity=Math.max(0,eq*BUNDLE_EVENT_CAP_PCT-bundleEventExposure(p,bundleEventKey(s)));
+ if(eventCapacityObject.assign(suggestion,{
depth_verified:true,fees_verified:true,fee_model:"verified-zero-fee",execution_model:"fixture-vwap",verification_status:"executable",execution_units:1000,
}));
+ const capitalEfficiencyOrder=[
+ Object.assign({},dominanceSuggestion,{bundle_id:"dominance:slow-event:a:b",bundle_event_id:"slow-event",net_edge:0.03,days_to_resolution:30}),
+ Object.assign({},dominanceSuggestion,{bundle_id:"dominance:fast-event:a:b",bundle_event_id:"fast-event",net_edge:0.02,days_to_resolution:5}),
+ ].sort(compareBundleOpportunities);
+ const eventCapBook=defaultPortfolio();
+ eventCapBook.cash=8800;
+ eventCapBook.positions=[
+ {bundle_id:"dominance:dominance-test:held-a:held-b",bundle_event_id:"dominance-test",requires_complete_bundle:true,cost:600,value:600},
+ {bundle_id:"dominance:dominance-test:held-c:held-d",bundle_event_id:"dominance-test",requires_complete_bundle:true,cost:600,value:600},
+ ];
+ openPositions(eventCapBook,AGENTS.find(a=>a.id==="value"),[dominanceSuggestion],"All",{
+ minConv:0,maxNew:1,maxFrac:0.04,reserve:0.10,targetExposure:0.60,learning:buildAdaptiveProfile(eventCapBook),marketLearning:{samples:0,pending:0,buckets:{}},
+ },new Set(),{});
const unverifiedBundleBook=defaultPortfolio(),unverifiedBundleOpen=openNegativeRiskBundle(unverifiedBundleBook,AGENTS.find(a=>a.id==="value"),
Object.assign({},bundleSuggestion,{depth_verified:false,verification_status:"top-of-book-estimate"}),100,null);
const depthCappedBundleBook=defaultPortfolio(),depthCappedBundle=Object.assign({},bundleSuggestion,{execution_units:50});
@@ -7670,6 +7715,9 @@ function runEngineSelfTest(){
rejectsVolatileMarket:makerPairCandidate(Object.assign({},makerMarket,{price_change_1d:0.12}))===null,
},
bundleArbitrage:{
+ prioritizesReturnPerLockedDay:capitalEfficiencyOrder[0].bundle_event_id==="fast-event",
+ derivesLegacyBundleEventKey:bundleEventKey({bundle_id:"dominance:legacy-event:market-a:market-b"})==="legacy-event",
+ capsUnderlyingEventExposure:eventCapBook.positions.length===2&&eventCapBook.lastDecision.rejectionCounts.bundle_event_cap===1,
usesMarketSpecificZeroFee:Boolean(zeroFeeBundleSuggestion&&zeroFeeBundleSuggestion.friction===0
&&zeroFeeBundleSuggestion.bundle_cost_per_unit===0.95&&zeroFeeBundleSuggestion.bundle_net_profit_per_unit===0.05),
appliesPublishedFeeCurve:bundleFeePerShare({rate:0.07,exponent:1,takerOnly:true},0.5)===0.0175,
diff --git a/scripts/run-autonomous-cycle.mjs b/scripts/run-autonomous-cycle.mjs
index babbf96..c362f00 100644
--- a/scripts/run-autonomous-cycle.mjs
+++ b/scripts/run-autonomous-cycle.mjs
@@ -252,7 +252,7 @@ async function main() {
const branch = process.env.RUNTIME_BRANCH || "runtime-state";
const pathname = process.env.RUNTIME_STATE_PATH || "runtime/state.json";
const arenaUrl = (process.env.ARENA_URL || "https://polymarket-site-eta.vercel.app").replace(/\/$/, "");
- const expectedBuild = Number(required("EXPECTED_BUILD", "120"));
+ const expectedBuild = Number(required("EXPECTED_BUILD", "121"));
await ensureRuntimeBranch(repository, branch);
const prior = await readRuntimeFile(repository, branch, pathname);
if (prior.snapshot) validateRuntimeSnapshot(prior.snapshot, 0, { allowIncomplete: true });
diff --git a/scripts/test-autonomous-runtime.mjs b/scripts/test-autonomous-runtime.mjs
index 7388f2a..774bca7 100644
--- a/scripts/test-autonomous-runtime.mjs
+++ b/scripts/test-autonomous-runtime.mjs
@@ -10,7 +10,7 @@ const resolutionAudit = JSON.parse(fs.readFileSync(new URL("../research/resoluti
const sportsContestAudit = JSON.parse(fs.readFileSync(new URL("../research/sports-contest-no-exploration-audit.json", import.meta.url), "utf8"));
const build = Number(index.match(/const BUILD_VERSION = (\d+);/)?.[1]);
-assert.equal(build, 120);
+assert.equal(build, 121);
assert.deepEqual([...ALLOWED_RUNTIME_KEYS].sort(), ["pma_agents_v2", "pma_suggestions_v5"]);
assert.match(index, /function collectPublicRuntimeItems\(\)/);
assert.match(index, /const PUBLIC_RUNTIME_KEYS=Object\.freeze\(\[AGENTS_KEY,SUG_KEY\]\)/);
@@ -74,6 +74,11 @@ assert.match(index, /bundleRequiresClobFeeVerification:true/);
assert.match(index, /function bundleExecutableLeg\(book,units,feeSchedule\)/);
assert.match(index, /function maximizeBundleExecution\(candidate,books,minimumUnits\)/);
assert.match(index, /const BUNDLE_MAX_VERIFIED_NOTIONAL=400;/);
+assert.match(index, /const BUNDLE_EVENT_CAP_PCT=0\.12;/);
+assert.match(index, /function compareBundleOpportunities\(a,b\)/);
+assert.match(index, /function bundleEventExposure\(portfolio,eventKey\)/);
+assert.match(index, /prioritizesReturnPerLockedDay:/);
+assert.match(index, /capsUnderlyingEventExposure:/);
assert.match(index, /sizesToLargestVerifiedProfitableFill:/);
assert.match(index, /stopsSizingAtShallowestBundleLeg:/);
assert.match(index, /!s\.depth_verified\|\|!s\.fees_verified\|\|s\.verification_status!=="executable"/);
diff --git a/scripts/test-server-state.mjs b/scripts/test-server-state.mjs
index 30cb1dc..d38497c 100644
--- a/scripts/test-server-state.mjs
+++ b/scripts/test-server-state.mjs
@@ -33,9 +33,9 @@ else process.env.NEON_DATABASE_URL = originalNeonUrl;
const compacted = compactSuggestion({
market_id: "bundle:1:yes", side: "YES", signal_type: "bundle-arb", signal_confidence: 1,
- entry_candidate: true, adaptive_promotion: false, requires_live: true, bundle_id: "bundle:1:yes",
+ entry_candidate: true, adaptive_promotion: false, requires_live: true, bundle_id: "bundle:1:yes", bundle_event_id: "1",
bundle_side: "YES", bundle_logic: "threshold-dominance", bundle_cost_per_unit: 0.95, bundle_payout_per_unit: 1,
- bundle_net_profit_per_unit: 0.05, bundle_legs: [{ market_id: "1", side: "YES" }],
+ bundle_net_profit_per_unit: 0.05, bundle_capital_efficiency: 0.0025, bundle_legs: [{ market_id: "1", side: "YES" }],
fees_enabled: false, fee_schedule: { rate: 0, exponent: 1, takerOnly: true },
depth_verified: true, fees_verified: true, fee_model: "verified-market-specific", execution_model: "live-order-book-vwap",
execution_units: 250, execution_notional: 237.5, verification_status: "executable", promoted_for_agents: ["value"],
@@ -44,7 +44,9 @@ assert.equal(compacted.signal_type, "bundle-arb");
assert.equal(compacted.entry_candidate, true);
assert.equal(compacted.requires_live, true);
assert.equal(compacted.bundle_side, "YES");
+assert.equal(compacted.bundle_event_id, "1");
assert.equal(compacted.bundle_logic, "threshold-dominance");
+assert.equal(compacted.bundle_capital_efficiency, 0.0025);
assert.equal(compacted.bundle_legs.length, 1);
assert.equal(compacted.fee_schedule.rate, 0);
assert.equal(compacted.execution_units, 250);
diff --git a/sw.js b/sw.js
index f06664d..c378bbe 100644
--- a/sw.js
+++ b/sw.js
@@ -1,4 +1,4 @@
-const CACHE_NAME = "polymarket-arena-build-120";
+const CACHE_NAME = "polymarket-arena-build-121";
const APP_SHELL = ["/", "/index.html", "/personal.html", "/cycle-worker.js"];
self.addEventListener("install", event => {