polly-gamba 1.0.13 → 1.0.14
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 +2 -6
- package/dist/mcp-server.js +2 -0
- package/dist/position-monitor.d.ts +4 -1
- package/dist/position-monitor.js +23 -7
- package/package.json +1 -4
- package/src/index.ts +2 -7
- package/src/mcp-server.ts +2 -0
- package/src/position-monitor.ts +25 -7
- package/.claude/settings.json +0 -14
- package/.mcp.json +0 -11
- package/service.log +0 -2908
package/dist/index.js
CHANGED
|
@@ -88,6 +88,7 @@ async function main() {
|
|
|
88
88
|
const signals = new coinbase_ws_1.CoinbaseSignalDetector();
|
|
89
89
|
const monitor = new position_monitor_1.PositionMonitor(process.env.REDIS_URL || 'redis://localhost:6379', 'polly');
|
|
90
90
|
monitor.setTrader(anthropicTrader);
|
|
91
|
+
monitor.start();
|
|
91
92
|
anthropicTrader.start();
|
|
92
93
|
ollamaTrader.start();
|
|
93
94
|
expiringTrader.start();
|
|
@@ -99,13 +100,8 @@ async function main() {
|
|
|
99
100
|
});
|
|
100
101
|
signals.start();
|
|
101
102
|
console.log('[polly-gamba] Listening for BTC/ETH price signals (threshold: 0.5% in 60s)...');
|
|
102
|
-
// Position monitor: check every 15 minutes for exits
|
|
103
|
-
const SCAN_INTERVAL_MS = 15 * 60 * 1000; // 15 minutes
|
|
104
|
-
monitor.checkPositions().catch((e) => console.error('[monitor]', e.message));
|
|
105
|
-
setInterval(() => {
|
|
106
|
-
monitor.checkPositions().catch((e) => console.error('[monitor]', e.message));
|
|
107
|
-
}, SCAN_INTERVAL_MS);
|
|
108
103
|
// Autonomous scan on startup and every 15 minutes
|
|
104
|
+
const SCAN_INTERVAL_MS = 15 * 60 * 1000; // 15 minutes
|
|
109
105
|
autonomousScan(anthropicTrader, ollamaTrader).catch((e) => {
|
|
110
106
|
console.error('[error] autonomous scan failed:', e.message);
|
|
111
107
|
});
|
package/dist/mcp-server.js
CHANGED
|
@@ -348,6 +348,8 @@ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (req) => {
|
|
|
348
348
|
throw new Error(`Unknown tool: ${name}`);
|
|
349
349
|
});
|
|
350
350
|
async function main() {
|
|
351
|
+
// Initialize budget key (NX = don't overwrite if already set)
|
|
352
|
+
await redis.set(`${REDIS_PREFIX}:budget`, '500', 'NX');
|
|
351
353
|
const transport = new stdio_js_1.StdioServerTransport();
|
|
352
354
|
await server.connect(transport);
|
|
353
355
|
console.error('[mcp] polly-gamba-paper MCP server started (stdio)');
|
|
@@ -18,7 +18,10 @@ export declare class PositionMonitor {
|
|
|
18
18
|
private prefix;
|
|
19
19
|
private trader;
|
|
20
20
|
setTrader(trader: ClaudeTrader): void;
|
|
21
|
-
|
|
21
|
+
private intervalMs;
|
|
22
|
+
private intervalHandle;
|
|
23
|
+
constructor(redisUrl: string, prefix: string, intervalMs?: number);
|
|
24
|
+
start(): void;
|
|
22
25
|
checkPositions(): Promise<void>;
|
|
23
26
|
private fetchCurrentPrice;
|
|
24
27
|
stop(): void;
|
package/dist/position-monitor.js
CHANGED
|
@@ -12,7 +12,9 @@ class PositionMonitor {
|
|
|
12
12
|
setTrader(trader) {
|
|
13
13
|
this.trader = trader;
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
intervalMs;
|
|
16
|
+
intervalHandle = null;
|
|
17
|
+
constructor(redisUrl, prefix, intervalMs = 15 * 60 * 1000) {
|
|
16
18
|
this.redis = new ioredis_1.default(redisUrl, {
|
|
17
19
|
retryStrategy: (times) => Math.min(times * 500, 5000),
|
|
18
20
|
maxRetriesPerRequest: null,
|
|
@@ -20,6 +22,13 @@ class PositionMonitor {
|
|
|
20
22
|
});
|
|
21
23
|
this.redis.on('error', (e) => console.error('[position-monitor:redis]', e.message));
|
|
22
24
|
this.prefix = prefix;
|
|
25
|
+
this.intervalMs = intervalMs;
|
|
26
|
+
}
|
|
27
|
+
start() {
|
|
28
|
+
this.checkPositions().catch((e) => console.error('[monitor]', e.message));
|
|
29
|
+
this.intervalHandle = setInterval(() => {
|
|
30
|
+
this.checkPositions().catch((e) => console.error('[monitor]', e.message));
|
|
31
|
+
}, this.intervalMs);
|
|
23
32
|
}
|
|
24
33
|
async checkPositions() {
|
|
25
34
|
const raw = await this.redis.lrange(`${this.prefix}:positions`, 0, -1);
|
|
@@ -42,17 +51,20 @@ class PositionMonitor {
|
|
|
42
51
|
console.log(`[position-monitor] 0 open positions to check`);
|
|
43
52
|
return;
|
|
44
53
|
}
|
|
45
|
-
// Deduplicate market fetches
|
|
54
|
+
// Deduplicate market fetches keyed by marketId_outcome
|
|
46
55
|
const marketPriceCache = new Map();
|
|
47
|
-
const
|
|
48
|
-
await Promise.all(
|
|
49
|
-
const
|
|
50
|
-
|
|
56
|
+
const uniqueKeys = [...new Set(toCheck.map(p => `${p.market_id}|${p.outcome}`))];
|
|
57
|
+
await Promise.all(uniqueKeys.map(async (key) => {
|
|
58
|
+
const sep = key.indexOf('|');
|
|
59
|
+
const marketId = key.slice(0, sep);
|
|
60
|
+
const outcome = key.slice(sep + 1);
|
|
61
|
+
const result = await this.fetchCurrentPrice(marketId, outcome).catch(() => null);
|
|
62
|
+
marketPriceCache.set(key, result);
|
|
51
63
|
}));
|
|
52
64
|
// Build review candidates with current price data for Claude
|
|
53
65
|
const reviewCandidates = [];
|
|
54
66
|
for (const pos of toCheck) {
|
|
55
|
-
const currentPrice =
|
|
67
|
+
const currentPrice = marketPriceCache.get(`${pos.market_id}|${pos.outcome}`) ?? null;
|
|
56
68
|
if (!currentPrice)
|
|
57
69
|
continue;
|
|
58
70
|
const entryPrice = pos.price;
|
|
@@ -108,6 +120,10 @@ class PositionMonitor {
|
|
|
108
120
|
}
|
|
109
121
|
}
|
|
110
122
|
stop() {
|
|
123
|
+
if (this.intervalHandle) {
|
|
124
|
+
clearInterval(this.intervalHandle);
|
|
125
|
+
this.intervalHandle = null;
|
|
126
|
+
}
|
|
111
127
|
this.redis.disconnect();
|
|
112
128
|
}
|
|
113
129
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "polly-gamba",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.14",
|
|
4
4
|
"description": "Coinbase price signal → Claude brain → Polymarket CLOB execution",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"polly-gamba": "dist/index.js"
|
|
8
|
-
},
|
|
9
6
|
"scripts": {
|
|
10
7
|
"start": "ts-node src/index.ts",
|
|
11
8
|
"mcp": "ts-node src/mcp/server.ts",
|
package/src/index.ts
CHANGED
|
@@ -65,6 +65,7 @@ async function main(): Promise<void> {
|
|
|
65
65
|
)
|
|
66
66
|
|
|
67
67
|
monitor.setTrader(anthropicTrader)
|
|
68
|
+
monitor.start()
|
|
68
69
|
|
|
69
70
|
anthropicTrader.start()
|
|
70
71
|
ollamaTrader.start()
|
|
@@ -80,14 +81,8 @@ async function main(): Promise<void> {
|
|
|
80
81
|
signals.start()
|
|
81
82
|
console.log('[polly-gamba] Listening for BTC/ETH price signals (threshold: 0.5% in 60s)...')
|
|
82
83
|
|
|
83
|
-
// Position monitor: check every 15 minutes for exits
|
|
84
|
-
const SCAN_INTERVAL_MS = 15 * 60 * 1000 // 15 minutes
|
|
85
|
-
monitor.checkPositions().catch((e: Error) => console.error('[monitor]', e.message))
|
|
86
|
-
setInterval(() => {
|
|
87
|
-
monitor.checkPositions().catch((e: Error) => console.error('[monitor]', e.message))
|
|
88
|
-
}, SCAN_INTERVAL_MS)
|
|
89
|
-
|
|
90
84
|
// Autonomous scan on startup and every 15 minutes
|
|
85
|
+
const SCAN_INTERVAL_MS = 15 * 60 * 1000 // 15 minutes
|
|
91
86
|
autonomousScan(anthropicTrader, ollamaTrader).catch((e: Error) => {
|
|
92
87
|
console.error('[error] autonomous scan failed:', e.message)
|
|
93
88
|
})
|
package/src/mcp-server.ts
CHANGED
|
@@ -348,6 +348,8 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
|
348
348
|
})
|
|
349
349
|
|
|
350
350
|
async function main() {
|
|
351
|
+
// Initialize budget key (NX = don't overwrite if already set)
|
|
352
|
+
await redis.set(`${REDIS_PREFIX}:budget`, '500', 'NX')
|
|
351
353
|
const transport = new StdioServerTransport()
|
|
352
354
|
await server.connect(transport)
|
|
353
355
|
console.error('[mcp] polly-gamba-paper MCP server started (stdio)')
|
package/src/position-monitor.ts
CHANGED
|
@@ -30,7 +30,10 @@ export class PositionMonitor {
|
|
|
30
30
|
this.trader = trader
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
private intervalMs: number
|
|
34
|
+
private intervalHandle: NodeJS.Timeout | null = null
|
|
35
|
+
|
|
36
|
+
constructor(redisUrl: string, prefix: string, intervalMs = 15 * 60 * 1000) {
|
|
34
37
|
this.redis = new Redis(redisUrl, {
|
|
35
38
|
retryStrategy: (times) => Math.min(times * 500, 5000),
|
|
36
39
|
maxRetriesPerRequest: null,
|
|
@@ -38,6 +41,14 @@ export class PositionMonitor {
|
|
|
38
41
|
})
|
|
39
42
|
this.redis.on('error', (e) => console.error('[position-monitor:redis]', e.message))
|
|
40
43
|
this.prefix = prefix
|
|
44
|
+
this.intervalMs = intervalMs
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
start(): void {
|
|
48
|
+
this.checkPositions().catch((e: Error) => console.error('[monitor]', e.message))
|
|
49
|
+
this.intervalHandle = setInterval(() => {
|
|
50
|
+
this.checkPositions().catch((e: Error) => console.error('[monitor]', e.message))
|
|
51
|
+
}, this.intervalMs)
|
|
41
52
|
}
|
|
42
53
|
|
|
43
54
|
async checkPositions(): Promise<void> {
|
|
@@ -61,12 +72,15 @@ export class PositionMonitor {
|
|
|
61
72
|
return
|
|
62
73
|
}
|
|
63
74
|
|
|
64
|
-
// Deduplicate market fetches
|
|
75
|
+
// Deduplicate market fetches keyed by marketId_outcome
|
|
65
76
|
const marketPriceCache = new Map<string, MarketPrice | null>()
|
|
66
|
-
const
|
|
67
|
-
await Promise.all(
|
|
68
|
-
const
|
|
69
|
-
|
|
77
|
+
const uniqueKeys = [...new Set(toCheck.map(p => `${p.market_id}|${p.outcome}`))]
|
|
78
|
+
await Promise.all(uniqueKeys.map(async (key) => {
|
|
79
|
+
const sep = key.indexOf('|')
|
|
80
|
+
const marketId = key.slice(0, sep)
|
|
81
|
+
const outcome = key.slice(sep + 1)
|
|
82
|
+
const result = await this.fetchCurrentPrice(marketId, outcome).catch(() => null)
|
|
83
|
+
marketPriceCache.set(key, result)
|
|
70
84
|
}))
|
|
71
85
|
|
|
72
86
|
// Build review candidates with current price data for Claude
|
|
@@ -83,7 +97,7 @@ export class PositionMonitor {
|
|
|
83
97
|
}> = []
|
|
84
98
|
|
|
85
99
|
for (const pos of toCheck) {
|
|
86
|
-
const currentPrice =
|
|
100
|
+
const currentPrice = marketPriceCache.get(`${pos.market_id}|${pos.outcome}`) ?? null
|
|
87
101
|
if (!currentPrice) continue
|
|
88
102
|
|
|
89
103
|
const entryPrice = pos.price
|
|
@@ -146,6 +160,10 @@ export class PositionMonitor {
|
|
|
146
160
|
}
|
|
147
161
|
|
|
148
162
|
stop(): void {
|
|
163
|
+
if (this.intervalHandle) {
|
|
164
|
+
clearInterval(this.intervalHandle)
|
|
165
|
+
this.intervalHandle = null
|
|
166
|
+
}
|
|
149
167
|
this.redis.disconnect()
|
|
150
168
|
}
|
|
151
169
|
}
|
package/.claude/settings.json
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"mcpServers": {
|
|
3
|
-
"polly-paper": {
|
|
4
|
-
"command": "npx",
|
|
5
|
-
"args": ["ts-node", "/Users/feral/polly-gamba/src/mcp-server.ts"],
|
|
6
|
-
"env": {
|
|
7
|
-
"REDIS_URL": "redis://localhost:6379"
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
|
-
},
|
|
11
|
-
"permissions": {
|
|
12
|
-
"allow": ["mcp__polly-paper__place_order", "mcp__polly-paper__skip_market"]
|
|
13
|
-
}
|
|
14
|
-
}
|