@exagent/agent 0.3.6 → 0.3.8

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.
Files changed (69) hide show
  1. package/dist/chunk-7UGLJO6W.js +6392 -0
  2. package/dist/chunk-EHAOPCTJ.js +6406 -0
  3. package/dist/chunk-FGMXTW5I.js +6540 -0
  4. package/dist/chunk-GYYW4EKM.js +6756 -0
  5. package/dist/chunk-IVA2SCSN.js +6756 -0
  6. package/dist/chunk-JHXCSGPC.js +6352 -0
  7. package/dist/chunk-V6O4UXVN.js +6345 -0
  8. package/dist/chunk-WTECTX2Z.js +6345 -0
  9. package/dist/cli.js +2 -2
  10. package/dist/index.d.ts +24 -2
  11. package/dist/index.js +1 -1
  12. package/package.json +12 -9
  13. package/src/bridge/across.ts +0 -240
  14. package/src/bridge/bridge-manager.ts +0 -87
  15. package/src/bridge/index.ts +0 -9
  16. package/src/bridge/types.ts +0 -77
  17. package/src/chains.ts +0 -105
  18. package/src/cli.ts +0 -250
  19. package/src/config.ts +0 -502
  20. package/src/diagnostics.ts +0 -335
  21. package/src/index.ts +0 -98
  22. package/src/llm/anthropic.ts +0 -63
  23. package/src/llm/base.ts +0 -264
  24. package/src/llm/deepseek.ts +0 -48
  25. package/src/llm/google.ts +0 -63
  26. package/src/llm/groq.ts +0 -48
  27. package/src/llm/index.ts +0 -42
  28. package/src/llm/mistral.ts +0 -48
  29. package/src/llm/ollama.ts +0 -52
  30. package/src/llm/openai.ts +0 -94
  31. package/src/llm/together.ts +0 -48
  32. package/src/llm-providers.ts +0 -8
  33. package/src/logger.ts +0 -137
  34. package/src/paper/executor.ts +0 -201
  35. package/src/paper/index.ts +0 -1
  36. package/src/perp/client.ts +0 -200
  37. package/src/perp/index.ts +0 -12
  38. package/src/perp/msgpack.ts +0 -272
  39. package/src/perp/orders.ts +0 -234
  40. package/src/perp/positions.ts +0 -126
  41. package/src/perp/signer.ts +0 -277
  42. package/src/perp/types.ts +0 -192
  43. package/src/perp/websocket.ts +0 -274
  44. package/src/position-tracker.ts +0 -243
  45. package/src/prediction/client.ts +0 -288
  46. package/src/prediction/index.ts +0 -3
  47. package/src/prediction/order-manager.ts +0 -297
  48. package/src/prediction/types.ts +0 -151
  49. package/src/relay.ts +0 -254
  50. package/src/runtime.ts +0 -1755
  51. package/src/scrub-secrets.ts +0 -39
  52. package/src/setup.ts +0 -392
  53. package/src/signal.ts +0 -212
  54. package/src/spot/aerodrome.ts +0 -158
  55. package/src/spot/client.ts +0 -138
  56. package/src/spot/index.ts +0 -11
  57. package/src/spot/swap-manager.ts +0 -219
  58. package/src/spot/types.ts +0 -203
  59. package/src/spot/uniswap.ts +0 -150
  60. package/src/store.ts +0 -50
  61. package/src/strategy/index.ts +0 -2
  62. package/src/strategy/loader.ts +0 -265
  63. package/src/strategy/templates.ts +0 -74
  64. package/src/trading/index.ts +0 -2
  65. package/src/trading/market.ts +0 -120
  66. package/src/trading/risk.ts +0 -107
  67. package/src/ui.ts +0 -75
  68. package/test/strategy-loader.test.ts +0 -150
  69. package/tsconfig.json +0 -8
@@ -1,74 +0,0 @@
1
- import type { StrategyTemplate } from '@exagent/sdk';
2
-
3
- const templates: StrategyTemplate[] = [
4
- {
5
- id: 'momentum',
6
- name: 'Momentum Trader',
7
- description: 'Identifies tokens with strong upward price momentum and rides the trend. Uses LLM to analyze price action and market sentiment.',
8
- category: 'momentum',
9
- venues: ['hyperliquid_perp', 'hyperliquid_spot'],
10
- riskLevel: 'moderate',
11
- systemPrompt: `You are a momentum trading agent. Analyze the provided market data and identify tokens with strong upward momentum.
12
-
13
- Rules:
14
- - Only trade tokens with clear directional momentum (avoid choppy markets)
15
- - Use trailing stops to protect gains
16
- - Size positions based on conviction (higher confidence = larger position)
17
- - Cut losses quickly if momentum reverses
18
- - Consider volume as confirmation of momentum
19
-
20
- Return a JSON array of trade signals.`,
21
- },
22
- {
23
- id: 'value',
24
- name: 'Value Investor',
25
- description: 'Identifies undervalued tokens based on fundamental analysis. Buys dips and holds for mean reversion.',
26
- category: 'value',
27
- venues: ['hyperliquid_spot'],
28
- riskLevel: 'conservative',
29
- systemPrompt: `You are a value investing agent. Identify tokens trading below their intrinsic value.
30
-
31
- Rules:
32
- - Focus on established tokens with strong fundamentals
33
- - Buy on significant dips (>10% from recent highs)
34
- - Hold positions longer (swing/position timeframe)
35
- - Avoid chasing pumps
36
- - Diversify across sectors
37
-
38
- Return a JSON array of trade signals.`,
39
- },
40
- {
41
- id: 'arbitrage',
42
- name: 'Arbitrage Hunter',
43
- description: 'Scans for price discrepancies across venues and chains. Executes quickly to capture spreads.',
44
- category: 'arbitrage',
45
- venues: ['hyperliquid_perp', 'hyperliquid_spot', 'uniswap', 'aerodrome'],
46
- riskLevel: 'aggressive',
47
- systemPrompt: `You are an arbitrage agent. Find price discrepancies between venues.
48
-
49
- Rules:
50
- - Speed is critical — execute quickly before spreads close
51
- - Account for fees and slippage in profitability calculations
52
- - Only trade when net profit > 0.5% after all costs
53
- - Monitor cross-chain opportunities (CEX vs DEX spreads)
54
-
55
- Return a JSON array of trade signals.`,
56
- },
57
- {
58
- id: 'hold',
59
- name: 'Hold (No Trading)',
60
- description: 'Passive strategy that makes no trades. Useful for monitoring only.',
61
- category: 'custom',
62
- venues: [],
63
- riskLevel: 'conservative',
64
- systemPrompt: '',
65
- },
66
- ];
67
-
68
- export function getTemplate(id: string): StrategyTemplate | undefined {
69
- return templates.find(t => t.id === id);
70
- }
71
-
72
- export function listTemplates(): StrategyTemplate[] {
73
- return [...templates];
74
- }
@@ -1,2 +0,0 @@
1
- export { RiskManager } from './risk.js';
2
- export { MarketDataService } from './market.js';
@@ -1,120 +0,0 @@
1
- import type { MarketData, OHLCV } from '@exagent/sdk';
2
-
3
- export class MarketDataService implements MarketData {
4
- private prices: Record<string, number> = {};
5
- private lastFetch = 0;
6
- private cacheTTL: number;
7
- private apiUrl: string | null;
8
- private ohlcvCache: Map<string, { data: OHLCV[]; fetchedAt: number }> = new Map();
9
-
10
- constructor(cacheTTLMs: number = 30000, apiUrl?: string) {
11
- this.cacheTTL = cacheTTLMs;
12
- this.apiUrl = apiUrl || null;
13
- }
14
-
15
- async refreshPrices(symbols: string[]): Promise<Record<string, number>> {
16
- if (symbols.length === 0) return this.prices;
17
-
18
- const now = Date.now();
19
- if (now - this.lastFetch < this.cacheTTL && Object.keys(this.prices).length > 0) {
20
- return this.prices;
21
- }
22
-
23
- // Try API first (if relay URL is configured)
24
- if (this.apiUrl) {
25
- try {
26
- const res = await fetch(`${this.apiUrl}/v1/prices`);
27
- if (res.ok) {
28
- const data = await res.json() as Record<string, number>;
29
- for (const [symbol, price] of Object.entries(data)) {
30
- this.prices[symbol.toUpperCase()] = price;
31
- }
32
- this.lastFetch = now;
33
- return this.prices;
34
- }
35
- } catch {
36
- // Fall through to CoinGecko
37
- }
38
- }
39
-
40
- // Fallback: CoinGecko (free, no API key)
41
- try {
42
- const ids = symbols.map(s => s.toLowerCase()).join(',');
43
- const res = await fetch(
44
- `https://api.coingecko.com/api/v3/simple/price?ids=${ids}&vs_currencies=usd`,
45
- );
46
-
47
- if (res.ok) {
48
- const data = await res.json() as Record<string, { usd: number }>;
49
- for (const [id, price] of Object.entries(data)) {
50
- this.prices[id.toUpperCase()] = price.usd;
51
- }
52
- this.lastFetch = now;
53
- }
54
- } catch (err) {
55
- console.warn('[market] Price fetch failed:', (err as Error).message);
56
- }
57
-
58
- return this.prices;
59
- }
60
-
61
- getPrice(symbol: string): number | undefined {
62
- return this.prices[symbol.toUpperCase()];
63
- }
64
-
65
- getPrices(): Record<string, number> {
66
- return { ...this.prices };
67
- }
68
-
69
- setPrice(symbol: string, price: number): void {
70
- this.prices[symbol.toUpperCase()] = price;
71
- }
72
-
73
- setPrices(prices: Record<string, number>): void {
74
- for (const [symbol, price] of Object.entries(prices)) {
75
- this.prices[symbol.toUpperCase()] = price;
76
- }
77
- }
78
-
79
- getOHLCV(symbol: string, timeframe: string): OHLCV[] {
80
- const cacheKey = `${symbol.toUpperCase()}:${timeframe}`;
81
- const cached = this.ohlcvCache.get(cacheKey);
82
- if (cached && Date.now() - cached.fetchedAt < this.cacheTTL) {
83
- return cached.data;
84
- }
85
-
86
- // Trigger async fetch (non-blocking) — returns cached or empty on first call
87
- this.fetchOHLCV(symbol, timeframe).catch(() => {});
88
- return cached?.data || [];
89
- }
90
-
91
- private async fetchOHLCV(symbol: string, timeframe: string): Promise<void> {
92
- const cacheKey = `${symbol.toUpperCase()}:${timeframe}`;
93
-
94
- // Try API first
95
- if (this.apiUrl) {
96
- try {
97
- const res = await fetch(
98
- `${this.apiUrl}/v1/prices/${symbol.toUpperCase()}/ohlcv?interval=${timeframe}&limit=100`,
99
- );
100
- if (res.ok) {
101
- const bars = await res.json() as Array<{ t: number; o: number; h: number; l: number; c: number; v: number }>;
102
- const ohlcv: OHLCV[] = bars.map((b) => ({
103
- timestamp: b.t,
104
- open: b.o,
105
- high: b.h,
106
- low: b.l,
107
- close: b.c,
108
- volume: b.v,
109
- }));
110
- this.ohlcvCache.set(cacheKey, { data: ohlcv, fetchedAt: Date.now() });
111
- return;
112
- }
113
- } catch {
114
- // Fall through
115
- }
116
- }
117
-
118
- // No fallback for OHLCV — requires API
119
- }
120
- }
@@ -1,107 +0,0 @@
1
- import type { TradeSignal, RiskParams, MarketData } from '@exagent/sdk';
2
-
3
- export class RiskManager {
4
- private params: RiskParams;
5
- private dailyPnL = 0;
6
- private dailyFees = 0;
7
- private lastResetDate: string = '';
8
- private initialCapitalUSD: number;
9
-
10
- constructor(params: RiskParams, initialCapitalUSD: number = 10000) {
11
- this.params = params;
12
- this.initialCapitalUSD = initialCapitalUSD;
13
- this.resetIfNewDay();
14
- }
15
-
16
- filterSignals(signals: TradeSignal[], market: MarketData, openPositionCount: number): TradeSignal[] {
17
- this.resetIfNewDay();
18
-
19
- if (this.isDailyLossLimitHit()) {
20
- console.log('[risk] Daily loss limit hit — blocking all trades');
21
- return [];
22
- }
23
-
24
- return signals.filter(signal => this.validateSignal(signal, market, openPositionCount));
25
- }
26
-
27
- private validateSignal(signal: TradeSignal, market: MarketData, openPositionCount: number): boolean {
28
- // Confidence threshold
29
- const threshold = this.params.confidenceThreshold ?? 0.5;
30
- if (signal.confidence !== undefined && signal.confidence < threshold) {
31
- console.log(`[risk] Blocked ${signal.symbol}: confidence ${signal.confidence} < ${threshold}`);
32
- return false;
33
- }
34
-
35
- // Position size limit — use leverage-adjusted notional exposure
36
- // A 10x leveraged $5k position = $50k notional exposure
37
- const leverage = signal.leverage || 1;
38
- const tradeValue = signal.size * signal.price * leverage;
39
- const maxPositionValue = (this.params.maxPositionSizeBps / 10000) * this.initialCapitalUSD;
40
- if (tradeValue > maxPositionValue) {
41
- console.log(`[risk] Blocked ${signal.symbol}: notional $${tradeValue.toFixed(0)} (${leverage}x leverage) > max $${maxPositionValue.toFixed(0)}`);
42
- return false;
43
- }
44
-
45
- // Min trade value
46
- if (tradeValue < this.params.minTradeValueUSD) {
47
- console.log(`[risk] Blocked ${signal.symbol}: trade $${tradeValue.toFixed(2)} < min $${this.params.minTradeValueUSD}`);
48
- return false;
49
- }
50
-
51
- // Max concurrent positions (only for buys/longs)
52
- if ((signal.side === 'buy' || signal.side === 'long') && openPositionCount >= this.params.maxConcurrentPositions) {
53
- console.log(`[risk] Blocked ${signal.symbol}: ${openPositionCount} positions >= max ${this.params.maxConcurrentPositions}`);
54
- return false;
55
- }
56
-
57
- // Max slippage
58
- if (signal.orderType === 'market') {
59
- const currentPrice = market.getPrice(signal.symbol);
60
- if (currentPrice) {
61
- const slippageBps = Math.abs(signal.price - currentPrice) / currentPrice * 10000;
62
- if (slippageBps > this.params.maxSlippageBps) {
63
- console.log(`[risk] Blocked ${signal.symbol}: slippage ${slippageBps.toFixed(0)}bps > max ${this.params.maxSlippageBps}bps`);
64
- return false;
65
- }
66
- }
67
- }
68
-
69
- return true;
70
- }
71
-
72
- updateParams(updates: Record<string, number>): void {
73
- if (updates.maxPositionSizeBps !== undefined) this.params.maxPositionSizeBps = updates.maxPositionSizeBps;
74
- if (updates.maxDailyLossBps !== undefined) this.params.maxDailyLossBps = updates.maxDailyLossBps;
75
- if (updates.maxConcurrentPositions !== undefined) this.params.maxConcurrentPositions = updates.maxConcurrentPositions;
76
- if (updates.maxSlippageBps !== undefined) this.params.maxSlippageBps = updates.maxSlippageBps;
77
- if (updates.minTradeValueUSD !== undefined) this.params.minTradeValueUSD = updates.minTradeValueUSD;
78
- console.log('[risk] Params updated:', JSON.stringify(this.params));
79
- }
80
-
81
- recordTrade(pnl: number, fee: number): void {
82
- this.dailyPnL += pnl;
83
- this.dailyFees += fee;
84
- }
85
-
86
- isDailyLossLimitHit(): boolean {
87
- const limit = (this.params.maxDailyLossBps / 10000) * this.initialCapitalUSD;
88
- return this.dailyPnL < -limit;
89
- }
90
-
91
- getDailyPnL(): number {
92
- return this.dailyPnL;
93
- }
94
-
95
- getDailyLossLimit(): number {
96
- return (this.params.maxDailyLossBps / 10000) * this.initialCapitalUSD;
97
- }
98
-
99
- private resetIfNewDay(): void {
100
- const today = new Date().toISOString().slice(0, 10);
101
- if (today !== this.lastResetDate) {
102
- this.dailyPnL = 0;
103
- this.dailyFees = 0;
104
- this.lastResetDate = today;
105
- }
106
- }
107
- }
package/src/ui.ts DELETED
@@ -1,75 +0,0 @@
1
- import figlet from 'figlet';
2
- import gradient from 'gradient-string';
3
- import boxen from 'boxen';
4
- import pc from 'picocolors';
5
- import { createRequire } from 'node:module';
6
-
7
- // Brand gradient: Blue → Indigo → Violet (from Exagent design system)
8
- const brandGradient: (text: string) => string = gradient(['#3B82F6', '#6366F1', '#7C3AED']);
9
-
10
- // Secondary gradient: Cyan → Blue
11
- const accentGradient: (text: string) => string = gradient(['#22D3EE', '#3B82F6']);
12
-
13
- function getVersion(): string {
14
- try {
15
- const require = createRequire(import.meta.url);
16
- const pkg = require('../package.json');
17
- return pkg.version || '0.0.0';
18
- } catch {
19
- return '0.0.0';
20
- }
21
- }
22
-
23
- export function printBanner(): void {
24
- const art = figlet.textSync('EXAGENT', {
25
- font: 'Small',
26
- horizontalLayout: 'default',
27
- });
28
-
29
- console.log();
30
- console.log(brandGradient(art));
31
- console.log(pc.dim(` v${getVersion()}`));
32
- console.log();
33
- }
34
-
35
- export function printSuccess(title: string, lines: string[]): void {
36
- const body = [
37
- '',
38
- pc.bold(pc.white(title)),
39
- '',
40
- ...lines.map(l => ` ${l}`),
41
- '',
42
- ].join('\n');
43
-
44
- console.log();
45
- console.log(boxen(body, {
46
- padding: { top: 0, bottom: 0, left: 2, right: 2 },
47
- borderColor: '#3B82F6',
48
- borderStyle: 'round',
49
- dimBorder: false,
50
- }));
51
- console.log();
52
- }
53
-
54
- export function printStep(step: number, total: number, label: string): void {
55
- console.log();
56
- console.log(accentGradient(` Step ${step} of ${total}`) + pc.dim(` — ${label}`));
57
- }
58
-
59
- export function printDone(message: string): void {
60
- console.log(` ${pc.green('✓')} ${message}`);
61
- }
62
-
63
- export function printInfo(message: string): void {
64
- console.log(` ${pc.dim('│')} ${message}`);
65
- }
66
-
67
- export function printWarn(message: string): void {
68
- console.log(` ${pc.yellow('!')} ${message}`);
69
- }
70
-
71
- export function printError(message: string): void {
72
- console.log(` ${pc.red('✗')} ${message}`);
73
- }
74
-
75
- export { pc, brandGradient, accentGradient };
@@ -1,150 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
4
- import { tmpdir } from 'node:os';
5
- import { join } from 'node:path';
6
- import { loadStrategy } from '../src/strategy/loader.js';
7
- import type { StrategyContext } from '@exagent/sdk';
8
-
9
- function createContext(llmContent: string, prices: Record<string, number> = { ETH: 3000 }): StrategyContext {
10
- const logs: string[] = [];
11
- return {
12
- llm: {
13
- async chat() {
14
- return { content: llmContent };
15
- },
16
- getMetadata() {
17
- return { provider: 'test', model: 'test' };
18
- },
19
- },
20
- market: {
21
- getPrice(symbol: string) {
22
- return prices[symbol] ?? prices[symbol.toUpperCase()];
23
- },
24
- getPrices() {
25
- return prices;
26
- },
27
- getOHLCV() {
28
- return [];
29
- },
30
- },
31
- position: {
32
- openPositions: [],
33
- totalUnrealizedPnL: 0,
34
- totalRealizedPnL: 0,
35
- },
36
- store: {
37
- get() {
38
- return undefined;
39
- },
40
- set() {
41
- // no-op
42
- },
43
- delete() {
44
- // no-op
45
- },
46
- keys() {
47
- return [];
48
- },
49
- },
50
- config: {
51
- mode: 'paper',
52
- timeHorizon: 'swing',
53
- maxPositionSizeBps: 2000,
54
- maxDailyLossBps: 500,
55
- maxConcurrentPositions: 5,
56
- tradingIntervalMs: 60000,
57
- maxSlippageBps: 100,
58
- minTradeValueUSD: 10,
59
- initialCapitalUSD: 10000,
60
- },
61
- log(message: string) {
62
- logs.push(message);
63
- },
64
- };
65
- }
66
-
67
- test('loadStrategy rejects raw JavaScript code configs', async () => {
68
- await assert.rejects(
69
- () => loadStrategy({ code: 'globalThis.stoleSecrets = true; return [];' }),
70
- /Raw JavaScript strategy code is disabled/,
71
- );
72
- });
73
-
74
- test('template strategies run through prompt-backed validation', async () => {
75
- const strategy = await loadStrategy({ template: 'momentum' });
76
- const signals = await strategy(createContext(JSON.stringify([
77
- { symbol: 'ETH', side: 'buy', venue: 'hyperliquid_perp', confidence: 0.8 },
78
- ])));
79
-
80
- assert.equal(signals.length, 1);
81
- assert.equal(signals[0].symbol, 'ETH');
82
- assert.equal(signals[0].venue, 'hyperliquid_perp');
83
- assert.equal(signals[0].price, 3000);
84
- assert.equal(signals[0].venueFillId, '');
85
- });
86
-
87
- test('prompt strategies drop venues outside the configured strategy scope', async () => {
88
- const strategy = await loadStrategy({
89
- prompt: {
90
- name: 'Scoped Prompt',
91
- systemPrompt: 'Return scoped trade intents.',
92
- venues: ['hyperliquid_perp'],
93
- },
94
- });
95
-
96
- const signals = await strategy(createContext(JSON.stringify([
97
- { symbol: 'ETH', side: 'buy', venue: 'polymarket', confidence: 0.9 },
98
- { symbol: 'ETH', side: 'buy', venue: 'hyperliquid_perp', confidence: 0.9 },
99
- ])));
100
-
101
- assert.equal(signals.length, 1);
102
- assert.equal(signals[0].venue, 'hyperliquid_perp');
103
- });
104
-
105
- test('strategy metadata files are parsed as data, not executable modules', async () => {
106
- const dir = mkdtempSync(join(tmpdir(), 'exagent-strategy-'));
107
- const strategyPath = join(dir, 'strategy.ts');
108
-
109
- writeFileSync(strategyPath, [
110
- '// Generated by Exagent',
111
- 'export const name = "Prompt File";',
112
- 'export const venues = ["hyperliquid_perp"];',
113
- 'export const systemPrompt = "Return trade intents.";'
114
- ].join('\n'));
115
-
116
- try {
117
- const strategy = await loadStrategy({ file: strategyPath });
118
- const signals = await strategy(createContext(JSON.stringify([
119
- { symbol: 'ETH', side: 'long', confidence: 0.7 },
120
- ])));
121
-
122
- assert.equal(signals.length, 1);
123
- assert.equal(signals[0].venue, 'hyperliquid_perp');
124
- } finally {
125
- rmSync(dir, { recursive: true, force: true });
126
- }
127
- });
128
-
129
- test('strategy files cannot export code or arbitrary statements', async () => {
130
- const dir = mkdtempSync(join(tmpdir(), 'exagent-strategy-'));
131
- const codeExportPath = join(dir, 'code-strategy.ts');
132
- const statementPath = join(dir, 'statement-strategy.ts');
133
-
134
- writeFileSync(codeExportPath, 'export const code = "return [];";\n');
135
- writeFileSync(statementPath, 'export const systemPrompt = "Return []";\nglobalThis.compromised = true;\n');
136
-
137
- try {
138
- await assert.rejects(
139
- () => loadStrategy({ file: codeExportPath }),
140
- /Raw JavaScript strategy code is disabled/,
141
- );
142
- await assert.rejects(
143
- () => loadStrategy({ file: statementPath }),
144
- /Unsupported statement/,
145
- );
146
- assert.equal((globalThis as Record<string, unknown>).compromised, undefined);
147
- } finally {
148
- rmSync(dir, { recursive: true, force: true });
149
- }
150
- });
package/tsconfig.json DELETED
@@ -1,8 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "dist",
5
- "rootDir": "src"
6
- },
7
- "include": ["src"]
8
- }