@vizzor/cli 0.5.0 → 0.6.0
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 +1854 -1635
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -50,6 +50,15 @@ var init_schema = __esm({
|
|
|
50
50
|
type: z.enum(["sqlite", "postgres"]).default("sqlite"),
|
|
51
51
|
url: z.string().optional()
|
|
52
52
|
}).default(() => ({ type: "sqlite" })),
|
|
53
|
+
ml: z.object({
|
|
54
|
+
enabled: z.boolean().default(false),
|
|
55
|
+
sidecarUrl: z.string().default("http://localhost:8000"),
|
|
56
|
+
fallbackToRules: z.boolean().default(true)
|
|
57
|
+
}).default(() => ({
|
|
58
|
+
enabled: false,
|
|
59
|
+
sidecarUrl: "http://localhost:8000",
|
|
60
|
+
fallbackToRules: true
|
|
61
|
+
})),
|
|
53
62
|
discordToken: z.string().optional(),
|
|
54
63
|
discordGuildId: z.string().optional(),
|
|
55
64
|
telegramToken: z.string().optional()
|
|
@@ -647,12 +656,12 @@ var init_adapter = __esm({
|
|
|
647
656
|
toBlock: options?.toBlock,
|
|
648
657
|
args: options?.args
|
|
649
658
|
});
|
|
650
|
-
return logs.map((
|
|
651
|
-
eventName:
|
|
652
|
-
blockNumber:
|
|
653
|
-
transactionHash:
|
|
654
|
-
args:
|
|
655
|
-
logIndex:
|
|
659
|
+
return logs.map((log3) => ({
|
|
660
|
+
eventName: log3.eventName ?? eventName,
|
|
661
|
+
blockNumber: log3.blockNumber ?? 0n,
|
|
662
|
+
transactionHash: log3.transactionHash ?? "0x",
|
|
663
|
+
args: log3.args ?? {},
|
|
664
|
+
logIndex: log3.logIndex ?? 0
|
|
656
665
|
}));
|
|
657
666
|
}
|
|
658
667
|
// ── Tokens ──────────────────────────────────────────────────────────────
|
|
@@ -1714,6 +1723,111 @@ var init_fear_greed = __esm({
|
|
|
1714
1723
|
}
|
|
1715
1724
|
});
|
|
1716
1725
|
|
|
1726
|
+
// src/utils/logger.ts
|
|
1727
|
+
import pino from "pino";
|
|
1728
|
+
function createLogger(name) {
|
|
1729
|
+
const isDev = process.env["NODE_ENV"] !== "production";
|
|
1730
|
+
if (isDev) {
|
|
1731
|
+
return pino({
|
|
1732
|
+
name,
|
|
1733
|
+
level,
|
|
1734
|
+
transport: {
|
|
1735
|
+
target: "pino-pretty",
|
|
1736
|
+
options: {
|
|
1737
|
+
colorize: true
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
});
|
|
1741
|
+
}
|
|
1742
|
+
return pino({ name, level });
|
|
1743
|
+
}
|
|
1744
|
+
var level;
|
|
1745
|
+
var init_logger = __esm({
|
|
1746
|
+
"src/utils/logger.ts"() {
|
|
1747
|
+
"use strict";
|
|
1748
|
+
level = process.env["VIZZOR_LOG_LEVEL"] ?? "info";
|
|
1749
|
+
}
|
|
1750
|
+
});
|
|
1751
|
+
|
|
1752
|
+
// src/ml/client.ts
|
|
1753
|
+
function getMLClient() {
|
|
1754
|
+
return mlClient;
|
|
1755
|
+
}
|
|
1756
|
+
var log, mlClient;
|
|
1757
|
+
var init_client = __esm({
|
|
1758
|
+
"src/ml/client.ts"() {
|
|
1759
|
+
"use strict";
|
|
1760
|
+
init_logger();
|
|
1761
|
+
log = createLogger("ml-client");
|
|
1762
|
+
mlClient = null;
|
|
1763
|
+
}
|
|
1764
|
+
});
|
|
1765
|
+
|
|
1766
|
+
// src/ml/feature-engineer.ts
|
|
1767
|
+
async function buildFeatureVector(symbol) {
|
|
1768
|
+
const [ta, fundingResult, tickerResult, fgResult, klines] = await Promise.allSettled([
|
|
1769
|
+
analyzeTechnicals(symbol, "4h"),
|
|
1770
|
+
fetchFundingRate(symbol),
|
|
1771
|
+
fetchTickerPrice(symbol),
|
|
1772
|
+
fetchFearGreedIndex(1),
|
|
1773
|
+
fetchKlines(symbol, "4h", 100)
|
|
1774
|
+
]);
|
|
1775
|
+
const indicators = ta.status === "fulfilled" ? ta.value.indicators : null;
|
|
1776
|
+
const funding = fundingResult.status === "fulfilled" ? fundingResult.value : null;
|
|
1777
|
+
const ticker = tickerResult.status === "fulfilled" ? tickerResult.value : null;
|
|
1778
|
+
const fg = fgResult.status === "fulfilled" ? fgResult.value : null;
|
|
1779
|
+
const candles = klines.status === "fulfilled" ? klines.value : [];
|
|
1780
|
+
let rsiSlope = 0;
|
|
1781
|
+
if (candles.length >= 17) {
|
|
1782
|
+
const closes = candles.map((k) => k.close);
|
|
1783
|
+
const recentRsi = calculateRSI(closes, 14);
|
|
1784
|
+
const olderCloses = closes.slice(0, -3);
|
|
1785
|
+
const olderRsi = calculateRSI(olderCloses, 14);
|
|
1786
|
+
if (recentRsi !== null && olderRsi !== null) {
|
|
1787
|
+
rsiSlope = recentRsi - olderRsi;
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
let volumeRatio = 1;
|
|
1791
|
+
if (candles.length >= 21) {
|
|
1792
|
+
const currentVolume = candles[candles.length - 1].volume;
|
|
1793
|
+
const avgVolume = candles.slice(-21, -1).reduce((sum, k) => sum + k.volume, 0) / 20;
|
|
1794
|
+
volumeRatio = avgVolume > 0 ? currentVolume / avgVolume : 1;
|
|
1795
|
+
}
|
|
1796
|
+
const price = ticker?.price ?? candles[candles.length - 1]?.close ?? 0;
|
|
1797
|
+
const ema12 = indicators?.ema12 ?? 0;
|
|
1798
|
+
const ema26 = indicators?.ema26 ?? 0;
|
|
1799
|
+
const emaCrossoverPct = price > 0 ? (ema12 - ema26) / price * 100 : 0;
|
|
1800
|
+
const atr = indicators?.atr ?? 0;
|
|
1801
|
+
const atrPct = price > 0 ? atr / price * 100 : 0;
|
|
1802
|
+
return {
|
|
1803
|
+
rsi: indicators?.rsi ?? 50,
|
|
1804
|
+
macdHistogram: indicators?.macd?.histogram ?? 0,
|
|
1805
|
+
bollingerPercentB: indicators?.bollingerBands?.percentB ?? 0.5,
|
|
1806
|
+
ema12,
|
|
1807
|
+
ema26,
|
|
1808
|
+
atr,
|
|
1809
|
+
obv: indicators?.obv ?? 0,
|
|
1810
|
+
fundingRate: funding?.fundingRate ?? 0,
|
|
1811
|
+
fearGreed: fg?.current.value ?? 50,
|
|
1812
|
+
priceChange24h: ticker?.change24h ?? 0,
|
|
1813
|
+
rsiSlope,
|
|
1814
|
+
volumeRatio,
|
|
1815
|
+
emaCrossoverPct,
|
|
1816
|
+
atrPct,
|
|
1817
|
+
symbol: symbol.toUpperCase(),
|
|
1818
|
+
timestamp: Date.now()
|
|
1819
|
+
};
|
|
1820
|
+
}
|
|
1821
|
+
var init_feature_engineer = __esm({
|
|
1822
|
+
"src/ml/feature-engineer.ts"() {
|
|
1823
|
+
"use strict";
|
|
1824
|
+
init_technical_analysis();
|
|
1825
|
+
init_binance();
|
|
1826
|
+
init_fear_greed();
|
|
1827
|
+
init_indicators();
|
|
1828
|
+
}
|
|
1829
|
+
});
|
|
1830
|
+
|
|
1717
1831
|
// src/core/trends/predictor.ts
|
|
1718
1832
|
async function generatePrediction(symbol) {
|
|
1719
1833
|
const reasoning = [];
|
|
@@ -1834,7 +1948,7 @@ async function generatePrediction(symbol) {
|
|
|
1834
1948
|
const negativeCount = signalValues.filter((v) => v < 0).length;
|
|
1835
1949
|
const agreement = Math.max(positiveCount, negativeCount) / Math.max(1, positiveCount + negativeCount);
|
|
1836
1950
|
const confidence = Math.round(Math.min(95, completeness / 5 * agreement * 100));
|
|
1837
|
-
|
|
1951
|
+
const rulePrediction = {
|
|
1838
1952
|
symbol: symbol.toUpperCase(),
|
|
1839
1953
|
direction,
|
|
1840
1954
|
confidence,
|
|
@@ -1844,6 +1958,34 @@ async function generatePrediction(symbol) {
|
|
|
1844
1958
|
composite: Math.round(composite),
|
|
1845
1959
|
disclaimer: "This is not financial advice. Predictions are based on historical data and AI analysis. Always do your own research."
|
|
1846
1960
|
};
|
|
1961
|
+
const mlClient2 = getMLClient();
|
|
1962
|
+
if (mlClient2) {
|
|
1963
|
+
try {
|
|
1964
|
+
const features = await buildFeatureVector(symbol);
|
|
1965
|
+
const mlPred = await mlClient2.predict(features);
|
|
1966
|
+
if (mlPred) {
|
|
1967
|
+
return mergePredictions(rulePrediction, mlPred);
|
|
1968
|
+
}
|
|
1969
|
+
} catch {
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1972
|
+
return rulePrediction;
|
|
1973
|
+
}
|
|
1974
|
+
function mergePredictions(rule, ml) {
|
|
1975
|
+
const mlComposite = ml.direction === "up" ? ml.probability * 100 : ml.direction === "down" ? -(ml.probability * 100) : 0;
|
|
1976
|
+
const mergedComposite = Math.round(rule.composite * 0.4 + mlComposite * 0.6);
|
|
1977
|
+
const mergedDirection = mergedComposite > 15 ? "up" : mergedComposite < -15 ? "down" : "sideways";
|
|
1978
|
+
const mergedConfidence = Math.round(Math.min(95, rule.confidence * 0.4 + ml.confidence * 0.6));
|
|
1979
|
+
return {
|
|
1980
|
+
...rule,
|
|
1981
|
+
direction: mergedDirection,
|
|
1982
|
+
confidence: mergedConfidence,
|
|
1983
|
+
composite: mergedComposite,
|
|
1984
|
+
reasoning: [
|
|
1985
|
+
...rule.reasoning,
|
|
1986
|
+
`ML (${ml.model}): ${ml.direction} with ${(ml.probability * 100).toFixed(1)}% probability (horizon: ${ml.horizon})`
|
|
1987
|
+
]
|
|
1988
|
+
};
|
|
1847
1989
|
}
|
|
1848
1990
|
var WEIGHTS2;
|
|
1849
1991
|
var init_predictor = __esm({
|
|
@@ -1853,6 +1995,8 @@ var init_predictor = __esm({
|
|
|
1853
1995
|
init_sentiment();
|
|
1854
1996
|
init_fear_greed();
|
|
1855
1997
|
init_binance();
|
|
1998
|
+
init_client();
|
|
1999
|
+
init_feature_engineer();
|
|
1856
2000
|
WEIGHTS2 = {
|
|
1857
2001
|
technical: 40,
|
|
1858
2002
|
sentiment: 20,
|
|
@@ -5233,7 +5377,7 @@ async function analyze(systemPrompt, userMessage, tools) {
|
|
|
5233
5377
|
return p.analyze(systemPrompt, userMessage, tools, toolHandler);
|
|
5234
5378
|
}
|
|
5235
5379
|
var provider, config, toolHandler;
|
|
5236
|
-
var
|
|
5380
|
+
var init_client2 = __esm({
|
|
5237
5381
|
"src/ai/client.ts"() {
|
|
5238
5382
|
"use strict";
|
|
5239
5383
|
init_registry2();
|
|
@@ -5557,32 +5701,6 @@ var init_cache = __esm({
|
|
|
5557
5701
|
}
|
|
5558
5702
|
});
|
|
5559
5703
|
|
|
5560
|
-
// src/utils/logger.ts
|
|
5561
|
-
import pino from "pino";
|
|
5562
|
-
function createLogger(name) {
|
|
5563
|
-
const isDev = process.env["NODE_ENV"] !== "production";
|
|
5564
|
-
if (isDev) {
|
|
5565
|
-
return pino({
|
|
5566
|
-
name,
|
|
5567
|
-
level,
|
|
5568
|
-
transport: {
|
|
5569
|
-
target: "pino-pretty",
|
|
5570
|
-
options: {
|
|
5571
|
-
colorize: true
|
|
5572
|
-
}
|
|
5573
|
-
}
|
|
5574
|
-
});
|
|
5575
|
-
}
|
|
5576
|
-
return pino({ name, level });
|
|
5577
|
-
}
|
|
5578
|
-
var level;
|
|
5579
|
-
var init_logger = __esm({
|
|
5580
|
-
"src/utils/logger.ts"() {
|
|
5581
|
-
"use strict";
|
|
5582
|
-
level = process.env["VIZZOR_LOG_LEVEL"] ?? "info";
|
|
5583
|
-
}
|
|
5584
|
-
});
|
|
5585
|
-
|
|
5586
5704
|
// src/core/agent/engine.ts
|
|
5587
5705
|
var logger, AgentEngine;
|
|
5588
5706
|
var init_engine = __esm({
|
|
@@ -6053,792 +6171,1320 @@ var init_agent = __esm({
|
|
|
6053
6171
|
}
|
|
6054
6172
|
});
|
|
6055
6173
|
|
|
6056
|
-
// src/
|
|
6057
|
-
|
|
6058
|
-
const
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
|
|
6087
|
-
|
|
6088
|
-
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
};
|
|
6105
|
-
}
|
|
6106
|
-
case "get_market_data": {
|
|
6107
|
-
const symbol = String(params["symbol"] ?? "");
|
|
6108
|
-
try {
|
|
6109
|
-
const binance = await fetchTickerPrice(symbol);
|
|
6110
|
-
const gecko = await fetchMarketData(symbol).catch(() => null);
|
|
6111
|
-
return {
|
|
6112
|
-
symbol: binance.symbol,
|
|
6113
|
-
name: gecko?.name ?? binance.symbol,
|
|
6114
|
-
price: binance.price,
|
|
6115
|
-
priceChange24h: binance.change24h,
|
|
6116
|
-
priceChange7d: gecko?.priceChange7d ?? null,
|
|
6117
|
-
volume24h: gecko?.volume24h ?? null,
|
|
6118
|
-
marketCap: gecko?.marketCap ?? null,
|
|
6119
|
-
rank: gecko?.rank ?? null,
|
|
6120
|
-
source: "binance+coingecko"
|
|
6121
|
-
};
|
|
6122
|
-
} catch {
|
|
6123
|
-
const data = await fetchMarketData(symbol);
|
|
6124
|
-
if (!data) {
|
|
6125
|
-
return { error: `No market data found for "${symbol}"` };
|
|
6126
|
-
}
|
|
6127
|
-
return data;
|
|
6174
|
+
// src/data/sqlite-store.ts
|
|
6175
|
+
function ensureAgentTables2() {
|
|
6176
|
+
const db2 = getDb();
|
|
6177
|
+
db2.exec(`
|
|
6178
|
+
CREATE TABLE IF NOT EXISTS agents (
|
|
6179
|
+
id TEXT PRIMARY KEY,
|
|
6180
|
+
name TEXT NOT NULL UNIQUE,
|
|
6181
|
+
strategy TEXT NOT NULL,
|
|
6182
|
+
pairs TEXT NOT NULL,
|
|
6183
|
+
interval_seconds INTEGER NOT NULL DEFAULT 60,
|
|
6184
|
+
created_at INTEGER NOT NULL,
|
|
6185
|
+
updated_at INTEGER NOT NULL
|
|
6186
|
+
)
|
|
6187
|
+
`);
|
|
6188
|
+
db2.exec(`
|
|
6189
|
+
CREATE TABLE IF NOT EXISTS agent_decisions (
|
|
6190
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
6191
|
+
agent_id TEXT NOT NULL,
|
|
6192
|
+
symbol TEXT NOT NULL,
|
|
6193
|
+
action TEXT NOT NULL,
|
|
6194
|
+
confidence INTEGER NOT NULL,
|
|
6195
|
+
reasoning TEXT NOT NULL,
|
|
6196
|
+
signals TEXT NOT NULL,
|
|
6197
|
+
created_at INTEGER NOT NULL,
|
|
6198
|
+
FOREIGN KEY (agent_id) REFERENCES agents(id)
|
|
6199
|
+
)
|
|
6200
|
+
`);
|
|
6201
|
+
}
|
|
6202
|
+
function rowToConfig(row) {
|
|
6203
|
+
return {
|
|
6204
|
+
id: row.id,
|
|
6205
|
+
name: row.name,
|
|
6206
|
+
strategy: row.strategy,
|
|
6207
|
+
pairs: JSON.parse(row.pairs),
|
|
6208
|
+
interval: row.interval_seconds,
|
|
6209
|
+
createdAt: row.created_at,
|
|
6210
|
+
updatedAt: row.updated_at
|
|
6211
|
+
};
|
|
6212
|
+
}
|
|
6213
|
+
var SqliteStore;
|
|
6214
|
+
var init_sqlite_store = __esm({
|
|
6215
|
+
"src/data/sqlite-store.ts"() {
|
|
6216
|
+
"use strict";
|
|
6217
|
+
init_cache();
|
|
6218
|
+
SqliteStore = class {
|
|
6219
|
+
// ---- Cache ---------------------------------------------------------------
|
|
6220
|
+
async getCached(key) {
|
|
6221
|
+
return getCached(key);
|
|
6128
6222
|
}
|
|
6129
|
-
|
|
6130
|
-
|
|
6131
|
-
const category = params["category"] ? String(params["category"]) : void 0;
|
|
6132
|
-
const chain = params["chain"] ? String(params["chain"]) : void 0;
|
|
6133
|
-
const roundType = params["roundType"] ? String(params["roundType"]) : void 0;
|
|
6134
|
-
const projects = category || chain || roundType ? await searchICOs(void 0, category, chain, roundType) : await fetchUpcomingICOs();
|
|
6135
|
-
return {
|
|
6136
|
-
projects: projects.map((p) => ({
|
|
6137
|
-
name: p.name,
|
|
6138
|
-
category: p.category,
|
|
6139
|
-
chain: p.chain,
|
|
6140
|
-
roundType: p.roundType,
|
|
6141
|
-
raisedAmount: p.raisedAmount,
|
|
6142
|
-
valuation: p.valuation,
|
|
6143
|
-
investors: p.investors.slice(0, 5),
|
|
6144
|
-
startDate: p.startDate,
|
|
6145
|
-
description: p.description,
|
|
6146
|
-
website: p.website
|
|
6147
|
-
}))
|
|
6148
|
-
};
|
|
6149
|
-
}
|
|
6150
|
-
case "get_funding_history": {
|
|
6151
|
-
const fundingName = String(params["name"] ?? "");
|
|
6152
|
-
const type = String(params["type"] ?? "project");
|
|
6153
|
-
if (type === "investor") {
|
|
6154
|
-
const portfolio = await getInvestorPortfolio(fundingName);
|
|
6155
|
-
return {
|
|
6156
|
-
investor: fundingName,
|
|
6157
|
-
investments: portfolio.map((p) => ({
|
|
6158
|
-
name: p.name,
|
|
6159
|
-
round: p.roundType,
|
|
6160
|
-
amount: p.raisedAmount,
|
|
6161
|
-
chain: p.chain,
|
|
6162
|
-
category: p.category,
|
|
6163
|
-
date: p.startDate
|
|
6164
|
-
}))
|
|
6165
|
-
};
|
|
6223
|
+
async setCache(key, value, ttlSeconds) {
|
|
6224
|
+
setCache(key, value, ttlSeconds);
|
|
6166
6225
|
}
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
const query = String(params["query"] ?? "");
|
|
6182
|
-
const pairs = await fetchTokenFromDex(query);
|
|
6183
|
-
return {
|
|
6184
|
-
results: pairs.slice(0, 5).map((p) => ({
|
|
6185
|
-
name: p.baseToken.name,
|
|
6186
|
-
symbol: p.baseToken.symbol,
|
|
6187
|
-
chain: p.chainId,
|
|
6188
|
-
dex: p.dexId,
|
|
6189
|
-
priceUsd: p.priceUsd,
|
|
6190
|
-
volume24h: p.volume?.h24 ?? 0,
|
|
6191
|
-
liquidity: p.liquidity?.usd ?? 0,
|
|
6192
|
-
priceChange24h: p.priceChange?.h24 ?? 0,
|
|
6193
|
-
marketCap: p.marketCap ?? p.fdv ?? null,
|
|
6194
|
-
buys24h: p.txns?.h24?.buys ?? 0,
|
|
6195
|
-
sells24h: p.txns?.h24?.sells ?? 0,
|
|
6196
|
-
pairAddress: p.pairAddress,
|
|
6197
|
-
url: p.url
|
|
6198
|
-
}))
|
|
6199
|
-
};
|
|
6200
|
-
}
|
|
6201
|
-
case "get_trending": {
|
|
6202
|
-
const trending = await fetchTrendingTokens();
|
|
6203
|
-
return {
|
|
6204
|
-
trending: trending.slice(0, 10).map((t) => ({
|
|
6205
|
-
name: t.name,
|
|
6206
|
-
symbol: t.symbol,
|
|
6207
|
-
chain: t.chain,
|
|
6208
|
-
priceUsd: t.priceUsd,
|
|
6209
|
-
priceChange24h: t.priceChange24h,
|
|
6210
|
-
volume24h: t.volume24h,
|
|
6211
|
-
marketCap: t.marketCap,
|
|
6212
|
-
source: t.source,
|
|
6213
|
-
url: t.url
|
|
6214
|
-
}))
|
|
6215
|
-
};
|
|
6216
|
-
}
|
|
6217
|
-
case "get_crypto_news": {
|
|
6218
|
-
const symbol = params["symbol"] ? String(params["symbol"]) : void 0;
|
|
6219
|
-
const news = await fetchCryptoNews(symbol, getConfig().cryptopanicApiKey);
|
|
6220
|
-
return {
|
|
6221
|
-
news: news.slice(0, 10).map((n) => ({
|
|
6222
|
-
title: n.title,
|
|
6223
|
-
sentiment: n.sentiment,
|
|
6224
|
-
source: n.source.title,
|
|
6225
|
-
publishedAt: n.publishedAt,
|
|
6226
|
-
url: n.url
|
|
6227
|
-
}))
|
|
6228
|
-
};
|
|
6229
|
-
}
|
|
6230
|
-
case "get_raises": {
|
|
6231
|
-
const raises = await fetchRecentRaises(30);
|
|
6232
|
-
let filtered = raises;
|
|
6233
|
-
if (params["category"]) {
|
|
6234
|
-
const cat = String(params["category"]).toLowerCase();
|
|
6235
|
-
filtered = filtered.filter(
|
|
6236
|
-
(r) => r.category?.toLowerCase().includes(cat) || r.sector?.toLowerCase().includes(cat)
|
|
6226
|
+
// ---- Agents --------------------------------------------------------------
|
|
6227
|
+
async createAgent(config2) {
|
|
6228
|
+
ensureAgentTables2();
|
|
6229
|
+
getDb().prepare(
|
|
6230
|
+
`INSERT INTO agents (id, name, strategy, pairs, interval_seconds, created_at, updated_at)
|
|
6231
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
6232
|
+
).run(
|
|
6233
|
+
config2.id,
|
|
6234
|
+
config2.name,
|
|
6235
|
+
config2.strategy,
|
|
6236
|
+
JSON.stringify(config2.pairs),
|
|
6237
|
+
config2.interval,
|
|
6238
|
+
config2.createdAt,
|
|
6239
|
+
config2.updatedAt
|
|
6237
6240
|
);
|
|
6241
|
+
return config2;
|
|
6238
6242
|
}
|
|
6239
|
-
|
|
6240
|
-
|
|
6241
|
-
|
|
6243
|
+
async listAgents() {
|
|
6244
|
+
ensureAgentTables2();
|
|
6245
|
+
const rows = getDb().prepare("SELECT * FROM agents ORDER BY created_at DESC").all();
|
|
6246
|
+
return rows.map(rowToConfig);
|
|
6242
6247
|
}
|
|
6243
|
-
|
|
6244
|
-
|
|
6245
|
-
|
|
6246
|
-
|
|
6247
|
-
amount: r.amount,
|
|
6248
|
-
chains: r.chains,
|
|
6249
|
-
sector: r.sector,
|
|
6250
|
-
category: r.category,
|
|
6251
|
-
leadInvestors: r.leadInvestors,
|
|
6252
|
-
date: new Date(r.date * 1e3).toISOString().split("T")[0]
|
|
6253
|
-
}))
|
|
6254
|
-
};
|
|
6255
|
-
}
|
|
6256
|
-
case "get_token_security": {
|
|
6257
|
-
const address = String(params["address"] ?? "");
|
|
6258
|
-
const chain = String(params["chain"] ?? "ethereum");
|
|
6259
|
-
const security = await checkTokenSecurity(address, chain);
|
|
6260
|
-
if (!security) {
|
|
6261
|
-
return { error: `No security data for ${address} on ${chain}` };
|
|
6248
|
+
async getAgentById(id) {
|
|
6249
|
+
ensureAgentTables2();
|
|
6250
|
+
const row = getDb().prepare("SELECT * FROM agents WHERE id = ?").get(id);
|
|
6251
|
+
return row ? rowToConfig(row) : null;
|
|
6262
6252
|
}
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
isHoneypot: security.isHoneypot,
|
|
6268
|
-
isMintable: security.isMintable,
|
|
6269
|
-
buyTax: security.buyTax,
|
|
6270
|
-
sellTax: security.sellTax,
|
|
6271
|
-
isOpenSource: security.isOpenSource,
|
|
6272
|
-
isProxy: security.isProxy,
|
|
6273
|
-
hiddenOwner: security.hiddenOwner,
|
|
6274
|
-
cannotBuy: security.cannotBuy,
|
|
6275
|
-
cannotSellAll: security.cannotSellAll,
|
|
6276
|
-
isBlacklisted: security.isBlacklisted,
|
|
6277
|
-
holderCount: security.holderCount,
|
|
6278
|
-
lpHolderCount: security.lpHolderCount,
|
|
6279
|
-
creatorPercent: security.creatorPercent,
|
|
6280
|
-
ownerPercent: security.ownerPercent,
|
|
6281
|
-
trustList: security.trustList
|
|
6282
|
-
};
|
|
6283
|
-
}
|
|
6284
|
-
case "get_fear_greed": {
|
|
6285
|
-
const data = await fetchFearGreedIndex(7);
|
|
6286
|
-
return {
|
|
6287
|
-
current: { value: data.current.value, classification: data.current.classification },
|
|
6288
|
-
previous: data.previous ? { value: data.previous.value, classification: data.previous.classification } : null,
|
|
6289
|
-
history: data.history.map((h) => ({
|
|
6290
|
-
value: h.value,
|
|
6291
|
-
classification: h.classification,
|
|
6292
|
-
date: new Date(h.timestamp * 1e3).toISOString().split("T")[0]
|
|
6293
|
-
}))
|
|
6294
|
-
};
|
|
6295
|
-
}
|
|
6296
|
-
case "get_derivatives_data": {
|
|
6297
|
-
const symbol = String(params["symbol"] ?? "BTC");
|
|
6298
|
-
const [fundingResult, oiResult] = await Promise.allSettled([
|
|
6299
|
-
fetchFundingRate(symbol),
|
|
6300
|
-
fetchOpenInterest(symbol)
|
|
6301
|
-
]);
|
|
6302
|
-
const result = { symbol: symbol.toUpperCase() };
|
|
6303
|
-
if (fundingResult.status === "fulfilled") {
|
|
6304
|
-
result["fundingRate"] = fundingResult.value.fundingRate;
|
|
6305
|
-
result["fundingRatePct"] = `${(fundingResult.value.fundingRate * 100).toFixed(4)}%`;
|
|
6306
|
-
result["markPrice"] = fundingResult.value.markPrice;
|
|
6253
|
+
async getAgentByName(name) {
|
|
6254
|
+
ensureAgentTables2();
|
|
6255
|
+
const row = getDb().prepare("SELECT * FROM agents WHERE name = ?").get(name);
|
|
6256
|
+
return row ? rowToConfig(row) : null;
|
|
6307
6257
|
}
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
result
|
|
6258
|
+
async deleteAgent(id) {
|
|
6259
|
+
ensureAgentTables2();
|
|
6260
|
+
const result = getDb().prepare("DELETE FROM agents WHERE id = ?").run(id);
|
|
6261
|
+
getDb().prepare("DELETE FROM agent_decisions WHERE agent_id = ?").run(id);
|
|
6262
|
+
return result.changes > 0;
|
|
6311
6263
|
}
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
6319
|
-
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
|
|
6345
|
-
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
}
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6359
|
-
|
|
6360
|
-
|
|
6361
|
-
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
const agents = listAgents();
|
|
6370
|
-
return {
|
|
6371
|
-
agents: agents.map((a) => {
|
|
6372
|
-
const status = getAgentStatus(a.id);
|
|
6373
|
-
return {
|
|
6374
|
-
name: a.name,
|
|
6375
|
-
strategy: a.strategy,
|
|
6376
|
-
pairs: a.pairs,
|
|
6377
|
-
interval: a.interval,
|
|
6378
|
-
status: status?.status ?? "idle",
|
|
6379
|
-
cycleCount: status?.cycleCount ?? 0
|
|
6380
|
-
};
|
|
6381
|
-
})
|
|
6382
|
-
};
|
|
6383
|
-
}
|
|
6384
|
-
case "get_agent_status": {
|
|
6385
|
-
const agentName = String(params["name"] ?? "");
|
|
6386
|
-
const agent = getAgentByName(agentName);
|
|
6387
|
-
if (!agent) return { error: `Agent "${agentName}" not found` };
|
|
6388
|
-
const state = getAgentStatus(agent.id);
|
|
6389
|
-
if (!state) return { error: `Agent "${agentName}" not found` };
|
|
6390
|
-
const decisions = getRecentDecisions(agent.id, 5);
|
|
6391
|
-
return {
|
|
6392
|
-
name: state.config.name,
|
|
6393
|
-
status: state.status,
|
|
6394
|
-
strategy: state.config.strategy,
|
|
6395
|
-
pairs: state.config.pairs,
|
|
6396
|
-
cycleCount: state.cycleCount,
|
|
6397
|
-
error: state.error,
|
|
6398
|
-
recentDecisions: decisions.map((d) => ({
|
|
6399
|
-
symbol: d.symbol,
|
|
6400
|
-
action: d.decision.action,
|
|
6401
|
-
confidence: d.decision.confidence,
|
|
6402
|
-
reasoning: d.decision.reasoning,
|
|
6403
|
-
timestamp: new Date(d.timestamp).toISOString()
|
|
6404
|
-
}))
|
|
6405
|
-
};
|
|
6406
|
-
}
|
|
6407
|
-
default:
|
|
6408
|
-
return { error: `Unknown tool: ${name}` };
|
|
6409
|
-
}
|
|
6410
|
-
}
|
|
6411
|
-
var init_tool_handler = __esm({
|
|
6412
|
-
"src/ai/tool-handler.ts"() {
|
|
6413
|
-
"use strict";
|
|
6414
|
-
init_registry();
|
|
6415
|
-
init_loader();
|
|
6416
|
-
init_constants();
|
|
6417
|
-
init_wallet_analyzer();
|
|
6418
|
-
init_rug_detector();
|
|
6419
|
-
init_market();
|
|
6420
|
-
init_ico_tracker();
|
|
6421
|
-
init_cryptopanic();
|
|
6422
|
-
init_defillama();
|
|
6423
|
-
init_binance();
|
|
6424
|
-
init_goplus();
|
|
6425
|
-
init_fear_greed();
|
|
6426
|
-
init_technical_analysis();
|
|
6427
|
-
init_predictor();
|
|
6428
|
-
init_agent();
|
|
6264
|
+
async logDecision(result) {
|
|
6265
|
+
ensureAgentTables2();
|
|
6266
|
+
getDb().prepare(
|
|
6267
|
+
`INSERT INTO agent_decisions (agent_id, symbol, action, confidence, reasoning, signals, created_at)
|
|
6268
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
6269
|
+
).run(
|
|
6270
|
+
result.agentId,
|
|
6271
|
+
result.symbol,
|
|
6272
|
+
result.decision.action,
|
|
6273
|
+
result.decision.confidence,
|
|
6274
|
+
JSON.stringify(result.decision.reasoning),
|
|
6275
|
+
JSON.stringify(result.signals),
|
|
6276
|
+
result.timestamp
|
|
6277
|
+
);
|
|
6278
|
+
}
|
|
6279
|
+
async getDecisions(agentId, limit) {
|
|
6280
|
+
ensureAgentTables2();
|
|
6281
|
+
const rows = getDb().prepare("SELECT * FROM agent_decisions WHERE agent_id = ? ORDER BY created_at DESC LIMIT ?").all(agentId, limit);
|
|
6282
|
+
return rows.map((r) => ({
|
|
6283
|
+
agentId: r.agent_id,
|
|
6284
|
+
symbol: r.symbol,
|
|
6285
|
+
timestamp: r.created_at,
|
|
6286
|
+
signals: JSON.parse(r.signals),
|
|
6287
|
+
decision: {
|
|
6288
|
+
action: r.action,
|
|
6289
|
+
confidence: r.confidence,
|
|
6290
|
+
reasoning: JSON.parse(r.reasoning)
|
|
6291
|
+
}
|
|
6292
|
+
}));
|
|
6293
|
+
}
|
|
6294
|
+
// ---- Time-series (no-op for SQLite) --------------------------------------
|
|
6295
|
+
async insertOHLCV(_records) {
|
|
6296
|
+
}
|
|
6297
|
+
async queryOHLCV(_symbol, _timeframe, _from, _to) {
|
|
6298
|
+
return [];
|
|
6299
|
+
}
|
|
6300
|
+
// ---- Predictions (no-op for SQLite) --------------------------------------
|
|
6301
|
+
async logPrediction(_prediction) {
|
|
6302
|
+
}
|
|
6303
|
+
async getPredictionAccuracy(_model, _days) {
|
|
6304
|
+
return {
|
|
6305
|
+
model: _model,
|
|
6306
|
+
totalPredictions: 0,
|
|
6307
|
+
correctPredictions: 0,
|
|
6308
|
+
accuracy: 0,
|
|
6309
|
+
byDirection: {
|
|
6310
|
+
up: { total: 0, correct: 0, accuracy: 0 },
|
|
6311
|
+
down: { total: 0, correct: 0, accuracy: 0 },
|
|
6312
|
+
sideways: { total: 0, correct: 0, accuracy: 0 }
|
|
6313
|
+
},
|
|
6314
|
+
period: `${_days}d`
|
|
6315
|
+
};
|
|
6316
|
+
}
|
|
6317
|
+
// ---- Lifecycle -----------------------------------------------------------
|
|
6318
|
+
async close() {
|
|
6319
|
+
}
|
|
6320
|
+
};
|
|
6429
6321
|
}
|
|
6430
6322
|
});
|
|
6431
6323
|
|
|
6432
|
-
// src/
|
|
6433
|
-
var
|
|
6434
|
-
|
|
6435
|
-
|
|
6324
|
+
// src/data/postgres-store.ts
|
|
6325
|
+
var postgres_store_exports = {};
|
|
6326
|
+
__export(postgres_store_exports, {
|
|
6327
|
+
PostgresStore: () => PostgresStore
|
|
6328
|
+
});
|
|
6329
|
+
import pg from "pg";
|
|
6330
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
6331
|
+
import { resolve as resolve2, dirname } from "path";
|
|
6332
|
+
import { fileURLToPath } from "url";
|
|
6333
|
+
var __filename, __dirname, PostgresStore;
|
|
6334
|
+
var init_postgres_store = __esm({
|
|
6335
|
+
"src/data/postgres-store.ts"() {
|
|
6436
6336
|
"use strict";
|
|
6437
|
-
|
|
6438
|
-
|
|
6439
|
-
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6443
|
-
|
|
6444
|
-
address: {
|
|
6445
|
-
type: "string",
|
|
6446
|
-
description: "The token contract address (e.g. 0x...)."
|
|
6447
|
-
},
|
|
6448
|
-
chain: {
|
|
6449
|
-
type: "string",
|
|
6450
|
-
description: 'The blockchain to query (e.g. "ethereum", "bsc", "polygon", "arbitrum").'
|
|
6451
|
-
}
|
|
6452
|
-
},
|
|
6453
|
-
required: ["address", "chain"]
|
|
6454
|
-
}
|
|
6455
|
-
},
|
|
6456
|
-
{
|
|
6457
|
-
name: "analyze_wallet",
|
|
6458
|
-
description: "Analyze a wallet address for transaction patterns, token holdings, DeFi interactions, and behavioral signals such as accumulation or distribution phases.",
|
|
6459
|
-
input_schema: {
|
|
6460
|
-
type: "object",
|
|
6461
|
-
properties: {
|
|
6462
|
-
address: {
|
|
6463
|
-
type: "string",
|
|
6464
|
-
description: "The wallet address to analyze."
|
|
6465
|
-
},
|
|
6466
|
-
chain: {
|
|
6467
|
-
type: "string",
|
|
6468
|
-
description: 'The blockchain the wallet resides on (e.g. "ethereum", "bsc").'
|
|
6469
|
-
},
|
|
6470
|
-
depth: {
|
|
6471
|
-
type: "number",
|
|
6472
|
-
description: "How many recent transactions to inspect. Defaults to 100."
|
|
6473
|
-
}
|
|
6474
|
-
},
|
|
6475
|
-
required: ["address", "chain"]
|
|
6476
|
-
}
|
|
6477
|
-
},
|
|
6478
|
-
{
|
|
6479
|
-
name: "check_rug_indicators",
|
|
6480
|
-
description: "Check a token for common rug pull indicators including honeypot detection, liquidity locks, ownership status, hidden mints, and holder concentration.",
|
|
6481
|
-
input_schema: {
|
|
6482
|
-
type: "object",
|
|
6483
|
-
properties: {
|
|
6484
|
-
address: {
|
|
6485
|
-
type: "string",
|
|
6486
|
-
description: "The token contract address to check."
|
|
6487
|
-
},
|
|
6488
|
-
chain: {
|
|
6489
|
-
type: "string",
|
|
6490
|
-
description: "The blockchain the token is deployed on."
|
|
6491
|
-
}
|
|
6492
|
-
},
|
|
6493
|
-
required: ["address", "chain"]
|
|
6494
|
-
}
|
|
6495
|
-
},
|
|
6496
|
-
{
|
|
6497
|
-
name: "get_market_data",
|
|
6498
|
-
description: "Get LIVE current market data for a token including price, 24h volume, market cap, price change percentages, and circulating supply. Returns real-time data \u2014 never quote prices from training data.",
|
|
6499
|
-
input_schema: {
|
|
6500
|
-
type: "object",
|
|
6501
|
-
properties: {
|
|
6502
|
-
symbol: {
|
|
6503
|
-
type: "string",
|
|
6504
|
-
description: 'The token ticker symbol (e.g. "ETH", "BTC", "UNI").'
|
|
6505
|
-
},
|
|
6506
|
-
currency: {
|
|
6507
|
-
type: "string",
|
|
6508
|
-
description: 'The fiat currency for price quotes. Defaults to "usd".'
|
|
6509
|
-
}
|
|
6510
|
-
},
|
|
6511
|
-
required: ["symbol"]
|
|
6512
|
-
}
|
|
6513
|
-
},
|
|
6514
|
-
{
|
|
6515
|
-
name: "search_upcoming_icos",
|
|
6516
|
-
description: "Search for CURRENT upcoming ICOs, token launches, and fundraising rounds filtered by category, blockchain, or round type. Returns LIVE data from DeFiLlama raises and Pump.fun launches, updated daily. MUST call for any ICO/launch question \u2014 training data is stale.",
|
|
6517
|
-
input_schema: {
|
|
6518
|
-
type: "object",
|
|
6519
|
-
properties: {
|
|
6520
|
-
category: {
|
|
6521
|
-
type: "string",
|
|
6522
|
-
description: 'Filter by project category (e.g. "defi", "gaming", "infrastructure", "nft", "ai").'
|
|
6523
|
-
},
|
|
6524
|
-
chain: {
|
|
6525
|
-
type: "string",
|
|
6526
|
-
description: 'Filter by blockchain (e.g. "ethereum", "solana", "bsc").'
|
|
6527
|
-
},
|
|
6528
|
-
roundType: {
|
|
6529
|
-
type: "string",
|
|
6530
|
-
description: 'Filter by funding round type (e.g. "Seed", "Pre-Seed", "Series A", "Series B", "Token Launch").'
|
|
6531
|
-
},
|
|
6532
|
-
limit: {
|
|
6533
|
-
type: "number",
|
|
6534
|
-
description: "Maximum number of results to return. Defaults to 10."
|
|
6535
|
-
}
|
|
6536
|
-
},
|
|
6537
|
-
required: []
|
|
6538
|
-
}
|
|
6539
|
-
},
|
|
6540
|
-
{
|
|
6541
|
-
name: "get_funding_history",
|
|
6542
|
-
description: "Get complete funding history for a project by name. Returns all known fundraising rounds with amounts, investors, valuations, and dates. Also works for looking up an investor portfolio.",
|
|
6543
|
-
input_schema: {
|
|
6544
|
-
type: "object",
|
|
6545
|
-
properties: {
|
|
6546
|
-
name: {
|
|
6547
|
-
type: "string",
|
|
6548
|
-
description: "Project name or investor name to look up."
|
|
6549
|
-
},
|
|
6550
|
-
type: {
|
|
6551
|
-
type: "string",
|
|
6552
|
-
description: 'Type of lookup: "project" for project funding history, "investor" for investor portfolio. Defaults to "project".'
|
|
6553
|
-
}
|
|
6554
|
-
},
|
|
6555
|
-
required: ["name"]
|
|
6556
|
-
}
|
|
6557
|
-
},
|
|
6558
|
-
{
|
|
6559
|
-
name: "search_token_dex",
|
|
6560
|
-
description: "Search for any token on decentralized exchanges via DexScreener. Returns real-time price, volume, liquidity, buy/sell counts, pair info. Works for all tokens including meme coins and newly launched tokens.",
|
|
6561
|
-
input_schema: {
|
|
6562
|
-
type: "object",
|
|
6563
|
-
properties: {
|
|
6564
|
-
query: {
|
|
6565
|
-
type: "string",
|
|
6566
|
-
description: "Token name, symbol, or contract address to search for."
|
|
6567
|
-
}
|
|
6568
|
-
},
|
|
6569
|
-
required: ["query"]
|
|
6570
|
-
}
|
|
6571
|
-
},
|
|
6572
|
-
{
|
|
6573
|
-
name: "get_trending",
|
|
6574
|
-
description: "Get REAL-TIME trending and hot tokens from DexScreener (boosted tokens) and CoinGecko trending combined. Returns what the market is excited about RIGHT NOW \u2014 trends change hourly. Always call for trending/hot token questions.",
|
|
6575
|
-
input_schema: {
|
|
6576
|
-
type: "object",
|
|
6577
|
-
properties: {},
|
|
6578
|
-
required: []
|
|
6579
|
-
}
|
|
6580
|
-
},
|
|
6581
|
-
{
|
|
6582
|
-
name: "get_crypto_news",
|
|
6583
|
-
description: "Get LIVE latest crypto news with sentiment analysis for a specific token or the market in general. Returns current headlines from CryptoPanic \u2014 MUST call for news questions, training data is outdated.",
|
|
6584
|
-
input_schema: {
|
|
6585
|
-
type: "object",
|
|
6586
|
-
properties: {
|
|
6587
|
-
symbol: {
|
|
6588
|
-
type: "string",
|
|
6589
|
-
description: 'Token symbol to filter news for (e.g. "BTC", "ETH", "SOL"). Omit for general crypto news.'
|
|
6590
|
-
}
|
|
6591
|
-
},
|
|
6592
|
-
required: []
|
|
6593
|
-
}
|
|
6594
|
-
},
|
|
6595
|
-
{
|
|
6596
|
-
name: "get_raises",
|
|
6597
|
-
description: "Get LIVE recent crypto fundraising rounds, venture capital investments, and token launches. Returns CURRENT data updated daily from DeFiLlama \u2014 always call this for ICO/funding questions, never use training data.",
|
|
6598
|
-
input_schema: {
|
|
6599
|
-
type: "object",
|
|
6600
|
-
properties: {
|
|
6601
|
-
category: {
|
|
6602
|
-
type: "string",
|
|
6603
|
-
description: 'Filter by sector/category (e.g. "defi", "infrastructure", "gaming").'
|
|
6604
|
-
},
|
|
6605
|
-
chain: {
|
|
6606
|
-
type: "string",
|
|
6607
|
-
description: 'Filter by blockchain (e.g. "ethereum", "solana").'
|
|
6608
|
-
}
|
|
6609
|
-
},
|
|
6610
|
-
required: []
|
|
6611
|
-
}
|
|
6612
|
-
},
|
|
6613
|
-
{
|
|
6614
|
-
name: "get_token_security",
|
|
6615
|
-
description: "Check token security via GoPlus API. Returns honeypot detection, tax analysis, mint/pause/blacklist capabilities, holder stats, and overall risk level. No API key required.",
|
|
6616
|
-
input_schema: {
|
|
6617
|
-
type: "object",
|
|
6618
|
-
properties: {
|
|
6619
|
-
address: {
|
|
6620
|
-
type: "string",
|
|
6621
|
-
description: "The token contract address."
|
|
6622
|
-
},
|
|
6623
|
-
chain: {
|
|
6624
|
-
type: "string",
|
|
6625
|
-
description: 'The blockchain (e.g. "ethereum", "bsc", "polygon", "arbitrum", "base").'
|
|
6626
|
-
}
|
|
6627
|
-
},
|
|
6628
|
-
required: ["address", "chain"]
|
|
6629
|
-
}
|
|
6630
|
-
},
|
|
6631
|
-
{
|
|
6632
|
-
name: "get_fear_greed",
|
|
6633
|
-
description: "Get the LIVE Crypto Fear & Greed Index with 7-day history (updated daily). Values: 0-20 Extreme Fear, 21-40 Fear, 41-60 Neutral, 61-80 Greed, 81-100 Extreme Greed. Call for any market sentiment question.",
|
|
6634
|
-
input_schema: {
|
|
6635
|
-
type: "object",
|
|
6636
|
-
properties: {},
|
|
6637
|
-
required: []
|
|
6638
|
-
}
|
|
6639
|
-
},
|
|
6640
|
-
{
|
|
6641
|
-
name: "get_derivatives_data",
|
|
6642
|
-
description: "Get LIVE derivatives data from Binance Futures: funding rate (updates every 8h), open interest, and mark price for a trading pair. Essential for current market positioning analysis.",
|
|
6643
|
-
input_schema: {
|
|
6644
|
-
type: "object",
|
|
6645
|
-
properties: {
|
|
6646
|
-
symbol: {
|
|
6647
|
-
type: "string",
|
|
6648
|
-
description: 'The token symbol (e.g. "BTC", "ETH", "SOL").'
|
|
6649
|
-
}
|
|
6650
|
-
},
|
|
6651
|
-
required: ["symbol"]
|
|
6652
|
-
}
|
|
6653
|
-
},
|
|
6654
|
-
{
|
|
6655
|
-
name: "get_technical_analysis",
|
|
6656
|
-
description: "Run technical analysis on a token: RSI, MACD, Bollinger Bands, EMA crossovers, ATR, OBV. Returns individual indicator signals and a composite direction with confidence.",
|
|
6657
|
-
input_schema: {
|
|
6658
|
-
type: "object",
|
|
6659
|
-
properties: {
|
|
6660
|
-
symbol: {
|
|
6661
|
-
type: "string",
|
|
6662
|
-
description: 'The token symbol (e.g. "BTC", "ETH", "SOL").'
|
|
6663
|
-
},
|
|
6664
|
-
timeframe: {
|
|
6665
|
-
type: "string",
|
|
6666
|
-
description: 'Kline interval: "1h", "4h", "1d". Defaults to "4h".'
|
|
6667
|
-
}
|
|
6668
|
-
},
|
|
6669
|
-
required: ["symbol"]
|
|
6670
|
-
}
|
|
6671
|
-
},
|
|
6672
|
-
{
|
|
6673
|
-
name: "get_prediction",
|
|
6674
|
-
description: "Generate a multi-signal composite prediction combining technical analysis (40%), sentiment (20%), derivatives (20%), trend (15%), and macro (5%). Returns direction, confidence, composite score, and reasoning.",
|
|
6675
|
-
input_schema: {
|
|
6676
|
-
type: "object",
|
|
6677
|
-
properties: {
|
|
6678
|
-
symbol: {
|
|
6679
|
-
type: "string",
|
|
6680
|
-
description: 'The token symbol (e.g. "BTC", "ETH", "SOL").'
|
|
6681
|
-
}
|
|
6682
|
-
},
|
|
6683
|
-
required: ["symbol"]
|
|
6684
|
-
}
|
|
6685
|
-
},
|
|
6686
|
-
{
|
|
6687
|
-
name: "create_agent",
|
|
6688
|
-
description: "Create an autonomous trading agent that monitors crypto pairs using a strategy (momentum or trend-following). Returns the created agent config.",
|
|
6689
|
-
input_schema: {
|
|
6690
|
-
type: "object",
|
|
6691
|
-
properties: {
|
|
6692
|
-
name: {
|
|
6693
|
-
type: "string",
|
|
6694
|
-
description: 'A unique name for the agent (e.g. "btc-momentum-bot").'
|
|
6695
|
-
},
|
|
6696
|
-
strategy: {
|
|
6697
|
-
type: "string",
|
|
6698
|
-
description: 'Trading strategy: "momentum" (RSI+MACD) or "trend-following" (EMA crossover).'
|
|
6699
|
-
},
|
|
6700
|
-
pairs: {
|
|
6701
|
-
type: "string",
|
|
6702
|
-
description: 'Comma-separated trading pairs (e.g. "BTC,ETH,SOL").'
|
|
6703
|
-
},
|
|
6704
|
-
interval: {
|
|
6705
|
-
type: "number",
|
|
6706
|
-
description: "Cycle interval in seconds. Defaults to 60."
|
|
6707
|
-
}
|
|
6708
|
-
},
|
|
6709
|
-
required: ["name", "strategy", "pairs"]
|
|
6710
|
-
}
|
|
6711
|
-
},
|
|
6712
|
-
{
|
|
6713
|
-
name: "list_agents",
|
|
6714
|
-
description: "List all created trading agents with their status, strategy, and monitored pairs.",
|
|
6715
|
-
input_schema: {
|
|
6716
|
-
type: "object",
|
|
6717
|
-
properties: {},
|
|
6718
|
-
required: []
|
|
6719
|
-
}
|
|
6720
|
-
},
|
|
6721
|
-
{
|
|
6722
|
-
name: "get_agent_status",
|
|
6723
|
-
description: "Get detailed status of a trading agent including cycle count, last decision, and recent trade signals.",
|
|
6724
|
-
input_schema: {
|
|
6725
|
-
type: "object",
|
|
6726
|
-
properties: {
|
|
6727
|
-
name: {
|
|
6728
|
-
type: "string",
|
|
6729
|
-
description: "The agent name."
|
|
6730
|
-
}
|
|
6731
|
-
},
|
|
6732
|
-
required: ["name"]
|
|
6733
|
-
}
|
|
6337
|
+
__filename = fileURLToPath(import.meta.url);
|
|
6338
|
+
__dirname = dirname(__filename);
|
|
6339
|
+
PostgresStore = class {
|
|
6340
|
+
pool;
|
|
6341
|
+
initialized = false;
|
|
6342
|
+
constructor(connectionUrl) {
|
|
6343
|
+
this.pool = new pg.Pool({ connectionString: connectionUrl, max: 10 });
|
|
6734
6344
|
}
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
|
|
6741
|
-
if (text.length <= maxLen) return [text];
|
|
6742
|
-
const chunks = [];
|
|
6743
|
-
let remaining = text;
|
|
6744
|
-
while (remaining.length > 0) {
|
|
6745
|
-
if (remaining.length <= maxLen) {
|
|
6746
|
-
chunks.push(remaining);
|
|
6747
|
-
break;
|
|
6748
|
-
}
|
|
6749
|
-
let splitAt = remaining.lastIndexOf("\n\n", maxLen);
|
|
6750
|
-
if (splitAt <= 0) splitAt = remaining.lastIndexOf("\n", maxLen);
|
|
6751
|
-
if (splitAt <= 0) splitAt = remaining.lastIndexOf(" ", maxLen);
|
|
6752
|
-
if (splitAt <= 0) splitAt = maxLen;
|
|
6753
|
-
chunks.push(remaining.slice(0, splitAt));
|
|
6754
|
-
remaining = remaining.slice(splitAt).trimStart();
|
|
6755
|
-
}
|
|
6756
|
-
return chunks;
|
|
6757
|
-
}
|
|
6758
|
-
var init_message_split = __esm({
|
|
6759
|
-
"src/utils/message-split.ts"() {
|
|
6760
|
-
"use strict";
|
|
6761
|
-
}
|
|
6762
|
-
});
|
|
6763
|
-
|
|
6764
|
-
// src/discord/middleware/rate-limit.ts
|
|
6765
|
-
function checkRateLimit(userId) {
|
|
6766
|
-
const now = Date.now();
|
|
6767
|
-
const entry = userLimits.get(userId);
|
|
6768
|
-
if (!entry || now >= entry.resetAt) {
|
|
6769
|
-
userLimits.set(userId, { count: 1, resetAt: now + WINDOW_MS });
|
|
6770
|
-
return { allowed: true };
|
|
6771
|
-
}
|
|
6772
|
-
if (entry.count >= MAX_REQUESTS) {
|
|
6773
|
-
return { allowed: false };
|
|
6774
|
-
}
|
|
6775
|
-
entry.count++;
|
|
6776
|
-
return { allowed: true };
|
|
6777
|
-
}
|
|
6778
|
-
function startRateLimitCleanup(intervalMs = 3e5) {
|
|
6779
|
-
return setInterval(() => {
|
|
6780
|
-
const now = Date.now();
|
|
6781
|
-
for (const [userId, entry] of userLimits) {
|
|
6782
|
-
if (now >= entry.resetAt) {
|
|
6783
|
-
userLimits.delete(userId);
|
|
6345
|
+
async init() {
|
|
6346
|
+
if (this.initialized) return;
|
|
6347
|
+
const migrationPath = resolve2(__dirname, "migrations", "001-init.sql");
|
|
6348
|
+
const sql = readFileSync2(migrationPath, "utf-8");
|
|
6349
|
+
await this.pool.query(sql);
|
|
6350
|
+
this.initialized = true;
|
|
6784
6351
|
}
|
|
6785
|
-
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
|
|
6818
|
-
|
|
6819
|
-
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
|
|
6823
|
-
|
|
6824
|
-
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
|
|
6839
|
-
|
|
6840
|
-
|
|
6841
|
-
|
|
6352
|
+
async query(text, params) {
|
|
6353
|
+
await this.init();
|
|
6354
|
+
return this.pool.query(text, params);
|
|
6355
|
+
}
|
|
6356
|
+
// ---- Cache ---------------------------------------------------------------
|
|
6357
|
+
async getCached(key) {
|
|
6358
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
6359
|
+
const { rows } = await this.query(
|
|
6360
|
+
"SELECT value FROM cache WHERE key = $1 AND expires_at > $2",
|
|
6361
|
+
[key, now]
|
|
6362
|
+
);
|
|
6363
|
+
if (rows.length === 0) return null;
|
|
6364
|
+
return rows[0].value;
|
|
6365
|
+
}
|
|
6366
|
+
async setCache(key, value, ttlSeconds) {
|
|
6367
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
6368
|
+
const expiresAt = now + ttlSeconds;
|
|
6369
|
+
await this.query(
|
|
6370
|
+
`INSERT INTO cache (key, value, expires_at, created_at)
|
|
6371
|
+
VALUES ($1, $2, $3, $4)
|
|
6372
|
+
ON CONFLICT (key) DO UPDATE SET value = $2, expires_at = $3`,
|
|
6373
|
+
[key, JSON.stringify(value), expiresAt, now]
|
|
6374
|
+
);
|
|
6375
|
+
}
|
|
6376
|
+
// ---- Agents --------------------------------------------------------------
|
|
6377
|
+
async createAgent(config2) {
|
|
6378
|
+
await this.query(
|
|
6379
|
+
`INSERT INTO agents (id, name, strategy, pairs, interval_seconds, created_at, updated_at)
|
|
6380
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
|
6381
|
+
[
|
|
6382
|
+
config2.id,
|
|
6383
|
+
config2.name,
|
|
6384
|
+
config2.strategy,
|
|
6385
|
+
JSON.stringify(config2.pairs),
|
|
6386
|
+
config2.interval,
|
|
6387
|
+
config2.createdAt,
|
|
6388
|
+
config2.updatedAt
|
|
6389
|
+
]
|
|
6390
|
+
);
|
|
6391
|
+
return config2;
|
|
6392
|
+
}
|
|
6393
|
+
async listAgents() {
|
|
6394
|
+
const { rows } = await this.query("SELECT * FROM agents ORDER BY created_at DESC");
|
|
6395
|
+
return rows.map((r) => ({
|
|
6396
|
+
id: r.id,
|
|
6397
|
+
name: r.name,
|
|
6398
|
+
strategy: r.strategy,
|
|
6399
|
+
pairs: typeof r.pairs === "string" ? JSON.parse(r.pairs) : r.pairs,
|
|
6400
|
+
interval: r.interval_seconds,
|
|
6401
|
+
createdAt: Number(r.created_at),
|
|
6402
|
+
updatedAt: Number(r.updated_at)
|
|
6403
|
+
}));
|
|
6404
|
+
}
|
|
6405
|
+
async getAgentById(id) {
|
|
6406
|
+
const { rows } = await this.query("SELECT * FROM agents WHERE id = $1", [id]);
|
|
6407
|
+
if (rows.length === 0) return null;
|
|
6408
|
+
const r = rows[0];
|
|
6409
|
+
return {
|
|
6410
|
+
id: r.id,
|
|
6411
|
+
name: r.name,
|
|
6412
|
+
strategy: r.strategy,
|
|
6413
|
+
pairs: typeof r.pairs === "string" ? JSON.parse(r.pairs) : r.pairs,
|
|
6414
|
+
interval: r.interval_seconds,
|
|
6415
|
+
createdAt: Number(r.created_at),
|
|
6416
|
+
updatedAt: Number(r.updated_at)
|
|
6417
|
+
};
|
|
6418
|
+
}
|
|
6419
|
+
async getAgentByName(name) {
|
|
6420
|
+
const { rows } = await this.query("SELECT * FROM agents WHERE name = $1", [name]);
|
|
6421
|
+
if (rows.length === 0) return null;
|
|
6422
|
+
const r = rows[0];
|
|
6423
|
+
return {
|
|
6424
|
+
id: r.id,
|
|
6425
|
+
name: r.name,
|
|
6426
|
+
strategy: r.strategy,
|
|
6427
|
+
pairs: typeof r.pairs === "string" ? JSON.parse(r.pairs) : r.pairs,
|
|
6428
|
+
interval: r.interval_seconds,
|
|
6429
|
+
createdAt: Number(r.created_at),
|
|
6430
|
+
updatedAt: Number(r.updated_at)
|
|
6431
|
+
};
|
|
6432
|
+
}
|
|
6433
|
+
async deleteAgent(id) {
|
|
6434
|
+
const result = await this.query("DELETE FROM agents WHERE id = $1", [id]);
|
|
6435
|
+
return (result.rowCount ?? 0) > 0;
|
|
6436
|
+
}
|
|
6437
|
+
async logDecision(result) {
|
|
6438
|
+
await this.query(
|
|
6439
|
+
`INSERT INTO agent_decisions (agent_id, symbol, action, confidence, reasoning, signals, created_at)
|
|
6440
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
|
6441
|
+
[
|
|
6442
|
+
result.agentId,
|
|
6443
|
+
result.symbol,
|
|
6444
|
+
result.decision.action,
|
|
6445
|
+
result.decision.confidence,
|
|
6446
|
+
JSON.stringify(result.decision.reasoning),
|
|
6447
|
+
JSON.stringify(result.signals),
|
|
6448
|
+
result.timestamp
|
|
6449
|
+
]
|
|
6450
|
+
);
|
|
6451
|
+
}
|
|
6452
|
+
async getDecisions(agentId, limit) {
|
|
6453
|
+
const { rows } = await this.query("SELECT * FROM agent_decisions WHERE agent_id = $1 ORDER BY created_at DESC LIMIT $2", [
|
|
6454
|
+
agentId,
|
|
6455
|
+
limit
|
|
6456
|
+
]);
|
|
6457
|
+
return rows.map((r) => ({
|
|
6458
|
+
agentId: r.agent_id,
|
|
6459
|
+
symbol: r.symbol,
|
|
6460
|
+
timestamp: Number(r.created_at),
|
|
6461
|
+
signals: typeof r.signals === "string" ? JSON.parse(r.signals) : r.signals,
|
|
6462
|
+
decision: {
|
|
6463
|
+
action: r.action,
|
|
6464
|
+
confidence: r.confidence,
|
|
6465
|
+
reasoning: typeof r.reasoning === "string" ? JSON.parse(r.reasoning) : r.reasoning
|
|
6466
|
+
}
|
|
6467
|
+
}));
|
|
6468
|
+
}
|
|
6469
|
+
// ---- Time-series ---------------------------------------------------------
|
|
6470
|
+
async insertOHLCV(records) {
|
|
6471
|
+
if (records.length === 0) return;
|
|
6472
|
+
const values = [];
|
|
6473
|
+
const placeholders = [];
|
|
6474
|
+
for (let i = 0; i < records.length; i++) {
|
|
6475
|
+
const r = records[i];
|
|
6476
|
+
const offset = i * 9;
|
|
6477
|
+
placeholders.push(
|
|
6478
|
+
`($${offset + 1}, $${offset + 2}, $${offset + 3}, $${offset + 4}, $${offset + 5}, $${offset + 6}, $${offset + 7}, $${offset + 8}, $${offset + 9})`
|
|
6479
|
+
);
|
|
6480
|
+
values.push(
|
|
6481
|
+
new Date(r.time).toISOString(),
|
|
6482
|
+
r.symbol,
|
|
6483
|
+
r.timeframe,
|
|
6484
|
+
r.open,
|
|
6485
|
+
r.high,
|
|
6486
|
+
r.low,
|
|
6487
|
+
r.close,
|
|
6488
|
+
r.volume,
|
|
6489
|
+
r.trades
|
|
6490
|
+
);
|
|
6491
|
+
}
|
|
6492
|
+
await this.query(
|
|
6493
|
+
`INSERT INTO ohlcv (time, symbol, timeframe, open, high, low, close, volume, trades)
|
|
6494
|
+
VALUES ${placeholders.join(", ")}
|
|
6495
|
+
ON CONFLICT (symbol, timeframe, time) DO UPDATE
|
|
6496
|
+
SET open = EXCLUDED.open, high = EXCLUDED.high, low = EXCLUDED.low,
|
|
6497
|
+
close = EXCLUDED.close, volume = EXCLUDED.volume, trades = EXCLUDED.trades`,
|
|
6498
|
+
values
|
|
6499
|
+
);
|
|
6500
|
+
}
|
|
6501
|
+
async queryOHLCV(symbol, timeframe, from, to) {
|
|
6502
|
+
const { rows } = await this.query(
|
|
6503
|
+
`SELECT * FROM ohlcv
|
|
6504
|
+
WHERE symbol = $1 AND timeframe = $2 AND time >= $3 AND time <= $4
|
|
6505
|
+
ORDER BY time ASC`,
|
|
6506
|
+
[symbol, timeframe, new Date(from).toISOString(), new Date(to).toISOString()]
|
|
6507
|
+
);
|
|
6508
|
+
return rows.map((r) => ({
|
|
6509
|
+
time: new Date(r.time).getTime(),
|
|
6510
|
+
symbol: r.symbol,
|
|
6511
|
+
timeframe: r.timeframe,
|
|
6512
|
+
open: r.open,
|
|
6513
|
+
high: r.high,
|
|
6514
|
+
low: r.low,
|
|
6515
|
+
close: r.close,
|
|
6516
|
+
volume: r.volume,
|
|
6517
|
+
trades: r.trades
|
|
6518
|
+
}));
|
|
6519
|
+
}
|
|
6520
|
+
// ---- Predictions ---------------------------------------------------------
|
|
6521
|
+
async logPrediction(prediction) {
|
|
6522
|
+
await this.query(
|
|
6523
|
+
`INSERT INTO predictions (symbol, model, direction, probability, horizon, features, predicted_at)
|
|
6524
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
|
6525
|
+
[
|
|
6526
|
+
prediction.symbol,
|
|
6527
|
+
prediction.model,
|
|
6528
|
+
prediction.direction,
|
|
6529
|
+
prediction.probability,
|
|
6530
|
+
prediction.horizon,
|
|
6531
|
+
JSON.stringify(prediction.features),
|
|
6532
|
+
new Date(prediction.predictedAt).toISOString()
|
|
6533
|
+
]
|
|
6534
|
+
);
|
|
6535
|
+
}
|
|
6536
|
+
async getPredictionAccuracy(model, days) {
|
|
6537
|
+
const since = new Date(Date.now() - days * 864e5).toISOString();
|
|
6538
|
+
const { rows: totals } = await this.query(
|
|
6539
|
+
`SELECT direction,
|
|
6540
|
+
COUNT(*)::text AS total,
|
|
6541
|
+
COUNT(*) FILTER (WHERE was_correct = true)::text AS correct
|
|
6542
|
+
FROM predictions
|
|
6543
|
+
WHERE model = $1 AND predicted_at >= $2 AND was_correct IS NOT NULL
|
|
6544
|
+
GROUP BY direction`,
|
|
6545
|
+
[model, since]
|
|
6546
|
+
);
|
|
6547
|
+
const byDir = {
|
|
6548
|
+
up: { total: 0, correct: 0, accuracy: 0 },
|
|
6549
|
+
down: { total: 0, correct: 0, accuracy: 0 },
|
|
6550
|
+
sideways: { total: 0, correct: 0, accuracy: 0 }
|
|
6551
|
+
};
|
|
6552
|
+
let totalAll = 0;
|
|
6553
|
+
let correctAll = 0;
|
|
6554
|
+
for (const row of totals) {
|
|
6555
|
+
const t = parseInt(row.total, 10);
|
|
6556
|
+
const c = parseInt(row.correct, 10);
|
|
6557
|
+
totalAll += t;
|
|
6558
|
+
correctAll += c;
|
|
6559
|
+
const dir = row.direction;
|
|
6560
|
+
if (byDir[dir]) {
|
|
6561
|
+
byDir[dir] = { total: t, correct: c, accuracy: t > 0 ? c / t : 0 };
|
|
6562
|
+
}
|
|
6563
|
+
}
|
|
6564
|
+
return {
|
|
6565
|
+
model,
|
|
6566
|
+
totalPredictions: totalAll,
|
|
6567
|
+
correctPredictions: correctAll,
|
|
6568
|
+
accuracy: totalAll > 0 ? correctAll / totalAll : 0,
|
|
6569
|
+
byDirection: byDir,
|
|
6570
|
+
period: `${days}d`
|
|
6571
|
+
};
|
|
6572
|
+
}
|
|
6573
|
+
// ---- Lifecycle -----------------------------------------------------------
|
|
6574
|
+
async close() {
|
|
6575
|
+
await this.pool.end();
|
|
6576
|
+
}
|
|
6577
|
+
};
|
|
6578
|
+
}
|
|
6579
|
+
});
|
|
6580
|
+
|
|
6581
|
+
// src/data/store-factory.ts
|
|
6582
|
+
async function getStore(config2) {
|
|
6583
|
+
if (instance) return instance;
|
|
6584
|
+
if (config2.database?.type === "postgres" && config2.database.url) {
|
|
6585
|
+
const { PostgresStore: PostgresStore2 } = await Promise.resolve().then(() => (init_postgres_store(), postgres_store_exports));
|
|
6586
|
+
instance = new PostgresStore2(config2.database.url);
|
|
6587
|
+
} else {
|
|
6588
|
+
instance = new SqliteStore();
|
|
6589
|
+
}
|
|
6590
|
+
return instance;
|
|
6591
|
+
}
|
|
6592
|
+
function getStoreInstance() {
|
|
6593
|
+
return instance;
|
|
6594
|
+
}
|
|
6595
|
+
var instance;
|
|
6596
|
+
var init_store_factory = __esm({
|
|
6597
|
+
"src/data/store-factory.ts"() {
|
|
6598
|
+
"use strict";
|
|
6599
|
+
init_sqlite_store();
|
|
6600
|
+
instance = null;
|
|
6601
|
+
}
|
|
6602
|
+
});
|
|
6603
|
+
|
|
6604
|
+
// src/ai/tool-handler.ts
|
|
6605
|
+
async function handleTool(name, input) {
|
|
6606
|
+
const params = input;
|
|
6607
|
+
switch (name) {
|
|
6608
|
+
case "get_token_info": {
|
|
6609
|
+
const address = String(params["address"] ?? "");
|
|
6610
|
+
const chain = String(params["chain"] ?? DEFAULT_CHAIN);
|
|
6611
|
+
const adapter = getAdapter(chain);
|
|
6612
|
+
await adapter.connect(void 0, getConfig().etherscanApiKey);
|
|
6613
|
+
const info = await adapter.getTokenInfo(address);
|
|
6614
|
+
return {
|
|
6615
|
+
address: info.address,
|
|
6616
|
+
name: info.name,
|
|
6617
|
+
symbol: info.symbol,
|
|
6618
|
+
decimals: info.decimals,
|
|
6619
|
+
totalSupply: info.totalSupply.toString()
|
|
6620
|
+
};
|
|
6621
|
+
}
|
|
6622
|
+
case "analyze_wallet": {
|
|
6623
|
+
const address = String(params["address"] ?? "");
|
|
6624
|
+
const chain = String(params["chain"] ?? DEFAULT_CHAIN);
|
|
6625
|
+
const adapter = getAdapter(chain);
|
|
6626
|
+
await adapter.connect(void 0, getConfig().etherscanApiKey);
|
|
6627
|
+
const analysis = await analyzeWallet(address, adapter);
|
|
6628
|
+
return {
|
|
6629
|
+
address: analysis.address,
|
|
6630
|
+
chain: analysis.chain,
|
|
6631
|
+
balance: analysis.balance.toString(),
|
|
6632
|
+
transactionCount: analysis.transactionCount,
|
|
6633
|
+
riskLevel: analysis.riskLevel,
|
|
6634
|
+
patterns: analysis.patterns
|
|
6635
|
+
};
|
|
6636
|
+
}
|
|
6637
|
+
case "check_rug_indicators": {
|
|
6638
|
+
const address = String(params["address"] ?? "");
|
|
6639
|
+
const chain = String(params["chain"] ?? DEFAULT_CHAIN);
|
|
6640
|
+
const adapter = getAdapter(chain);
|
|
6641
|
+
await adapter.connect(void 0, getConfig().etherscanApiKey);
|
|
6642
|
+
const indicators = await detectRugIndicators(address, adapter);
|
|
6643
|
+
return {
|
|
6644
|
+
isHoneypot: indicators.isHoneypot,
|
|
6645
|
+
hasLiquidityLock: indicators.hasLiquidityLock,
|
|
6646
|
+
ownerCanMint: indicators.ownerCanMint,
|
|
6647
|
+
ownerCanPause: indicators.ownerCanPause,
|
|
6648
|
+
hasBlacklist: indicators.hasBlacklist,
|
|
6649
|
+
highSellTax: indicators.highSellTax,
|
|
6650
|
+
riskScore: indicators.riskScore,
|
|
6651
|
+
details: indicators.details
|
|
6652
|
+
};
|
|
6653
|
+
}
|
|
6654
|
+
case "get_market_data": {
|
|
6655
|
+
const symbol = String(params["symbol"] ?? "");
|
|
6656
|
+
try {
|
|
6657
|
+
const binance = await fetchTickerPrice(symbol);
|
|
6658
|
+
const gecko = await fetchMarketData(symbol).catch(() => null);
|
|
6659
|
+
return {
|
|
6660
|
+
symbol: binance.symbol,
|
|
6661
|
+
name: gecko?.name ?? binance.symbol,
|
|
6662
|
+
price: binance.price,
|
|
6663
|
+
priceChange24h: binance.change24h,
|
|
6664
|
+
priceChange7d: gecko?.priceChange7d ?? null,
|
|
6665
|
+
volume24h: gecko?.volume24h ?? null,
|
|
6666
|
+
marketCap: gecko?.marketCap ?? null,
|
|
6667
|
+
rank: gecko?.rank ?? null,
|
|
6668
|
+
source: "binance+coingecko"
|
|
6669
|
+
};
|
|
6670
|
+
} catch {
|
|
6671
|
+
const data = await fetchMarketData(symbol);
|
|
6672
|
+
if (!data) {
|
|
6673
|
+
return { error: `No market data found for "${symbol}"` };
|
|
6674
|
+
}
|
|
6675
|
+
return data;
|
|
6676
|
+
}
|
|
6677
|
+
}
|
|
6678
|
+
case "search_upcoming_icos": {
|
|
6679
|
+
const category = params["category"] ? String(params["category"]) : void 0;
|
|
6680
|
+
const chain = params["chain"] ? String(params["chain"]) : void 0;
|
|
6681
|
+
const roundType = params["roundType"] ? String(params["roundType"]) : void 0;
|
|
6682
|
+
const projects = category || chain || roundType ? await searchICOs(void 0, category, chain, roundType) : await fetchUpcomingICOs();
|
|
6683
|
+
return {
|
|
6684
|
+
projects: projects.map((p) => ({
|
|
6685
|
+
name: p.name,
|
|
6686
|
+
category: p.category,
|
|
6687
|
+
chain: p.chain,
|
|
6688
|
+
roundType: p.roundType,
|
|
6689
|
+
raisedAmount: p.raisedAmount,
|
|
6690
|
+
valuation: p.valuation,
|
|
6691
|
+
investors: p.investors.slice(0, 5),
|
|
6692
|
+
startDate: p.startDate,
|
|
6693
|
+
description: p.description,
|
|
6694
|
+
website: p.website
|
|
6695
|
+
}))
|
|
6696
|
+
};
|
|
6697
|
+
}
|
|
6698
|
+
case "get_funding_history": {
|
|
6699
|
+
const fundingName = String(params["name"] ?? "");
|
|
6700
|
+
const type = String(params["type"] ?? "project");
|
|
6701
|
+
if (type === "investor") {
|
|
6702
|
+
const portfolio = await getInvestorPortfolio(fundingName);
|
|
6703
|
+
return {
|
|
6704
|
+
investor: fundingName,
|
|
6705
|
+
investments: portfolio.map((p) => ({
|
|
6706
|
+
name: p.name,
|
|
6707
|
+
round: p.roundType,
|
|
6708
|
+
amount: p.raisedAmount,
|
|
6709
|
+
chain: p.chain,
|
|
6710
|
+
category: p.category,
|
|
6711
|
+
date: p.startDate
|
|
6712
|
+
}))
|
|
6713
|
+
};
|
|
6714
|
+
}
|
|
6715
|
+
const history = await getProjectFundingHistory(fundingName);
|
|
6716
|
+
return {
|
|
6717
|
+
project: history.name,
|
|
6718
|
+
rounds: history.rounds.map((r) => ({
|
|
6719
|
+
round: r.roundType,
|
|
6720
|
+
amount: r.raisedAmount,
|
|
6721
|
+
valuation: r.valuation,
|
|
6722
|
+
investors: r.investors.slice(0, 5),
|
|
6723
|
+
date: r.startDate,
|
|
6724
|
+
previousRounds: r.previousRounds
|
|
6725
|
+
}))
|
|
6726
|
+
};
|
|
6727
|
+
}
|
|
6728
|
+
case "search_token_dex": {
|
|
6729
|
+
const query = String(params["query"] ?? "");
|
|
6730
|
+
const pairs = await fetchTokenFromDex(query);
|
|
6731
|
+
return {
|
|
6732
|
+
results: pairs.slice(0, 5).map((p) => ({
|
|
6733
|
+
name: p.baseToken.name,
|
|
6734
|
+
symbol: p.baseToken.symbol,
|
|
6735
|
+
chain: p.chainId,
|
|
6736
|
+
dex: p.dexId,
|
|
6737
|
+
priceUsd: p.priceUsd,
|
|
6738
|
+
volume24h: p.volume?.h24 ?? 0,
|
|
6739
|
+
liquidity: p.liquidity?.usd ?? 0,
|
|
6740
|
+
priceChange24h: p.priceChange?.h24 ?? 0,
|
|
6741
|
+
marketCap: p.marketCap ?? p.fdv ?? null,
|
|
6742
|
+
buys24h: p.txns?.h24?.buys ?? 0,
|
|
6743
|
+
sells24h: p.txns?.h24?.sells ?? 0,
|
|
6744
|
+
pairAddress: p.pairAddress,
|
|
6745
|
+
url: p.url
|
|
6746
|
+
}))
|
|
6747
|
+
};
|
|
6748
|
+
}
|
|
6749
|
+
case "get_trending": {
|
|
6750
|
+
const trending = await fetchTrendingTokens();
|
|
6751
|
+
return {
|
|
6752
|
+
trending: trending.slice(0, 10).map((t) => ({
|
|
6753
|
+
name: t.name,
|
|
6754
|
+
symbol: t.symbol,
|
|
6755
|
+
chain: t.chain,
|
|
6756
|
+
priceUsd: t.priceUsd,
|
|
6757
|
+
priceChange24h: t.priceChange24h,
|
|
6758
|
+
volume24h: t.volume24h,
|
|
6759
|
+
marketCap: t.marketCap,
|
|
6760
|
+
source: t.source,
|
|
6761
|
+
url: t.url
|
|
6762
|
+
}))
|
|
6763
|
+
};
|
|
6764
|
+
}
|
|
6765
|
+
case "get_crypto_news": {
|
|
6766
|
+
const symbol = params["symbol"] ? String(params["symbol"]) : void 0;
|
|
6767
|
+
const news = await fetchCryptoNews(symbol, getConfig().cryptopanicApiKey);
|
|
6768
|
+
return {
|
|
6769
|
+
news: news.slice(0, 10).map((n) => ({
|
|
6770
|
+
title: n.title,
|
|
6771
|
+
sentiment: n.sentiment,
|
|
6772
|
+
source: n.source.title,
|
|
6773
|
+
publishedAt: n.publishedAt,
|
|
6774
|
+
url: n.url
|
|
6775
|
+
}))
|
|
6776
|
+
};
|
|
6777
|
+
}
|
|
6778
|
+
case "get_raises": {
|
|
6779
|
+
const raises = await fetchRecentRaises(30);
|
|
6780
|
+
let filtered = raises;
|
|
6781
|
+
if (params["category"]) {
|
|
6782
|
+
const cat = String(params["category"]).toLowerCase();
|
|
6783
|
+
filtered = filtered.filter(
|
|
6784
|
+
(r) => r.category?.toLowerCase().includes(cat) || r.sector?.toLowerCase().includes(cat)
|
|
6785
|
+
);
|
|
6786
|
+
}
|
|
6787
|
+
if (params["chain"]) {
|
|
6788
|
+
const ch = String(params["chain"]).toLowerCase();
|
|
6789
|
+
filtered = filtered.filter((r) => r.chains.some((c) => c.toLowerCase().includes(ch)));
|
|
6790
|
+
}
|
|
6791
|
+
return {
|
|
6792
|
+
raises: filtered.slice(0, 10).map((r) => ({
|
|
6793
|
+
name: r.name,
|
|
6794
|
+
round: r.round,
|
|
6795
|
+
amount: r.amount,
|
|
6796
|
+
chains: r.chains,
|
|
6797
|
+
sector: r.sector,
|
|
6798
|
+
category: r.category,
|
|
6799
|
+
leadInvestors: r.leadInvestors,
|
|
6800
|
+
date: new Date(r.date * 1e3).toISOString().split("T")[0]
|
|
6801
|
+
}))
|
|
6802
|
+
};
|
|
6803
|
+
}
|
|
6804
|
+
case "get_token_security": {
|
|
6805
|
+
const address = String(params["address"] ?? "");
|
|
6806
|
+
const chain = String(params["chain"] ?? "ethereum");
|
|
6807
|
+
const security = await checkTokenSecurity(address, chain);
|
|
6808
|
+
if (!security) {
|
|
6809
|
+
return { error: `No security data for ${address} on ${chain}` };
|
|
6810
|
+
}
|
|
6811
|
+
return {
|
|
6812
|
+
contractAddress: security.contractAddress,
|
|
6813
|
+
chain: security.chain,
|
|
6814
|
+
riskLevel: security.riskLevel,
|
|
6815
|
+
isHoneypot: security.isHoneypot,
|
|
6816
|
+
isMintable: security.isMintable,
|
|
6817
|
+
buyTax: security.buyTax,
|
|
6818
|
+
sellTax: security.sellTax,
|
|
6819
|
+
isOpenSource: security.isOpenSource,
|
|
6820
|
+
isProxy: security.isProxy,
|
|
6821
|
+
hiddenOwner: security.hiddenOwner,
|
|
6822
|
+
cannotBuy: security.cannotBuy,
|
|
6823
|
+
cannotSellAll: security.cannotSellAll,
|
|
6824
|
+
isBlacklisted: security.isBlacklisted,
|
|
6825
|
+
holderCount: security.holderCount,
|
|
6826
|
+
lpHolderCount: security.lpHolderCount,
|
|
6827
|
+
creatorPercent: security.creatorPercent,
|
|
6828
|
+
ownerPercent: security.ownerPercent,
|
|
6829
|
+
trustList: security.trustList
|
|
6830
|
+
};
|
|
6831
|
+
}
|
|
6832
|
+
case "get_fear_greed": {
|
|
6833
|
+
const data = await fetchFearGreedIndex(7);
|
|
6834
|
+
return {
|
|
6835
|
+
current: { value: data.current.value, classification: data.current.classification },
|
|
6836
|
+
previous: data.previous ? { value: data.previous.value, classification: data.previous.classification } : null,
|
|
6837
|
+
history: data.history.map((h) => ({
|
|
6838
|
+
value: h.value,
|
|
6839
|
+
classification: h.classification,
|
|
6840
|
+
date: new Date(h.timestamp * 1e3).toISOString().split("T")[0]
|
|
6841
|
+
}))
|
|
6842
|
+
};
|
|
6843
|
+
}
|
|
6844
|
+
case "get_derivatives_data": {
|
|
6845
|
+
const symbol = String(params["symbol"] ?? "BTC");
|
|
6846
|
+
const [fundingResult, oiResult] = await Promise.allSettled([
|
|
6847
|
+
fetchFundingRate(symbol),
|
|
6848
|
+
fetchOpenInterest(symbol)
|
|
6849
|
+
]);
|
|
6850
|
+
const result = { symbol: symbol.toUpperCase() };
|
|
6851
|
+
if (fundingResult.status === "fulfilled") {
|
|
6852
|
+
result["fundingRate"] = fundingResult.value.fundingRate;
|
|
6853
|
+
result["fundingRatePct"] = `${(fundingResult.value.fundingRate * 100).toFixed(4)}%`;
|
|
6854
|
+
result["markPrice"] = fundingResult.value.markPrice;
|
|
6855
|
+
}
|
|
6856
|
+
if (oiResult.status === "fulfilled") {
|
|
6857
|
+
result["openInterest"] = oiResult.value.openInterest;
|
|
6858
|
+
result["openInterestNotional"] = oiResult.value.notionalValue;
|
|
6859
|
+
}
|
|
6860
|
+
return result;
|
|
6861
|
+
}
|
|
6862
|
+
case "get_technical_analysis": {
|
|
6863
|
+
const symbol = String(params["symbol"] ?? "BTC");
|
|
6864
|
+
const timeframe = String(params["timeframe"] ?? "4h");
|
|
6865
|
+
const ta = await analyzeTechnicals(symbol, timeframe);
|
|
6866
|
+
return {
|
|
6867
|
+
symbol: ta.symbol,
|
|
6868
|
+
timeframe: ta.timeframe,
|
|
6869
|
+
composite: ta.composite,
|
|
6870
|
+
signals: ta.signals.map((s) => ({
|
|
6871
|
+
name: s.name,
|
|
6872
|
+
signal: s.signal,
|
|
6873
|
+
strength: s.strength,
|
|
6874
|
+
description: s.description
|
|
6875
|
+
})),
|
|
6876
|
+
indicators: {
|
|
6877
|
+
rsi: ta.indicators.rsi ? Math.round(ta.indicators.rsi * 100) / 100 : null,
|
|
6878
|
+
macd: ta.indicators.macd,
|
|
6879
|
+
bollingerBands: ta.indicators.bollingerBands,
|
|
6880
|
+
ema12: ta.indicators.ema12,
|
|
6881
|
+
ema26: ta.indicators.ema26,
|
|
6882
|
+
atr: ta.indicators.atr
|
|
6883
|
+
}
|
|
6884
|
+
};
|
|
6885
|
+
}
|
|
6886
|
+
case "get_prediction": {
|
|
6887
|
+
const symbol = String(params["symbol"] ?? "BTC");
|
|
6888
|
+
const prediction = await generatePrediction(symbol);
|
|
6889
|
+
return {
|
|
6890
|
+
symbol: prediction.symbol,
|
|
6891
|
+
direction: prediction.direction,
|
|
6892
|
+
confidence: prediction.confidence,
|
|
6893
|
+
composite: prediction.composite,
|
|
6894
|
+
timeframe: prediction.timeframe,
|
|
6895
|
+
signals: prediction.signals,
|
|
6896
|
+
reasoning: prediction.reasoning,
|
|
6897
|
+
disclaimer: prediction.disclaimer
|
|
6898
|
+
};
|
|
6899
|
+
}
|
|
6900
|
+
case "get_ml_prediction": {
|
|
6901
|
+
const symbol = String(params["symbol"] ?? "BTC");
|
|
6902
|
+
const mlClient2 = getMLClient();
|
|
6903
|
+
if (!mlClient2) {
|
|
6904
|
+
const prediction = await generatePrediction(symbol);
|
|
6905
|
+
return {
|
|
6906
|
+
...prediction,
|
|
6907
|
+
mlAvailable: false,
|
|
6908
|
+
note: "ML sidecar not configured; using rule-based prediction"
|
|
6909
|
+
};
|
|
6910
|
+
}
|
|
6911
|
+
const features = await buildFeatureVector(symbol);
|
|
6912
|
+
const mlPred = await mlClient2.predict(features);
|
|
6913
|
+
if (!mlPred) {
|
|
6914
|
+
const prediction = await generatePrediction(symbol);
|
|
6915
|
+
return {
|
|
6916
|
+
...prediction,
|
|
6917
|
+
mlAvailable: false,
|
|
6918
|
+
note: "ML sidecar unavailable; using rule-based prediction"
|
|
6919
|
+
};
|
|
6920
|
+
}
|
|
6921
|
+
return {
|
|
6922
|
+
symbol: mlPred.symbol,
|
|
6923
|
+
direction: mlPred.direction,
|
|
6924
|
+
probability: mlPred.probability,
|
|
6925
|
+
confidence: mlPred.confidence,
|
|
6926
|
+
model: mlPred.model,
|
|
6927
|
+
horizon: mlPred.horizon,
|
|
6928
|
+
mlAvailable: true,
|
|
6929
|
+
features: {
|
|
6930
|
+
rsi: features.rsi,
|
|
6931
|
+
macdHistogram: features.macdHistogram,
|
|
6932
|
+
bollingerPercentB: features.bollingerPercentB,
|
|
6933
|
+
fundingRate: features.fundingRate,
|
|
6934
|
+
fearGreed: features.fearGreed,
|
|
6935
|
+
rsiSlope: features.rsiSlope,
|
|
6936
|
+
volumeRatio: features.volumeRatio,
|
|
6937
|
+
emaCrossoverPct: features.emaCrossoverPct,
|
|
6938
|
+
atrPct: features.atrPct
|
|
6939
|
+
}
|
|
6940
|
+
};
|
|
6941
|
+
}
|
|
6942
|
+
case "get_model_accuracy": {
|
|
6943
|
+
const model = String(params["model"] ?? "lstm-predictor");
|
|
6944
|
+
const days = params["days"] ? Number(params["days"]) : 30;
|
|
6945
|
+
const store = getStoreInstance();
|
|
6946
|
+
if (!store) {
|
|
6947
|
+
return { error: "DataStore not initialized. ML accuracy requires PostgreSQL backend." };
|
|
6948
|
+
}
|
|
6949
|
+
const accuracy = await store.getPredictionAccuracy(model, days);
|
|
6950
|
+
return {
|
|
6951
|
+
model: accuracy.model,
|
|
6952
|
+
period: accuracy.period,
|
|
6953
|
+
totalPredictions: accuracy.totalPredictions,
|
|
6954
|
+
correctPredictions: accuracy.correctPredictions,
|
|
6955
|
+
accuracy: `${(accuracy.accuracy * 100).toFixed(1)}%`,
|
|
6956
|
+
byDirection: {
|
|
6957
|
+
up: `${(accuracy.byDirection.up.accuracy * 100).toFixed(1)}% (${accuracy.byDirection.up.correct}/${accuracy.byDirection.up.total})`,
|
|
6958
|
+
down: `${(accuracy.byDirection.down.accuracy * 100).toFixed(1)}% (${accuracy.byDirection.down.correct}/${accuracy.byDirection.down.total})`,
|
|
6959
|
+
sideways: `${(accuracy.byDirection.sideways.accuracy * 100).toFixed(1)}% (${accuracy.byDirection.sideways.correct}/${accuracy.byDirection.sideways.total})`
|
|
6960
|
+
}
|
|
6961
|
+
};
|
|
6962
|
+
}
|
|
6963
|
+
case "create_agent": {
|
|
6964
|
+
const agentName = String(params["name"] ?? "");
|
|
6965
|
+
const strategy = String(params["strategy"] ?? "momentum");
|
|
6966
|
+
const pairsRaw = String(params["pairs"] ?? "BTC,ETH");
|
|
6967
|
+
const interval = params["interval"] ? Number(params["interval"]) : 60;
|
|
6968
|
+
const agentPairs = pairsRaw.split(",").map((p) => p.trim().toUpperCase());
|
|
6969
|
+
const agent = createAgent(agentName, strategy, agentPairs, interval);
|
|
6970
|
+
return {
|
|
6971
|
+
id: agent.id,
|
|
6972
|
+
name: agent.name,
|
|
6973
|
+
strategy: agent.strategy,
|
|
6974
|
+
pairs: agent.pairs,
|
|
6975
|
+
interval: agent.interval,
|
|
6976
|
+
message: `Agent "${agent.name}" created. Use /agent start ${agent.name} to activate.`
|
|
6977
|
+
};
|
|
6978
|
+
}
|
|
6979
|
+
case "list_agents": {
|
|
6980
|
+
const agents = listAgents();
|
|
6981
|
+
return {
|
|
6982
|
+
agents: agents.map((a) => {
|
|
6983
|
+
const status = getAgentStatus(a.id);
|
|
6984
|
+
return {
|
|
6985
|
+
name: a.name,
|
|
6986
|
+
strategy: a.strategy,
|
|
6987
|
+
pairs: a.pairs,
|
|
6988
|
+
interval: a.interval,
|
|
6989
|
+
status: status?.status ?? "idle",
|
|
6990
|
+
cycleCount: status?.cycleCount ?? 0
|
|
6991
|
+
};
|
|
6992
|
+
})
|
|
6993
|
+
};
|
|
6994
|
+
}
|
|
6995
|
+
case "get_agent_status": {
|
|
6996
|
+
const agentName = String(params["name"] ?? "");
|
|
6997
|
+
const agent = getAgentByName(agentName);
|
|
6998
|
+
if (!agent) return { error: `Agent "${agentName}" not found` };
|
|
6999
|
+
const state = getAgentStatus(agent.id);
|
|
7000
|
+
if (!state) return { error: `Agent "${agentName}" not found` };
|
|
7001
|
+
const decisions = getRecentDecisions(agent.id, 5);
|
|
7002
|
+
return {
|
|
7003
|
+
name: state.config.name,
|
|
7004
|
+
status: state.status,
|
|
7005
|
+
strategy: state.config.strategy,
|
|
7006
|
+
pairs: state.config.pairs,
|
|
7007
|
+
cycleCount: state.cycleCount,
|
|
7008
|
+
error: state.error,
|
|
7009
|
+
recentDecisions: decisions.map((d) => ({
|
|
7010
|
+
symbol: d.symbol,
|
|
7011
|
+
action: d.decision.action,
|
|
7012
|
+
confidence: d.decision.confidence,
|
|
7013
|
+
reasoning: d.decision.reasoning,
|
|
7014
|
+
timestamp: new Date(d.timestamp).toISOString()
|
|
7015
|
+
}))
|
|
7016
|
+
};
|
|
7017
|
+
}
|
|
7018
|
+
default:
|
|
7019
|
+
return { error: `Unknown tool: ${name}` };
|
|
7020
|
+
}
|
|
7021
|
+
}
|
|
7022
|
+
var init_tool_handler = __esm({
|
|
7023
|
+
"src/ai/tool-handler.ts"() {
|
|
7024
|
+
"use strict";
|
|
7025
|
+
init_registry();
|
|
7026
|
+
init_loader();
|
|
7027
|
+
init_constants();
|
|
7028
|
+
init_wallet_analyzer();
|
|
7029
|
+
init_rug_detector();
|
|
7030
|
+
init_market();
|
|
7031
|
+
init_ico_tracker();
|
|
7032
|
+
init_cryptopanic();
|
|
7033
|
+
init_defillama();
|
|
7034
|
+
init_binance();
|
|
7035
|
+
init_goplus();
|
|
7036
|
+
init_fear_greed();
|
|
7037
|
+
init_technical_analysis();
|
|
7038
|
+
init_predictor();
|
|
7039
|
+
init_agent();
|
|
7040
|
+
init_client();
|
|
7041
|
+
init_feature_engineer();
|
|
7042
|
+
init_store_factory();
|
|
7043
|
+
}
|
|
7044
|
+
});
|
|
7045
|
+
|
|
7046
|
+
// src/ai/tools.ts
|
|
7047
|
+
var VIZZOR_TOOLS;
|
|
7048
|
+
var init_tools = __esm({
|
|
7049
|
+
"src/ai/tools.ts"() {
|
|
7050
|
+
"use strict";
|
|
7051
|
+
VIZZOR_TOOLS = [
|
|
7052
|
+
{
|
|
7053
|
+
name: "get_token_info",
|
|
7054
|
+
description: "Get on-chain token information including name, symbol, decimals, total supply, and top holders for a given contract address and chain.",
|
|
7055
|
+
input_schema: {
|
|
7056
|
+
type: "object",
|
|
7057
|
+
properties: {
|
|
7058
|
+
address: {
|
|
7059
|
+
type: "string",
|
|
7060
|
+
description: "The token contract address (e.g. 0x...)."
|
|
7061
|
+
},
|
|
7062
|
+
chain: {
|
|
7063
|
+
type: "string",
|
|
7064
|
+
description: 'The blockchain to query (e.g. "ethereum", "bsc", "polygon", "arbitrum").'
|
|
7065
|
+
}
|
|
7066
|
+
},
|
|
7067
|
+
required: ["address", "chain"]
|
|
7068
|
+
}
|
|
7069
|
+
},
|
|
7070
|
+
{
|
|
7071
|
+
name: "analyze_wallet",
|
|
7072
|
+
description: "Analyze a wallet address for transaction patterns, token holdings, DeFi interactions, and behavioral signals such as accumulation or distribution phases.",
|
|
7073
|
+
input_schema: {
|
|
7074
|
+
type: "object",
|
|
7075
|
+
properties: {
|
|
7076
|
+
address: {
|
|
7077
|
+
type: "string",
|
|
7078
|
+
description: "The wallet address to analyze."
|
|
7079
|
+
},
|
|
7080
|
+
chain: {
|
|
7081
|
+
type: "string",
|
|
7082
|
+
description: 'The blockchain the wallet resides on (e.g. "ethereum", "bsc").'
|
|
7083
|
+
},
|
|
7084
|
+
depth: {
|
|
7085
|
+
type: "number",
|
|
7086
|
+
description: "How many recent transactions to inspect. Defaults to 100."
|
|
7087
|
+
}
|
|
7088
|
+
},
|
|
7089
|
+
required: ["address", "chain"]
|
|
7090
|
+
}
|
|
7091
|
+
},
|
|
7092
|
+
{
|
|
7093
|
+
name: "check_rug_indicators",
|
|
7094
|
+
description: "Check a token for common rug pull indicators including honeypot detection, liquidity locks, ownership status, hidden mints, and holder concentration.",
|
|
7095
|
+
input_schema: {
|
|
7096
|
+
type: "object",
|
|
7097
|
+
properties: {
|
|
7098
|
+
address: {
|
|
7099
|
+
type: "string",
|
|
7100
|
+
description: "The token contract address to check."
|
|
7101
|
+
},
|
|
7102
|
+
chain: {
|
|
7103
|
+
type: "string",
|
|
7104
|
+
description: "The blockchain the token is deployed on."
|
|
7105
|
+
}
|
|
7106
|
+
},
|
|
7107
|
+
required: ["address", "chain"]
|
|
7108
|
+
}
|
|
7109
|
+
},
|
|
7110
|
+
{
|
|
7111
|
+
name: "get_market_data",
|
|
7112
|
+
description: "Get LIVE current market data for a token including price, 24h volume, market cap, price change percentages, and circulating supply. Returns real-time data \u2014 never quote prices from training data.",
|
|
7113
|
+
input_schema: {
|
|
7114
|
+
type: "object",
|
|
7115
|
+
properties: {
|
|
7116
|
+
symbol: {
|
|
7117
|
+
type: "string",
|
|
7118
|
+
description: 'The token ticker symbol (e.g. "ETH", "BTC", "UNI").'
|
|
7119
|
+
},
|
|
7120
|
+
currency: {
|
|
7121
|
+
type: "string",
|
|
7122
|
+
description: 'The fiat currency for price quotes. Defaults to "usd".'
|
|
7123
|
+
}
|
|
7124
|
+
},
|
|
7125
|
+
required: ["symbol"]
|
|
7126
|
+
}
|
|
7127
|
+
},
|
|
7128
|
+
{
|
|
7129
|
+
name: "search_upcoming_icos",
|
|
7130
|
+
description: "Search for CURRENT upcoming ICOs, token launches, and fundraising rounds filtered by category, blockchain, or round type. Returns LIVE data from DeFiLlama raises and Pump.fun launches, updated daily. MUST call for any ICO/launch question \u2014 training data is stale.",
|
|
7131
|
+
input_schema: {
|
|
7132
|
+
type: "object",
|
|
7133
|
+
properties: {
|
|
7134
|
+
category: {
|
|
7135
|
+
type: "string",
|
|
7136
|
+
description: 'Filter by project category (e.g. "defi", "gaming", "infrastructure", "nft", "ai").'
|
|
7137
|
+
},
|
|
7138
|
+
chain: {
|
|
7139
|
+
type: "string",
|
|
7140
|
+
description: 'Filter by blockchain (e.g. "ethereum", "solana", "bsc").'
|
|
7141
|
+
},
|
|
7142
|
+
roundType: {
|
|
7143
|
+
type: "string",
|
|
7144
|
+
description: 'Filter by funding round type (e.g. "Seed", "Pre-Seed", "Series A", "Series B", "Token Launch").'
|
|
7145
|
+
},
|
|
7146
|
+
limit: {
|
|
7147
|
+
type: "number",
|
|
7148
|
+
description: "Maximum number of results to return. Defaults to 10."
|
|
7149
|
+
}
|
|
7150
|
+
},
|
|
7151
|
+
required: []
|
|
7152
|
+
}
|
|
7153
|
+
},
|
|
7154
|
+
{
|
|
7155
|
+
name: "get_funding_history",
|
|
7156
|
+
description: "Get complete funding history for a project by name. Returns all known fundraising rounds with amounts, investors, valuations, and dates. Also works for looking up an investor portfolio.",
|
|
7157
|
+
input_schema: {
|
|
7158
|
+
type: "object",
|
|
7159
|
+
properties: {
|
|
7160
|
+
name: {
|
|
7161
|
+
type: "string",
|
|
7162
|
+
description: "Project name or investor name to look up."
|
|
7163
|
+
},
|
|
7164
|
+
type: {
|
|
7165
|
+
type: "string",
|
|
7166
|
+
description: 'Type of lookup: "project" for project funding history, "investor" for investor portfolio. Defaults to "project".'
|
|
7167
|
+
}
|
|
7168
|
+
},
|
|
7169
|
+
required: ["name"]
|
|
7170
|
+
}
|
|
7171
|
+
},
|
|
7172
|
+
{
|
|
7173
|
+
name: "search_token_dex",
|
|
7174
|
+
description: "Search for any token on decentralized exchanges via DexScreener. Returns real-time price, volume, liquidity, buy/sell counts, pair info. Works for all tokens including meme coins and newly launched tokens.",
|
|
7175
|
+
input_schema: {
|
|
7176
|
+
type: "object",
|
|
7177
|
+
properties: {
|
|
7178
|
+
query: {
|
|
7179
|
+
type: "string",
|
|
7180
|
+
description: "Token name, symbol, or contract address to search for."
|
|
7181
|
+
}
|
|
7182
|
+
},
|
|
7183
|
+
required: ["query"]
|
|
7184
|
+
}
|
|
7185
|
+
},
|
|
7186
|
+
{
|
|
7187
|
+
name: "get_trending",
|
|
7188
|
+
description: "Get REAL-TIME trending and hot tokens from DexScreener (boosted tokens) and CoinGecko trending combined. Returns what the market is excited about RIGHT NOW \u2014 trends change hourly. Always call for trending/hot token questions.",
|
|
7189
|
+
input_schema: {
|
|
7190
|
+
type: "object",
|
|
7191
|
+
properties: {},
|
|
7192
|
+
required: []
|
|
7193
|
+
}
|
|
7194
|
+
},
|
|
7195
|
+
{
|
|
7196
|
+
name: "get_crypto_news",
|
|
7197
|
+
description: "Get LIVE latest crypto news with sentiment analysis for a specific token or the market in general. Returns current headlines from CryptoPanic \u2014 MUST call for news questions, training data is outdated.",
|
|
7198
|
+
input_schema: {
|
|
7199
|
+
type: "object",
|
|
7200
|
+
properties: {
|
|
7201
|
+
symbol: {
|
|
7202
|
+
type: "string",
|
|
7203
|
+
description: 'Token symbol to filter news for (e.g. "BTC", "ETH", "SOL"). Omit for general crypto news.'
|
|
7204
|
+
}
|
|
7205
|
+
},
|
|
7206
|
+
required: []
|
|
7207
|
+
}
|
|
7208
|
+
},
|
|
7209
|
+
{
|
|
7210
|
+
name: "get_raises",
|
|
7211
|
+
description: "Get LIVE recent crypto fundraising rounds, venture capital investments, and token launches. Returns CURRENT data updated daily from DeFiLlama \u2014 always call this for ICO/funding questions, never use training data.",
|
|
7212
|
+
input_schema: {
|
|
7213
|
+
type: "object",
|
|
7214
|
+
properties: {
|
|
7215
|
+
category: {
|
|
7216
|
+
type: "string",
|
|
7217
|
+
description: 'Filter by sector/category (e.g. "defi", "infrastructure", "gaming").'
|
|
7218
|
+
},
|
|
7219
|
+
chain: {
|
|
7220
|
+
type: "string",
|
|
7221
|
+
description: 'Filter by blockchain (e.g. "ethereum", "solana").'
|
|
7222
|
+
}
|
|
7223
|
+
},
|
|
7224
|
+
required: []
|
|
7225
|
+
}
|
|
7226
|
+
},
|
|
7227
|
+
{
|
|
7228
|
+
name: "get_token_security",
|
|
7229
|
+
description: "Check token security via GoPlus API. Returns honeypot detection, tax analysis, mint/pause/blacklist capabilities, holder stats, and overall risk level. No API key required.",
|
|
7230
|
+
input_schema: {
|
|
7231
|
+
type: "object",
|
|
7232
|
+
properties: {
|
|
7233
|
+
address: {
|
|
7234
|
+
type: "string",
|
|
7235
|
+
description: "The token contract address."
|
|
7236
|
+
},
|
|
7237
|
+
chain: {
|
|
7238
|
+
type: "string",
|
|
7239
|
+
description: 'The blockchain (e.g. "ethereum", "bsc", "polygon", "arbitrum", "base").'
|
|
7240
|
+
}
|
|
7241
|
+
},
|
|
7242
|
+
required: ["address", "chain"]
|
|
7243
|
+
}
|
|
7244
|
+
},
|
|
7245
|
+
{
|
|
7246
|
+
name: "get_fear_greed",
|
|
7247
|
+
description: "Get the LIVE Crypto Fear & Greed Index with 7-day history (updated daily). Values: 0-20 Extreme Fear, 21-40 Fear, 41-60 Neutral, 61-80 Greed, 81-100 Extreme Greed. Call for any market sentiment question.",
|
|
7248
|
+
input_schema: {
|
|
7249
|
+
type: "object",
|
|
7250
|
+
properties: {},
|
|
7251
|
+
required: []
|
|
7252
|
+
}
|
|
7253
|
+
},
|
|
7254
|
+
{
|
|
7255
|
+
name: "get_derivatives_data",
|
|
7256
|
+
description: "Get LIVE derivatives data from Binance Futures: funding rate (updates every 8h), open interest, and mark price for a trading pair. Essential for current market positioning analysis.",
|
|
7257
|
+
input_schema: {
|
|
7258
|
+
type: "object",
|
|
7259
|
+
properties: {
|
|
7260
|
+
symbol: {
|
|
7261
|
+
type: "string",
|
|
7262
|
+
description: 'The token symbol (e.g. "BTC", "ETH", "SOL").'
|
|
7263
|
+
}
|
|
7264
|
+
},
|
|
7265
|
+
required: ["symbol"]
|
|
7266
|
+
}
|
|
7267
|
+
},
|
|
7268
|
+
{
|
|
7269
|
+
name: "get_technical_analysis",
|
|
7270
|
+
description: "Run technical analysis on a token: RSI, MACD, Bollinger Bands, EMA crossovers, ATR, OBV. Returns individual indicator signals and a composite direction with confidence.",
|
|
7271
|
+
input_schema: {
|
|
7272
|
+
type: "object",
|
|
7273
|
+
properties: {
|
|
7274
|
+
symbol: {
|
|
7275
|
+
type: "string",
|
|
7276
|
+
description: 'The token symbol (e.g. "BTC", "ETH", "SOL").'
|
|
7277
|
+
},
|
|
7278
|
+
timeframe: {
|
|
7279
|
+
type: "string",
|
|
7280
|
+
description: 'Kline interval: "1h", "4h", "1d". Defaults to "4h".'
|
|
7281
|
+
}
|
|
7282
|
+
},
|
|
7283
|
+
required: ["symbol"]
|
|
7284
|
+
}
|
|
7285
|
+
},
|
|
7286
|
+
{
|
|
7287
|
+
name: "get_prediction",
|
|
7288
|
+
description: "Generate a multi-signal composite prediction combining technical analysis (40%), sentiment (20%), derivatives (20%), trend (15%), and macro (5%). Returns direction, confidence, composite score, and reasoning.",
|
|
7289
|
+
input_schema: {
|
|
7290
|
+
type: "object",
|
|
7291
|
+
properties: {
|
|
7292
|
+
symbol: {
|
|
7293
|
+
type: "string",
|
|
7294
|
+
description: 'The token symbol (e.g. "BTC", "ETH", "SOL").'
|
|
7295
|
+
}
|
|
7296
|
+
},
|
|
7297
|
+
required: ["symbol"]
|
|
7298
|
+
}
|
|
7299
|
+
},
|
|
7300
|
+
{
|
|
7301
|
+
name: "get_ml_prediction",
|
|
7302
|
+
description: "Get an ML-enhanced prediction using LSTM/Random Forest models from the ML sidecar. Returns direction, probability, model confidence, and feature importance. Falls back to rule-based prediction if ML sidecar is unavailable.",
|
|
7303
|
+
input_schema: {
|
|
7304
|
+
type: "object",
|
|
7305
|
+
properties: {
|
|
7306
|
+
symbol: {
|
|
7307
|
+
type: "string",
|
|
7308
|
+
description: 'The token symbol (e.g. "BTC", "ETH", "SOL").'
|
|
7309
|
+
}
|
|
7310
|
+
},
|
|
7311
|
+
required: ["symbol"]
|
|
7312
|
+
}
|
|
7313
|
+
},
|
|
7314
|
+
{
|
|
7315
|
+
name: "get_model_accuracy",
|
|
7316
|
+
description: "Get historical accuracy metrics for ML prediction models. Shows total predictions, accuracy percentage, and breakdown by direction (up/down/sideways).",
|
|
7317
|
+
input_schema: {
|
|
7318
|
+
type: "object",
|
|
7319
|
+
properties: {
|
|
7320
|
+
model: {
|
|
7321
|
+
type: "string",
|
|
7322
|
+
description: 'Model name (e.g. "lstm-predictor", "signal-classifier"). Defaults to "lstm-predictor".'
|
|
7323
|
+
},
|
|
7324
|
+
days: {
|
|
7325
|
+
type: "number",
|
|
7326
|
+
description: "Number of days to look back for accuracy stats. Defaults to 30."
|
|
7327
|
+
}
|
|
7328
|
+
},
|
|
7329
|
+
required: []
|
|
7330
|
+
}
|
|
7331
|
+
},
|
|
7332
|
+
{
|
|
7333
|
+
name: "create_agent",
|
|
7334
|
+
description: "Create an autonomous trading agent that monitors crypto pairs using a strategy (momentum or trend-following). Returns the created agent config.",
|
|
7335
|
+
input_schema: {
|
|
7336
|
+
type: "object",
|
|
7337
|
+
properties: {
|
|
7338
|
+
name: {
|
|
7339
|
+
type: "string",
|
|
7340
|
+
description: 'A unique name for the agent (e.g. "btc-momentum-bot").'
|
|
7341
|
+
},
|
|
7342
|
+
strategy: {
|
|
7343
|
+
type: "string",
|
|
7344
|
+
description: 'Trading strategy: "momentum" (RSI+MACD) or "trend-following" (EMA crossover).'
|
|
7345
|
+
},
|
|
7346
|
+
pairs: {
|
|
7347
|
+
type: "string",
|
|
7348
|
+
description: 'Comma-separated trading pairs (e.g. "BTC,ETH,SOL").'
|
|
7349
|
+
},
|
|
7350
|
+
interval: {
|
|
7351
|
+
type: "number",
|
|
7352
|
+
description: "Cycle interval in seconds. Defaults to 60."
|
|
7353
|
+
}
|
|
7354
|
+
},
|
|
7355
|
+
required: ["name", "strategy", "pairs"]
|
|
7356
|
+
}
|
|
7357
|
+
},
|
|
7358
|
+
{
|
|
7359
|
+
name: "list_agents",
|
|
7360
|
+
description: "List all created trading agents with their status, strategy, and monitored pairs.",
|
|
7361
|
+
input_schema: {
|
|
7362
|
+
type: "object",
|
|
7363
|
+
properties: {},
|
|
7364
|
+
required: []
|
|
7365
|
+
}
|
|
7366
|
+
},
|
|
7367
|
+
{
|
|
7368
|
+
name: "get_agent_status",
|
|
7369
|
+
description: "Get detailed status of a trading agent including cycle count, last decision, and recent trade signals.",
|
|
7370
|
+
input_schema: {
|
|
7371
|
+
type: "object",
|
|
7372
|
+
properties: {
|
|
7373
|
+
name: {
|
|
7374
|
+
type: "string",
|
|
7375
|
+
description: "The agent name."
|
|
7376
|
+
}
|
|
7377
|
+
},
|
|
7378
|
+
required: ["name"]
|
|
7379
|
+
}
|
|
7380
|
+
}
|
|
7381
|
+
];
|
|
7382
|
+
}
|
|
7383
|
+
});
|
|
7384
|
+
|
|
7385
|
+
// src/utils/message-split.ts
|
|
7386
|
+
function splitMessage(text, maxLen) {
|
|
7387
|
+
if (text.length <= maxLen) return [text];
|
|
7388
|
+
const chunks = [];
|
|
7389
|
+
let remaining = text;
|
|
7390
|
+
while (remaining.length > 0) {
|
|
7391
|
+
if (remaining.length <= maxLen) {
|
|
7392
|
+
chunks.push(remaining);
|
|
7393
|
+
break;
|
|
7394
|
+
}
|
|
7395
|
+
let splitAt = remaining.lastIndexOf("\n\n", maxLen);
|
|
7396
|
+
if (splitAt <= 0) splitAt = remaining.lastIndexOf("\n", maxLen);
|
|
7397
|
+
if (splitAt <= 0) splitAt = remaining.lastIndexOf(" ", maxLen);
|
|
7398
|
+
if (splitAt <= 0) splitAt = maxLen;
|
|
7399
|
+
chunks.push(remaining.slice(0, splitAt));
|
|
7400
|
+
remaining = remaining.slice(splitAt).trimStart();
|
|
7401
|
+
}
|
|
7402
|
+
return chunks;
|
|
7403
|
+
}
|
|
7404
|
+
var init_message_split = __esm({
|
|
7405
|
+
"src/utils/message-split.ts"() {
|
|
7406
|
+
"use strict";
|
|
7407
|
+
}
|
|
7408
|
+
});
|
|
7409
|
+
|
|
7410
|
+
// src/discord/middleware/rate-limit.ts
|
|
7411
|
+
function checkRateLimit(userId) {
|
|
7412
|
+
const now = Date.now();
|
|
7413
|
+
const entry = userLimits.get(userId);
|
|
7414
|
+
if (!entry || now >= entry.resetAt) {
|
|
7415
|
+
userLimits.set(userId, { count: 1, resetAt: now + WINDOW_MS });
|
|
7416
|
+
return { allowed: true };
|
|
7417
|
+
}
|
|
7418
|
+
if (entry.count >= MAX_REQUESTS) {
|
|
7419
|
+
return { allowed: false };
|
|
7420
|
+
}
|
|
7421
|
+
entry.count++;
|
|
7422
|
+
return { allowed: true };
|
|
7423
|
+
}
|
|
7424
|
+
function startRateLimitCleanup(intervalMs = 3e5) {
|
|
7425
|
+
return setInterval(() => {
|
|
7426
|
+
const now = Date.now();
|
|
7427
|
+
for (const [userId, entry] of userLimits) {
|
|
7428
|
+
if (now >= entry.resetAt) {
|
|
7429
|
+
userLimits.delete(userId);
|
|
7430
|
+
}
|
|
7431
|
+
}
|
|
7432
|
+
}, intervalMs);
|
|
7433
|
+
}
|
|
7434
|
+
var userLimits, MAX_REQUESTS, WINDOW_MS;
|
|
7435
|
+
var init_rate_limit = __esm({
|
|
7436
|
+
"src/discord/middleware/rate-limit.ts"() {
|
|
7437
|
+
"use strict";
|
|
7438
|
+
userLimits = /* @__PURE__ */ new Map();
|
|
7439
|
+
MAX_REQUESTS = 10;
|
|
7440
|
+
WINDOW_MS = 6e4;
|
|
7441
|
+
}
|
|
7442
|
+
});
|
|
7443
|
+
|
|
7444
|
+
// src/discord/commands/index.ts
|
|
7445
|
+
import { SlashCommandBuilder, EmbedBuilder } from "discord.js";
|
|
7446
|
+
function registerSlashCommands() {
|
|
7447
|
+
return [
|
|
7448
|
+
// Core commands
|
|
7449
|
+
new SlashCommandBuilder().setName("scan").setDescription("Analyze a crypto project").addStringOption(
|
|
7450
|
+
(opt) => opt.setName("project").setDescription("Project name or contract address").setRequired(true)
|
|
7451
|
+
).addStringOption(
|
|
7452
|
+
(opt) => opt.setName("chain").setDescription("Target chain").setRequired(false)
|
|
7453
|
+
).toJSON(),
|
|
7454
|
+
new SlashCommandBuilder().setName("trends").setDescription("Trending tokens + market data").toJSON(),
|
|
7455
|
+
new SlashCommandBuilder().setName("track").setDescription("Analyze a wallet").addStringOption(
|
|
7456
|
+
(opt) => opt.setName("wallet").setDescription("Wallet address").setRequired(true)
|
|
7457
|
+
).addStringOption(
|
|
7458
|
+
(opt) => opt.setName("chain").setDescription("Target chain").setRequired(false)
|
|
7459
|
+
).toJSON(),
|
|
7460
|
+
new SlashCommandBuilder().setName("ico").setDescription("Upcoming ICOs & fundraising rounds").toJSON(),
|
|
7461
|
+
new SlashCommandBuilder().setName("audit").setDescription("Audit a smart contract").addStringOption(
|
|
7462
|
+
(opt) => opt.setName("contract").setDescription("Contract address").setRequired(true)
|
|
7463
|
+
).addStringOption(
|
|
7464
|
+
(opt) => opt.setName("chain").setDescription("Target chain").setRequired(false)
|
|
7465
|
+
).toJSON(),
|
|
7466
|
+
new SlashCommandBuilder().setName("help").setDescription("Show all Vizzor commands").toJSON(),
|
|
7467
|
+
// Quick commands
|
|
7468
|
+
new SlashCommandBuilder().setName("price").setDescription("Quick price check").addStringOption(
|
|
7469
|
+
(opt) => opt.setName("symbol").setDescription("Token symbol (e.g. BTC, ETH)").setRequired(true)
|
|
7470
|
+
).toJSON(),
|
|
7471
|
+
new SlashCommandBuilder().setName("predict").setDescription("AI prediction with multi-signal analysis").addStringOption(
|
|
7472
|
+
(opt) => opt.setName("symbol").setDescription("Token symbol (e.g. BTC, ETH)").setRequired(true)
|
|
7473
|
+
).toJSON(),
|
|
7474
|
+
new SlashCommandBuilder().setName("wallet").setDescription("Check wallet balance").addStringOption(
|
|
7475
|
+
(opt) => opt.setName("address").setDescription("Ethereum address").setRequired(true)
|
|
7476
|
+
).toJSON(),
|
|
7477
|
+
// Agent commands
|
|
7478
|
+
new SlashCommandBuilder().setName("agent_create").setDescription("Create a trading agent").addStringOption((opt) => opt.setName("name").setDescription("Agent name").setRequired(true)).addStringOption(
|
|
7479
|
+
(opt) => opt.setName("strategy").setDescription("Strategy (momentum, trend-following)").setRequired(false)
|
|
7480
|
+
).addStringOption(
|
|
7481
|
+
(opt) => opt.setName("pairs").setDescription("Comma-separated pairs (e.g. BTC,ETH,SOL)").setRequired(false)
|
|
7482
|
+
).toJSON(),
|
|
7483
|
+
new SlashCommandBuilder().setName("agent_list").setDescription("List all trading agents").toJSON(),
|
|
7484
|
+
new SlashCommandBuilder().setName("agent_start").setDescription("Start a trading agent").addStringOption((opt) => opt.setName("name").setDescription("Agent name").setRequired(true)).toJSON(),
|
|
7485
|
+
new SlashCommandBuilder().setName("agent_stop").setDescription("Stop a trading agent").addStringOption((opt) => opt.setName("name").setDescription("Agent name").setRequired(true)).toJSON(),
|
|
7486
|
+
new SlashCommandBuilder().setName("agent_status").setDescription("View agent status & recent decisions").addStringOption((opt) => opt.setName("name").setDescription("Agent name").setRequired(true)).toJSON(),
|
|
7487
|
+
new SlashCommandBuilder().setName("agent_delete").setDescription("Delete a trading agent").addStringOption((opt) => opt.setName("name").setDescription("Agent name").setRequired(true)).toJSON()
|
|
6842
7488
|
];
|
|
6843
7489
|
}
|
|
6844
7490
|
async function handleSlashCommand(interaction) {
|
|
@@ -7345,7 +7991,7 @@ var init_bot = __esm({
|
|
|
7345
7991
|
"src/discord/bot.ts"() {
|
|
7346
7992
|
"use strict";
|
|
7347
7993
|
init_loader();
|
|
7348
|
-
|
|
7994
|
+
init_client2();
|
|
7349
7995
|
init_tool_handler();
|
|
7350
7996
|
init_tools();
|
|
7351
7997
|
init_chat();
|
|
@@ -7688,879 +8334,452 @@ async function handlePredict(ctx) {
|
|
|
7688
8334
|
msg += `\u{1F4C8} Composite: ${prediction.composite.toFixed(2)}
|
|
7689
8335
|
`;
|
|
7690
8336
|
msg += `\u23F1 Timeframe: ${prediction.timeframe}
|
|
7691
|
-
|
|
7692
|
-
`;
|
|
7693
|
-
msg += `Signals:
|
|
7694
|
-
`;
|
|
7695
|
-
msg += `\u2022 Technical: ${prediction.signals.technical}
|
|
7696
|
-
`;
|
|
7697
|
-
msg += `\u2022 Sentiment: ${prediction.signals.sentiment}
|
|
7698
|
-
`;
|
|
7699
|
-
msg += `\u2022 Derivatives: ${prediction.signals.derivatives}
|
|
7700
|
-
`;
|
|
7701
|
-
msg += `\u2022 Trend: ${prediction.signals.trend}
|
|
7702
|
-
`;
|
|
7703
|
-
msg += `\u2022 Macro: ${prediction.signals.macro}
|
|
7704
|
-
|
|
7705
|
-
`;
|
|
7706
|
-
if (prediction.reasoning.length > 0) {
|
|
7707
|
-
msg += prediction.reasoning.join("\n") + "\n\n";
|
|
7708
|
-
}
|
|
7709
|
-
msg += prediction.disclaimer;
|
|
7710
|
-
await ctx.reply(msg);
|
|
7711
|
-
} catch (error) {
|
|
7712
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
7713
|
-
await ctx.reply(`Prediction failed: ${msg}`);
|
|
7714
|
-
}
|
|
7715
|
-
}
|
|
7716
|
-
async function handleWallet(ctx) {
|
|
7717
|
-
const args2 = ctx.message?.text?.split(" ").slice(1) ?? [];
|
|
7718
|
-
const address = args2[0];
|
|
7719
|
-
if (!address) {
|
|
7720
|
-
await ctx.reply("Usage: /wallet <ethereum_address>\nExample: /wallet 0x...");
|
|
7721
|
-
return;
|
|
7722
|
-
}
|
|
7723
|
-
if (!isValidAddress(address)) {
|
|
7724
|
-
await ctx.reply("Invalid Ethereum address. Must start with 0x followed by 40 hex characters.");
|
|
7725
|
-
return;
|
|
7726
|
-
}
|
|
7727
|
-
try {
|
|
7728
|
-
const balance = await getWalletBalance(address);
|
|
7729
|
-
await ctx.reply(
|
|
7730
|
-
`\u{1F45B} Wallet Balance
|
|
7731
|
-
|
|
7732
|
-
Address: ${address}
|
|
7733
|
-
Balance: ${balance} ETH
|
|
7734
|
-
|
|
7735
|
-
Use /track <address> for full forensic analysis`
|
|
7736
|
-
);
|
|
7737
|
-
} catch (error) {
|
|
7738
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
7739
|
-
await ctx.reply(`Wallet query failed: ${msg}`);
|
|
7740
|
-
}
|
|
7741
|
-
}
|
|
7742
|
-
async function handleAgentHelp(ctx) {
|
|
7743
|
-
const strategies = listStrategies();
|
|
7744
|
-
await ctx.reply(
|
|
7745
|
-
`\u{1F916} Agent Management
|
|
7746
|
-
|
|
7747
|
-
Commands:
|
|
7748
|
-
/agent_create <name> <strategy> <pairs> - Create agent
|
|
7749
|
-
/agent_list - List all agents
|
|
7750
|
-
/agent_start <name> - Start an agent
|
|
7751
|
-
/agent_stop <name> - Stop an agent
|
|
7752
|
-
/agent_status <name> - View status & decisions
|
|
7753
|
-
/agent_delete <name> - Delete an agent
|
|
7754
|
-
|
|
7755
|
-
Available strategies: ${strategies.join(", ")}
|
|
7756
|
-
|
|
7757
|
-
Example:
|
|
7758
|
-
/agent_create mybot momentum BTC,ETH`
|
|
7759
|
-
);
|
|
7760
|
-
}
|
|
7761
|
-
async function handleAgentCreate(ctx) {
|
|
7762
|
-
const args2 = ctx.message?.text?.split(" ").slice(1) ?? [];
|
|
7763
|
-
const name = args2[0];
|
|
7764
|
-
const strategy = args2[1] ?? "momentum";
|
|
7765
|
-
const pairsStr = args2[2] ?? "BTC,ETH";
|
|
7766
|
-
if (!name) {
|
|
7767
|
-
await ctx.reply(
|
|
7768
|
-
"Usage: /agent_create <name> <strategy> <pairs>\nExample: /agent_create mybot momentum BTC,ETH,SOL"
|
|
7769
|
-
);
|
|
7770
|
-
return;
|
|
7771
|
-
}
|
|
7772
|
-
try {
|
|
7773
|
-
const pairs = pairsStr.split(",").map((p) => p.trim().toUpperCase());
|
|
7774
|
-
const agent = createAgent(name, strategy, pairs);
|
|
7775
|
-
await ctx.reply(
|
|
7776
|
-
`\u2705 Agent Created
|
|
7777
|
-
|
|
7778
|
-
Name: ${agent.name}
|
|
7779
|
-
Strategy: ${agent.strategy}
|
|
7780
|
-
Pairs: ${agent.pairs.join(", ")}
|
|
7781
|
-
Interval: ${agent.interval}s
|
|
7782
|
-
|
|
7783
|
-
Use /agent_start ${agent.name} to activate`
|
|
7784
|
-
);
|
|
7785
|
-
} catch (error) {
|
|
7786
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
7787
|
-
await ctx.reply(`Failed to create agent: ${msg}`);
|
|
7788
|
-
}
|
|
7789
|
-
}
|
|
7790
|
-
async function handleAgentList(ctx) {
|
|
7791
|
-
const agents = listAgents();
|
|
7792
|
-
if (agents.length === 0) {
|
|
7793
|
-
await ctx.reply("No agents created yet. Use /agent_create to create one.");
|
|
7794
|
-
return;
|
|
7795
|
-
}
|
|
7796
|
-
let msg = "\u{1F916} Your Agents\n\n";
|
|
7797
|
-
for (const agent of agents) {
|
|
7798
|
-
const state = getAgentStatus(agent.id);
|
|
7799
|
-
const statusEmoji = state?.status === "running" ? "\u{1F7E2}" : state?.status === "stopped" ? "\u{1F534}" : "\u26AA";
|
|
7800
|
-
msg += `${statusEmoji} ${agent.name} [${state?.status ?? "idle"}]
|
|
7801
|
-
`;
|
|
7802
|
-
msg += ` Strategy: ${agent.strategy} | Pairs: ${agent.pairs.join(", ")}
|
|
7803
|
-
`;
|
|
7804
|
-
msg += ` Cycles: ${state?.cycleCount ?? 0} | Interval: ${agent.interval}s
|
|
7805
|
-
|
|
7806
|
-
`;
|
|
7807
|
-
}
|
|
7808
|
-
await ctx.reply(msg);
|
|
7809
|
-
}
|
|
7810
|
-
async function handleAgentStart(ctx) {
|
|
7811
|
-
const args2 = ctx.message?.text?.split(" ").slice(1) ?? [];
|
|
7812
|
-
const name = args2[0];
|
|
7813
|
-
if (!name) {
|
|
7814
|
-
await ctx.reply("Usage: /agent_start <name>");
|
|
7815
|
-
return;
|
|
7816
|
-
}
|
|
7817
|
-
try {
|
|
7818
|
-
const agent = getAgentByName(name);
|
|
7819
|
-
if (!agent) {
|
|
7820
|
-
await ctx.reply(`Agent "${name}" not found. Use /agent_list to see your agents.`);
|
|
7821
|
-
return;
|
|
7822
|
-
}
|
|
7823
|
-
const state = startAgent(agent.id);
|
|
7824
|
-
await ctx.reply(
|
|
7825
|
-
`\u{1F7E2} Agent "${state.config.name}" started. Monitoring ${state.config.pairs.join(", ")}.`
|
|
7826
|
-
);
|
|
7827
|
-
} catch (error) {
|
|
7828
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
7829
|
-
await ctx.reply(`Failed to start agent: ${msg}`);
|
|
7830
|
-
}
|
|
7831
|
-
}
|
|
7832
|
-
async function handleAgentStop(ctx) {
|
|
7833
|
-
const args2 = ctx.message?.text?.split(" ").slice(1) ?? [];
|
|
7834
|
-
const name = args2[0];
|
|
7835
|
-
if (!name) {
|
|
7836
|
-
await ctx.reply("Usage: /agent_stop <name>");
|
|
7837
|
-
return;
|
|
7838
|
-
}
|
|
7839
|
-
try {
|
|
7840
|
-
const agent = getAgentByName(name);
|
|
7841
|
-
if (!agent) {
|
|
7842
|
-
await ctx.reply(`Agent "${name}" not found.`);
|
|
7843
|
-
return;
|
|
7844
|
-
}
|
|
7845
|
-
const state = stopAgent(agent.id);
|
|
7846
|
-
await ctx.reply(`\u{1F534} Agent "${state.config.name}" stopped after ${state.cycleCount} cycles.`);
|
|
7847
|
-
} catch (error) {
|
|
7848
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
7849
|
-
await ctx.reply(`Failed to stop agent: ${msg}`);
|
|
7850
|
-
}
|
|
7851
|
-
}
|
|
7852
|
-
async function handleAgentStatus(ctx) {
|
|
7853
|
-
const args2 = ctx.message?.text?.split(" ").slice(1) ?? [];
|
|
7854
|
-
const name = args2[0];
|
|
7855
|
-
if (!name) {
|
|
7856
|
-
await ctx.reply("Usage: /agent_status <name>");
|
|
7857
|
-
return;
|
|
7858
|
-
}
|
|
7859
|
-
const agent = getAgentByName(name);
|
|
7860
|
-
if (!agent) {
|
|
7861
|
-
await ctx.reply(`Agent "${name}" not found.`);
|
|
7862
|
-
return;
|
|
7863
|
-
}
|
|
7864
|
-
const state = getAgentStatus(agent.id);
|
|
7865
|
-
if (!state) {
|
|
7866
|
-
await ctx.reply(`Agent "${name}" not found.`);
|
|
7867
|
-
return;
|
|
7868
|
-
}
|
|
7869
|
-
const statusEmoji = state.status === "running" ? "\u{1F7E2}" : state.status === "stopped" ? "\u{1F534}" : "\u26AA";
|
|
7870
|
-
let msg = `\u{1F916} Agent: ${state.config.name}
|
|
7871
|
-
|
|
7872
|
-
`;
|
|
7873
|
-
msg += `${statusEmoji} Status: ${state.status}
|
|
7874
|
-
`;
|
|
7875
|
-
msg += `Strategy: ${state.config.strategy}
|
|
8337
|
+
|
|
7876
8338
|
`;
|
|
7877
|
-
|
|
8339
|
+
msg += `Signals:
|
|
7878
8340
|
`;
|
|
7879
|
-
|
|
8341
|
+
msg += `\u2022 Technical: ${prediction.signals.technical}
|
|
7880
8342
|
`;
|
|
7881
|
-
|
|
8343
|
+
msg += `\u2022 Sentiment: ${prediction.signals.sentiment}
|
|
7882
8344
|
`;
|
|
7883
|
-
|
|
8345
|
+
msg += `\u2022 Derivatives: ${prediction.signals.derivatives}
|
|
7884
8346
|
`;
|
|
7885
|
-
|
|
7886
|
-
if (decisions.length > 0) {
|
|
7887
|
-
msg += "\nRecent Decisions:\n";
|
|
7888
|
-
for (const d of decisions) {
|
|
7889
|
-
const actionEmoji = d.decision.action === "buy" ? "\u{1F7E2}" : d.decision.action === "sell" ? "\u{1F534}" : "\u26AA";
|
|
7890
|
-
const time = new Date(d.timestamp).toLocaleString();
|
|
7891
|
-
msg += `${actionEmoji} ${d.symbol} ${d.decision.action.toUpperCase()} (${d.decision.confidence}%) \u2014 ${time}
|
|
8347
|
+
msg += `\u2022 Trend: ${prediction.signals.trend}
|
|
7892
8348
|
`;
|
|
7893
|
-
|
|
7894
|
-
|
|
8349
|
+
msg += `\u2022 Macro: ${prediction.signals.macro}
|
|
8350
|
+
|
|
7895
8351
|
`;
|
|
7896
|
-
|
|
8352
|
+
if (prediction.reasoning.length > 0) {
|
|
8353
|
+
msg += prediction.reasoning.join("\n") + "\n\n";
|
|
7897
8354
|
}
|
|
8355
|
+
msg += prediction.disclaimer;
|
|
8356
|
+
await ctx.reply(msg);
|
|
8357
|
+
} catch (error) {
|
|
8358
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
8359
|
+
await ctx.reply(`Prediction failed: ${msg}`);
|
|
7898
8360
|
}
|
|
7899
|
-
await ctx.reply(msg);
|
|
7900
8361
|
}
|
|
7901
|
-
async function
|
|
8362
|
+
async function handleWallet(ctx) {
|
|
7902
8363
|
const args2 = ctx.message?.text?.split(" ").slice(1) ?? [];
|
|
7903
|
-
const
|
|
7904
|
-
if (!
|
|
7905
|
-
await ctx.reply("Usage: /
|
|
8364
|
+
const address = args2[0];
|
|
8365
|
+
if (!address) {
|
|
8366
|
+
await ctx.reply("Usage: /wallet <ethereum_address>\nExample: /wallet 0x...");
|
|
8367
|
+
return;
|
|
8368
|
+
}
|
|
8369
|
+
if (!isValidAddress(address)) {
|
|
8370
|
+
await ctx.reply("Invalid Ethereum address. Must start with 0x followed by 40 hex characters.");
|
|
7906
8371
|
return;
|
|
7907
8372
|
}
|
|
7908
8373
|
try {
|
|
7909
|
-
const
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
8374
|
+
const balance = await getWalletBalance(address);
|
|
8375
|
+
await ctx.reply(
|
|
8376
|
+
`\u{1F45B} Wallet Balance
|
|
8377
|
+
|
|
8378
|
+
Address: ${address}
|
|
8379
|
+
Balance: ${balance} ETH
|
|
8380
|
+
|
|
8381
|
+
Use /track <address> for full forensic analysis`
|
|
8382
|
+
);
|
|
7916
8383
|
} catch (error) {
|
|
7917
8384
|
const msg = error instanceof Error ? error.message : String(error);
|
|
7918
|
-
await ctx.reply(`
|
|
8385
|
+
await ctx.reply(`Wallet query failed: ${msg}`);
|
|
7919
8386
|
}
|
|
7920
8387
|
}
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
init_loader();
|
|
7926
|
-
init_project_analyzer();
|
|
7927
|
-
init_risk_scorer();
|
|
7928
|
-
init_wallet_analyzer();
|
|
7929
|
-
init_contract_auditor();
|
|
7930
|
-
init_market();
|
|
7931
|
-
init_ico_tracker();
|
|
7932
|
-
init_defillama();
|
|
7933
|
-
init_binance();
|
|
7934
|
-
init_predictor();
|
|
7935
|
-
init_agent();
|
|
7936
|
-
init_market2();
|
|
7937
|
-
}
|
|
7938
|
-
});
|
|
8388
|
+
async function handleAgentHelp(ctx) {
|
|
8389
|
+
const strategies = listStrategies();
|
|
8390
|
+
await ctx.reply(
|
|
8391
|
+
`\u{1F916} Agent Management
|
|
7939
8392
|
|
|
7940
|
-
|
|
7941
|
-
|
|
7942
|
-
|
|
7943
|
-
|
|
7944
|
-
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
|
|
7948
|
-
|
|
7949
|
-
|
|
7950
|
-
|
|
7951
|
-
|
|
7952
|
-
|
|
7953
|
-
}
|
|
7954
|
-
if (entry.count >= MAX_REQUESTS2) {
|
|
7955
|
-
await ctx.reply("Rate limited. Please wait a moment before sending more commands.");
|
|
7956
|
-
return;
|
|
7957
|
-
}
|
|
7958
|
-
entry.count++;
|
|
7959
|
-
await next();
|
|
7960
|
-
}
|
|
7961
|
-
function startRateLimitCleanup2(intervalMs = 3e5) {
|
|
7962
|
-
return setInterval(() => {
|
|
7963
|
-
const now = Date.now();
|
|
7964
|
-
for (const [userId, entry] of userLimits2) {
|
|
7965
|
-
if (now >= entry.resetAt) {
|
|
7966
|
-
userLimits2.delete(userId);
|
|
7967
|
-
}
|
|
7968
|
-
}
|
|
7969
|
-
}, intervalMs);
|
|
8393
|
+
Commands:
|
|
8394
|
+
/agent_create <name> <strategy> <pairs> - Create agent
|
|
8395
|
+
/agent_list - List all agents
|
|
8396
|
+
/agent_start <name> - Start an agent
|
|
8397
|
+
/agent_stop <name> - Stop an agent
|
|
8398
|
+
/agent_status <name> - View status & decisions
|
|
8399
|
+
/agent_delete <name> - Delete an agent
|
|
8400
|
+
|
|
8401
|
+
Available strategies: ${strategies.join(", ")}
|
|
8402
|
+
|
|
8403
|
+
Example:
|
|
8404
|
+
/agent_create mybot momentum BTC,ETH`
|
|
8405
|
+
);
|
|
7970
8406
|
}
|
|
7971
|
-
|
|
7972
|
-
|
|
7973
|
-
|
|
7974
|
-
|
|
7975
|
-
|
|
7976
|
-
|
|
7977
|
-
|
|
8407
|
+
async function handleAgentCreate(ctx) {
|
|
8408
|
+
const args2 = ctx.message?.text?.split(" ").slice(1) ?? [];
|
|
8409
|
+
const name = args2[0];
|
|
8410
|
+
const strategy = args2[1] ?? "momentum";
|
|
8411
|
+
const pairsStr = args2[2] ?? "BTC,ETH";
|
|
8412
|
+
if (!name) {
|
|
8413
|
+
await ctx.reply(
|
|
8414
|
+
"Usage: /agent_create <name> <strategy> <pairs>\nExample: /agent_create mybot momentum BTC,ETH,SOL"
|
|
8415
|
+
);
|
|
8416
|
+
return;
|
|
7978
8417
|
}
|
|
7979
|
-
|
|
8418
|
+
try {
|
|
8419
|
+
const pairs = pairsStr.split(",").map((p) => p.trim().toUpperCase());
|
|
8420
|
+
const agent = createAgent(name, strategy, pairs);
|
|
8421
|
+
await ctx.reply(
|
|
8422
|
+
`\u2705 Agent Created
|
|
7980
8423
|
|
|
7981
|
-
|
|
7982
|
-
|
|
7983
|
-
|
|
7984
|
-
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
|
|
7988
|
-
|
|
7989
|
-
|
|
7990
|
-
|
|
7991
|
-
throw new Error("Telegram token not configured. Run: vizzor config set telegramToken <token>");
|
|
8424
|
+
Name: ${agent.name}
|
|
8425
|
+
Strategy: ${agent.strategy}
|
|
8426
|
+
Pairs: ${agent.pairs.join(", ")}
|
|
8427
|
+
Interval: ${agent.interval}s
|
|
8428
|
+
|
|
8429
|
+
Use /agent_start ${agent.name} to activate`
|
|
8430
|
+
);
|
|
8431
|
+
} catch (error) {
|
|
8432
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
8433
|
+
await ctx.reply(`Failed to create agent: ${msg}`);
|
|
7992
8434
|
}
|
|
7993
|
-
setConfig(config2);
|
|
7994
|
-
setToolHandler(handleTool);
|
|
7995
|
-
const bot = new Bot(token);
|
|
7996
|
-
bot.use(rateLimitMiddleware);
|
|
7997
|
-
registerCommands(bot);
|
|
7998
|
-
bot.on("message:text", async (ctx) => {
|
|
7999
|
-
const text = ctx.message.text;
|
|
8000
|
-
if (text.startsWith("/")) return;
|
|
8001
|
-
await ctx.reply("\u{1F52E} Analyzing...");
|
|
8002
|
-
try {
|
|
8003
|
-
const response = await analyze(buildChatSystemPrompt(), text, VIZZOR_TOOLS);
|
|
8004
|
-
const chunks = splitMessage(response, 4e3);
|
|
8005
|
-
for (const chunk of chunks) {
|
|
8006
|
-
await ctx.reply(chunk);
|
|
8007
|
-
}
|
|
8008
|
-
} catch (error) {
|
|
8009
|
-
const msg = error instanceof Error ? error.message : String(error);
|
|
8010
|
-
await ctx.reply(`Analysis failed: ${msg}`);
|
|
8011
|
-
}
|
|
8012
|
-
});
|
|
8013
|
-
bot.catch((err) => {
|
|
8014
|
-
logger2.error({ err: err.message }, "Telegram bot error");
|
|
8015
|
-
});
|
|
8016
|
-
const cleanupInterval = startRateLimitCleanup2();
|
|
8017
|
-
logger2.info("Telegram bot starting...");
|
|
8018
|
-
await bot.start({
|
|
8019
|
-
onStart: (botInfo) => {
|
|
8020
|
-
logger2.info(`Telegram bot started as @${botInfo.username}`);
|
|
8021
|
-
}
|
|
8022
|
-
});
|
|
8023
|
-
clearInterval(cleanupInterval);
|
|
8024
8435
|
}
|
|
8025
|
-
|
|
8026
|
-
|
|
8027
|
-
|
|
8028
|
-
"
|
|
8029
|
-
init_loader();
|
|
8030
|
-
init_client();
|
|
8031
|
-
init_tool_handler();
|
|
8032
|
-
init_tools();
|
|
8033
|
-
init_chat();
|
|
8034
|
-
init_message_split();
|
|
8035
|
-
init_commands2();
|
|
8036
|
-
init_rate_limit2();
|
|
8037
|
-
init_logger();
|
|
8038
|
-
logger2 = createLogger("telegram-bot");
|
|
8039
|
-
}
|
|
8040
|
-
});
|
|
8041
|
-
|
|
8042
|
-
// src/cli/commands/bot.ts
|
|
8043
|
-
var bot_exports3 = {};
|
|
8044
|
-
__export(bot_exports3, {
|
|
8045
|
-
handleBotStart: () => handleBotStart,
|
|
8046
|
-
handleBotValidate: () => handleBotValidate
|
|
8047
|
-
});
|
|
8048
|
-
import chalk7 from "chalk";
|
|
8049
|
-
async function handleBotStart(options) {
|
|
8050
|
-
const config2 = getConfig();
|
|
8051
|
-
const startDiscord = options.discord || options.all;
|
|
8052
|
-
const startTelegram = options.telegram || options.all;
|
|
8053
|
-
if (!startDiscord && !startTelegram) {
|
|
8054
|
-
console.log(chalk7.yellow("Specify which bot to start:"));
|
|
8055
|
-
console.log(" --discord Start Discord bot");
|
|
8056
|
-
console.log(" --telegram Start Telegram bot");
|
|
8057
|
-
console.log(" --all Start all bots");
|
|
8436
|
+
async function handleAgentList(ctx) {
|
|
8437
|
+
const agents = listAgents();
|
|
8438
|
+
if (agents.length === 0) {
|
|
8439
|
+
await ctx.reply("No agents created yet. Use /agent_create to create one.");
|
|
8058
8440
|
return;
|
|
8059
8441
|
}
|
|
8060
|
-
|
|
8061
|
-
|
|
8062
|
-
|
|
8063
|
-
|
|
8064
|
-
|
|
8065
|
-
|
|
8066
|
-
}
|
|
8067
|
-
|
|
8068
|
-
|
|
8069
|
-
|
|
8070
|
-
|
|
8071
|
-
promises.push(startTelegramBot2());
|
|
8442
|
+
let msg = "\u{1F916} Your Agents\n\n";
|
|
8443
|
+
for (const agent of agents) {
|
|
8444
|
+
const state = getAgentStatus(agent.id);
|
|
8445
|
+
const statusEmoji = state?.status === "running" ? "\u{1F7E2}" : state?.status === "stopped" ? "\u{1F534}" : "\u26AA";
|
|
8446
|
+
msg += `${statusEmoji} ${agent.name} [${state?.status ?? "idle"}]
|
|
8447
|
+
`;
|
|
8448
|
+
msg += ` Strategy: ${agent.strategy} | Pairs: ${agent.pairs.join(", ")}
|
|
8449
|
+
`;
|
|
8450
|
+
msg += ` Cycles: ${state?.cycleCount ?? 0} | Interval: ${agent.interval}s
|
|
8451
|
+
|
|
8452
|
+
`;
|
|
8072
8453
|
}
|
|
8073
|
-
|
|
8074
|
-
console.log(chalk7.dim("\nShutting down bots..."));
|
|
8075
|
-
process.exit(0);
|
|
8076
|
-
};
|
|
8077
|
-
process.on("SIGINT", shutdown);
|
|
8078
|
-
process.on("SIGTERM", shutdown);
|
|
8079
|
-
await Promise.all(promises);
|
|
8454
|
+
await ctx.reply(msg);
|
|
8080
8455
|
}
|
|
8081
|
-
function
|
|
8082
|
-
const
|
|
8083
|
-
|
|
8084
|
-
|
|
8085
|
-
|
|
8086
|
-
|
|
8087
|
-
{ label: "Discord Token", isSet: hasKey(config2.discordToken), required: false },
|
|
8088
|
-
{ label: "Discord Guild ID", isSet: hasKey(config2.discordGuildId), required: false },
|
|
8089
|
-
{ label: "Telegram Token", isSet: hasKey(config2.telegramToken), required: false },
|
|
8090
|
-
{ label: "CryptoPanic Key", isSet: hasKey(config2.cryptopanicApiKey), required: false }
|
|
8091
|
-
];
|
|
8092
|
-
let allRequired = true;
|
|
8093
|
-
for (const check of checks) {
|
|
8094
|
-
const status = check.isSet ? chalk7.green("OK") : check.required ? chalk7.red("MISSING") : chalk7.yellow("NOT SET");
|
|
8095
|
-
console.log(` ${status.padEnd(18)} ${check.label}`);
|
|
8096
|
-
if (check.required && !check.isSet) allRequired = false;
|
|
8456
|
+
async function handleAgentStart(ctx) {
|
|
8457
|
+
const args2 = ctx.message?.text?.split(" ").slice(1) ?? [];
|
|
8458
|
+
const name = args2[0];
|
|
8459
|
+
if (!name) {
|
|
8460
|
+
await ctx.reply("Usage: /agent_start <name>");
|
|
8461
|
+
return;
|
|
8097
8462
|
}
|
|
8098
|
-
|
|
8099
|
-
|
|
8100
|
-
|
|
8101
|
-
|
|
8102
|
-
|
|
8103
|
-
|
|
8463
|
+
try {
|
|
8464
|
+
const agent = getAgentByName(name);
|
|
8465
|
+
if (!agent) {
|
|
8466
|
+
await ctx.reply(`Agent "${name}" not found. Use /agent_list to see your agents.`);
|
|
8467
|
+
return;
|
|
8468
|
+
}
|
|
8469
|
+
const state = startAgent(agent.id);
|
|
8470
|
+
await ctx.reply(
|
|
8471
|
+
`\u{1F7E2} Agent "${state.config.name}" started. Monitoring ${state.config.pairs.join(", ")}.`
|
|
8472
|
+
);
|
|
8473
|
+
} catch (error) {
|
|
8474
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
8475
|
+
await ctx.reply(`Failed to start agent: ${msg}`);
|
|
8104
8476
|
}
|
|
8105
|
-
|
|
8106
|
-
|
|
8107
|
-
|
|
8108
|
-
|
|
8109
|
-
|
|
8477
|
+
}
|
|
8478
|
+
async function handleAgentStop(ctx) {
|
|
8479
|
+
const args2 = ctx.message?.text?.split(" ").slice(1) ?? [];
|
|
8480
|
+
const name = args2[0];
|
|
8481
|
+
if (!name) {
|
|
8482
|
+
await ctx.reply("Usage: /agent_stop <name>");
|
|
8483
|
+
return;
|
|
8110
8484
|
}
|
|
8111
|
-
|
|
8112
|
-
|
|
8113
|
-
|
|
8114
|
-
|
|
8115
|
-
|
|
8116
|
-
|
|
8117
|
-
const
|
|
8118
|
-
|
|
8485
|
+
try {
|
|
8486
|
+
const agent = getAgentByName(name);
|
|
8487
|
+
if (!agent) {
|
|
8488
|
+
await ctx.reply(`Agent "${name}" not found.`);
|
|
8489
|
+
return;
|
|
8490
|
+
}
|
|
8491
|
+
const state = stopAgent(agent.id);
|
|
8492
|
+
await ctx.reply(`\u{1F534} Agent "${state.config.name}" stopped after ${state.cycleCount} cycles.`);
|
|
8493
|
+
} catch (error) {
|
|
8494
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
8495
|
+
await ctx.reply(`Failed to stop agent: ${msg}`);
|
|
8119
8496
|
}
|
|
8120
8497
|
}
|
|
8121
|
-
|
|
8122
|
-
"
|
|
8123
|
-
|
|
8124
|
-
|
|
8125
|
-
|
|
8498
|
+
async function handleAgentStatus(ctx) {
|
|
8499
|
+
const args2 = ctx.message?.text?.split(" ").slice(1) ?? [];
|
|
8500
|
+
const name = args2[0];
|
|
8501
|
+
if (!name) {
|
|
8502
|
+
await ctx.reply("Usage: /agent_status <name>");
|
|
8503
|
+
return;
|
|
8126
8504
|
}
|
|
8127
|
-
|
|
8128
|
-
|
|
8129
|
-
|
|
8130
|
-
|
|
8131
|
-
const db2 = getDb();
|
|
8132
|
-
db2.exec(`
|
|
8133
|
-
CREATE TABLE IF NOT EXISTS agents (
|
|
8134
|
-
id TEXT PRIMARY KEY,
|
|
8135
|
-
name TEXT NOT NULL UNIQUE,
|
|
8136
|
-
strategy TEXT NOT NULL,
|
|
8137
|
-
pairs TEXT NOT NULL,
|
|
8138
|
-
interval_seconds INTEGER NOT NULL DEFAULT 60,
|
|
8139
|
-
created_at INTEGER NOT NULL,
|
|
8140
|
-
updated_at INTEGER NOT NULL
|
|
8141
|
-
)
|
|
8142
|
-
`);
|
|
8143
|
-
db2.exec(`
|
|
8144
|
-
CREATE TABLE IF NOT EXISTS agent_decisions (
|
|
8145
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
8146
|
-
agent_id TEXT NOT NULL,
|
|
8147
|
-
symbol TEXT NOT NULL,
|
|
8148
|
-
action TEXT NOT NULL,
|
|
8149
|
-
confidence INTEGER NOT NULL,
|
|
8150
|
-
reasoning TEXT NOT NULL,
|
|
8151
|
-
signals TEXT NOT NULL,
|
|
8152
|
-
created_at INTEGER NOT NULL,
|
|
8153
|
-
FOREIGN KEY (agent_id) REFERENCES agents(id)
|
|
8154
|
-
)
|
|
8155
|
-
`);
|
|
8156
|
-
}
|
|
8157
|
-
function rowToConfig(row) {
|
|
8158
|
-
return {
|
|
8159
|
-
id: row.id,
|
|
8160
|
-
name: row.name,
|
|
8161
|
-
strategy: row.strategy,
|
|
8162
|
-
pairs: JSON.parse(row.pairs),
|
|
8163
|
-
interval: row.interval_seconds,
|
|
8164
|
-
createdAt: row.created_at,
|
|
8165
|
-
updatedAt: row.updated_at
|
|
8166
|
-
};
|
|
8167
|
-
}
|
|
8168
|
-
var SqliteStore;
|
|
8169
|
-
var init_sqlite_store = __esm({
|
|
8170
|
-
"src/data/sqlite-store.ts"() {
|
|
8171
|
-
"use strict";
|
|
8172
|
-
init_cache();
|
|
8173
|
-
SqliteStore = class {
|
|
8174
|
-
// ---- Cache ---------------------------------------------------------------
|
|
8175
|
-
async getCached(key) {
|
|
8176
|
-
return getCached(key);
|
|
8177
|
-
}
|
|
8178
|
-
async setCache(key, value, ttlSeconds) {
|
|
8179
|
-
setCache(key, value, ttlSeconds);
|
|
8180
|
-
}
|
|
8181
|
-
// ---- Agents --------------------------------------------------------------
|
|
8182
|
-
async createAgent(config2) {
|
|
8183
|
-
ensureAgentTables2();
|
|
8184
|
-
getDb().prepare(
|
|
8185
|
-
`INSERT INTO agents (id, name, strategy, pairs, interval_seconds, created_at, updated_at)
|
|
8186
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
8187
|
-
).run(
|
|
8188
|
-
config2.id,
|
|
8189
|
-
config2.name,
|
|
8190
|
-
config2.strategy,
|
|
8191
|
-
JSON.stringify(config2.pairs),
|
|
8192
|
-
config2.interval,
|
|
8193
|
-
config2.createdAt,
|
|
8194
|
-
config2.updatedAt
|
|
8195
|
-
);
|
|
8196
|
-
return config2;
|
|
8197
|
-
}
|
|
8198
|
-
async listAgents() {
|
|
8199
|
-
ensureAgentTables2();
|
|
8200
|
-
const rows = getDb().prepare("SELECT * FROM agents ORDER BY created_at DESC").all();
|
|
8201
|
-
return rows.map(rowToConfig);
|
|
8202
|
-
}
|
|
8203
|
-
async getAgentById(id) {
|
|
8204
|
-
ensureAgentTables2();
|
|
8205
|
-
const row = getDb().prepare("SELECT * FROM agents WHERE id = ?").get(id);
|
|
8206
|
-
return row ? rowToConfig(row) : null;
|
|
8207
|
-
}
|
|
8208
|
-
async getAgentByName(name) {
|
|
8209
|
-
ensureAgentTables2();
|
|
8210
|
-
const row = getDb().prepare("SELECT * FROM agents WHERE name = ?").get(name);
|
|
8211
|
-
return row ? rowToConfig(row) : null;
|
|
8212
|
-
}
|
|
8213
|
-
async deleteAgent(id) {
|
|
8214
|
-
ensureAgentTables2();
|
|
8215
|
-
const result = getDb().prepare("DELETE FROM agents WHERE id = ?").run(id);
|
|
8216
|
-
getDb().prepare("DELETE FROM agent_decisions WHERE agent_id = ?").run(id);
|
|
8217
|
-
return result.changes > 0;
|
|
8218
|
-
}
|
|
8219
|
-
async logDecision(result) {
|
|
8220
|
-
ensureAgentTables2();
|
|
8221
|
-
getDb().prepare(
|
|
8222
|
-
`INSERT INTO agent_decisions (agent_id, symbol, action, confidence, reasoning, signals, created_at)
|
|
8223
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
8224
|
-
).run(
|
|
8225
|
-
result.agentId,
|
|
8226
|
-
result.symbol,
|
|
8227
|
-
result.decision.action,
|
|
8228
|
-
result.decision.confidence,
|
|
8229
|
-
JSON.stringify(result.decision.reasoning),
|
|
8230
|
-
JSON.stringify(result.signals),
|
|
8231
|
-
result.timestamp
|
|
8232
|
-
);
|
|
8233
|
-
}
|
|
8234
|
-
async getDecisions(agentId, limit) {
|
|
8235
|
-
ensureAgentTables2();
|
|
8236
|
-
const rows = getDb().prepare("SELECT * FROM agent_decisions WHERE agent_id = ? ORDER BY created_at DESC LIMIT ?").all(agentId, limit);
|
|
8237
|
-
return rows.map((r) => ({
|
|
8238
|
-
agentId: r.agent_id,
|
|
8239
|
-
symbol: r.symbol,
|
|
8240
|
-
timestamp: r.created_at,
|
|
8241
|
-
signals: JSON.parse(r.signals),
|
|
8242
|
-
decision: {
|
|
8243
|
-
action: r.action,
|
|
8244
|
-
confidence: r.confidence,
|
|
8245
|
-
reasoning: JSON.parse(r.reasoning)
|
|
8246
|
-
}
|
|
8247
|
-
}));
|
|
8248
|
-
}
|
|
8249
|
-
// ---- Time-series (no-op for SQLite) --------------------------------------
|
|
8250
|
-
async insertOHLCV(_records) {
|
|
8251
|
-
}
|
|
8252
|
-
async queryOHLCV(_symbol, _timeframe, _from, _to) {
|
|
8253
|
-
return [];
|
|
8254
|
-
}
|
|
8255
|
-
// ---- Predictions (no-op for SQLite) --------------------------------------
|
|
8256
|
-
async logPrediction(_prediction) {
|
|
8257
|
-
}
|
|
8258
|
-
async getPredictionAccuracy(_model, _days) {
|
|
8259
|
-
return {
|
|
8260
|
-
model: _model,
|
|
8261
|
-
totalPredictions: 0,
|
|
8262
|
-
correctPredictions: 0,
|
|
8263
|
-
accuracy: 0,
|
|
8264
|
-
byDirection: {
|
|
8265
|
-
up: { total: 0, correct: 0, accuracy: 0 },
|
|
8266
|
-
down: { total: 0, correct: 0, accuracy: 0 },
|
|
8267
|
-
sideways: { total: 0, correct: 0, accuracy: 0 }
|
|
8268
|
-
},
|
|
8269
|
-
period: `${_days}d`
|
|
8270
|
-
};
|
|
8271
|
-
}
|
|
8272
|
-
// ---- Lifecycle -----------------------------------------------------------
|
|
8273
|
-
async close() {
|
|
8274
|
-
}
|
|
8275
|
-
};
|
|
8505
|
+
const agent = getAgentByName(name);
|
|
8506
|
+
if (!agent) {
|
|
8507
|
+
await ctx.reply(`Agent "${name}" not found.`);
|
|
8508
|
+
return;
|
|
8276
8509
|
}
|
|
8277
|
-
|
|
8510
|
+
const state = getAgentStatus(agent.id);
|
|
8511
|
+
if (!state) {
|
|
8512
|
+
await ctx.reply(`Agent "${name}" not found.`);
|
|
8513
|
+
return;
|
|
8514
|
+
}
|
|
8515
|
+
const statusEmoji = state.status === "running" ? "\u{1F7E2}" : state.status === "stopped" ? "\u{1F534}" : "\u26AA";
|
|
8516
|
+
let msg = `\u{1F916} Agent: ${state.config.name}
|
|
8278
8517
|
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8291
|
-
|
|
8292
|
-
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8303
|
-
const sql = readFileSync2(migrationPath, "utf-8");
|
|
8304
|
-
await this.pool.query(sql);
|
|
8305
|
-
this.initialized = true;
|
|
8306
|
-
}
|
|
8307
|
-
async query(text, params) {
|
|
8308
|
-
await this.init();
|
|
8309
|
-
return this.pool.query(text, params);
|
|
8310
|
-
}
|
|
8311
|
-
// ---- Cache ---------------------------------------------------------------
|
|
8312
|
-
async getCached(key) {
|
|
8313
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
8314
|
-
const { rows } = await this.query(
|
|
8315
|
-
"SELECT value FROM cache WHERE key = $1 AND expires_at > $2",
|
|
8316
|
-
[key, now]
|
|
8317
|
-
);
|
|
8318
|
-
if (rows.length === 0) return null;
|
|
8319
|
-
return rows[0].value;
|
|
8320
|
-
}
|
|
8321
|
-
async setCache(key, value, ttlSeconds) {
|
|
8322
|
-
const now = Math.floor(Date.now() / 1e3);
|
|
8323
|
-
const expiresAt = now + ttlSeconds;
|
|
8324
|
-
await this.query(
|
|
8325
|
-
`INSERT INTO cache (key, value, expires_at, created_at)
|
|
8326
|
-
VALUES ($1, $2, $3, $4)
|
|
8327
|
-
ON CONFLICT (key) DO UPDATE SET value = $2, expires_at = $3`,
|
|
8328
|
-
[key, JSON.stringify(value), expiresAt, now]
|
|
8329
|
-
);
|
|
8330
|
-
}
|
|
8331
|
-
// ---- Agents --------------------------------------------------------------
|
|
8332
|
-
async createAgent(config2) {
|
|
8333
|
-
await this.query(
|
|
8334
|
-
`INSERT INTO agents (id, name, strategy, pairs, interval_seconds, created_at, updated_at)
|
|
8335
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
|
8336
|
-
[
|
|
8337
|
-
config2.id,
|
|
8338
|
-
config2.name,
|
|
8339
|
-
config2.strategy,
|
|
8340
|
-
JSON.stringify(config2.pairs),
|
|
8341
|
-
config2.interval,
|
|
8342
|
-
config2.createdAt,
|
|
8343
|
-
config2.updatedAt
|
|
8344
|
-
]
|
|
8345
|
-
);
|
|
8346
|
-
return config2;
|
|
8347
|
-
}
|
|
8348
|
-
async listAgents() {
|
|
8349
|
-
const { rows } = await this.query("SELECT * FROM agents ORDER BY created_at DESC");
|
|
8350
|
-
return rows.map((r) => ({
|
|
8351
|
-
id: r.id,
|
|
8352
|
-
name: r.name,
|
|
8353
|
-
strategy: r.strategy,
|
|
8354
|
-
pairs: typeof r.pairs === "string" ? JSON.parse(r.pairs) : r.pairs,
|
|
8355
|
-
interval: r.interval_seconds,
|
|
8356
|
-
createdAt: Number(r.created_at),
|
|
8357
|
-
updatedAt: Number(r.updated_at)
|
|
8358
|
-
}));
|
|
8359
|
-
}
|
|
8360
|
-
async getAgentById(id) {
|
|
8361
|
-
const { rows } = await this.query("SELECT * FROM agents WHERE id = $1", [id]);
|
|
8362
|
-
if (rows.length === 0) return null;
|
|
8363
|
-
const r = rows[0];
|
|
8364
|
-
return {
|
|
8365
|
-
id: r.id,
|
|
8366
|
-
name: r.name,
|
|
8367
|
-
strategy: r.strategy,
|
|
8368
|
-
pairs: typeof r.pairs === "string" ? JSON.parse(r.pairs) : r.pairs,
|
|
8369
|
-
interval: r.interval_seconds,
|
|
8370
|
-
createdAt: Number(r.created_at),
|
|
8371
|
-
updatedAt: Number(r.updated_at)
|
|
8372
|
-
};
|
|
8373
|
-
}
|
|
8374
|
-
async getAgentByName(name) {
|
|
8375
|
-
const { rows } = await this.query("SELECT * FROM agents WHERE name = $1", [name]);
|
|
8376
|
-
if (rows.length === 0) return null;
|
|
8377
|
-
const r = rows[0];
|
|
8378
|
-
return {
|
|
8379
|
-
id: r.id,
|
|
8380
|
-
name: r.name,
|
|
8381
|
-
strategy: r.strategy,
|
|
8382
|
-
pairs: typeof r.pairs === "string" ? JSON.parse(r.pairs) : r.pairs,
|
|
8383
|
-
interval: r.interval_seconds,
|
|
8384
|
-
createdAt: Number(r.created_at),
|
|
8385
|
-
updatedAt: Number(r.updated_at)
|
|
8386
|
-
};
|
|
8387
|
-
}
|
|
8388
|
-
async deleteAgent(id) {
|
|
8389
|
-
const result = await this.query("DELETE FROM agents WHERE id = $1", [id]);
|
|
8390
|
-
return (result.rowCount ?? 0) > 0;
|
|
8391
|
-
}
|
|
8392
|
-
async logDecision(result) {
|
|
8393
|
-
await this.query(
|
|
8394
|
-
`INSERT INTO agent_decisions (agent_id, symbol, action, confidence, reasoning, signals, created_at)
|
|
8395
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
|
8396
|
-
[
|
|
8397
|
-
result.agentId,
|
|
8398
|
-
result.symbol,
|
|
8399
|
-
result.decision.action,
|
|
8400
|
-
result.decision.confidence,
|
|
8401
|
-
JSON.stringify(result.decision.reasoning),
|
|
8402
|
-
JSON.stringify(result.signals),
|
|
8403
|
-
result.timestamp
|
|
8404
|
-
]
|
|
8405
|
-
);
|
|
8406
|
-
}
|
|
8407
|
-
async getDecisions(agentId, limit) {
|
|
8408
|
-
const { rows } = await this.query("SELECT * FROM agent_decisions WHERE agent_id = $1 ORDER BY created_at DESC LIMIT $2", [
|
|
8409
|
-
agentId,
|
|
8410
|
-
limit
|
|
8411
|
-
]);
|
|
8412
|
-
return rows.map((r) => ({
|
|
8413
|
-
agentId: r.agent_id,
|
|
8414
|
-
symbol: r.symbol,
|
|
8415
|
-
timestamp: Number(r.created_at),
|
|
8416
|
-
signals: typeof r.signals === "string" ? JSON.parse(r.signals) : r.signals,
|
|
8417
|
-
decision: {
|
|
8418
|
-
action: r.action,
|
|
8419
|
-
confidence: r.confidence,
|
|
8420
|
-
reasoning: typeof r.reasoning === "string" ? JSON.parse(r.reasoning) : r.reasoning
|
|
8421
|
-
}
|
|
8422
|
-
}));
|
|
8423
|
-
}
|
|
8424
|
-
// ---- Time-series ---------------------------------------------------------
|
|
8425
|
-
async insertOHLCV(records) {
|
|
8426
|
-
if (records.length === 0) return;
|
|
8427
|
-
const values = [];
|
|
8428
|
-
const placeholders = [];
|
|
8429
|
-
for (let i = 0; i < records.length; i++) {
|
|
8430
|
-
const r = records[i];
|
|
8431
|
-
const offset = i * 9;
|
|
8432
|
-
placeholders.push(
|
|
8433
|
-
`($${offset + 1}, $${offset + 2}, $${offset + 3}, $${offset + 4}, $${offset + 5}, $${offset + 6}, $${offset + 7}, $${offset + 8}, $${offset + 9})`
|
|
8434
|
-
);
|
|
8435
|
-
values.push(
|
|
8436
|
-
new Date(r.time).toISOString(),
|
|
8437
|
-
r.symbol,
|
|
8438
|
-
r.timeframe,
|
|
8439
|
-
r.open,
|
|
8440
|
-
r.high,
|
|
8441
|
-
r.low,
|
|
8442
|
-
r.close,
|
|
8443
|
-
r.volume,
|
|
8444
|
-
r.trades
|
|
8445
|
-
);
|
|
8446
|
-
}
|
|
8447
|
-
await this.query(
|
|
8448
|
-
`INSERT INTO ohlcv (time, symbol, timeframe, open, high, low, close, volume, trades)
|
|
8449
|
-
VALUES ${placeholders.join(", ")}
|
|
8450
|
-
ON CONFLICT (symbol, timeframe, time) DO UPDATE
|
|
8451
|
-
SET open = EXCLUDED.open, high = EXCLUDED.high, low = EXCLUDED.low,
|
|
8452
|
-
close = EXCLUDED.close, volume = EXCLUDED.volume, trades = EXCLUDED.trades`,
|
|
8453
|
-
values
|
|
8454
|
-
);
|
|
8455
|
-
}
|
|
8456
|
-
async queryOHLCV(symbol, timeframe, from, to) {
|
|
8457
|
-
const { rows } = await this.query(
|
|
8458
|
-
`SELECT * FROM ohlcv
|
|
8459
|
-
WHERE symbol = $1 AND timeframe = $2 AND time >= $3 AND time <= $4
|
|
8460
|
-
ORDER BY time ASC`,
|
|
8461
|
-
[symbol, timeframe, new Date(from).toISOString(), new Date(to).toISOString()]
|
|
8462
|
-
);
|
|
8463
|
-
return rows.map((r) => ({
|
|
8464
|
-
time: new Date(r.time).getTime(),
|
|
8465
|
-
symbol: r.symbol,
|
|
8466
|
-
timeframe: r.timeframe,
|
|
8467
|
-
open: r.open,
|
|
8468
|
-
high: r.high,
|
|
8469
|
-
low: r.low,
|
|
8470
|
-
close: r.close,
|
|
8471
|
-
volume: r.volume,
|
|
8472
|
-
trades: r.trades
|
|
8473
|
-
}));
|
|
8474
|
-
}
|
|
8475
|
-
// ---- Predictions ---------------------------------------------------------
|
|
8476
|
-
async logPrediction(prediction) {
|
|
8477
|
-
await this.query(
|
|
8478
|
-
`INSERT INTO predictions (symbol, model, direction, probability, horizon, features, predicted_at)
|
|
8479
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7)`,
|
|
8480
|
-
[
|
|
8481
|
-
prediction.symbol,
|
|
8482
|
-
prediction.model,
|
|
8483
|
-
prediction.direction,
|
|
8484
|
-
prediction.probability,
|
|
8485
|
-
prediction.horizon,
|
|
8486
|
-
JSON.stringify(prediction.features),
|
|
8487
|
-
new Date(prediction.predictedAt).toISOString()
|
|
8488
|
-
]
|
|
8489
|
-
);
|
|
8490
|
-
}
|
|
8491
|
-
async getPredictionAccuracy(model, days) {
|
|
8492
|
-
const since = new Date(Date.now() - days * 864e5).toISOString();
|
|
8493
|
-
const { rows: totals } = await this.query(
|
|
8494
|
-
`SELECT direction,
|
|
8495
|
-
COUNT(*)::text AS total,
|
|
8496
|
-
COUNT(*) FILTER (WHERE was_correct = true)::text AS correct
|
|
8497
|
-
FROM predictions
|
|
8498
|
-
WHERE model = $1 AND predicted_at >= $2 AND was_correct IS NOT NULL
|
|
8499
|
-
GROUP BY direction`,
|
|
8500
|
-
[model, since]
|
|
8501
|
-
);
|
|
8502
|
-
const byDir = {
|
|
8503
|
-
up: { total: 0, correct: 0, accuracy: 0 },
|
|
8504
|
-
down: { total: 0, correct: 0, accuracy: 0 },
|
|
8505
|
-
sideways: { total: 0, correct: 0, accuracy: 0 }
|
|
8506
|
-
};
|
|
8507
|
-
let totalAll = 0;
|
|
8508
|
-
let correctAll = 0;
|
|
8509
|
-
for (const row of totals) {
|
|
8510
|
-
const t = parseInt(row.total, 10);
|
|
8511
|
-
const c = parseInt(row.correct, 10);
|
|
8512
|
-
totalAll += t;
|
|
8513
|
-
correctAll += c;
|
|
8514
|
-
const dir = row.direction;
|
|
8515
|
-
if (byDir[dir]) {
|
|
8516
|
-
byDir[dir] = { total: t, correct: c, accuracy: t > 0 ? c / t : 0 };
|
|
8517
|
-
}
|
|
8518
|
-
}
|
|
8519
|
-
return {
|
|
8520
|
-
model,
|
|
8521
|
-
totalPredictions: totalAll,
|
|
8522
|
-
correctPredictions: correctAll,
|
|
8523
|
-
accuracy: totalAll > 0 ? correctAll / totalAll : 0,
|
|
8524
|
-
byDirection: byDir,
|
|
8525
|
-
period: `${days}d`
|
|
8526
|
-
};
|
|
8518
|
+
`;
|
|
8519
|
+
msg += `${statusEmoji} Status: ${state.status}
|
|
8520
|
+
`;
|
|
8521
|
+
msg += `Strategy: ${state.config.strategy}
|
|
8522
|
+
`;
|
|
8523
|
+
msg += `Pairs: ${state.config.pairs.join(", ")}
|
|
8524
|
+
`;
|
|
8525
|
+
msg += `Interval: ${state.config.interval}s
|
|
8526
|
+
`;
|
|
8527
|
+
msg += `Cycles: ${state.cycleCount}
|
|
8528
|
+
`;
|
|
8529
|
+
if (state.error) msg += `Error: ${state.error}
|
|
8530
|
+
`;
|
|
8531
|
+
const decisions = getRecentDecisions(agent.id, 5);
|
|
8532
|
+
if (decisions.length > 0) {
|
|
8533
|
+
msg += "\nRecent Decisions:\n";
|
|
8534
|
+
for (const d of decisions) {
|
|
8535
|
+
const actionEmoji = d.decision.action === "buy" ? "\u{1F7E2}" : d.decision.action === "sell" ? "\u{1F534}" : "\u26AA";
|
|
8536
|
+
const time = new Date(d.timestamp).toLocaleString();
|
|
8537
|
+
msg += `${actionEmoji} ${d.symbol} ${d.decision.action.toUpperCase()} (${d.decision.confidence}%) \u2014 ${time}
|
|
8538
|
+
`;
|
|
8539
|
+
if (d.decision.reasoning.length > 0) {
|
|
8540
|
+
msg += ` \u2192 ${d.decision.reasoning[0]}
|
|
8541
|
+
`;
|
|
8527
8542
|
}
|
|
8528
|
-
|
|
8529
|
-
|
|
8530
|
-
|
|
8543
|
+
}
|
|
8544
|
+
}
|
|
8545
|
+
await ctx.reply(msg);
|
|
8546
|
+
}
|
|
8547
|
+
async function handleAgentDelete(ctx) {
|
|
8548
|
+
const args2 = ctx.message?.text?.split(" ").slice(1) ?? [];
|
|
8549
|
+
const name = args2[0];
|
|
8550
|
+
if (!name) {
|
|
8551
|
+
await ctx.reply("Usage: /agent_delete <name>");
|
|
8552
|
+
return;
|
|
8553
|
+
}
|
|
8554
|
+
try {
|
|
8555
|
+
const agent = getAgentByName(name);
|
|
8556
|
+
if (!agent) {
|
|
8557
|
+
await ctx.reply(`Agent "${name}" not found.`);
|
|
8558
|
+
return;
|
|
8559
|
+
}
|
|
8560
|
+
deleteAgent(agent.id);
|
|
8561
|
+
await ctx.reply(`\u{1F5D1} Agent "${name}" deleted.`);
|
|
8562
|
+
} catch (error) {
|
|
8563
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
8564
|
+
await ctx.reply(`Failed to delete agent: ${msg}`);
|
|
8565
|
+
}
|
|
8566
|
+
}
|
|
8567
|
+
var init_commands2 = __esm({
|
|
8568
|
+
"src/telegram/commands/index.ts"() {
|
|
8569
|
+
"use strict";
|
|
8570
|
+
init_registry();
|
|
8571
|
+
init_loader();
|
|
8572
|
+
init_project_analyzer();
|
|
8573
|
+
init_risk_scorer();
|
|
8574
|
+
init_wallet_analyzer();
|
|
8575
|
+
init_contract_auditor();
|
|
8576
|
+
init_market();
|
|
8577
|
+
init_ico_tracker();
|
|
8578
|
+
init_defillama();
|
|
8579
|
+
init_binance();
|
|
8580
|
+
init_predictor();
|
|
8581
|
+
init_agent();
|
|
8582
|
+
init_market2();
|
|
8583
|
+
}
|
|
8584
|
+
});
|
|
8585
|
+
|
|
8586
|
+
// src/telegram/middleware/rate-limit.ts
|
|
8587
|
+
async function rateLimitMiddleware(ctx, next) {
|
|
8588
|
+
const userId = ctx.from?.id;
|
|
8589
|
+
if (!userId) {
|
|
8590
|
+
await next();
|
|
8591
|
+
return;
|
|
8592
|
+
}
|
|
8593
|
+
const now = Date.now();
|
|
8594
|
+
const entry = userLimits2.get(userId);
|
|
8595
|
+
if (!entry || now >= entry.resetAt) {
|
|
8596
|
+
userLimits2.set(userId, { count: 1, resetAt: now + WINDOW_MS2 });
|
|
8597
|
+
await next();
|
|
8598
|
+
return;
|
|
8599
|
+
}
|
|
8600
|
+
if (entry.count >= MAX_REQUESTS2) {
|
|
8601
|
+
await ctx.reply("Rate limited. Please wait a moment before sending more commands.");
|
|
8602
|
+
return;
|
|
8603
|
+
}
|
|
8604
|
+
entry.count++;
|
|
8605
|
+
await next();
|
|
8606
|
+
}
|
|
8607
|
+
function startRateLimitCleanup2(intervalMs = 3e5) {
|
|
8608
|
+
return setInterval(() => {
|
|
8609
|
+
const now = Date.now();
|
|
8610
|
+
for (const [userId, entry] of userLimits2) {
|
|
8611
|
+
if (now >= entry.resetAt) {
|
|
8612
|
+
userLimits2.delete(userId);
|
|
8531
8613
|
}
|
|
8532
|
-
}
|
|
8614
|
+
}
|
|
8615
|
+
}, intervalMs);
|
|
8616
|
+
}
|
|
8617
|
+
var userLimits2, MAX_REQUESTS2, WINDOW_MS2;
|
|
8618
|
+
var init_rate_limit2 = __esm({
|
|
8619
|
+
"src/telegram/middleware/rate-limit.ts"() {
|
|
8620
|
+
"use strict";
|
|
8621
|
+
userLimits2 = /* @__PURE__ */ new Map();
|
|
8622
|
+
MAX_REQUESTS2 = 10;
|
|
8623
|
+
WINDOW_MS2 = 6e4;
|
|
8533
8624
|
}
|
|
8534
8625
|
});
|
|
8535
8626
|
|
|
8536
|
-
// src/
|
|
8537
|
-
|
|
8538
|
-
|
|
8539
|
-
|
|
8540
|
-
|
|
8541
|
-
|
|
8627
|
+
// src/telegram/bot.ts
|
|
8628
|
+
var bot_exports2 = {};
|
|
8629
|
+
__export(bot_exports2, {
|
|
8630
|
+
startTelegramBot: () => startTelegramBot
|
|
8631
|
+
});
|
|
8632
|
+
import { Bot } from "grammy";
|
|
8633
|
+
async function startTelegramBot() {
|
|
8634
|
+
const config2 = loadConfig();
|
|
8635
|
+
const token = config2.telegramToken;
|
|
8636
|
+
if (!token) {
|
|
8637
|
+
throw new Error("Telegram token not configured. Run: vizzor config set telegramToken <token>");
|
|
8638
|
+
}
|
|
8639
|
+
setConfig(config2);
|
|
8640
|
+
setToolHandler(handleTool);
|
|
8641
|
+
const bot = new Bot(token);
|
|
8642
|
+
bot.use(rateLimitMiddleware);
|
|
8643
|
+
registerCommands(bot);
|
|
8644
|
+
bot.on("message:text", async (ctx) => {
|
|
8645
|
+
const text = ctx.message.text;
|
|
8646
|
+
if (text.startsWith("/")) return;
|
|
8647
|
+
await ctx.reply("\u{1F52E} Analyzing...");
|
|
8648
|
+
try {
|
|
8649
|
+
const response = await analyze(buildChatSystemPrompt(), text, VIZZOR_TOOLS);
|
|
8650
|
+
const chunks = splitMessage(response, 4e3);
|
|
8651
|
+
for (const chunk of chunks) {
|
|
8652
|
+
await ctx.reply(chunk);
|
|
8653
|
+
}
|
|
8654
|
+
} catch (error) {
|
|
8655
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
8656
|
+
await ctx.reply(`Analysis failed: ${msg}`);
|
|
8657
|
+
}
|
|
8658
|
+
});
|
|
8659
|
+
bot.catch((err) => {
|
|
8660
|
+
logger2.error({ err: err.message }, "Telegram bot error");
|
|
8661
|
+
});
|
|
8662
|
+
const cleanupInterval = startRateLimitCleanup2();
|
|
8663
|
+
logger2.info("Telegram bot starting...");
|
|
8664
|
+
await bot.start({
|
|
8665
|
+
onStart: (botInfo) => {
|
|
8666
|
+
logger2.info(`Telegram bot started as @${botInfo.username}`);
|
|
8667
|
+
}
|
|
8668
|
+
});
|
|
8669
|
+
clearInterval(cleanupInterval);
|
|
8670
|
+
}
|
|
8671
|
+
var logger2;
|
|
8672
|
+
var init_bot2 = __esm({
|
|
8673
|
+
"src/telegram/bot.ts"() {
|
|
8674
|
+
"use strict";
|
|
8675
|
+
init_loader();
|
|
8676
|
+
init_client2();
|
|
8677
|
+
init_tool_handler();
|
|
8678
|
+
init_tools();
|
|
8679
|
+
init_chat();
|
|
8680
|
+
init_message_split();
|
|
8681
|
+
init_commands2();
|
|
8682
|
+
init_rate_limit2();
|
|
8683
|
+
init_logger();
|
|
8684
|
+
logger2 = createLogger("telegram-bot");
|
|
8685
|
+
}
|
|
8686
|
+
});
|
|
8687
|
+
|
|
8688
|
+
// src/cli/commands/bot.ts
|
|
8689
|
+
var bot_exports3 = {};
|
|
8690
|
+
__export(bot_exports3, {
|
|
8691
|
+
handleBotStart: () => handleBotStart,
|
|
8692
|
+
handleBotValidate: () => handleBotValidate
|
|
8693
|
+
});
|
|
8694
|
+
import chalk7 from "chalk";
|
|
8695
|
+
async function handleBotStart(options) {
|
|
8696
|
+
const config2 = getConfig();
|
|
8697
|
+
const startDiscord = options.discord || options.all;
|
|
8698
|
+
const startTelegram = options.telegram || options.all;
|
|
8699
|
+
if (!startDiscord && !startTelegram) {
|
|
8700
|
+
console.log(chalk7.yellow("Specify which bot to start:"));
|
|
8701
|
+
console.log(" --discord Start Discord bot");
|
|
8702
|
+
console.log(" --telegram Start Telegram bot");
|
|
8703
|
+
console.log(" --all Start all bots");
|
|
8704
|
+
return;
|
|
8705
|
+
}
|
|
8706
|
+
const promises = [];
|
|
8707
|
+
if (startDiscord) {
|
|
8708
|
+
requireKey("DISCORD_TOKEN", config2.discordToken);
|
|
8709
|
+
console.log(chalk7.blue("Starting Discord bot..."));
|
|
8710
|
+
const { startDiscordBot: startDiscordBot2 } = await Promise.resolve().then(() => (init_bot(), bot_exports));
|
|
8711
|
+
promises.push(startDiscordBot2());
|
|
8712
|
+
}
|
|
8713
|
+
if (startTelegram) {
|
|
8714
|
+
requireKey("TELEGRAM_BOT_TOKEN", config2.telegramToken);
|
|
8715
|
+
console.log(chalk7.blue("Starting Telegram bot..."));
|
|
8716
|
+
const { startTelegramBot: startTelegramBot2 } = await Promise.resolve().then(() => (init_bot2(), bot_exports2));
|
|
8717
|
+
promises.push(startTelegramBot2());
|
|
8718
|
+
}
|
|
8719
|
+
const shutdown = () => {
|
|
8720
|
+
console.log(chalk7.dim("\nShutting down bots..."));
|
|
8721
|
+
process.exit(0);
|
|
8722
|
+
};
|
|
8723
|
+
process.on("SIGINT", shutdown);
|
|
8724
|
+
process.on("SIGTERM", shutdown);
|
|
8725
|
+
await Promise.all(promises);
|
|
8726
|
+
}
|
|
8727
|
+
function handleBotValidate() {
|
|
8728
|
+
const config2 = getConfig();
|
|
8729
|
+
console.log(chalk7.bold("\nVizzor Bot Configuration Check\n"));
|
|
8730
|
+
const checks = [
|
|
8731
|
+
{ label: "Anthropic API Key", isSet: hasKey(config2.anthropicApiKey), required: true },
|
|
8732
|
+
{ label: "Etherscan API Key", isSet: hasKey(config2.etherscanApiKey), required: true },
|
|
8733
|
+
{ label: "Discord Token", isSet: hasKey(config2.discordToken), required: false },
|
|
8734
|
+
{ label: "Discord Guild ID", isSet: hasKey(config2.discordGuildId), required: false },
|
|
8735
|
+
{ label: "Telegram Token", isSet: hasKey(config2.telegramToken), required: false },
|
|
8736
|
+
{ label: "CryptoPanic Key", isSet: hasKey(config2.cryptopanicApiKey), required: false }
|
|
8737
|
+
];
|
|
8738
|
+
let allRequired = true;
|
|
8739
|
+
for (const check of checks) {
|
|
8740
|
+
const status = check.isSet ? chalk7.green("OK") : check.required ? chalk7.red("MISSING") : chalk7.yellow("NOT SET");
|
|
8741
|
+
console.log(` ${status.padEnd(18)} ${check.label}`);
|
|
8742
|
+
if (check.required && !check.isSet) allRequired = false;
|
|
8743
|
+
}
|
|
8744
|
+
console.log();
|
|
8745
|
+
if (hasKey(config2.discordToken)) {
|
|
8746
|
+
console.log(chalk7.green(" Discord bot: Ready to start"));
|
|
8542
8747
|
} else {
|
|
8543
|
-
|
|
8748
|
+
console.log(chalk7.yellow(" Discord bot: Set discordToken to enable"));
|
|
8749
|
+
console.log(chalk7.dim(" vizzor config set discordToken <token>"));
|
|
8750
|
+
}
|
|
8751
|
+
if (hasKey(config2.telegramToken)) {
|
|
8752
|
+
console.log(chalk7.green(" Telegram bot: Ready to start"));
|
|
8753
|
+
} else {
|
|
8754
|
+
console.log(chalk7.yellow(" Telegram bot: Set telegramToken to enable"));
|
|
8755
|
+
console.log(chalk7.dim(" vizzor config set telegramToken <token>"));
|
|
8756
|
+
}
|
|
8757
|
+
console.log();
|
|
8758
|
+
if (!allRequired) {
|
|
8759
|
+
console.log(chalk7.red("Required keys are missing. Run: vizzor config set <key> <value>"));
|
|
8760
|
+
} else if (hasKey(config2.discordToken) && hasKey(config2.telegramToken)) {
|
|
8761
|
+
console.log(chalk7.green("All bots ready. Run: vizzor bot start --all"));
|
|
8762
|
+
} else if (hasKey(config2.discordToken) || hasKey(config2.telegramToken)) {
|
|
8763
|
+
const which = hasKey(config2.discordToken) ? "--discord" : "--telegram";
|
|
8764
|
+
console.log(chalk7.green(`Bot ready. Run: vizzor bot start ${which}`));
|
|
8544
8765
|
}
|
|
8545
|
-
return instance;
|
|
8546
8766
|
}
|
|
8547
|
-
var
|
|
8548
|
-
|
|
8549
|
-
"src/data/store-factory.ts"() {
|
|
8767
|
+
var init_bot3 = __esm({
|
|
8768
|
+
"src/cli/commands/bot.ts"() {
|
|
8550
8769
|
"use strict";
|
|
8551
|
-
|
|
8552
|
-
|
|
8770
|
+
init_loader();
|
|
8771
|
+
init_keys();
|
|
8553
8772
|
}
|
|
8554
8773
|
});
|
|
8555
8774
|
|
|
8556
8775
|
// src/data/collector.ts
|
|
8557
|
-
var
|
|
8776
|
+
var log2, MAJOR_SYMBOLS, TIMEFRAMES, COLLECTION_INTERVAL_MS, DataCollector;
|
|
8558
8777
|
var init_collector = __esm({
|
|
8559
8778
|
"src/data/collector.ts"() {
|
|
8560
8779
|
"use strict";
|
|
8561
8780
|
init_binance();
|
|
8562
8781
|
init_logger();
|
|
8563
|
-
|
|
8782
|
+
log2 = createLogger("collector");
|
|
8564
8783
|
MAJOR_SYMBOLS = [
|
|
8565
8784
|
"BTC",
|
|
8566
8785
|
"ETH",
|
|
@@ -8611,10 +8830,10 @@ var init_collector = __esm({
|
|
|
8611
8830
|
}
|
|
8612
8831
|
start() {
|
|
8613
8832
|
if (this.status.running) {
|
|
8614
|
-
|
|
8833
|
+
log2.warn("Collector already running");
|
|
8615
8834
|
return;
|
|
8616
8835
|
}
|
|
8617
|
-
|
|
8836
|
+
log2.info(
|
|
8618
8837
|
`Starting data collector: ${this.symbols.length} symbols, ${TIMEFRAMES.length} timeframes, every ${this.intervalMs / 1e3}s`
|
|
8619
8838
|
);
|
|
8620
8839
|
this.status.running = true;
|
|
@@ -8627,10 +8846,10 @@ var init_collector = __esm({
|
|
|
8627
8846
|
this.timer = null;
|
|
8628
8847
|
}
|
|
8629
8848
|
this.status.running = false;
|
|
8630
|
-
|
|
8849
|
+
log2.info("Data collector stopped");
|
|
8631
8850
|
}
|
|
8632
8851
|
async collectAll() {
|
|
8633
|
-
|
|
8852
|
+
log2.info("Collection cycle starting");
|
|
8634
8853
|
const start = Date.now();
|
|
8635
8854
|
for (const timeframe of TIMEFRAMES) {
|
|
8636
8855
|
for (const symbol of this.symbols) {
|
|
@@ -8638,7 +8857,7 @@ var init_collector = __esm({
|
|
|
8638
8857
|
await this.collectSymbol(symbol, timeframe);
|
|
8639
8858
|
} catch (err) {
|
|
8640
8859
|
this.status.errors++;
|
|
8641
|
-
|
|
8860
|
+
log2.error(
|
|
8642
8861
|
`Failed to collect ${symbol}/${timeframe}: ${err instanceof Error ? err.message : String(err)}`
|
|
8643
8862
|
);
|
|
8644
8863
|
}
|
|
@@ -8646,7 +8865,7 @@ var init_collector = __esm({
|
|
|
8646
8865
|
}
|
|
8647
8866
|
this.status.lastRun = Date.now();
|
|
8648
8867
|
const elapsed = ((Date.now() - start) / 1e3).toFixed(1);
|
|
8649
|
-
|
|
8868
|
+
log2.info(
|
|
8650
8869
|
`Collection cycle complete in ${elapsed}s (total records: ${this.status.totalRecords})`
|
|
8651
8870
|
);
|
|
8652
8871
|
}
|
|
@@ -9473,7 +9692,7 @@ function useAIStream() {
|
|
|
9473
9692
|
var init_use_ai_stream = __esm({
|
|
9474
9693
|
"src/tui/hooks/use-ai-stream.ts"() {
|
|
9475
9694
|
"use strict";
|
|
9476
|
-
|
|
9695
|
+
init_client2();
|
|
9477
9696
|
init_chat();
|
|
9478
9697
|
init_tools();
|
|
9479
9698
|
init_context_injector();
|
|
@@ -10025,7 +10244,7 @@ var init_commands3 = __esm({
|
|
|
10025
10244
|
init_constants();
|
|
10026
10245
|
init_binance();
|
|
10027
10246
|
init_agent();
|
|
10028
|
-
|
|
10247
|
+
init_client2();
|
|
10029
10248
|
init_registry2();
|
|
10030
10249
|
init_types();
|
|
10031
10250
|
}
|
|
@@ -10467,7 +10686,7 @@ var init_app = __esm({
|
|
|
10467
10686
|
init_commands3();
|
|
10468
10687
|
init_loader();
|
|
10469
10688
|
init_constants();
|
|
10470
|
-
|
|
10689
|
+
init_client2();
|
|
10471
10690
|
init_tool_handler();
|
|
10472
10691
|
init_tools();
|
|
10473
10692
|
init_binance();
|