Skip to content

Commit 61cc51b

Browse files
committed
Prioritize latest external signals
1 parent dac20b9 commit 61cc51b

4 files changed

Lines changed: 72 additions & 14 deletions

File tree

poly_strategy/external_signals.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ def polymarket_market_ids_from_external_signals(path: Path, limit: Optional[int]
7070
raise ValueError("limit must be at least 1")
7171
market_ids = []
7272
seen = set()
73-
for row in _read_external_signals(path):
73+
rows = list(_read_external_signals(path))
74+
iterable = reversed(rows) if limit is not None else rows
75+
for row in iterable:
7476
for leg in row.get("legs", []):
7577
if str(leg.get("venue") or "").lower() != "polymarket":
7678
continue

poly_strategy/watchlist.py

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -264,30 +264,34 @@ def _top_neg_risk_group_market_ids(
264264

265265

266266
def _external_signal_market_scores(path: Optional[Path], alias_to_market_id: Optional[dict] = None) -> Counter:
267-
scores = Counter()
267+
scores = {}
268268
if not path or not path.exists():
269-
return scores
269+
return Counter()
270270
alias_to_market_id = alias_to_market_id or {}
271+
rows = []
271272
with path.open() as handle:
272273
for raw_line in handle:
273274
line = raw_line.strip()
274275
if not line:
275276
continue
276277
try:
277-
row = json.loads(line)
278+
rows.append(json.loads(line))
278279
except json.JSONDecodeError:
279280
continue
280-
if row.get("type") != "external_signal":
281+
for index, row in enumerate(rows):
282+
signal_score = 100_000.0 + 10_000.0 * float(row.get("quoted_edge") or 0.0) + float(index)
283+
if row.get("type") != "external_signal":
284+
continue
285+
for leg in row.get("legs", []):
286+
if str(leg.get("venue") or "").lower() != "polymarket":
281287
continue
282-
signal_score = 100_000.0 + 10_000.0 * float(row.get("quoted_edge") or 0.0)
283-
for leg in row.get("legs", []):
284-
if str(leg.get("venue") or "").lower() != "polymarket":
285-
continue
286-
raw_market_id = str(leg.get("market_id") or "").strip()
287-
market_id = alias_to_market_id.get(raw_market_id, raw_market_id)
288-
if market_id:
289-
scores[market_id] += signal_score
290-
return scores
288+
raw_market_id = str(leg.get("market_id") or "").strip()
289+
market_id = alias_to_market_id.get(raw_market_id, raw_market_id)
290+
if market_id:
291+
current = scores.get(market_id)
292+
if current is None or signal_score > current:
293+
scores[market_id] = signal_score
294+
return Counter(scores)
291295

292296

293297
def _canonicalize_market_ids(market_ids: Iterable[str], alias_to_market_id: dict) -> set:

tests/test_external_signals.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,23 @@ def test_polymarket_market_ids_from_external_signals_dedupes_in_order(self):
213213

214214
self.assertEqual(market_ids, ["pm-1", "pm-2"])
215215

216+
def test_polymarket_market_ids_from_external_signals_limit_prefers_latest_unique_ids(self):
217+
rows = [
218+
{"type": "external_signal", "legs": [{"venue": "polymarket", "market_id": "old-1"}]},
219+
{"type": "external_signal", "legs": [{"venue": "polymarket", "market_id": "old-2"}]},
220+
{"type": "external_signal", "legs": [{"venue": "polymarket", "market_id": "new-1"}]},
221+
{"type": "external_signal", "legs": [{"venue": "polymarket", "market_id": "old-1"}]},
222+
{"type": "external_signal", "legs": [{"venue": "polymarket", "market_id": "new-2"}]},
223+
]
224+
225+
with tempfile.TemporaryDirectory() as tmp:
226+
path = Path(tmp) / "external.ndjson"
227+
path.write_text("\n".join(json.dumps(row) for row in rows) + "\n")
228+
229+
market_ids = polymarket_market_ids_from_external_signals(path, limit=3)
230+
231+
self.assertEqual(market_ids, ["new-2", "old-1", "new-1"])
232+
216233

217234
if __name__ == "__main__":
218235
unittest.main()

tests/test_watchlist.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,41 @@ def test_build_polymarket_watchlist_keeps_external_signals_under_cap(self):
204204
signal = next(row for row in rows if row["market_id"] == "signal")
205205
self.assertIn("external_signal", signal["priority_reasons"])
206206

207+
def test_build_polymarket_watchlist_cap_prefers_latest_external_signal(self):
208+
gamma_rows = [
209+
_gamma_row("old-signal", "", ["old-yes", "old-no"], "", liquidity=0, volume24hr=0),
210+
_gamma_row("new-signal", "", ["new-yes", "new-no"], "", liquidity=0, volume24hr=0),
211+
_gamma_row("top", "", ["top-yes", "top-no"], "", liquidity=1000, volume24hr=1000),
212+
]
213+
rules = {"mutually_exclusive": []}
214+
old_signal = {
215+
"type": "external_signal",
216+
"legs": [{"venue": "polymarket", "market_id": "old-signal"}],
217+
}
218+
new_signal = {
219+
"type": "external_signal",
220+
"legs": [{"venue": "polymarket", "market_id": "new-signal"}],
221+
}
222+
223+
with tempfile.TemporaryDirectory() as tmp:
224+
gamma_path = Path(tmp) / "gamma.ndjson"
225+
rules_path = Path(tmp) / "rules.json"
226+
signals_path = Path(tmp) / "signals.ndjson"
227+
gamma_path.write_text("\n".join(json.dumps(row) for row in gamma_rows) + "\n")
228+
rules_path.write_text(json.dumps(rules))
229+
signals_path.write_text("\n".join(json.dumps(row) for row in [old_signal, old_signal, old_signal, new_signal]) + "\n")
230+
231+
rows = build_polymarket_watchlist(
232+
gamma_path,
233+
rules_path,
234+
external_signals_path=signals_path,
235+
include_top_markets=1,
236+
max_markets=1,
237+
)
238+
239+
self.assertIn("new-signal", {row["market_id"] for row in rows})
240+
self.assertNotIn("old-signal", {row["market_id"] for row in rows})
241+
207242
def test_build_polymarket_watchlist_keeps_external_signal_neg_risk_group_atomic(self):
208243
gamma_rows = [
209244
_gamma_row("group-a", "group", ["ga-yes", "ga-no"], "1", liquidity=100, volume24hr=10),

0 commit comments

Comments
 (0)