diff --git a/api/state.js b/api/state.js
index 8a29034..2996358 100644
--- a/api/state.js
+++ b/api/state.js
@@ -51,7 +51,8 @@ function compactSuggestion(s) {
market_id: s.market_id, question: s.question, event: s.event, url: s.url, category: s.category,
clob_yes: s.clob_yes, clob_no: s.clob_no, yes_price: s.yes_price, no_price: s.no_price,
fair_value: s.fair_value, edge: s.edge, side: s.side, entry_price: s.entry_price,
- conviction: s.conviction, volume: s.volume, volume_24hr: s.volume_24hr,
+ conviction: s.conviction, volume: s.volume, volume_24hr: s.volume_24hr, liquidity: s.liquidity,
+ trade_ready: s.trade_ready,
days_to_resolution: s.days_to_resolution, drivers: s.drivers, rationale: s.rationale,
};
}
diff --git a/index.html b/index.html
index 4e4c262..1b52cb6 100644
--- a/index.html
+++ b/index.html
@@ -739,7 +739,7 @@ footer{margin-top:34px;padding-top:22px;border-top:1px solid var(--border);color
@@ -766,7 +766,7 @@ const EXIT_STALE_DAYS = 10;
const EXIT_RESOLUTION_DAYS = 2;
const AGENTS_KEY = "pma_agents_v2";
const SUG_KEY = "pma_suggestions_v5";
-const SUGGESTION_ENGINE_VERSION = 9;
+const SUGGESTION_ENGINE_VERSION = 10;
const FOCUS_KEY = "pma_focus_v1";
const VIEW_KEY = "pma_view_v1";
const PF_SORT_KEY = "pma_portfolio_sort_v1";
@@ -987,6 +987,9 @@ async function fetchMarketPrice(id){
const W={LIQ:0.28,MOM:0.22,MIS:0.34,TIME:0.16};
const MIN_VOLUME=18000;
const MIN_LIQUIDITY=1400;
+const MIN_SCOUT_VOLUME=1500;
+const MIN_SCOUT_LIQUIDITY=150;
+const MIN_SCOUT_EDGE=0.006;
const MIN_ENTRY_EDGE=0.032;
const MIN_ENTRY_DAYS=3.0;
const EDGE_SCALE=0.13;
@@ -1006,15 +1009,17 @@ function fairValue(m){const p=m.yes_price,mom=momentumSignal(m),liq=liquiditySig
return clamp(p+directional+liquidBump,0.03,0.97);}
function timingSignal(d){if(d==null)return 0.4;if(d<1)return 0.1;if(d<=3)return 0.5;if(d<=90)return 1.0-(d-3)/87.0*0.4;return 0.35;}
function analyzeMarket(m){
- if(m.volumeMIN_ENTRY_EDGE){side="YES";entry=p;rationale=`Model fair value ${pct(fair)} vs market ${pct(p)} — YES looks underpriced by ~${(edgeYes*100).toFixed(1)}c.`;}
- else if(edgeYes<-MIN_ENTRY_EDGE){side="NO";entry=m.no_price;rationale=`Model fair value ${pct(fair)} vs market ${pct(p)} — YES looks overpriced, so NO at ${pct(m.no_price)} has the edge.`;}
- else{side="HOLD";entry=p;rationale=`Price ${pct(p)} is close to model fair value ${pct(fair)}; no clear edge — watch only.`;}
+ const tradeReady=edge>=MIN_ENTRY_EDGE&&m.volume>=MIN_VOLUME&&m.liquidity>=MIN_LIQUIDITY;
+ let side=edgeYes>=0?"YES":"NO",entry=side==="YES"?p:m.no_price,rationale;
+ if(tradeReady&&edgeYes>0){rationale=`Trade-ready: model fair value ${pct(fair)} vs market ${pct(p)} — YES looks underpriced by ~${(edgeYes*100).toFixed(1)}c.`;}
+ else if(tradeReady&&edgeYes<0){rationale=`Trade-ready: model fair value ${pct(fair)} vs market ${pct(p)} — YES looks overpriced, so NO at ${pct(m.no_price)} has the edge.`;}
+ else{rationale=`Scout idea: model sees a smaller ${(edge*100).toFixed(1)}c gap, so agents can monitor it but need stricter edge/liquidity before buying.`;}
const drivers=[];
if(liq>0.6)drivers.push("deep/liquid market");
if(mom>0.6)drivers.push("strong fresh volume (24h surge)");
@@ -1024,7 +1029,8 @@ function analyzeMarket(m){
return {market_id:m.id,question:m.question,event:m.event,url:m.url,category:m.category,tags:m.tags,
clob_yes:(m.clob_token_ids||[])[0]||null,clob_no:(m.clob_token_ids||[])[1]||null,
yes_price:p,no_price:m.no_price,fair_value:+fair.toFixed(4),edge:+edgeYes.toFixed(4),
- side,entry_price:+entry.toFixed(4),conviction,volume:m.volume,volume_24hr:m.volume_24hr,
+ side,entry_price:+entry.toFixed(4),conviction,volume:m.volume,volume_24hr:m.volume_24hr,liquidity:m.liquidity,
+ trade_ready:tradeReady,
days_to_resolution:m.days_to_resolution!=null?+m.days_to_resolution.toFixed(1):null,drivers,rationale};
}
const pct=(x)=>Math.round(x*100)+"%";
@@ -1088,7 +1094,8 @@ function compactSuggestionForSync(s){
market_id:s.market_id,question:s.question,event:s.event,url:s.url,category:s.category,
clob_yes:s.clob_yes,clob_no:s.clob_no,yes_price:s.yes_price,no_price:s.no_price,
fair_value:s.fair_value,edge:s.edge,side:s.side,entry_price:s.entry_price,
- conviction:s.conviction,volume:s.volume,volume_24hr:s.volume_24hr,
+ conviction:s.conviction,volume:s.volume,volume_24hr:s.volume_24hr,liquidity:s.liquidity,
+ trade_ready:s.trade_ready,
days_to_resolution:s.days_to_resolution,drivers:s.drivers,rationale:s.rationale,
};
}
@@ -1531,10 +1538,10 @@ function openPositions(p,cfg,rankedSugs,focus,decision,avoidMarketIds,peerStats=
const d=decision||{minConv:cfg.minConv,maxNew:cfg.maxNew,maxFrac:cfg.maxFrac,reserve:0.05};
p.lastDecision=decision||null;
if((p.positions||[]).length>=MAX_STRATEGY_POSITIONS)return [];
- const cands=rankedSugs.map(s=>peerAdjustedSuggestion(s,peerStats)).filter(s=>(s.side==="YES"||s.side==="NO")&&s.peer_conviction>=d.minConv
+ const cands=rankedSugs.map(s=>peerAdjustedSuggestion(s,peerStats)).filter(s=>s.trade_ready&&(s.side==="YES"||s.side==="NO")&&s.peer_conviction>=d.minConv
&&s.conviction>=48&&s.entry_price>=0.08&&s.entry_price<=0.92
&&Math.abs(s.edge)>=MIN_ENTRY_EDGE&&(s.days_to_resolution==null||s.days_to_resolution>=MIN_ENTRY_DAYS)
- &&(s.volume_24hr>=500||s.conviction>=62)
+ &&s.volume>=MIN_VOLUME&&s.liquidity>=MIN_LIQUIDITY&&(s.volume_24hr>=500||s.conviction>=62)
&&!hasPosition(p,s.market_id)&&!hasStoppedToday(p,s.market_id)&&(focus==="All"||!focus||s.category===focus))
.sort((a,b)=>(b.peer_conviction-a.peer_conviction)||((b.peer_boost||0)-(a.peer_boost||0)));
const avoid=avoidMarketIds||new Set();
@@ -2294,7 +2301,7 @@ function renderSuggestions(){
${esc(s.event||"")}
-
BUY ${s.side}
+
${s.trade_ready?"BUY":"WATCH"} ${s.side}
@ ${Math.round(s.entry_price*100)}¢
${Math.round(s.conviction)}