@exagent/agent 0.3.6 → 0.3.7

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 (68) 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-IVA2SCSN.js +6756 -0
  5. package/dist/chunk-JHXCSGPC.js +6352 -0
  6. package/dist/chunk-V6O4UXVN.js +6345 -0
  7. package/dist/chunk-WTECTX2Z.js +6345 -0
  8. package/dist/cli.js +2 -2
  9. package/dist/index.d.ts +24 -2
  10. package/dist/index.js +1 -1
  11. package/package.json +12 -9
  12. package/src/bridge/across.ts +0 -240
  13. package/src/bridge/bridge-manager.ts +0 -87
  14. package/src/bridge/index.ts +0 -9
  15. package/src/bridge/types.ts +0 -77
  16. package/src/chains.ts +0 -105
  17. package/src/cli.ts +0 -250
  18. package/src/config.ts +0 -502
  19. package/src/diagnostics.ts +0 -335
  20. package/src/index.ts +0 -98
  21. package/src/llm/anthropic.ts +0 -63
  22. package/src/llm/base.ts +0 -264
  23. package/src/llm/deepseek.ts +0 -48
  24. package/src/llm/google.ts +0 -63
  25. package/src/llm/groq.ts +0 -48
  26. package/src/llm/index.ts +0 -42
  27. package/src/llm/mistral.ts +0 -48
  28. package/src/llm/ollama.ts +0 -52
  29. package/src/llm/openai.ts +0 -94
  30. package/src/llm/together.ts +0 -48
  31. package/src/llm-providers.ts +0 -8
  32. package/src/logger.ts +0 -137
  33. package/src/paper/executor.ts +0 -201
  34. package/src/paper/index.ts +0 -1
  35. package/src/perp/client.ts +0 -200
  36. package/src/perp/index.ts +0 -12
  37. package/src/perp/msgpack.ts +0 -272
  38. package/src/perp/orders.ts +0 -234
  39. package/src/perp/positions.ts +0 -126
  40. package/src/perp/signer.ts +0 -277
  41. package/src/perp/types.ts +0 -192
  42. package/src/perp/websocket.ts +0 -274
  43. package/src/position-tracker.ts +0 -243
  44. package/src/prediction/client.ts +0 -288
  45. package/src/prediction/index.ts +0 -3
  46. package/src/prediction/order-manager.ts +0 -297
  47. package/src/prediction/types.ts +0 -151
  48. package/src/relay.ts +0 -254
  49. package/src/runtime.ts +0 -1755
  50. package/src/scrub-secrets.ts +0 -39
  51. package/src/setup.ts +0 -392
  52. package/src/signal.ts +0 -212
  53. package/src/spot/aerodrome.ts +0 -158
  54. package/src/spot/client.ts +0 -138
  55. package/src/spot/index.ts +0 -11
  56. package/src/spot/swap-manager.ts +0 -219
  57. package/src/spot/types.ts +0 -203
  58. package/src/spot/uniswap.ts +0 -150
  59. package/src/store.ts +0 -50
  60. package/src/strategy/index.ts +0 -2
  61. package/src/strategy/loader.ts +0 -265
  62. package/src/strategy/templates.ts +0 -74
  63. package/src/trading/index.ts +0 -2
  64. package/src/trading/market.ts +0 -120
  65. package/src/trading/risk.ts +0 -107
  66. package/src/ui.ts +0 -75
  67. package/test/strategy-loader.test.ts +0 -150
  68. 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
- }