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 +1 -1
- package/src/claude-trader.ts +7 -11
- package/src/position-monitor.ts +41 -60
package/package.json
CHANGED
package/src/claude-trader.ts
CHANGED
|
@@ -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
|
-
|
|
292
|
-
|
|
291
|
+
entry_price: number
|
|
292
|
+
current_price: number
|
|
293
|
+
gain_pct: number
|
|
293
294
|
size_usdc: number
|
|
294
|
-
|
|
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.
|
|
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
|
-
-
|
|
310
|
-
-
|
|
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()}
|
package/src/position-monitor.ts
CHANGED
|
@@ -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
|
-
|
|
67
|
-
|
|
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
|
|
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
|
|
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
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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}
|
|
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> {
|