create-polyclaw-agent 0.1.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +12 -3
- package/dist/index.js.map +1 -1
- package/dist/scaffold.js +352 -74
- package/dist/scaffold.js.map +1 -1
- package/package.json +7 -2
- package/src/index.ts +12 -3
- package/src/scaffold.ts +352 -74
package/dist/index.js
CHANGED
|
@@ -11,9 +11,18 @@ import { generateName, registerAgent, getLinkUrl } from './api.js';
|
|
|
11
11
|
import { scaffoldProject, nameToDir } from './scaffold.js';
|
|
12
12
|
const LOBSTER = '\u{1F99E}';
|
|
13
13
|
async function main() {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const BANNER = `
|
|
15
|
+
${chalk.red(` .-----.
|
|
16
|
+
_ | o o | _
|
|
17
|
+
| | | w | | |
|
|
18
|
+
|_| '-----' |_|`)}
|
|
19
|
+
|
|
20
|
+
${chalk.bold(` █▀█ █▀█ █ █ █ █▀▀ █ █▀█ █ █
|
|
21
|
+
█▀▀ █ █ █ █ █ █ █▀█ █ █ █
|
|
22
|
+
▀ ▀▀▀ ▀▀▀ ▀ ▀▀▀ ▀▀▀ ▀ ▀ ▀ ▀`)}
|
|
23
|
+
${chalk.dim(' AI-Powered Prediction Market Agent')}
|
|
24
|
+
`;
|
|
25
|
+
console.log(BANNER);
|
|
17
26
|
// Step 1: Generate and pick a name
|
|
18
27
|
let agentName;
|
|
19
28
|
try {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;GAKG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,OAAO,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE3D,MAAM,OAAO,GAAG,WAAW,CAAC;AAE5B,KAAK,UAAU,IAAI;IACjB,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/scaffold.js
CHANGED
|
@@ -44,7 +44,7 @@ function scaffoldPython(dir, options) {
|
|
|
44
44
|
? `AI_API_KEY = os.getenv("OPENAI_API_KEY")`
|
|
45
45
|
: '';
|
|
46
46
|
const aiAnalyzeFunc = options.aiProvider === 'anthropic' ? `
|
|
47
|
-
def analyze_markets(markets):
|
|
47
|
+
def analyze_markets(markets, num_proposals=5):
|
|
48
48
|
"""Use Claude to analyze prediction markets and pick the best bets."""
|
|
49
49
|
client = anthropic.Anthropic(api_key=AI_API_KEY)
|
|
50
50
|
|
|
@@ -65,10 +65,10 @@ def analyze_markets(markets):
|
|
|
65
65
|
|
|
66
66
|
response = client.messages.create(
|
|
67
67
|
model="claude-sonnet-4-20250514",
|
|
68
|
-
max_tokens=
|
|
68
|
+
max_tokens=2048,
|
|
69
69
|
messages=[{
|
|
70
70
|
"role": "user",
|
|
71
|
-
"content": f"""You are a prediction market analyst. Analyze these Polymarket markets and pick up to
|
|
71
|
+
"content": f"""You are a prediction market analyst. Analyze these Polymarket markets and pick up to {num_proposals} best betting opportunities.
|
|
72
72
|
|
|
73
73
|
Markets:
|
|
74
74
|
{markets_text}
|
|
@@ -86,7 +86,7 @@ For each pick, respond in this exact JSON format (no markdown, just raw JSON arr
|
|
|
86
86
|
]
|
|
87
87
|
|
|
88
88
|
Rules:
|
|
89
|
-
- Pick
|
|
89
|
+
- Pick up to {num_proposals} markets that look like good opportunities
|
|
90
90
|
- Consider the current price, volume, and whether the market seems mispriced
|
|
91
91
|
- Be diverse -- don't pick markets that are all about the same topic
|
|
92
92
|
- Higher confidence = stronger conviction (0.5 = coin flip, 0.9 = very confident)
|
|
@@ -95,16 +95,21 @@ Rules:
|
|
|
95
95
|
}]
|
|
96
96
|
)
|
|
97
97
|
|
|
98
|
-
# Parse the AI response
|
|
98
|
+
# Parse the AI response (strip markdown code fences if present)
|
|
99
99
|
try:
|
|
100
|
-
|
|
100
|
+
text = response.content[0].text.strip()
|
|
101
|
+
if text.startswith("\`\`\`"):
|
|
102
|
+
text = text.split("\`\`\`", 2)[1]
|
|
103
|
+
if text.startswith("json"):
|
|
104
|
+
text = text[4:]
|
|
105
|
+
picks = json.loads(text.strip())
|
|
101
106
|
model_name = response.model
|
|
102
107
|
return picks, model_name
|
|
103
108
|
except (json.JSONDecodeError, IndexError):
|
|
104
109
|
print(f" AI response: {response.content[0].text}")
|
|
105
110
|
return [], response.model
|
|
106
111
|
` : options.aiProvider === 'openai' ? `
|
|
107
|
-
def analyze_markets(markets):
|
|
112
|
+
def analyze_markets(markets, num_proposals=5):
|
|
108
113
|
"""Use GPT to analyze prediction markets and pick the best bets."""
|
|
109
114
|
client = openai.OpenAI(api_key=AI_API_KEY)
|
|
110
115
|
|
|
@@ -127,7 +132,7 @@ def analyze_markets(markets):
|
|
|
127
132
|
model="gpt-4o",
|
|
128
133
|
messages=[{
|
|
129
134
|
"role": "user",
|
|
130
|
-
"content": f"""You are a prediction market analyst. Analyze these Polymarket markets and pick up to
|
|
135
|
+
"content": f"""You are a prediction market analyst. Analyze these Polymarket markets and pick up to {num_proposals} best betting opportunities.
|
|
131
136
|
|
|
132
137
|
Markets:
|
|
133
138
|
{markets_text}
|
|
@@ -145,7 +150,7 @@ For each pick, respond in this exact JSON format (no markdown, just raw JSON arr
|
|
|
145
150
|
]
|
|
146
151
|
|
|
147
152
|
Rules:
|
|
148
|
-
- Pick
|
|
153
|
+
- Pick up to {num_proposals} markets that look like good opportunities
|
|
149
154
|
- Consider the current price, volume, and whether the market seems mispriced
|
|
150
155
|
- Be diverse -- don't pick markets that are all about the same topic
|
|
151
156
|
- Higher confidence = stronger conviction (0.5 = coin flip, 0.9 = very confident)
|
|
@@ -154,16 +159,21 @@ Rules:
|
|
|
154
159
|
}],
|
|
155
160
|
)
|
|
156
161
|
|
|
157
|
-
# Parse the AI response
|
|
162
|
+
# Parse the AI response (strip markdown code fences if present)
|
|
158
163
|
try:
|
|
159
|
-
|
|
164
|
+
text = response.choices[0].message.content.strip()
|
|
165
|
+
if text.startswith("\`\`\`"):
|
|
166
|
+
text = text.split("\`\`\`", 2)[1]
|
|
167
|
+
if text.startswith("json"):
|
|
168
|
+
text = text[4:]
|
|
169
|
+
picks = json.loads(text.strip())
|
|
160
170
|
model_name = response.model
|
|
161
171
|
return picks, model_name
|
|
162
172
|
except (json.JSONDecodeError, IndexError):
|
|
163
173
|
print(f" AI response: {response.choices[0].message.content}")
|
|
164
174
|
return [], response.model
|
|
165
175
|
` : `
|
|
166
|
-
def analyze_markets(markets):
|
|
176
|
+
def analyze_markets(markets, num_proposals=5):
|
|
167
177
|
"""Placeholder: Add your own AI logic here to analyze markets."""
|
|
168
178
|
# This is where you connect your AI model.
|
|
169
179
|
# Return a list of picks in this format:
|
|
@@ -240,8 +250,14 @@ class PolyclawClient:
|
|
|
240
250
|
response = requests.request(method, url, headers=headers, json=body)
|
|
241
251
|
return response.json()
|
|
242
252
|
|
|
243
|
-
def get_markets(self, page: int = 1, limit: int = 20):
|
|
244
|
-
|
|
253
|
+
def get_markets(self, page: int = 1, limit: int = 20, tag: str = None):
|
|
254
|
+
path = f"/api/markets?page={page}&limit={limit}"
|
|
255
|
+
if tag and tag != "all":
|
|
256
|
+
path += f"&tag={tag}"
|
|
257
|
+
return self._request("GET", path)
|
|
258
|
+
|
|
259
|
+
def get_my_proposals(self, limit: int = 100):
|
|
260
|
+
return self._request("GET", f"/api/proposals?limit={limit}")
|
|
245
261
|
|
|
246
262
|
def submit_proposal(self, market_id, outcome, side, recommended_amount, confidence, reasoning=None, model=None):
|
|
247
263
|
body = {"marketId": market_id, "outcome": outcome, "side": side,
|
|
@@ -252,35 +268,50 @@ class PolyclawClient:
|
|
|
252
268
|
|
|
253
269
|
${aiAnalyzeFunc}
|
|
254
270
|
|
|
255
|
-
def
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
if
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
271
|
+
def get_proposed_market_ids(client):
|
|
272
|
+
"""Fetch markets we've already proposed on to avoid duplicates."""
|
|
273
|
+
result = client.get_my_proposals(limit=100)
|
|
274
|
+
if not result.get("success"):
|
|
275
|
+
return set()
|
|
276
|
+
proposals = result.get("data", result.get("proposals", []))
|
|
277
|
+
if isinstance(proposals, dict):
|
|
278
|
+
proposals = proposals.get("proposals", [])
|
|
279
|
+
return {p.get("marketId") for p in proposals if p.get("marketId")}
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def run_round(client, tag="all", search_size=20, num_proposals=5, page=1):
|
|
283
|
+
"""Fetch markets, filter out already-proposed ones, analyze, and submit."""
|
|
284
|
+
category_label = tag if tag != "all" else "all topics"
|
|
268
285
|
|
|
269
|
-
#
|
|
270
|
-
print("
|
|
271
|
-
|
|
286
|
+
# Get markets we've already proposed on
|
|
287
|
+
print("Checking existing proposals...")
|
|
288
|
+
already_proposed = get_proposed_market_ids(client)
|
|
289
|
+
if already_proposed:
|
|
290
|
+
print(f" Skipping {len(already_proposed)} market(s) with existing proposals")
|
|
291
|
+
|
|
292
|
+
# Fetch markets with category filter
|
|
293
|
+
print(f"Scanning {search_size} {category_label} markets...")
|
|
294
|
+
result = client.get_markets(page=page, limit=search_size, tag=tag)
|
|
272
295
|
|
|
273
296
|
if not result.get("success"):
|
|
274
297
|
print(f"Error: {result.get('error', 'Unknown error')}")
|
|
275
298
|
return
|
|
276
299
|
|
|
277
|
-
|
|
278
|
-
|
|
300
|
+
all_markets = result.get("data", [])
|
|
301
|
+
|
|
302
|
+
# Filter out markets we've already proposed on
|
|
303
|
+
markets = [m for m in all_markets if m.get("id") not in already_proposed]
|
|
304
|
+
print(f"Found {len(all_markets)} markets, {len(markets)} new ones to analyze")
|
|
305
|
+
|
|
306
|
+
if not markets:
|
|
307
|
+
print("No new markets to analyze. Try again later or pick a different category.")
|
|
308
|
+
return
|
|
309
|
+
|
|
279
310
|
print()
|
|
280
311
|
|
|
281
312
|
# Ask AI to analyze and pick the best bets
|
|
282
|
-
print("Asking AI to
|
|
283
|
-
picks, model_name = analyze_markets(markets)
|
|
313
|
+
print(f"Asking AI to pick {num_proposals} best bets...\\n")
|
|
314
|
+
picks, model_name = analyze_markets(markets, num_proposals=num_proposals)
|
|
284
315
|
|
|
285
316
|
if not picks:
|
|
286
317
|
print("No picks from AI. Try again later.")
|
|
@@ -317,6 +348,124 @@ ${options.aiProvider !== 'none' ? `
|
|
|
317
348
|
print("Done! Check your dashboard to review proposals.")
|
|
318
349
|
|
|
319
350
|
|
|
351
|
+
BANNER = """
|
|
352
|
+
\\033[91m .-----.
|
|
353
|
+
_ | o o | _
|
|
354
|
+
| | | w | | |
|
|
355
|
+
|_| '-----' |_|\\033[0m
|
|
356
|
+
|
|
357
|
+
\\033[1m █▀█ █▀█ █ █ █ █▀▀ █ █▀█ █ █
|
|
358
|
+
█▀▀ █ █ █ █ █ █ █▀█ █ █ █
|
|
359
|
+
▀ ▀▀▀ ▀▀▀ ▀ ▀▀▀ ▀▀▀ ▀ ▀ ▀ ▀\\033[0m
|
|
360
|
+
\\033[90m AI-Powered Prediction Market Agent\\033[0m
|
|
361
|
+
"""
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
# --- Interactive prompts ---
|
|
365
|
+
|
|
366
|
+
CATEGORIES = [
|
|
367
|
+
("politics", "Politics"),
|
|
368
|
+
("sports", "Sports"),
|
|
369
|
+
("crypto", "Crypto"),
|
|
370
|
+
("finance", "Finance"),
|
|
371
|
+
("tech", "Tech"),
|
|
372
|
+
("culture", "Culture"),
|
|
373
|
+
("all", "All topics"),
|
|
374
|
+
]
|
|
375
|
+
|
|
376
|
+
SEARCH_SIZES = [20, 40, 60, 100, 200, 400]
|
|
377
|
+
PROPOSAL_COUNTS = [5, 10, 15]
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def ask_category(agent_name):
|
|
381
|
+
print(f"What category would you like {agent_name} to look into?")
|
|
382
|
+
for i, (_, label) in enumerate(CATEGORIES, 1):
|
|
383
|
+
print(f" {i}. {label}")
|
|
384
|
+
print()
|
|
385
|
+
while True:
|
|
386
|
+
try:
|
|
387
|
+
choice = input("Pick a number: ").strip()
|
|
388
|
+
except (EOFError, KeyboardInterrupt):
|
|
389
|
+
return "all"
|
|
390
|
+
if choice.isdigit() and 1 <= int(choice) <= len(CATEGORIES):
|
|
391
|
+
print()
|
|
392
|
+
return CATEGORIES[int(choice) - 1][0]
|
|
393
|
+
print(f" Please enter a number between 1 and {len(CATEGORIES)}")
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def ask_search_size():
|
|
397
|
+
print("How big do you want your search to be?")
|
|
398
|
+
for i, size in enumerate(SEARCH_SIZES, 1):
|
|
399
|
+
print(f" {i}. {size} markets")
|
|
400
|
+
print()
|
|
401
|
+
while True:
|
|
402
|
+
try:
|
|
403
|
+
choice = input("Pick a number [1]: ").strip() or "1"
|
|
404
|
+
except (EOFError, KeyboardInterrupt):
|
|
405
|
+
return SEARCH_SIZES[0]
|
|
406
|
+
if choice.isdigit() and 1 <= int(choice) <= len(SEARCH_SIZES):
|
|
407
|
+
print()
|
|
408
|
+
return SEARCH_SIZES[int(choice) - 1]
|
|
409
|
+
print(f" Please enter a number between 1 and {len(SEARCH_SIZES)}")
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def ask_num_proposals():
|
|
413
|
+
print("How many proposals do you want?")
|
|
414
|
+
for i, count in enumerate(PROPOSAL_COUNTS, 1):
|
|
415
|
+
print(f" {i}. {count} proposals")
|
|
416
|
+
print()
|
|
417
|
+
while True:
|
|
418
|
+
try:
|
|
419
|
+
choice = input("Pick a number [1]: ").strip() or "1"
|
|
420
|
+
except (EOFError, KeyboardInterrupt):
|
|
421
|
+
return PROPOSAL_COUNTS[0]
|
|
422
|
+
if choice.isdigit() and 1 <= int(choice) <= len(PROPOSAL_COUNTS):
|
|
423
|
+
print()
|
|
424
|
+
return PROPOSAL_COUNTS[int(choice) - 1]
|
|
425
|
+
print(f" Please enter a number between 1 and {len(PROPOSAL_COUNTS)}")
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
# --- Main ---
|
|
429
|
+
|
|
430
|
+
def main():
|
|
431
|
+
if not API_KEY or not API_SECRET:
|
|
432
|
+
print("Error: POLYCLAW_API_KEY and POLYCLAW_API_SECRET must be set in .env")
|
|
433
|
+
return
|
|
434
|
+
${options.aiProvider !== 'none' ? `
|
|
435
|
+
ai_key_name = "${options.aiProvider === 'anthropic' ? 'ANTHROPIC_API_KEY' : 'OPENAI_API_KEY'}"
|
|
436
|
+
if not AI_API_KEY:
|
|
437
|
+
print(f"Error: {ai_key_name} must be set in .env")
|
|
438
|
+
return
|
|
439
|
+
` : ''}
|
|
440
|
+
client = PolyclawClient(API_KEY, API_SECRET, BASE_URL)
|
|
441
|
+
print(BANNER)
|
|
442
|
+
print(" Welcome to Polyclaw!")
|
|
443
|
+
print()
|
|
444
|
+
print(" Your agent: \\033[1m${options.name}\\033[0m")
|
|
445
|
+
print()
|
|
446
|
+
|
|
447
|
+
# Ask user preferences
|
|
448
|
+
tag = ask_category("${options.name}")
|
|
449
|
+
search_size = ask_search_size()
|
|
450
|
+
num_proposals = ask_num_proposals()
|
|
451
|
+
|
|
452
|
+
page = 1
|
|
453
|
+
run_round(client, tag=tag, search_size=search_size, num_proposals=num_proposals, page=page)
|
|
454
|
+
|
|
455
|
+
# Interactive loop
|
|
456
|
+
while True:
|
|
457
|
+
print()
|
|
458
|
+
try:
|
|
459
|
+
reply = input("Press Enter for more proposals, or q to quit: ").strip().lower()
|
|
460
|
+
except (EOFError, KeyboardInterrupt):
|
|
461
|
+
break
|
|
462
|
+
if reply in ("q", "quit", "exit"):
|
|
463
|
+
break
|
|
464
|
+
page += 1
|
|
465
|
+
print()
|
|
466
|
+
run_round(client, tag=tag, search_size=search_size, num_proposals=num_proposals, page=page)
|
|
467
|
+
|
|
468
|
+
|
|
320
469
|
if __name__ == "__main__":
|
|
321
470
|
main()
|
|
322
471
|
`;
|
|
@@ -391,8 +540,14 @@ class PolyclawClient {
|
|
|
391
540
|
return response.json() as T;
|
|
392
541
|
}
|
|
393
542
|
|
|
394
|
-
async getMarkets(page = 1, limit = 20) {
|
|
395
|
-
|
|
543
|
+
async getMarkets(page = 1, limit = 20, tag?: string) {
|
|
544
|
+
let path = \`/api/markets?page=\${page}&limit=\${limit}\`;
|
|
545
|
+
if (tag && tag !== 'all') path += \`&tag=\${tag}\`;
|
|
546
|
+
return this.request<any>('GET', path);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
async getMyProposals(limit = 100) {
|
|
550
|
+
return this.request<any>('GET', \`/api/proposals?limit=\${limit}\`);
|
|
396
551
|
}
|
|
397
552
|
|
|
398
553
|
async submitProposal(proposal: {
|
|
@@ -456,63 +611,186 @@ function findBestPlay(markets: any[]): MarketPlay | null {
|
|
|
456
611
|
return scored[0] || null;
|
|
457
612
|
}
|
|
458
613
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
614
|
+
import * as readline from 'readline';
|
|
615
|
+
|
|
616
|
+
function prompt(question: string): Promise<string> {
|
|
617
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
618
|
+
return new Promise((resolve) => {
|
|
619
|
+
rl.question(question, (answer) => { rl.close(); resolve(answer.trim().toLowerCase()); });
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
// --- Interactive prompts ---
|
|
624
|
+
|
|
625
|
+
const BANNER = \`
|
|
626
|
+
\\x1b[91m .-----.
|
|
627
|
+
_ | o o | _
|
|
628
|
+
| | | w | | |
|
|
629
|
+
|_| '-----' |_|\\x1b[0m
|
|
630
|
+
|
|
631
|
+
\\x1b[1m █▀█ █▀█ █ █ █ █▀▀ █ █▀█ █ █
|
|
632
|
+
█▀▀ █ █ █ █ █ █ █▀█ █ █ █
|
|
633
|
+
▀ ▀▀▀ ▀▀▀ ▀ ▀▀▀ ▀▀▀ ▀ ▀ ▀ ▀\\x1b[0m
|
|
634
|
+
\\x1b[90m AI-Powered Prediction Market Agent\\x1b[0m
|
|
635
|
+
\`;
|
|
636
|
+
|
|
637
|
+
const CATEGORIES = [
|
|
638
|
+
['politics', 'Politics'],
|
|
639
|
+
['sports', 'Sports'],
|
|
640
|
+
['crypto', 'Crypto'],
|
|
641
|
+
['finance', 'Finance'],
|
|
642
|
+
['tech', 'Tech'],
|
|
643
|
+
['culture', 'Culture'],
|
|
644
|
+
['all', 'All topics'],
|
|
645
|
+
] as const;
|
|
646
|
+
|
|
647
|
+
const SEARCH_SIZES = [20, 40, 60, 100, 200, 400];
|
|
648
|
+
const PROPOSAL_COUNTS = [5, 10, 15];
|
|
649
|
+
|
|
650
|
+
async function askCategory(agentName: string): Promise<string> {
|
|
651
|
+
console.log(\`What category would you like \${agentName} to look into?\`);
|
|
652
|
+
CATEGORIES.forEach(([, label], i) => console.log(\` \${i + 1}. \${label}\`));
|
|
653
|
+
console.log();
|
|
654
|
+
while (true) {
|
|
655
|
+
const choice = await prompt('Pick a number: ');
|
|
656
|
+
const n = parseInt(choice);
|
|
657
|
+
if (n >= 1 && n <= CATEGORIES.length) { console.log(); return CATEGORIES[n - 1][0]; }
|
|
658
|
+
console.log(\` Please enter a number between 1 and \${CATEGORIES.length}\`);
|
|
463
659
|
}
|
|
660
|
+
}
|
|
464
661
|
|
|
465
|
-
|
|
466
|
-
console.log('
|
|
662
|
+
async function askSearchSize(): Promise<number> {
|
|
663
|
+
console.log('How big do you want your search to be?');
|
|
664
|
+
SEARCH_SIZES.forEach((s, i) => console.log(\` \${i + 1}. \${s} markets\`));
|
|
467
665
|
console.log();
|
|
666
|
+
while (true) {
|
|
667
|
+
const choice = (await prompt('Pick a number [1]: ')) || '1';
|
|
668
|
+
const n = parseInt(choice);
|
|
669
|
+
if (n >= 1 && n <= SEARCH_SIZES.length) { console.log(); return SEARCH_SIZES[n - 1]; }
|
|
670
|
+
console.log(\` Please enter a number between 1 and \${SEARCH_SIZES.length}\`);
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
async function askNumProposals(): Promise<number> {
|
|
675
|
+
console.log('How many proposals do you want?');
|
|
676
|
+
PROPOSAL_COUNTS.forEach((c, i) => console.log(\` \${i + 1}. \${c} proposals\`));
|
|
677
|
+
console.log();
|
|
678
|
+
while (true) {
|
|
679
|
+
const choice = (await prompt('Pick a number [1]: ')) || '1';
|
|
680
|
+
const n = parseInt(choice);
|
|
681
|
+
if (n >= 1 && n <= PROPOSAL_COUNTS.length) { console.log(); return PROPOSAL_COUNTS[n - 1]; }
|
|
682
|
+
console.log(\` Please enter a number between 1 and \${PROPOSAL_COUNTS.length}\`);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
468
685
|
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
686
|
+
async function getProposedMarketIds(client: PolyclawClient): Promise<Set<string>> {
|
|
687
|
+
const result = await client.getMyProposals(100);
|
|
688
|
+
if (!result.success) return new Set();
|
|
689
|
+
const proposals = Array.isArray(result.data) ? result.data : (result.data?.proposals || []);
|
|
690
|
+
return new Set(proposals.map((p: any) => p.marketId).filter(Boolean));
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
async function runRound(client: PolyclawClient, opts: { tag: string; searchSize: number; numProposals: number; page: number }) {
|
|
694
|
+
const categoryLabel = opts.tag !== 'all' ? opts.tag : 'all topics';
|
|
695
|
+
|
|
696
|
+
console.log('Checking existing proposals...');
|
|
697
|
+
const alreadyProposed = await getProposedMarketIds(client);
|
|
698
|
+
if (alreadyProposed.size) console.log(\` Skipping \${alreadyProposed.size} market(s) with existing proposals\`);
|
|
699
|
+
|
|
700
|
+
console.log(\`Scanning \${opts.searchSize} \${categoryLabel} markets...\`);
|
|
701
|
+
const result = await client.getMarkets(opts.page, opts.searchSize, opts.tag);
|
|
472
702
|
|
|
473
703
|
if (!result.success) {
|
|
474
704
|
console.error(\`Error: \${result.error || 'Unknown error'}\`);
|
|
475
705
|
return;
|
|
476
706
|
}
|
|
477
707
|
|
|
478
|
-
const
|
|
479
|
-
|
|
708
|
+
const allMarkets = result.data || [];
|
|
709
|
+
const markets = allMarkets.filter((m: any) => !alreadyProposed.has(m.id));
|
|
710
|
+
console.log(\`Found \${allMarkets.length} markets, \${markets.length} new ones to analyze\\n\`);
|
|
480
711
|
|
|
481
|
-
|
|
482
|
-
|
|
712
|
+
if (!markets.length) {
|
|
713
|
+
console.log('No new markets to analyze. Try again later or pick a different category.');
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
483
716
|
|
|
484
|
-
|
|
717
|
+
// Find top plays using the scoring heuristic
|
|
718
|
+
const scored = markets
|
|
719
|
+
.map((market: any) => {
|
|
720
|
+
const yesPrice = parseFloat(market.outcomePrices?.[0] ?? '0.5');
|
|
721
|
+
const volume = parseFloat(market.volume ?? '0');
|
|
722
|
+
if (yesPrice < 0.05 || yesPrice > 0.95) return null;
|
|
723
|
+
const edge = Math.abs(yesPrice - 0.5);
|
|
724
|
+
const liquidityScore = Math.min(volume / 500000, 1.0);
|
|
725
|
+
const score = edge * 0.6 + liquidityScore * 0.4;
|
|
726
|
+
const outcome = yesPrice < 0.5 ? 'Yes' : 'No';
|
|
727
|
+
const confidence = Math.min(Math.round((0.5 + edge) * 100) / 100, 0.95);
|
|
728
|
+
const reasoning = \`Priced at \${(yesPrice * 100).toFixed(0)}% with $\${volume.toLocaleString()} volume\`;
|
|
729
|
+
return { market, outcome, side: 'BUY', confidence, reasoning, score };
|
|
730
|
+
})
|
|
731
|
+
.filter(Boolean)
|
|
732
|
+
.sort((a: any, b: any) => b.score - a.score)
|
|
733
|
+
.slice(0, opts.numProposals);
|
|
734
|
+
|
|
735
|
+
if (!scored.length) {
|
|
485
736
|
console.log('No good opportunities found right now.');
|
|
486
737
|
return;
|
|
487
738
|
}
|
|
488
739
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
740
|
+
console.log(\`Submitting \${scored.length} proposal(s)...\\n\`);
|
|
741
|
+
|
|
742
|
+
for (const pick of scored) {
|
|
743
|
+
console.log(\` -> \${pick.market.question}\`);
|
|
744
|
+
console.log(\` BUY \${pick.outcome} | Confidence: \${(pick.confidence * 100).toFixed(0)}%\`);
|
|
745
|
+
console.log(\` \${pick.reasoning}\`);
|
|
746
|
+
|
|
747
|
+
const proposal = await client.submitProposal({
|
|
748
|
+
marketId: pick.market.id,
|
|
749
|
+
outcome: pick.outcome,
|
|
750
|
+
side: pick.side,
|
|
751
|
+
recommendedAmount: 10.0,
|
|
752
|
+
confidence: pick.confidence,
|
|
753
|
+
reasoning: pick.reasoning,
|
|
754
|
+
model: 'your-model-here',
|
|
755
|
+
});
|
|
756
|
+
|
|
757
|
+
if (proposal.success) {
|
|
758
|
+
console.log(' Submitted!\\n');
|
|
759
|
+
} else {
|
|
760
|
+
console.error(\` Error: \${proposal.error || 'Unknown'}\\n\`);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
console.log('Done! Check your dashboard to review proposals.');
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
async function main() {
|
|
768
|
+
if (!API_KEY || !API_SECRET) {
|
|
769
|
+
console.error('Error: POLYCLAW_API_KEY and POLYCLAW_API_SECRET must be set in .env');
|
|
770
|
+
return;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
const client = new PolyclawClient({ apiKey: API_KEY, apiSecret: API_SECRET, baseUrl: BASE_URL });
|
|
774
|
+
console.log(BANNER);
|
|
775
|
+
console.log(' Welcome to Polyclaw!');
|
|
776
|
+
console.log();
|
|
777
|
+
console.log(\` Your agent: \\x1b[1m${options.name}\\x1b[0m\`);
|
|
495
778
|
console.log();
|
|
496
779
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
marketId: best.market.id,
|
|
504
|
-
outcome: best.outcome,
|
|
505
|
-
side: best.side,
|
|
506
|
-
recommendedAmount: 10.0,
|
|
507
|
-
confidence: best.confidence,
|
|
508
|
-
reasoning: best.reasoning,
|
|
509
|
-
model: 'your-model-here', // Replace with your AI model name
|
|
510
|
-
});
|
|
780
|
+
const tag = await askCategory('${options.name}');
|
|
781
|
+
const searchSize = await askSearchSize();
|
|
782
|
+
const numProposals = await askNumProposals();
|
|
783
|
+
|
|
784
|
+
let page = 1;
|
|
785
|
+
await runRound(client, { tag, searchSize, numProposals, page });
|
|
511
786
|
|
|
512
|
-
|
|
513
|
-
console.log(
|
|
514
|
-
|
|
515
|
-
|
|
787
|
+
while (true) {
|
|
788
|
+
console.log();
|
|
789
|
+
const reply = await prompt('Press Enter for more proposals, or q to quit: ');
|
|
790
|
+
if (['q', 'quit', 'exit'].includes(reply)) break;
|
|
791
|
+
page++;
|
|
792
|
+
console.log();
|
|
793
|
+
await runRound(client, { tag, searchSize, numProposals, page });
|
|
516
794
|
}
|
|
517
795
|
}
|
|
518
796
|
|
package/dist/scaffold.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACjD,CAAC;AAYD,MAAM,UAAU,eAAe,CAAC,OAAwB;IACtD,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IACxD,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE3C,mCAAmC;IACnC,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,KAAK,WAAW;QAClD,CAAC,CAAC,uBAAuB,OAAO,CAAC,QAAQ,IAAI,wBAAwB,IAAI;QACzE,CAAC,CAAC,OAAO,CAAC,UAAU,KAAK,QAAQ;YACjC,CAAC,CAAC,oBAAoB,OAAO,CAAC,QAAQ,IAAI,qBAAqB,IAAI;YACnE,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,UAAU,GAAG,qBAAqB,OAAO,CAAC,IAAI;;;mBAGnC,OAAO,CAAC,MAAM;sBACX,OAAO,CAAC,SAAS;;EAErC,SAAS,EAAE,CAAC;IAEZ,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC;IAEpD,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAClC,cAAc,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC;SAAM,CAAC;QACN,kBAAkB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,cAAc,CAAC,GAAW,EAAE,OAAwB;IAC3D,4CAA4C;IAC5C,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,KAAK,WAAW;QACjD,CAAC,CAAC,kBAAkB;QACpB,CAAC,CAAC,OAAO,CAAC,UAAU,KAAK,QAAQ;YACjC,CAAC,CAAC,eAAe;YACjB,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,KAAK,WAAW;QACjD,CAAC,CAAC,6CAA6C;QAC/C,CAAC,CAAC,OAAO,CAAC,UAAU,KAAK,QAAQ;YACjC,CAAC,CAAC,0CAA0C;YAC5C,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,aAAa,GAAG,OAAO,CAAC,UAAU,KAAK,WAAW,CAAC,CAAC,CAAC
|
|
1
|
+
{"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AACjD,CAAC;AAYD,MAAM,UAAU,eAAe,CAAC,OAAwB;IACtD,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IACxD,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAE3C,mCAAmC;IACnC,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,KAAK,WAAW;QAClD,CAAC,CAAC,uBAAuB,OAAO,CAAC,QAAQ,IAAI,wBAAwB,IAAI;QACzE,CAAC,CAAC,OAAO,CAAC,UAAU,KAAK,QAAQ;YACjC,CAAC,CAAC,oBAAoB,OAAO,CAAC,QAAQ,IAAI,qBAAqB,IAAI;YACnE,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,UAAU,GAAG,qBAAqB,OAAO,CAAC,IAAI;;;mBAGnC,OAAO,CAAC,MAAM;sBACX,OAAO,CAAC,SAAS;;EAErC,SAAS,EAAE,CAAC;IAEZ,aAAa,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,UAAU,CAAC,CAAC;IAEpD,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAClC,cAAc,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC;SAAM,CAAC;QACN,kBAAkB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,SAAS,cAAc,CAAC,GAAW,EAAE,OAAwB;IAC3D,4CAA4C;IAC5C,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,KAAK,WAAW;QACjD,CAAC,CAAC,kBAAkB;QACpB,CAAC,CAAC,OAAO,CAAC,UAAU,KAAK,QAAQ;YACjC,CAAC,CAAC,eAAe;YACjB,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,KAAK,WAAW;QACjD,CAAC,CAAC,6CAA6C;QAC/C,CAAC,CAAC,OAAO,CAAC,UAAU,KAAK,QAAQ;YACjC,CAAC,CAAC,0CAA0C;YAC5C,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,aAAa,GAAG,OAAO,CAAC,UAAU,KAAK,WAAW,CAAC,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiE5D,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgErC,CAAC,CAAC,CAAC;;;;;;;;;;;;;;;;;;;;CAoBH,CAAC;IAEA,MAAM,KAAK,GAAG;EACd,OAAO,CAAC,IAAI;;;;;;;;;;;;;;;;;;EAkBZ,QAAQ;;;;;;;EAOR,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+CR,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAqKb,OAAO,CAAC,UAAU,KAAK,MAAM,CAAC,CAAC,CAAC;qBACb,OAAO,CAAC,UAAU,KAAK,WAAW,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,gBAAgB;;;;CAI/F,CAAC,CAAC,CAAC,EAAE;;;;;mCAK6B,OAAO,CAAC,IAAI;;;;0BAIrB,OAAO,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;CAuBrC,CAAC;IAEA,MAAM,KAAK,GAAG,OAAO,CAAC,UAAU,KAAK,WAAW;QAC9C,CAAC,CAAC,qBAAqB;QACvB,CAAC,CAAC,OAAO,CAAC,UAAU,KAAK,QAAQ;YACjC,CAAC,CAAC,kBAAkB;YACpB,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,YAAY,GAAG;;EAErB,KAAK,EAAE,CAAC;IAER,MAAM,SAAS,GAAG;;;;CAInB,CAAC;IAEA,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,KAAK,CAAC,CAAC;IAC1C,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,kBAAkB,CAAC,EAAE,YAAY,CAAC,CAAC;IAC3D,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,EAAE,SAAS,CAAC,CAAC;AACpD,CAAC;AAED,SAAS,kBAAkB,CAAC,GAAW,EAAE,OAAwB;IAC/D,MAAM,KAAK,GAAG;KACX,OAAO,CAAC,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wCA8RuB,OAAO,CAAC,IAAI;;;mCAGjB,OAAO,CAAC,IAAI;;;;;;;;;;;;;;;;;;CAkB9C,CAAC;IAEA,MAAM,WAAW,GAAG;aACT,OAAO,CAAC,OAAO;;;;;;;;;;;;CAY3B,CAAC;IAEA,MAAM,SAAS,GAAG;;;CAGnB,CAAC;IAEA,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,KAAK,CAAC,CAAC;IAC1C,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,WAAW,CAAC,CAAC;IACtD,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,EAAE,SAAS,CAAC,CAAC;AACpD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-polyclaw-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "Create a Polyclaw AI trading agent in seconds",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -20,6 +20,11 @@
|
|
|
20
20
|
"@types/prompts": "^2.4.9",
|
|
21
21
|
"typescript": "^5.4.0"
|
|
22
22
|
},
|
|
23
|
-
"keywords": [
|
|
23
|
+
"keywords": [
|
|
24
|
+
"polyclaw",
|
|
25
|
+
"polymarket",
|
|
26
|
+
"ai-agent",
|
|
27
|
+
"prediction-markets"
|
|
28
|
+
],
|
|
24
29
|
"license": "MIT"
|
|
25
30
|
}
|
package/src/index.ts
CHANGED
|
@@ -15,9 +15,18 @@ import { scaffoldProject, nameToDir } from './scaffold.js';
|
|
|
15
15
|
const LOBSTER = '\u{1F99E}';
|
|
16
16
|
|
|
17
17
|
async function main() {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
const BANNER = `
|
|
19
|
+
${chalk.red(` .-----.
|
|
20
|
+
_ | o o | _
|
|
21
|
+
| | | w | | |
|
|
22
|
+
|_| '-----' |_|`)}
|
|
23
|
+
|
|
24
|
+
${chalk.bold(` █▀█ █▀█ █ █ █ █▀▀ █ █▀█ █ █
|
|
25
|
+
█▀▀ █ █ █ █ █ █ █▀█ █ █ █
|
|
26
|
+
▀ ▀▀▀ ▀▀▀ ▀ ▀▀▀ ▀▀▀ ▀ ▀ ▀ ▀`)}
|
|
27
|
+
${chalk.dim(' AI-Powered Prediction Market Agent')}
|
|
28
|
+
`;
|
|
29
|
+
console.log(BANNER);
|
|
21
30
|
|
|
22
31
|
// Step 1: Generate and pick a name
|
|
23
32
|
let agentName: string;
|
package/src/scaffold.ts
CHANGED
|
@@ -63,7 +63,7 @@ function scaffoldPython(dir: string, options: ScaffoldOptions) {
|
|
|
63
63
|
: '';
|
|
64
64
|
|
|
65
65
|
const aiAnalyzeFunc = options.aiProvider === 'anthropic' ? `
|
|
66
|
-
def analyze_markets(markets):
|
|
66
|
+
def analyze_markets(markets, num_proposals=5):
|
|
67
67
|
"""Use Claude to analyze prediction markets and pick the best bets."""
|
|
68
68
|
client = anthropic.Anthropic(api_key=AI_API_KEY)
|
|
69
69
|
|
|
@@ -84,10 +84,10 @@ def analyze_markets(markets):
|
|
|
84
84
|
|
|
85
85
|
response = client.messages.create(
|
|
86
86
|
model="claude-sonnet-4-20250514",
|
|
87
|
-
max_tokens=
|
|
87
|
+
max_tokens=2048,
|
|
88
88
|
messages=[{
|
|
89
89
|
"role": "user",
|
|
90
|
-
"content": f"""You are a prediction market analyst. Analyze these Polymarket markets and pick up to
|
|
90
|
+
"content": f"""You are a prediction market analyst. Analyze these Polymarket markets and pick up to {num_proposals} best betting opportunities.
|
|
91
91
|
|
|
92
92
|
Markets:
|
|
93
93
|
{markets_text}
|
|
@@ -105,7 +105,7 @@ For each pick, respond in this exact JSON format (no markdown, just raw JSON arr
|
|
|
105
105
|
]
|
|
106
106
|
|
|
107
107
|
Rules:
|
|
108
|
-
- Pick
|
|
108
|
+
- Pick up to {num_proposals} markets that look like good opportunities
|
|
109
109
|
- Consider the current price, volume, and whether the market seems mispriced
|
|
110
110
|
- Be diverse -- don't pick markets that are all about the same topic
|
|
111
111
|
- Higher confidence = stronger conviction (0.5 = coin flip, 0.9 = very confident)
|
|
@@ -114,16 +114,21 @@ Rules:
|
|
|
114
114
|
}]
|
|
115
115
|
)
|
|
116
116
|
|
|
117
|
-
# Parse the AI response
|
|
117
|
+
# Parse the AI response (strip markdown code fences if present)
|
|
118
118
|
try:
|
|
119
|
-
|
|
119
|
+
text = response.content[0].text.strip()
|
|
120
|
+
if text.startswith("\`\`\`"):
|
|
121
|
+
text = text.split("\`\`\`", 2)[1]
|
|
122
|
+
if text.startswith("json"):
|
|
123
|
+
text = text[4:]
|
|
124
|
+
picks = json.loads(text.strip())
|
|
120
125
|
model_name = response.model
|
|
121
126
|
return picks, model_name
|
|
122
127
|
except (json.JSONDecodeError, IndexError):
|
|
123
128
|
print(f" AI response: {response.content[0].text}")
|
|
124
129
|
return [], response.model
|
|
125
130
|
` : options.aiProvider === 'openai' ? `
|
|
126
|
-
def analyze_markets(markets):
|
|
131
|
+
def analyze_markets(markets, num_proposals=5):
|
|
127
132
|
"""Use GPT to analyze prediction markets and pick the best bets."""
|
|
128
133
|
client = openai.OpenAI(api_key=AI_API_KEY)
|
|
129
134
|
|
|
@@ -146,7 +151,7 @@ def analyze_markets(markets):
|
|
|
146
151
|
model="gpt-4o",
|
|
147
152
|
messages=[{
|
|
148
153
|
"role": "user",
|
|
149
|
-
"content": f"""You are a prediction market analyst. Analyze these Polymarket markets and pick up to
|
|
154
|
+
"content": f"""You are a prediction market analyst. Analyze these Polymarket markets and pick up to {num_proposals} best betting opportunities.
|
|
150
155
|
|
|
151
156
|
Markets:
|
|
152
157
|
{markets_text}
|
|
@@ -164,7 +169,7 @@ For each pick, respond in this exact JSON format (no markdown, just raw JSON arr
|
|
|
164
169
|
]
|
|
165
170
|
|
|
166
171
|
Rules:
|
|
167
|
-
- Pick
|
|
172
|
+
- Pick up to {num_proposals} markets that look like good opportunities
|
|
168
173
|
- Consider the current price, volume, and whether the market seems mispriced
|
|
169
174
|
- Be diverse -- don't pick markets that are all about the same topic
|
|
170
175
|
- Higher confidence = stronger conviction (0.5 = coin flip, 0.9 = very confident)
|
|
@@ -173,16 +178,21 @@ Rules:
|
|
|
173
178
|
}],
|
|
174
179
|
)
|
|
175
180
|
|
|
176
|
-
# Parse the AI response
|
|
181
|
+
# Parse the AI response (strip markdown code fences if present)
|
|
177
182
|
try:
|
|
178
|
-
|
|
183
|
+
text = response.choices[0].message.content.strip()
|
|
184
|
+
if text.startswith("\`\`\`"):
|
|
185
|
+
text = text.split("\`\`\`", 2)[1]
|
|
186
|
+
if text.startswith("json"):
|
|
187
|
+
text = text[4:]
|
|
188
|
+
picks = json.loads(text.strip())
|
|
179
189
|
model_name = response.model
|
|
180
190
|
return picks, model_name
|
|
181
191
|
except (json.JSONDecodeError, IndexError):
|
|
182
192
|
print(f" AI response: {response.choices[0].message.content}")
|
|
183
193
|
return [], response.model
|
|
184
194
|
` : `
|
|
185
|
-
def analyze_markets(markets):
|
|
195
|
+
def analyze_markets(markets, num_proposals=5):
|
|
186
196
|
"""Placeholder: Add your own AI logic here to analyze markets."""
|
|
187
197
|
# This is where you connect your AI model.
|
|
188
198
|
# Return a list of picks in this format:
|
|
@@ -260,8 +270,14 @@ class PolyclawClient:
|
|
|
260
270
|
response = requests.request(method, url, headers=headers, json=body)
|
|
261
271
|
return response.json()
|
|
262
272
|
|
|
263
|
-
def get_markets(self, page: int = 1, limit: int = 20):
|
|
264
|
-
|
|
273
|
+
def get_markets(self, page: int = 1, limit: int = 20, tag: str = None):
|
|
274
|
+
path = f"/api/markets?page={page}&limit={limit}"
|
|
275
|
+
if tag and tag != "all":
|
|
276
|
+
path += f"&tag={tag}"
|
|
277
|
+
return self._request("GET", path)
|
|
278
|
+
|
|
279
|
+
def get_my_proposals(self, limit: int = 100):
|
|
280
|
+
return self._request("GET", f"/api/proposals?limit={limit}")
|
|
265
281
|
|
|
266
282
|
def submit_proposal(self, market_id, outcome, side, recommended_amount, confidence, reasoning=None, model=None):
|
|
267
283
|
body = {"marketId": market_id, "outcome": outcome, "side": side,
|
|
@@ -272,35 +288,50 @@ class PolyclawClient:
|
|
|
272
288
|
|
|
273
289
|
${aiAnalyzeFunc}
|
|
274
290
|
|
|
275
|
-
def
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
if
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
291
|
+
def get_proposed_market_ids(client):
|
|
292
|
+
"""Fetch markets we've already proposed on to avoid duplicates."""
|
|
293
|
+
result = client.get_my_proposals(limit=100)
|
|
294
|
+
if not result.get("success"):
|
|
295
|
+
return set()
|
|
296
|
+
proposals = result.get("data", result.get("proposals", []))
|
|
297
|
+
if isinstance(proposals, dict):
|
|
298
|
+
proposals = proposals.get("proposals", [])
|
|
299
|
+
return {p.get("marketId") for p in proposals if p.get("marketId")}
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def run_round(client, tag="all", search_size=20, num_proposals=5, page=1):
|
|
303
|
+
"""Fetch markets, filter out already-proposed ones, analyze, and submit."""
|
|
304
|
+
category_label = tag if tag != "all" else "all topics"
|
|
288
305
|
|
|
289
|
-
#
|
|
290
|
-
print("
|
|
291
|
-
|
|
306
|
+
# Get markets we've already proposed on
|
|
307
|
+
print("Checking existing proposals...")
|
|
308
|
+
already_proposed = get_proposed_market_ids(client)
|
|
309
|
+
if already_proposed:
|
|
310
|
+
print(f" Skipping {len(already_proposed)} market(s) with existing proposals")
|
|
311
|
+
|
|
312
|
+
# Fetch markets with category filter
|
|
313
|
+
print(f"Scanning {search_size} {category_label} markets...")
|
|
314
|
+
result = client.get_markets(page=page, limit=search_size, tag=tag)
|
|
292
315
|
|
|
293
316
|
if not result.get("success"):
|
|
294
317
|
print(f"Error: {result.get('error', 'Unknown error')}")
|
|
295
318
|
return
|
|
296
319
|
|
|
297
|
-
|
|
298
|
-
|
|
320
|
+
all_markets = result.get("data", [])
|
|
321
|
+
|
|
322
|
+
# Filter out markets we've already proposed on
|
|
323
|
+
markets = [m for m in all_markets if m.get("id") not in already_proposed]
|
|
324
|
+
print(f"Found {len(all_markets)} markets, {len(markets)} new ones to analyze")
|
|
325
|
+
|
|
326
|
+
if not markets:
|
|
327
|
+
print("No new markets to analyze. Try again later or pick a different category.")
|
|
328
|
+
return
|
|
329
|
+
|
|
299
330
|
print()
|
|
300
331
|
|
|
301
332
|
# Ask AI to analyze and pick the best bets
|
|
302
|
-
print("Asking AI to
|
|
303
|
-
picks, model_name = analyze_markets(markets)
|
|
333
|
+
print(f"Asking AI to pick {num_proposals} best bets...\\n")
|
|
334
|
+
picks, model_name = analyze_markets(markets, num_proposals=num_proposals)
|
|
304
335
|
|
|
305
336
|
if not picks:
|
|
306
337
|
print("No picks from AI. Try again later.")
|
|
@@ -337,6 +368,124 @@ ${options.aiProvider !== 'none' ? `
|
|
|
337
368
|
print("Done! Check your dashboard to review proposals.")
|
|
338
369
|
|
|
339
370
|
|
|
371
|
+
BANNER = """
|
|
372
|
+
\\033[91m .-----.
|
|
373
|
+
_ | o o | _
|
|
374
|
+
| | | w | | |
|
|
375
|
+
|_| '-----' |_|\\033[0m
|
|
376
|
+
|
|
377
|
+
\\033[1m █▀█ █▀█ █ █ █ █▀▀ █ █▀█ █ █
|
|
378
|
+
█▀▀ █ █ █ █ █ █ █▀█ █ █ █
|
|
379
|
+
▀ ▀▀▀ ▀▀▀ ▀ ▀▀▀ ▀▀▀ ▀ ▀ ▀ ▀\\033[0m
|
|
380
|
+
\\033[90m AI-Powered Prediction Market Agent\\033[0m
|
|
381
|
+
"""
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
# --- Interactive prompts ---
|
|
385
|
+
|
|
386
|
+
CATEGORIES = [
|
|
387
|
+
("politics", "Politics"),
|
|
388
|
+
("sports", "Sports"),
|
|
389
|
+
("crypto", "Crypto"),
|
|
390
|
+
("finance", "Finance"),
|
|
391
|
+
("tech", "Tech"),
|
|
392
|
+
("culture", "Culture"),
|
|
393
|
+
("all", "All topics"),
|
|
394
|
+
]
|
|
395
|
+
|
|
396
|
+
SEARCH_SIZES = [20, 40, 60, 100, 200, 400]
|
|
397
|
+
PROPOSAL_COUNTS = [5, 10, 15]
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def ask_category(agent_name):
|
|
401
|
+
print(f"What category would you like {agent_name} to look into?")
|
|
402
|
+
for i, (_, label) in enumerate(CATEGORIES, 1):
|
|
403
|
+
print(f" {i}. {label}")
|
|
404
|
+
print()
|
|
405
|
+
while True:
|
|
406
|
+
try:
|
|
407
|
+
choice = input("Pick a number: ").strip()
|
|
408
|
+
except (EOFError, KeyboardInterrupt):
|
|
409
|
+
return "all"
|
|
410
|
+
if choice.isdigit() and 1 <= int(choice) <= len(CATEGORIES):
|
|
411
|
+
print()
|
|
412
|
+
return CATEGORIES[int(choice) - 1][0]
|
|
413
|
+
print(f" Please enter a number between 1 and {len(CATEGORIES)}")
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def ask_search_size():
|
|
417
|
+
print("How big do you want your search to be?")
|
|
418
|
+
for i, size in enumerate(SEARCH_SIZES, 1):
|
|
419
|
+
print(f" {i}. {size} markets")
|
|
420
|
+
print()
|
|
421
|
+
while True:
|
|
422
|
+
try:
|
|
423
|
+
choice = input("Pick a number [1]: ").strip() or "1"
|
|
424
|
+
except (EOFError, KeyboardInterrupt):
|
|
425
|
+
return SEARCH_SIZES[0]
|
|
426
|
+
if choice.isdigit() and 1 <= int(choice) <= len(SEARCH_SIZES):
|
|
427
|
+
print()
|
|
428
|
+
return SEARCH_SIZES[int(choice) - 1]
|
|
429
|
+
print(f" Please enter a number between 1 and {len(SEARCH_SIZES)}")
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
def ask_num_proposals():
|
|
433
|
+
print("How many proposals do you want?")
|
|
434
|
+
for i, count in enumerate(PROPOSAL_COUNTS, 1):
|
|
435
|
+
print(f" {i}. {count} proposals")
|
|
436
|
+
print()
|
|
437
|
+
while True:
|
|
438
|
+
try:
|
|
439
|
+
choice = input("Pick a number [1]: ").strip() or "1"
|
|
440
|
+
except (EOFError, KeyboardInterrupt):
|
|
441
|
+
return PROPOSAL_COUNTS[0]
|
|
442
|
+
if choice.isdigit() and 1 <= int(choice) <= len(PROPOSAL_COUNTS):
|
|
443
|
+
print()
|
|
444
|
+
return PROPOSAL_COUNTS[int(choice) - 1]
|
|
445
|
+
print(f" Please enter a number between 1 and {len(PROPOSAL_COUNTS)}")
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
# --- Main ---
|
|
449
|
+
|
|
450
|
+
def main():
|
|
451
|
+
if not API_KEY or not API_SECRET:
|
|
452
|
+
print("Error: POLYCLAW_API_KEY and POLYCLAW_API_SECRET must be set in .env")
|
|
453
|
+
return
|
|
454
|
+
${options.aiProvider !== 'none' ? `
|
|
455
|
+
ai_key_name = "${options.aiProvider === 'anthropic' ? 'ANTHROPIC_API_KEY' : 'OPENAI_API_KEY'}"
|
|
456
|
+
if not AI_API_KEY:
|
|
457
|
+
print(f"Error: {ai_key_name} must be set in .env")
|
|
458
|
+
return
|
|
459
|
+
` : ''}
|
|
460
|
+
client = PolyclawClient(API_KEY, API_SECRET, BASE_URL)
|
|
461
|
+
print(BANNER)
|
|
462
|
+
print(" Welcome to Polyclaw!")
|
|
463
|
+
print()
|
|
464
|
+
print(" Your agent: \\033[1m${options.name}\\033[0m")
|
|
465
|
+
print()
|
|
466
|
+
|
|
467
|
+
# Ask user preferences
|
|
468
|
+
tag = ask_category("${options.name}")
|
|
469
|
+
search_size = ask_search_size()
|
|
470
|
+
num_proposals = ask_num_proposals()
|
|
471
|
+
|
|
472
|
+
page = 1
|
|
473
|
+
run_round(client, tag=tag, search_size=search_size, num_proposals=num_proposals, page=page)
|
|
474
|
+
|
|
475
|
+
# Interactive loop
|
|
476
|
+
while True:
|
|
477
|
+
print()
|
|
478
|
+
try:
|
|
479
|
+
reply = input("Press Enter for more proposals, or q to quit: ").strip().lower()
|
|
480
|
+
except (EOFError, KeyboardInterrupt):
|
|
481
|
+
break
|
|
482
|
+
if reply in ("q", "quit", "exit"):
|
|
483
|
+
break
|
|
484
|
+
page += 1
|
|
485
|
+
print()
|
|
486
|
+
run_round(client, tag=tag, search_size=search_size, num_proposals=num_proposals, page=page)
|
|
487
|
+
|
|
488
|
+
|
|
340
489
|
if __name__ == "__main__":
|
|
341
490
|
main()
|
|
342
491
|
`;
|
|
@@ -416,8 +565,14 @@ class PolyclawClient {
|
|
|
416
565
|
return response.json() as T;
|
|
417
566
|
}
|
|
418
567
|
|
|
419
|
-
async getMarkets(page = 1, limit = 20) {
|
|
420
|
-
|
|
568
|
+
async getMarkets(page = 1, limit = 20, tag?: string) {
|
|
569
|
+
let path = \`/api/markets?page=\${page}&limit=\${limit}\`;
|
|
570
|
+
if (tag && tag !== 'all') path += \`&tag=\${tag}\`;
|
|
571
|
+
return this.request<any>('GET', path);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
async getMyProposals(limit = 100) {
|
|
575
|
+
return this.request<any>('GET', \`/api/proposals?limit=\${limit}\`);
|
|
421
576
|
}
|
|
422
577
|
|
|
423
578
|
async submitProposal(proposal: {
|
|
@@ -481,63 +636,186 @@ function findBestPlay(markets: any[]): MarketPlay | null {
|
|
|
481
636
|
return scored[0] || null;
|
|
482
637
|
}
|
|
483
638
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
639
|
+
import * as readline from 'readline';
|
|
640
|
+
|
|
641
|
+
function prompt(question: string): Promise<string> {
|
|
642
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
643
|
+
return new Promise((resolve) => {
|
|
644
|
+
rl.question(question, (answer) => { rl.close(); resolve(answer.trim().toLowerCase()); });
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// --- Interactive prompts ---
|
|
649
|
+
|
|
650
|
+
const BANNER = \`
|
|
651
|
+
\\x1b[91m .-----.
|
|
652
|
+
_ | o o | _
|
|
653
|
+
| | | w | | |
|
|
654
|
+
|_| '-----' |_|\\x1b[0m
|
|
655
|
+
|
|
656
|
+
\\x1b[1m █▀█ █▀█ █ █ █ █▀▀ █ █▀█ █ █
|
|
657
|
+
█▀▀ █ █ █ █ █ █ █▀█ █ █ █
|
|
658
|
+
▀ ▀▀▀ ▀▀▀ ▀ ▀▀▀ ▀▀▀ ▀ ▀ ▀ ▀\\x1b[0m
|
|
659
|
+
\\x1b[90m AI-Powered Prediction Market Agent\\x1b[0m
|
|
660
|
+
\`;
|
|
661
|
+
|
|
662
|
+
const CATEGORIES = [
|
|
663
|
+
['politics', 'Politics'],
|
|
664
|
+
['sports', 'Sports'],
|
|
665
|
+
['crypto', 'Crypto'],
|
|
666
|
+
['finance', 'Finance'],
|
|
667
|
+
['tech', 'Tech'],
|
|
668
|
+
['culture', 'Culture'],
|
|
669
|
+
['all', 'All topics'],
|
|
670
|
+
] as const;
|
|
671
|
+
|
|
672
|
+
const SEARCH_SIZES = [20, 40, 60, 100, 200, 400];
|
|
673
|
+
const PROPOSAL_COUNTS = [5, 10, 15];
|
|
674
|
+
|
|
675
|
+
async function askCategory(agentName: string): Promise<string> {
|
|
676
|
+
console.log(\`What category would you like \${agentName} to look into?\`);
|
|
677
|
+
CATEGORIES.forEach(([, label], i) => console.log(\` \${i + 1}. \${label}\`));
|
|
678
|
+
console.log();
|
|
679
|
+
while (true) {
|
|
680
|
+
const choice = await prompt('Pick a number: ');
|
|
681
|
+
const n = parseInt(choice);
|
|
682
|
+
if (n >= 1 && n <= CATEGORIES.length) { console.log(); return CATEGORIES[n - 1][0]; }
|
|
683
|
+
console.log(\` Please enter a number between 1 and \${CATEGORIES.length}\`);
|
|
488
684
|
}
|
|
685
|
+
}
|
|
489
686
|
|
|
490
|
-
|
|
491
|
-
console.log('
|
|
687
|
+
async function askSearchSize(): Promise<number> {
|
|
688
|
+
console.log('How big do you want your search to be?');
|
|
689
|
+
SEARCH_SIZES.forEach((s, i) => console.log(\` \${i + 1}. \${s} markets\`));
|
|
690
|
+
console.log();
|
|
691
|
+
while (true) {
|
|
692
|
+
const choice = (await prompt('Pick a number [1]: ')) || '1';
|
|
693
|
+
const n = parseInt(choice);
|
|
694
|
+
if (n >= 1 && n <= SEARCH_SIZES.length) { console.log(); return SEARCH_SIZES[n - 1]; }
|
|
695
|
+
console.log(\` Please enter a number between 1 and \${SEARCH_SIZES.length}\`);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
async function askNumProposals(): Promise<number> {
|
|
700
|
+
console.log('How many proposals do you want?');
|
|
701
|
+
PROPOSAL_COUNTS.forEach((c, i) => console.log(\` \${i + 1}. \${c} proposals\`));
|
|
492
702
|
console.log();
|
|
703
|
+
while (true) {
|
|
704
|
+
const choice = (await prompt('Pick a number [1]: ')) || '1';
|
|
705
|
+
const n = parseInt(choice);
|
|
706
|
+
if (n >= 1 && n <= PROPOSAL_COUNTS.length) { console.log(); return PROPOSAL_COUNTS[n - 1]; }
|
|
707
|
+
console.log(\` Please enter a number between 1 and \${PROPOSAL_COUNTS.length}\`);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
493
710
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
711
|
+
async function getProposedMarketIds(client: PolyclawClient): Promise<Set<string>> {
|
|
712
|
+
const result = await client.getMyProposals(100);
|
|
713
|
+
if (!result.success) return new Set();
|
|
714
|
+
const proposals = Array.isArray(result.data) ? result.data : (result.data?.proposals || []);
|
|
715
|
+
return new Set(proposals.map((p: any) => p.marketId).filter(Boolean));
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
async function runRound(client: PolyclawClient, opts: { tag: string; searchSize: number; numProposals: number; page: number }) {
|
|
719
|
+
const categoryLabel = opts.tag !== 'all' ? opts.tag : 'all topics';
|
|
720
|
+
|
|
721
|
+
console.log('Checking existing proposals...');
|
|
722
|
+
const alreadyProposed = await getProposedMarketIds(client);
|
|
723
|
+
if (alreadyProposed.size) console.log(\` Skipping \${alreadyProposed.size} market(s) with existing proposals\`);
|
|
724
|
+
|
|
725
|
+
console.log(\`Scanning \${opts.searchSize} \${categoryLabel} markets...\`);
|
|
726
|
+
const result = await client.getMarkets(opts.page, opts.searchSize, opts.tag);
|
|
497
727
|
|
|
498
728
|
if (!result.success) {
|
|
499
729
|
console.error(\`Error: \${result.error || 'Unknown error'}\`);
|
|
500
730
|
return;
|
|
501
731
|
}
|
|
502
732
|
|
|
503
|
-
const
|
|
504
|
-
|
|
733
|
+
const allMarkets = result.data || [];
|
|
734
|
+
const markets = allMarkets.filter((m: any) => !alreadyProposed.has(m.id));
|
|
735
|
+
console.log(\`Found \${allMarkets.length} markets, \${markets.length} new ones to analyze\\n\`);
|
|
505
736
|
|
|
506
|
-
|
|
507
|
-
|
|
737
|
+
if (!markets.length) {
|
|
738
|
+
console.log('No new markets to analyze. Try again later or pick a different category.');
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
508
741
|
|
|
509
|
-
|
|
742
|
+
// Find top plays using the scoring heuristic
|
|
743
|
+
const scored = markets
|
|
744
|
+
.map((market: any) => {
|
|
745
|
+
const yesPrice = parseFloat(market.outcomePrices?.[0] ?? '0.5');
|
|
746
|
+
const volume = parseFloat(market.volume ?? '0');
|
|
747
|
+
if (yesPrice < 0.05 || yesPrice > 0.95) return null;
|
|
748
|
+
const edge = Math.abs(yesPrice - 0.5);
|
|
749
|
+
const liquidityScore = Math.min(volume / 500000, 1.0);
|
|
750
|
+
const score = edge * 0.6 + liquidityScore * 0.4;
|
|
751
|
+
const outcome = yesPrice < 0.5 ? 'Yes' : 'No';
|
|
752
|
+
const confidence = Math.min(Math.round((0.5 + edge) * 100) / 100, 0.95);
|
|
753
|
+
const reasoning = \`Priced at \${(yesPrice * 100).toFixed(0)}% with $\${volume.toLocaleString()} volume\`;
|
|
754
|
+
return { market, outcome, side: 'BUY', confidence, reasoning, score };
|
|
755
|
+
})
|
|
756
|
+
.filter(Boolean)
|
|
757
|
+
.sort((a: any, b: any) => b.score - a.score)
|
|
758
|
+
.slice(0, opts.numProposals);
|
|
759
|
+
|
|
760
|
+
if (!scored.length) {
|
|
510
761
|
console.log('No good opportunities found right now.');
|
|
511
762
|
return;
|
|
512
763
|
}
|
|
513
764
|
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
765
|
+
console.log(\`Submitting \${scored.length} proposal(s)...\\n\`);
|
|
766
|
+
|
|
767
|
+
for (const pick of scored) {
|
|
768
|
+
console.log(\` -> \${pick.market.question}\`);
|
|
769
|
+
console.log(\` BUY \${pick.outcome} | Confidence: \${(pick.confidence * 100).toFixed(0)}%\`);
|
|
770
|
+
console.log(\` \${pick.reasoning}\`);
|
|
771
|
+
|
|
772
|
+
const proposal = await client.submitProposal({
|
|
773
|
+
marketId: pick.market.id,
|
|
774
|
+
outcome: pick.outcome,
|
|
775
|
+
side: pick.side,
|
|
776
|
+
recommendedAmount: 10.0,
|
|
777
|
+
confidence: pick.confidence,
|
|
778
|
+
reasoning: pick.reasoning,
|
|
779
|
+
model: 'your-model-here',
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
if (proposal.success) {
|
|
783
|
+
console.log(' Submitted!\\n');
|
|
784
|
+
} else {
|
|
785
|
+
console.error(\` Error: \${proposal.error || 'Unknown'}\\n\`);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
console.log('Done! Check your dashboard to review proposals.');
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
async function main() {
|
|
793
|
+
if (!API_KEY || !API_SECRET) {
|
|
794
|
+
console.error('Error: POLYCLAW_API_KEY and POLYCLAW_API_SECRET must be set in .env');
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
const client = new PolyclawClient({ apiKey: API_KEY, apiSecret: API_SECRET, baseUrl: BASE_URL });
|
|
799
|
+
console.log(BANNER);
|
|
800
|
+
console.log(' Welcome to Polyclaw!');
|
|
801
|
+
console.log();
|
|
802
|
+
console.log(\` Your agent: \\x1b[1m${options.name}\\x1b[0m\`);
|
|
520
803
|
console.log();
|
|
521
804
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
// const modelName = aiResponse.model; // e.g. "gpt-4o-2025-01-15"
|
|
526
|
-
console.log('Submitting proposal...');
|
|
527
|
-
const proposal = await client.submitProposal({
|
|
528
|
-
marketId: best.market.id,
|
|
529
|
-
outcome: best.outcome,
|
|
530
|
-
side: best.side,
|
|
531
|
-
recommendedAmount: 10.0,
|
|
532
|
-
confidence: best.confidence,
|
|
533
|
-
reasoning: best.reasoning,
|
|
534
|
-
model: 'your-model-here', // Replace with your AI model name
|
|
535
|
-
});
|
|
805
|
+
const tag = await askCategory('${options.name}');
|
|
806
|
+
const searchSize = await askSearchSize();
|
|
807
|
+
const numProposals = await askNumProposals();
|
|
536
808
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
809
|
+
let page = 1;
|
|
810
|
+
await runRound(client, { tag, searchSize, numProposals, page });
|
|
811
|
+
|
|
812
|
+
while (true) {
|
|
813
|
+
console.log();
|
|
814
|
+
const reply = await prompt('Press Enter for more proposals, or q to quit: ');
|
|
815
|
+
if (['q', 'quit', 'exit'].includes(reply)) break;
|
|
816
|
+
page++;
|
|
817
|
+
console.log();
|
|
818
|
+
await runRound(client, { tag, searchSize, numProposals, page });
|
|
541
819
|
}
|
|
542
820
|
}
|
|
543
821
|
|