melaya 0.1.0 → 0.1.4
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/README.md +163 -163
- data/lib/melaya/account.rb +30 -30
- data/lib/melaya/backtest.rb +101 -101
- data/lib/melaya/errors.rb +15 -15
- data/lib/melaya/http_client.rb +97 -97
- data/lib/melaya/market.rb +156 -156
- data/lib/melaya/sim.rb +110 -110
- data/lib/melaya/strategies.rb +155 -155
- data/lib/melaya/stream.rb +336 -336
- data/lib/melaya/trade.rb +168 -168
- data/lib/melaya/version.rb +5 -5
- data/lib/melaya.rb +79 -79
- data/melaya.gemspec +23 -23
- metadata +6 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d1dac0d815408d37da57f6288caec96ebd16db731405ae977759211cba627ac0
|
|
4
|
+
data.tar.gz: adc032f6a8446ae788f0c4288b0b14d755288c92c50cd10cd5400dd8874dad74
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6088333696eec5b43ec4d917477b4afd628f136507d2b492f4d50d540836645aaf76e7bbc2427fa19bca2ed05a5444a148d9b189a8c4a04b4a257d8536a9a777
|
|
7
|
+
data.tar.gz: 1c2e6cb42d6a23940c49a1b0fbd7a07160416923584541106b2a11935a9ca67bf16f6b18561d8f1099a4be7b3b70e3e93a7eb0f31ee4b5e298325635ad74ab3e
|
data/README.md
CHANGED
|
@@ -1,163 +1,163 @@
|
|
|
1
|
-
# melaya
|
|
2
|
-
|
|
3
|
-
Official Ruby SDK for the **[Melaya](https://melaya.org)** trading platform — normalized market data, paper + live trading, backtesting, and an AI agentic trading crew across **70+ venues**, powered by an in-house Rust engine.
|
|
4
|
-
|
|
5
|
-
- Zero runtime gem dependencies (stdlib `net/http`, `openssl`, `json` only).
|
|
6
|
-
- Pure Ruby WebSocket client (RFC 6455) — no external gem required for streaming.
|
|
7
|
-
- Full market data, strategies, sim trading, backtesting, and streaming from one client.
|
|
8
|
-
|
|
9
|
-
## Install
|
|
10
|
-
|
|
11
|
-
Add to your `Gemfile`:
|
|
12
|
-
|
|
13
|
-
```ruby
|
|
14
|
-
gem "melaya", path: "path/to/sdk-ruby" # local checkout
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Or once published to RubyGems:
|
|
18
|
-
|
|
19
|
-
```bash
|
|
20
|
-
gem install melaya
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Quick start
|
|
24
|
-
|
|
25
|
-
```ruby
|
|
26
|
-
require "melaya"
|
|
27
|
-
|
|
28
|
-
melaya = Melaya::Client.new(api_key: ENV["MELAYA_API_KEY"]) # keys are prefixed mk_
|
|
29
|
-
|
|
30
|
-
# REST — normalized ticker from any of 70+ venues
|
|
31
|
-
t = melaya.market.ticker(exchange: "binance", symbol: "BTC/USDT", market: "spot")
|
|
32
|
-
puts t["last"], t["bid"], t["ask"]
|
|
33
|
-
|
|
34
|
-
# Order book
|
|
35
|
-
ob = melaya.market.orderbook(exchange: "bybit", symbol: "BTC/USDT", market: "spot", limit: 20)
|
|
36
|
-
|
|
37
|
-
# Candles
|
|
38
|
-
candles = melaya.market.ohlcv(exchange: "okx", symbol: "ETH/USDT", timeframe: "1h", limit: 200)
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## Streaming
|
|
42
|
-
|
|
43
|
-
```ruby
|
|
44
|
-
# Live ticker (block form — closes when block returns)
|
|
45
|
-
melaya.stream.ticker(exchange: "binance", symbol: "BTC/USDT", market: "spot") do |frame|
|
|
46
|
-
puts frame["last"]
|
|
47
|
-
break # close after first frame
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# Liquidation firehose
|
|
51
|
-
melaya.stream.liquidations(exchange: "binance") do |ev|
|
|
52
|
-
puts ev["side"], ev["notional"]
|
|
53
|
-
break
|
|
54
|
-
end
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
## Trading
|
|
58
|
-
|
|
59
|
-
```ruby
|
|
60
|
-
# Account: connected exchange keys and usage
|
|
61
|
-
keys = melaya.account.keys # [{ "apiKeyId" => "BINANCEUSDM_0", "exchange" => "binanceusdm", ... }]
|
|
62
|
-
usage = melaya.account.usage
|
|
63
|
-
|
|
64
|
-
# Strategies — launch immediately. Paper (dry_run: true) needs no exchange key.
|
|
65
|
-
# SDK-launchable strategies are `custom` Rhai definitions.
|
|
66
|
-
result = melaya.strategies.create(
|
|
67
|
-
name: "my-bot",
|
|
68
|
-
strategy_type: "custom",
|
|
69
|
-
exchange: "binanceusdm",
|
|
70
|
-
symbol: "BTC/USDT:USDT",
|
|
71
|
-
market: "FUTURES",
|
|
72
|
-
dry_run: true,
|
|
73
|
-
params: {
|
|
74
|
-
"language" => "rhai",
|
|
75
|
-
"definition" => 'fn evaluate() { emit_long(param("qty")); }',
|
|
76
|
-
"qty" => 0.001,
|
|
77
|
-
}
|
|
78
|
-
)
|
|
79
|
-
sid = result["strategyId"]
|
|
80
|
-
melaya.strategies.pause(sid)
|
|
81
|
-
melaya.strategies.resume(sid)
|
|
82
|
-
trades = melaya.strategies.trades(sid)
|
|
83
|
-
|
|
84
|
-
# Paper trading (sim broker) — synthetic fills, no venue state
|
|
85
|
-
bal = melaya.sim.balance(strategy_id: sid)
|
|
86
|
-
fill = melaya.sim.create_order(
|
|
87
|
-
strategy_id: sid,
|
|
88
|
-
exchange: "binanceusdm",
|
|
89
|
-
symbol: "BTC/USDT:USDT",
|
|
90
|
-
side: "buy",
|
|
91
|
-
type: "market",
|
|
92
|
-
amount: 0.001,
|
|
93
|
-
market: "FUTURES"
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
# Backtest on the Rust engine
|
|
97
|
-
r = melaya.backtest.start(
|
|
98
|
-
"strategyType" => "custom",
|
|
99
|
-
"exchange" => "binance",
|
|
100
|
-
"symbol" => "BTC/USDT",
|
|
101
|
-
"timeframe" => "1h",
|
|
102
|
-
"since_ms" => (Time.now.to_i - 90 * 86400) * 1000,
|
|
103
|
-
"until_ms" => Time.now.to_i * 1000,
|
|
104
|
-
"language" => "rhai",
|
|
105
|
-
"definition" => 'fn evaluate() { emit_long(param("qty")); }',
|
|
106
|
-
"params" => { "qty" => 0.001 }
|
|
107
|
-
)
|
|
108
|
-
job_id = r["job_id"]
|
|
109
|
-
loop do
|
|
110
|
-
j = melaya.backtest.job(job_id)
|
|
111
|
-
break if %w[done error].include?(j["status"])
|
|
112
|
-
sleep 2
|
|
113
|
-
end
|
|
114
|
-
result = melaya.backtest.results(job_id)
|
|
115
|
-
|
|
116
|
-
# Private streaming (ticket minted automatically)
|
|
117
|
-
melaya.stream.strategies do |ev|
|
|
118
|
-
puts ev["type"], ev["strategyId"]
|
|
119
|
-
break
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
# Always clean up
|
|
123
|
-
melaya.strategies.stop(sid)
|
|
124
|
-
melaya.strategies.delete(sid)
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
## Authentication
|
|
128
|
-
|
|
129
|
-
Create an API key in the dashboard (**melaya.org → Settings → API Keys**). Keys are prefixed `mk_`; the SDK sends it automatically on every REST call and WebSocket connection.
|
|
130
|
-
|
|
131
|
-
Public market-data and account/strategy reads work with the `mk_` key alone. **Live** order placement and live strategy launches additionally require a connected exchange key — connect one in **Settings → Connectors**, then reference it by `apiKeyId`. Paper trading and backtesting never touch a venue and need no exchange credentials.
|
|
132
|
-
|
|
133
|
-
## TLS verification
|
|
134
|
-
|
|
135
|
-
The SDK verifies TLS certificates by default. To disable on a dev box with TLS interception:
|
|
136
|
-
|
|
137
|
-
```bash
|
|
138
|
-
MELAYA_INSECURE_TLS=1 ruby your_script.rb
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
Or pass `verify_ssl: false` to the constructor. **Never disable TLS in production.**
|
|
142
|
-
|
|
143
|
-
## API surface
|
|
144
|
-
|
|
145
|
-
| Area | Methods |
|
|
146
|
-
|---|---|
|
|
147
|
-
| Reference | `market.list_exchanges`, `catalog_counts` |
|
|
148
|
-
| Market data | `market.ticker`, `orderbook`, `ohlcv`, `ohlcv_multi`, `trades`, `markets`, `currencies`, `market_constraints`, `status`, `time` |
|
|
149
|
-
| Batch / derivatives | `market.tickers`, `funding_rates`, `funding_rate_history`, `funding_rate_history_multi`, `open_interest`, `open_interest_history`, `open_interest_history_multi`, `instruments`, `liquidation_events` |
|
|
150
|
-
| Prediction markets | `market.prediction_markets` (polymarket, kalshi, drift_pm, sxbet, azuro, overtime) |
|
|
151
|
-
| Account | `account.keys`, `usage`, `api_key_status` |
|
|
152
|
-
| Strategies | `strategies.create`, `list`, `get`, `pause`, `resume`, `stop`, `delete`, `update_params`, `status`, `performance`, `executions`, `trades`, `logs` |
|
|
153
|
-
| AI optimizer | `strategies.ai_opt_start`, `ai_opt_status`, `ai_opt_approve`, `ai_opt_stop`, `ai_opt_runs` |
|
|
154
|
-
| Paper trading | `sim.balance`, `positions`, `open_orders`, `my_trades`, `create_order`, `cancel_order`, `list_accounts` |
|
|
155
|
-
| Backtesting | `backtest.start`, `job`, `results`, `trades`, `sweep`, `list`, `favorites`, `funding_range`, `cancel`, `delete`, `delete_all` |
|
|
156
|
-
| Public streaming | `stream.ticker`, `orderbook`, `ohlcv`, `trades`, `liquidations` |
|
|
157
|
-
| Private streaming | `stream.strategies`, `stream.private` |
|
|
158
|
-
|
|
159
|
-
Full docs: **[melaya.org/docs](https://melaya.org/docs)**.
|
|
160
|
-
|
|
161
|
-
## License
|
|
162
|
-
|
|
163
|
-
[Apache-2.0](../../LICENSE)
|
|
1
|
+
# melaya
|
|
2
|
+
|
|
3
|
+
Official Ruby SDK for the **[Melaya](https://melaya.org)** trading platform — normalized market data, paper + live trading, backtesting, and an AI agentic trading crew across **70+ venues**, powered by an in-house Rust engine.
|
|
4
|
+
|
|
5
|
+
- Zero runtime gem dependencies (stdlib `net/http`, `openssl`, `json` only).
|
|
6
|
+
- Pure Ruby WebSocket client (RFC 6455) — no external gem required for streaming.
|
|
7
|
+
- Full market data, strategies, sim trading, backtesting, and streaming from one client.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
Add to your `Gemfile`:
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
gem "melaya", path: "path/to/sdk-ruby" # local checkout
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Or once published to RubyGems:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
gem install melaya
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
require "melaya"
|
|
27
|
+
|
|
28
|
+
melaya = Melaya::Client.new(api_key: ENV["MELAYA_API_KEY"]) # keys are prefixed mk_
|
|
29
|
+
|
|
30
|
+
# REST — normalized ticker from any of 70+ venues
|
|
31
|
+
t = melaya.market.ticker(exchange: "binance", symbol: "BTC/USDT", market: "spot")
|
|
32
|
+
puts t["last"], t["bid"], t["ask"]
|
|
33
|
+
|
|
34
|
+
# Order book
|
|
35
|
+
ob = melaya.market.orderbook(exchange: "bybit", symbol: "BTC/USDT", market: "spot", limit: 20)
|
|
36
|
+
|
|
37
|
+
# Candles
|
|
38
|
+
candles = melaya.market.ohlcv(exchange: "okx", symbol: "ETH/USDT", timeframe: "1h", limit: 200)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Streaming
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
# Live ticker (block form — closes when block returns)
|
|
45
|
+
melaya.stream.ticker(exchange: "binance", symbol: "BTC/USDT", market: "spot") do |frame|
|
|
46
|
+
puts frame["last"]
|
|
47
|
+
break # close after first frame
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Liquidation firehose
|
|
51
|
+
melaya.stream.liquidations(exchange: "binance") do |ev|
|
|
52
|
+
puts ev["side"], ev["notional"]
|
|
53
|
+
break
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Trading
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
# Account: connected exchange keys and usage
|
|
61
|
+
keys = melaya.account.keys # [{ "apiKeyId" => "BINANCEUSDM_0", "exchange" => "binanceusdm", ... }]
|
|
62
|
+
usage = melaya.account.usage
|
|
63
|
+
|
|
64
|
+
# Strategies — launch immediately. Paper (dry_run: true) needs no exchange key.
|
|
65
|
+
# SDK-launchable strategies are `custom` Rhai definitions.
|
|
66
|
+
result = melaya.strategies.create(
|
|
67
|
+
name: "my-bot",
|
|
68
|
+
strategy_type: "custom",
|
|
69
|
+
exchange: "binanceusdm",
|
|
70
|
+
symbol: "BTC/USDT:USDT",
|
|
71
|
+
market: "FUTURES",
|
|
72
|
+
dry_run: true,
|
|
73
|
+
params: {
|
|
74
|
+
"language" => "rhai",
|
|
75
|
+
"definition" => 'fn evaluate() { emit_long(param("qty")); }',
|
|
76
|
+
"qty" => 0.001,
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
sid = result["strategyId"]
|
|
80
|
+
melaya.strategies.pause(sid)
|
|
81
|
+
melaya.strategies.resume(sid)
|
|
82
|
+
trades = melaya.strategies.trades(sid)
|
|
83
|
+
|
|
84
|
+
# Paper trading (sim broker) — synthetic fills, no venue state
|
|
85
|
+
bal = melaya.sim.balance(strategy_id: sid)
|
|
86
|
+
fill = melaya.sim.create_order(
|
|
87
|
+
strategy_id: sid,
|
|
88
|
+
exchange: "binanceusdm",
|
|
89
|
+
symbol: "BTC/USDT:USDT",
|
|
90
|
+
side: "buy",
|
|
91
|
+
type: "market",
|
|
92
|
+
amount: 0.001,
|
|
93
|
+
market: "FUTURES"
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Backtest on the Rust engine
|
|
97
|
+
r = melaya.backtest.start(
|
|
98
|
+
"strategyType" => "custom",
|
|
99
|
+
"exchange" => "binance",
|
|
100
|
+
"symbol" => "BTC/USDT",
|
|
101
|
+
"timeframe" => "1h",
|
|
102
|
+
"since_ms" => (Time.now.to_i - 90 * 86400) * 1000,
|
|
103
|
+
"until_ms" => Time.now.to_i * 1000,
|
|
104
|
+
"language" => "rhai",
|
|
105
|
+
"definition" => 'fn evaluate() { emit_long(param("qty")); }',
|
|
106
|
+
"params" => { "qty" => 0.001 }
|
|
107
|
+
)
|
|
108
|
+
job_id = r["job_id"]
|
|
109
|
+
loop do
|
|
110
|
+
j = melaya.backtest.job(job_id)
|
|
111
|
+
break if %w[done error].include?(j["status"])
|
|
112
|
+
sleep 2
|
|
113
|
+
end
|
|
114
|
+
result = melaya.backtest.results(job_id)
|
|
115
|
+
|
|
116
|
+
# Private streaming (ticket minted automatically)
|
|
117
|
+
melaya.stream.strategies do |ev|
|
|
118
|
+
puts ev["type"], ev["strategyId"]
|
|
119
|
+
break
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Always clean up
|
|
123
|
+
melaya.strategies.stop(sid)
|
|
124
|
+
melaya.strategies.delete(sid)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Authentication
|
|
128
|
+
|
|
129
|
+
Create an API key in the dashboard (**melaya.org → Settings → API Keys**). Keys are prefixed `mk_`; the SDK sends it automatically on every REST call and WebSocket connection.
|
|
130
|
+
|
|
131
|
+
Public market-data and account/strategy reads work with the `mk_` key alone. **Live** order placement and live strategy launches additionally require a connected exchange key — connect one in **Settings → Connectors**, then reference it by `apiKeyId`. Paper trading and backtesting never touch a venue and need no exchange credentials.
|
|
132
|
+
|
|
133
|
+
## TLS verification
|
|
134
|
+
|
|
135
|
+
The SDK verifies TLS certificates by default. To disable on a dev box with TLS interception:
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
MELAYA_INSECURE_TLS=1 ruby your_script.rb
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Or pass `verify_ssl: false` to the constructor. **Never disable TLS in production.**
|
|
142
|
+
|
|
143
|
+
## API surface
|
|
144
|
+
|
|
145
|
+
| Area | Methods |
|
|
146
|
+
|---|---|
|
|
147
|
+
| Reference | `market.list_exchanges`, `catalog_counts` |
|
|
148
|
+
| Market data | `market.ticker`, `orderbook`, `ohlcv`, `ohlcv_multi`, `trades`, `markets`, `currencies`, `market_constraints`, `status`, `time` |
|
|
149
|
+
| Batch / derivatives | `market.tickers`, `funding_rates`, `funding_rate_history`, `funding_rate_history_multi`, `open_interest`, `open_interest_history`, `open_interest_history_multi`, `instruments`, `liquidation_events` |
|
|
150
|
+
| Prediction markets | `market.prediction_markets` (polymarket, kalshi, drift_pm, sxbet, azuro, overtime) |
|
|
151
|
+
| Account | `account.keys`, `usage`, `api_key_status` |
|
|
152
|
+
| Strategies | `strategies.create`, `list`, `get`, `pause`, `resume`, `stop`, `delete`, `update_params`, `status`, `performance`, `executions`, `trades`, `logs` |
|
|
153
|
+
| AI optimizer | `strategies.ai_opt_start`, `ai_opt_status`, `ai_opt_approve`, `ai_opt_stop`, `ai_opt_runs` |
|
|
154
|
+
| Paper trading | `sim.balance`, `positions`, `open_orders`, `my_trades`, `create_order`, `cancel_order`, `list_accounts` |
|
|
155
|
+
| Backtesting | `backtest.start`, `job`, `results`, `trades`, `sweep`, `list`, `favorites`, `funding_range`, `cancel`, `delete`, `delete_all` |
|
|
156
|
+
| Public streaming | `stream.ticker`, `orderbook`, `ohlcv`, `trades`, `liquidations` |
|
|
157
|
+
| Private streaming | `stream.strategies`, `stream.private` |
|
|
158
|
+
|
|
159
|
+
Full docs: **[melaya.org/docs](https://melaya.org/docs)**.
|
|
160
|
+
|
|
161
|
+
## License
|
|
162
|
+
|
|
163
|
+
[Apache-2.0](../../LICENSE)
|
data/lib/melaya/account.rb
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Melaya
|
|
4
|
-
# Account API — authenticated reads about your Melaya account.
|
|
5
|
-
#
|
|
6
|
-
# Connected-exchange key references (masked), tier limits, and live usage
|
|
7
|
-
# counters. Requires an mk_ key on the private plane.
|
|
8
|
-
class AccountAPI
|
|
9
|
-
def initialize(http)
|
|
10
|
-
@http = http
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
# The exchange API keys connected to your account. +api_key+ is masked
|
|
14
|
-
# (display-only); use +api_key_id+ (e.g. BINANCEUSDM_0) when launching
|
|
15
|
-
# strategies or minting a private stream ticket.
|
|
16
|
-
def keys
|
|
17
|
-
@http.get("/api/v1/private/keys")["keys"]
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# Tier, plan limits, and live usage counters (mirrors the dashboard's usage page).
|
|
21
|
-
def usage
|
|
22
|
-
@http.get("/api/v1/private/usage")
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# Status of your platform API key (tier, max concurrent connections).
|
|
26
|
-
def api_key_status
|
|
27
|
-
@http.get("/api/v1/private/api-key")
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Melaya
|
|
4
|
+
# Account API — authenticated reads about your Melaya account.
|
|
5
|
+
#
|
|
6
|
+
# Connected-exchange key references (masked), tier limits, and live usage
|
|
7
|
+
# counters. Requires an mk_ key on the private plane.
|
|
8
|
+
class AccountAPI
|
|
9
|
+
def initialize(http)
|
|
10
|
+
@http = http
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# The exchange API keys connected to your account. +api_key+ is masked
|
|
14
|
+
# (display-only); use +api_key_id+ (e.g. BINANCEUSDM_0) when launching
|
|
15
|
+
# strategies or minting a private stream ticket.
|
|
16
|
+
def keys
|
|
17
|
+
@http.get("/api/v1/private/keys")["keys"]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Tier, plan limits, and live usage counters (mirrors the dashboard's usage page).
|
|
21
|
+
def usage
|
|
22
|
+
@http.get("/api/v1/private/usage")
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Status of your platform API key (tier, max concurrent connections).
|
|
26
|
+
def api_key_status
|
|
27
|
+
@http.get("/api/v1/private/api-key")
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
data/lib/melaya/backtest.rb
CHANGED
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Melaya
|
|
4
|
-
# Backtest API — run strategies against historical data on the Rust engine.
|
|
5
|
-
#
|
|
6
|
-
# Start a single run or a parameter sweep (grid / random), poll the job,
|
|
7
|
-
# then pull metrics, the equity curve, and the trade list. All backtests run
|
|
8
|
-
# natively on Melaya's in-house engine — no per-venue SDK in the loop.
|
|
9
|
-
#
|
|
10
|
-
# Maps to https://api.melaya.org/api/v1/private/backtest/* on the private plane.
|
|
11
|
-
class BacktestAPI
|
|
12
|
-
def initialize(http)
|
|
13
|
-
@http = http
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# Start a backtest. +mode+ defaults to a single run; pass "grid_sweep" /
|
|
17
|
-
# "random_sweep" with +param_ranges+ to fan out a parameter search.
|
|
18
|
-
# Returns a hash with "job_id" (and optionally "count").
|
|
19
|
-
#
|
|
20
|
-
# @param body [Hash] see BacktestStart type in the reference SDKs
|
|
21
|
-
def start(body)
|
|
22
|
-
@http.post("/api/v1/private/backtest/start", body)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# Job status + progress (+status+, +progress_pct+, ...).
|
|
26
|
-
# @param job_id [String]
|
|
27
|
-
def job(job_id)
|
|
28
|
-
@http.get("/api/v1/private/backtest/jobs/#{job_id}")
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# Metrics, equity curve, and OHLCV for a completed job.
|
|
32
|
-
# @param job_id [String]
|
|
33
|
-
def results(job_id)
|
|
34
|
-
@http.get("/api/v1/private/backtest/results/#{job_id}")["result"]
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# The trade list for a completed job (default 500, max 5000 per call).
|
|
38
|
-
# @param job_id [String]
|
|
39
|
-
# @param limit [Integer, nil]
|
|
40
|
-
# @param offset [Integer, nil]
|
|
41
|
-
def trades(job_id, limit: nil, offset: nil)
|
|
42
|
-
@http.get("/api/v1/private/backtest/trades/#{job_id}",
|
|
43
|
-
"limit" => limit, "offset" => offset
|
|
44
|
-
)["trades"]
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# Ranked children of a sweep parent (default objective: sharpe DESC).
|
|
48
|
-
# @param parent_id [String]
|
|
49
|
-
# @param objective [String, nil]
|
|
50
|
-
# @param limit [Integer, nil]
|
|
51
|
-
def sweep(parent_id, objective: nil, limit: nil)
|
|
52
|
-
@http.get("/api/v1/private/backtest/sweep/#{parent_id}",
|
|
53
|
-
"objective" => objective, "limit" => limit
|
|
54
|
-
)
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Your backtest jobs, newest first.
|
|
58
|
-
# @param limit [Integer, nil]
|
|
59
|
-
# @param offset [Integer, nil]
|
|
60
|
-
def list(limit: nil, offset: nil)
|
|
61
|
-
@http.get("/api/v1/private/backtest",
|
|
62
|
-
"limit" => limit, "offset" => offset
|
|
63
|
-
).dig("data", "jobs")
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Your favorited backtest jobs (Forge tier and above).
|
|
67
|
-
# @param limit [Integer, nil]
|
|
68
|
-
# @param offset [Integer, nil]
|
|
69
|
-
def favorites(limit: nil, offset: nil)
|
|
70
|
-
@http.get("/api/v1/private/backtest/favorites",
|
|
71
|
-
"limit" => limit, "offset" => offset
|
|
72
|
-
).dig("data", "jobs")
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# Earliest funding-rate timestamp available for an exchange+symbol (ms, or nil).
|
|
76
|
-
# @param exchange [String]
|
|
77
|
-
# @param symbol [String]
|
|
78
|
-
def funding_range(exchange:, symbol:)
|
|
79
|
-
@http.get("/api/v1/private/backtest/funding-range",
|
|
80
|
-
"exchange" => exchange, "symbol" => symbol
|
|
81
|
-
)["earliest_ms"]
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# Cancel an in-flight job.
|
|
85
|
-
# @param job_id [String]
|
|
86
|
-
def cancel(job_id)
|
|
87
|
-
@http.post("/api/v1/private/backtest/#{job_id}/cancel")
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
# Soft-delete a single job.
|
|
91
|
-
# @param job_id [String]
|
|
92
|
-
def delete(job_id)
|
|
93
|
-
@http.delete("/api/v1/private/backtest/#{job_id}")
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# Soft-delete every non-favorited job. Returns a hash with "deleted" count.
|
|
97
|
-
def delete_all
|
|
98
|
-
@http.delete("/api/v1/private/backtest")
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Melaya
|
|
4
|
+
# Backtest API — run strategies against historical data on the Rust engine.
|
|
5
|
+
#
|
|
6
|
+
# Start a single run or a parameter sweep (grid / random), poll the job,
|
|
7
|
+
# then pull metrics, the equity curve, and the trade list. All backtests run
|
|
8
|
+
# natively on Melaya's in-house engine — no per-venue SDK in the loop.
|
|
9
|
+
#
|
|
10
|
+
# Maps to https://api.melaya.org/api/v1/private/backtest/* on the private plane.
|
|
11
|
+
class BacktestAPI
|
|
12
|
+
def initialize(http)
|
|
13
|
+
@http = http
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Start a backtest. +mode+ defaults to a single run; pass "grid_sweep" /
|
|
17
|
+
# "random_sweep" with +param_ranges+ to fan out a parameter search.
|
|
18
|
+
# Returns a hash with "job_id" (and optionally "count").
|
|
19
|
+
#
|
|
20
|
+
# @param body [Hash] see BacktestStart type in the reference SDKs
|
|
21
|
+
def start(body)
|
|
22
|
+
@http.post("/api/v1/private/backtest/start", body)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Job status + progress (+status+, +progress_pct+, ...).
|
|
26
|
+
# @param job_id [String]
|
|
27
|
+
def job(job_id)
|
|
28
|
+
@http.get("/api/v1/private/backtest/jobs/#{job_id}")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Metrics, equity curve, and OHLCV for a completed job.
|
|
32
|
+
# @param job_id [String]
|
|
33
|
+
def results(job_id)
|
|
34
|
+
@http.get("/api/v1/private/backtest/results/#{job_id}")["result"]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# The trade list for a completed job (default 500, max 5000 per call).
|
|
38
|
+
# @param job_id [String]
|
|
39
|
+
# @param limit [Integer, nil]
|
|
40
|
+
# @param offset [Integer, nil]
|
|
41
|
+
def trades(job_id, limit: nil, offset: nil)
|
|
42
|
+
@http.get("/api/v1/private/backtest/trades/#{job_id}",
|
|
43
|
+
"limit" => limit, "offset" => offset
|
|
44
|
+
)["trades"]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Ranked children of a sweep parent (default objective: sharpe DESC).
|
|
48
|
+
# @param parent_id [String]
|
|
49
|
+
# @param objective [String, nil]
|
|
50
|
+
# @param limit [Integer, nil]
|
|
51
|
+
def sweep(parent_id, objective: nil, limit: nil)
|
|
52
|
+
@http.get("/api/v1/private/backtest/sweep/#{parent_id}",
|
|
53
|
+
"objective" => objective, "limit" => limit
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Your backtest jobs, newest first.
|
|
58
|
+
# @param limit [Integer, nil]
|
|
59
|
+
# @param offset [Integer, nil]
|
|
60
|
+
def list(limit: nil, offset: nil)
|
|
61
|
+
@http.get("/api/v1/private/backtest",
|
|
62
|
+
"limit" => limit, "offset" => offset
|
|
63
|
+
).dig("data", "jobs")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Your favorited backtest jobs (Forge tier and above).
|
|
67
|
+
# @param limit [Integer, nil]
|
|
68
|
+
# @param offset [Integer, nil]
|
|
69
|
+
def favorites(limit: nil, offset: nil)
|
|
70
|
+
@http.get("/api/v1/private/backtest/favorites",
|
|
71
|
+
"limit" => limit, "offset" => offset
|
|
72
|
+
).dig("data", "jobs")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Earliest funding-rate timestamp available for an exchange+symbol (ms, or nil).
|
|
76
|
+
# @param exchange [String]
|
|
77
|
+
# @param symbol [String]
|
|
78
|
+
def funding_range(exchange:, symbol:)
|
|
79
|
+
@http.get("/api/v1/private/backtest/funding-range",
|
|
80
|
+
"exchange" => exchange, "symbol" => symbol
|
|
81
|
+
)["earliest_ms"]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Cancel an in-flight job.
|
|
85
|
+
# @param job_id [String]
|
|
86
|
+
def cancel(job_id)
|
|
87
|
+
@http.post("/api/v1/private/backtest/#{job_id}/cancel")
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Soft-delete a single job.
|
|
91
|
+
# @param job_id [String]
|
|
92
|
+
def delete(job_id)
|
|
93
|
+
@http.delete("/api/v1/private/backtest/#{job_id}")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Soft-delete every non-favorited job. Returns a hash with "deleted" count.
|
|
97
|
+
def delete_all
|
|
98
|
+
@http.delete("/api/v1/private/backtest")
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
data/lib/melaya/errors.rb
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Melaya
|
|
4
|
-
# Raised for non-2xx REST responses or ok:false envelopes.
|
|
5
|
-
class MelayaError < StandardError
|
|
6
|
-
attr_reader :status, :code, :body
|
|
7
|
-
|
|
8
|
-
def initialize(message, status: 0, code: nil, body: nil)
|
|
9
|
-
super(message)
|
|
10
|
-
@status = status
|
|
11
|
-
@code = code
|
|
12
|
-
@body = body
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Melaya
|
|
4
|
+
# Raised for non-2xx REST responses or ok:false envelopes.
|
|
5
|
+
class MelayaError < StandardError
|
|
6
|
+
attr_reader :status, :code, :body
|
|
7
|
+
|
|
8
|
+
def initialize(message, status: 0, code: nil, body: nil)
|
|
9
|
+
super(message)
|
|
10
|
+
@status = status
|
|
11
|
+
@code = code
|
|
12
|
+
@body = body
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|