Convert verified complete NO bundles immediately

This commit is contained in:
Theodore Song
2026-08-22 11:18:31 -04:00
parent 1e0921fc49
commit c15dc12323
7 changed files with 168 additions and 57 deletions
@@ -1,4 +1,4 @@
name: Release Build 123 after Vercel quota reset name: Release Build 124 after Vercel quota reset
on: on:
schedule: schedule:
@@ -9,7 +9,7 @@ permissions:
contents: write contents: write
concurrency: concurrency:
group: release-build-123 group: release-build-124
cancel-in-progress: false cancel-in-progress: false
jobs: jobs:
@@ -25,36 +25,36 @@ jobs:
env: env:
ARENA_URL: https://polymarket-site-eta.vercel.app ARENA_URL: https://polymarket-site-eta.vercel.app
run: | run: |
if curl --fail --silent --show-error "${ARENA_URL}/?build_123_preflight=${GITHUB_RUN_ID}" | grep -q 'const BUILD_VERSION = 123;'; then if curl --fail --silent --show-error "${ARENA_URL}/?build_124_preflight=${GITHUB_RUN_ID}" | grep -q 'const BUILD_VERSION = 124;'; then
echo "Build 123 is already live." echo "Build 124 is already live."
exit 0 exit 0
fi fi
printf 'Build 123 deployment retry from run %s at %s\n' "$GITHUB_RUN_ID" "$(date -u +%FT%TZ)" > .build-123-release-trigger printf 'Build 124 deployment retry from run %s at %s\n' "$GITHUB_RUN_ID" "$(date -u +%FT%TZ)" > .build-124-release-trigger
git config user.name "github-actions[bot]" git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com" git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add .build-123-release-trigger git add .build-124-release-trigger
git commit -m "Retry Build 123 production deployment" git commit -m "Retry Build 124 production deployment"
git push origin HEAD:main git push origin HEAD:main
- name: Wait for Build 123 in production - name: Wait for Build 124 in production
env: env:
ARENA_URL: https://polymarket-site-eta.vercel.app ARENA_URL: https://polymarket-site-eta.vercel.app
run: | run: |
for attempt in $(seq 1 40); do for attempt in $(seq 1 40); do
if curl --fail --silent --show-error "${ARENA_URL}/?build_123_wait=${GITHUB_RUN_ID}-${attempt}" | grep -q 'const BUILD_VERSION = 123;'; then if curl --fail --silent --show-error "${ARENA_URL}/?build_124_wait=${GITHUB_RUN_ID}-${attempt}" | grep -q 'const BUILD_VERSION = 124;'; then
echo "Build 123 is live." echo "Build 124 is live."
exit 0 exit 0
fi fi
sleep 30 sleep 30
done done
echo "Build 123 did not become live; Build 118 remains the autonomous runtime target." echo "Build 124 did not become live; Build 118 remains the autonomous runtime target."
exit 1 exit 1
- name: Activate the Build 123 autonomous runner - name: Activate the Build 124 autonomous runner
run: | run: |
git pull --ff-only origin main git pull --ff-only origin main
perl -0pi -e 's/EXPECTED_BUILD: "118"/EXPECTED_BUILD: "123"/' .github/workflows/autonomous-cycle.yml perl -0pi -e 's/EXPECTED_BUILD: "118"/EXPECTED_BUILD: "124"/' .github/workflows/autonomous-cycle.yml
perl -0pi -e 's/assert\.match\(workflow, \/EXPECTED_BUILD: "118"\/\);/assert.match(workflow, \/EXPECTED_BUILD: "123"\/);/' scripts/test-autonomous-runtime.mjs perl -0pi -e 's/assert\.match\(workflow, \/EXPECTED_BUILD: "118"\/\);/assert.match(workflow, \/EXPECTED_BUILD: "124"\/);/' scripts/test-autonomous-runtime.mjs
rm -f .build-123-release-trigger rm -f .build-124-release-trigger
git rm .github/workflows/release-build-123.yml git rm .github/workflows/release-build-124.yml
git add .github/workflows/autonomous-cycle.yml scripts/test-autonomous-runtime.mjs git add .github/workflows/autonomous-cycle.yml scripts/test-autonomous-runtime.mjs
git commit -m "Activate Build 123 autonomous cycles" git commit -m "Activate Build 124 autonomous cycles"
git push origin HEAD:main git push origin HEAD:main
+16 -9
View File
@@ -23,7 +23,7 @@ Offline cycles can apply calibration already earned from live observations, but
the evidence ledger is read-only: cached prices cannot grade pending signals, the evidence ledger is read-only: cached prices cannot grade pending signals,
expire horizons, or create new observations. expire horizons, or create new observations.
Build 123 targets five-minute slots with a serialized, self-chained GitHub Actions runtime. Build 124 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 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 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 workflow dispatch waits for the next five-minute boundary; one concurrency group
@@ -63,12 +63,19 @@ Polymarket event key. Related Ethereum or Bitcoin contracts cannot create
several simultaneous copies of one move, and outcomes from the same underlying several simultaneous copies of one move, and outcomes from the same underlying
three-hour shock window count as one learner event. three-hour shock window count as one learner event.
Each Build 123 cycle also scans the 1,000 most-active Polymarket events for Each Build 124 cycle also scans the 1,000 most-active Polymarket events for
complete negative-risk bundles and logically nested threshold or deadline complete negative-risk bundles and logically nested threshold or deadline
pairs, plus same-market YES/NO complements whose equal shares have a fixed pairs, plus same-market YES/NO complements whose equal shares have a fixed
$1 redemption value. Polymarket's documented complete-set merge converts equal $1 redemption value. Polymarket's documented complete-set merge converts equal
YES and NO amounts directly back into collateral, so a verified binary complement 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. is merged and realized in the same paper cycle instead of waiting for resolution.
The official negative-risk adapter also converts a complete set of NO tokens into
`n - 1` units of collateral, less the event's `negRiskFeeBips`. Strategy 65 treats
that as a separate immediate path: every leg must expose one consistent
`negRiskMarketID` and conversion fee, all asks and CLOB fee schedules must verify,
and the post-conversion proceeds must remain profitable. Missing or inconsistent
adapter metadata cannot produce paper P&L. Complete YES sets and logical dominance
pairs are not convertible and continue to use settlement or verified live-bid exits.
Gasless CTF operations include merge transactions; see Gasless CTF operations include merge transactions; see
[Positions & Tokens](https://docs.polymarket.com/concepts/positions-tokens) and [Positions & Tokens](https://docs.polymarket.com/concepts/positions-tokens) and
[Gasless Transactions](https://docs.polymarket.com/trading/gasless). [Gasless Transactions](https://docs.polymarket.com/trading/gasless).
@@ -83,7 +90,7 @@ paper order up to a $400 verified-notional ceiling. The scanner applies each mar
consumed ask level and checks the exact CLOB condition metadata for a matching consumed ask level and checks the exact CLOB condition metadata for a matching
fee curve or fee-free state. Any opened position is capped to the exact equal-unit size fee curve 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 of the largest fill that stays profitable and passes this depth test; portfolio
cash, reserve, and 4% limits can reduce it further. Strategy 64 ranks verified cash, reserve, and 4% limits can reduce it further. Strategy 65 ranks verified
structures by net return per expected locked day before raw edge, and caps active 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 cost from one underlying event at 12% of Value Hunter equity. This prevents
several related threshold pairs from monopolizing the non-directional book. New bundles must several related threshold pairs from monopolizing the non-directional book. New bundles must
@@ -103,7 +110,7 @@ The Suggestions view stores scan, depth, fee, actionable, and closest
executable-margin counts so an empty lane is evidence rather than an ambiguous executable-margin counts so an empty lane is evidence rather than an ambiguous
failure. failure.
Build 123 retains the directional learner's exact-fee policy, which replaced the blanket half-cent cost with Build 124 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 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 separate half-cent round-trip slippage allowance. Fee-free markets pay only the
slippage allowance; an unavailable fee schedule gets a conservative four-cent slippage allowance; an unavailable fee schedule gets a conservative four-cent
@@ -114,7 +121,7 @@ available at reduced weight. The exact-fee replay covered the top 1,000
active markets and 208 independent events: broad trends, reversals, and active markets and 208 independent events: broad trends, reversals, and
favorite trends all had 90% upper bounds below zero at 12 hours. A separate 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 3,000-recently-closed-market study tested 297 one-decision-per-event settlement
rules and found zero robust positive rule. Strategy 64 retains online rules and found zero robust positive rule. Strategy 65 retains online
directional evidence under fee policy 2. A separate probation gate can now use 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 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. six-hour lower confidence bound above 1% for every required cohort feature.
@@ -128,7 +135,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 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. same cohort independently passes both the 24-hour and 72-hour promotion gates.
Build 123 retains Build 100's retirement of the old 3-6 day resolution-window Build 124 retains Build 100's retirement of the old 3-6 day resolution-window
capital permission. That audit clustered confidence by event but still averaged capital permission. That audit clustered confidence by event but still averaged
several correlated contracts inside each event, while production could choose several correlated contracts inside each event, while production could choose
only one. The corrected replay chooses the highest-volume eligible contract per event and only one. The corrected replay chooses the highest-volume eligible contract per event and
@@ -248,9 +255,9 @@ evidence proves an edge.
Run `npm run evaluate:sports-favorites` for the retired pregame favorite audit. 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 Its 12-hour, 60%-75% cohort had positive point estimates but did not establish a
reliable confidence bound, so Build 123 no longer allocates capital to that rule. reliable confidence bound, so Build 124 no longer allocates capital to that rule.
Strategy 64 also tracks the exact-fee settlement calibration's three-day Sports Strategy 65 also tracks the exact-fee settlement calibration's three-day Sports
NO cohort as a zero-capital forward lane. The corrected 5,000-market run NO cohort as a zero-capital forward lane. The corrected 5,000-market run
groups every prop with the same dated contest slug, anchors the decision to the 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 published game start, and keeps only the highest-priced eligible NO contract per
@@ -378,7 +385,7 @@ Strategy 54 gives previously opened Politics trend positions that 72-hour observ
ordinary signal exits. Stops, profit locks, settlement handling, and risk-budget ordinary signal exits. Stops, profit locks, settlement handling, and risk-budget
reductions remain immediate. reductions remain immediate.
Strategy 64 retains exact Gamma entry and exit taker fees plus a half-cent Strategy 65 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, 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 not the sum of five overlapping feature buckets, and evidence from older engine
versions is down-weighted. This prevents a handful of duplicated observations versions is down-weighted. This prevents a handful of duplicated observations
+5 -2
View File
@@ -289,8 +289,11 @@ export function compactSuggestion(s) {
promoted_for_agents: s.promoted_for_agents, promoted_for_agents: s.promoted_for_agents,
watch_only: s.watch_only, jump_risk: s.jump_risk, requires_live: s.requires_live, 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_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_immediate_merge: s.bundle_immediate_merge, bundle_immediate_convert: s.bundle_immediate_convert,
bundle_payout_per_unit: s.bundle_payout_per_unit, bundle_net_profit_per_unit: s.bundle_net_profit_per_unit, neg_risk_market_id: s.neg_risk_market_id, neg_risk_fee_bips: s.neg_risk_fee_bips,
bundle_cost_per_unit: s.bundle_cost_per_unit, bundle_payout_per_unit: s.bundle_payout_per_unit,
bundle_settlement_payout_per_unit: s.bundle_settlement_payout_per_unit,
bundle_net_profit_per_unit: s.bundle_net_profit_per_unit,
bundle_capital_efficiency: s.bundle_capital_efficiency, bundle_capital_efficiency: s.bundle_capital_efficiency,
bundle_legs: s.bundle_legs, depth_verified: s.depth_verified, fees_verified: s.fees_verified, 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, fee_model: s.fee_model, execution_model: s.execution_model, execution_units: s.execution_units,
+105 -23
View File
@@ -341,7 +341,7 @@ footer{margin-top:34px;padding-top:22px;border-top:1px solid var(--border);color
<nav class="topnav"> <nav class="topnav">
<div class="brand"> <div class="brand">
<div class="logo">🏆</div> <div class="logo">🏆</div>
<div><div class="brand-name">Polymarket Arena</div><div class="brand-sub">10 agents · 5 core + 5 aggressive</div><div class="build-badge">Adaptive strategy 64 · durable evidence · build 123</div></div> <div><div class="brand-name">Polymarket Arena</div><div class="brand-sub">10 agents · 5 core + 5 aggressive</div><div class="build-badge">Adaptive strategy 65 · exact bundle conversion · build 124</div></div>
</div> </div>
<div class="tabs" id="tabs"> <div class="tabs" id="tabs">
<button class="tab" data-tab="overview">Overview</button> <button class="tab" data-tab="overview">Overview</button>
@@ -363,7 +363,7 @@ footer{margin-top:34px;padding-top:22px;border-top:1px solid var(--border);color
<div class="personal-banner" id="personalBanner"> <div class="personal-banner" id="personalBanner">
<b>Personal research mode.</b> 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. <b>Personal research mode.</b> 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.
</div> </div>
<div class="live-build-banner"><b>Build 123 active:</b> 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. Sports contest NO research is zero-capital until its forward confidence gate passes. Protected bundles require exact depth and fee checks on entry; when every live bid can realize at least 85% of remaining guaranteed profit, all legs exit atomically to book the gain and recycle capital. Phones and computers display the same read-only autonomous state. Profits are not guaranteed.</div> <div class="live-build-banner"><b>Build 124 active:</b> Strategy 65 keeps the durable evidence and bounded six-hour probation gates from Strategy 64. Complete negative-risk NO sets now use Polymarket's exact event market ID and conversion fee: after every ask and CLOB fee is verified, all legs convert atomically to collateral instead of locking cash until settlement. YES bundles and logical dominance pairs still remain intact unless verified live bids realize at least 85% of their guaranteed settlement profit. Sports contest NO research remains zero-capital until forward evidence passes. Phones and computers display the same read-only autonomous state. Profits are not guaranteed.</div>
<!-- ============ OVERVIEW ============ --> <!-- ============ OVERVIEW ============ -->
<section class="tabpanel" data-tab="overview"> <section class="tabpanel" data-tab="overview">
@@ -745,7 +745,7 @@ footer{margin-top:34px;padding-top:22px;border-top:1px solid var(--border);color
</section> </section>
<footer> <footer>
Build 123 · Adaptive strategy 64 · Evidence-first bounded snapshots · Six-hour bounded probation with matched executable exits · Serialized self-chained five-minute slots with cron recovery · Oldest-first bounded learning transport · Restart-safe offline runtime · Exact-fee directional learning · Capital-efficient, event-capped verified bundles · Atomic verified bundle-profit recycling · Conservative intact-bundle accounting · Contest-deduplicated sports shadow research · Corrected one-decision-per-event settlement audit · Autonomous runtime 2 · Offline runtime 3 · Maker research 3 · Agent learning 3 · Paper trading only · Live prices from Polymarket's public Gamma and CLOB APIs · Not financial advice · Build 124 · Adaptive strategy 65 · Exact-fee complete-NO conversion · Evidence-first bounded snapshots · Six-hour bounded probation with matched executable exits · Serialized self-chained five-minute slots with cron recovery · Oldest-first bounded learning transport · Restart-safe offline runtime · Exact-fee directional learning · Capital-efficient, event-capped verified bundles · Atomic verified bundle-profit recycling · Conservative intact-bundle accounting · Contest-deduplicated sports shadow research · Corrected one-decision-per-event settlement audit · Autonomous runtime 2 · Offline runtime 3 · Maker research 3 · Agent learning 3 · Paper trading only · Live prices from Polymarket's public Gamma and CLOB APIs · Not financial advice ·
<a class="market-link" href="https://github.com/theodore-song/polymarket-analyst" target="_blank" rel="noopener">Source on GitHub</a> <a class="market-link" href="https://github.com/theodore-song/polymarket-analyst" target="_blank" rel="noopener">Source on GitHub</a>
</footer> </footer>
</div> </div>
@@ -774,17 +774,17 @@ const POLITICS_TREND_MIN_HOLD_HOURS = 72;
const EXIT_CONFIRM_HOURS = 6; const EXIT_CONFIRM_HOURS = 6;
const AGENTS_KEY = "pma_agents_v2"; const AGENTS_KEY = "pma_agents_v2";
const SUG_KEY = "pma_suggestions_v5"; const SUG_KEY = "pma_suggestions_v5";
const BUILD_VERSION = 123; const BUILD_VERSION = 124;
const AGENT_LEARNING_VERSION = 3; const AGENT_LEARNING_VERSION = 3;
const SUGGESTION_ENGINE_VERSION = 64; const SUGGESTION_ENGINE_VERSION = 65;
const MAKER_STRATEGY_VERSION = 3; const MAKER_STRATEGY_VERSION = 3;
const SHOCK_FADE_STRATEGY_VERSION = 4; const SHOCK_FADE_STRATEGY_VERSION = 4;
const RESOLUTION_WEEK_NO_STRATEGY_VERSION = 3; const RESOLUTION_WEEK_NO_STRATEGY_VERSION = 3;
const SPORTS_CONTEST_NO_STRATEGY_VERSION = 1; const SPORTS_CONTEST_NO_STRATEGY_VERSION = 1;
const PREVIOUS_STRATEGY_VERSION = 63; const PREVIOUS_STRATEGY_VERSION = 64;
const DIRECTIONAL_SIGNAL_POLICY_VERSION = 2; const DIRECTIONAL_SIGNAL_POLICY_VERSION = 2;
const DIRECTIONAL_SIGNAL_COMPATIBLE_STRATEGY_MIN = 51; const DIRECTIONAL_SIGNAL_COMPATIBLE_STRATEGY_MIN = 51;
const DIRECTIONAL_SIGNAL_COMPATIBLE_STRATEGY_MAX = 64; const DIRECTIONAL_SIGNAL_COMPATIBLE_STRATEGY_MAX = 65;
const LEGACY_BUILD_STRATEGY_LINEAGE = Object.freeze({40:40,41:40}); const LEGACY_BUILD_STRATEGY_LINEAGE = Object.freeze({40:40,41:40});
function normalizedStrategyVersion(value){ function normalizedStrategyVersion(value){
const version=Number(value||0); const version=Number(value||0);
@@ -1226,8 +1226,16 @@ function negativeRiskBundleSuggestion(event,{includeUnprofitable=false}={}){
if(quotes.some(leg=>!leg.binary_labels||!leg.market_id||!leg.yes_token||!leg.no_token||!Number.isFinite(leg.yes_bid)||!Number.isFinite(leg.yes_ask) if(quotes.some(leg=>!leg.binary_labels||!leg.market_id||!leg.yes_token||!leg.no_token||!Number.isFinite(leg.yes_bid)||!Number.isFinite(leg.yes_ask)
||leg.yes_bid<0||leg.yes_ask>1||leg.yes_ask<leg.yes_bid||!Number.isFinite(leg.yes_mid)||!Number.isFinite(leg.no_mid) ||leg.yes_bid<0||leg.yes_ask>1||leg.yes_ask<leg.yes_bid||!Number.isFinite(leg.yes_mid)||!Number.isFinite(leg.no_mid)
||leg.liquidity<NEG_RISK_MIN_LIQUIDITY))return null; ||leg.liquidity<NEG_RISK_MIN_LIQUIDITY))return null;
const conversionIds=[event.negRiskMarketID,...rawLegs.map(raw=>raw.negRiskMarketID)].filter(value=>String(value||"").trim()).map(String);
const conversionFees=[event.negRiskFeeBips,...rawLegs.map(raw=>raw.negRiskFeeBips)].filter(value=>value!==null&&value!==undefined&&value!=="").map(Number);
const conversionMarketId=conversionIds.length&&new Set(conversionIds).size===1?conversionIds[0]:null;
const conversionFeeBips=conversionFees.length&&conversionFees.every(value=>Number.isFinite(value)&&value>=0&&value<=10000)
&&new Set(conversionFees.map(value=>value.toFixed(8))).size===1?conversionFees[0]:null;
const conversionTermsVerified=Boolean(conversionMarketId&&conversionFeeBips!==null);
const makeCandidate=(side)=>{ const makeCandidate=(side)=>{
const yesSide=side==="YES",payout=yesSide?1:quotes.length-1; const yesSide=side==="YES",settlementPayout=yesSide?1:quotes.length-1;
const immediateConvert=!yesSide&&conversionTermsVerified;
const payout=immediateConvert?settlementPayout*(1-conversionFeeBips/10000):settlementPayout;
const legs=quotes.map(leg=>{ const legs=quotes.map(leg=>{
const topAsk=yesSide?leg.yes_ask:1-leg.yes_bid,feeReserve=bundleFeeReserve(leg.raw_market,topAsk),entry=topAsk+feeReserve,current=yesSide?leg.yes_mid:leg.no_mid; const topAsk=yesSide?leg.yes_ask:1-leg.yes_bid,feeReserve=bundleFeeReserve(leg.raw_market,topAsk),entry=topAsk+feeReserve,current=yesSide?leg.yes_mid:leg.no_mid;
return {market_id:leg.market_id,condition_id:leg.condition_id,question:leg.question,side,token_id:yesSide?leg.yes_token:leg.no_token, return {market_id:leg.market_id,condition_id:leg.condition_id,question:leg.question,side,token_id:yesSide?leg.yes_token:leg.no_token,
@@ -1236,13 +1244,16 @@ function negativeRiskBundleSuggestion(event,{includeUnprofitable=false}={}){
}); });
if(legs.some(leg=>!Number.isFinite(leg.entry_price)||!Number.isFinite(leg.current_price)||leg.entry_price<=0||leg.entry_price>=1))return null; if(legs.some(leg=>!Number.isFinite(leg.entry_price)||!Number.isFinite(leg.current_price)||leg.entry_price<=0||leg.entry_price>=1))return null;
const cost=legs.reduce((sum,leg)=>sum+leg.entry_price,0),profit=payout-cost,netReturn=cost>0?profit/cost:0; const cost=legs.reduce((sum,leg)=>sum+leg.entry_price,0),profit=payout-cost,netReturn=cost>0?profit/cost:0;
return {side,legs,cost,payout,profit,netReturn}; return {side,legs,cost,payout,settlementPayout,profit,netReturn,immediateConvert};
}; };
const candidate=[makeCandidate("YES"),makeCandidate("NO")].filter(Boolean) const candidate=[makeCandidate("YES"),makeCandidate("NO")].filter(Boolean)
.filter(x=>includeUnprofitable||(x.profit>=NEG_RISK_MIN_NET_PROFIT&&x.netReturn>=NEG_RISK_MIN_NET_RETURN)) .filter(x=>includeUnprofitable||(x.profit>=NEG_RISK_MIN_NET_PROFIT&&x.netReturn>=NEG_RISK_MIN_NET_RETURN))
.sort((a,b)=>b.netReturn-a.netReturn)[0]; .sort((a,b)=>{
const days=Math.max(1,Number(daysUntil(event.endDate))||365);
return (b.netReturn/(b.immediateConvert?1:days))-(a.netReturn/(a.immediateConvert?1:days))||b.netReturn-a.netReturn;
})[0];
if(!candidate)return null; if(!candidate)return null;
const {side,legs,cost,payout,profit,netReturn}=candidate,minLiquidity=Math.min(...legs.map(leg=>leg.liquidity)); const {side,legs,cost,payout,settlementPayout,profit,netReturn,immediateConvert}=candidate,minLiquidity=Math.min(...legs.map(leg=>leg.liquidity));
const actionable=profit>=NEG_RISK_MIN_NET_PROFIT&&netReturn>=NEG_RISK_MIN_NET_RETURN; 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_event_id:String(event.id||""),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||"", question:`Complete ${side} bundle: ${event.title||"multi-outcome event"}`,event:event.title||"",
@@ -1255,10 +1266,15 @@ function negativeRiskBundleSuggestion(event,{includeUnprofitable=false}={}){
spread:Math.max(...rawLegs.map(m=>toNum(m.spread))),price_change_1h:0,price_change_1d:0,price_change_1w:0,momentum_strength:0, spread:Math.max(...rawLegs.map(m=>toNum(m.spread))),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, 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,days_to_resolution:daysUntil(event.endDate), adaptive_promotion:false,watch_only:!actionable,jump_risk:false,requires_live:true,opportunity_actionable:actionable,days_to_resolution:daysUntil(event.endDate),
bundle_cost_per_unit:+cost.toFixed(4),bundle_payout_per_unit:payout,bundle_net_profit_per_unit:+profit.toFixed(4),bundle_legs:legs, bundle_logic:`neg-risk-complete-${side.toLowerCase()}`,bundle_immediate_convert:immediateConvert,
neg_risk_market_id:immediateConvert?conversionMarketId:null,neg_risk_fee_bips:immediateConvert?conversionFeeBips:null,
bundle_cost_per_unit:+cost.toFixed(4),bundle_payout_per_unit:+payout.toFixed(8),bundle_settlement_payout_per_unit:settlementPayout,
bundle_net_profit_per_unit:+profit.toFixed(4),bundle_legs:legs,
depth_verified:false,fees_verified:false,execution_model:"top-of-book-estimate", depth_verified:false,fees_verified:false,execution_model:"top-of-book-estimate",
drivers:["complete negative-risk event","executable bid/ask gap","positive margin after estimated costs"], drivers:["complete negative-risk event",immediateConvert?"exact adapter conversion payout":"guaranteed settlement payout","executable bid/ask gap","positive margin after estimated costs"],
rationale:`Complete bundle: buy every ${side} leg together for ${cost.toFixed(3)} per bundle unit against a ${payout.toFixed(2)} worst-case payout. The preliminary margin is ${profit.toFixed(3)} (${(netReturn*100).toFixed(2)}%); a live depth and market-specific fee check is required before entry. This must stay intact until settlement.`}; rationale:immediateConvert
?`Complete NO conversion: buy every NO leg together for ${cost.toFixed(3)} per bundle unit, then atomically convert them through Polymarket's negative-risk adapter for ${payout.toFixed(4)} collateral after the exact ${conversionFeeBips.toFixed(2)} bps event conversion fee. The preliminary immediate margin is ${profit.toFixed(3)} (${(netReturn*100).toFixed(2)}%); every live ask, CLOB fee schedule, event market ID, and conversion fee must still verify before entry.`
:`Complete bundle: buy every ${side} leg together for ${cost.toFixed(3)} per bundle unit against a ${payout.toFixed(2)} worst-case settlement payout. The preliminary margin is ${profit.toFixed(3)} (${(netReturn*100).toFixed(2)}%); a live depth and market-specific fee check is required before entry. This must stay intact until settlement.`};
} }
function binaryComplementBundleSuggestions(event,{includeUnprofitable=false}={}){ function binaryComplementBundleSuggestions(event,{includeUnprofitable=false}={}){
if(!event)return []; if(!event)return [];
@@ -1391,7 +1407,7 @@ function bundleEventKey(item={}){
} }
function bundleCapitalEfficiency(item={}){ function bundleCapitalEfficiency(item={}){
const netReturn=Math.max(0,Number(item.net_edge)||0),days=Number(item.days_to_resolution); const netReturn=Math.max(0,Number(item.net_edge)||0),days=Number(item.days_to_resolution);
const lockDays=item.bundle_immediate_merge?1:(Number.isFinite(days)&&days>0?Math.max(1,days):365); const lockDays=item.bundle_immediate_merge||item.bundle_immediate_convert?1:(Number.isFinite(days)&&days>0?Math.max(1,days):365);
return netReturn/lockDays; return netReturn/lockDays;
} }
function compareBundleOpportunities(a,b){ function compareBundleOpportunities(a,b){
@@ -2142,8 +2158,10 @@ function compactSuggestionForSync(s){
adaptive_promotion:s.adaptive_promotion,adaptive_probation:s.adaptive_probation, 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, 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, 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_immediate_merge:s.bundle_immediate_merge,bundle_immediate_convert:s.bundle_immediate_convert,
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, neg_risk_market_id:s.neg_risk_market_id,neg_risk_fee_bips:s.neg_risk_fee_bips,
bundle_cost_per_unit:s.bundle_cost_per_unit,bundle_payout_per_unit:s.bundle_payout_per_unit,
bundle_settlement_payout_per_unit:s.bundle_settlement_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, 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, 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, execution_units:s.execution_units,execution_notional:s.execution_notional,verification_status:s.verification_status,
@@ -2602,6 +2620,36 @@ function mergeCompleteBinaryBundle(p,bundleId){
detail:`Merged equal YES/NO shares immediately for ${fmtUSD(proceeds)} collateral (realized ${fmtUSD(realized)} after exact modeled execution costs)`}); 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}; return {units,proceeds,cost:totalCost,realized};
} }
function convertibleNegativeRiskNoBundleGroup(group){
if(!Array.isArray(group)||group.length<2)return false;
const expected=Math.max(...group.map(pos=>Number(pos.bundle_leg_count||0)));
const marketIds=new Set(group.map(pos=>String(pos.market_id||""))),adapterIds=new Set(group.map(pos=>String(pos.neg_risk_market_id||"")));
const feeBips=group.map(pos=>Number(pos.neg_risk_fee_bips));
return group.length===expected&&marketIds.size===expected&&adapterIds.size===1&&Boolean([...adapterIds][0])
&&group.every(pos=>pos.side==="NO"&&pos.bundle_immediate_convert&&pos.bundle_logic==="neg-risk-complete-no"&&Number(pos.shares)>0)
&&feeBips.every(value=>Number.isFinite(value)&&value>=0&&value<=10000&&Math.abs(value-feeBips[0])<=1e-9);
}
function convertCompleteNegativeRiskNoBundle(p,bundleId){
const group=completeBundleGroups(p).find(rows=>String(rows[0]&&rows[0].bundle_id||"")===String(bundleId||""));
if(!convertibleNegativeRiskNoBundleGroup(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-NO negative-risk conversion";pos.close_action="CONVERT";pos.bundle_converted_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 converted=new Set(group);p.positions=(p.positions||[]).filter(pos=>!converted.has(pos));
const realized=+(proceeds-totalCost).toFixed(2),feeBips=Number(group[0].neg_risk_fee_bips||0);
p.history.push({date:logDay(),action:"CONVERT",question:group[0].bundle_name||group[0].question,side:"NO",
detail:`Converted every equal NO leg through negative-risk market ${group[0].neg_risk_market_id} for ${fmtUSD(proceeds)} collateral after the exact ${feeBips.toFixed(2)} bps conversion fee (realized ${fmtUSD(realized)} after modeled execution costs)`});
return {units,proceeds,cost:totalCost,realized,fee_bips:feeBips};
}
function closeCompleteBundleGroupAtVerifiedBids(p,group,quote){ function closeCompleteBundleGroupAtVerifiedBids(p,group,quote){
if(!p||!quote||!quote.actionable||!Array.isArray(group)||group.length<2)return null; if(!p||!quote||!quote.actionable||!Array.isArray(group)||group.length<2)return null;
const members=new Set(group),closedAt=cycleIso();p.cash=+(Number(p.cash||0)+Number(quote.proceeds||0)).toFixed(2); const members=new Set(group),closedAt=cycleIso();p.cash=+(Number(p.cash||0)+Number(quote.proceeds||0)).toFixed(2);
@@ -4315,7 +4363,8 @@ 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, 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, 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_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),fee_schedule:leg.fee_schedule||null, bundle_logic:s.bundle_logic||null,bundle_immediate_merge:Boolean(s.bundle_immediate_merge),bundle_immediate_convert:Boolean(s.bundle_immediate_convert),
neg_risk_market_id:s.neg_risk_market_id||null,neg_risk_fee_bips:s.neg_risk_fee_bips==null?null:Number(s.neg_risk_fee_bips),fee_schedule:leg.fee_schedule||null,
learning_score:0,learning_confidence:0,market_learning_score:0,market_learning_confidence:0,learning_state:"priced-bundle",market_learning_state:"priced-bundle", 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, 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), learning_multiplier:1,learning_exploration:false,risk_budget_pct:+(tradeLossBudgetPct(cfg,s)*100).toFixed(2),
@@ -4325,9 +4374,10 @@ function openNegativeRiskBundle(p,cfg,s,stake,decision){
if(totalCost>p.cash||totalCost<BUNDLE_MIN_NOTIONAL)return null; if(totalCost>p.cash||totalCost<BUNDLE_MIN_NOTIONAL)return null;
p.cash=+(p.cash-totalCost).toFixed(2);p.positions.push(...positions); p.cash=+(p.cash-totalCost).toFixed(2);p.positions.push(...positions);
p.history.push({date:logDay(),action:"OPEN",question:s.question,side:bundleSide, 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 ${s.bundle_immediate_merge?"immediate merge":"settlement"} profit ${fmtUSD(units*Number(s.bundle_net_profit_per_unit))} after estimated costs · live prices required`}); detail:`${decision?decision.mode+" mode — ":""}Opened complete ${legs.length}-leg ${bundleSide} bundle for ${fmtUSD(totalCost)} · ${units} bundle units · modeled ${s.bundle_immediate_convert?"immediate negative-risk conversion":(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; const conversion=s.bundle_immediate_convert?convertCompleteNegativeRiskNoBundle(p,s.bundle_id):null;
return {cost:totalCost,marketIds:positions.map(pos=>pos.market_id),merge}; const merge=!conversion&&s.bundle_immediate_merge?mergeCompleteBinaryBundle(p,s.bundle_id):null;
return {cost:totalCost,marketIds:positions.map(pos=>pos.market_id),conversion,merge};
} }
function openPositions(p,cfg,rankedSugs,focus,decision,avoidMarketIds,peerStats=null){ 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}; 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};
@@ -4954,7 +5004,7 @@ function renderTopPicks(){
if(!top.length){root.innerHTML=`<div class="empty">Run a cycle to see top picks.</div>`;return;} 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> 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="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"?`${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 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_convert?"CONVERTIBLE":(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(""); </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(){ function renderEmailAlerts(){
@@ -5293,7 +5343,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="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="event">${esc(s.event||"")}</div>
<div class="sug-row"> <div class="sug-row">
<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="pill ${s.side}">${s.quality==="bundle-arb"?`${s.bundle_immediate_convert?"CONVERTIBLE":(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">${esc(String(s.quality||"watch").toUpperCase())}</span>
<span class="muted small">@ ${Math.round(s.entry_price*100)}¢</span> <span class="muted small">@ ${Math.round(s.entry_price*100)}¢</span>
<div class="conv-bar"><span style="width:${s.conviction}%"></span></div> <div class="conv-bar"><span style="width:${s.conviction}%"></span></div>
@@ -7391,6 +7441,29 @@ function runEngineSelfTest(){
{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}, {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},
{id:"yes-bundle-draw",question:"Draw",outcomes:'["Yes","No"]',outcomePrices:'["0.335","0.665"]',clobTokenIds:'["yd-yes","yd-no"]',bestBid:0.33,bestAsk:0.34,liquidityNum:20000,volumeNum:100000,volume24hr:10000,acceptingOrders:true}, {id:"yes-bundle-draw",question:"Draw",outcomes:'["Yes","No"]',outcomePrices:'["0.335","0.665"]',clobTokenIds:'["yd-yes","yd-no"]',bestBid:0.33,bestAsk:0.34,liquidityNum:20000,volumeNum:100000,volume24hr:10000,acceptingOrders:true},
]}); ]});
const convertibleNoBundleSuggestion=negativeRiskBundleSuggestion({id:"bundle-no-convert",title:"Three-way exact conversion",slug:"bundle-no-convert",
negRisk:true,enableNegRisk:true,negRiskMarketID:"0xneg-risk-market",negRiskFeeBips:25,markets:[
{id:"convert-a",conditionId:"condition-a",question:"A wins",outcomes:'["Yes","No"]',outcomePrices:'["0.455","0.545"]',clobTokenIds:'["ca-yes","ca-no"]',bestBid:0.45,bestAsk:0.46,negRiskMarketID:"0xneg-risk-market",negRiskFeeBips:25,feesEnabled:false,liquidityNum:30000,acceptingOrders:true},
{id:"convert-b",conditionId:"condition-b",question:"B wins",outcomes:'["Yes","No"]',outcomePrices:'["0.355","0.645"]',clobTokenIds:'["cb-yes","cb-no"]',bestBid:0.35,bestAsk:0.36,negRiskMarketID:"0xneg-risk-market",negRiskFeeBips:25,feesEnabled:false,liquidityNum:30000,acceptingOrders:true},
{id:"convert-c",conditionId:"condition-c",question:"C wins",outcomes:'["Yes","No"]',outcomePrices:'["0.255","0.745"]',clobTokenIds:'["cc-yes","cc-no"]',bestBid:0.25,bestAsk:0.26,negRiskMarketID:"0xneg-risk-market",negRiskFeeBips:25,feesEnabled:false,liquidityNum:30000,acceptingOrders:true},
]});
const convertibleNoBooks=new Map((convertibleNoBundleSuggestion.bundle_legs||[]).map(leg=>[String(leg.token_id),
{min_order_size:"5",asks:[{price:String(leg.top_ask_price),size:"100"}]}]));
const convertibleNoQuote=bundleQuoteAtUnits(convertibleNoBundleSuggestion,convertibleNoBooks,100);
const executableNoConversion=Object.assign({},convertibleNoBundleSuggestion,{bundle_legs:convertibleNoQuote.legs,
bundle_cost_per_unit:+convertibleNoQuote.cost.toFixed(6),bundle_net_profit_per_unit:+convertibleNoQuote.profit.toFixed(6),
net_edge:+convertibleNoQuote.net_return.toFixed(6),edge:+convertibleNoQuote.net_return.toFixed(6),
bundle_capital_efficiency:+bundleCapitalEfficiency(Object.assign({},convertibleNoBundleSuggestion,{net_edge:convertibleNoQuote.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:+convertibleNoQuote.notional.toFixed(2)});
const conversionBook=defaultPortfolio(),conversionOpen=openNegativeRiskBundle(conversionBook,AGENTS.find(agent=>agent.id==="value"),
executableNoConversion,convertibleNoQuote.notional,{mode:"Fixture"});
const mismatchedConversionMetadata=negativeRiskBundleSuggestion({id:"bundle-no-mismatch",title:"Mismatched conversion metadata",negRisk:true,enableNegRisk:true,
negRiskMarketID:"0xneg-risk-market",negRiskFeeBips:25,markets:[
{id:"mca",question:"A wins",outcomes:'["Yes","No"]',outcomePrices:'["0.455","0.545"]',clobTokenIds:'["mca-yes","mca-no"]',bestBid:0.45,bestAsk:0.46,negRiskMarketID:"0xneg-risk-market",negRiskFeeBips:25,liquidityNum:30000,acceptingOrders:true},
{id:"mcb",question:"B wins",outcomes:'["Yes","No"]',outcomePrices:'["0.355","0.645"]',clobTokenIds:'["mcb-yes","mcb-no"]',bestBid:0.35,bestAsk:0.36,negRiskMarketID:"0xother-market",negRiskFeeBips:25,liquidityNum:30000,acceptingOrders:true},
{id:"mcc",question:"C wins",outcomes:'["Yes","No"]',outcomePrices:'["0.255","0.745"]',clobTokenIds:'["mcc-yes","mcc-no"]',bestBid:0.25,bestAsk:0.26,negRiskMarketID:"0xneg-risk-market",negRiskFeeBips:25,liquidityNum:30000,acceptingOrders:true},
]});
const zeroFeeBundleSuggestion=negativeRiskBundleSuggestion({id:"bundle-zero-fee",title:"Three-way zero-fee result",slug:"bundle-zero-fee",negRisk:true,enableNegRisk:true,markets:[ const zeroFeeBundleSuggestion=negativeRiskBundleSuggestion({id:"bundle-zero-fee",title:"Three-way zero-fee result",slug:"bundle-zero-fee",negRisk:true,enableNegRisk:true,markets:[
{id:"zero-a",question:"A wins",outcomes:'["Yes","No"]',outcomePrices:'["0.295","0.705"]',clobTokenIds:'["za-yes","za-no"]',bestBid:0.29,bestAsk:0.30,feesEnabled:false,liquidityNum:30000,acceptingOrders:true}, {id:"zero-a",question:"A wins",outcomes:'["Yes","No"]',outcomePrices:'["0.295","0.705"]',clobTokenIds:'["za-yes","za-no"]',bestBid:0.29,bestAsk:0.30,feesEnabled:false,liquidityNum:30000,acceptingOrders:true},
{id:"zero-b",question:"B wins",outcomes:'["Yes","No"]',outcomePrices:'["0.305","0.695"]',clobTokenIds:'["zb-yes","zb-no"]',bestBid:0.30,bestAsk:0.31,feesEnabled:false,liquidityNum:30000,acceptingOrders:true}, {id:"zero-b",question:"B wins",outcomes:'["Yes","No"]',outcomePrices:'["0.305","0.695"]',clobTokenIds:'["zb-yes","zb-no"]',bestBid:0.30,bestAsk:0.31,feesEnabled:false,liquidityNum:30000,acceptingOrders:true},
@@ -8008,6 +8081,15 @@ function runEngineSelfTest(){
rejectsVolatileMarket:makerPairCandidate(Object.assign({},makerMarket,{price_change_1d:0.12}))===null, rejectsVolatileMarket:makerPairCandidate(Object.assign({},makerMarket,{price_change_1d:0.12}))===null,
}, },
bundleArbitrage:{ bundleArbitrage:{
discoversExactCompleteNoConversion:Boolean(convertibleNoBundleSuggestion&&convertibleNoBundleSuggestion.bundle_immediate_convert
&&convertibleNoBundleSuggestion.bundle_logic==="neg-risk-complete-no"&&convertibleNoBundleSuggestion.neg_risk_market_id==="0xneg-risk-market"),
deductsAdapterFeeFromConversionPayout:Boolean(convertibleNoBundleSuggestion
&&Math.abs(convertibleNoBundleSuggestion.bundle_payout_per_unit-1.995)<0.0000001
&&convertibleNoBundleSuggestion.neg_risk_fee_bips===25),
atomicallyConvertsCompleteNoSet:Boolean(conversionOpen&&conversionOpen.conversion&&conversionBook.positions.length===0
&&conversionBook.closed.length===3&&conversionBook.cash===10004.5&&conversionOpen.conversion.realized===4.5),
blocksImmediateConversionOnMismatchedMetadata:Boolean(mismatchedConversionMetadata&&!mismatchedConversionMetadata.bundle_immediate_convert),
doesNotInventConversionWithoutAdapterMetadata:Boolean(bundleSuggestion&&!bundleSuggestion.bundle_immediate_convert),
discoversExecutableBinaryComplements:Boolean(binaryComplementSuggestion&&binaryComplementSuggestion.bundle_logic==="binary-complement" discoversExecutableBinaryComplements:Boolean(binaryComplementSuggestion&&binaryComplementSuggestion.bundle_logic==="binary-complement"
&&binaryComplementQuote.depth_verified&&bundleQuoteIsActionable(binaryComplementQuote)&&Math.abs(binaryComplementQuote.profit-0.02)<0.000001), &&binaryComplementQuote.depth_verified&&bundleQuoteIsActionable(binaryComplementQuote)&&Math.abs(binaryComplementQuote.profit-0.02)<0.000001),
immediatelyMergesBinaryCompleteSet:Boolean(binaryMergeOpen&&binaryMergeOpen.merge&&binaryMergeBook.positions.length===0 immediatelyMergesBinaryCompleteSet:Boolean(binaryMergeOpen&&binaryMergeOpen.merge&&binaryMergeBook.positions.length===0
+15 -2
View File
@@ -5,6 +5,7 @@ import { ALLOWED_RUNTIME_KEYS, compactRuntimeTransportSnapshot, validateRuntimeS
const index = fs.readFileSync(new URL("../index.html", import.meta.url), "utf8"); const index = fs.readFileSync(new URL("../index.html", import.meta.url), "utf8");
const api = fs.readFileSync(new URL("../api/state.js", import.meta.url), "utf8"); const api = fs.readFileSync(new URL("../api/state.js", import.meta.url), "utf8");
const workflow = fs.readFileSync(new URL("../.github/workflows/autonomous-cycle.yml", import.meta.url), "utf8"); const workflow = fs.readFileSync(new URL("../.github/workflows/autonomous-cycle.yml", import.meta.url), "utf8");
const releaseWorkflow = fs.readFileSync(new URL("../.github/workflows/release-build-124.yml", import.meta.url), "utf8");
const runner = fs.readFileSync(new URL("./run-autonomous-cycle.mjs", import.meta.url), "utf8"); const runner = fs.readFileSync(new URL("./run-autonomous-cycle.mjs", import.meta.url), "utf8");
const resolutionAudit = JSON.parse(fs.readFileSync(new URL("../research/resolution-week-no-audit.json", import.meta.url), "utf8")); const resolutionAudit = JSON.parse(fs.readFileSync(new URL("../research/resolution-week-no-audit.json", import.meta.url), "utf8"));
const sportsContestAudit = JSON.parse(fs.readFileSync(new URL("../research/sports-contest-no-exploration-audit.json", import.meta.url), "utf8")); const sportsContestAudit = JSON.parse(fs.readFileSync(new URL("../research/sports-contest-no-exploration-audit.json", import.meta.url), "utf8"));
@@ -13,8 +14,8 @@ const adaptiveAudit = JSON.parse(fs.readFileSync(new URL("../research/adaptive-s
const sportsEvaluator = fs.readFileSync(new URL("./evaluate-sports-favorites.mjs", import.meta.url), "utf8"); const sportsEvaluator = fs.readFileSync(new URL("./evaluate-sports-favorites.mjs", import.meta.url), "utf8");
const build = Number(index.match(/const BUILD_VERSION = (\d+);/)?.[1]); const build = Number(index.match(/const BUILD_VERSION = (\d+);/)?.[1]);
assert.equal(build, 123); assert.equal(build, 124);
assert.match(index, /Adaptive strategy 64 · durable evidence · build 123/); assert.match(index, /Adaptive strategy 65 · exact bundle conversion · build 124/);
assert.deepEqual([...ALLOWED_RUNTIME_KEYS].sort(), ["pma_agents_v2", "pma_suggestions_v5"]); assert.deepEqual([...ALLOWED_RUNTIME_KEYS].sort(), ["pma_agents_v2", "pma_suggestions_v5"]);
assert.match(index, /function collectPublicRuntimeItems\(\)/); assert.match(index, /function collectPublicRuntimeItems\(\)/);
assert.match(index, /const PUBLIC_RUNTIME_KEYS=Object\.freeze\(\[AGENTS_KEY,SUG_KEY\]\)/); assert.match(index, /const PUBLIC_RUNTIME_KEYS=Object\.freeze\(\[AGENTS_KEY,SUG_KEY\]\)/);
@@ -70,6 +71,10 @@ assert.match(workflow, /cancel-in-progress: false/);
assert.match(workflow, /next=\$\(\( \(now \/ 300 \+ 1\) \* 300 \+ 15 \)\)/); assert.match(workflow, /next=\$\(\( \(now \/ 300 \+ 1\) \* 300 \+ 15 \)\)/);
assert.match(workflow, /actions\/workflows\/autonomous-cycle\.yml\/dispatches/); assert.match(workflow, /actions\/workflows\/autonomous-cycle\.yml\/dispatches/);
assert.match(workflow, /--data '\{"ref":"main"\}'/); assert.match(workflow, /--data '\{"ref":"main"\}'/);
assert.match(releaseWorkflow, /name: Release Build 124 after Vercel quota reset/);
assert.match(releaseWorkflow, /grep -q 'const BUILD_VERSION = 124;'/);
assert.match(releaseWorkflow, /EXPECTED_BUILD: "124"/);
assert.doesNotMatch(releaseWorkflow, /Build 123|build_123|build-123/);
assert.match(index, /cloudRestartReconstructsOfflineCache:true/); assert.match(index, /cloudRestartReconstructsOfflineCache:true/);
assert.match(index, /saveSuggestions\(sugs,markets\.length,analysisMarkets\.length,bundleAudit\)/); assert.match(index, /saveSuggestions\(sugs,markets\.length,analysisMarkets\.length,bundleAudit\)/);
assert.match(index, /const NEG_RISK_EVENT_SCAN_LIMIT=1000;/); assert.match(index, /const NEG_RISK_EVENT_SCAN_LIMIT=1000;/);
@@ -96,6 +101,14 @@ assert.match(index, /immediatelyMergesBinaryCompleteSet:/);
assert.match(index, /rejectsCrossMarketBinaryMerge:/); assert.match(index, /rejectsCrossMarketBinaryMerge:/);
assert.match(index, /immediateMergeMetadataSurvivesSync:/); assert.match(index, /immediateMergeMetadataSurvivesSync:/);
assert.match(api, /bundle_immediate_merge: s\.bundle_immediate_merge/); assert.match(api, /bundle_immediate_merge: s\.bundle_immediate_merge/);
assert.match(index, /function convertCompleteNegativeRiskNoBundle\(p,bundleId\)/);
assert.match(index, /discoversExactCompleteNoConversion:/);
assert.match(index, /deductsAdapterFeeFromConversionPayout:/);
assert.match(index, /atomicallyConvertsCompleteNoSet:/);
assert.match(index, /blocksImmediateConversionOnMismatchedMetadata:/);
assert.match(index, /doesNotInventConversionWithoutAdapterMetadata:/);
assert.match(api, /bundle_immediate_convert: s\.bundle_immediate_convert/);
assert.match(api, /neg_risk_market_id: s\.neg_risk_market_id, neg_risk_fee_bips: s\.neg_risk_fee_bips/);
assert.match(index, /reservesBinaryComplementDepthChecks:/); assert.match(index, /reservesBinaryComplementDepthChecks:/);
assert.match(index, /function bundleVerificationShortlist\(candidates\)/); assert.match(index, /function bundleVerificationShortlist\(candidates\)/);
assert.match(index, /function prioritizeIndependentBundles\(candidates,limit=Infinity\)/); assert.match(index, /function prioritizeIndependentBundles\(candidates,limit=Infinity\)/);
+9 -3
View File
@@ -34,7 +34,9 @@ else process.env.NEON_DATABASE_URL = originalNeonUrl;
const compacted = compactSuggestion({ const compacted = compactSuggestion({
market_id: "bundle:1:yes", side: "YES", signal_type: "bundle-arb", signal_confidence: 1, 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", bundle_event_id: "1", 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_side: "NO", bundle_logic: "neg-risk-complete-no", bundle_immediate_convert: true,
neg_risk_market_id: "0xmarket", neg_risk_fee_bips: 25, bundle_cost_per_unit: 1.95,
bundle_payout_per_unit: 1.995, bundle_settlement_payout_per_unit: 2,
bundle_net_profit_per_unit: 0.05, bundle_capital_efficiency: 0.0025, 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 }, 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", depth_verified: true, fees_verified: true, fee_model: "verified-market-specific", execution_model: "live-order-book-vwap",
@@ -43,9 +45,13 @@ const compacted = compactSuggestion({
assert.equal(compacted.signal_type, "bundle-arb"); assert.equal(compacted.signal_type, "bundle-arb");
assert.equal(compacted.entry_candidate, true); assert.equal(compacted.entry_candidate, true);
assert.equal(compacted.requires_live, true); assert.equal(compacted.requires_live, true);
assert.equal(compacted.bundle_side, "YES"); assert.equal(compacted.bundle_side, "NO");
assert.equal(compacted.bundle_event_id, "1"); assert.equal(compacted.bundle_event_id, "1");
assert.equal(compacted.bundle_logic, "threshold-dominance"); assert.equal(compacted.bundle_logic, "neg-risk-complete-no");
assert.equal(compacted.bundle_immediate_convert, true);
assert.equal(compacted.neg_risk_market_id, "0xmarket");
assert.equal(compacted.neg_risk_fee_bips, 25);
assert.equal(compacted.bundle_settlement_payout_per_unit, 2);
assert.equal(compacted.bundle_capital_efficiency, 0.0025); assert.equal(compacted.bundle_capital_efficiency, 0.0025);
assert.equal(compacted.bundle_legs.length, 1); assert.equal(compacted.bundle_legs.length, 1);
assert.equal(compacted.fee_schedule.rate, 0); assert.equal(compacted.fee_schedule.rate, 0);
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE_NAME = "polymarket-arena-build-123"; const CACHE_NAME = "polymarket-arena-build-124";
const APP_SHELL = ["/", "/index.html", "/personal.html", "/cycle-worker.js"]; const APP_SHELL = ["/", "/index.html", "/personal.html", "/cycle-worker.js"];
self.addEventListener("install", event => { self.addEventListener("install", event => {