polly-gamba 1.0.5 → 1.0.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "polly-gamba",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "Coinbase price signal → Claude brain → Polymarket CLOB execution",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -288,13 +288,12 @@ For EVERY market above: if price differs from fair probability by >8%, place a t
288
288
  market_id: string
289
289
  market_question: string
290
290
  outcome: string
291
- side: string
292
- price: number
291
+ entry_price: number
292
+ current_price: number
293
+ gain_pct: number
293
294
  size_usdc: number
294
- exit_trigger?: string
295
+ hours_to_expiry: number
295
296
  ts: number
296
- current_price?: number
297
- hours_to_expiry?: number
298
297
  }>) {
299
298
  const prefix = this.config.redisPrefix
300
299
 
@@ -302,13 +301,10 @@ For EVERY market above: if price differs from fair probability by >8%, place a t
302
301
  await this.redis.ltrim(`${prefix}:reviews`, 0, 9999)
303
302
 
304
303
  const positionLines = positions.map(p => {
305
- const gainPct = p.current_price != null && p.price > 0
306
- ? (((p.current_price - p.price) / p.price) * 100).toFixed(1)
307
- : 'N/A'
304
+ const gainPct = (p.gain_pct * 100).toFixed(1)
308
305
  return `### ${p.market_id} ${p.market_question}
309
- - Side: ${p.side} ${p.outcome} | Entry: ${p.price} | Now: ${p.current_price ?? 'N/A'} | Gain: ${gainPct}%
310
- - Exit trigger: ${p.exit_trigger || '(none set)'}
311
- - Size: $${p.size_usdc} | Hours to expiry: ${p.hours_to_expiry ?? 'N/A'}h`
306
+ - Outcome: ${p.outcome} | Entry: ${p.entry_price} | Now: ${p.current_price} | Gain: ${gainPct}%
307
+ - Size: $${p.size_usdc} | Hours to expiry: ${p.hours_to_expiry.toFixed(1)}h`
312
308
  }).join('\n\n')
313
309
 
314
310
  const prompt = `## Position Review — ${new Date().toISOString()}
@@ -1,4 +1,5 @@
1
1
  import Redis from 'ioredis'
2
+ import { ClaudeTrader } from './claude-trader'
2
3
 
3
4
  export interface Position {
4
5
  market_id: string
@@ -23,6 +24,11 @@ interface MarketPrice {
23
24
  export class PositionMonitor {
24
25
  private redis: Redis
25
26
  private prefix: string
27
+ private trader: ClaudeTrader | null = null
28
+
29
+ setTrader(trader: ClaudeTrader) {
30
+ this.trader = trader
31
+ }
26
32
 
27
33
  constructor(redisUrl: string, prefix: string) {
28
34
  this.redis = new Redis(redisUrl, {
@@ -63,78 +69,53 @@ export class PositionMonitor {
63
69
  marketPriceCache.set(marketId, result)
64
70
  }))
65
71
 
66
- const counts = { take_profit: 0, stop_loss: 0, expiry: 0 }
67
- let closedCount = 0
72
+ // Build review candidates with current price data for Claude
73
+ const reviewCandidates: Array<{
74
+ market_id: string
75
+ market_question: string
76
+ outcome: string
77
+ entry_price: number
78
+ current_price: number
79
+ gain_pct: number
80
+ size_usdc: number
81
+ hours_to_expiry: number
82
+ ts: number
83
+ }> = []
68
84
 
69
85
  for (const pos of toCheck) {
70
- const marketData = marketPriceCache.get(pos.market_id)
71
- if (!marketData) continue
72
-
73
- const outcome = pos.outcome
74
- const currentPrice = await this.fetchCurrentPrice(pos.market_id, outcome)
86
+ const currentPrice = await this.fetchCurrentPrice(pos.market_id, pos.outcome)
75
87
  if (!currentPrice) continue
76
88
 
77
89
  const entryPrice = pos.price
78
- const exitPrice = currentPrice.price
79
- const gain = (exitPrice - entryPrice) / entryPrice
80
-
81
- let reason: Position['reason'] | null = null
82
-
83
- // Check exit conditions
90
+ const gain = (currentPrice.price - entryPrice) / entryPrice
84
91
  const hoursToEnd = (new Date(currentPrice.endDate).getTime() - Date.now()) / (1000 * 60 * 60)
85
- if (hoursToEnd <= 48) {
86
- reason = 'expiry'
87
- } else if (gain >= 0.15) {
88
- reason = 'take_profit'
89
- } else if (gain <= -0.20) {
90
- reason = 'stop_loss'
91
- }
92
92
 
93
- if (!reason) continue
94
-
95
- // Calculate P&L
96
- const shares = pos.size_usdc / entryPrice
97
- const pnl = shares * exitPrice - pos.size_usdc
98
-
99
- // Build closed position record
100
- const closed: Position = {
101
- ...pos,
102
- status: 'closed',
103
- exit_price: exitPrice,
104
- pnl,
105
- closed_at: Date.now(),
106
- reason,
93
+ // Only send to Claude if something notable: moved >5% either way, or <72h to expiry
94
+ if (Math.abs(gain) >= 0.05 || hoursToEnd <= 72) {
95
+ reviewCandidates.push({
96
+ market_id: pos.market_id,
97
+ market_question: pos.market_question,
98
+ outcome: pos.outcome,
99
+ entry_price: entryPrice,
100
+ current_price: currentPrice.price,
101
+ gain_pct: gain,
102
+ size_usdc: pos.size_usdc,
103
+ hours_to_expiry: hoursToEnd,
104
+ ts: pos.ts,
105
+ })
107
106
  }
108
-
109
- // Track in closed_ids set
110
- const posKey = `${pos.market_id}_${pos.outcome}_${pos.ts}`
111
- await this.redis.sadd(`${this.prefix}:closed_ids`, posKey)
112
-
113
- // Push to closed_positions history
114
- await this.redis.lpush(`${this.prefix}:closed_positions`, JSON.stringify(closed))
115
- await this.redis.ltrim(`${this.prefix}:closed_positions`, 0, 9999)
116
-
117
- // Log entry
118
- await this.redis.lpush(`${this.prefix}:log`, JSON.stringify({
119
- type: 'position_closed',
120
- data: closed,
121
- ts: Date.now(),
122
- }))
123
- await this.redis.ltrim(`${this.prefix}:log`, 0, 9999)
124
-
125
- counts[reason]++
126
- closedCount++
127
-
128
- console.log(
129
- `[position-monitor] CLOSE ${reason.toUpperCase()} "${String(pos.market_question).slice(0, 50)}" ` +
130
- `${pos.outcome} entry=${entryPrice.toFixed(3)} exit=${exitPrice.toFixed(3)} pnl=${pnl >= 0 ? '+' : ''}${pnl.toFixed(2)}`
131
- )
132
107
  }
133
108
 
134
109
  console.log(
135
- `[position-monitor] checked=${toCheck.length} closed=${closedCount} ` +
136
- `(take_profit=${counts.take_profit} stop_loss=${counts.stop_loss} expiry=${counts.expiry})`
110
+ `[position-monitor] checked=${toCheck.length} review_candidates=${reviewCandidates.length} (moved>5% or <72h expiry)`
137
111
  )
112
+
113
+ // Pass to Claude for judgment-based exits with web search
114
+ if (reviewCandidates.length > 0 && this.trader) {
115
+ await this.trader.onPositionReview(reviewCandidates)
116
+ } else if (reviewCandidates.length > 0) {
117
+ console.log('[position-monitor] no trader set — skipping Claude review')
118
+ }
138
119
  }
139
120
 
140
121
  private async fetchCurrentPrice(marketId: string, outcome: string): Promise<MarketPrice | null> {