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/team.rb
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Melaya
|
|
4
|
+
# Team API — manage project team membership, roles, and invitations.
|
|
5
|
+
#
|
|
6
|
+
# Maps to:
|
|
7
|
+
# /api/v1/private/projects/:project/members/*
|
|
8
|
+
# /api/v1/private/team/*
|
|
9
|
+
# /api/v1/private/projects/:project/pipelines/:pipeline/visibility
|
|
10
|
+
#
|
|
11
|
+
# @example
|
|
12
|
+
# members = melaya.team.list_members("my-project")
|
|
13
|
+
# melaya.team.invite("my-project", username: "alice")
|
|
14
|
+
# link = melaya.team.create_invite_link("my-project")
|
|
15
|
+
class TeamAPI
|
|
16
|
+
def initialize(http)
|
|
17
|
+
@http = http
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# GET /api/v1/private/projects/:project/members
|
|
21
|
+
# List members of a project team.
|
|
22
|
+
# @param project [String] project name
|
|
23
|
+
def list_members(project)
|
|
24
|
+
@http.get("/api/v1/private/projects/#{enc(project)}/members")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# POST /api/v1/private/projects/:project/members/invite
|
|
28
|
+
# Invite a user to a project team by username.
|
|
29
|
+
# @param project [String]
|
|
30
|
+
# @param username [String]
|
|
31
|
+
def invite(project, username:)
|
|
32
|
+
@http.post("/api/v1/private/projects/#{enc(project)}/members/invite",
|
|
33
|
+
"username" => username)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# POST /api/v1/private/projects/:project/invite-link
|
|
37
|
+
# Create a shareable invite link for a project.
|
|
38
|
+
# Returns the URL to send to new team members.
|
|
39
|
+
# @param project [String]
|
|
40
|
+
def create_invite_link(project)
|
|
41
|
+
@http.post("/api/v1/private/projects/#{enc(project)}/invite-link")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# POST /api/v1/private/team/invite/accept
|
|
45
|
+
# Accept a project invite using the token from an invite link.
|
|
46
|
+
# @param token [String]
|
|
47
|
+
def accept_invite(token)
|
|
48
|
+
@http.post("/api/v1/private/team/invite/accept", "token" => token)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# PATCH /api/v1/private/projects/:project/members/:userId
|
|
52
|
+
# Update a team member's role in a project.
|
|
53
|
+
# @param project [String]
|
|
54
|
+
# @param user_id [String]
|
|
55
|
+
# @param role [String] one of "owner", "editor", "viewer"
|
|
56
|
+
def update_member_role(project, user_id, role:)
|
|
57
|
+
@http.patch(
|
|
58
|
+
"/api/v1/private/projects/#{enc(project)}/members/#{enc(user_id)}",
|
|
59
|
+
"role" => role
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# DELETE /api/v1/private/projects/:project/members/:userId
|
|
64
|
+
# Remove a member from a project team.
|
|
65
|
+
# @param project [String]
|
|
66
|
+
# @param user_id [String]
|
|
67
|
+
def remove_member(project, user_id)
|
|
68
|
+
@http.delete("/api/v1/private/projects/#{enc(project)}/members/#{enc(user_id)}")
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# ── Pipeline visibility ────────────────────────────────────────────────────
|
|
72
|
+
|
|
73
|
+
# GET /api/v1/private/projects/:project/pipelines/:pipeline/visibility
|
|
74
|
+
# Get visibility settings for a pipeline within a project.
|
|
75
|
+
# @param project [String]
|
|
76
|
+
# @param pipeline [String]
|
|
77
|
+
def get_pipeline_visibility(project, pipeline)
|
|
78
|
+
@http.get(
|
|
79
|
+
"/api/v1/private/projects/#{enc(project)}/pipelines/#{enc(pipeline)}/visibility"
|
|
80
|
+
)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# PUT /api/v1/private/projects/:project/pipelines/:pipeline/visibility
|
|
84
|
+
# Set pipeline visibility within a project.
|
|
85
|
+
# @param project [String]
|
|
86
|
+
# @param pipeline [String]
|
|
87
|
+
# @param body [Hash]
|
|
88
|
+
def set_pipeline_visibility(project, pipeline, body = {})
|
|
89
|
+
@http.put(
|
|
90
|
+
"/api/v1/private/projects/#{enc(project)}/pipelines/#{enc(pipeline)}/visibility",
|
|
91
|
+
body
|
|
92
|
+
)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
private
|
|
96
|
+
|
|
97
|
+
def enc(s)
|
|
98
|
+
URI.encode_www_form_component(s.to_s)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Melaya
|
|
4
|
+
# Templates API — create, manage, share, and assign pipeline templates.
|
|
5
|
+
#
|
|
6
|
+
# Templates bundle a pipeline definition into a reusable, shareable artifact.
|
|
7
|
+
# Visibility levels: private → team → community → assigned.
|
|
8
|
+
#
|
|
9
|
+
# Maps to:
|
|
10
|
+
# /api/v1/private/user-templates/* — CRUD + share + assignments
|
|
11
|
+
# /api/v1/private/templates/* — global/validated lists
|
|
12
|
+
#
|
|
13
|
+
# @example
|
|
14
|
+
# templates = melaya.templates.list
|
|
15
|
+
# t = melaya.templates.save(name: "My report", payload: { steps: [] })
|
|
16
|
+
# melaya.templates.share(t["id"], "team")
|
|
17
|
+
# melaya.templates.delete(t["id"])
|
|
18
|
+
class TemplatesAPI
|
|
19
|
+
def initialize(http)
|
|
20
|
+
@http = http
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# GET /api/v1/private/user-templates
|
|
24
|
+
# List all templates visible to the caller (own + team + community + assigned).
|
|
25
|
+
def list
|
|
26
|
+
@http.get("/api/v1/private/user-templates")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# GET /api/v1/private/templates/global
|
|
30
|
+
# List all community-visibility (global) templates.
|
|
31
|
+
def list_global
|
|
32
|
+
@http.get("/api/v1/private/templates/global")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# GET /api/v1/private/templates/validated
|
|
36
|
+
# List IDs of all validated (platform-approved) templates.
|
|
37
|
+
def list_validated
|
|
38
|
+
@http.get("/api/v1/private/templates/validated")
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# POST /api/v1/private/user-templates
|
|
42
|
+
# Create a new private user template.
|
|
43
|
+
# @param name [String]
|
|
44
|
+
# @param payload [Hash]
|
|
45
|
+
# @param description [String, nil]
|
|
46
|
+
# @param category [String, nil]
|
|
47
|
+
def save(name:, payload:, description: nil, category: nil)
|
|
48
|
+
body = compact(
|
|
49
|
+
"name" => name,
|
|
50
|
+
"payload" => payload,
|
|
51
|
+
"description" => description,
|
|
52
|
+
"category" => category
|
|
53
|
+
)
|
|
54
|
+
@http.post("/api/v1/private/user-templates", body)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# PATCH /api/v1/private/user-templates/:id
|
|
58
|
+
# Update name/description/category/payload of a private user template.
|
|
59
|
+
# @param template_id [String]
|
|
60
|
+
# @param body [Hash]
|
|
61
|
+
def update(template_id, body = {})
|
|
62
|
+
@http.patch("/api/v1/private/user-templates/#{enc(template_id)}", body)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# POST /api/v1/private/user-templates/:sourceId/duplicate
|
|
66
|
+
# Duplicate a readable template into the caller's private library.
|
|
67
|
+
# @param template_id [String] the source template to copy
|
|
68
|
+
# @param new_name [String, nil]
|
|
69
|
+
def duplicate(template_id, new_name: nil)
|
|
70
|
+
body = new_name ? { "newName" => new_name } : nil
|
|
71
|
+
@http.post("/api/v1/private/user-templates/#{enc(template_id)}/duplicate", body)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# DELETE /api/v1/private/user-templates/:id
|
|
75
|
+
# Delete (or soft-demote if shared) a template.
|
|
76
|
+
# @param template_id [String]
|
|
77
|
+
def delete(template_id)
|
|
78
|
+
@http.delete("/api/v1/private/user-templates/#{enc(template_id)}")
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# PUT /api/v1/private/user-templates/:id/visibility
|
|
82
|
+
# Change the visibility of a template.
|
|
83
|
+
# @param template_id [String]
|
|
84
|
+
# @param visibility [String] "private", "team", "community", or "assigned"
|
|
85
|
+
def share(template_id, visibility)
|
|
86
|
+
@http.put("/api/v1/private/user-templates/#{enc(template_id)}/visibility",
|
|
87
|
+
"visibility" => visibility)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# GET /api/v1/private/user-templates/share-targets
|
|
91
|
+
# List projects the caller is a member of (for the share target picker UI).
|
|
92
|
+
def share_targets
|
|
93
|
+
@http.get("/api/v1/private/user-templates/share-targets")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# ── Assignments ────────────────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
# GET /api/v1/private/user-templates/:templateId/assignments
|
|
99
|
+
# List all assignments (users / projects) for a template.
|
|
100
|
+
# @param template_id [String]
|
|
101
|
+
def list_assignments(template_id)
|
|
102
|
+
@http.get("/api/v1/private/user-templates/#{enc(template_id)}/assignments")
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# POST /api/v1/private/user-templates/:templateId/assignments
|
|
106
|
+
# Assign a template to a user or project.
|
|
107
|
+
# Provide exactly one of +user_id:+ or +project_id:+ (both UUIDs);
|
|
108
|
+
# the server rejects requests carrying both or neither.
|
|
109
|
+
# @param template_id [String]
|
|
110
|
+
# @param user_id [String, nil] target user UUID
|
|
111
|
+
# @param project_id [String, nil] target project UUID
|
|
112
|
+
def assign(template_id, user_id: nil, project_id: nil)
|
|
113
|
+
@http.post("/api/v1/private/user-templates/#{enc(template_id)}/assignments",
|
|
114
|
+
assignment_target(user_id, project_id))
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# DELETE /api/v1/private/user-templates/:templateId/assignments
|
|
118
|
+
# Remove an assignment from a template.
|
|
119
|
+
# Provide exactly one of +user_id:+ or +project_id:+ (both UUIDs).
|
|
120
|
+
# The target is sent as query params — the server ignores DELETE
|
|
121
|
+
# request bodies.
|
|
122
|
+
# @param template_id [String]
|
|
123
|
+
# @param user_id [String, nil] target user UUID
|
|
124
|
+
# @param project_id [String, nil] target project UUID
|
|
125
|
+
def unassign(template_id, user_id: nil, project_id: nil)
|
|
126
|
+
@http.delete("/api/v1/private/user-templates/#{enc(template_id)}/assignments",
|
|
127
|
+
assignment_target(user_id, project_id))
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
private
|
|
131
|
+
|
|
132
|
+
# Exactly one of user_id / project_id must be given.
|
|
133
|
+
def assignment_target(user_id, project_id)
|
|
134
|
+
if user_id.nil? == project_id.nil?
|
|
135
|
+
raise ArgumentError, "Melaya: provide exactly one of user_id: or project_id:"
|
|
136
|
+
end
|
|
137
|
+
user_id ? { "userId" => user_id } : { "projectId" => project_id }
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def enc(s)
|
|
141
|
+
URI.encode_www_form_component(s.to_s)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def compact(hash)
|
|
145
|
+
hash.reject { |_, v| v.nil? }
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
data/lib/melaya/trade.rb
CHANGED
|
@@ -1,168 +1,168 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Melaya
|
|
4
|
-
# Live trading API — credentialed order placement, account state, and
|
|
5
|
-
# position management on a CONNECTED exchange.
|
|
6
|
-
#
|
|
7
|
-
# Every method POSTs to +https://api.melaya.org/api/v1/private/<op>+; the server
|
|
8
|
-
# resolves your connected exchange credential (referenced by +api_key_id+ — see
|
|
9
|
-
# AccountAPI#keys) and forwards the call to the venue through Melaya's in-house
|
|
10
|
-
# Rust engine. Responses share an envelope:
|
|
11
|
-
# +{ ok, exchange, operation, orderId, clientOrderId, payload, data, ... }+.
|
|
12
|
-
#
|
|
13
|
-
# WARNING: these hit the REAL venue with REAL funds. The write methods
|
|
14
|
-
# (create_order, cancel_order, amend_order, cancel_all_orders, cancel_plan_orders,
|
|
15
|
-
# close_position, set_leverage, set_margin_mode, set_position_mode) move money or
|
|
16
|
-
# change account state. For risk-free testing use the sim (paper) broker or a
|
|
17
|
-
# paper strategy instead.
|
|
18
|
-
class TradeAPI
|
|
19
|
-
def initialize(http)
|
|
20
|
-
@http = http
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# ── Account state (reads) ──────────────────────────────────────────────────
|
|
24
|
-
|
|
25
|
-
# Live account balance on a connected venue.
|
|
26
|
-
def balance(exchange:, api_key_id: nil, key_id: nil, market_type: nil, params: nil)
|
|
27
|
-
op("balance", exchange: exchange, apiKeyId: api_key_id, keyId: key_id,
|
|
28
|
-
marketType: market_type, params: params)
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
# Live open positions.
|
|
32
|
-
def positions(exchange:, api_key_id: nil, market_type: nil, symbol: nil, params: nil)
|
|
33
|
-
op("positions", exchange: exchange, apiKeyId: api_key_id,
|
|
34
|
-
marketType: market_type, symbol: symbol, params: params)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
# Historical positions (venue-dependent).
|
|
38
|
-
def positions_history(exchange:, api_key_id: nil, market_type: nil, symbol: nil)
|
|
39
|
-
op("positions-history", exchange: exchange, apiKeyId: api_key_id,
|
|
40
|
-
marketType: market_type, symbol: symbol)
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Resting (open) orders.
|
|
44
|
-
def open_orders(exchange:, api_key_id: nil, market_type: nil, symbol: nil)
|
|
45
|
-
op("open-orders", exchange: exchange, apiKeyId: api_key_id,
|
|
46
|
-
marketType: market_type, symbol: symbol)
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
# All orders (open + recent).
|
|
50
|
-
def orders(exchange:, api_key_id: nil, market_type: nil, symbol: nil)
|
|
51
|
-
op("orders", exchange: exchange, apiKeyId: api_key_id,
|
|
52
|
-
marketType: market_type, symbol: symbol)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Closed/filled orders.
|
|
56
|
-
def closed_orders(exchange:, api_key_id: nil, market_type: nil, symbol: nil)
|
|
57
|
-
op("closed-orders", exchange: exchange, apiKeyId: api_key_id,
|
|
58
|
-
marketType: market_type, symbol: symbol)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
# Your trade (fill) history.
|
|
62
|
-
def my_trades(exchange:, api_key_id: nil, market_type: nil, symbol: nil)
|
|
63
|
-
op("my-trades", exchange: exchange, apiKeyId: api_key_id,
|
|
64
|
-
marketType: market_type, symbol: symbol)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
# Extended trade history (venue-dependent).
|
|
68
|
-
def my_trades_history(exchange:, api_key_id: nil, market_type: nil, symbol: nil)
|
|
69
|
-
op("my-trades-history", exchange: exchange, apiKeyId: api_key_id,
|
|
70
|
-
marketType: market_type, symbol: symbol)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
# Resting conditional/plan (trigger) orders.
|
|
74
|
-
def plan_orders(exchange:, api_key_id: nil, market_type: nil, symbol: nil)
|
|
75
|
-
op("plan-orders", exchange: exchange, apiKeyId: api_key_id,
|
|
76
|
-
marketType: market_type, symbol: symbol)
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
# Current leverage for a symbol.
|
|
80
|
-
def leverage(exchange:, api_key_id: nil, symbol: nil, market_type: nil)
|
|
81
|
-
op("leverage", exchange: exchange, apiKeyId: api_key_id,
|
|
82
|
-
symbol: symbol, marketType: market_type)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# Leverage tiers / brackets for a symbol.
|
|
86
|
-
def leverage_tiers(exchange:, api_key_id: nil, symbol: nil, market_type: nil)
|
|
87
|
-
op("leverage-tiers", exchange: exchange, apiKeyId: api_key_id,
|
|
88
|
-
symbol: symbol, marketType: market_type)
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
# ── Order placement & management (LIVE writes — real funds) ───────────────
|
|
92
|
-
|
|
93
|
-
# Place a live order on the venue. WARNING: real money.
|
|
94
|
-
# stop_price, take_profit_price, and reduce_only are folded into +params+.
|
|
95
|
-
def create_order(exchange:, symbol:, side:, amount:,
|
|
96
|
-
api_key_id: nil, type: "market", price: nil,
|
|
97
|
-
market_type: nil, stop_price: nil, take_profit_price: nil,
|
|
98
|
-
reduce_only: nil, leverage: nil, client_order_id: nil, params: nil)
|
|
99
|
-
p = (params || {}).dup
|
|
100
|
-
p["stopPrice"] = stop_price unless stop_price.nil?
|
|
101
|
-
p["takeProfitPrice"] = take_profit_price unless take_profit_price.nil?
|
|
102
|
-
p["reduceOnly"] = reduce_only unless reduce_only.nil?
|
|
103
|
-
op("create-order",
|
|
104
|
-
exchange: exchange, apiKeyId: api_key_id, symbol: symbol,
|
|
105
|
-
side: side, amount: amount, type: type, price: price,
|
|
106
|
-
marketType: market_type, leverage: leverage,
|
|
107
|
-
clientOrderId: client_order_id, params: p.empty? ? nil : p)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
# Cancel a live order by id. WARNING.
|
|
111
|
-
def cancel_order(exchange:, api_key_id: nil, order_id: nil, client_order_id: nil,
|
|
112
|
-
symbol: nil, market_type: nil)
|
|
113
|
-
op("cancel-order", exchange: exchange, apiKeyId: api_key_id,
|
|
114
|
-
orderId: order_id, clientOrderId: client_order_id,
|
|
115
|
-
symbol: symbol, marketType: market_type)
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
# Amend (modify) a live order. WARNING.
|
|
119
|
-
def amend_order(exchange:, api_key_id: nil, order_id: nil, symbol: nil,
|
|
120
|
-
amount: nil, price: nil)
|
|
121
|
-
op("amend-order", exchange: exchange, apiKeyId: api_key_id,
|
|
122
|
-
orderId: order_id, symbol: symbol, amount: amount, price: price)
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
# Cancel every open order (optionally scoped to a symbol). WARNING.
|
|
126
|
-
def cancel_all_orders(exchange:, api_key_id: nil, symbol: nil, market_type: nil)
|
|
127
|
-
op("cancel-all-orders", exchange: exchange, apiKeyId: api_key_id,
|
|
128
|
-
symbol: symbol, marketType: market_type)
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
# Cancel resting plan/trigger orders. WARNING.
|
|
132
|
-
def cancel_plan_orders(exchange:, api_key_id: nil, symbol: nil, market_type: nil)
|
|
133
|
-
op("cancel-plan-orders", exchange: exchange, apiKeyId: api_key_id,
|
|
134
|
-
symbol: symbol, marketType: market_type)
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
# Close an open position (market reduce-only). WARNING.
|
|
138
|
-
def close_position(exchange:, symbol:, api_key_id: nil, market_type: nil)
|
|
139
|
-
op("close-position", exchange: exchange, apiKeyId: api_key_id,
|
|
140
|
-
symbol: symbol, marketType: market_type)
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
# Set leverage for a symbol. WARNING.
|
|
144
|
-
def set_leverage(exchange:, symbol:, leverage:, api_key_id: nil, market_type: nil)
|
|
145
|
-
op("set-leverage", exchange: exchange, apiKeyId: api_key_id,
|
|
146
|
-
symbol: symbol, leverage: leverage, marketType: market_type)
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
# Set margin mode (cross/isolated). WARNING.
|
|
150
|
-
def set_margin_mode(exchange:, margin_mode:, api_key_id: nil, symbol: nil, market_type: nil)
|
|
151
|
-
op("set-margin-mode", exchange: exchange, apiKeyId: api_key_id,
|
|
152
|
-
marginMode: margin_mode, symbol: symbol, marketType: market_type)
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
# Set position mode (one-way / hedge). WARNING.
|
|
156
|
-
def set_position_mode(exchange:, api_key_id: nil, hedged: nil, mode: nil, market_type: nil)
|
|
157
|
-
op("set-position-mode", exchange: exchange, apiKeyId: api_key_id,
|
|
158
|
-
hedged: hedged, mode: mode, marketType: market_type)
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
private
|
|
162
|
-
|
|
163
|
-
def op(path_op, **kwargs)
|
|
164
|
-
body = kwargs.reject { |_, v| v.nil? }
|
|
165
|
-
@http.post("/api/v1/private/#{path_op}", body)
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Melaya
|
|
4
|
+
# Live trading API — credentialed order placement, account state, and
|
|
5
|
+
# position management on a CONNECTED exchange.
|
|
6
|
+
#
|
|
7
|
+
# Every method POSTs to +https://api.melaya.org/api/v1/private/<op>+; the server
|
|
8
|
+
# resolves your connected exchange credential (referenced by +api_key_id+ — see
|
|
9
|
+
# AccountAPI#keys) and forwards the call to the venue through Melaya's in-house
|
|
10
|
+
# Rust engine. Responses share an envelope:
|
|
11
|
+
# +{ ok, exchange, operation, orderId, clientOrderId, payload, data, ... }+.
|
|
12
|
+
#
|
|
13
|
+
# WARNING: these hit the REAL venue with REAL funds. The write methods
|
|
14
|
+
# (create_order, cancel_order, amend_order, cancel_all_orders, cancel_plan_orders,
|
|
15
|
+
# close_position, set_leverage, set_margin_mode, set_position_mode) move money or
|
|
16
|
+
# change account state. For risk-free testing use the sim (paper) broker or a
|
|
17
|
+
# paper strategy instead.
|
|
18
|
+
class TradeAPI
|
|
19
|
+
def initialize(http)
|
|
20
|
+
@http = http
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# ── Account state (reads) ──────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
# Live account balance on a connected venue.
|
|
26
|
+
def balance(exchange:, api_key_id: nil, key_id: nil, market_type: nil, params: nil)
|
|
27
|
+
op("balance", exchange: exchange, apiKeyId: api_key_id, keyId: key_id,
|
|
28
|
+
marketType: market_type, params: params)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Live open positions.
|
|
32
|
+
def positions(exchange:, api_key_id: nil, market_type: nil, symbol: nil, params: nil)
|
|
33
|
+
op("positions", exchange: exchange, apiKeyId: api_key_id,
|
|
34
|
+
marketType: market_type, symbol: symbol, params: params)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Historical positions (venue-dependent).
|
|
38
|
+
def positions_history(exchange:, api_key_id: nil, market_type: nil, symbol: nil)
|
|
39
|
+
op("positions-history", exchange: exchange, apiKeyId: api_key_id,
|
|
40
|
+
marketType: market_type, symbol: symbol)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Resting (open) orders.
|
|
44
|
+
def open_orders(exchange:, api_key_id: nil, market_type: nil, symbol: nil)
|
|
45
|
+
op("open-orders", exchange: exchange, apiKeyId: api_key_id,
|
|
46
|
+
marketType: market_type, symbol: symbol)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# All orders (open + recent).
|
|
50
|
+
def orders(exchange:, api_key_id: nil, market_type: nil, symbol: nil)
|
|
51
|
+
op("orders", exchange: exchange, apiKeyId: api_key_id,
|
|
52
|
+
marketType: market_type, symbol: symbol)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Closed/filled orders.
|
|
56
|
+
def closed_orders(exchange:, api_key_id: nil, market_type: nil, symbol: nil)
|
|
57
|
+
op("closed-orders", exchange: exchange, apiKeyId: api_key_id,
|
|
58
|
+
marketType: market_type, symbol: symbol)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Your trade (fill) history.
|
|
62
|
+
def my_trades(exchange:, api_key_id: nil, market_type: nil, symbol: nil)
|
|
63
|
+
op("my-trades", exchange: exchange, apiKeyId: api_key_id,
|
|
64
|
+
marketType: market_type, symbol: symbol)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Extended trade history (venue-dependent).
|
|
68
|
+
def my_trades_history(exchange:, api_key_id: nil, market_type: nil, symbol: nil)
|
|
69
|
+
op("my-trades-history", exchange: exchange, apiKeyId: api_key_id,
|
|
70
|
+
marketType: market_type, symbol: symbol)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Resting conditional/plan (trigger) orders.
|
|
74
|
+
def plan_orders(exchange:, api_key_id: nil, market_type: nil, symbol: nil)
|
|
75
|
+
op("plan-orders", exchange: exchange, apiKeyId: api_key_id,
|
|
76
|
+
marketType: market_type, symbol: symbol)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Current leverage for a symbol.
|
|
80
|
+
def leverage(exchange:, api_key_id: nil, symbol: nil, market_type: nil)
|
|
81
|
+
op("leverage", exchange: exchange, apiKeyId: api_key_id,
|
|
82
|
+
symbol: symbol, marketType: market_type)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Leverage tiers / brackets for a symbol.
|
|
86
|
+
def leverage_tiers(exchange:, api_key_id: nil, symbol: nil, market_type: nil)
|
|
87
|
+
op("leverage-tiers", exchange: exchange, apiKeyId: api_key_id,
|
|
88
|
+
symbol: symbol, marketType: market_type)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# ── Order placement & management (LIVE writes — real funds) ───────────────
|
|
92
|
+
|
|
93
|
+
# Place a live order on the venue. WARNING: real money.
|
|
94
|
+
# stop_price, take_profit_price, and reduce_only are folded into +params+.
|
|
95
|
+
def create_order(exchange:, symbol:, side:, amount:,
|
|
96
|
+
api_key_id: nil, type: "market", price: nil,
|
|
97
|
+
market_type: nil, stop_price: nil, take_profit_price: nil,
|
|
98
|
+
reduce_only: nil, leverage: nil, client_order_id: nil, params: nil)
|
|
99
|
+
p = (params || {}).dup
|
|
100
|
+
p["stopPrice"] = stop_price unless stop_price.nil?
|
|
101
|
+
p["takeProfitPrice"] = take_profit_price unless take_profit_price.nil?
|
|
102
|
+
p["reduceOnly"] = reduce_only unless reduce_only.nil?
|
|
103
|
+
op("create-order",
|
|
104
|
+
exchange: exchange, apiKeyId: api_key_id, symbol: symbol,
|
|
105
|
+
side: side, amount: amount, type: type, price: price,
|
|
106
|
+
marketType: market_type, leverage: leverage,
|
|
107
|
+
clientOrderId: client_order_id, params: p.empty? ? nil : p)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Cancel a live order by id. WARNING.
|
|
111
|
+
def cancel_order(exchange:, api_key_id: nil, order_id: nil, client_order_id: nil,
|
|
112
|
+
symbol: nil, market_type: nil)
|
|
113
|
+
op("cancel-order", exchange: exchange, apiKeyId: api_key_id,
|
|
114
|
+
orderId: order_id, clientOrderId: client_order_id,
|
|
115
|
+
symbol: symbol, marketType: market_type)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Amend (modify) a live order. WARNING.
|
|
119
|
+
def amend_order(exchange:, api_key_id: nil, order_id: nil, symbol: nil,
|
|
120
|
+
amount: nil, price: nil)
|
|
121
|
+
op("amend-order", exchange: exchange, apiKeyId: api_key_id,
|
|
122
|
+
orderId: order_id, symbol: symbol, amount: amount, price: price)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Cancel every open order (optionally scoped to a symbol). WARNING.
|
|
126
|
+
def cancel_all_orders(exchange:, api_key_id: nil, symbol: nil, market_type: nil)
|
|
127
|
+
op("cancel-all-orders", exchange: exchange, apiKeyId: api_key_id,
|
|
128
|
+
symbol: symbol, marketType: market_type)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Cancel resting plan/trigger orders. WARNING.
|
|
132
|
+
def cancel_plan_orders(exchange:, api_key_id: nil, symbol: nil, market_type: nil)
|
|
133
|
+
op("cancel-plan-orders", exchange: exchange, apiKeyId: api_key_id,
|
|
134
|
+
symbol: symbol, marketType: market_type)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Close an open position (market reduce-only). WARNING.
|
|
138
|
+
def close_position(exchange:, symbol:, api_key_id: nil, market_type: nil)
|
|
139
|
+
op("close-position", exchange: exchange, apiKeyId: api_key_id,
|
|
140
|
+
symbol: symbol, marketType: market_type)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Set leverage for a symbol. WARNING.
|
|
144
|
+
def set_leverage(exchange:, symbol:, leverage:, api_key_id: nil, market_type: nil)
|
|
145
|
+
op("set-leverage", exchange: exchange, apiKeyId: api_key_id,
|
|
146
|
+
symbol: symbol, leverage: leverage, marketType: market_type)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Set margin mode (cross/isolated). WARNING.
|
|
150
|
+
def set_margin_mode(exchange:, margin_mode:, api_key_id: nil, symbol: nil, market_type: nil)
|
|
151
|
+
op("set-margin-mode", exchange: exchange, apiKeyId: api_key_id,
|
|
152
|
+
marginMode: margin_mode, symbol: symbol, marketType: market_type)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Set position mode (one-way / hedge). WARNING.
|
|
156
|
+
def set_position_mode(exchange:, api_key_id: nil, hedged: nil, mode: nil, market_type: nil)
|
|
157
|
+
op("set-position-mode", exchange: exchange, apiKeyId: api_key_id,
|
|
158
|
+
hedged: hedged, mode: mode, marketType: market_type)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
private
|
|
162
|
+
|
|
163
|
+
def op(path_op, **kwargs)
|
|
164
|
+
body = kwargs.reject { |_, v| v.nil? }
|
|
165
|
+
@http.post("/api/v1/private/#{path_op}", body)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
data/lib/melaya/version.rb
CHANGED