mirror of
https://github.com/theodore-song/polymarket-analyst.git
synced 2026-08-24 04:58:08 +00:00
Share one-hour maker research across agents
This commit is contained in:
@@ -15,7 +15,7 @@ https://polymarket-site-eta.vercel.app/personal.html
|
|||||||
|
|
||||||
The site fetches live Polymarket markets, generates agent suggestions, lets you
|
The site fetches live Polymarket markets, generates agent suggestions, lets you
|
||||||
run frequent paper cycles, and syncs the shared arena state through Neon or
|
run frequent paper cycles, and syncs the shared arena state through Neon or
|
||||||
Vercel Blob. Build 67 also installs an offline app shell and caches timestamped
|
Vercel Blob. Build 68 also installs an offline app shell and caches timestamped
|
||||||
market snapshots. During an outage, cycles continue locally; cached entries are
|
market snapshots. During an outage, cycles continue locally; cached entries are
|
||||||
allowed for 90 minutes, older snapshots become mark-only, and all cached data
|
allowed for 90 minutes, older snapshots become mark-only, and all cached data
|
||||||
expires after 24 hours.
|
expires after 24 hours.
|
||||||
@@ -26,7 +26,7 @@ team names, Over/Under, or another pair are rejected instead of being silently
|
|||||||
reinterpreted as Yes/No. The same semantic check applies to complete event
|
reinterpreted as Yes/No. The same semantic check applies to complete event
|
||||||
bundles and the offline evaluators.
|
bundles and the offline evaluators.
|
||||||
|
|
||||||
Build 67 ranks the competition by each agent's return since Strategy 56 began.
|
Build 68 ranks the competition by each agent's return since Strategy 56 began.
|
||||||
Historical replay equity remains visible for context, but it no longer makes an
|
Historical replay equity remains visible for context, but it no longer makes an
|
||||||
agent look like the current leader when the live adaptive strategy is losing.
|
agent look like the current leader when the live adaptive strategy is losing.
|
||||||
|
|
||||||
@@ -119,15 +119,24 @@ produced adverse one-leg inventory. Wider quotes traded less but remained
|
|||||||
negative. Strategy 56 therefore does not risk paper capital on an unproven
|
negative. Strategy 56 therefore does not risk paper capital on an unproven
|
||||||
maker rule.
|
maker rule.
|
||||||
|
|
||||||
Value Hunter now tracks up to six zero-capital shadow quote pairs selected by
|
Run `npm run evaluate:reward-maker` to stress current reward-qualified books
|
||||||
the shared reward-book audit. A later live
|
against 30 days of token history. The August 19 comparison found zero passing
|
||||||
|
candidates at a three-hour exit window. At one hour, 2 of 24 current candidates
|
||||||
|
passed the chronological path screen at 25% of the present reward estimate, but
|
||||||
|
both did so without an adverse historical touch; this prioritizes shadow
|
||||||
|
research and does not authorize capital.
|
||||||
|
|
||||||
|
Maker research version 2 lets all ten agents split distinct zero-capital shadow
|
||||||
|
quote pairs selected by the shared reward-book audit. Token histories are fetched
|
||||||
|
in batches, so the learner no longer silently ignores quotes after the first ten
|
||||||
|
markets. A later live
|
||||||
cycle must still verify each touch from public CLOB price history or a current
|
cycle must still verify each touch from public CLOB price history or a current
|
||||||
book cross. A three-hour executable exit grades one-sided touches after a
|
book cross. A one-hour executable exit grades one-sided touches after a
|
||||||
conservative half-cent cost, while paired touches and unfilled attempts are also
|
conservative half-cent cost, while paired touches and unfilled attempts are also
|
||||||
retained. Results are clustered by Polymarket event. Capital exposure can only
|
retained in one shared cross-agent ledger and clustered by Polymarket event. Capital exposure can only
|
||||||
resume after at least 20 current-strategy event clusters in the matching
|
resume after at least 20 current-strategy event clusters in the matching
|
||||||
category, spread, and estimated reward-yield cohorts have positive 90% confidence
|
category, spread, and estimated reward-yield cohorts have positive 90% confidence
|
||||||
bounds and at least three completed pairs, with any mature losing cohort acting as a veto. There is
|
bounds and at least three distinct-event completed pairs, with any mature losing cohort acting as a veto. There is
|
||||||
no capital-backed exploration lane. Existing paper inventory from prior builds
|
no capital-backed exploration lane. Existing paper inventory from prior builds
|
||||||
is still reconciled honestly. The engine does not credit hypothetical rewards.
|
is still reconciled honestly. The engine does not credit hypothetical rewards.
|
||||||
See Polymarket's official [fees](https://docs.polymarket.com/trading/fees),
|
See Polymarket's official [fees](https://docs.polymarket.com/trading/fees),
|
||||||
|
|||||||
+89
-42
@@ -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 56 · build 67</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 56 · maker research 2 · build 68</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 67 active:</b> a corrected 5,000-market settlement audit rejected every static side, price, category, and horizon rule after event-overlap and stale-price controls. Emotion is commentary only and can never increase stake size; a small peer boost requires independent promotion from both personal outcomes and the shared walk-forward ledger. Reward-book maker observations remain zero-capital until current cohorts prove positive net outcomes. Offline snapshots can value positions but cannot invent fills. This remains paper trading; profits are not guaranteed.</div>
|
<div class="live-build-banner"><b>Build 68 active:</b> all ten agents now split distinct one-hour reward-book shadow quotes and learn from one shared, event-deduplicated maker ledger. The shorter window passed a conservative path screen in 2 of 24 current candidates, while the three-hour rule passed none; actual paired touches and positive confidence bounds are still required before paper capital is enabled. Offline snapshots can value positions but cannot invent fills. This remains paper trading; 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 67 · Adaptive strategy 56 · Paper trading only · Live prices from Polymarket's public Gamma and CLOB APIs · Not financial advice ·
|
Build 68 · Adaptive strategy 56 · Maker research 2 · 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,8 +774,9 @@ 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 = 67;
|
const BUILD_VERSION = 68;
|
||||||
const SUGGESTION_ENGINE_VERSION = 56;
|
const SUGGESTION_ENGINE_VERSION = 56;
|
||||||
|
const MAKER_STRATEGY_VERSION = 2;
|
||||||
const PREVIOUS_STRATEGY_VERSION = 55;
|
const PREVIOUS_STRATEGY_VERSION = 55;
|
||||||
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){
|
||||||
@@ -1165,7 +1166,7 @@ const MAKER_MAX_QUOTES=6;
|
|||||||
const MAKER_MAX_QUOTE_CAPITAL_PCT=0.02;
|
const MAKER_MAX_QUOTE_CAPITAL_PCT=0.02;
|
||||||
const MAKER_TOTAL_CAPITAL_PCT=0.10;
|
const MAKER_TOTAL_CAPITAL_PCT=0.10;
|
||||||
const MAKER_QUOTE_EXPIRY_HOURS=24;
|
const MAKER_QUOTE_EXPIRY_HOURS=24;
|
||||||
const MAKER_SHADOW_HORIZON_HOURS=3;
|
const MAKER_SHADOW_HORIZON_HOURS=1;
|
||||||
const MAKER_MIN_LOCK_MARGIN=0.005;
|
const MAKER_MIN_LOCK_MARGIN=0.005;
|
||||||
const MAKER_OUTCOME_LIMIT=240;
|
const MAKER_OUTCOME_LIMIT=240;
|
||||||
const MAKER_MIN_COHORT_ATTEMPTS=20;
|
const MAKER_MIN_COHORT_ATTEMPTS=20;
|
||||||
@@ -2364,42 +2365,49 @@ function makerRewardBand(row){
|
|||||||
function summarizeMakerOutcomes(rows){
|
function summarizeMakerOutcomes(rows){
|
||||||
const weighted=[];
|
const weighted=[];
|
||||||
(rows||[]).forEach(row=>{
|
(rows||[]).forEach(row=>{
|
||||||
const version=normalizedStrategyVersion(row.strategy_version),weight=version===SUGGESTION_ENGINE_VERSION?1:(version===PREVIOUS_STRATEGY_VERSION?0.5:0.2);
|
const version=Number(row.maker_strategy_version||1),weight=version===MAKER_STRATEGY_VERSION?1:0.2;
|
||||||
const reserved=Math.max(0.01,Number(row.reserved_capital||row.deployed_capital||0.01));
|
const reserved=Math.max(0.01,Number(row.reserved_capital||row.deployed_capital||0.01));
|
||||||
weighted.push({value:Number(row.pnl||0)/reserved,weight,status:row.status,shadow:Boolean(row.shadow_only),
|
weighted.push({value:Number(row.pnl||0)/reserved,weight,status:row.status,shadow:Boolean(row.shadow_only),
|
||||||
current:version===SUGGESTION_ENGINE_VERSION,eventKey:String(row.event_key||row.market_id||row.quote_id)});
|
current:version===MAKER_STRATEGY_VERSION,eventKey:String(row.event_key||row.market_id||row.quote_id)});
|
||||||
});
|
});
|
||||||
const clusters=new Map();
|
const clusters=new Map();
|
||||||
weighted.forEach(row=>{const cluster=clusters.get(row.eventKey)||{weighted:0,weight:0,evidenceWeight:0,current:false};
|
weighted.forEach(row=>{const cluster=clusters.get(row.eventKey)||{weighted:0,weight:0,evidenceWeight:0,current:false,locked:false,currentLocked:false};
|
||||||
cluster.weighted+=row.value*row.weight;cluster.weight+=row.weight;cluster.evidenceWeight=Math.max(cluster.evidenceWeight,row.weight);
|
cluster.weighted+=row.value*row.weight;cluster.weight+=row.weight;cluster.evidenceWeight=Math.max(cluster.evidenceWeight,row.weight);
|
||||||
cluster.current=cluster.current||row.current;clusters.set(row.eventKey,cluster);});
|
cluster.current=cluster.current||row.current;cluster.locked=cluster.locked||row.status==="locked";
|
||||||
const evidence=[...clusters.values()].map(cluster=>({value:cluster.weighted/Math.max(0.01,cluster.weight),weight:cluster.evidenceWeight,current:cluster.current}));
|
cluster.currentLocked=cluster.currentLocked||(row.current&&row.status==="locked");clusters.set(row.eventKey,cluster);});
|
||||||
|
const evidence=[...clusters.values()].map(cluster=>({value:cluster.weighted/Math.max(0.01,cluster.weight),weight:cluster.evidenceWeight,
|
||||||
|
current:cluster.current,locked:cluster.locked,currentLocked:cluster.currentLocked}));
|
||||||
const totalWeight=evidence.reduce((sum,row)=>sum+row.weight,0),attempts=weighted.length;
|
const totalWeight=evidence.reduce((sum,row)=>sum+row.weight,0),attempts=weighted.length;
|
||||||
const mean=totalWeight?evidence.reduce((sum,row)=>sum+row.value*row.weight,0)/totalWeight:0;
|
const mean=totalWeight?evidence.reduce((sum,row)=>sum+row.value*row.weight,0)/totalWeight:0;
|
||||||
const variance=totalWeight?evidence.reduce((sum,row)=>sum+row.weight*(row.value-mean)**2,0)/totalWeight:0;
|
const variance=totalWeight?evidence.reduce((sum,row)=>sum+row.weight*(row.value-mean)**2,0)/totalWeight:0;
|
||||||
const margin=evidence.length>1?1.645*Math.sqrt(variance/Math.max(1,totalWeight)):1;
|
const margin=evidence.length>1?1.645*Math.sqrt(variance/Math.max(1,totalWeight)):1;
|
||||||
return {attempts,events:evidence.length,current_events:evidence.filter(row=>row.current).length,effective_attempts:+totalWeight.toFixed(2),
|
return {attempts,events:evidence.length,current_events:evidence.filter(row=>row.current).length,effective_attempts:+totalWeight.toFixed(2),
|
||||||
locked:weighted.filter(row=>row.status==="locked").length,current_locked:weighted.filter(row=>row.current&&row.status==="locked").length,
|
locked:weighted.filter(row=>row.status==="locked").length,current_locked:weighted.filter(row=>row.current&&row.status==="locked").length,
|
||||||
|
locked_events:evidence.filter(row=>row.locked).length,current_locked_events:evidence.filter(row=>row.currentLocked).length,
|
||||||
adverse:weighted.filter(row=>row.status==="single-exit").length,unfilled:weighted.filter(row=>row.status==="unfilled").length,
|
adverse:weighted.filter(row=>row.status==="single-exit").length,unfilled:weighted.filter(row=>row.status==="unfilled").length,
|
||||||
pnl:+(rows||[]).filter(row=>!row.shadow_only).reduce((sum,row)=>sum+Number(row.pnl||0),0).toFixed(2),
|
pnl:+(rows||[]).filter(row=>!row.shadow_only).reduce((sum,row)=>sum+Number(row.pnl||0),0).toFixed(2),
|
||||||
shadow_pnl:+(rows||[]).filter(row=>row.shadow_only).reduce((sum,row)=>sum+Number(row.pnl||0),0).toFixed(2),mean:+mean.toFixed(5),
|
shadow_pnl:+(rows||[]).filter(row=>row.shadow_only).reduce((sum,row)=>sum+Number(row.pnl||0),0).toFixed(2),mean:+mean.toFixed(5),
|
||||||
lower:+(mean-margin).toFixed(5),upper:+(mean+margin).toFixed(5),confidence:+(totalWeight/(totalWeight+8)).toFixed(4)};
|
lower:+(mean-margin).toFixed(5),upper:+(mean+margin).toFixed(5),confidence:+(totalWeight/(totalWeight+8)).toFixed(4)};
|
||||||
}
|
}
|
||||||
function buildMakerProfile(p){
|
function buildMakerProfile(p,limit=MAKER_OUTCOME_LIMIT){
|
||||||
const outcomes=(p&&p.maker_outcomes||[]).slice(-MAKER_OUTCOME_LIMIT),buckets={};
|
const outcomes=(p&&p.maker_outcomes||[]).slice(-limit),buckets={};
|
||||||
const add=(key,row)=>{if(!buckets[key])buckets[key]=[];buckets[key].push(row);};
|
const add=(key,row)=>{if(!buckets[key])buckets[key]=[];buckets[key].push(row);};
|
||||||
outcomes.forEach(row=>{add(`category:${row.category||"Other"}`,row);add(`spread:${row.spread_band||makerSpreadBand(row.spread)}`,row);
|
outcomes.forEach(row=>{add(`category:${row.category||"Other"}`,row);add(`spread:${row.spread_band||makerSpreadBand(row.spread)}`,row);
|
||||||
add(`reward:${row.reward_yield_band||makerRewardBand(row)}`,row);});
|
add(`reward:${row.reward_yield_band||makerRewardBand(row)}`,row);});
|
||||||
return {version:SUGGESTION_ENGINE_VERSION,outcomes:outcomes.length,global:summarizeMakerOutcomes(outcomes),
|
return {version:MAKER_STRATEGY_VERSION,outcomes:outcomes.length,global:summarizeMakerOutcomes(outcomes),
|
||||||
buckets:Object.fromEntries(Object.entries(buckets).map(([key,rows])=>[key,summarizeMakerOutcomes(rows)]))};
|
buckets:Object.fromEntries(Object.entries(buckets).map(([key,rows])=>[key,summarizeMakerOutcomes(rows)]))};
|
||||||
}
|
}
|
||||||
|
function buildSharedMakerProfile(st){
|
||||||
|
const outcomes=AGENTS.flatMap(agent=>(st.agents[agent.id].maker_outcomes||[])).slice(-MAKER_OUTCOME_LIMIT*AGENTS.length);
|
||||||
|
return buildMakerProfile({maker_outcomes:outcomes},MAKER_OUTCOME_LIMIT*AGENTS.length);
|
||||||
|
}
|
||||||
function makerCandidateLearning(candidate,profile){
|
function makerCandidateLearning(candidate,profile){
|
||||||
const category=profile.buckets[`category:${candidate.category}`],spread=profile.buckets[`spread:${makerSpreadBand(candidate.spread)}`];
|
const category=profile.buckets[`category:${candidate.category}`],spread=profile.buckets[`spread:${makerSpreadBand(candidate.spread)}`];
|
||||||
const rewardBand=candidate.reward_yield_band||makerRewardBand(candidate),reward=profile.buckets[`reward:${rewardBand}`];
|
const rewardBand=candidate.reward_yield_band||makerRewardBand(candidate),reward=profile.buckets[`reward:${rewardBand}`];
|
||||||
const matching=[profile.global,category,spread,reward].filter(Boolean),mature=matching.filter(stats=>stats.current_events>=MAKER_MIN_COHORT_ATTEMPTS);
|
const matching=[profile.global,category,spread,reward].filter(Boolean),mature=matching.filter(stats=>stats.current_events>=MAKER_MIN_COHORT_ATTEMPTS);
|
||||||
const losing=mature.some(stats=>stats.upper<0),promoted=!losing&&category&&spread&&[category,spread].every(stats=>
|
const losing=mature.some(stats=>stats.upper<0),promoted=!losing&&category&&spread&&[category,spread].every(stats=>
|
||||||
stats.current_events>=MAKER_MIN_COHORT_ATTEMPTS&&stats.current_locked>=MAKER_MIN_PROMOTION_LOCKS&&stats.lower>0)
|
stats.current_events>=MAKER_MIN_COHORT_ATTEMPTS&&stats.current_locked_events>=MAKER_MIN_PROMOTION_LOCKS&&stats.lower>0)
|
||||||
&&reward&&reward.current_events>=MAKER_MIN_COHORT_ATTEMPTS&&reward.current_locked>=MAKER_MIN_PROMOTION_LOCKS&&reward.lower>0;
|
&&reward&&reward.current_events>=MAKER_MIN_COHORT_ATTEMPTS&&reward.current_locked_events>=MAKER_MIN_PROMOTION_LOCKS&&reward.lower>0;
|
||||||
const positive=[category,spread,reward].filter(Boolean).sort((a,b)=>b.lower-a.lower)[0];
|
const positive=[category,spread,reward].filter(Boolean).sort((a,b)=>b.lower-a.lower)[0];
|
||||||
const multiplier=promoted?clamp(1+Math.max(0,positive.lower)*6*positive.confidence,1,1.25):1;
|
const multiplier=promoted?clamp(1+Math.max(0,positive.lower)*6*positive.confidence,1,1.25):1;
|
||||||
const units=+(candidate.units*multiplier).toFixed(2),requiredCapital=+(units*candidate.paired_cost).toFixed(2),lockedProfit=+(units*(1-candidate.paired_cost)).toFixed(2);
|
const units=+(candidate.units*multiplier).toFixed(2),requiredCapital=+(units*candidate.paired_cost).toFixed(2),lockedProfit=+(units*(1-candidate.paired_cost)).toFixed(2);
|
||||||
@@ -2418,7 +2426,8 @@ function recordMakerOutcome(p,quote,status,pnl,deployedCapital){
|
|||||||
reward_share_floor:+Number(quote.reward_share_floor||0).toFixed(6),status,pnl:+Number(pnl||0).toFixed(2),shadow_only:Boolean(quote.shadow_only),
|
reward_share_floor:+Number(quote.reward_share_floor||0).toFixed(6),status,pnl:+Number(pnl||0).toFixed(2),shadow_only:Boolean(quote.shadow_only),
|
||||||
deployed_capital:+Number(deployedCapital||0).toFixed(2),reserved_capital:+reserved.toFixed(2),return_on_reserved:+(Number(pnl||0)/reserved).toFixed(5),
|
deployed_capital:+Number(deployedCapital||0).toFixed(2),reserved_capital:+reserved.toFixed(2),return_on_reserved:+(Number(pnl||0)/reserved).toFixed(5),
|
||||||
created_at:quote.created_at,completed_at:cycleIso(),hours_to_outcome:+hoursSince(quote.created_at).toFixed(2),
|
created_at:quote.created_at,completed_at:cycleIso(),hours_to_outcome:+hoursSince(quote.created_at).toFixed(2),
|
||||||
strategy_version:Number(quote.strategy_version||SUGGESTION_ENGINE_VERSION),build_version:Number(quote.build_version||BUILD_VERSION)});
|
strategy_version:Number(quote.strategy_version||SUGGESTION_ENGINE_VERSION),maker_strategy_version:Number(quote.maker_strategy_version||MAKER_STRATEGY_VERSION),
|
||||||
|
build_version:Number(quote.build_version||BUILD_VERSION)});
|
||||||
p.maker_outcomes=p.maker_outcomes.slice(-MAKER_OUTCOME_LIMIT);
|
p.maker_outcomes=p.maker_outcomes.slice(-MAKER_OUTCOME_LIMIT);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -2450,23 +2459,27 @@ function makerPairCandidate(m){
|
|||||||
competitive:+competition.toFixed(4),spread:+spread.toFixed(4),tick_size:tick,day_move:+dayMove.toFixed(4),
|
competitive:+competition.toFixed(4),spread:+spread.toFixed(4),tick_size:tick,day_move:+dayMove.toFixed(4),
|
||||||
score:+(activity*0.25+(1-competition)*1.5+spread*20-dayMove*12+Math.log10(reward+1)*0.2).toFixed(4)};
|
score:+(activity*0.25+(1-competition)*1.5+spread*20-dayMove*12+Math.log10(reward+1)*0.2).toFixed(4)};
|
||||||
}
|
}
|
||||||
function stageMakerQuotes(p,markets){
|
function stageMakerQuotes(p,markets,{profile=null,agentIndex=null,agentCount=1,globalMarketIds=null,globalEventKeys=null}={}){
|
||||||
p.maker_quotes=Array.isArray(p.maker_quotes)?p.maker_quotes:[];
|
p.maker_quotes=Array.isArray(p.maker_quotes)?p.maker_quotes:[];
|
||||||
const eq=equity(p),existing=new Set(p.maker_quotes.map(quote=>String(quote.market_id))),held=new Set((p.positions||[]).map(pos=>String(pos.market_id)));
|
const eq=equity(p),existing=new Set(p.maker_quotes.map(quote=>String(quote.market_id))),held=new Set((p.positions||[]).map(pos=>String(pos.market_id)));
|
||||||
let reserved=p.maker_quotes.filter(quote=>!quote.shadow_only).reduce((sum,quote)=>sum+Number(quote.required_capital||0),0),staged=0,shadowStaged=0,capitalStaged=0;
|
let reserved=p.maker_quotes.filter(quote=>!quote.shadow_only).reduce((sum,quote)=>sum+Number(quote.required_capital||0),0),staged=0,shadowStaged=0,capitalStaged=0;
|
||||||
const makerProfile=buildMakerProfile(p);
|
const makerProfile=profile||buildMakerProfile(p);
|
||||||
const candidates=(markets||[]).map(makerPairCandidate).filter(Boolean).map(candidate=>makerCandidateLearning(candidate,makerProfile)).filter(Boolean).sort((a,b)=>b.score-a.score);
|
let candidates=(markets||[]).map(makerPairCandidate).filter(Boolean).map(candidate=>makerCandidateLearning(candidate,makerProfile)).filter(Boolean).sort((a,b)=>b.score-a.score);
|
||||||
|
if(Number.isInteger(agentIndex)&&agentCount>1)candidates=candidates.filter((candidate,index)=>index%agentCount===agentIndex);
|
||||||
for(const candidate of candidates){
|
for(const candidate of candidates){
|
||||||
if(p.maker_quotes.length>=MAKER_MAX_QUOTES)break;
|
if(p.maker_quotes.length>=MAKER_MAX_QUOTES)break;
|
||||||
const capitalEnabled=Boolean(candidate.maker_capital_enabled);
|
const capitalEnabled=Boolean(candidate.maker_capital_enabled);
|
||||||
if(existing.has(candidate.market_id)||held.has(candidate.market_id))continue;
|
if(existing.has(candidate.market_id)||held.has(candidate.market_id)||(globalMarketIds&&globalMarketIds.has(candidate.market_id))
|
||||||
|
||(globalEventKeys&&globalEventKeys.has(String(candidate.event_key))))continue;
|
||||||
if(capitalEnabled&&(reserved>=eq*MAKER_TOTAL_CAPITAL_PCT||candidate.required_capital>eq*MAKER_MAX_QUOTE_CAPITAL_PCT
|
if(capitalEnabled&&(reserved>=eq*MAKER_TOTAL_CAPITAL_PCT||candidate.required_capital>eq*MAKER_MAX_QUOTE_CAPITAL_PCT
|
||||||
||reserved+candidate.required_capital>Math.min(p.cash,eq*MAKER_TOTAL_CAPITAL_PCT)))continue;
|
||reserved+candidate.required_capital>Math.min(p.cash,eq*MAKER_TOTAL_CAPITAL_PCT)))continue;
|
||||||
const createdAt=cycleIso(),quote=Object.assign({},candidate,{id:`maker:${candidate.market_id}:${Date.now()}:${p.maker_quotes.length}`,
|
const createdAt=cycleIso(),quote=Object.assign({},candidate,{id:`maker:${candidate.market_id}:${Date.now()}:${p.maker_quotes.length}`,
|
||||||
created_at:createdAt,yes_quote_updated_at:createdAt,no_quote_updated_at:createdAt,strategy_version:SUGGESTION_ENGINE_VERSION,build_version:BUILD_VERSION,
|
created_at:createdAt,yes_quote_updated_at:createdAt,no_quote_updated_at:createdAt,strategy_version:SUGGESTION_ENGINE_VERSION,
|
||||||
|
maker_strategy_version:MAKER_STRATEGY_VERSION,build_version:BUILD_VERSION,
|
||||||
shadow_only:!capitalEnabled,shadow_horizon_hours:MAKER_SHADOW_HORIZON_HOURS,
|
shadow_only:!capitalEnabled,shadow_horizon_hours:MAKER_SHADOW_HORIZON_HOURS,
|
||||||
yes_filled_at:null,no_filled_at:null,yes_fill_price:null,no_fill_price:null});
|
yes_filled_at:null,no_filled_at:null,yes_fill_price:null,no_fill_price:null});
|
||||||
p.maker_quotes.push(quote);existing.add(candidate.market_id);staged++;
|
p.maker_quotes.push(quote);existing.add(candidate.market_id);if(globalMarketIds)globalMarketIds.add(candidate.market_id);
|
||||||
|
if(globalEventKeys)globalEventKeys.add(String(candidate.event_key));staged++;
|
||||||
if(capitalEnabled){reserved+=candidate.required_capital;capitalStaged++;}else shadowStaged++;
|
if(capitalEnabled){reserved+=candidate.required_capital;capitalStaged++;}else shadowStaged++;
|
||||||
p.history.push({date:logDay(),action:capitalEnabled?"QUOTE":"SHADOW_QUOTE",question:candidate.question,side:"PAIR",
|
p.history.push({date:logDay(),action:capitalEnabled?"QUOTE":"SHADOW_QUOTE",question:candidate.question,side:"PAIR",
|
||||||
detail:capitalEnabled
|
detail:capitalEnabled
|
||||||
@@ -2489,10 +2502,14 @@ async function fetchMakerTouchHistory(quotes,marketMap){
|
|||||||
if(!tokens.size||!created.length)return {};
|
if(!tokens.size||!created.length)return {};
|
||||||
try{
|
try{
|
||||||
const endTs=Math.floor(Date.now()/1000),startTs=Math.max(endTs-(MAKER_QUOTE_EXPIRY_HOURS+1)*3600,Math.min(...created)-60);
|
const endTs=Math.floor(Date.now()/1000),startTs=Math.max(endTs-(MAKER_QUOTE_EXPIRY_HOURS+1)*3600,Math.min(...created)-60);
|
||||||
const response=await fetchWithTimeout(`${CLOB}/batch-prices-history`,{method:"POST",headers:{"content-type":"application/json"},
|
const tokenList=[...tokens],history={};
|
||||||
body:JSON.stringify({markets:[...tokens].slice(0,20),start_ts:startTs,end_ts:endTs,fidelity:MAKER_TOUCH_FIDELITY_MINUTES})},PRICE_REQUEST_TIMEOUT_MS*2);
|
for(let index=0;index<tokenList.length;index+=20){
|
||||||
if(!response.ok)return {};
|
const response=await fetchWithTimeout(`${CLOB}/batch-prices-history`,{method:"POST",headers:{"content-type":"application/json"},
|
||||||
const payload=await response.json();return payload&&payload.history&&typeof payload.history==="object"?payload.history:{};
|
body:JSON.stringify({markets:tokenList.slice(index,index+20),start_ts:startTs,end_ts:endTs,fidelity:MAKER_TOUCH_FIDELITY_MINUTES})},PRICE_REQUEST_TIMEOUT_MS*2);
|
||||||
|
if(!response.ok)continue;
|
||||||
|
const payload=await response.json();if(payload&&payload.history&&typeof payload.history==="object")Object.assign(history,payload.history);
|
||||||
|
}
|
||||||
|
return history;
|
||||||
}catch(e){return {};}
|
}catch(e){return {};}
|
||||||
}
|
}
|
||||||
function makerTouchEvidence(quote,side,touchHistory){
|
function makerTouchEvidence(quote,side,touchHistory){
|
||||||
@@ -2586,14 +2603,20 @@ function manageMakerQuotes(p,marketMap,{executeTrades=false,touchHistory={}}={})
|
|||||||
if(!executeTrades)return {active:p.maker_quotes.length,fills:0,locked:0,expired:0};
|
if(!executeTrades)return {active:p.maker_quotes.length,fills:0,locked:0,expired:0};
|
||||||
const keep=[];let fills=0,locked=0,expired=0,historyTouches=0,shadowCompleted=0;
|
const keep=[];let fills=0,locked=0,expired=0,historyTouches=0,shadowCompleted=0;
|
||||||
for(const quote of p.maker_quotes){
|
for(const quote of p.maker_quotes){
|
||||||
const fresh=marketMap[String(quote.market_id)];
|
const legacyMaker=Number(quote.maker_strategy_version||1)<MAKER_STRATEGY_VERSION;
|
||||||
if(!fresh){keep.push(quote);continue;}
|
if(legacyMaker&"e.shadow_only){
|
||||||
const legacyCapital=!quote.shadow_only&&normalizedStrategyVersion(quote.strategy_version)<SUGGESTION_ENGINE_VERSION;
|
p.history.push({date:logDay(),action:"SHADOW_RETIRE",question:quote.question,side:"PAIR",
|
||||||
|
detail:`Retired the old ${quote.shadow_horizon_hours||3}h maker-research window without grading it into the new one-hour learner`});
|
||||||
|
expired++;continue;
|
||||||
|
}
|
||||||
|
const legacyCapital=!quote.shadow_only&&legacyMaker;
|
||||||
if(legacyCapital&&!quote.yes_filled_at&&!quote.no_filled_at){
|
if(legacyCapital&&!quote.yes_filled_at&&!quote.no_filled_at){
|
||||||
p.history.push({date:logDay(),action:"MAKER_RETIRE",question:quote.question,side:"PAIR",
|
p.history.push({date:logDay(),action:"MAKER_RETIRE",question:quote.question,side:"PAIR",
|
||||||
detail:`Canceled untouched Strategy ${normalizedStrategyVersion(quote.strategy_version)} maker bids after the chronological audit rejected the rule; no fill or profit recorded`});
|
detail:`Canceled untouched Strategy ${normalizedStrategyVersion(quote.strategy_version)} maker bids after the chronological audit rejected the rule; no fill or profit recorded`});
|
||||||
expired++;continue;
|
expired++;continue;
|
||||||
}
|
}
|
||||||
|
const fresh=marketMap[String(quote.market_id)];
|
||||||
|
if(!fresh){keep.push(quote);continue;}
|
||||||
if(legacyCapital&&Boolean(quote.yes_filled_at)!==Boolean(quote.no_filled_at)){
|
if(legacyCapital&&Boolean(quote.yes_filled_at)!==Boolean(quote.no_filled_at)){
|
||||||
if(unwindMakerInventory(p,quote,fresh)){expired++;continue;}
|
if(unwindMakerInventory(p,quote,fresh)){expired++;continue;}
|
||||||
keep.push(quote);continue;
|
keep.push(quote);continue;
|
||||||
@@ -3076,10 +3099,13 @@ async function runDailyCycle(){
|
|||||||
const cachePolicy=offlineCachePolicy(cacheAgeMs);
|
const cachePolicy=offlineCachePolicy(cacheAgeMs);
|
||||||
const executeTrades=runMode==="live"||cachePolicy.exitsAllowed;
|
const executeTrades=runMode==="live"||cachePolicy.exitsAllowed;
|
||||||
const quoteMarketMap=Object.assign({},cachedById,freshById);
|
const quoteMarketMap=Object.assign({},cachedById,freshById);
|
||||||
const valuePortfolio=st.agents.value;
|
const allMakerQuotes=AGENTS.flatMap(agent=>st.agents[agent.id].maker_quotes||[]);
|
||||||
if(runMode==="live"&&(valuePortfolio.maker_quotes||[]).length)setStatus("verifying resting quote touches…",true);
|
if(runMode==="live"&&allMakerQuotes.length)setStatus("verifying resting quote touches…",true);
|
||||||
const makerTouchHistory=runMode==="live"?await fetchMakerTouchHistory(valuePortfolio.maker_quotes,quoteMarketMap):{};
|
const makerTouchHistory=runMode==="live"?await fetchMakerTouchHistory(allMakerQuotes,quoteMarketMap):{};
|
||||||
const makerActivity=manageMakerQuotes(valuePortfolio,quoteMarketMap,{executeTrades:runMode==="live"&&executeTrades,touchHistory:makerTouchHistory});
|
const makerActivityByAgent={};
|
||||||
|
AGENTS.forEach(agent=>{makerActivityByAgent[agent.id]=manageMakerQuotes(st.agents[agent.id],quoteMarketMap,
|
||||||
|
{executeTrades:runMode==="live"&&executeTrades,touchHistory:makerTouchHistory});});
|
||||||
|
const sharedMakerProfile=buildSharedMakerProfile(st);
|
||||||
for(const cfg of AGENTS){
|
for(const cfg of AGENTS){
|
||||||
const p=st.agents[cfg.id];
|
const p=st.agents[cfg.id];
|
||||||
markToMarket(p,priceMap,cfg,{policyExits:executeTrades,executeTrades});
|
markToMarket(p,priceMap,cfg,{policyExits:executeTrades,executeTrades});
|
||||||
@@ -3088,6 +3114,11 @@ async function runDailyCycle(){
|
|||||||
const entriesAllowed=runMode==="live"||cachePolicy.entriesAllowed;
|
const entriesAllowed=runMode==="live"||cachePolicy.entriesAllowed;
|
||||||
const cycleSuggestions=prepareCycleSuggestions(sugs,runMode,entriesAllowed);
|
const cycleSuggestions=prepareCycleSuggestions(sugs,runMode,entriesAllowed);
|
||||||
const claimedMarkets=new Set();
|
const claimedMarkets=new Set();
|
||||||
|
const globalMakerMarkets=new Set(AGENTS.flatMap(agent=>[
|
||||||
|
...(st.agents[agent.id].maker_quotes||[]).map(quote=>String(quote.market_id)),
|
||||||
|
...(st.agents[agent.id].positions||[]).filter(pos=>pos.signal_type==="maker-pair").map(pos=>String(pos.market_id)),
|
||||||
|
]));
|
||||||
|
const globalMakerEvents=new Set(AGENTS.flatMap(agent=>(st.agents[agent.id].maker_quotes||[]).map(quote=>String(quote.event_key))));
|
||||||
for(const cfg of strategyExecutionOrder(st)){
|
for(const cfg of strategyExecutionOrder(st)){
|
||||||
const p=st.agents[cfg.id];
|
const p=st.agents[cfg.id];
|
||||||
const rank=preBoard.findIndex(x=>x.id===cfg.id)+1||preBoard.length;
|
const rank=preBoard.findIndex(x=>x.id===cfg.id)+1||preBoard.length;
|
||||||
@@ -3095,13 +3126,15 @@ async function runDailyCycle(){
|
|||||||
const occupied=occupiedStrategyMarkets(st,cfg.id);
|
const occupied=occupiedStrategyMarkets(st,cfg.id);
|
||||||
claimedMarkets.forEach(id=>occupied.add(id));
|
claimedMarkets.forEach(id=>occupied.add(id));
|
||||||
openPositions(p,cfg,cfg.rank(cycleSuggestions),focus,decision,occupied,peerMarketStats(st,cfg.id)).forEach(id=>claimedMarkets.add(id));
|
openPositions(p,cfg,cfg.rank(cycleSuggestions),focus,decision,occupied,peerMarketStats(st,cfg.id)).forEach(id=>claimedMarkets.add(id));
|
||||||
if(cfg.id==="value"&&runMode==="live"&&entriesAllowed){
|
if(runMode==="live"&&entriesAllowed){
|
||||||
const staged=stageMakerQuotes(p,[...makerAuditCandidates,...analysisMarkets]);
|
const agentIndex=AGENTS.findIndex(agent=>agent.id===cfg.id),makerActivity=makerActivityByAgent[cfg.id];
|
||||||
|
const staged=stageMakerQuotes(p,[...makerAuditCandidates,...analysisMarkets],{profile:sharedMakerProfile,agentIndex,agentCount:AGENTS.length,
|
||||||
|
globalMarketIds:globalMakerMarkets,globalEventKeys:globalMakerEvents});
|
||||||
p.lastDecision=Object.assign({},p.lastDecision,{makerQuotes:p.maker_quotes.length,makerCandidates:staged.candidates,makerStaged:staged.staged,
|
p.lastDecision=Object.assign({},p.lastDecision,{makerQuotes:p.maker_quotes.length,makerCandidates:staged.candidates,makerStaged:staged.staged,
|
||||||
makerShadowStaged:staged.shadowStaged,makerCapitalStaged:staged.capitalStaged,makerShadowActive:staged.shadowActive,makerCapitalActive:staged.capitalActive,
|
makerShadowStaged:staged.shadowStaged,makerCapitalStaged:staged.capitalStaged,makerShadowActive:staged.shadowActive,makerCapitalActive:staged.capitalActive,
|
||||||
makerFills:makerActivity.fills,makerLocked:makerActivity.locked,makerExpired:makerActivity.expired,makerHistoryTouches:makerActivity.historyTouches,
|
makerFills:makerActivity.fills,makerLocked:makerActivity.locked,makerExpired:makerActivity.expired,makerHistoryTouches:makerActivity.historyTouches,
|
||||||
makerShadowCompleted:makerActivity.shadowCompleted,
|
makerShadowCompleted:makerActivity.shadowCompleted,
|
||||||
makerReserved:staged.reserved,makerProfile:makerActivity.profile});
|
makerReserved:staged.reserved,makerProfile:sharedMakerProfile});
|
||||||
if(staged.staged||makerActivity.fills||makerActivity.locked||makerActivity.expired){
|
if(staged.staged||makerActivity.fills||makerActivity.locked||makerActivity.expired){
|
||||||
p.lastDecision.allocationStatus=`${p.lastDecision.allocationStatus||""} Reward-book learner: ${staged.shadowActive} zero-capital shadow observation${staged.shadowActive===1?"":"s"}, ${staged.capitalActive} evidence-promoted capital quote${staged.capitalActive===1?"":"s"}, ${makerActivity.shadowCompleted||0} shadow outcome${makerActivity.shadowCompleted===1?"":"s"} graded this cycle; displayed reward estimates are never credited as P&L.`.trim();
|
p.lastDecision.allocationStatus=`${p.lastDecision.allocationStatus||""} Reward-book learner: ${staged.shadowActive} zero-capital shadow observation${staged.shadowActive===1?"":"s"}, ${staged.capitalActive} evidence-promoted capital quote${staged.capitalActive===1?"":"s"}, ${makerActivity.shadowCompleted||0} shadow outcome${makerActivity.shadowCompleted===1?"":"s"} graded this cycle; displayed reward estimates are never credited as P&L.`.trim();
|
||||||
}
|
}
|
||||||
@@ -4915,10 +4948,12 @@ window.PMA_ENGINE_DIAGNOSTICS=Object.freeze({
|
|||||||
onePendingObservationPerMarketSide:true,oldestPendingEvidenceFirst:true,coverageAwareObservationSampling:true,uncertaintyGatedCalibration:true,
|
onePendingObservationPerMarketSide:true,oldestPendingEvidenceFirst:true,coverageAwareObservationSampling:true,uncertaintyGatedCalibration:true,
|
||||||
directionalSignalsRequirePromotion:true,liveOnlyCompleteBundles:true,negativeRiskBundleSides:["YES","NO"],negativeRiskEventScanLimit:NEG_RISK_EVENT_SCAN_LIMIT,
|
directionalSignalsRequirePromotion:true,liveOnlyCompleteBundles:true,negativeRiskBundleSides:["YES","NO"],negativeRiskEventScanLimit:NEG_RISK_EVENT_SCAN_LIMIT,
|
||||||
negativeRiskMinimumNetReturnPct:NEG_RISK_MIN_NET_RETURN*100,
|
negativeRiskMinimumNetReturnPct:NEG_RISK_MIN_NET_RETURN*100,
|
||||||
pairedMakerQuotes:"reward-book-audited-shadow-until-promoted",makerShadowHorizonHours:MAKER_SHADOW_HORIZON_HOURS,makerLegacyQuoteExpiryHours:MAKER_QUOTE_EXPIRY_HOURS,makerMinimumLockMarginPct:MAKER_MIN_LOCK_MARGIN*100,
|
pairedMakerQuotes:"reward-book-audited-shadow-until-promoted",makerStrategyVersion:MAKER_STRATEGY_VERSION,
|
||||||
|
makerShadowHorizonHours:MAKER_SHADOW_HORIZON_HOURS,makerLegacyQuoteExpiryHours:MAKER_QUOTE_EXPIRY_HOURS,makerMinimumLockMarginPct:MAKER_MIN_LOCK_MARGIN*100,
|
||||||
makerTouchSource:"Polymarket CLOB batch price history",makerOutcomeLimit:MAKER_OUTCOME_LIMIT,makerMinimumCohortAttempts:MAKER_MIN_COHORT_ATTEMPTS,
|
makerTouchSource:"Polymarket CLOB batch price history",makerOutcomeLimit:MAKER_OUTCOME_LIMIT,makerMinimumCohortAttempts:MAKER_MIN_COHORT_ATTEMPTS,
|
||||||
makerMinimumPromotionLocks:MAKER_MIN_PROMOTION_LOCKS,makerMaximumLearningMultiplier:1.25,makerCapitalExplorationPct:0,hypotheticalRewardsCredited:false,
|
makerMinimumPromotionLocks:MAKER_MIN_PROMOTION_LOCKS,makerMaximumLearningMultiplier:1.25,makerCapitalExplorationPct:0,hypotheticalRewardsCredited:false,
|
||||||
makerCompetitionSource:"shared public CLOB order-book audit",makerPromotionCohorts:["category","spread","reward-yield"],makerRewardPayoutMinimum:1,
|
makerCompetitionSource:"shared public CLOB order-book audit",makerResearchSharedAcrossAgents:true,makerMarketsPartitionedAcrossAgents:true,
|
||||||
|
makerHistoryRequestsBatched:true,makerPromotionCohorts:["category","spread","reward-yield"],makerRewardPayoutMinimum:1,
|
||||||
emotionCanIncreaseSize:false,dualPromotionPeerBoostPct:5,
|
emotionCanIncreaseSize:false,dualPromotionPeerBoostPct:5,
|
||||||
historicalPrior:"All tested directional, sports-favorite, and corrected settlement-calibration rules failed clean validation and remain observation-only; live-priced complete negative-risk bundles may trade; paired maker quotes require a public-book reward audit and remain zero-capital observations until current category, spread, and reward-yield cohorts independently promote"}),
|
historicalPrior:"All tested directional, sports-favorite, and corrected settlement-calibration rules failed clean validation and remain observation-only; live-priced complete negative-risk bundles may trade; paired maker quotes require a public-book reward audit and remain zero-capital observations until current category, spread, and reward-yield cohorts independently promote"}),
|
||||||
});
|
});
|
||||||
@@ -5103,11 +5138,11 @@ function runEngineSelfTest(){
|
|||||||
makerQuote.created_at=hoursAgo(1);
|
makerQuote.created_at=hoursAgo(1);
|
||||||
const makerFirst=manageMakerQuotes(makerBook,{"maker-test":market(Object.assign({},makerMarket,{yes_price:0.39,no_price:0.61,best_bid:0.38,best_ask:0.40}))},{executeTrades:true});
|
const makerFirst=manageMakerQuotes(makerBook,{"maker-test":market(Object.assign({},makerMarket,{yes_price:0.39,no_price:0.61,best_bid:0.38,best_ask:0.40}))},{executeTrades:true});
|
||||||
const makerSingleFill=makerBook.positions.length===1&&makerBook.positions[0].side==="YES"&&makerBook.maker_quotes.length===1;
|
const makerSingleFill=makerBook.positions.length===1&&makerBook.positions[0].side==="YES"&&makerBook.maker_quotes.length===1;
|
||||||
const legacyUntouchedBook=defaultPortfolio();stageMakerQuotes(legacyUntouchedBook,[makerMarket]);legacyUntouchedBook.maker_quotes[0].shadow_only=false;legacyUntouchedBook.maker_quotes[0].strategy_version=PREVIOUS_STRATEGY_VERSION;
|
const legacyUntouchedBook=defaultPortfolio();stageMakerQuotes(legacyUntouchedBook,[makerMarket]);legacyUntouchedBook.maker_quotes[0].shadow_only=false;legacyUntouchedBook.maker_quotes[0].maker_strategy_version=1;
|
||||||
const legacyUntouched=manageMakerQuotes(legacyUntouchedBook,{"maker-test":makerMarket},{executeTrades:true});
|
const legacyUntouched=manageMakerQuotes(legacyUntouchedBook,{"maker-test":makerMarket},{executeTrades:true});
|
||||||
const legacyOneLegBook=defaultPortfolio();stageMakerQuotes(legacyOneLegBook,[makerMarket]);legacyOneLegBook.maker_quotes[0].shadow_only=false;legacyOneLegBook.maker_quotes[0].created_at=hoursAgo(1);
|
const legacyOneLegBook=defaultPortfolio();stageMakerQuotes(legacyOneLegBook,[makerMarket]);legacyOneLegBook.maker_quotes[0].shadow_only=false;legacyOneLegBook.maker_quotes[0].created_at=hoursAgo(1);
|
||||||
manageMakerQuotes(legacyOneLegBook,{"maker-test":market(Object.assign({},makerMarket,{yes_price:0.39,no_price:0.61,best_bid:0.38,best_ask:0.40}))},{executeTrades:true});
|
manageMakerQuotes(legacyOneLegBook,{"maker-test":market(Object.assign({},makerMarket,{yes_price:0.39,no_price:0.61,best_bid:0.38,best_ask:0.40}))},{executeTrades:true});
|
||||||
legacyOneLegBook.maker_quotes[0].strategy_version=PREVIOUS_STRATEGY_VERSION;legacyOneLegBook.positions[0].strategy_version=PREVIOUS_STRATEGY_VERSION;
|
legacyOneLegBook.maker_quotes[0].maker_strategy_version=1;legacyOneLegBook.positions[0].strategy_version=PREVIOUS_STRATEGY_VERSION;
|
||||||
const legacyOneLeg=manageMakerQuotes(legacyOneLegBook,{"maker-test":market(Object.assign({},makerMarket,{yes_price:0.36,no_price:0.64,best_bid:0.35,best_ask:0.37}))},{executeTrades:true});
|
const legacyOneLeg=manageMakerQuotes(legacyOneLegBook,{"maker-test":market(Object.assign({},makerMarket,{yes_price:0.36,no_price:0.64,best_bid:0.35,best_ask:0.37}))},{executeTrades:true});
|
||||||
const makerSecond=manageMakerQuotes(makerBook,{"maker-test":market(Object.assign({},makerMarket,{yes_price:0.60,no_price:0.40,best_bid:0.57,best_ask:0.61}))},{executeTrades:true});
|
const makerSecond=manageMakerQuotes(makerBook,{"maker-test":market(Object.assign({},makerMarket,{yes_price:0.60,no_price:0.40,best_bid:0.57,best_ask:0.61}))},{executeTrades:true});
|
||||||
const makerExpectedProfit=+(makerQuote.units*(1-Number(makerQuote.yes_fill_price)-Number(makerQuote.no_fill_price))).toFixed(2);
|
const makerExpectedProfit=+(makerQuote.units*(1-Number(makerQuote.yes_fill_price)-Number(makerQuote.no_fill_price))).toFixed(2);
|
||||||
@@ -5135,8 +5170,8 @@ function runEngineSelfTest(){
|
|||||||
const unfilledExpiry=manageMakerQuotes(unfilledBook,{"maker-test":touchMarket},{executeTrades:true});
|
const unfilledExpiry=manageMakerQuotes(unfilledBook,{"maker-test":touchMarket},{executeTrades:true});
|
||||||
const profitableMakerBook=defaultPortfolio(),losingMakerBook=defaultPortfolio();
|
const profitableMakerBook=defaultPortfolio(),losingMakerBook=defaultPortfolio();
|
||||||
for(let i=0;i<24;i++){
|
for(let i=0;i<24;i++){
|
||||||
profitableMakerBook.maker_outcomes.push({quote_id:`profit-${i}`,market_id:`profit-${i}`,event_key:`profit-event-${i}`,category:"Politics",spread:0.04,spread_band:"medium",status:"locked",pnl:4,reserved_capital:100,shadow_only:true,strategy_version:SUGGESTION_ENGINE_VERSION});
|
profitableMakerBook.maker_outcomes.push({quote_id:`profit-${i}`,market_id:`profit-${i}`,event_key:`profit-event-${i}`,category:"Politics",spread:0.04,spread_band:"medium",status:"locked",pnl:4,reserved_capital:100,shadow_only:true,strategy_version:SUGGESTION_ENGINE_VERSION,maker_strategy_version:MAKER_STRATEGY_VERSION});
|
||||||
losingMakerBook.maker_outcomes.push({quote_id:`loss-${i}`,market_id:`loss-${i}`,event_key:`loss-event-${i}`,category:"Politics",spread:0.04,spread_band:"medium",status:"single-exit",pnl:-4,reserved_capital:100,shadow_only:true,strategy_version:SUGGESTION_ENGINE_VERSION});
|
losingMakerBook.maker_outcomes.push({quote_id:`loss-${i}`,market_id:`loss-${i}`,event_key:`loss-event-${i}`,category:"Politics",spread:0.04,spread_band:"medium",status:"single-exit",pnl:-4,reserved_capital:100,shadow_only:true,strategy_version:SUGGESTION_ENGINE_VERSION,maker_strategy_version:MAKER_STRATEGY_VERSION});
|
||||||
}
|
}
|
||||||
const profitableCandidate=makerCandidateLearning(makerPairCandidate(makerMarket),buildMakerProfile(profitableMakerBook));
|
const profitableCandidate=makerCandidateLearning(makerPairCandidate(makerMarket),buildMakerProfile(profitableMakerBook));
|
||||||
let blockedMarket=null,explorationMarket=null;
|
let blockedMarket=null,explorationMarket=null;
|
||||||
@@ -5153,6 +5188,15 @@ function runEngineSelfTest(){
|
|||||||
recordMakerOutcome(makerDedupeBook,dedupeQuote,"unfilled",0,0);recordMakerOutcome(makerDedupeBook,dedupeQuote,"unfilled",0,0);
|
recordMakerOutcome(makerDedupeBook,dedupeQuote,"unfilled",0,0);recordMakerOutcome(makerDedupeBook,dedupeQuote,"unfilled",0,0);
|
||||||
const makerCompactionBook=defaultPortfolio();makerCompactionBook.maker_quotes=[Object.assign({},makerQuote)];makerCompactionBook.maker_outcomes=[...profitableMakerBook.maker_outcomes];
|
const makerCompactionBook=defaultPortfolio();makerCompactionBook.maker_quotes=[Object.assign({},makerQuote)];makerCompactionBook.maker_outcomes=[...profitableMakerBook.maker_outcomes];
|
||||||
const compactedMakerBook=compactPortfolioForSync(makerCompactionBook);
|
const compactedMakerBook=compactPortfolioForSync(makerCompactionBook);
|
||||||
|
const sharedMakerState=defaultState();sharedMakerState.agents.value.maker_outcomes=[profitableMakerBook.maker_outcomes[0]];
|
||||||
|
sharedMakerState.agents.momentum.maker_outcomes=[profitableMakerBook.maker_outcomes[1]];
|
||||||
|
const sharedMakerFixture=buildSharedMakerProfile(sharedMakerState);
|
||||||
|
const distributedMarkets=Array.from({length:4},(_,index)=>Object.assign({},makerMarket,{id:`distributed-maker-${index}`,event:`Distributed event ${index}`}));
|
||||||
|
const distributedIds=new Set(),distributedEvents=new Set(),distributedBookA=defaultPortfolio(),distributedBookB=defaultPortfolio();
|
||||||
|
stageMakerQuotes(distributedBookA,distributedMarkets,{agentIndex:0,agentCount:2,globalMarketIds:distributedIds,globalEventKeys:distributedEvents});
|
||||||
|
stageMakerQuotes(distributedBookB,distributedMarkets,{agentIndex:1,agentCount:2,globalMarketIds:distributedIds,globalEventKeys:distributedEvents});
|
||||||
|
const distributedA=new Set(distributedBookA.maker_quotes.map(quote=>quote.market_id));
|
||||||
|
const distributedB=new Set(distributedBookB.maker_quotes.map(quote=>quote.market_id));
|
||||||
const bundleSuggestion=negativeRiskBundleSuggestion({id:"bundle-test",title:"Three-way result",slug:"bundle-test",negRisk:true,enableNegRisk:true,tags:[{slug:"sports",label:"Sports"}],markets:[
|
const bundleSuggestion=negativeRiskBundleSuggestion({id:"bundle-test",title:"Three-way result",slug:"bundle-test",negRisk:true,enableNegRisk:true,tags:[{slug:"sports",label:"Sports"}],markets:[
|
||||||
{id:"bundle-a",question:"A wins",outcomes:'["Yes","No"]',outcomePrices:'["0.415","0.585"]',clobTokenIds:'["a-yes","a-no"]',bestBid:0.41,bestAsk:0.42,liquidityNum:30000,volumeNum:100000,volume24hr:10000,acceptingOrders:true},
|
{id:"bundle-a",question:"A wins",outcomes:'["Yes","No"]',outcomePrices:'["0.415","0.585"]',clobTokenIds:'["a-yes","a-no"]',bestBid:0.41,bestAsk:0.42,liquidityNum:30000,volumeNum:100000,volume24hr:10000,acceptingOrders:true},
|
||||||
{id:"bundle-b",question:"B wins",outcomes:'["Yes","No"]',outcomePrices:'["0.315","0.685"]',clobTokenIds:'["b-yes","b-no"]',bestBid:0.31,bestAsk:0.32,liquidityNum:28000,volumeNum:100000,volume24hr:10000,acceptingOrders:true},
|
{id:"bundle-b",question:"B wins",outcomes:'["Yes","No"]',outcomePrices:'["0.315","0.685"]',clobTokenIds:'["b-yes","b-no"]',bestBid:0.31,bestAsk:0.32,liquidityNum:28000,volumeNum:100000,volume24hr:10000,acceptingOrders:true},
|
||||||
@@ -5357,12 +5401,15 @@ function runEngineSelfTest(){
|
|||||||
repricedQuoteRejectsEarlierTouch:preRepriceIgnored,
|
repricedQuoteRejectsEarlierTouch:preRepriceIgnored,
|
||||||
repricedQuoteAcceptsLaterTouch:postRepriceTouch.fills===1&&postRepriceTouch.locked===1&&repriceBook.maker_quotes.length===0,
|
repricedQuoteAcceptsLaterTouch:postRepriceTouch.fills===1&&postRepriceTouch.locked===1&&repriceBook.maker_quotes.length===0,
|
||||||
profitableCohortScalesWithinCap:profitableCandidate&&profitableCandidate.maker_learning_state==="promoted"&&profitableCandidate.maker_capital_enabled&&profitableCandidate.units>makerQuote.units&&profitableCandidate.units<=makerQuote.units*1.25,
|
profitableCohortScalesWithinCap:profitableCandidate&&profitableCandidate.maker_learning_state==="promoted"&&profitableCandidate.maker_capital_enabled&&profitableCandidate.units>makerQuote.units&&profitableCandidate.units<=makerQuote.units*1.25,
|
||||||
|
promotionRequiresDistinctLockedEvents:buildMakerProfile(profitableMakerBook).global.current_locked_events===24,
|
||||||
losingCohortRemainsShadowOnly:blockedMakerCandidate&&blockedMakerCandidate.maker_learning_state==="shadow-rejected"&&!blockedMakerCandidate.maker_capital_enabled,
|
losingCohortRemainsShadowOnly:blockedMakerCandidate&&blockedMakerCandidate.maker_learning_state==="shadow-rejected"&&!blockedMakerCandidate.maker_capital_enabled,
|
||||||
noRealMoneyExplorationLane:explorationMakerCandidate&&explorationMakerCandidate.maker_learning_state==="shadow-rejected"&&!explorationMakerCandidate.maker_capital_enabled,
|
noRealMoneyExplorationLane:explorationMakerCandidate&&explorationMakerCandidate.maker_learning_state==="shadow-rejected"&&!explorationMakerCandidate.maker_capital_enabled,
|
||||||
shadowSingleTouchChangesNoCash:shadowFinished.shadowCompleted===1&&shadowBook.cash===shadowCashBefore&&shadowBook.positions.length===0&&shadowBook.maker_quotes.length===0,
|
shadowSingleTouchChangesNoCash:shadowFinished.shadowCompleted===1&&shadowBook.cash===shadowCashBefore&&shadowBook.positions.length===0&&shadowBook.maker_quotes.length===0,
|
||||||
shadowSingleTouchRecordsAdverseOutcome:shadowBook.maker_outcomes.length===1&&shadowBook.maker_outcomes[0].shadow_only&&shadowBook.maker_outcomes[0].status==="single-exit"&&shadowBook.maker_outcomes[0].pnl<0,
|
shadowSingleTouchRecordsAdverseOutcome:shadowBook.maker_outcomes.length===1&&shadowBook.maker_outcomes[0].shadow_only&&shadowBook.maker_outcomes[0].status==="single-exit"&&shadowBook.maker_outcomes[0].pnl<0,
|
||||||
outcomesAreDeduplicated:makerDedupeBook.maker_outcomes.length===1,
|
outcomesAreDeduplicated:makerDedupeBook.maker_outcomes.length===1,
|
||||||
makerLearningSurvivesCompaction:compactedMakerBook.maker_quotes.length===1&&compactedMakerBook.maker_outcomes.length===24,
|
makerLearningSurvivesCompaction:compactedMakerBook.maker_quotes.length===1&&compactedMakerBook.maker_outcomes.length===24,
|
||||||
|
sharedLedgerCombinesAgentEvidence:sharedMakerFixture.global.current_events===2&&sharedMakerFixture.global.current_locked_events===2,
|
||||||
|
agentsReceiveDisjointShadowMarkets:distributedA.size===2&&distributedB.size===2&&[...distributedA].every(id=>!distributedB.has(id)),
|
||||||
noHypotheticalRewardCredited:makerStage.staged===1&&Math.abs(makerBook.cash-(10000+makerExpectedProfit))<=0.02,
|
noHypotheticalRewardCredited:makerStage.staged===1&&Math.abs(makerBook.cash-(10000+makerExpectedProfit))<=0.02,
|
||||||
rejectsUnrewardedMarket:makerPairCandidate(Object.assign({},makerMarket,{rewards_daily_rate:0}))===null,
|
rejectsUnrewardedMarket:makerPairCandidate(Object.assign({},makerMarket,{rewards_daily_rate:0}))===null,
|
||||||
rejectsVolatileMarket:makerPairCandidate(Object.assign({},makerMarket,{price_change_1d:0.12}))===null,
|
rejectsVolatileMarket:makerPairCandidate(Object.assign({},makerMarket,{price_change_1d:0.12}))===null,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
"evaluate:adaptive": "node scripts/evaluate-adaptive.mjs",
|
"evaluate:adaptive": "node scripts/evaluate-adaptive.mjs",
|
||||||
"evaluate:liquidity": "node scripts/evaluate-liquidity.mjs",
|
"evaluate:liquidity": "node scripts/evaluate-liquidity.mjs",
|
||||||
"evaluate:maker": "node scripts/evaluate-maker.mjs",
|
"evaluate:maker": "node scripts/evaluate-maker.mjs",
|
||||||
|
"evaluate:reward-maker": "node scripts/evaluate-reward-assisted-maker.mjs",
|
||||||
"evaluate:signals": "node scripts/evaluate-signals.mjs",
|
"evaluate:signals": "node scripts/evaluate-signals.mjs",
|
||||||
"evaluate:settlements": "node scripts/evaluate-settlements.mjs",
|
"evaluate:settlements": "node scripts/evaluate-settlements.mjs",
|
||||||
"evaluate:settlement-calibration": "node scripts/evaluate-settlement-calibration.mjs",
|
"evaluate:settlement-calibration": "node scripts/evaluate-settlement-calibration.mjs",
|
||||||
|
|||||||
@@ -0,0 +1,167 @@
|
|||||||
|
import { auditLiquidity } from "../lib/liquidity-audit.js";
|
||||||
|
|
||||||
|
const CLOB = "https://clob.polymarket.com";
|
||||||
|
const MARKET_LIMIT = Math.max(10, Math.min(250, Number(process.env.REWARD_MAKER_MARKETS || 100)));
|
||||||
|
const HISTORY_DAYS = Math.max(14, Math.min(30, Number(process.env.REWARD_MAKER_HISTORY_DAYS || 30)));
|
||||||
|
const HORIZON_HOURS = Math.max(1, Math.min(24, Number(process.env.REWARD_MAKER_HORIZON_HOURS || 3)));
|
||||||
|
const EXIT_COST = Math.max(0, Math.min(0.05, Number(process.env.REWARD_MAKER_EXIT_COST_CENTS || 1) / 100));
|
||||||
|
const SUMMARY_ONLY = process.env.REWARD_MAKER_SUMMARY === "1";
|
||||||
|
const HOUR = 3600;
|
||||||
|
|
||||||
|
async function fetchJson(url, options = {}, attempts = 4) {
|
||||||
|
let lastError;
|
||||||
|
for (let attempt = 0; attempt < attempts; attempt++) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, { ...options, signal: AbortSignal.timeout(30000),
|
||||||
|
headers: { accept: "application/json", ...(options.headers || {}) } });
|
||||||
|
if (response.ok) return response.json();
|
||||||
|
lastError = new Error(`${response.status} ${response.statusText}`);
|
||||||
|
if (response.status !== 429 && response.status < 500) break;
|
||||||
|
} catch (error) { lastError = error; }
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 600 * (attempt + 1)));
|
||||||
|
}
|
||||||
|
throw lastError || new Error("request failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
function atOrBefore(points, target) {
|
||||||
|
let lo = 0, hi = points.length - 1, answer = null;
|
||||||
|
while (lo <= hi) {
|
||||||
|
const mid = (lo + hi) >> 1;
|
||||||
|
if (points[mid].t <= target) { answer = points[mid]; lo = mid + 1; }
|
||||||
|
else hi = mid - 1;
|
||||||
|
}
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchHistories(candidates) {
|
||||||
|
const tokenIds = [...new Set(candidates.flatMap((candidate) => candidate.clob_token_ids || []).map(String))];
|
||||||
|
const history = {};
|
||||||
|
for (let index = 0; index < tokenIds.length; index += 20) {
|
||||||
|
const batch = tokenIds.slice(index, index + 20);
|
||||||
|
const response = await fetchJson(`${CLOB}/batch-prices-history`, {
|
||||||
|
method: "POST", headers: { "content-type": "application/json" },
|
||||||
|
body: JSON.stringify({ markets: batch, interval: "1m", fidelity: 60 }),
|
||||||
|
});
|
||||||
|
Object.entries(response.history || {}).forEach(([token, points]) => {
|
||||||
|
history[token] = (points || []).map((point) => ({ t: Number(point.t), p: Number(point.p) }))
|
||||||
|
.filter((point) => Number.isFinite(point.t) && Number.isFinite(point.p)).sort((a, b) => a.t - b.t);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return history;
|
||||||
|
}
|
||||||
|
|
||||||
|
function firstTouch(points, start, end, quote) {
|
||||||
|
return points.find((point) => point.t > start && point.t <= end && point.p <= quote) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function observations(candidate, yesPoints, noPoints) {
|
||||||
|
const rows = [], seen = new Set(), cutoff = Date.now() / 1000 - HISTORY_DAYS * 24 * HOUR;
|
||||||
|
const yesDistance = Math.max(Number(candidate.tick_size || 0.01), Number(candidate.yes_adjusted_mid) - Number(candidate.yes_quote));
|
||||||
|
const noDistance = Math.max(Number(candidate.tick_size || 0.01), Number(candidate.no_adjusted_mid) - Number(candidate.no_quote));
|
||||||
|
const size = Number(candidate.reward_min_size), endBuffer = HORIZON_HOURS * HOUR;
|
||||||
|
for (const yes of yesPoints) {
|
||||||
|
if (yes.t < cutoff || yes.t + endBuffer > Date.now() / 1000) continue;
|
||||||
|
const bucket = Math.floor(yes.t / (24 * HOUR));
|
||||||
|
if (seen.has(bucket)) continue;
|
||||||
|
const no = atOrBefore(noPoints, yes.t + HOUR);
|
||||||
|
if (!no || Math.abs(no.t - yes.t) > 2 * HOUR || yes.p <= 0.04 || yes.p >= 0.96 || no.p <= 0.04 || no.p >= 0.96) continue;
|
||||||
|
const priorYes = yesPoints.filter((point) => point.t >= yes.t - 24 * HOUR && point.t <= yes.t);
|
||||||
|
if (priorYes.length < 12 || Math.max(...priorYes.map((point) => point.p)) - Math.min(...priorYes.map((point) => point.p)) > 0.08) continue;
|
||||||
|
const yesQuote = yes.p - yesDistance, noQuote = no.p - noDistance, pairedCost = yesQuote + noQuote;
|
||||||
|
if (yesQuote < 0.02 || noQuote < 0.02 || pairedCost >= 0.995) continue;
|
||||||
|
const horizonEnd = yes.t + endBuffer;
|
||||||
|
const yesTouch = firstTouch(yesPoints, yes.t, horizonEnd, yesQuote);
|
||||||
|
const noTouch = firstTouch(noPoints, yes.t, horizonEnd, noQuote);
|
||||||
|
const endYes = atOrBefore(yesPoints, horizonEnd), endNo = atOrBefore(noPoints, horizonEnd);
|
||||||
|
if (!endYes || !endNo || horizonEnd - Math.min(endYes.t, endNo.t) > 2 * HOUR) continue;
|
||||||
|
seen.add(bucket);
|
||||||
|
let pnl = 0, status = "unfilled";
|
||||||
|
if (yesTouch && noTouch) { pnl = (1 - pairedCost) * size; status = "locked"; }
|
||||||
|
else if (yesTouch) { pnl = (endYes.p - yesQuote - EXIT_COST) * size; status = "single-exit"; }
|
||||||
|
else if (noTouch) { pnl = (endNo.p - noQuote - EXIT_COST) * size; status = "single-exit"; }
|
||||||
|
const firstFillAt = Math.min(yesTouch?.t || Infinity, noTouch?.t || Infinity);
|
||||||
|
const activeHours = Number.isFinite(firstFillAt) ? Math.max(0, Math.min(HORIZON_HOURS, (firstFillAt - yes.t) / HOUR)) : HORIZON_HOURS;
|
||||||
|
rows.push({ observedAt: yes.t, pnl, status, activeHours, capital: pairedCost * size });
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
function summarize(rows, dailyReward, rewardHaircut) {
|
||||||
|
if (!rows.length) return { observations: 0, mean: 0, lower90: 0, pnl: 0, reward: 0, net: 0,
|
||||||
|
lockedRate: 0, adverseRate: 0, activeDays: 0, breakEvenRewardDaily: 0 };
|
||||||
|
const results = rows.map((row) => {
|
||||||
|
const reward = dailyReward * rewardHaircut * row.activeHours / 24;
|
||||||
|
return { ...row, reward, net: row.pnl + reward, netReturn: (row.pnl + reward) / Math.max(1, row.capital) };
|
||||||
|
});
|
||||||
|
const values = results.map((row) => row.netReturn), mean = values.reduce((sum, value) => sum + value, 0) / values.length;
|
||||||
|
const variance = values.length > 1 ? values.reduce((sum, value) => sum + (value - mean) ** 2, 0) / (values.length - 1) : 0;
|
||||||
|
const lower90 = mean - 1.645 * Math.sqrt(variance / values.length);
|
||||||
|
const pnl = results.reduce((sum, row) => sum + row.pnl, 0), activeDays = results.reduce((sum, row) => sum + row.activeHours / 24, 0);
|
||||||
|
return { observations: rows.length, mean, lower90, pnl,
|
||||||
|
reward: results.reduce((sum, row) => sum + row.reward, 0), net: results.reduce((sum, row) => sum + row.net, 0),
|
||||||
|
lockedRate: rows.filter((row) => row.status === "locked").length / rows.length,
|
||||||
|
adverseRate: rows.filter((row) => row.status === "single-exit").length / rows.length,
|
||||||
|
activeDays, breakEvenRewardDaily: Math.max(0, -pnl / Math.max(activeDays, 1e-9)) };
|
||||||
|
}
|
||||||
|
|
||||||
|
function split(rows) {
|
||||||
|
const ordered = [...rows].sort((a, b) => a.observedAt - b.observedAt);
|
||||||
|
return {
|
||||||
|
train: ordered.slice(0, Math.floor(ordered.length * 0.60)),
|
||||||
|
validation: ordered.slice(Math.floor(ordered.length * 0.60), Math.floor(ordered.length * 0.80)),
|
||||||
|
holdout: ordered.slice(Math.floor(ordered.length * 0.80)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const compact = (stats) => Object.fromEntries(Object.entries(stats).map(([key, value]) => [key, Number.isFinite(value) ? +value.toFixed(5) : value]));
|
||||||
|
const audit = await auditLiquidity({ marketLimit: MARKET_LIMIT, minHoursToEnd: 48 });
|
||||||
|
const histories = await fetchHistories(audit.candidates);
|
||||||
|
const candidates = audit.candidates.map((candidate) => {
|
||||||
|
const rows = observations(candidate, histories[String(candidate.clob_token_ids[0])] || [], histories[String(candidate.clob_token_ids[1])] || []);
|
||||||
|
const partitions = split(rows), floor = Number(candidate.estimated_reward_floor_daily || 0);
|
||||||
|
const stress = Object.fromEntries([0, 0.25, 0.5, 1].map((haircut) => [haircut, {
|
||||||
|
all: compact(summarize(rows, floor, haircut)), train: compact(summarize(partitions.train, floor, haircut)),
|
||||||
|
validation: compact(summarize(partitions.validation, floor, haircut)), holdout: compact(summarize(partitions.holdout, floor, haircut)),
|
||||||
|
}]));
|
||||||
|
const conservative = stress[0.25], passesStress = rows.length >= 20 && partitions.validation.length >= 4 && partitions.holdout.length >= 4
|
||||||
|
&& conservative.all.lower90 > 0 && conservative.train.mean > 0 && conservative.validation.mean > 0 && conservative.holdout.mean > 0;
|
||||||
|
return { market_id: candidate.market_id, question: candidate.question, url: candidate.url, category: candidate.category,
|
||||||
|
required_capital: candidate.required_capital, estimated_reward_floor_daily: floor,
|
||||||
|
reward_share_floor: candidate.reward_share_floor, maximum_one_leg_loss: candidate.maximum_one_leg_loss,
|
||||||
|
observations: rows.length, passes_stressed_path_gate: passesStress,
|
||||||
|
reward_floor_to_break_even: conservative.all.breakEvenRewardDaily > 0 ? floor / conservative.all.breakEvenRewardDaily : null,
|
||||||
|
stress };
|
||||||
|
}).sort((a, b) => Number(b.passes_stressed_path_gate) - Number(a.passes_stressed_path_gate)
|
||||||
|
|| Number(b.reward_floor_to_break_even || 0) - Number(a.reward_floor_to_break_even || 0));
|
||||||
|
|
||||||
|
const observedCandidates = candidates.filter((candidate) => candidate.observations > 0);
|
||||||
|
const outputCandidates = SUMMARY_ONLY ? observedCandidates.slice(0, 12).map((candidate) => ({
|
||||||
|
market_id: candidate.market_id,
|
||||||
|
question: candidate.question,
|
||||||
|
estimated_reward_floor_daily: candidate.estimated_reward_floor_daily,
|
||||||
|
observations: candidate.observations,
|
||||||
|
passes_stressed_path_gate: candidate.passes_stressed_path_gate,
|
||||||
|
reward_floor_to_break_even: candidate.reward_floor_to_break_even,
|
||||||
|
no_reward: {
|
||||||
|
pnl: candidate.stress[0].all.pnl,
|
||||||
|
lower90: candidate.stress[0].all.lower90,
|
||||||
|
adverseRate: candidate.stress[0].all.adverseRate,
|
||||||
|
},
|
||||||
|
reward_at_25pct: {
|
||||||
|
net: candidate.stress[0.25].all.net,
|
||||||
|
lower90: candidate.stress[0.25].all.lower90,
|
||||||
|
trainMean: candidate.stress[0.25].train.mean,
|
||||||
|
validationMean: candidate.stress[0.25].validation.mean,
|
||||||
|
holdoutMean: candidate.stress[0.25].holdout.mean,
|
||||||
|
},
|
||||||
|
})) : candidates;
|
||||||
|
|
||||||
|
console.log(JSON.stringify({ generatedAt: new Date().toISOString(), requestedRewardMarkets: MARKET_LIMIT,
|
||||||
|
auditedRewardMarkets: audit.reward_markets, shadowQualified: audit.shadow_qualified, evaluatedCandidates: candidates.length,
|
||||||
|
candidatesWithHistory: observedCandidates.length,
|
||||||
|
stressedPathPassed: candidates.filter((candidate) => candidate.passes_stressed_path_gate).length,
|
||||||
|
methodology: { historyDays: HISTORY_DAYS, horizonHours: HORIZON_HOURS, exitCostCents: EXIT_COST * 100,
|
||||||
|
rewardStress: "25% of the current public-book reward-share floor", rewardAccrual: "stops at the first simulated fill",
|
||||||
|
fillProxy: "hourly CLOB token price touches a resting bid", split: "60% / 20% / 20% chronological path stability",
|
||||||
|
limitation: "Repeated paths from a current market are not independent events and cannot authorize capital; this gate only prioritizes live shadow observations." },
|
||||||
|
candidates: outputCandidates }, null, 2));
|
||||||
Reference in New Issue
Block a user