@metaflux/fluxaction 0.1.5 → 0.1.6

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.
@@ -0,0 +1,301 @@
1
+ import ccxt, { type gateio as GateCcxt } from 'ccxt'
2
+ import type {
3
+ ExchangeAdapter,
4
+ ExchangeCapabilities,
5
+ MarketInfo,
6
+ OrderInfo,
7
+ PositionInfo,
8
+ PositionSide,
9
+ } from '../../../core/exchange'
10
+ import { mapCcxtOrder } from '../spot/gate.mapOrder'
11
+
12
+ export interface GateFuturesAdapterConfig {
13
+ id: string
14
+ label?: string
15
+ apiKey: string
16
+ secret: string
17
+ settle?: 'usdt' | 'btc'
18
+ defaultLeverage?: number
19
+ }
20
+
21
+ export class GateFuturesAdapter implements ExchangeAdapter {
22
+ readonly id: string
23
+ readonly label: string
24
+ readonly capabilities: ExchangeCapabilities
25
+
26
+ private ex: GateCcxt
27
+ private settle: 'usdt' | 'btc'
28
+ private defaultLeverage?: number
29
+
30
+ constructor(cfg: GateFuturesAdapterConfig) {
31
+ this.id = cfg.id
32
+ this.label = cfg.label ?? 'gate-futures'
33
+ this.settle = cfg.settle ?? 'usdt'
34
+ this.defaultLeverage = cfg.defaultLeverage
35
+
36
+ this.ex = new ccxt.gateio({
37
+ apiKey: cfg.apiKey,
38
+ secret: cfg.secret,
39
+ enableRateLimit: true,
40
+ options: { defaultType: 'swap' },
41
+ }) as GateCcxt
42
+
43
+ this.capabilities = {
44
+ spot: { nativeTakeProfit: false, nativeStopLoss: false, nativeTrailingStop: false },
45
+ futures: { nativeTakeProfit: false, nativeStopLoss: false, nativeTrailingStop: false },
46
+ }
47
+ }
48
+
49
+ async loadMarkets(): Promise<void> {
50
+ await this.ex.loadMarkets()
51
+ }
52
+
53
+ hasSpotMarket(_symbol: string): boolean {
54
+ return false
55
+ }
56
+
57
+ hasFuturesMarket(symbol: string): boolean {
58
+ const m: any = (this.ex as any).markets?.[symbol]
59
+ return !!m && (m.type === 'swap' || m.swap === true)
60
+ }
61
+
62
+ getMarket(symbol: string): MarketInfo | undefined {
63
+ const m: any = (this.ex as any).markets?.[symbol]
64
+ if (!m) return undefined
65
+ return {
66
+ symbol: m.symbol,
67
+ base: m.base,
68
+ quote: m.quote,
69
+ type: 'futures',
70
+ precisionAmount: m.precision?.amount,
71
+ precisionPrice: m.precision?.price,
72
+ minCost: m.limits?.cost?.min,
73
+ minAmount: m.limits?.amount?.min,
74
+ }
75
+ }
76
+
77
+ private roundAmount(symbol: string, amount: number): number {
78
+ const m: any = (this.ex as any).markets?.[symbol]
79
+ if (!m) return amount
80
+ return Number((this.ex as any).amountToPrecision(symbol, amount))
81
+ }
82
+
83
+ private roundPrice(symbol: string, price: number): number {
84
+ const m: any = (this.ex as any).markets?.[symbol]
85
+ if (!m) return price
86
+ return Number((this.ex as any).priceToPrecision(symbol, price))
87
+ }
88
+
89
+ async fetchTicker(symbol: string): Promise<{ last: number }> {
90
+ const t: any = await (this.ex as any).fetchTicker(symbol)
91
+ return { last: Number(t?.last ?? 0) }
92
+ }
93
+
94
+ async fetchBalance(): Promise<Record<string, { free: number; used: number; total: number }>> {
95
+ const bal: any = await (this.ex as any).fetchBalance()
96
+ return bal as any
97
+ }
98
+
99
+ async fetchOpenOrders(symbol?: string): Promise<OrderInfo[]> {
100
+ const orders: any[] = await (this.ex as any).fetchOpenOrders(symbol)
101
+ return orders.map((o: any) => mapCcxtOrder(o.symbol, o))
102
+ }
103
+
104
+ async cancelOrder(symbol: string, orderId: string): Promise<void> {
105
+ await (this.ex as any).cancelOrder(orderId, symbol)
106
+ }
107
+
108
+ // ===== helpers =====
109
+ private getMarketRaw(symbol: string): any {
110
+ const m: any = (this.ex as any).markets?.[symbol]
111
+ if (!m) throw new Error(`gate-futures: unknown market ${symbol}`)
112
+ return m
113
+ }
114
+
115
+ private getContractSize(symbol: string): number {
116
+ const m = this.getMarketRaw(symbol)
117
+ const cs = Number(m.contractSize ?? 1)
118
+ return cs > 0 ? cs : 1
119
+ }
120
+
121
+ /**
122
+ * Конвертирует USDT стоимость в дробное количество контрактов
123
+ * Gate API поддерживает дробные размеры через X-Gate-Size-Decimal: 1
124
+ */
125
+ private costUSDTToContracts(symbol: string, costUSDT: number, price: number): number {
126
+ const contractSize = this.getContractSize(symbol)
127
+ const notionalPerContract = price * contractSize
128
+
129
+ // Дробное количество контрактов (может быть 0.17, 1.71 и т.д.)
130
+ const contractsFloat = costUSDT / notionalPerContract
131
+
132
+ if (contractsFloat < 0.0001) {
133
+ throw new Error(
134
+ `gate-futures: costUSDT too small. ` +
135
+ `Minimum notional per contract: ${notionalPerContract.toFixed(2)} USDT, got ${costUSDT}`,
136
+ )
137
+ }
138
+
139
+ return contractsFloat
140
+ }
141
+
142
+ async quoteToBaseQty(params: { symbol: string; quoteCost: number; price?: number }): Promise<number> {
143
+ const { symbol, quoteCost } = params
144
+ if (!quoteCost || quoteCost <= 0) throw new Error(`gate-futures quoteCost must be > 0, got ${quoteCost}`)
145
+
146
+ const p = params.price ?? (await this.fetchTicker(symbol)).last
147
+ if (!p || p <= 0) throw new Error(`gate-futures invalid price for ${symbol}: ${p}`)
148
+
149
+ // Возвращаем дробное количество контрактов
150
+ const contracts = this.costUSDTToContracts(symbol, quoteCost, p)
151
+ return contracts
152
+ }
153
+
154
+ async baseQtyToQuote(params: { symbol: string; baseQty: number; price?: number }): Promise<number> {
155
+ // baseQty здесь = contracts (может быть дробное)
156
+ if (!params.baseQty || params.baseQty <= 0) return 0
157
+
158
+ const p = params.price ?? (await this.fetchTicker(params.symbol)).last
159
+ if (!p || p <= 0) throw new Error(`gate-futures invalid price for ${params.symbol}: ${p}`)
160
+
161
+ const contractSize = this.getContractSize(params.symbol)
162
+ // contracts * contractSize * price = notional in USDT
163
+ return params.baseQty * contractSize * p
164
+ }
165
+
166
+ // ===== spot methods not supported =====
167
+ async marketBuySpot(): Promise<OrderInfo> {
168
+ throw new Error('gate-futures: spot not supported')
169
+ }
170
+ async marketSellSpot(): Promise<OrderInfo> {
171
+ throw new Error('gate-futures: spot not supported')
172
+ }
173
+ async limitOrderSpot(): Promise<OrderInfo> {
174
+ throw new Error('gate-futures: spot not supported')
175
+ }
176
+
177
+ // ===== futures =====
178
+ async openFutures(params: {
179
+ symbol: string
180
+ side: PositionSide
181
+ costUSDT: number
182
+ leverage?: number
183
+ price?: number | null
184
+ }): Promise<OrderInfo> {
185
+ const symbol = params.symbol
186
+
187
+ // Set leverage
188
+ const leverage = params.leverage ?? this.defaultLeverage
189
+ if (leverage) {
190
+ try {
191
+ await (this.ex as any).setLeverage(leverage, symbol)
192
+ } catch {
193
+ // ignore best-effort
194
+ }
195
+ }
196
+
197
+ // Конвертируем costUSDT -> contracts (дробное)
198
+ const last = (await this.fetchTicker(symbol)).last
199
+ const contracts = await this.quoteToBaseQty({ symbol, quoteCost: params.costUSDT, price: last })
200
+ const actualCostUSDT = await this.baseQtyToQuote({ symbol, baseQty: contracts, price: last })
201
+
202
+ console.log(
203
+ `[${this.id}] openFutures: ` +
204
+ `requested ${params.costUSDT} USDT -> ` +
205
+ `calculated ${contracts.toFixed(8)} contracts -> ` +
206
+ `notional ${actualCostUSDT.toFixed(2)} USDT`,
207
+ )
208
+
209
+ const side = params.side === 'long' ? 'buy' : 'sell'
210
+ const type: 'market' | 'limit' = typeof params.price === 'number' ? 'limit' : 'market'
211
+ const price = typeof params.price === 'number' ? this.roundPrice(symbol, params.price) : undefined
212
+
213
+ // DEBUG: выводим то, что передаём в createOrder
214
+ console.log(`[${this.id}] createOrder args: symbol=${symbol}, type=${type}, side=${side}, amount=${contracts}, price=${price}`)
215
+
216
+ // Создаём ордер с дробным количеством контрактов
217
+ const order: any = await (this.ex as any).createOrder(symbol, type, side, contracts, price, {
218
+ settle: this.settle,
219
+ })
220
+
221
+ console.log(`[${this.id}] createOrder response: id=${order.id}, filled=${order.filled}, cost=${order.cost}, status=${order.status}`)
222
+
223
+ // fetchOrder чтобы получить точные filled values
224
+ const filledOrder: any = await (this.ex as any).fetchOrder(order.id, symbol).catch(() => order)
225
+
226
+ console.log(
227
+ `[${this.id}] fetchOrder response: id=${filledOrder.id}, filled=${filledOrder.filled}, cost=${filledOrder.cost}, average=${filledOrder.average}`,
228
+ )
229
+
230
+ return mapCcxtOrder(symbol, filledOrder)
231
+ }
232
+
233
+ async closeFutures(params: {
234
+ symbol: string
235
+ side: PositionSide
236
+ closeMode: 'all' | 'cost'
237
+ costUSDT?: number
238
+ }): Promise<OrderInfo> {
239
+ const symbol = params.symbol
240
+ const closeSide = params.side === 'long' ? 'sell' : 'buy'
241
+
242
+ if (params.closeMode === 'cost') {
243
+ if (!params.costUSDT) throw new Error('gate-futures closeMode=cost requires costUSDT')
244
+
245
+ const last = (await this.fetchTicker(symbol)).last
246
+ const contracts = await this.quoteToBaseQty({
247
+ symbol,
248
+ quoteCost: params.costUSDT,
249
+ price: last,
250
+ })
251
+
252
+ const order: any = await (this.ex as any).createOrder(symbol, 'market', closeSide, contracts, undefined, {
253
+ settle: this.settle,
254
+ reduceOnly: true,
255
+ })
256
+
257
+ const filledOrder: any = await (this.ex as any).fetchOrder(order.id, symbol).catch(() => order)
258
+ return mapCcxtOrder(symbol, filledOrder)
259
+ }
260
+
261
+ // closeMode === 'all': close entire position
262
+ const positions: any[] = await (this.ex as any).fetchPositions?.([symbol]).catch(() => []) ?? []
263
+ const p0: any = positions.find((p) => p?.symbol === symbol) ?? positions[0]
264
+ const rawSize = Math.abs(Number(p0?.contracts ?? p0?.size ?? p0?.positionAmt ?? 0))
265
+
266
+ if (!rawSize || rawSize <= 0) {
267
+ throw new Error(`gate-futures closeMode=all: no open position for ${symbol}`)
268
+ }
269
+
270
+ const order: any = await (this.ex as any).createOrder(symbol, 'market', closeSide, rawSize, undefined, {
271
+ settle: this.settle,
272
+ reduceOnly: true,
273
+ })
274
+
275
+ const filledOrder: any = await (this.ex as any).fetchOrder(order.id, symbol).catch(() => order)
276
+ return mapCcxtOrder(symbol, filledOrder)
277
+ }
278
+
279
+ async fetchPositions(symbol?: string): Promise<PositionInfo[]> {
280
+ const positions: any[] = await (this.ex as any).fetchPositions?.(symbol ? [symbol] : undefined).catch(() => []) ?? []
281
+ if (!Array.isArray(positions)) return []
282
+
283
+ return positions.map((p: any) => {
284
+ const rawSize = Number(p?.contracts ?? p?.size ?? p?.positionAmt ?? 0)
285
+ const side: PositionSide =
286
+ p?.side === 'long' || p?.side === 'short'
287
+ ? p.side
288
+ : rawSize >= 0
289
+ ? 'long'
290
+ : 'short'
291
+
292
+ return {
293
+ symbol: p?.symbol ?? symbol ?? '',
294
+ side,
295
+ size: Math.abs(rawSize),
296
+ entryPrice: Number(p?.entryPrice ?? p?.entry ?? 0),
297
+ raw: p,
298
+ }
299
+ })
300
+ }
301
+ }
@@ -0,0 +1,2 @@
1
+ export { GateSpotAdapter, type GateSpotAdapterConfig } from './spot/GateSpotAdapter'
2
+ export { GateFuturesAdapter, type GateFuturesAdapterConfig } from './futures/GateFuturesAdapter'
@@ -0,0 +1,208 @@
1
+ import ccxt, { type gateio as GateCcxt } from 'ccxt'
2
+ import type {
3
+ ExchangeAdapter,
4
+ ExchangeCapabilities,
5
+ MarketInfo,
6
+ OrderInfo,
7
+ PositionInfo,
8
+ } from '../../../core/exchange'
9
+ import { mapCcxtOrder } from './gate.mapOrder'
10
+
11
+ export interface GateSpotAdapterConfig {
12
+ id: string
13
+ label?: string
14
+ apiKey: string
15
+ secret: string
16
+ }
17
+
18
+ export class GateSpotAdapter implements ExchangeAdapter {
19
+ readonly id: string
20
+ readonly label: string
21
+ readonly capabilities: ExchangeCapabilities
22
+ private ex: GateCcxt
23
+
24
+ constructor(cfg: GateSpotAdapterConfig) {
25
+ this.id = cfg.id
26
+ this.label = cfg.label ?? 'gate-spot'
27
+
28
+ this.ex = new ccxt.gateio({
29
+ apiKey: cfg.apiKey,
30
+ secret: cfg.secret,
31
+ enableRateLimit: true,
32
+ options: {
33
+ defaultType: 'spot',
34
+ createMarketBuyOrderRequiresPrice: false,
35
+ },
36
+ }) as GateCcxt
37
+
38
+ this.capabilities = {
39
+ spot: { nativeTakeProfit: false, nativeStopLoss: false, nativeTrailingStop: false },
40
+ futures: { nativeTakeProfit: false, nativeStopLoss: false, nativeTrailingStop: false },
41
+ }
42
+ }
43
+
44
+ async loadMarkets(): Promise<void> {
45
+ await this.ex.loadMarkets()
46
+ }
47
+
48
+ hasSpotMarket(symbol: string): boolean {
49
+ const m: any = (this.ex as any).markets?.[symbol]
50
+ return !!m && m.type === 'spot'
51
+ }
52
+
53
+ hasFuturesMarket(_symbol: string): boolean {
54
+ return false
55
+ }
56
+
57
+ getMarket(symbol: string): MarketInfo | undefined {
58
+ const m: any = (this.ex as any).markets?.[symbol]
59
+ if (!m) return undefined
60
+ return {
61
+ symbol: m.symbol,
62
+ base: m.base,
63
+ quote: m.quote,
64
+ type: 'spot',
65
+ precisionAmount: m.precision?.amount,
66
+ precisionPrice: m.precision?.price,
67
+ minCost: m.limits?.cost?.min,
68
+ minAmount: m.limits?.amount?.min,
69
+ }
70
+ }
71
+
72
+ private roundAmount(symbol: string, amount: number): number {
73
+ const m: any = (this.ex as any).markets?.[symbol]
74
+ if (!m) return amount
75
+ return Number((this.ex as any).amountToPrecision(symbol, amount))
76
+ }
77
+
78
+ private roundPrice(symbol: string, price: number): number {
79
+ const m: any = (this.ex as any).markets?.[symbol]
80
+ if (!m) return price
81
+ return Number((this.ex as any).priceToPrecision(symbol, price))
82
+ }
83
+
84
+ async fetchTicker(symbol: string): Promise<{ last: number }> {
85
+ const t: any = await (this.ex as any).fetchTicker(symbol)
86
+ return { last: Number(t?.last ?? 0) }
87
+ }
88
+
89
+ async fetchBalance(): Promise<Record<string, { free: number; used: number; total: number }>> {
90
+ const bal: any = await (this.ex as any).fetchBalance()
91
+ return bal as any
92
+ }
93
+
94
+ async fetchOpenOrders(symbol?: string): Promise<OrderInfo[]> {
95
+ const orders: any[] = await (this.ex as any).fetchOpenOrders(symbol)
96
+ return orders.map((o: any) => mapCcxtOrder(o.symbol, o))
97
+ }
98
+
99
+ async cancelOrder(symbol: string, orderId: string): Promise<void> {
100
+ await (this.ex as any).cancelOrder(orderId, symbol)
101
+ }
102
+
103
+ async quoteToBaseQty(params: { symbol: string; quoteCost: number; price?: number }): Promise<number> {
104
+ const { symbol, quoteCost } = params
105
+ if (!quoteCost || quoteCost <= 0) throw new Error(`gate-spot quoteCost must be > 0, got ${quoteCost}`)
106
+
107
+ const p = params.price ?? (await this.fetchTicker(symbol)).last
108
+ if (!p || p <= 0) throw new Error(`gate-spot invalid price for ${symbol}: ${p}`)
109
+
110
+ const m = this.getMarket(symbol)
111
+
112
+ // 1) первичное значение и округление вниз (как обычно делает ccxt)
113
+ let baseQty = this.roundAmount(symbol, quoteCost / p)
114
+
115
+ // 2) minAmount
116
+ if (m?.minAmount && baseQty < m.minAmount) {
117
+ baseQty = this.roundAmount(symbol, m.minAmount)
118
+ }
119
+
120
+ // 3) minCost (ключевой фикс): если из-за округления ниже minCost — увеличиваем на один шаг
121
+ if (m?.minCost) {
122
+ const cost = baseQty * p
123
+ if (cost + 1e-12 < m.minCost) {
124
+ // вычисляем 1 шаг по precision.amount
125
+ const prec = m.precisionAmount
126
+ if (typeof prec === 'number') {
127
+ const step = Math.pow(10, -prec)
128
+ baseQty = this.roundAmount(symbol, baseQty + step)
129
+ } else {
130
+ // fallback: небольшой буфер по quoteCost
131
+ baseQty = this.roundAmount(symbol, (quoteCost * 1.002) / p)
132
+ }
133
+ }
134
+ if (baseQty * p + 1e-12 < m.minCost) {
135
+ throw new Error(`gate-spot violates minCost: ${baseQty * p} < ${m.minCost} ${m.quote}`)
136
+ }
137
+ }
138
+
139
+ return baseQty
140
+ }
141
+
142
+ async baseQtyToQuote(params: { symbol: string; baseQty: number; price?: number }): Promise<number> {
143
+ const qty = this.roundAmount(params.symbol, params.baseQty)
144
+ if (!qty || qty <= 0) return 0
145
+ const p = params.price ?? (await this.fetchTicker(params.symbol)).last
146
+ if (!p || p <= 0) throw new Error(`gate-spot invalid price for ${params.symbol}: ${p}`)
147
+ return qty * p
148
+ }
149
+
150
+ // spot trading
151
+ async marketBuySpot(symbol: string, quoteCost: number): Promise<OrderInfo> {
152
+ const m = this.getMarket(symbol)
153
+ if (m?.minCost && quoteCost < m.minCost) {
154
+ throw new Error(`gate-spot buy violates minCost: ${quoteCost} < ${m.minCost} ${m.quote}`)
155
+ }
156
+
157
+ const order: any = await (this.ex as any).createMarketBuyOrderWithCost(symbol, quoteCost, {
158
+ createMarketBuyOrderRequiresPrice: false,
159
+ })
160
+
161
+ return mapCcxtOrder(symbol, order)
162
+ }
163
+
164
+ async marketSellSpot(symbol: string, baseAmount: number): Promise<OrderInfo> {
165
+ const amount = this.roundAmount(symbol, baseAmount)
166
+ if (!amount || amount <= 0) throw new Error(`gate-spot sell amount too small: ${amount}`)
167
+
168
+ const m = this.getMarket(symbol)
169
+ if (m?.minAmount && amount < m.minAmount) {
170
+ throw new Error(`gate-spot sell violates minAmount: ${amount} < ${m.minAmount} ${m.base}`)
171
+ }
172
+
173
+ const order: any = await (this.ex as any).createOrder(symbol, 'market', 'sell', amount)
174
+ return mapCcxtOrder(symbol, order)
175
+ }
176
+
177
+ async limitOrderSpot(symbol: string, side: 'buy' | 'sell', baseAmount: number, price: number): Promise<OrderInfo> {
178
+ const amount = this.roundAmount(symbol, baseAmount)
179
+ const p = this.roundPrice(symbol, price)
180
+
181
+ if (!amount || amount <= 0) throw new Error(`gate-spot limit amount too small: ${amount}`)
182
+ if (!p || p <= 0) throw new Error(`gate-spot limit price invalid: ${p}`)
183
+
184
+ const m = this.getMarket(symbol)
185
+ if (m?.minAmount && amount < m.minAmount) {
186
+ throw new Error(`gate-spot limit violates minAmount: ${amount} < ${m.minAmount} ${m.base}`)
187
+ }
188
+ if (m?.minCost && amount * p < m.minCost) {
189
+ throw new Error(`gate-spot limit violates minCost: ${amount * p} < ${m.minCost} ${m.quote}`)
190
+ }
191
+
192
+ const order: any = await (this.ex as any).createOrder(symbol, 'limit', side, amount, p)
193
+ return mapCcxtOrder(symbol, order)
194
+ }
195
+
196
+ // futures not supported
197
+ async openFutures(): Promise<OrderInfo> {
198
+ throw new Error('gate-spot: futures not supported')
199
+ }
200
+ async closeFutures(): Promise<OrderInfo> {
201
+ throw new Error('gate-spot: futures not supported')
202
+ }
203
+
204
+ // positions for spot: empty (НЕ используем ccxt Position typing)
205
+ async fetchPositions(): Promise<PositionInfo[]> {
206
+ return []
207
+ }
208
+ }
@@ -0,0 +1,22 @@
1
+ import type { OrderInfo } from '../../../core/exchange'
2
+
3
+ export function mapCcxtOrder(symbol: string, o: any): OrderInfo {
4
+ const price = o.price ?? o.average ?? o.avgPrice
5
+
6
+ // ВАЖНО: в ccxt amount обычно base, filled тоже base, cost = quote.
7
+ const amount =
8
+ typeof o.filled === 'number' && o.filled > 0 ? o.filled :
9
+ typeof o.amount === 'number' && o.amount > 0 ? o.amount :
10
+ 0
11
+
12
+ return {
13
+ id: String(o.id),
14
+ symbol,
15
+ side: o.side,
16
+ type: o.type,
17
+ price,
18
+ amount,
19
+ status: o.status,
20
+ raw: o,
21
+ }
22
+ }
package/src/index.ts CHANGED
@@ -1,138 +1,33 @@
1
- import type { ExchangeAdapter } from './core/exchange'
2
- import { RiskManager } from './core/RiskManager'
3
- import {
4
- type TakeProfitOrder,
5
- type StopLossOrder,
6
- type TrailingStopOrder,
7
- } from './core/logicalOrders'
8
- import { GateSpotAdapter } from './exchanges/GateSpotAdapter'
9
-
10
- export interface FluxActionConfig {
11
- okx?: {
12
- apiKey: string
13
- secret: string
14
- passphrase: string
15
- isDemo?: boolean
16
- }
17
- gate?: {
18
- apiKey: string
19
- secret: string
20
- }
21
- risk?: {
22
- pollIntervalMs?: number
23
- }
24
- }
25
-
26
- export class FluxAction {
27
- private adapters: Record<string, ExchangeAdapter> = {}
28
- private risk: RiskManager
29
-
30
- constructor(cfg: FluxActionConfig) {
31
- if (cfg.gate) {
32
- const gate = new GateSpotAdapter({
33
- id: 'gate-spot',
34
- apiKey: cfg.gate.apiKey,
35
- secret: cfg.gate.secret,
36
- })
37
- this.adapters[gate.id] = gate
38
- }
39
-
40
- this.risk = new RiskManager({
41
- exchanges: this.adapters,
42
- pollIntervalMs: cfg.risk?.pollIntervalMs,
43
- })
44
- }
45
-
46
- async init(): Promise<void> {
47
- await Promise.all(Object.values(this.adapters).map((a) => a.loadMarkets()))
48
- }
49
-
50
- getExchange(id: string): ExchangeAdapter | undefined {
51
- return this.adapters[id]
52
- }
53
-
54
- async openSpotPosition(params: {
55
- exchangeId: string
56
- symbol: string
57
- quoteCost: number
58
- }): Promise<{ orderId: string; filledBase: number; avgPrice: number }> {
59
- const ex = this.requireExchange(params.exchangeId)
60
- const order = await ex.marketBuySpot(params.symbol, params.quoteCost)
61
-
62
- return {
63
- orderId: order.id,
64
- filledBase: order.amount,
65
- avgPrice: order.price ?? 0,
66
- }
67
- }
68
-
69
- async closeSpotMarket(params: {
70
- exchangeId: string
71
- symbol: string
72
- baseQty: number
73
- }): Promise<{ orderId: string }> {
74
- const ex = this.requireExchange(params.exchangeId)
75
- const order = await ex.marketSellSpot(params.symbol, params.baseQty)
76
- return { orderId: order.id }
77
- }
78
-
79
- async placeSpotLimit(params: {
80
- exchangeId: string
81
- symbol: string
82
- side: 'buy' | 'sell'
83
- baseQty: number
84
- price: number
85
- }): Promise<{ orderId: string }> {
86
- const ex = this.requireExchange(params.exchangeId)
87
- const order = await ex.limitOrderSpot(
88
- params.symbol,
89
- params.side,
90
- params.baseQty,
91
- params.price,
92
- )
93
- return { orderId: order.id }
94
- }
1
+ export { FluxAction, type FluxActionConfig } from './FluxAction'
95
2
 
96
- async placeTakeProfit(o: TakeProfitOrder): Promise<{ id: string }> {
97
- await this.risk.placeTakeProfit(o)
98
- return { id: o.id }
99
- }
100
-
101
- async placeStopLoss(o: StopLossOrder): Promise<{ id: string }> {
102
- await this.risk.placeStopLoss(o)
103
- return { id: o.id }
104
- }
105
-
106
- async placeTrailingStop(o: TrailingStopOrder): Promise<{ id: string }> {
107
- await this.risk.placeTrailingStop(o)
108
- return { id: o.id }
109
- }
110
-
111
- async cancelLogical(id: string): Promise<void> {
112
- await this.risk.cancel(id)
113
- }
114
-
115
- getActiveLogicalOrders() {
116
- return this.risk.getActiveOrders()
117
- }
118
-
119
- private requireExchange(id: string): ExchangeAdapter {
120
- const ex = this.adapters[id]
121
- if (!ex) throw new Error(`Exchange ${id} is not configured`)
122
- return ex
123
- }
124
- }
125
-
126
- export { RiskManager, type RiskManagerConfig } from './core/RiskManager'
127
3
  export type {
128
4
  ExchangeAdapter,
129
5
  ExchangeCapabilities,
130
6
  MarketInfo,
131
7
  OrderInfo,
132
8
  PositionInfo,
9
+ PositionSide,
10
+ MarketType,
11
+ Side,
133
12
  } from './core/exchange'
134
13
 
14
+ export type { OrderKind, MarketOrLimit, SpotSide } from './core/orders'
15
+
16
+ export type {
17
+ LogicalOrder,
18
+ TakeProfitOrder,
19
+ StopLossOrder,
20
+ TrailingStopOrder,
21
+ LogicalOrderKind,
22
+ MarketScope,
23
+ FuturesPositionSide,
24
+ ExitMode,
25
+ } from './core/logicalOrders'
26
+
27
+ export { GateClient } from './clients/GateClient'
135
28
  export {
136
29
  GateSpotAdapter,
137
30
  type GateSpotAdapterConfig,
138
- } from './exchanges/GateSpotAdapter'
31
+ GateFuturesAdapter,
32
+ type GateFuturesAdapterConfig,
33
+ } from './exchanges/gate'