Skip to content

Commit 7fae2ce

Browse files
alsk1992claude
andcommitted
Add bot state persistence, safe streaming, and strategy builder
## Bot State Persistence (state.ts) - Checkpoint price history for technical indicators - Save/restore positions between restarts - Strategy parameter versioning with notes - Auto-recovery on bot restart ## Safe Trading Stream (stream.ts) - Privacy levels: public, obscured, private - Auto-sanitize sensitive data (addresses, keys, etc) - Broadcast to Discord, Slack, Telegram, webhooks - Configurable what to show (prices, sizes, PnL) ## Strategy Builder (builder.ts) - Create bots via natural language - Templates: mean_reversion, momentum, arbitrage, etc - Validation before live trading - Always starts in dry-run mode ## New Commands - /strategy create|list|delete|templates - /stream on|off|privacy|webhook|discord|slack Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent b17a9ea commit 7fae2ce

5 files changed

Lines changed: 1707 additions & 0 deletions

File tree

src/commands/registry.ts

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1339,6 +1339,218 @@ export function createDefaultCommands(): CommandDefinition[] {
13391339
}
13401340
},
13411341
},
1342+
{
1343+
name: 'strategy',
1344+
description: 'Create or manage trading strategies',
1345+
usage: '/strategy [create|list|delete|templates] [args]',
1346+
aliases: ['strat'],
1347+
handler: async (args, ctx) => {
1348+
const trading = (ctx as any).trading;
1349+
if (!trading?.builder) {
1350+
return 'Strategy builder not initialized.';
1351+
}
1352+
1353+
const parts = args.trim().split(/\s+/);
1354+
const subcommand = parts[0]?.toLowerCase() || 'list';
1355+
const rest = parts.slice(1).join(' ');
1356+
1357+
switch (subcommand) {
1358+
case 'templates': {
1359+
const templates = trading.builder.listTemplates();
1360+
const lines = ['Available Strategy Templates', ''];
1361+
for (const tmpl of templates) {
1362+
lines.push(`**${tmpl.name}**`);
1363+
lines.push(` ${tmpl.description}`);
1364+
}
1365+
lines.push('', 'Use: /strategy create <description>');
1366+
return lines.join('\n');
1367+
}
1368+
1369+
case 'create': {
1370+
if (!rest) {
1371+
return [
1372+
'Usage: /strategy create <natural language description>',
1373+
'',
1374+
'Examples:',
1375+
' /strategy create buy the dip on polymarket when price drops 5%',
1376+
' /strategy create momentum strategy with 10% take profit',
1377+
' /strategy create arbitrage between polymarket and kalshi',
1378+
].join('\n');
1379+
}
1380+
1381+
const result = trading.builder.parseNaturalLanguage(rest);
1382+
if ('error' in result) {
1383+
return `Error: ${result.error}`;
1384+
}
1385+
1386+
const validation = trading.builder.validate(result);
1387+
if (!validation.valid) {
1388+
return `Validation errors:\n${validation.errors.map((e: string) => `- ${e}`).join('\n')}`;
1389+
}
1390+
1391+
// Save the definition
1392+
const defId = trading.builder.saveDefinition(ctx.session.userId, result);
1393+
1394+
// Create and register the strategy
1395+
const strategy = trading.builder.createStrategy(result);
1396+
trading.bots.registerStrategy(strategy);
1397+
1398+
return [
1399+
`Strategy Created: ${result.name}`,
1400+
'',
1401+
`ID: ${strategy.config.id}`,
1402+
`Template: ${result.template}`,
1403+
`Platforms: ${result.platforms.join(', ')}`,
1404+
'',
1405+
'Entry conditions:',
1406+
...result.entry.map((e: any) => ` - ${e.type}: ${e.value}`),
1407+
'',
1408+
'Exit conditions:',
1409+
...result.exit.map((e: any) => ` - ${e.type}: ${e.value}`),
1410+
'',
1411+
'Risk:',
1412+
` - Max position: $${result.risk.maxPositionSize}`,
1413+
` - Stop loss: ${result.risk.stopLossPct}%`,
1414+
` - Take profit: ${result.risk.takeProfitPct}%`,
1415+
'',
1416+
'Mode: DRY RUN (use /bot start to begin)',
1417+
'',
1418+
`Use /bot start ${strategy.config.id} to start trading.`,
1419+
].join('\n');
1420+
}
1421+
1422+
case 'list': {
1423+
const definitions = trading.builder.loadDefinitions(ctx.session.userId);
1424+
if (definitions.length === 0) {
1425+
return 'No strategies saved. Use /strategy create to create one.';
1426+
}
1427+
1428+
const lines = ['Your Strategies', ''];
1429+
for (const def of definitions) {
1430+
lines.push(`**${def.definition.name}** (${def.id})`);
1431+
lines.push(` Template: ${def.definition.template} | Platforms: ${def.definition.platforms.join(', ')}`);
1432+
lines.push(` Created: ${def.createdAt.toLocaleDateString()}`);
1433+
}
1434+
return lines.join('\n');
1435+
}
1436+
1437+
case 'delete': {
1438+
if (!rest) {
1439+
return 'Usage: /strategy delete <strategy-id>';
1440+
}
1441+
const deleted = trading.builder.deleteDefinition(ctx.session.userId, rest);
1442+
return deleted ? `Strategy ${rest} deleted.` : `Strategy ${rest} not found.`;
1443+
}
1444+
1445+
default:
1446+
return [
1447+
'Usage: /strategy [command]',
1448+
'',
1449+
'Commands:',
1450+
' create <description> - Create strategy from natural language',
1451+
' list - List your strategies',
1452+
' delete <id> - Delete a strategy',
1453+
' templates - Show available templates',
1454+
].join('\n');
1455+
}
1456+
},
1457+
},
1458+
{
1459+
name: 'stream',
1460+
description: 'Configure trading activity streaming',
1461+
usage: '/stream [on|off|status|channel] [args]',
1462+
handler: async (args, ctx) => {
1463+
const trading = (ctx as any).trading;
1464+
if (!trading?.stream) {
1465+
return 'Trading stream not initialized.';
1466+
}
1467+
1468+
const parts = args.trim().split(/\s+/);
1469+
const subcommand = parts[0]?.toLowerCase() || 'status';
1470+
const rest = parts.slice(1);
1471+
1472+
switch (subcommand) {
1473+
case 'status': {
1474+
const config = trading.stream.getConfig();
1475+
return [
1476+
'Trading Stream Status',
1477+
'',
1478+
`Privacy: ${config.privacy}`,
1479+
`Channels: ${config.channels.length}`,
1480+
`Events: ${config.events.join(', ')}`,
1481+
'',
1482+
'Privacy settings:',
1483+
` Show platforms: ${config.showPlatforms}`,
1484+
` Show markets: ${config.showMarkets}`,
1485+
` Show exact prices: ${config.showExactPrices}`,
1486+
` Show sizes: ${config.showSizes}`,
1487+
` Show PnL amounts: ${config.showPnL}`,
1488+
].join('\n');
1489+
}
1490+
1491+
case 'on': {
1492+
// Subscribe to console output
1493+
trading.stream.addChannel({ type: 'console', id: 'console' });
1494+
return 'Streaming enabled (console output). Use /stream webhook <url> to add webhook.';
1495+
}
1496+
1497+
case 'off': {
1498+
trading.stream.removeChannel('console');
1499+
return 'Streaming disabled.';
1500+
}
1501+
1502+
case 'privacy': {
1503+
const level = rest[0]?.toLowerCase();
1504+
if (!level || !['public', 'obscured', 'private'].includes(level)) {
1505+
return 'Usage: /stream privacy [public|obscured|private]';
1506+
}
1507+
trading.stream.configure({ privacy: level });
1508+
return `Privacy set to: ${level}`;
1509+
}
1510+
1511+
case 'webhook': {
1512+
const url = rest[0];
1513+
if (!url || !url.startsWith('http')) {
1514+
return 'Usage: /stream webhook <url>';
1515+
}
1516+
trading.stream.addChannel({ type: 'webhook', id: `wh_${Date.now()}`, webhookUrl: url });
1517+
return `Webhook added: ${url}`;
1518+
}
1519+
1520+
case 'discord': {
1521+
const url = rest[0];
1522+
if (!url || !url.includes('discord')) {
1523+
return 'Usage: /stream discord <webhook-url>';
1524+
}
1525+
trading.stream.addChannel({ type: 'discord', id: `discord_${Date.now()}`, webhookUrl: url });
1526+
return 'Discord webhook added.';
1527+
}
1528+
1529+
case 'slack': {
1530+
const url = rest[0];
1531+
if (!url || !url.includes('slack')) {
1532+
return 'Usage: /stream slack <webhook-url>';
1533+
}
1534+
trading.stream.addChannel({ type: 'slack', id: `slack_${Date.now()}`, webhookUrl: url });
1535+
return 'Slack webhook added.';
1536+
}
1537+
1538+
default:
1539+
return [
1540+
'Usage: /stream [command]',
1541+
'',
1542+
'Commands:',
1543+
' status - Show stream configuration',
1544+
' on - Enable console streaming',
1545+
' off - Disable streaming',
1546+
' privacy <level> - Set privacy (public/obscured/private)',
1547+
' webhook <url> - Add webhook endpoint',
1548+
' discord <url> - Add Discord webhook',
1549+
' slack <url> - Add Slack webhook',
1550+
].join('\n');
1551+
}
1552+
},
1553+
},
13421554
{
13431555
name: 'trades',
13441556
description: 'View trade history and stats',

0 commit comments

Comments
 (0)