melaya 0.1.2 → 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 +1 -1
- data/lib/melaya.rb +79 -79
- data/melaya.gemspec +23 -23
- metadata +6 -3
data/lib/melaya/sim.rb
CHANGED
|
@@ -1,110 +1,110 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Melaya
|
|
4
|
-
# Paper-trading (sim broker) API.
|
|
5
|
-
#
|
|
6
|
-
# The sim broker synthesises fills from Melaya's live ticker tape and keeps a
|
|
7
|
-
# virtual wallet per strategy — no venue-side state changes, no exchange
|
|
8
|
-
# credentials needed. Every call is scoped to a +strategy_id+.
|
|
9
|
-
#
|
|
10
|
-
# Create a paper strategy first:
|
|
11
|
-
# result = melaya.strategies.create(name: "test", strategy_type: "custom", ...)
|
|
12
|
-
# sid = result["strategyId"]
|
|
13
|
-
class SimAPI
|
|
14
|
-
def initialize(http)
|
|
15
|
-
@http = http
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
# Paper accounts (one virtual wallet per paper strategy).
|
|
19
|
-
def list_accounts
|
|
20
|
-
r = @http.get("/api/v1/private/sim/list-accounts")
|
|
21
|
-
r.is_a?(Array) ? r : (r.is_a?(Hash) ? (r["accounts"] || []) : [])
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# Virtual balance for a paper strategy (equity, realized/unrealized PnL, free/used).
|
|
25
|
-
# @param strategy_id [String]
|
|
26
|
-
# @param asset [String, nil]
|
|
27
|
-
def balance(strategy_id:, asset: nil)
|
|
28
|
-
@http.get("/api/v1/private/sim/balance",
|
|
29
|
-
"strategy_id" => strategy_id, "asset" => asset
|
|
30
|
-
)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# Open paper positions for a strategy.
|
|
34
|
-
def positions(strategy_id:)
|
|
35
|
-
r = @http.get("/api/v1/private/sim/positions", "strategy_id" => strategy_id)
|
|
36
|
-
r.is_a?(Array) ? r : (r.is_a?(Hash) ? (r["positions"] || []) : [])
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# Resting paper orders for a strategy.
|
|
40
|
-
def open_orders(strategy_id:)
|
|
41
|
-
r = @http.get("/api/v1/private/sim/open-orders", "strategy_id" => strategy_id)
|
|
42
|
-
r.is_a?(Array) ? r : (r.is_a?(Hash) ? (r["orders"] || []) : [])
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# Filled paper trades for a strategy.
|
|
46
|
-
def my_trades(strategy_id:)
|
|
47
|
-
r = @http.get("/api/v1/private/sim/my-trades", "strategy_id" => strategy_id)
|
|
48
|
-
r.is_a?(Array) ? r : (r.is_a?(Hash) ? (r["trades"] || []) : [])
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Place a paper order. Fills synthesise from the live ticker; nothing hits the venue.
|
|
52
|
-
#
|
|
53
|
-
# @param strategy_id [String]
|
|
54
|
-
# @param exchange [String]
|
|
55
|
-
# @param symbol [String]
|
|
56
|
-
# @param side [String] "buy" or "sell"
|
|
57
|
-
# @param amount [Numeric]
|
|
58
|
-
# @param type [String] "market" or "limit" (default "market")
|
|
59
|
-
# @param price [Numeric, nil] required for limit orders
|
|
60
|
-
# @param market [String, nil]
|
|
61
|
-
# @param leverage [Numeric, nil]
|
|
62
|
-
# @param reduce_only [Boolean, nil]
|
|
63
|
-
# @param sl_price [Numeric, nil]
|
|
64
|
-
# @param tp_price [Numeric, nil]
|
|
65
|
-
# @param client_order_id [String, nil]
|
|
66
|
-
# @param params [Hash, nil]
|
|
67
|
-
def create_order(strategy_id:, exchange:, symbol:, side:, amount:,
|
|
68
|
-
type: "market", price: nil, market: nil, leverage: nil,
|
|
69
|
-
reduce_only: nil, sl_price: nil, tp_price: nil,
|
|
70
|
-
client_order_id: nil, params: nil)
|
|
71
|
-
body = {
|
|
72
|
-
"strategy_id" => strategy_id,
|
|
73
|
-
"exchange" => exchange,
|
|
74
|
-
"symbol" => symbol,
|
|
75
|
-
"side" => side,
|
|
76
|
-
"amount" => amount,
|
|
77
|
-
"order_type" => type,
|
|
78
|
-
"orderType" => type,
|
|
79
|
-
"price" => price,
|
|
80
|
-
"market" => market,
|
|
81
|
-
"market_type" => market,
|
|
82
|
-
"leverage" => leverage,
|
|
83
|
-
"reduceOnly" => reduce_only,
|
|
84
|
-
"slPrice" => sl_price,
|
|
85
|
-
"tpPrice" => tp_price,
|
|
86
|
-
"client_order_id" => client_order_id,
|
|
87
|
-
"clientOrderId" => client_order_id,
|
|
88
|
-
"params" => params,
|
|
89
|
-
}.reject { |_, v| v.nil? }
|
|
90
|
-
@http.post("/api/v1/private/sim/create-order", body)
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
# Cancel a resting paper order.
|
|
94
|
-
#
|
|
95
|
-
# @param strategy_id [String]
|
|
96
|
-
# @param order_id [String]
|
|
97
|
-
# @param symbol [String, nil]
|
|
98
|
-
# @param exchange [String, nil]
|
|
99
|
-
def cancel_order(strategy_id:, order_id:, symbol: nil, exchange: nil)
|
|
100
|
-
body = {
|
|
101
|
-
"strategy_id" => strategy_id,
|
|
102
|
-
"order_id" => order_id,
|
|
103
|
-
"orderId" => order_id,
|
|
104
|
-
"symbol" => symbol,
|
|
105
|
-
"exchange" => exchange,
|
|
106
|
-
}.reject { |_, v| v.nil? }
|
|
107
|
-
@http.post("/api/v1/private/sim/cancel-order", body)
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Melaya
|
|
4
|
+
# Paper-trading (sim broker) API.
|
|
5
|
+
#
|
|
6
|
+
# The sim broker synthesises fills from Melaya's live ticker tape and keeps a
|
|
7
|
+
# virtual wallet per strategy — no venue-side state changes, no exchange
|
|
8
|
+
# credentials needed. Every call is scoped to a +strategy_id+.
|
|
9
|
+
#
|
|
10
|
+
# Create a paper strategy first:
|
|
11
|
+
# result = melaya.strategies.create(name: "test", strategy_type: "custom", ...)
|
|
12
|
+
# sid = result["strategyId"]
|
|
13
|
+
class SimAPI
|
|
14
|
+
def initialize(http)
|
|
15
|
+
@http = http
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Paper accounts (one virtual wallet per paper strategy).
|
|
19
|
+
def list_accounts
|
|
20
|
+
r = @http.get("/api/v1/private/sim/list-accounts")
|
|
21
|
+
r.is_a?(Array) ? r : (r.is_a?(Hash) ? (r["accounts"] || []) : [])
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Virtual balance for a paper strategy (equity, realized/unrealized PnL, free/used).
|
|
25
|
+
# @param strategy_id [String]
|
|
26
|
+
# @param asset [String, nil]
|
|
27
|
+
def balance(strategy_id:, asset: nil)
|
|
28
|
+
@http.get("/api/v1/private/sim/balance",
|
|
29
|
+
"strategy_id" => strategy_id, "asset" => asset
|
|
30
|
+
)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Open paper positions for a strategy.
|
|
34
|
+
def positions(strategy_id:)
|
|
35
|
+
r = @http.get("/api/v1/private/sim/positions", "strategy_id" => strategy_id)
|
|
36
|
+
r.is_a?(Array) ? r : (r.is_a?(Hash) ? (r["positions"] || []) : [])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Resting paper orders for a strategy.
|
|
40
|
+
def open_orders(strategy_id:)
|
|
41
|
+
r = @http.get("/api/v1/private/sim/open-orders", "strategy_id" => strategy_id)
|
|
42
|
+
r.is_a?(Array) ? r : (r.is_a?(Hash) ? (r["orders"] || []) : [])
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Filled paper trades for a strategy.
|
|
46
|
+
def my_trades(strategy_id:)
|
|
47
|
+
r = @http.get("/api/v1/private/sim/my-trades", "strategy_id" => strategy_id)
|
|
48
|
+
r.is_a?(Array) ? r : (r.is_a?(Hash) ? (r["trades"] || []) : [])
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Place a paper order. Fills synthesise from the live ticker; nothing hits the venue.
|
|
52
|
+
#
|
|
53
|
+
# @param strategy_id [String]
|
|
54
|
+
# @param exchange [String]
|
|
55
|
+
# @param symbol [String]
|
|
56
|
+
# @param side [String] "buy" or "sell"
|
|
57
|
+
# @param amount [Numeric]
|
|
58
|
+
# @param type [String] "market" or "limit" (default "market")
|
|
59
|
+
# @param price [Numeric, nil] required for limit orders
|
|
60
|
+
# @param market [String, nil]
|
|
61
|
+
# @param leverage [Numeric, nil]
|
|
62
|
+
# @param reduce_only [Boolean, nil]
|
|
63
|
+
# @param sl_price [Numeric, nil]
|
|
64
|
+
# @param tp_price [Numeric, nil]
|
|
65
|
+
# @param client_order_id [String, nil]
|
|
66
|
+
# @param params [Hash, nil]
|
|
67
|
+
def create_order(strategy_id:, exchange:, symbol:, side:, amount:,
|
|
68
|
+
type: "market", price: nil, market: nil, leverage: nil,
|
|
69
|
+
reduce_only: nil, sl_price: nil, tp_price: nil,
|
|
70
|
+
client_order_id: nil, params: nil)
|
|
71
|
+
body = {
|
|
72
|
+
"strategy_id" => strategy_id,
|
|
73
|
+
"exchange" => exchange,
|
|
74
|
+
"symbol" => symbol,
|
|
75
|
+
"side" => side,
|
|
76
|
+
"amount" => amount,
|
|
77
|
+
"order_type" => type,
|
|
78
|
+
"orderType" => type,
|
|
79
|
+
"price" => price,
|
|
80
|
+
"market" => market,
|
|
81
|
+
"market_type" => market,
|
|
82
|
+
"leverage" => leverage,
|
|
83
|
+
"reduceOnly" => reduce_only,
|
|
84
|
+
"slPrice" => sl_price,
|
|
85
|
+
"tpPrice" => tp_price,
|
|
86
|
+
"client_order_id" => client_order_id,
|
|
87
|
+
"clientOrderId" => client_order_id,
|
|
88
|
+
"params" => params,
|
|
89
|
+
}.reject { |_, v| v.nil? }
|
|
90
|
+
@http.post("/api/v1/private/sim/create-order", body)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Cancel a resting paper order.
|
|
94
|
+
#
|
|
95
|
+
# @param strategy_id [String]
|
|
96
|
+
# @param order_id [String]
|
|
97
|
+
# @param symbol [String, nil]
|
|
98
|
+
# @param exchange [String, nil]
|
|
99
|
+
def cancel_order(strategy_id:, order_id:, symbol: nil, exchange: nil)
|
|
100
|
+
body = {
|
|
101
|
+
"strategy_id" => strategy_id,
|
|
102
|
+
"order_id" => order_id,
|
|
103
|
+
"orderId" => order_id,
|
|
104
|
+
"symbol" => symbol,
|
|
105
|
+
"exchange" => exchange,
|
|
106
|
+
}.reject { |_, v| v.nil? }
|
|
107
|
+
@http.post("/api/v1/private/sim/cancel-order", body)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
data/lib/melaya/strategies.rb
CHANGED
|
@@ -1,155 +1,155 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Melaya
|
|
4
|
-
# Strategies API — launch, control, and inspect trading strategies.
|
|
5
|
-
#
|
|
6
|
-
# A strategy is a server-managed runner (the Trading Engine, or an Agentic
|
|
7
|
-
# Trading Crew) that trades a universe on a cadence with server-side SL/TP
|
|
8
|
-
# and safety rails. Launch in paper mode (dry_run: true) or live
|
|
9
|
-
# (dry_run: false, which requires a connected exchange key).
|
|
10
|
-
#
|
|
11
|
-
# Maps to https://api.melaya.org/api/v1/strategies/* on the private plane.
|
|
12
|
-
class StrategiesAPI
|
|
13
|
-
def initialize(http)
|
|
14
|
-
@http = http
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# Every strategy you own (running, paused, paper, and live).
|
|
18
|
-
def list
|
|
19
|
-
@http.get("/api/v1/strategies/list")["strategies"]
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
# A single strategy by id.
|
|
23
|
-
# @param strategy_id [String]
|
|
24
|
-
def get(strategy_id)
|
|
25
|
-
@http.get("/api/v1/strategies/#{strategy_id}")["strategy"]
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Launch a strategy. Pass dry_run: true for paper; live needs api_key_id.
|
|
29
|
-
# Returns the full response hash (includes "strategyId").
|
|
30
|
-
#
|
|
31
|
-
# @param name [String]
|
|
32
|
-
# @param strategy_type [String] e.g. "custom"
|
|
33
|
-
# @param exchange [String]
|
|
34
|
-
# @param market [String, nil]
|
|
35
|
-
# @param symbol [String, nil]
|
|
36
|
-
# @param api_key_id [String, nil]
|
|
37
|
-
# @param params [Hash, nil]
|
|
38
|
-
# @param runtime_mode [String, nil]
|
|
39
|
-
# @param dry_run [Boolean]
|
|
40
|
-
# @param key_bindings [Hash, nil]
|
|
41
|
-
def create(name:, strategy_type:, exchange:, market: nil, symbol: nil,
|
|
42
|
-
api_key_id: nil, params: nil, runtime_mode: nil, dry_run: true,
|
|
43
|
-
key_bindings: nil)
|
|
44
|
-
body = {
|
|
45
|
-
"name" => name,
|
|
46
|
-
"strategyType" => strategy_type,
|
|
47
|
-
"exchange" => exchange,
|
|
48
|
-
"market" => market,
|
|
49
|
-
"symbol" => symbol,
|
|
50
|
-
"apiKeyId" => api_key_id,
|
|
51
|
-
"params" => params,
|
|
52
|
-
"runtimeMode" => runtime_mode,
|
|
53
|
-
"dryRun" => dry_run,
|
|
54
|
-
"keyBindings" => key_bindings,
|
|
55
|
-
}.reject { |_, v| v.nil? }
|
|
56
|
-
@http.post("/api/v1/strategies", body)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# Pause a running strategy (stops entering new cycles until resumed).
|
|
60
|
-
def pause(strategy_id)
|
|
61
|
-
@http.post("/api/v1/strategies/#{strategy_id}/pause")
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# Resume a paused strategy.
|
|
65
|
-
def resume(strategy_id)
|
|
66
|
-
@http.post("/api/v1/strategies/#{strategy_id}/resume")
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# Stop a strategy and tear down its runner. Cancels any in-flight approvals.
|
|
70
|
-
def stop(strategy_id)
|
|
71
|
-
@http.post("/api/v1/strategies/#{strategy_id}/stop")
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
# Soft-delete a strategy.
|
|
75
|
-
def delete(strategy_id)
|
|
76
|
-
@http.delete("/api/v1/strategies/#{strategy_id}")
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
# Update a running strategy's params (e.g. universe, cadence, risk caps).
|
|
80
|
-
# @param strategy_id [String]
|
|
81
|
-
# @param params [Hash]
|
|
82
|
-
def update_params(strategy_id, params)
|
|
83
|
-
@http.post("/api/v1/strategies/#{strategy_id}/update-params", params)
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# Live runtime status of a strategy's runner (container health, tick count).
|
|
87
|
-
def status(strategy_id)
|
|
88
|
-
@http.get("/api/v1/strategies/#{strategy_id}/status")
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
# Performance series for a strategy (equity, PnL over time).
|
|
92
|
-
def performance(strategy_id)
|
|
93
|
-
@http.get("/api/v1/strategies/#{strategy_id}/performance")["rows"]
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# Execution (order) rows for a strategy.
|
|
97
|
-
def executions(strategy_id)
|
|
98
|
-
@http.get("/api/v1/strategies/#{strategy_id}/executions")["rows"]
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
# Trade (fill) rows for a strategy.
|
|
102
|
-
def trades(strategy_id)
|
|
103
|
-
@http.get("/api/v1/strategies/#{strategy_id}/trades")["rows"]
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
# Log rows for a strategy (cycle markers, persona messages, errors).
|
|
107
|
-
def logs(strategy_id)
|
|
108
|
-
@http.get("/api/v1/strategies/#{strategy_id}/logs")["rows"]
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
# ── AI parameter optimizer ────────────────────────────────────────────────
|
|
112
|
-
|
|
113
|
-
# Kick off an AI-driven parameter optimization.
|
|
114
|
-
# +param_bounds+ maps each param name to a [min, max] array.
|
|
115
|
-
# +objective+ defaults to "sharpe"; +max_iterations+ is clamped to 1-20.
|
|
116
|
-
# Returns a hash including "runId".
|
|
117
|
-
#
|
|
118
|
-
# @param strategy_id [String]
|
|
119
|
-
# @param param_bounds [Hash] e.g. { "qty" => [0.001, 0.1] }
|
|
120
|
-
# @param objective [String]
|
|
121
|
-
# @param max_iterations [Integer]
|
|
122
|
-
# @param require_approval [Boolean, nil]
|
|
123
|
-
def ai_opt_start(strategy_id, param_bounds:, objective: "sharpe",
|
|
124
|
-
max_iterations: 3, require_approval: nil)
|
|
125
|
-
body = {
|
|
126
|
-
"paramBounds" => param_bounds,
|
|
127
|
-
"objective" => objective,
|
|
128
|
-
"maxIterations" => max_iterations,
|
|
129
|
-
}
|
|
130
|
-
body["requireApproval"] = require_approval unless require_approval.nil?
|
|
131
|
-
@http.post("/api/v1/strategies/#{strategy_id}/ai-opt/start", body)
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
# Current optimization status for a strategy.
|
|
135
|
-
def ai_opt_status(strategy_id)
|
|
136
|
-
@http.get("/api/v1/strategies/#{strategy_id}/ai-opt/status")
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
# Approve and apply the optimizer's proposed params to the running strategy.
|
|
140
|
-
# @param body [Hash] optional extra params
|
|
141
|
-
def ai_opt_approve(strategy_id, body = {})
|
|
142
|
-
@http.post("/api/v1/strategies/#{strategy_id}/ai-opt/approve", body)
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
# Stop an in-progress optimization.
|
|
146
|
-
def ai_opt_stop(strategy_id)
|
|
147
|
-
@http.post("/api/v1/strategies/#{strategy_id}/ai-opt/stop")
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
# Past optimization runs for a strategy.
|
|
151
|
-
def ai_opt_runs(strategy_id)
|
|
152
|
-
@http.get("/api/v1/strategies/#{strategy_id}/ai-opt/runs")
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Melaya
|
|
4
|
+
# Strategies API — launch, control, and inspect trading strategies.
|
|
5
|
+
#
|
|
6
|
+
# A strategy is a server-managed runner (the Trading Engine, or an Agentic
|
|
7
|
+
# Trading Crew) that trades a universe on a cadence with server-side SL/TP
|
|
8
|
+
# and safety rails. Launch in paper mode (dry_run: true) or live
|
|
9
|
+
# (dry_run: false, which requires a connected exchange key).
|
|
10
|
+
#
|
|
11
|
+
# Maps to https://api.melaya.org/api/v1/strategies/* on the private plane.
|
|
12
|
+
class StrategiesAPI
|
|
13
|
+
def initialize(http)
|
|
14
|
+
@http = http
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Every strategy you own (running, paused, paper, and live).
|
|
18
|
+
def list
|
|
19
|
+
@http.get("/api/v1/strategies/list")["strategies"]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# A single strategy by id.
|
|
23
|
+
# @param strategy_id [String]
|
|
24
|
+
def get(strategy_id)
|
|
25
|
+
@http.get("/api/v1/strategies/#{strategy_id}")["strategy"]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Launch a strategy. Pass dry_run: true for paper; live needs api_key_id.
|
|
29
|
+
# Returns the full response hash (includes "strategyId").
|
|
30
|
+
#
|
|
31
|
+
# @param name [String]
|
|
32
|
+
# @param strategy_type [String] e.g. "custom"
|
|
33
|
+
# @param exchange [String]
|
|
34
|
+
# @param market [String, nil]
|
|
35
|
+
# @param symbol [String, nil]
|
|
36
|
+
# @param api_key_id [String, nil]
|
|
37
|
+
# @param params [Hash, nil]
|
|
38
|
+
# @param runtime_mode [String, nil]
|
|
39
|
+
# @param dry_run [Boolean]
|
|
40
|
+
# @param key_bindings [Hash, nil]
|
|
41
|
+
def create(name:, strategy_type:, exchange:, market: nil, symbol: nil,
|
|
42
|
+
api_key_id: nil, params: nil, runtime_mode: nil, dry_run: true,
|
|
43
|
+
key_bindings: nil)
|
|
44
|
+
body = {
|
|
45
|
+
"name" => name,
|
|
46
|
+
"strategyType" => strategy_type,
|
|
47
|
+
"exchange" => exchange,
|
|
48
|
+
"market" => market,
|
|
49
|
+
"symbol" => symbol,
|
|
50
|
+
"apiKeyId" => api_key_id,
|
|
51
|
+
"params" => params,
|
|
52
|
+
"runtimeMode" => runtime_mode,
|
|
53
|
+
"dryRun" => dry_run,
|
|
54
|
+
"keyBindings" => key_bindings,
|
|
55
|
+
}.reject { |_, v| v.nil? }
|
|
56
|
+
@http.post("/api/v1/strategies", body)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Pause a running strategy (stops entering new cycles until resumed).
|
|
60
|
+
def pause(strategy_id)
|
|
61
|
+
@http.post("/api/v1/strategies/#{strategy_id}/pause")
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Resume a paused strategy.
|
|
65
|
+
def resume(strategy_id)
|
|
66
|
+
@http.post("/api/v1/strategies/#{strategy_id}/resume")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Stop a strategy and tear down its runner. Cancels any in-flight approvals.
|
|
70
|
+
def stop(strategy_id)
|
|
71
|
+
@http.post("/api/v1/strategies/#{strategy_id}/stop")
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Soft-delete a strategy.
|
|
75
|
+
def delete(strategy_id)
|
|
76
|
+
@http.delete("/api/v1/strategies/#{strategy_id}")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Update a running strategy's params (e.g. universe, cadence, risk caps).
|
|
80
|
+
# @param strategy_id [String]
|
|
81
|
+
# @param params [Hash]
|
|
82
|
+
def update_params(strategy_id, params)
|
|
83
|
+
@http.post("/api/v1/strategies/#{strategy_id}/update-params", params)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Live runtime status of a strategy's runner (container health, tick count).
|
|
87
|
+
def status(strategy_id)
|
|
88
|
+
@http.get("/api/v1/strategies/#{strategy_id}/status")
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Performance series for a strategy (equity, PnL over time).
|
|
92
|
+
def performance(strategy_id)
|
|
93
|
+
@http.get("/api/v1/strategies/#{strategy_id}/performance")["rows"]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Execution (order) rows for a strategy.
|
|
97
|
+
def executions(strategy_id)
|
|
98
|
+
@http.get("/api/v1/strategies/#{strategy_id}/executions")["rows"]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Trade (fill) rows for a strategy.
|
|
102
|
+
def trades(strategy_id)
|
|
103
|
+
@http.get("/api/v1/strategies/#{strategy_id}/trades")["rows"]
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Log rows for a strategy (cycle markers, persona messages, errors).
|
|
107
|
+
def logs(strategy_id)
|
|
108
|
+
@http.get("/api/v1/strategies/#{strategy_id}/logs")["rows"]
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# ── AI parameter optimizer ────────────────────────────────────────────────
|
|
112
|
+
|
|
113
|
+
# Kick off an AI-driven parameter optimization.
|
|
114
|
+
# +param_bounds+ maps each param name to a [min, max] array.
|
|
115
|
+
# +objective+ defaults to "sharpe"; +max_iterations+ is clamped to 1-20.
|
|
116
|
+
# Returns a hash including "runId".
|
|
117
|
+
#
|
|
118
|
+
# @param strategy_id [String]
|
|
119
|
+
# @param param_bounds [Hash] e.g. { "qty" => [0.001, 0.1] }
|
|
120
|
+
# @param objective [String]
|
|
121
|
+
# @param max_iterations [Integer]
|
|
122
|
+
# @param require_approval [Boolean, nil]
|
|
123
|
+
def ai_opt_start(strategy_id, param_bounds:, objective: "sharpe",
|
|
124
|
+
max_iterations: 3, require_approval: nil)
|
|
125
|
+
body = {
|
|
126
|
+
"paramBounds" => param_bounds,
|
|
127
|
+
"objective" => objective,
|
|
128
|
+
"maxIterations" => max_iterations,
|
|
129
|
+
}
|
|
130
|
+
body["requireApproval"] = require_approval unless require_approval.nil?
|
|
131
|
+
@http.post("/api/v1/strategies/#{strategy_id}/ai-opt/start", body)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Current optimization status for a strategy.
|
|
135
|
+
def ai_opt_status(strategy_id)
|
|
136
|
+
@http.get("/api/v1/strategies/#{strategy_id}/ai-opt/status")
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Approve and apply the optimizer's proposed params to the running strategy.
|
|
140
|
+
# @param body [Hash] optional extra params
|
|
141
|
+
def ai_opt_approve(strategy_id, body = {})
|
|
142
|
+
@http.post("/api/v1/strategies/#{strategy_id}/ai-opt/approve", body)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Stop an in-progress optimization.
|
|
146
|
+
def ai_opt_stop(strategy_id)
|
|
147
|
+
@http.post("/api/v1/strategies/#{strategy_id}/ai-opt/stop")
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Past optimization runs for a strategy.
|
|
151
|
+
def ai_opt_runs(strategy_id)
|
|
152
|
+
@http.get("/api/v1/strategies/#{strategy_id}/ai-opt/runs")
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|