@guiie/buda-mcp 1.2.2 → 1.4.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/CHANGELOG.md +68 -0
- package/PUBLISH_CHECKLIST.md +71 -63
- package/README.md +4 -4
- package/dist/http.js +39 -0
- package/dist/index.js +29 -0
- package/dist/tools/arbitrage.d.ts +35 -0
- package/dist/tools/arbitrage.d.ts.map +1 -0
- package/dist/tools/arbitrage.js +142 -0
- package/dist/tools/balances.d.ts.map +1 -1
- package/dist/tools/balances.js +24 -4
- package/dist/tools/calculate_position_size.d.ts +48 -0
- package/dist/tools/calculate_position_size.d.ts.map +1 -0
- package/dist/tools/calculate_position_size.js +111 -0
- package/dist/tools/compare_markets.d.ts.map +1 -1
- package/dist/tools/compare_markets.js +11 -10
- package/dist/tools/dead_mans_switch.d.ts +84 -0
- package/dist/tools/dead_mans_switch.d.ts.map +1 -0
- package/dist/tools/dead_mans_switch.js +236 -0
- package/dist/tools/market_sentiment.d.ts +30 -0
- package/dist/tools/market_sentiment.d.ts.map +1 -0
- package/dist/tools/market_sentiment.js +104 -0
- package/dist/tools/market_summary.d.ts +43 -0
- package/dist/tools/market_summary.d.ts.map +1 -0
- package/dist/tools/market_summary.js +81 -0
- package/dist/tools/markets.d.ts.map +1 -1
- package/dist/tools/markets.js +4 -2
- package/dist/tools/orderbook.d.ts.map +1 -1
- package/dist/tools/orderbook.js +14 -4
- package/dist/tools/orders.d.ts.map +1 -1
- package/dist/tools/orders.js +41 -3
- package/dist/tools/price_history.d.ts.map +1 -1
- package/dist/tools/price_history.js +5 -43
- package/dist/tools/simulate_order.d.ts +45 -0
- package/dist/tools/simulate_order.d.ts.map +1 -0
- package/dist/tools/simulate_order.js +139 -0
- package/dist/tools/spread.d.ts.map +1 -1
- package/dist/tools/spread.js +10 -8
- package/dist/tools/technical_indicators.d.ts +39 -0
- package/dist/tools/technical_indicators.d.ts.map +1 -0
- package/dist/tools/technical_indicators.js +223 -0
- package/dist/tools/ticker.d.ts.map +1 -1
- package/dist/tools/ticker.js +24 -3
- package/dist/tools/trades.d.ts.map +1 -1
- package/dist/tools/trades.js +17 -3
- package/dist/tools/volume.d.ts.map +1 -1
- package/dist/tools/volume.js +21 -3
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +23 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +68 -0
- package/marketplace/README.md +1 -1
- package/marketplace/claude-listing.md +60 -14
- package/marketplace/gemini-tools.json +183 -9
- package/marketplace/openapi.yaml +335 -119
- package/package.json +1 -1
- package/server.json +2 -2
- package/src/http.ts +44 -0
- package/src/index.ts +34 -0
- package/src/tools/arbitrage.ts +202 -0
- package/src/tools/balances.ts +27 -4
- package/src/tools/calculate_position_size.ts +141 -0
- package/src/tools/compare_markets.ts +11 -10
- package/src/tools/dead_mans_switch.ts +314 -0
- package/src/tools/market_sentiment.ts +141 -0
- package/src/tools/market_summary.ts +124 -0
- package/src/tools/markets.ts +4 -2
- package/src/tools/orderbook.ts +15 -4
- package/src/tools/orders.ts +45 -4
- package/src/tools/price_history.ts +5 -57
- package/src/tools/simulate_order.ts +182 -0
- package/src/tools/spread.ts +10 -8
- package/src/tools/technical_indicators.ts +282 -0
- package/src/tools/ticker.ts +27 -3
- package/src/tools/trades.ts +18 -3
- package/src/tools/volume.ts +24 -3
- package/src/types.ts +12 -0
- package/src/utils.ts +73 -0
- package/test/unit.ts +758 -0
package/dist/tools/spread.js
CHANGED
|
@@ -4,8 +4,10 @@ import { CACHE_TTL } from "../cache.js";
|
|
|
4
4
|
import { validateMarketId } from "../validation.js";
|
|
5
5
|
export const toolSchema = {
|
|
6
6
|
name: "get_spread",
|
|
7
|
-
description: "
|
|
8
|
-
"
|
|
7
|
+
description: "Returns the best bid, best ask, absolute spread, and spread percentage for a Buda.com market. " +
|
|
8
|
+
"All prices are floats in the quote currency (e.g. CLP). spread_percentage is a float in percent " +
|
|
9
|
+
"(e.g. 0.15 means 0.15%). Use this to evaluate liquidity before placing a large order. " +
|
|
10
|
+
"Example: 'Is BTC-CLP liquid enough to buy 10M CLP without significant slippage?'",
|
|
9
11
|
inputSchema: {
|
|
10
12
|
type: "object",
|
|
11
13
|
properties: {
|
|
@@ -52,12 +54,12 @@ export function register(server, client, cache) {
|
|
|
52
54
|
const spreadPct = (spreadAbs / ask) * 100;
|
|
53
55
|
const result = {
|
|
54
56
|
market_id: ticker.market_id,
|
|
55
|
-
currency,
|
|
56
|
-
best_bid: bid
|
|
57
|
-
best_ask: ask
|
|
58
|
-
spread_absolute: spreadAbs.toFixed(2),
|
|
59
|
-
spread_percentage: spreadPct.toFixed(4)
|
|
60
|
-
last_price: ticker.last_price[0],
|
|
57
|
+
price_currency: currency,
|
|
58
|
+
best_bid: bid,
|
|
59
|
+
best_ask: ask,
|
|
60
|
+
spread_absolute: parseFloat(spreadAbs.toFixed(2)),
|
|
61
|
+
spread_percentage: parseFloat(spreadPct.toFixed(4)),
|
|
62
|
+
last_price: parseFloat(ticker.last_price[0]),
|
|
61
63
|
};
|
|
62
64
|
return {
|
|
63
65
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { BudaClient } from "../client.js";
|
|
3
|
+
export declare const toolSchema: {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: "object";
|
|
8
|
+
properties: {
|
|
9
|
+
market_id: {
|
|
10
|
+
type: string;
|
|
11
|
+
description: string;
|
|
12
|
+
};
|
|
13
|
+
period: {
|
|
14
|
+
type: string;
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
limit: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
required: string[];
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
type TechnicalIndicatorsArgs = {
|
|
26
|
+
market_id: string;
|
|
27
|
+
period: "1h" | "4h" | "1d";
|
|
28
|
+
limit?: number;
|
|
29
|
+
};
|
|
30
|
+
export declare function handleTechnicalIndicators(args: TechnicalIndicatorsArgs, client: BudaClient): Promise<{
|
|
31
|
+
content: Array<{
|
|
32
|
+
type: "text";
|
|
33
|
+
text: string;
|
|
34
|
+
}>;
|
|
35
|
+
isError?: boolean;
|
|
36
|
+
}>;
|
|
37
|
+
export declare function register(server: McpServer, client: BudaClient): void;
|
|
38
|
+
export {};
|
|
39
|
+
//# sourceMappingURL=technical_indicators.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"technical_indicators.d.ts","sourceRoot":"","sources":["../../src/tools/technical_indicators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AAKxD,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;CA6BtB,CAAC;AAsGF,KAAK,uBAAuB,GAAG;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAsB,yBAAyB,CAC7C,IAAI,EAAE,uBAAuB,EAC7B,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC,CA2GhF;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI,CAyBpE"}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { BudaApiError } from "../client.js";
|
|
3
|
+
import { validateMarketId } from "../validation.js";
|
|
4
|
+
import { aggregateTradesToCandles } from "../utils.js";
|
|
5
|
+
export const toolSchema = {
|
|
6
|
+
name: "get_technical_indicators",
|
|
7
|
+
description: "Computes RSI (14), MACD (12/26/9), Bollinger Bands (20, 2σ), SMA 20, and SMA 50 " +
|
|
8
|
+
"from Buda trade history — no external data or libraries. " +
|
|
9
|
+
"Uses at least 500 trades for reliable results (set limit=1000 for maximum depth). " +
|
|
10
|
+
"Returns latest indicator values and signal interpretations (overbought/oversold, crossover, band position). " +
|
|
11
|
+
"If fewer than 50 candles are available after aggregation, returns a structured warning instead. " +
|
|
12
|
+
"Example: 'Is BTC-CLP RSI overbought on the 4-hour chart?'",
|
|
13
|
+
inputSchema: {
|
|
14
|
+
type: "object",
|
|
15
|
+
properties: {
|
|
16
|
+
market_id: {
|
|
17
|
+
type: "string",
|
|
18
|
+
description: "Market ID (e.g. 'BTC-CLP', 'ETH-BTC').",
|
|
19
|
+
},
|
|
20
|
+
period: {
|
|
21
|
+
type: "string",
|
|
22
|
+
description: "Candle period: '1h', '4h', or '1d'. Default: '1h'.",
|
|
23
|
+
},
|
|
24
|
+
limit: {
|
|
25
|
+
type: "number",
|
|
26
|
+
description: "Number of raw trades to fetch (default: 500, max: 1000). " +
|
|
27
|
+
"More trades = more candles = more reliable indicators.",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
required: ["market_id"],
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
// ---- Pure math helpers ----
|
|
34
|
+
function sma(values, n) {
|
|
35
|
+
const slice = values.slice(-n);
|
|
36
|
+
return slice.reduce((sum, v) => sum + v, 0) / slice.length;
|
|
37
|
+
}
|
|
38
|
+
function ema(values, period) {
|
|
39
|
+
if (values.length < period)
|
|
40
|
+
return [];
|
|
41
|
+
const k = 2 / (period + 1);
|
|
42
|
+
const result = [];
|
|
43
|
+
// Seed with SMA of first `period` values
|
|
44
|
+
result.push(values.slice(0, period).reduce((s, v) => s + v, 0) / period);
|
|
45
|
+
for (let i = period; i < values.length; i++) {
|
|
46
|
+
result.push(values[i] * k + result[result.length - 1] * (1 - k));
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
function rsi(closes, period = 14) {
|
|
51
|
+
if (closes.length < period + 1)
|
|
52
|
+
return null;
|
|
53
|
+
const gains = [];
|
|
54
|
+
const losses = [];
|
|
55
|
+
for (let i = 1; i < closes.length; i++) {
|
|
56
|
+
const diff = closes[i] - closes[i - 1];
|
|
57
|
+
gains.push(diff > 0 ? diff : 0);
|
|
58
|
+
losses.push(diff < 0 ? -diff : 0);
|
|
59
|
+
}
|
|
60
|
+
// Use simple average for initial, then Wilder's smoothing
|
|
61
|
+
let avgGain = gains.slice(0, period).reduce((s, v) => s + v, 0) / period;
|
|
62
|
+
let avgLoss = losses.slice(0, period).reduce((s, v) => s + v, 0) / period;
|
|
63
|
+
for (let i = period; i < gains.length; i++) {
|
|
64
|
+
avgGain = (avgGain * (period - 1) + gains[i]) / period;
|
|
65
|
+
avgLoss = (avgLoss * (period - 1) + losses[i]) / period;
|
|
66
|
+
}
|
|
67
|
+
if (avgLoss === 0)
|
|
68
|
+
return 100;
|
|
69
|
+
const rs = avgGain / avgLoss;
|
|
70
|
+
return parseFloat((100 - 100 / (1 + rs)).toFixed(2));
|
|
71
|
+
}
|
|
72
|
+
function macd(closes, fast = 12, slow = 26, signalPeriod = 9) {
|
|
73
|
+
if (closes.length < slow + signalPeriod)
|
|
74
|
+
return null;
|
|
75
|
+
const ema12 = ema(closes, fast);
|
|
76
|
+
const ema26 = ema(closes, slow);
|
|
77
|
+
// Align: ema26 starts at index (slow-1) of closes; ema12 starts at index (fast-1)
|
|
78
|
+
// The MACD line length = ema26.length (shorter)
|
|
79
|
+
const offset = slow - fast;
|
|
80
|
+
const macdLine = ema26.map((e26, i) => ema12[i + offset] - e26);
|
|
81
|
+
const signalLine = ema(macdLine, signalPeriod);
|
|
82
|
+
if (signalLine.length === 0)
|
|
83
|
+
return null;
|
|
84
|
+
const lastMacd = macdLine[macdLine.length - 1];
|
|
85
|
+
const lastSignal = signalLine[signalLine.length - 1];
|
|
86
|
+
const histogram = lastMacd - lastSignal;
|
|
87
|
+
const prevHistogram = macdLine.length > 1 && signalLine.length > 1
|
|
88
|
+
? macdLine[macdLine.length - 2] - signalLine[signalLine.length - 2]
|
|
89
|
+
: null;
|
|
90
|
+
return {
|
|
91
|
+
line: parseFloat(lastMacd.toFixed(2)),
|
|
92
|
+
signal: parseFloat(lastSignal.toFixed(2)),
|
|
93
|
+
histogram: parseFloat(histogram.toFixed(2)),
|
|
94
|
+
prev_histogram: prevHistogram !== null ? parseFloat(prevHistogram.toFixed(2)) : null,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function bollingerBands(closes, period = 20, stdMult = 2) {
|
|
98
|
+
if (closes.length < period)
|
|
99
|
+
return null;
|
|
100
|
+
const slice = closes.slice(-period);
|
|
101
|
+
const mid = slice.reduce((s, v) => s + v, 0) / period;
|
|
102
|
+
const variance = slice.reduce((s, v) => s + (v - mid) ** 2, 0) / period;
|
|
103
|
+
const std = Math.sqrt(variance);
|
|
104
|
+
return {
|
|
105
|
+
upper: parseFloat((mid + stdMult * std).toFixed(2)),
|
|
106
|
+
mid: parseFloat(mid.toFixed(2)),
|
|
107
|
+
lower: parseFloat((mid - stdMult * std).toFixed(2)),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
// ---- Tool handler ----
|
|
111
|
+
const MIN_CANDLES = 50;
|
|
112
|
+
export async function handleTechnicalIndicators(args, client) {
|
|
113
|
+
const { market_id, period, limit } = args;
|
|
114
|
+
const validationError = validateMarketId(market_id);
|
|
115
|
+
if (validationError) {
|
|
116
|
+
return {
|
|
117
|
+
content: [{ type: "text", text: JSON.stringify({ error: validationError, code: "INVALID_MARKET_ID" }) }],
|
|
118
|
+
isError: true,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
const id = market_id.toLowerCase();
|
|
123
|
+
const tradesLimit = Math.max(limit ?? 500, 500);
|
|
124
|
+
const data = await client.get(`/markets/${id}/trades`, { limit: tradesLimit });
|
|
125
|
+
const candles = aggregateTradesToCandles(data.trades.entries, period);
|
|
126
|
+
const closes = candles.map((c) => c.close);
|
|
127
|
+
if (candles.length < MIN_CANDLES) {
|
|
128
|
+
return {
|
|
129
|
+
content: [
|
|
130
|
+
{
|
|
131
|
+
type: "text",
|
|
132
|
+
text: JSON.stringify({
|
|
133
|
+
market_id: market_id.toUpperCase(),
|
|
134
|
+
period,
|
|
135
|
+
indicators: null,
|
|
136
|
+
warning: "insufficient_data",
|
|
137
|
+
candles_available: candles.length,
|
|
138
|
+
minimum_required: MIN_CANDLES,
|
|
139
|
+
}),
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
const rsiValue = rsi(closes, 14);
|
|
145
|
+
const macdResult = macd(closes, 12, 26, 9);
|
|
146
|
+
const bbResult = bollingerBands(closes, 20, 2);
|
|
147
|
+
const sma20 = parseFloat(sma(closes, 20).toFixed(2));
|
|
148
|
+
const sma50 = parseFloat(sma(closes, 50).toFixed(2));
|
|
149
|
+
const lastClose = closes[closes.length - 1];
|
|
150
|
+
// Signal interpretations
|
|
151
|
+
const rsiSignal = rsiValue !== null && rsiValue > 70
|
|
152
|
+
? "overbought"
|
|
153
|
+
: rsiValue !== null && rsiValue < 30
|
|
154
|
+
? "oversold"
|
|
155
|
+
: "neutral";
|
|
156
|
+
let macdSignal = "neutral";
|
|
157
|
+
if (macdResult && macdResult.prev_histogram !== null) {
|
|
158
|
+
if (macdResult.histogram > 0 && macdResult.prev_histogram <= 0) {
|
|
159
|
+
macdSignal = "bullish_crossover";
|
|
160
|
+
}
|
|
161
|
+
else if (macdResult.histogram < 0 && macdResult.prev_histogram >= 0) {
|
|
162
|
+
macdSignal = "bearish_crossover";
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const bbSignal = bbResult && lastClose > bbResult.upper
|
|
166
|
+
? "above_upper"
|
|
167
|
+
: bbResult && lastClose < bbResult.lower
|
|
168
|
+
? "below_lower"
|
|
169
|
+
: "within_bands";
|
|
170
|
+
const result = {
|
|
171
|
+
market_id: market_id.toUpperCase(),
|
|
172
|
+
period,
|
|
173
|
+
candles_used: candles.length,
|
|
174
|
+
indicators: {
|
|
175
|
+
rsi: rsiValue,
|
|
176
|
+
macd: macdResult
|
|
177
|
+
? { line: macdResult.line, signal: macdResult.signal, histogram: macdResult.histogram }
|
|
178
|
+
: null,
|
|
179
|
+
bollinger_bands: bbResult,
|
|
180
|
+
sma_20: sma20,
|
|
181
|
+
sma_50: sma50,
|
|
182
|
+
},
|
|
183
|
+
signals: {
|
|
184
|
+
rsi_signal: rsiSignal,
|
|
185
|
+
macd_signal: macdSignal,
|
|
186
|
+
bb_signal: bbSignal,
|
|
187
|
+
},
|
|
188
|
+
disclaimer: "Technical indicators are computed from Buda trade history. Not investment advice.",
|
|
189
|
+
};
|
|
190
|
+
return {
|
|
191
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
const msg = err instanceof BudaApiError
|
|
196
|
+
? { error: err.message, code: err.status, path: err.path }
|
|
197
|
+
: { error: String(err), code: "UNKNOWN" };
|
|
198
|
+
return {
|
|
199
|
+
content: [{ type: "text", text: JSON.stringify(msg) }],
|
|
200
|
+
isError: true,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
export function register(server, client) {
|
|
205
|
+
server.tool(toolSchema.name, toolSchema.description, {
|
|
206
|
+
market_id: z
|
|
207
|
+
.string()
|
|
208
|
+
.describe("Market ID (e.g. 'BTC-CLP', 'ETH-BTC')."),
|
|
209
|
+
period: z
|
|
210
|
+
.enum(["1h", "4h", "1d"])
|
|
211
|
+
.default("1h")
|
|
212
|
+
.describe("Candle period: '1h', '4h', or '1d'. Default: '1h'."),
|
|
213
|
+
limit: z
|
|
214
|
+
.number()
|
|
215
|
+
.int()
|
|
216
|
+
.min(500)
|
|
217
|
+
.max(1000)
|
|
218
|
+
.optional()
|
|
219
|
+
.describe("Number of raw trades to fetch (default: 500, max: 1000). " +
|
|
220
|
+
"More trades = more candles = more reliable indicators."),
|
|
221
|
+
}, (args) => handleTechnicalIndicators(args, client));
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=technical_indicators.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ticker.d.ts","sourceRoot":"","sources":["../../src/tools/ticker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,WAAW,EAAa,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"ticker.d.ts","sourceRoot":"","sources":["../../src/tools/ticker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,WAAW,EAAa,MAAM,aAAa,CAAC;AAKrD,eAAO,MAAM,UAAU;;;;;;;;;;;;;CAiBtB,CAAC;AAEF,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,WAAW,GAAG,IAAI,CA6DxF"}
|
package/dist/tools/ticker.js
CHANGED
|
@@ -2,10 +2,13 @@ import { z } from "zod";
|
|
|
2
2
|
import { BudaApiError } from "../client.js";
|
|
3
3
|
import { CACHE_TTL } from "../cache.js";
|
|
4
4
|
import { validateMarketId } from "../validation.js";
|
|
5
|
+
import { flattenAmount } from "../utils.js";
|
|
5
6
|
export const toolSchema = {
|
|
6
7
|
name: "get_ticker",
|
|
7
|
-
description: "
|
|
8
|
-
"24h volume, and price change over 24h and 7d."
|
|
8
|
+
description: "Returns the current market snapshot for a Buda.com market: last traded price, best bid, " +
|
|
9
|
+
"best ask, 24h volume, and price change over 24h and 7d. All prices are floats in the quote " +
|
|
10
|
+
"currency (e.g. CLP for BTC-CLP). price_variation_24h is a decimal fraction (0.012 = +1.2%). " +
|
|
11
|
+
"Example: 'What is the current Bitcoin price in Chilean pesos?'",
|
|
9
12
|
inputSchema: {
|
|
10
13
|
type: "object",
|
|
11
14
|
properties: {
|
|
@@ -33,8 +36,26 @@ export function register(server, client, cache) {
|
|
|
33
36
|
}
|
|
34
37
|
const id = market_id.toLowerCase();
|
|
35
38
|
const data = await cache.getOrFetch(`ticker:${id}`, CACHE_TTL.TICKER, () => client.get(`/markets/${id}/ticker`));
|
|
39
|
+
const t = data.ticker;
|
|
40
|
+
const lastPrice = flattenAmount(t.last_price);
|
|
41
|
+
const minAsk = flattenAmount(t.min_ask);
|
|
42
|
+
const maxBid = flattenAmount(t.max_bid);
|
|
43
|
+
const volume = flattenAmount(t.volume);
|
|
44
|
+
const result = {
|
|
45
|
+
market_id: t.market_id,
|
|
46
|
+
last_price: lastPrice.value,
|
|
47
|
+
last_price_currency: lastPrice.currency,
|
|
48
|
+
min_ask: minAsk.value,
|
|
49
|
+
min_ask_currency: minAsk.currency,
|
|
50
|
+
max_bid: maxBid.value,
|
|
51
|
+
max_bid_currency: maxBid.currency,
|
|
52
|
+
volume: volume.value,
|
|
53
|
+
volume_currency: volume.currency,
|
|
54
|
+
price_variation_24h: parseFloat(t.price_variation_24h),
|
|
55
|
+
price_variation_7d: parseFloat(t.price_variation_7d),
|
|
56
|
+
};
|
|
36
57
|
return {
|
|
37
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
58
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
38
59
|
};
|
|
39
60
|
}
|
|
40
61
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trades.d.ts","sourceRoot":"","sources":["../../src/tools/trades.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI1C,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"trades.d.ts","sourceRoot":"","sources":["../../src/tools/trades.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI1C,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;CA0BtB,CAAC;AAEF,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAsEzF"}
|
package/dist/tools/trades.js
CHANGED
|
@@ -3,8 +3,10 @@ import { BudaApiError } from "../client.js";
|
|
|
3
3
|
import { validateMarketId } from "../validation.js";
|
|
4
4
|
export const toolSchema = {
|
|
5
5
|
name: "get_trades",
|
|
6
|
-
description: "
|
|
7
|
-
"
|
|
6
|
+
description: "Returns recent trade history for a Buda.com market as typed objects. Each entry has " +
|
|
7
|
+
"timestamp_ms (integer), amount (float, base currency), price (float, quote currency), " +
|
|
8
|
+
"and direction ('buy' or 'sell'). " +
|
|
9
|
+
"Example: 'What was the last executed price for BTC-CLP and was it a buy or sell?'",
|
|
8
10
|
inputSchema: {
|
|
9
11
|
type: "object",
|
|
10
12
|
properties: {
|
|
@@ -56,8 +58,20 @@ export function register(server, client, _cache) {
|
|
|
56
58
|
if (timestamp !== undefined)
|
|
57
59
|
params.timestamp = timestamp;
|
|
58
60
|
const data = await client.get(`/markets/${market_id.toLowerCase()}/trades`, Object.keys(params).length > 0 ? params : undefined);
|
|
61
|
+
const t = data.trades;
|
|
62
|
+
const result = {
|
|
63
|
+
timestamp: t.timestamp,
|
|
64
|
+
last_timestamp: t.last_timestamp,
|
|
65
|
+
market_id: t.market_id,
|
|
66
|
+
entries: t.entries.map(([tsMs, amount, price, direction]) => ({
|
|
67
|
+
timestamp_ms: parseInt(tsMs, 10),
|
|
68
|
+
amount: parseFloat(amount),
|
|
69
|
+
price: parseFloat(price),
|
|
70
|
+
direction,
|
|
71
|
+
})),
|
|
72
|
+
};
|
|
59
73
|
return {
|
|
60
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
74
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
61
75
|
};
|
|
62
76
|
}
|
|
63
77
|
catch (err) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"volume.d.ts","sourceRoot":"","sources":["../../src/tools/volume.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"volume.d.ts","sourceRoot":"","sources":["../../src/tools/volume.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AAEpE,OAAO,EAAE,UAAU,EAAgB,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK1C,eAAO,MAAM,UAAU;;;;;;;;;;;;;CAgBtB,CAAC;AAEF,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,CAwDzF"}
|
package/dist/tools/volume.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { BudaApiError } from "../client.js";
|
|
3
3
|
import { validateMarketId } from "../validation.js";
|
|
4
|
+
import { flattenAmount } from "../utils.js";
|
|
4
5
|
export const toolSchema = {
|
|
5
6
|
name: "get_market_volume",
|
|
6
|
-
description: "
|
|
7
|
-
"
|
|
7
|
+
description: "Returns 24h and 7-day transacted volume for a Buda.com market, split by buy (bid) and sell (ask) side. " +
|
|
8
|
+
"All volume values are floats in the base currency (e.g. BTC for BTC-CLP). " +
|
|
9
|
+
"Example: 'How much Bitcoin was sold on BTC-CLP in the last 24 hours?'",
|
|
8
10
|
inputSchema: {
|
|
9
11
|
type: "object",
|
|
10
12
|
properties: {
|
|
@@ -31,8 +33,24 @@ export function register(server, client, _cache) {
|
|
|
31
33
|
};
|
|
32
34
|
}
|
|
33
35
|
const data = await client.get(`/markets/${market_id.toLowerCase()}/volume`);
|
|
36
|
+
const v = data.volume;
|
|
37
|
+
const ask24 = flattenAmount(v.ask_volume_24h);
|
|
38
|
+
const ask7d = flattenAmount(v.ask_volume_7d);
|
|
39
|
+
const bid24 = flattenAmount(v.bid_volume_24h);
|
|
40
|
+
const bid7d = flattenAmount(v.bid_volume_7d);
|
|
41
|
+
const result = {
|
|
42
|
+
market_id: v.market_id,
|
|
43
|
+
ask_volume_24h: ask24.value,
|
|
44
|
+
ask_volume_24h_currency: ask24.currency,
|
|
45
|
+
ask_volume_7d: ask7d.value,
|
|
46
|
+
ask_volume_7d_currency: ask7d.currency,
|
|
47
|
+
bid_volume_24h: bid24.value,
|
|
48
|
+
bid_volume_24h_currency: bid24.currency,
|
|
49
|
+
bid_volume_7d: bid7d.value,
|
|
50
|
+
bid_volume_7d_currency: bid7d.currency,
|
|
51
|
+
};
|
|
34
52
|
return {
|
|
35
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
53
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
36
54
|
};
|
|
37
55
|
}
|
|
38
56
|
catch (err) {
|
package/dist/types.d.ts
CHANGED
|
@@ -99,4 +99,13 @@ export interface OrdersResponse {
|
|
|
99
99
|
total_pages: number;
|
|
100
100
|
};
|
|
101
101
|
}
|
|
102
|
+
export interface OhlcvCandle {
|
|
103
|
+
time: string;
|
|
104
|
+
open: number;
|
|
105
|
+
high: number;
|
|
106
|
+
low: number;
|
|
107
|
+
close: number;
|
|
108
|
+
volume: number;
|
|
109
|
+
trade_count: number;
|
|
110
|
+
}
|
|
102
111
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Amount, OhlcvCandle } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Flattens a Buda API Amount tuple [value_string, currency] into a typed object.
|
|
4
|
+
* All numeric strings are cast to float via parseFloat.
|
|
5
|
+
*/
|
|
6
|
+
export declare function flattenAmount(amount: Amount): {
|
|
7
|
+
value: number;
|
|
8
|
+
currency: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Returns a liquidity rating based on the bid/ask spread percentage.
|
|
12
|
+
* < 0.3% → "high"
|
|
13
|
+
* 0.3–1% → "medium"
|
|
14
|
+
* > 1% → "low"
|
|
15
|
+
*/
|
|
16
|
+
export declare function getLiquidityRating(spreadPct: number): "high" | "medium" | "low";
|
|
17
|
+
/**
|
|
18
|
+
* Aggregates raw Buda trade entries (newest-first) into OHLCV candles for the given period.
|
|
19
|
+
* Entries must be in the format [timestamp_ms, amount, price, direction].
|
|
20
|
+
* Returns candles sorted ascending by bucket start time.
|
|
21
|
+
*/
|
|
22
|
+
export declare function aggregateTradesToCandles(entries: [string, string, string, string][], period: string): OhlcvCandle[];
|
|
23
|
+
//# sourceMappingURL=utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEtD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAEjF;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAK,CAI/E;AAQD;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,EAC3C,MAAM,EAAE,MAAM,GACb,WAAW,EAAE,CAoCf"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flattens a Buda API Amount tuple [value_string, currency] into a typed object.
|
|
3
|
+
* All numeric strings are cast to float via parseFloat.
|
|
4
|
+
*/
|
|
5
|
+
export function flattenAmount(amount) {
|
|
6
|
+
return { value: parseFloat(amount[0]), currency: amount[1] };
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Returns a liquidity rating based on the bid/ask spread percentage.
|
|
10
|
+
* < 0.3% → "high"
|
|
11
|
+
* 0.3–1% → "medium"
|
|
12
|
+
* > 1% → "low"
|
|
13
|
+
*/
|
|
14
|
+
export function getLiquidityRating(spreadPct) {
|
|
15
|
+
if (spreadPct < 0.3)
|
|
16
|
+
return "high";
|
|
17
|
+
if (spreadPct <= 1.0)
|
|
18
|
+
return "medium";
|
|
19
|
+
return "low";
|
|
20
|
+
}
|
|
21
|
+
const PERIOD_MS = {
|
|
22
|
+
"1h": 60 * 60 * 1000,
|
|
23
|
+
"4h": 4 * 60 * 60 * 1000,
|
|
24
|
+
"1d": 24 * 60 * 60 * 1000,
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Aggregates raw Buda trade entries (newest-first) into OHLCV candles for the given period.
|
|
28
|
+
* Entries must be in the format [timestamp_ms, amount, price, direction].
|
|
29
|
+
* Returns candles sorted ascending by bucket start time.
|
|
30
|
+
*/
|
|
31
|
+
export function aggregateTradesToCandles(entries, period) {
|
|
32
|
+
const periodMs = PERIOD_MS[period];
|
|
33
|
+
if (!periodMs)
|
|
34
|
+
throw new Error(`Unknown period: ${period}`);
|
|
35
|
+
const sorted = [...entries].sort(([a], [b]) => parseInt(a, 10) - parseInt(b, 10));
|
|
36
|
+
const buckets = new Map();
|
|
37
|
+
for (const [tsMs, amount, price] of sorted) {
|
|
38
|
+
const ts = parseInt(tsMs, 10);
|
|
39
|
+
const bucketStart = Math.floor(ts / periodMs) * periodMs;
|
|
40
|
+
const p = parseFloat(price);
|
|
41
|
+
const v = parseFloat(amount);
|
|
42
|
+
if (!buckets.has(bucketStart)) {
|
|
43
|
+
buckets.set(bucketStart, {
|
|
44
|
+
time: new Date(bucketStart).toISOString(),
|
|
45
|
+
open: p,
|
|
46
|
+
high: p,
|
|
47
|
+
low: p,
|
|
48
|
+
close: p,
|
|
49
|
+
volume: v,
|
|
50
|
+
trade_count: 1,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
const candle = buckets.get(bucketStart);
|
|
55
|
+
if (p > candle.high)
|
|
56
|
+
candle.high = p;
|
|
57
|
+
if (p < candle.low)
|
|
58
|
+
candle.low = p;
|
|
59
|
+
candle.close = p;
|
|
60
|
+
candle.volume = parseFloat((candle.volume + v).toFixed(8));
|
|
61
|
+
candle.trade_count++;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return Array.from(buckets.entries())
|
|
65
|
+
.sort(([a], [b]) => a - b)
|
|
66
|
+
.map(([, candle]) => candle);
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=utils.js.map
|
package/marketplace/README.md
CHANGED