@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.
Files changed (79) hide show
  1. package/CHANGELOG.md +68 -0
  2. package/PUBLISH_CHECKLIST.md +71 -63
  3. package/README.md +4 -4
  4. package/dist/http.js +39 -0
  5. package/dist/index.js +29 -0
  6. package/dist/tools/arbitrage.d.ts +35 -0
  7. package/dist/tools/arbitrage.d.ts.map +1 -0
  8. package/dist/tools/arbitrage.js +142 -0
  9. package/dist/tools/balances.d.ts.map +1 -1
  10. package/dist/tools/balances.js +24 -4
  11. package/dist/tools/calculate_position_size.d.ts +48 -0
  12. package/dist/tools/calculate_position_size.d.ts.map +1 -0
  13. package/dist/tools/calculate_position_size.js +111 -0
  14. package/dist/tools/compare_markets.d.ts.map +1 -1
  15. package/dist/tools/compare_markets.js +11 -10
  16. package/dist/tools/dead_mans_switch.d.ts +84 -0
  17. package/dist/tools/dead_mans_switch.d.ts.map +1 -0
  18. package/dist/tools/dead_mans_switch.js +236 -0
  19. package/dist/tools/market_sentiment.d.ts +30 -0
  20. package/dist/tools/market_sentiment.d.ts.map +1 -0
  21. package/dist/tools/market_sentiment.js +104 -0
  22. package/dist/tools/market_summary.d.ts +43 -0
  23. package/dist/tools/market_summary.d.ts.map +1 -0
  24. package/dist/tools/market_summary.js +81 -0
  25. package/dist/tools/markets.d.ts.map +1 -1
  26. package/dist/tools/markets.js +4 -2
  27. package/dist/tools/orderbook.d.ts.map +1 -1
  28. package/dist/tools/orderbook.js +14 -4
  29. package/dist/tools/orders.d.ts.map +1 -1
  30. package/dist/tools/orders.js +41 -3
  31. package/dist/tools/price_history.d.ts.map +1 -1
  32. package/dist/tools/price_history.js +5 -43
  33. package/dist/tools/simulate_order.d.ts +45 -0
  34. package/dist/tools/simulate_order.d.ts.map +1 -0
  35. package/dist/tools/simulate_order.js +139 -0
  36. package/dist/tools/spread.d.ts.map +1 -1
  37. package/dist/tools/spread.js +10 -8
  38. package/dist/tools/technical_indicators.d.ts +39 -0
  39. package/dist/tools/technical_indicators.d.ts.map +1 -0
  40. package/dist/tools/technical_indicators.js +223 -0
  41. package/dist/tools/ticker.d.ts.map +1 -1
  42. package/dist/tools/ticker.js +24 -3
  43. package/dist/tools/trades.d.ts.map +1 -1
  44. package/dist/tools/trades.js +17 -3
  45. package/dist/tools/volume.d.ts.map +1 -1
  46. package/dist/tools/volume.js +21 -3
  47. package/dist/types.d.ts +9 -0
  48. package/dist/types.d.ts.map +1 -1
  49. package/dist/utils.d.ts +23 -0
  50. package/dist/utils.d.ts.map +1 -0
  51. package/dist/utils.js +68 -0
  52. package/marketplace/README.md +1 -1
  53. package/marketplace/claude-listing.md +60 -14
  54. package/marketplace/gemini-tools.json +183 -9
  55. package/marketplace/openapi.yaml +335 -119
  56. package/package.json +1 -1
  57. package/server.json +2 -2
  58. package/src/http.ts +44 -0
  59. package/src/index.ts +34 -0
  60. package/src/tools/arbitrage.ts +202 -0
  61. package/src/tools/balances.ts +27 -4
  62. package/src/tools/calculate_position_size.ts +141 -0
  63. package/src/tools/compare_markets.ts +11 -10
  64. package/src/tools/dead_mans_switch.ts +314 -0
  65. package/src/tools/market_sentiment.ts +141 -0
  66. package/src/tools/market_summary.ts +124 -0
  67. package/src/tools/markets.ts +4 -2
  68. package/src/tools/orderbook.ts +15 -4
  69. package/src/tools/orders.ts +45 -4
  70. package/src/tools/price_history.ts +5 -57
  71. package/src/tools/simulate_order.ts +182 -0
  72. package/src/tools/spread.ts +10 -8
  73. package/src/tools/technical_indicators.ts +282 -0
  74. package/src/tools/ticker.ts +27 -3
  75. package/src/tools/trades.ts +18 -3
  76. package/src/tools/volume.ts +24 -3
  77. package/src/types.ts +12 -0
  78. package/src/utils.ts +73 -0
  79. package/test/unit.ts +758 -0
@@ -3,13 +3,16 @@ import { z } from "zod";
3
3
  import { BudaClient, BudaApiError } from "../client.js";
4
4
  import { MemoryCache, CACHE_TTL } from "../cache.js";
5
5
  import { validateMarketId } from "../validation.js";
6
+ import { flattenAmount } from "../utils.js";
6
7
  import type { TickerResponse } from "../types.js";
7
8
 
8
9
  export const toolSchema = {
9
10
  name: "get_ticker",
10
11
  description:
11
- "Get the current ticker for a Buda.com market: last traded price, best bid/ask, " +
12
- "24h volume, and price change over 24h and 7d.",
12
+ "Returns the current market snapshot for a Buda.com market: last traded price, best bid, " +
13
+ "best ask, 24h volume, and price change over 24h and 7d. All prices are floats in the quote " +
14
+ "currency (e.g. CLP for BTC-CLP). price_variation_24h is a decimal fraction (0.012 = +1.2%). " +
15
+ "Example: 'What is the current Bitcoin price in Chilean pesos?'",
13
16
  inputSchema: {
14
17
  type: "object" as const,
15
18
  properties: {
@@ -47,8 +50,29 @@ export function register(server: McpServer, client: BudaClient, cache: MemoryCac
47
50
  CACHE_TTL.TICKER,
48
51
  () => client.get<TickerResponse>(`/markets/${id}/ticker`),
49
52
  );
53
+
54
+ const t = data.ticker;
55
+ const lastPrice = flattenAmount(t.last_price);
56
+ const minAsk = flattenAmount(t.min_ask);
57
+ const maxBid = flattenAmount(t.max_bid);
58
+ const volume = flattenAmount(t.volume);
59
+
60
+ const result = {
61
+ market_id: t.market_id,
62
+ last_price: lastPrice.value,
63
+ last_price_currency: lastPrice.currency,
64
+ min_ask: minAsk.value,
65
+ min_ask_currency: minAsk.currency,
66
+ max_bid: maxBid.value,
67
+ max_bid_currency: maxBid.currency,
68
+ volume: volume.value,
69
+ volume_currency: volume.currency,
70
+ price_variation_24h: parseFloat(t.price_variation_24h),
71
+ price_variation_7d: parseFloat(t.price_variation_7d),
72
+ };
73
+
50
74
  return {
51
- content: [{ type: "text", text: JSON.stringify(data.ticker, null, 2) }],
75
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
52
76
  };
53
77
  } catch (err) {
54
78
  const msg =
@@ -8,8 +8,10 @@ import type { TradesResponse } from "../types.js";
8
8
  export const toolSchema = {
9
9
  name: "get_trades",
10
10
  description:
11
- "Get recent trade history for a Buda.com market. Each entry contains " +
12
- "[timestamp_ms, amount, price, direction]. Direction is 'buy' or 'sell'.",
11
+ "Returns recent trade history for a Buda.com market as typed objects. Each entry has " +
12
+ "timestamp_ms (integer), amount (float, base currency), price (float, quote currency), " +
13
+ "and direction ('buy' or 'sell'). " +
14
+ "Example: 'What was the last executed price for BTC-CLP and was it a buy or sell?'",
13
15
  inputSchema: {
14
16
  type: "object" as const,
15
17
  properties: {
@@ -73,8 +75,21 @@ export function register(server: McpServer, client: BudaClient, _cache: MemoryCa
73
75
  Object.keys(params).length > 0 ? params : undefined,
74
76
  );
75
77
 
78
+ const t = data.trades;
79
+ const result = {
80
+ timestamp: t.timestamp,
81
+ last_timestamp: t.last_timestamp,
82
+ market_id: t.market_id,
83
+ entries: t.entries.map(([tsMs, amount, price, direction]) => ({
84
+ timestamp_ms: parseInt(tsMs, 10),
85
+ amount: parseFloat(amount),
86
+ price: parseFloat(price),
87
+ direction,
88
+ })),
89
+ };
90
+
76
91
  return {
77
- content: [{ type: "text", text: JSON.stringify(data.trades, null, 2) }],
92
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
78
93
  };
79
94
  } catch (err) {
80
95
  const msg =
@@ -3,13 +3,15 @@ import { z } from "zod";
3
3
  import { BudaClient, BudaApiError } from "../client.js";
4
4
  import { MemoryCache } from "../cache.js";
5
5
  import { validateMarketId } from "../validation.js";
6
+ import { flattenAmount } from "../utils.js";
6
7
  import type { VolumeResponse } from "../types.js";
7
8
 
8
9
  export const toolSchema = {
9
10
  name: "get_market_volume",
10
11
  description:
11
- "Get 24h and 7-day transacted volume for a Buda.com market. " +
12
- "Returns ask (sell) and bid (buy) volumes in the market's base currency.",
12
+ "Returns 24h and 7-day transacted volume for a Buda.com market, split by buy (bid) and sell (ask) side. " +
13
+ "All volume values are floats in the base currency (e.g. BTC for BTC-CLP). " +
14
+ "Example: 'How much Bitcoin was sold on BTC-CLP in the last 24 hours?'",
13
15
  inputSchema: {
14
16
  type: "object" as const,
15
17
  properties: {
@@ -44,8 +46,27 @@ export function register(server: McpServer, client: BudaClient, _cache: MemoryCa
44
46
  const data = await client.get<VolumeResponse>(
45
47
  `/markets/${market_id.toLowerCase()}/volume`,
46
48
  );
49
+
50
+ const v = data.volume;
51
+ const ask24 = flattenAmount(v.ask_volume_24h);
52
+ const ask7d = flattenAmount(v.ask_volume_7d);
53
+ const bid24 = flattenAmount(v.bid_volume_24h);
54
+ const bid7d = flattenAmount(v.bid_volume_7d);
55
+
56
+ const result = {
57
+ market_id: v.market_id,
58
+ ask_volume_24h: ask24.value,
59
+ ask_volume_24h_currency: ask24.currency,
60
+ ask_volume_7d: ask7d.value,
61
+ ask_volume_7d_currency: ask7d.currency,
62
+ bid_volume_24h: bid24.value,
63
+ bid_volume_24h_currency: bid24.currency,
64
+ bid_volume_7d: bid7d.value,
65
+ bid_volume_7d_currency: bid7d.currency,
66
+ };
67
+
47
68
  return {
48
- content: [{ type: "text", text: JSON.stringify(data.volume, null, 2) }],
69
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
49
70
  };
50
71
  } catch (err) {
51
72
  const msg =
package/src/types.ts CHANGED
@@ -133,3 +133,15 @@ export interface OrdersResponse {
133
133
  total_pages: number;
134
134
  };
135
135
  }
136
+
137
+ // ----- OHLCV Candles -----
138
+
139
+ export interface OhlcvCandle {
140
+ time: string;
141
+ open: number;
142
+ high: number;
143
+ low: number;
144
+ close: number;
145
+ volume: number;
146
+ trade_count: number;
147
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,73 @@
1
+ import type { Amount, OhlcvCandle } from "./types.js";
2
+
3
+ /**
4
+ * Flattens a Buda API Amount tuple [value_string, currency] into a typed object.
5
+ * All numeric strings are cast to float via parseFloat.
6
+ */
7
+ export function flattenAmount(amount: Amount): { value: number; currency: string } {
8
+ return { value: parseFloat(amount[0]), currency: amount[1] };
9
+ }
10
+
11
+ /**
12
+ * Returns a liquidity rating based on the bid/ask spread percentage.
13
+ * < 0.3% → "high"
14
+ * 0.3–1% → "medium"
15
+ * > 1% → "low"
16
+ */
17
+ export function getLiquidityRating(spreadPct: number): "high" | "medium" | "low" {
18
+ if (spreadPct < 0.3) return "high";
19
+ if (spreadPct <= 1.0) return "medium";
20
+ return "low";
21
+ }
22
+
23
+ const PERIOD_MS: Record<string, number> = {
24
+ "1h": 60 * 60 * 1000,
25
+ "4h": 4 * 60 * 60 * 1000,
26
+ "1d": 24 * 60 * 60 * 1000,
27
+ };
28
+
29
+ /**
30
+ * Aggregates raw Buda trade entries (newest-first) into OHLCV candles for the given period.
31
+ * Entries must be in the format [timestamp_ms, amount, price, direction].
32
+ * Returns candles sorted ascending by bucket start time.
33
+ */
34
+ export function aggregateTradesToCandles(
35
+ entries: [string, string, string, string][],
36
+ period: string,
37
+ ): OhlcvCandle[] {
38
+ const periodMs = PERIOD_MS[period];
39
+ if (!periodMs) throw new Error(`Unknown period: ${period}`);
40
+
41
+ const sorted = [...entries].sort(([a], [b]) => parseInt(a, 10) - parseInt(b, 10));
42
+ const buckets = new Map<number, OhlcvCandle>();
43
+
44
+ for (const [tsMs, amount, price] of sorted) {
45
+ const ts = parseInt(tsMs, 10);
46
+ const bucketStart = Math.floor(ts / periodMs) * periodMs;
47
+ const p = parseFloat(price);
48
+ const v = parseFloat(amount);
49
+
50
+ if (!buckets.has(bucketStart)) {
51
+ buckets.set(bucketStart, {
52
+ time: new Date(bucketStart).toISOString(),
53
+ open: p,
54
+ high: p,
55
+ low: p,
56
+ close: p,
57
+ volume: v,
58
+ trade_count: 1,
59
+ });
60
+ } else {
61
+ const candle = buckets.get(bucketStart)!;
62
+ if (p > candle.high) candle.high = p;
63
+ if (p < candle.low) candle.low = p;
64
+ candle.close = p;
65
+ candle.volume = parseFloat((candle.volume + v).toFixed(8));
66
+ candle.trade_count++;
67
+ }
68
+ }
69
+
70
+ return Array.from(buckets.entries())
71
+ .sort(([a], [b]) => a - b)
72
+ .map(([, candle]) => candle);
73
+ }