melaya 0.1.2 → 0.2.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.
- checksums.yaml +4 -4
- data/LICENSE +158 -0
- data/README.md +244 -163
- data/lib/melaya/account.rb +30 -30
- data/lib/melaya/accounts.rb +42 -0
- data/lib/melaya/assistant.rb +41 -0
- data/lib/melaya/auth.rb +137 -0
- data/lib/melaya/backtest.rb +139 -101
- data/lib/melaya/billing.rb +75 -0
- data/lib/melaya/bugs.rb +67 -0
- data/lib/melaya/connectors.rb +71 -0
- data/lib/melaya/credentials.rb +255 -0
- data/lib/melaya/errors.rb +49 -15
- data/lib/melaya/evals.rb +78 -0
- data/lib/melaya/events.rb +425 -0
- data/lib/melaya/hitl.rb +114 -0
- data/lib/melaya/http_client.rb +226 -97
- data/lib/melaya/market.rb +198 -156
- data/lib/melaya/namespaces.rb +115 -0
- data/lib/melaya/phone.rb +75 -0
- data/lib/melaya/pipelines.rb +342 -0
- data/lib/melaya/projects.rb +58 -0
- data/lib/melaya/runner.rb +44 -0
- data/lib/melaya/sim.rb +110 -110
- data/lib/melaya/strategies.rb +170 -155
- data/lib/melaya/stream.rb +338 -336
- data/lib/melaya/team.rb +102 -0
- data/lib/melaya/templates.rb +148 -0
- data/lib/melaya/trade.rb +168 -168
- data/lib/melaya/version.rb +1 -1
- data/lib/melaya.rb +310 -79
- data/melaya.gemspec +28 -23
- metadata +32 -8
data/lib/melaya/market.rb
CHANGED
|
@@ -1,156 +1,198 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Melaya
|
|
4
|
-
# REST market-data API — normalized across all 70+ venues.
|
|
5
|
-
#
|
|
6
|
-
# Every method maps to a documented endpoint under
|
|
7
|
-
# https://api.melaya.org/api/v1/market/*. Unwraps the inner data key from the
|
|
8
|
-
# {ok, <data>} envelope.
|
|
9
|
-
class MarketAPI
|
|
10
|
-
def initialize(http)
|
|
11
|
-
@http = http
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
# List the exchanges Melaya supports right now (the source of truth).
|
|
15
|
-
def list_exchanges
|
|
16
|
-
@http.get("/api/v1/market/list-exchanges")["exchanges"]
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# Best bid/ask, last price, and 24h aggregates for one symbol.
|
|
20
|
-
# @param exchange [String]
|
|
21
|
-
# @param symbol [String]
|
|
22
|
-
# @param market [String, nil] e.g. "spot", "future", "swap"
|
|
23
|
-
def ticker(exchange:, symbol:, market: nil)
|
|
24
|
-
@http.get("/api/v1/market/ticker",
|
|
25
|
-
"exchange" => exchange, "symbol" => symbol, "market" => market
|
|
26
|
-
)["ticker"]
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Order book to a given depth.
|
|
30
|
-
def orderbook(exchange:, symbol:, limit: nil, market: nil)
|
|
31
|
-
@http.get("/api/v1/market/orderbook",
|
|
32
|
-
"exchange" => exchange, "symbol" => symbol, "limit" => limit, "market" => market
|
|
33
|
-
)["orderbook"]
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# OHLCV candles. Each candle is [timestamp, open, high, low, close, volume].
|
|
37
|
-
def ohlcv(exchange:, symbol:, timeframe:, limit: nil, market: nil)
|
|
38
|
-
@http.get("/api/v1/market/ohlcv",
|
|
39
|
-
"exchange" => exchange, "symbol" => symbol, "timeframe" => timeframe,
|
|
40
|
-
"limit" => limit, "market" => market
|
|
41
|
-
)["candles"]
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
# Recent public trades.
|
|
45
|
-
def trades(exchange:, symbol:, market: nil)
|
|
46
|
-
@http.get("/api/v1/market/trades",
|
|
47
|
-
"exchange" => exchange, "symbol" => symbol, "market" => market
|
|
48
|
-
)["trades"]
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Tradable markets on a venue.
|
|
52
|
-
def markets(exchange:)
|
|
53
|
-
@http.get("/api/v1/market/markets", "exchange" => exchange)["markets"]
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# Listed currencies on a venue. (Not supported on every venue.)
|
|
57
|
-
def currencies(exchange:)
|
|
58
|
-
@http.get("/api/v1/market/currencies", "exchange" => exchange)["currencies"]
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# Operational status: ok / maintenance / degraded.
|
|
62
|
-
def status(exchange:)
|
|
63
|
-
@http.get("/api/v1/market/status", "exchange" => exchange)["status"]
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Exchange server time.
|
|
67
|
-
def time(exchange:)
|
|
68
|
-
@http.get("/api/v1/market/time", "exchange" => exchange)["time"]
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# ── Batch / derivatives (POST) ──────────────────────────────────────────
|
|
72
|
-
|
|
73
|
-
# Tickers for many symbols on one venue in a single call. Keyed by symbol.
|
|
74
|
-
def tickers(exchange:, symbols:, market: nil)
|
|
75
|
-
body = compact("exchange" => exchange, "symbols" => symbols, "market" => market)
|
|
76
|
-
@http.post("/api/v1/market/tickers", body)["tickers"]
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
# Latest funding rates for perpetuals. Keyed by symbol.
|
|
80
|
-
def funding_rates(exchange:, symbols:, market: nil)
|
|
81
|
-
body = compact("exchange" => exchange, "symbols" => symbols, "market" => market)
|
|
82
|
-
@http.post("/api/v1/market/funding-rates", body)["rates"]
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# Funding-rate history.
|
|
86
|
-
def funding_rate_history(exchange:, symbol:, hours: nil, market: nil)
|
|
87
|
-
body = compact("exchange" => exchange, "symbol" => symbol, "hours" => hours, "market" => market)
|
|
88
|
-
@http.post("/api/v1/market/funding-rate-history", body)["history"]
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
# Open interest for one or more perpetuals. Keyed by symbol.
|
|
92
|
-
def open_interest(exchange:, symbols:, market: nil)
|
|
93
|
-
body = compact("exchange" => exchange, "symbols" => symbols, "market" => market)
|
|
94
|
-
@http.post("/api/v1/market/open-interest", body)["openInterest"]
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Open-interest history.
|
|
98
|
-
def open_interest_history(exchange:, symbol:, hours: nil, market: nil)
|
|
99
|
-
body = compact("exchange" => exchange, "symbol" => symbol, "hours" => hours, "market" => market)
|
|
100
|
-
@http.post("/api/v1/market/open-interest-history", body)["history"]
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
# Instrument list + trading constraints (tick size, min notional, qty step).
|
|
104
|
-
def instruments(exchange:, market: nil)
|
|
105
|
-
body = compact("exchange" => exchange, "market" => market)
|
|
106
|
-
@http.post("/api/v1/market/instruments", body)
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# Cross-exchange liquidation events (historical query).
|
|
110
|
-
def liquidation_events(exchange: nil, symbol: nil, since_ms: nil, limit: nil)
|
|
111
|
-
body = compact("exchange" => exchange, "symbol" => symbol, "sinceMs" => since_ms, "limit" => limit)
|
|
112
|
-
@http.post("/api/v1/market/liquidation-events", body)["events"]
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
# Multi-symbol OHLCV in one call. Returns candle arrays keyed by symbol.
|
|
116
|
-
def ohlcv_multi(exchange:, symbols:, timeframe:, limit: nil, market: nil)
|
|
117
|
-
body = compact("exchange" => exchange, "symbols" => symbols, "timeframe" => timeframe,
|
|
118
|
-
"limit" => limit, "market" => market)
|
|
119
|
-
@http.post("/api/v1/market/ohlcv-multi", body)["perSymbol"]
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
# Trading constraints for one symbol (tick size, min notional, qty step, leverage).
|
|
123
|
-
def market_constraints(exchange:, symbol:, market: nil)
|
|
124
|
-
body = compact("exchange" => exchange, "symbol" => symbol, "market" => market)
|
|
125
|
-
@http.post("/api/v1/market/market-constraints", body)["constraints"]
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
# Funding-rate history for one symbol across several venues. Keyed by exchange.
|
|
129
|
-
def funding_rate_history_multi(exchanges:, symbol:, hours: nil)
|
|
130
|
-
body = compact("exchanges" => exchanges, "symbol" => symbol, "hours" => hours)
|
|
131
|
-
@http.post("/api/v1/market/funding-rate-history-multi", body)["perExchange"]
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
# Open-interest history for one symbol across several venues. Keyed by exchange.
|
|
135
|
-
def open_interest_history_multi(exchanges:, symbol:, hours: nil)
|
|
136
|
-
body = compact("exchanges" => exchanges, "symbol" => symbol, "hours" => hours)
|
|
137
|
-
@http.post("/api/v1/market/open-interest-history-multi", body)["perExchange"]
|
|
138
|
-
end
|
|
139
|
-
|
|
140
|
-
# Prediction-market listings for a venue (polymarket, kalshi, drift_pm, sxbet, azuro, overtime).
|
|
141
|
-
def prediction_markets(venue: "polymarket")
|
|
142
|
-
@http.post("/api/v1/market/pm-markets", { "venue" => venue })["markets"]
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
# Live platform catalog counts (agentic tools, subagents, by category). Public.
|
|
146
|
-
def catalog_counts
|
|
147
|
-
@http.get("/api/v1/public/catalog-counts")
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Melaya
|
|
4
|
+
# REST market-data API — normalized across all 70+ venues.
|
|
5
|
+
#
|
|
6
|
+
# Every method maps to a documented endpoint under
|
|
7
|
+
# https://api.melaya.org/api/v1/market/*. Unwraps the inner data key from the
|
|
8
|
+
# {ok, <data>} envelope.
|
|
9
|
+
class MarketAPI
|
|
10
|
+
def initialize(http)
|
|
11
|
+
@http = http
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# List the exchanges Melaya supports right now (the source of truth).
|
|
15
|
+
def list_exchanges
|
|
16
|
+
@http.get("/api/v1/market/list-exchanges")["exchanges"]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Best bid/ask, last price, and 24h aggregates for one symbol.
|
|
20
|
+
# @param exchange [String]
|
|
21
|
+
# @param symbol [String]
|
|
22
|
+
# @param market [String, nil] e.g. "spot", "future", "swap"
|
|
23
|
+
def ticker(exchange:, symbol:, market: nil)
|
|
24
|
+
@http.get("/api/v1/market/ticker",
|
|
25
|
+
"exchange" => exchange, "symbol" => symbol, "market" => market
|
|
26
|
+
)["ticker"]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Order book to a given depth.
|
|
30
|
+
def orderbook(exchange:, symbol:, limit: nil, market: nil)
|
|
31
|
+
@http.get("/api/v1/market/orderbook",
|
|
32
|
+
"exchange" => exchange, "symbol" => symbol, "limit" => limit, "market" => market
|
|
33
|
+
)["orderbook"]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# OHLCV candles. Each candle is [timestamp, open, high, low, close, volume].
|
|
37
|
+
def ohlcv(exchange:, symbol:, timeframe:, limit: nil, market: nil)
|
|
38
|
+
@http.get("/api/v1/market/ohlcv",
|
|
39
|
+
"exchange" => exchange, "symbol" => symbol, "timeframe" => timeframe,
|
|
40
|
+
"limit" => limit, "market" => market
|
|
41
|
+
)["candles"]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Recent public trades.
|
|
45
|
+
def trades(exchange:, symbol:, market: nil)
|
|
46
|
+
@http.get("/api/v1/market/trades",
|
|
47
|
+
"exchange" => exchange, "symbol" => symbol, "market" => market
|
|
48
|
+
)["trades"]
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Tradable markets on a venue.
|
|
52
|
+
def markets(exchange:)
|
|
53
|
+
@http.get("/api/v1/market/markets", "exchange" => exchange)["markets"]
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Listed currencies on a venue. (Not supported on every venue.)
|
|
57
|
+
def currencies(exchange:)
|
|
58
|
+
@http.get("/api/v1/market/currencies", "exchange" => exchange)["currencies"]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Operational status: ok / maintenance / degraded.
|
|
62
|
+
def status(exchange:)
|
|
63
|
+
@http.get("/api/v1/market/status", "exchange" => exchange)["status"]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Exchange server time.
|
|
67
|
+
def time(exchange:)
|
|
68
|
+
@http.get("/api/v1/market/time", "exchange" => exchange)["time"]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# ── Batch / derivatives (POST) ──────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
# Tickers for many symbols on one venue in a single call. Keyed by symbol.
|
|
74
|
+
def tickers(exchange:, symbols:, market: nil)
|
|
75
|
+
body = compact("exchange" => exchange, "symbols" => symbols, "market" => market)
|
|
76
|
+
@http.post("/api/v1/market/tickers", body)["tickers"]
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Latest funding rates for perpetuals. Keyed by symbol.
|
|
80
|
+
def funding_rates(exchange:, symbols:, market: nil)
|
|
81
|
+
body = compact("exchange" => exchange, "symbols" => symbols, "market" => market)
|
|
82
|
+
@http.post("/api/v1/market/funding-rates", body)["rates"]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Funding-rate history.
|
|
86
|
+
def funding_rate_history(exchange:, symbol:, hours: nil, market: nil)
|
|
87
|
+
body = compact("exchange" => exchange, "symbol" => symbol, "hours" => hours, "market" => market)
|
|
88
|
+
@http.post("/api/v1/market/funding-rate-history", body)["history"]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Open interest for one or more perpetuals. Keyed by symbol.
|
|
92
|
+
def open_interest(exchange:, symbols:, market: nil)
|
|
93
|
+
body = compact("exchange" => exchange, "symbols" => symbols, "market" => market)
|
|
94
|
+
@http.post("/api/v1/market/open-interest", body)["openInterest"]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Open-interest history.
|
|
98
|
+
def open_interest_history(exchange:, symbol:, hours: nil, market: nil)
|
|
99
|
+
body = compact("exchange" => exchange, "symbol" => symbol, "hours" => hours, "market" => market)
|
|
100
|
+
@http.post("/api/v1/market/open-interest-history", body)["history"]
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Instrument list + trading constraints (tick size, min notional, qty step).
|
|
104
|
+
def instruments(exchange:, market: nil)
|
|
105
|
+
body = compact("exchange" => exchange, "market" => market)
|
|
106
|
+
@http.post("/api/v1/market/instruments", body)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Cross-exchange liquidation events (historical query).
|
|
110
|
+
def liquidation_events(exchange: nil, symbol: nil, since_ms: nil, limit: nil)
|
|
111
|
+
body = compact("exchange" => exchange, "symbol" => symbol, "sinceMs" => since_ms, "limit" => limit)
|
|
112
|
+
@http.post("/api/v1/market/liquidation-events", body)["events"]
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Multi-symbol OHLCV in one call. Returns candle arrays keyed by symbol.
|
|
116
|
+
def ohlcv_multi(exchange:, symbols:, timeframe:, limit: nil, market: nil)
|
|
117
|
+
body = compact("exchange" => exchange, "symbols" => symbols, "timeframe" => timeframe,
|
|
118
|
+
"limit" => limit, "market" => market)
|
|
119
|
+
@http.post("/api/v1/market/ohlcv-multi", body)["perSymbol"]
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Trading constraints for one symbol (tick size, min notional, qty step, leverage).
|
|
123
|
+
def market_constraints(exchange:, symbol:, market: nil)
|
|
124
|
+
body = compact("exchange" => exchange, "symbol" => symbol, "market" => market)
|
|
125
|
+
@http.post("/api/v1/market/market-constraints", body)["constraints"]
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Funding-rate history for one symbol across several venues. Keyed by exchange.
|
|
129
|
+
def funding_rate_history_multi(exchanges:, symbol:, hours: nil)
|
|
130
|
+
body = compact("exchanges" => exchanges, "symbol" => symbol, "hours" => hours)
|
|
131
|
+
@http.post("/api/v1/market/funding-rate-history-multi", body)["perExchange"]
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Open-interest history for one symbol across several venues. Keyed by exchange.
|
|
135
|
+
def open_interest_history_multi(exchanges:, symbol:, hours: nil)
|
|
136
|
+
body = compact("exchanges" => exchanges, "symbol" => symbol, "hours" => hours)
|
|
137
|
+
@http.post("/api/v1/market/open-interest-history-multi", body)["perExchange"]
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Prediction-market listings for a venue (polymarket, kalshi, drift_pm, sxbet, azuro, overtime).
|
|
141
|
+
def prediction_markets(venue: "polymarket")
|
|
142
|
+
@http.post("/api/v1/market/pm-markets", { "venue" => venue })["markets"]
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Live platform catalog counts (agentic tools, subagents, by category). Public.
|
|
146
|
+
def catalog_counts
|
|
147
|
+
@http.get("/api/v1/public/catalog-counts")
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# ── restRoutes.ts market endpoints ──────────────────────────────────────────
|
|
151
|
+
|
|
152
|
+
# POST /api/v1/private/market/liquidations
|
|
153
|
+
# Get aggregated CEX liquidation data. (requireAuth)
|
|
154
|
+
# @param params [Hash] e.g. exchange, symbol, since_ms
|
|
155
|
+
def cex_liquidations(params = {})
|
|
156
|
+
@http.post("/api/v1/private/market/liquidations", params)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# GET /api/v1/market/mdd-pairs (public)
|
|
160
|
+
# Get max-drawdown pairs list (public screener data).
|
|
161
|
+
def mdd_pairs
|
|
162
|
+
@http.get("/api/v1/market/mdd-pairs")
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# GET /api/v1/private/market/onchain-yields (Forge+ tier)
|
|
166
|
+
# Get on-chain yield data.
|
|
167
|
+
# @param params [Hash]
|
|
168
|
+
def onchain_yields(params = {})
|
|
169
|
+
@http.get("/api/v1/private/market/onchain-yields", params)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# GET /api/v1/private/market/onchain-liquidity (Forge+ tier)
|
|
173
|
+
# Get on-chain liquidity data.
|
|
174
|
+
# @param params [Hash]
|
|
175
|
+
def onchain_liquidity(params = {})
|
|
176
|
+
@http.get("/api/v1/private/market/onchain-liquidity", params)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# GET /api/v1/market/banner (public)
|
|
180
|
+
# Get marketing/notification banner content.
|
|
181
|
+
def banner
|
|
182
|
+
@http.get("/api/v1/market/banner")
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# GET /api/v1/market/price-history (public)
|
|
186
|
+
# Get price history for chart display.
|
|
187
|
+
# @param params [Hash] e.g. exchange, symbol, timeframe
|
|
188
|
+
def price_history(params = {})
|
|
189
|
+
@http.get("/api/v1/market/price-history", params)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
private
|
|
193
|
+
|
|
194
|
+
def compact(hash)
|
|
195
|
+
hash.reject { |_, v| v.nil? }
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Melaya
|
|
4
|
+
# ── Domain namespace objects ─────────────────────────────────────────────────
|
|
5
|
+
#
|
|
6
|
+
# Three read-only namespace objects hang off every +Melaya::Client+ instance,
|
|
7
|
+
# grouping the flat module accessors into logical planes:
|
|
8
|
+
#
|
|
9
|
+
# melaya.trading — market data, account, sim, strategies, backtest, stream, trade
|
|
10
|
+
# melaya.agents — pipelines/runs, hitl, assistant, phone, evals, models
|
|
11
|
+
# melaya.platform — projects, credentials, connectors, billing, team, templates,
|
|
12
|
+
# overview (via pipelines), runner, auth, mfa (via auth),
|
|
13
|
+
# accounts, bugs, events
|
|
14
|
+
#
|
|
15
|
+
# Every attribute on these objects is the *same* instance that is also reachable
|
|
16
|
+
# via the flat accessor on the client, so there is no duplication of state and
|
|
17
|
+
# no double HTTP calls.
|
|
18
|
+
#
|
|
19
|
+
# @example
|
|
20
|
+
# melaya = Melaya::Client.new(api_key: ENV["MELAYA_API_KEY"])
|
|
21
|
+
#
|
|
22
|
+
# # Namespaced (primary API)
|
|
23
|
+
# melaya.trading.market.ticker(exchange: "binance", symbol: "BTC/USDT", market: "spot")
|
|
24
|
+
# melaya.agents.pipelines.list(project: "my-project")
|
|
25
|
+
# melaya.platform.projects.list
|
|
26
|
+
#
|
|
27
|
+
# # Flat aliases still work (backward-compatible)
|
|
28
|
+
# melaya.market.ticker(exchange: "binance", symbol: "BTC/USDT", market: "spot")
|
|
29
|
+
|
|
30
|
+
# Namespace grouping all trading-plane modules.
|
|
31
|
+
#
|
|
32
|
+
# Modules:
|
|
33
|
+
# - +market+ — REST market-data + reference endpoints (public + authenticated).
|
|
34
|
+
# - +account+ — Authenticated account reads: connected keys, tier limits, usage.
|
|
35
|
+
# - +sim+ — Paper trading (sim broker): virtual balance, positions, and orders.
|
|
36
|
+
# - +strategies+ — Launch, control, and inspect trading strategies (paper + live).
|
|
37
|
+
# - +backtest+ — Historical backtests + parameter sweeps on the Rust engine.
|
|
38
|
+
# - +stream+ — WebSocket streaming endpoints (public market data + private feeds).
|
|
39
|
+
# - +trade+ — Live trading — credentialed order placement on a connected exchange.
|
|
40
|
+
TradingNamespace = Struct.new(
|
|
41
|
+
:market,
|
|
42
|
+
:account,
|
|
43
|
+
:sim,
|
|
44
|
+
:strategies,
|
|
45
|
+
:backtest,
|
|
46
|
+
:stream,
|
|
47
|
+
:trade,
|
|
48
|
+
keyword_init: true
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# Namespace grouping all agent-plane modules.
|
|
52
|
+
#
|
|
53
|
+
# Modules:
|
|
54
|
+
# - +pipelines+ — Pipeline runs, traces, schedules, and overview dashboard.
|
|
55
|
+
# (also aliased as +runs+ for ergonomics)
|
|
56
|
+
# - +hitl+ — Human-in-the-loop approval queue: list pending, approve, reject.
|
|
57
|
+
# - +assistant+ — Assistant onboarding profile (get + set).
|
|
58
|
+
# - +phone+ — Phone device control: pair, list, screen-tree, apps.
|
|
59
|
+
# - +evals+ — Agent evaluation runs and benchmarks.
|
|
60
|
+
# - +models+ — AI model list (reached via credentials#list_models; this is
|
|
61
|
+
# the CredentialsAPI instance filtered by convention — call
|
|
62
|
+
# +models.list_models(provider: "anthropic")+ etc.)
|
|
63
|
+
AgentsNamespace = Struct.new(
|
|
64
|
+
:pipelines,
|
|
65
|
+
:hitl,
|
|
66
|
+
:assistant,
|
|
67
|
+
:phone,
|
|
68
|
+
:evals,
|
|
69
|
+
:models,
|
|
70
|
+
keyword_init: true
|
|
71
|
+
) do
|
|
72
|
+
# +runs+ is an ergonomic alias for +pipelines+ (agents call them "runs").
|
|
73
|
+
def runs
|
|
74
|
+
pipelines
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Namespace grouping all platform-plane modules.
|
|
79
|
+
#
|
|
80
|
+
# Modules:
|
|
81
|
+
# - +projects+ — Create and list agent projects.
|
|
82
|
+
# - +credentials+ — User-scoped credential storage (services, OAuth, env handles, models).
|
|
83
|
+
# - +connectors+ — Project-scoped connector credentials.
|
|
84
|
+
# - +billing+ — Subscription, Stripe checkout/portal, pricing plans, credit balances.
|
|
85
|
+
# - +team+ — Project team management: members, roles, invite links.
|
|
86
|
+
# - +templates+ — Pipeline templates: create, share, assign, and manage visibility.
|
|
87
|
+
# - +overview+ — Pipeline overview dashboard (same object as +agents.pipelines+,
|
|
88
|
+
# exposed here for discoverability on the platform plane).
|
|
89
|
+
# - +runner+ — Runner tokens: mint, list, revoke +mel_run_+ tokens.
|
|
90
|
+
# - +auth+ — Login, MFA, registration, password management, session tokens.
|
|
91
|
+
# - +mfa+ — Alias for +auth+ (MFA operations live on the same AuthAPI object).
|
|
92
|
+
# - +accounts+ — Account management: GDPR export, CEX key removal, profile updates.
|
|
93
|
+
# - +bugs+ — Bug reports: submit, track, and comment.
|
|
94
|
+
# - +events+ — Platform real-time events over Socket.IO.
|
|
95
|
+
PlatformNamespace = Struct.new(
|
|
96
|
+
:projects,
|
|
97
|
+
:credentials,
|
|
98
|
+
:connectors,
|
|
99
|
+
:billing,
|
|
100
|
+
:team,
|
|
101
|
+
:templates,
|
|
102
|
+
:overview,
|
|
103
|
+
:runner,
|
|
104
|
+
:auth,
|
|
105
|
+
:accounts,
|
|
106
|
+
:bugs,
|
|
107
|
+
:events,
|
|
108
|
+
keyword_init: true
|
|
109
|
+
) do
|
|
110
|
+
# +mfa+ is an ergonomic alias for +auth+ (MFA methods live on AuthAPI).
|
|
111
|
+
def mfa
|
|
112
|
+
auth
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
data/lib/melaya/phone.rb
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Melaya
|
|
4
|
+
# Phone API — pair and control Android devices connected to the Melaya runner.
|
|
5
|
+
#
|
|
6
|
+
# Agents use these endpoints to drive a paired phone (tap, type, read
|
|
7
|
+
# screen state, launch apps) via the Melaya APK.
|
|
8
|
+
#
|
|
9
|
+
# Maps to /api/v1/private/phone/*.
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# result = melaya.phone.pair
|
|
13
|
+
# puts "Pairing code: #{result["code"]}"
|
|
14
|
+
# # User enters code in the Melaya APK on the phone
|
|
15
|
+
#
|
|
16
|
+
# devices = melaya.phone.list_devices
|
|
17
|
+
# tree = melaya.phone.screen_tree
|
|
18
|
+
class PhoneAPI
|
|
19
|
+
def initialize(http)
|
|
20
|
+
@http = http
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# POST /api/v1/private/phone/pair
|
|
24
|
+
# Start phone device pairing — generates a pairing code for the Melaya APK.
|
|
25
|
+
def pair
|
|
26
|
+
@http.post("/api/v1/private/phone/pair")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# GET /api/v1/private/phone/devices
|
|
30
|
+
# List all paired phone devices for the authenticated user.
|
|
31
|
+
def list_devices
|
|
32
|
+
@http.get("/api/v1/private/phone/devices").fetch("devices", [])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# DELETE /api/v1/private/phone/devices/:deviceId
|
|
36
|
+
# Revoke a paired phone device by ID.
|
|
37
|
+
# @param device_id [String]
|
|
38
|
+
def revoke_device(device_id)
|
|
39
|
+
@http.delete("/api/v1/private/phone/devices/#{enc(device_id)}")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# GET /api/v1/private/phone/screen-tree
|
|
43
|
+
# Get the current accessibility tree from the paired phone's screen.
|
|
44
|
+
def screen_tree
|
|
45
|
+
@http.get("/api/v1/private/phone/screen-tree")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# GET /api/v1/private/phone/apps
|
|
49
|
+
# List installed apps on the paired phone.
|
|
50
|
+
def list_apps
|
|
51
|
+
@http.get("/api/v1/private/phone/apps").dig("result", "apps") || []
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# PUT /api/v1/private/phone/apps/allowed
|
|
55
|
+
# Set the allowlist of apps that agents are permitted to interact with.
|
|
56
|
+
# @param package_names [Array<String>]
|
|
57
|
+
def set_allowed_apps(package_names)
|
|
58
|
+
apps = package_names.map { |package| { "package" => package } }
|
|
59
|
+
@http.put("/api/v1/private/phone/apps/allowed", "apps" => apps)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# POST /api/v1/private/phone/active-run
|
|
63
|
+
# Register the currently active pipeline run on the phone (used by agents).
|
|
64
|
+
# @param run_id [String]
|
|
65
|
+
def register_active_run(run_id)
|
|
66
|
+
@http.post("/api/v1/private/phone/active-run", "runId" => run_id)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def enc(s)
|
|
72
|
+
URI.encode_www_form_component(s.to_s)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|