2435 lines
100 KiB
Go
2435 lines
100 KiB
Go
package ccxt
|
|
|
|
// PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
|
// https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
|
|
|
type gemini struct {
|
|
Exchange
|
|
|
|
}
|
|
|
|
func NewGeminiCore() gemini {
|
|
p := gemini{}
|
|
setDefaults(&p)
|
|
return p
|
|
}
|
|
|
|
func (this *gemini) Describe() interface{} {
|
|
return this.DeepExtend(this.Exchange.Describe(), map[string]interface{} {
|
|
"id": "gemini",
|
|
"name": "Gemini",
|
|
"countries": []interface{}{"US"},
|
|
"rateLimit": 100,
|
|
"version": "v1",
|
|
"pro": true,
|
|
"has": map[string]interface{} {
|
|
"CORS": nil,
|
|
"spot": true,
|
|
"margin": false,
|
|
"swap": true,
|
|
"future": false,
|
|
"option": false,
|
|
"addMargin": false,
|
|
"cancelOrder": true,
|
|
"closeAllPositions": false,
|
|
"closePosition": false,
|
|
"createDepositAddress": true,
|
|
"createMarketOrder": false,
|
|
"createOrder": true,
|
|
"createReduceOnlyOrder": false,
|
|
"fetchBalance": true,
|
|
"fetchBidsAsks": false,
|
|
"fetchBorrowRateHistories": false,
|
|
"fetchBorrowRateHistory": false,
|
|
"fetchClosedOrders": false,
|
|
"fetchCrossBorrowRate": false,
|
|
"fetchCrossBorrowRates": false,
|
|
"fetchCurrencies": true,
|
|
"fetchDepositAddress": true,
|
|
"fetchDepositAddresses": false,
|
|
"fetchDepositAddressesByNetwork": true,
|
|
"fetchDepositsWithdrawals": true,
|
|
"fetchFundingHistory": false,
|
|
"fetchFundingRate": false,
|
|
"fetchFundingRateHistory": false,
|
|
"fetchFundingRates": false,
|
|
"fetchIndexOHLCV": false,
|
|
"fetchIsolatedBorrowRate": false,
|
|
"fetchIsolatedBorrowRates": false,
|
|
"fetchLeverage": false,
|
|
"fetchLeverageTiers": false,
|
|
"fetchMarginMode": false,
|
|
"fetchMarkets": true,
|
|
"fetchMarkOHLCV": false,
|
|
"fetchMyTrades": true,
|
|
"fetchOHLCV": true,
|
|
"fetchOpenInterestHistory": false,
|
|
"fetchOpenOrders": true,
|
|
"fetchOrder": true,
|
|
"fetchOrderBook": true,
|
|
"fetchOrders": false,
|
|
"fetchPosition": false,
|
|
"fetchPositionMode": false,
|
|
"fetchPositions": false,
|
|
"fetchPositionsRisk": false,
|
|
"fetchPremiumIndexOHLCV": false,
|
|
"fetchTicker": true,
|
|
"fetchTickers": true,
|
|
"fetchTrades": true,
|
|
"fetchTradingFee": false,
|
|
"fetchTradingFees": true,
|
|
"fetchTransactions": "emulated",
|
|
"postOnly": true,
|
|
"reduceMargin": false,
|
|
"sandbox": true,
|
|
"setLeverage": false,
|
|
"setMarginMode": false,
|
|
"setPositionMode": false,
|
|
"withdraw": true,
|
|
},
|
|
"urls": map[string]interface{} {
|
|
"logo": "https://user-images.githubusercontent.com/1294454/27816857-ce7be644-6096-11e7-82d6-3c257263229c.jpg",
|
|
"api": map[string]interface{} {
|
|
"public": "https://api.gemini.com",
|
|
"private": "https://api.gemini.com",
|
|
"web": "https://docs.gemini.com",
|
|
"webExchange": "https://exchange.gemini.com",
|
|
},
|
|
"www": "https://gemini.com/",
|
|
"doc": []interface{}{"https://docs.gemini.com/rest-api", "https://docs.sandbox.gemini.com"},
|
|
"test": map[string]interface{} {
|
|
"public": "https://api.sandbox.gemini.com",
|
|
"private": "https://api.sandbox.gemini.com",
|
|
"web": "https://docs.gemini.com",
|
|
"webExchange": "https://exchange.gemini.com",
|
|
},
|
|
"fees": []interface{}{"https://gemini.com/api-fee-schedule", "https://gemini.com/trading-fees", "https://gemini.com/transfer-fees"},
|
|
},
|
|
"api": map[string]interface{} {
|
|
"webExchange": map[string]interface{} {
|
|
"get": []interface{}{""},
|
|
},
|
|
"web": map[string]interface{} {
|
|
"get": []interface{}{"rest-api"},
|
|
},
|
|
"public": map[string]interface{} {
|
|
"get": map[string]interface{} {
|
|
"v1/symbols": 5,
|
|
"v1/symbols/details/{symbol}": 5,
|
|
"v1/staking/rates": 5,
|
|
"v1/pubticker/{symbol}": 5,
|
|
"v2/ticker/{symbol}": 5,
|
|
"v2/candles/{symbol}/{timeframe}": 5,
|
|
"v1/trades/{symbol}": 5,
|
|
"v1/auction/{symbol}": 5,
|
|
"v1/auction/{symbol}/history": 5,
|
|
"v1/pricefeed": 5,
|
|
"v1/book/{symbol}": 5,
|
|
"v1/earn/rates": 5,
|
|
},
|
|
},
|
|
"private": map[string]interface{} {
|
|
"post": map[string]interface{} {
|
|
"v1/staking/unstake": 1,
|
|
"v1/staking/stake": 1,
|
|
"v1/staking/rewards": 1,
|
|
"v1/staking/history": 1,
|
|
"v1/order/new": 1,
|
|
"v1/order/cancel": 1,
|
|
"v1/wrap/{symbol}": 1,
|
|
"v1/order/cancel/session": 1,
|
|
"v1/order/cancel/all": 1,
|
|
"v1/order/status": 1,
|
|
"v1/orders": 1,
|
|
"v1/mytrades": 1,
|
|
"v1/notionalvolume": 1,
|
|
"v1/tradevolume": 1,
|
|
"v1/clearing/new": 1,
|
|
"v1/clearing/status": 1,
|
|
"v1/clearing/cancel": 1,
|
|
"v1/clearing/confirm": 1,
|
|
"v1/balances": 1,
|
|
"v1/balances/staking": 1,
|
|
"v1/notionalbalances/{currency}": 1,
|
|
"v1/transfers": 1,
|
|
"v1/addresses/{network}": 1,
|
|
"v1/deposit/{network}/newAddress": 1,
|
|
"v1/deposit/{currency}/newAddress": 1,
|
|
"v1/withdraw/{currency}": 1,
|
|
"v1/account/transfer/{currency}": 1,
|
|
"v1/payments/addbank": 1,
|
|
"v1/payments/methods": 1,
|
|
"v1/payments/sen/withdraw": 1,
|
|
"v1/balances/earn": 1,
|
|
"v1/earn/interest": 1,
|
|
"v1/earn/history": 1,
|
|
"v1/approvedAddresses/{network}/request": 1,
|
|
"v1/approvedAddresses/account/{network}": 1,
|
|
"v1/approvedAddresses/{network}/remove": 1,
|
|
"v1/account": 1,
|
|
"v1/account/create": 1,
|
|
"v1/account/list": 1,
|
|
"v1/heartbeat": 1,
|
|
"v1/roles": 1,
|
|
},
|
|
},
|
|
},
|
|
"precisionMode": TICK_SIZE,
|
|
"fees": map[string]interface{} {
|
|
"trading": map[string]interface{} {
|
|
"taker": 0.004,
|
|
"maker": 0.002,
|
|
},
|
|
},
|
|
"httpExceptions": map[string]interface{} {
|
|
"400": BadRequest,
|
|
"403": PermissionDenied,
|
|
"404": OrderNotFound,
|
|
"406": InsufficientFunds,
|
|
"429": RateLimitExceeded,
|
|
"500": ExchangeError,
|
|
"502": ExchangeNotAvailable,
|
|
"503": OnMaintenance,
|
|
},
|
|
"timeframes": map[string]interface{} {
|
|
"1m": "1m",
|
|
"5m": "5m",
|
|
"15m": "15m",
|
|
"30m": "30m",
|
|
"1h": "1hr",
|
|
"6h": "6hr",
|
|
"1d": "1day",
|
|
},
|
|
"exceptions": map[string]interface{} {
|
|
"exact": map[string]interface{} {
|
|
"AuctionNotOpen": BadRequest,
|
|
"ClientOrderIdTooLong": BadRequest,
|
|
"ClientOrderIdMustBeString": BadRequest,
|
|
"ConflictingOptions": BadRequest,
|
|
"EndpointMismatch": BadRequest,
|
|
"EndpointNotFound": BadRequest,
|
|
"IneligibleTiming": BadRequest,
|
|
"InsufficientFunds": InsufficientFunds,
|
|
"InvalidJson": BadRequest,
|
|
"InvalidNonce": InvalidNonce,
|
|
"InvalidApiKey": AuthenticationError,
|
|
"InvalidOrderType": InvalidOrder,
|
|
"InvalidPrice": InvalidOrder,
|
|
"InvalidQuantity": InvalidOrder,
|
|
"InvalidSide": InvalidOrder,
|
|
"InvalidSignature": AuthenticationError,
|
|
"InvalidSymbol": BadRequest,
|
|
"InvalidTimestampInPayload": BadRequest,
|
|
"Maintenance": OnMaintenance,
|
|
"MarketNotOpen": InvalidOrder,
|
|
"MissingApikeyHeader": AuthenticationError,
|
|
"MissingOrderField": InvalidOrder,
|
|
"MissingRole": AuthenticationError,
|
|
"MissingPayloadHeader": AuthenticationError,
|
|
"MissingSignatureHeader": AuthenticationError,
|
|
"NoSSL": AuthenticationError,
|
|
"OptionsMustBeArray": BadRequest,
|
|
"OrderNotFound": OrderNotFound,
|
|
"RateLimit": RateLimitExceeded,
|
|
"System": ExchangeError,
|
|
"UnsupportedOption": BadRequest,
|
|
},
|
|
"broad": map[string]interface{} {
|
|
"The Gemini Exchange is currently undergoing maintenance.": OnMaintenance,
|
|
"We are investigating technical issues with the Gemini Exchange.": ExchangeNotAvailable,
|
|
"Internal Server Error": ExchangeNotAvailable,
|
|
},
|
|
},
|
|
"options": map[string]interface{} {
|
|
"fetchMarketsMethod": "fetch_markets_from_api",
|
|
"fetchMarketFromWebRetries": 10,
|
|
"fetchMarketsFromAPI": map[string]interface{} {
|
|
"fetchDetailsForAllSymbols": false,
|
|
"quoteCurrencies": []interface{}{"USDT", "GUSD", "USD", "DAI", "EUR", "GBP", "SGD", "BTC", "ETH", "LTC", "BCH"},
|
|
},
|
|
"fetchMarkets": map[string]interface{} {
|
|
"webApiEnable": true,
|
|
"webApiRetries": 10,
|
|
},
|
|
"fetchUsdtMarkets": []interface{}{"btcusdt", "ethusdt"},
|
|
"fetchCurrencies": map[string]interface{} {
|
|
"webApiEnable": true,
|
|
"webApiRetries": 5,
|
|
"webApiMuteFailure": true,
|
|
},
|
|
"fetchTickerMethod": "fetchTickerV1",
|
|
"networks": map[string]interface{} {
|
|
"BTC": "bitcoin",
|
|
"ERC20": "ethereum",
|
|
"BCH": "bitcoincash",
|
|
"LTC": "litecoin",
|
|
"ZEC": "zcash",
|
|
"FIL": "filecoin",
|
|
"DOGE": "dogecoin",
|
|
"XTZ": "tezos",
|
|
"AVAXX": "avalanche",
|
|
"SOL": "solana",
|
|
"ATOM": "cosmos",
|
|
"DOT": "polkadot",
|
|
},
|
|
"nonce": "milliseconds",
|
|
"conflictingMarkets": map[string]interface{} {
|
|
"paxgusd": map[string]interface{} {
|
|
"base": "PAXG",
|
|
"quote": "USD",
|
|
},
|
|
},
|
|
},
|
|
"features": map[string]interface{} {
|
|
"default": map[string]interface{} {
|
|
"sandbox": true,
|
|
"createOrder": map[string]interface{} {
|
|
"marginMode": false,
|
|
"triggerPrice": true,
|
|
"triggerPriceType": nil,
|
|
"triggerDirection": false,
|
|
"stopLossPrice": false,
|
|
"takeProfitPrice": false,
|
|
"attachedStopLossTakeProfit": nil,
|
|
"timeInForce": map[string]interface{} {
|
|
"IOC": true,
|
|
"FOK": true,
|
|
"PO": true,
|
|
"GTD": false,
|
|
},
|
|
"hedged": false,
|
|
"trailing": false,
|
|
"leverage": false,
|
|
"marketBuyByCost": true,
|
|
"marketBuyRequiresPrice": false,
|
|
"selfTradePrevention": false,
|
|
"iceberg": false,
|
|
},
|
|
"createOrders": nil,
|
|
"fetchMyTrades": map[string]interface{} {
|
|
"marginMode": false,
|
|
"limit": 500,
|
|
"daysBack": nil,
|
|
"untilDays": nil,
|
|
"symbolRequired": true,
|
|
},
|
|
"fetchOrder": map[string]interface{} {
|
|
"marginMode": false,
|
|
"trigger": false,
|
|
"trailing": false,
|
|
"symbolRequired": false,
|
|
},
|
|
"fetchOpenOrders": map[string]interface{} {
|
|
"marginMode": false,
|
|
"limit": nil,
|
|
"trigger": false,
|
|
"trailing": false,
|
|
"symbolRequired": false,
|
|
},
|
|
"fetchOrders": nil,
|
|
"fetchClosedOrders": nil,
|
|
"fetchOHLCV": map[string]interface{} {
|
|
"limit": nil,
|
|
},
|
|
},
|
|
"spot": map[string]interface{} {
|
|
"extends": "default",
|
|
},
|
|
"swap": map[string]interface{} {
|
|
"linear": map[string]interface{} {
|
|
"extends": "default",
|
|
},
|
|
"inverse": nil,
|
|
},
|
|
"future": map[string]interface{} {
|
|
"linear": nil,
|
|
"inverse": nil,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
/**
|
|
* @method
|
|
* @name gemini#fetchCurrencies
|
|
* @description fetches all available currencies on an exchange
|
|
* @param {object} [params] extra parameters specific to the endpoint
|
|
* @returns {object} an associative dictionary of currencies
|
|
*/
|
|
func (this *gemini) FetchCurrencies(optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
params := GetArg(optionalArgs, 0, map[string]interface{} {})
|
|
_ = params
|
|
|
|
retRes37715 := (<-this.FetchCurrenciesFromWeb(params))
|
|
PanicOnError(retRes37715)
|
|
ch <- retRes37715
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
/**
|
|
* @method
|
|
* @name gemini#fetchCurrenciesFromWeb
|
|
* @ignore
|
|
* @description fetches all available currencies on an exchange
|
|
* @param {object} [params] extra parameters specific to the endpoint
|
|
* @returns {object} an associative dictionary of currencies
|
|
*/
|
|
func (this *gemini) FetchCurrenciesFromWeb(optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
params := GetArg(optionalArgs, 0, map[string]interface{} {})
|
|
_ = params
|
|
|
|
data:= (<-this.FetchWebEndpoint("fetchCurrencies", "webExchangeGet", true, "=\"currencyData\">", "</script>"))
|
|
PanicOnError(data)
|
|
if IsTrue(IsEqual(data, nil)) {
|
|
|
|
return nil
|
|
}
|
|
//
|
|
// {
|
|
// "tradingPairs": [ [ 'BTCUSD', 2, 8, '0.00001', 10, true ], ... ],
|
|
// "currencies": [
|
|
// [ "ORCA", "Orca", 204, 6, 0, 6, 8, false, null, "solana" ], // as confirmed, precisions seem to be the 5th index
|
|
// [ "ATOM", "Cosmos", 44, 6, 0, 6, 8, false, null, "cosmos" ],
|
|
// [ "ETH", "Ether", 2, 6, 0, 18, 8, false, null, "ethereum" ],
|
|
// [ "GBP", "Pound Sterling", 22, 2, 2, 2, 2, true, "£", null ],
|
|
// ...
|
|
// ],
|
|
// "networks": [
|
|
// [ "solana", "SOL", "Solana" ],
|
|
// [ "zcash", "ZEC", "Zcash" ],
|
|
// [ "tezos", "XTZ", "Tezos" ],
|
|
// [ "cosmos", "ATOM", "Cosmos" ],
|
|
// [ "ethereum", "ETH", "Ethereum" ],
|
|
// ...
|
|
// ]
|
|
// }
|
|
//
|
|
var result interface{} = map[string]interface{} {}
|
|
AddElementToObject(this.Options, "tradingPairs", this.SafeList(data, "tradingPairs"))
|
|
var currenciesArray interface{} = this.SafeValue(data, "currencies", []interface{}{})
|
|
for i := 0; IsLessThan(i, GetArrayLength(currenciesArray)); i++ {
|
|
var currency interface{} = GetValue(currenciesArray, i)
|
|
var id interface{} = this.SafeString(currency, 0)
|
|
var code interface{} = this.SafeCurrencyCode(id)
|
|
var typeVar interface{} = Ternary(IsTrue(this.SafeString(currency, 7)), "fiat", "crypto")
|
|
var precision interface{} = this.ParseNumber(this.ParsePrecision(this.SafeString(currency, 5)))
|
|
var networks interface{} = map[string]interface{} {}
|
|
var networkId interface{} = this.SafeString(currency, 9)
|
|
var networkCode interface{} = nil
|
|
if IsTrue(!IsEqual(networkId, nil)) {
|
|
networkCode = this.NetworkIdToCode(networkId)
|
|
}
|
|
if IsTrue(!IsEqual(networkCode, nil)) {
|
|
AddElementToObject(networks, networkCode, map[string]interface{} {
|
|
"info": currency,
|
|
"id": networkId,
|
|
"network": networkCode,
|
|
"active": nil,
|
|
"deposit": nil,
|
|
"withdraw": nil,
|
|
"fee": nil,
|
|
"precision": precision,
|
|
"limits": map[string]interface{} {
|
|
"deposit": map[string]interface{} {
|
|
"min": nil,
|
|
"max": nil,
|
|
},
|
|
"withdraw": map[string]interface{} {
|
|
"min": nil,
|
|
"max": nil,
|
|
},
|
|
},
|
|
})
|
|
}
|
|
AddElementToObject(result, code, map[string]interface{} {
|
|
"info": currency,
|
|
"id": id,
|
|
"code": code,
|
|
"name": this.SafeString(currency, 1),
|
|
"active": nil,
|
|
"deposit": nil,
|
|
"withdraw": nil,
|
|
"fee": nil,
|
|
"type": typeVar,
|
|
"precision": precision,
|
|
"limits": map[string]interface{} {
|
|
"deposit": map[string]interface{} {
|
|
"min": nil,
|
|
"max": nil,
|
|
},
|
|
"withdraw": map[string]interface{} {
|
|
"min": nil,
|
|
"max": nil,
|
|
},
|
|
},
|
|
"networks": networks,
|
|
})
|
|
}
|
|
|
|
ch <- result
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
/**
|
|
* @method
|
|
* @name gemini#fetchMarkets
|
|
* @description retrieves data on all markets for gemini
|
|
* @see https://docs.gemini.com/rest-api/#symbols
|
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
* @returns {object[]} an array of objects representing market data
|
|
*/
|
|
func (this *gemini) FetchMarkets(optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
params := GetArg(optionalArgs, 0, map[string]interface{} {})
|
|
_ = params
|
|
var method interface{} = this.SafeValue(this.Options, "fetchMarketsMethod", "fetch_markets_from_api")
|
|
if IsTrue(IsEqual(method, "fetch_markets_from_web")) {
|
|
var promises interface{} = []interface{}{}
|
|
AppendToArray(&promises,this.FetchMarketsFromWeb(params)) // get usd markets
|
|
AppendToArray(&promises,this.FetchUSDTMarkets(params)) // get usdt markets
|
|
|
|
promisesResult:= (<-promiseAll(promises))
|
|
PanicOnError(promisesResult)
|
|
|
|
ch <- this.ArrayConcat(GetValue(promisesResult, 0), GetValue(promisesResult, 1))
|
|
return nil
|
|
}
|
|
|
|
retRes49415 := (<-this.FetchMarketsFromAPI(params))
|
|
PanicOnError(retRes49415)
|
|
ch <- retRes49415
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
func (this *gemini) FetchMarketsFromWeb(optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
params := GetArg(optionalArgs, 0, map[string]interface{} {})
|
|
_ = params
|
|
|
|
data:= (<-this.FetchWebEndpoint("fetchMarkets", "webGetRestApi", false, "<h1 id=\"symbols-and-minimums\">Symbols and minimums</h1>"))
|
|
PanicOnError(data)
|
|
var error interface{} = Add(this.Id, " fetchMarketsFromWeb() the API doc HTML markup has changed, breaking the parser of order limits and precision info for markets.")
|
|
var tables interface{} = Split(data, "tbody>")
|
|
var numTables interface{} = GetArrayLength(tables)
|
|
if IsTrue(IsLessThan(numTables, 2)) {
|
|
panic(NotSupported(error))
|
|
}
|
|
var rows interface{} = Split(GetValue(tables, 1), "\n<tr>\n") // eslint-disable-line quotes
|
|
var numRows interface{} = GetArrayLength(rows)
|
|
if IsTrue(IsLessThan(numRows, 2)) {
|
|
panic(NotSupported(error))
|
|
}
|
|
var result interface{} = []interface{}{}
|
|
// skip the first element (empty string)
|
|
for i := 1; IsLessThan(i, numRows); i++ {
|
|
var row interface{} = GetValue(rows, i)
|
|
var cells interface{} = Split(row, "</td>\n") // eslint-disable-line quotes
|
|
var numCells interface{} = GetArrayLength(cells)
|
|
if IsTrue(IsLessThan(numCells, 5)) {
|
|
panic(NotSupported(error))
|
|
}
|
|
// [
|
|
// '<td>btcusd', // currency
|
|
// '<td>0.00001 BTC (1e-5)', // min order size
|
|
// '<td>0.00000001 BTC (1e-8)', // tick size
|
|
// '<td>0.01 USD', // quote currency price increment
|
|
// '</tr>'
|
|
// ]
|
|
var marketId interface{} = Replace(GetValue(cells, 0), "<td>", "")
|
|
marketId = Replace(marketId, "*", "")
|
|
// const base = this.safeCurrencyCode (baseId);
|
|
var minAmountString interface{} = Replace(GetValue(cells, 1), "<td>", "")
|
|
var minAmountParts interface{} = Split(minAmountString, " ")
|
|
var minAmount interface{} = this.SafeNumber(minAmountParts, 0)
|
|
var amountPrecisionString interface{} = Replace(GetValue(cells, 2), "<td>", "")
|
|
var amountPrecisionParts interface{} = Split(amountPrecisionString, " ")
|
|
var idLength interface{} = Subtract(GetArrayLength(marketId), 0)
|
|
var startingIndex interface{} = Subtract(idLength, 3)
|
|
var pricePrecisionString interface{} = Replace(GetValue(cells, 3), "<td>", "")
|
|
var pricePrecisionParts interface{} = Split(pricePrecisionString, " ")
|
|
var quoteId interface{} = this.SafeStringLower(pricePrecisionParts, 1, Slice(marketId, startingIndex, idLength))
|
|
var baseId interface{} = this.SafeStringLower(amountPrecisionParts, 1, Replace(marketId, quoteId, ""))
|
|
var base interface{} = this.SafeCurrencyCode(baseId)
|
|
var quote interface{} = this.SafeCurrencyCode(quoteId)
|
|
AppendToArray(&result,map[string]interface{} {
|
|
"id": marketId,
|
|
"symbol": Add(Add(base, "/"), quote),
|
|
"base": base,
|
|
"quote": quote,
|
|
"settle": nil,
|
|
"baseId": baseId,
|
|
"quoteId": quoteId,
|
|
"settleId": nil,
|
|
"type": "spot",
|
|
"spot": true,
|
|
"margin": false,
|
|
"swap": false,
|
|
"future": false,
|
|
"option": false,
|
|
"active": nil,
|
|
"contract": false,
|
|
"linear": nil,
|
|
"inverse": nil,
|
|
"contractSize": nil,
|
|
"expiry": nil,
|
|
"expiryDatetime": nil,
|
|
"strike": nil,
|
|
"optionType": nil,
|
|
"precision": map[string]interface{} {
|
|
"amount": this.SafeNumber(amountPrecisionParts, 0),
|
|
"price": this.SafeNumber(pricePrecisionParts, 0),
|
|
},
|
|
"limits": map[string]interface{} {
|
|
"leverage": map[string]interface{} {
|
|
"min": nil,
|
|
"max": nil,
|
|
},
|
|
"amount": map[string]interface{} {
|
|
"min": minAmount,
|
|
"max": nil,
|
|
},
|
|
"price": map[string]interface{} {
|
|
"min": nil,
|
|
"max": nil,
|
|
},
|
|
"cost": map[string]interface{} {
|
|
"min": nil,
|
|
"max": nil,
|
|
},
|
|
},
|
|
"created": nil,
|
|
"info": row,
|
|
})
|
|
}
|
|
|
|
ch <- result
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
func (this *gemini) ParseMarketActive(status interface{}) interface{} {
|
|
var statuses interface{} = map[string]interface{} {
|
|
"open": true,
|
|
"closed": false,
|
|
"cancel_only": true,
|
|
"post_only": true,
|
|
"limit_only": true,
|
|
}
|
|
if IsTrue(IsEqual(status, nil)) {
|
|
return true // as defaulted below
|
|
}
|
|
return this.SafeBool(statuses, status, true)
|
|
}
|
|
func (this *gemini) FetchUSDTMarkets(optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
// these markets can't be scrapped and fetchMarketsFrom api does an extra call
|
|
// to load market ids which we don't need here
|
|
params := GetArg(optionalArgs, 0, map[string]interface{} {})
|
|
_ = params
|
|
if IsTrue(InOp(this.Urls, "test")) {
|
|
|
|
ch <- []interface{}{} // sandbox does not have usdt markets
|
|
return nil
|
|
}
|
|
var fetchUsdtMarkets interface{} = this.SafeValue(this.Options, "fetchUsdtMarkets", []interface{}{})
|
|
var result interface{} = []interface{}{}
|
|
for i := 0; IsLessThan(i, GetArrayLength(fetchUsdtMarkets)); i++ {
|
|
var marketId interface{} = GetValue(fetchUsdtMarkets, i)
|
|
var request interface{} = map[string]interface{} {
|
|
"symbol": marketId,
|
|
}
|
|
// don't use Promise.all here, for some reason the exchange can't handle it and crashes
|
|
|
|
rawResponse:= (<-this.PublicGetV1SymbolsDetailsSymbol(this.Extend(request, params)))
|
|
PanicOnError(rawResponse)
|
|
AppendToArray(&result,this.ParseMarket(rawResponse))
|
|
}
|
|
|
|
ch <- result
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
func (this *gemini) FetchMarketsFromAPI(optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
params := GetArg(optionalArgs, 0, map[string]interface{} {})
|
|
_ = params
|
|
|
|
marketIdsRaw:= (<-this.PublicGetV1Symbols(params))
|
|
PanicOnError(marketIdsRaw)
|
|
//
|
|
// [
|
|
// "btcusd",
|
|
// "linkusd",
|
|
// ...
|
|
// ]
|
|
//
|
|
var result interface{} = []interface{}{}
|
|
var options interface{} = this.SafeDict(this.Options, "fetchMarketsFromAPI", map[string]interface{} {})
|
|
var bugSymbol interface{} = "efilfil" // we skip this inexistent test symbol, which bugs other functions
|
|
var marketIds interface{} = []interface{}{}
|
|
for i := 0; IsLessThan(i, GetArrayLength(marketIdsRaw)); i++ {
|
|
if IsTrue(!IsEqual(GetValue(marketIdsRaw, i), bugSymbol)) {
|
|
AppendToArray(&marketIds,GetValue(marketIdsRaw, i))
|
|
}
|
|
}
|
|
if IsTrue(this.SafeBool(options, "fetchDetailsForAllSymbols", false)) {
|
|
var promises interface{} = []interface{}{}
|
|
for i := 0; IsLessThan(i, GetArrayLength(marketIds)); i++ {
|
|
var marketId interface{} = GetValue(marketIds, i)
|
|
var request interface{} = map[string]interface{} {
|
|
"symbol": marketId,
|
|
}
|
|
AppendToArray(&promises,this.PublicGetV1SymbolsDetailsSymbol(this.Extend(request, params)))
|
|
}
|
|
|
|
responses:= (<-promiseAll(promises))
|
|
PanicOnError(responses)
|
|
for i := 0; IsLessThan(i, GetArrayLength(responses)); i++ {
|
|
AppendToArray(&result,this.ParseMarket(GetValue(responses, i)))
|
|
}
|
|
} else {
|
|
// use trading-pairs info, if it was fetched
|
|
var tradingPairs interface{} = this.SafeList(this.Options, "tradingPairs")
|
|
if IsTrue(!IsEqual(tradingPairs, nil)) {
|
|
var indexedTradingPairs interface{} = this.IndexBy(tradingPairs, 0)
|
|
for i := 0; IsLessThan(i, GetArrayLength(marketIds)); i++ {
|
|
var marketId interface{} = GetValue(marketIds, i)
|
|
var tradingPair interface{} = this.SafeList(indexedTradingPairs, ToUpper(marketId))
|
|
if IsTrue(!IsEqual(tradingPair, nil)) {
|
|
AppendToArray(&result,this.ParseMarket(tradingPair))
|
|
}
|
|
}
|
|
} else {
|
|
for i := 0; IsLessThan(i, GetArrayLength(marketIds)); i++ {
|
|
AppendToArray(&result,this.ParseMarket(GetValue(marketIds, i)))
|
|
}
|
|
}
|
|
}
|
|
|
|
ch <- result
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
func (this *gemini) ParseMarket(response interface{}) interface{} {
|
|
//
|
|
// response might be:
|
|
//
|
|
// btcusd
|
|
//
|
|
// or
|
|
//
|
|
// [
|
|
// 'BTCUSD', // symbol
|
|
// 2, // priceTickDecimalPlaces
|
|
// 8, // quantityTickDecimalPlaces
|
|
// '0.00001', // quantityMinimum
|
|
// 10, // quantityRoundDecimalPlaces
|
|
// true // minimumsAreInclusive
|
|
// ],
|
|
//
|
|
// or
|
|
//
|
|
// {
|
|
// "symbol": "BTCUSD", // perpetuals have 'PERP' suffix, i.e. DOGEUSDPERP
|
|
// "base_currency": "BTC",
|
|
// "quote_currency": "USD",
|
|
// "tick_size": 1E-8,
|
|
// "quote_increment": 0.01,
|
|
// "min_order_size": "0.00001",
|
|
// "status": "open",
|
|
// "wrap_enabled": false
|
|
// "product_type": "swap", // only in perps
|
|
// "contract_type": "linear", // only in perps
|
|
// "contract_price_currency": "GUSD" // only in perps
|
|
// }
|
|
//
|
|
var marketId interface{} = nil
|
|
var baseId interface{} = nil
|
|
var quoteId interface{} = nil
|
|
var settleId interface{} = nil
|
|
var tickSize interface{} = nil
|
|
var amountPrecision interface{} = nil
|
|
var minSize interface{} = nil
|
|
var status interface{} = nil
|
|
var swap interface{} = false
|
|
var contractSize interface{} = nil
|
|
var linear interface{} = nil
|
|
var inverse interface{} = nil
|
|
var isString interface{} = (IsString(response))
|
|
var isArray interface{} = (IsArray(response))
|
|
if IsTrue(!IsTrue(isString) && !IsTrue(isArray)) {
|
|
marketId = this.SafeStringLower(response, "symbol")
|
|
amountPrecision = this.SafeNumber(response, "tick_size") // right, exchange has an imperfect naming and this turns out to be an amount-precision
|
|
tickSize = this.SafeNumber(response, "quote_increment") // this is tick-size actually
|
|
minSize = this.SafeNumber(response, "min_order_size")
|
|
status = this.ParseMarketActive(this.SafeString(response, "status"))
|
|
baseId = this.SafeString(response, "base_currency")
|
|
quoteId = this.SafeString(response, "quote_currency")
|
|
settleId = this.SafeString(response, "contract_price_currency")
|
|
} else {
|
|
// if no detailed API was called, then parse either string or array
|
|
if IsTrue(isString) {
|
|
marketId = response
|
|
} else {
|
|
marketId = this.SafeStringLower(response, 0)
|
|
tickSize = this.ParseNumber(this.ParsePrecision(this.SafeString(response, 1))) // priceTickDecimalPlaces
|
|
amountPrecision = this.ParseNumber(this.ParsePrecision(this.SafeString(response, 2))) // quantityTickDecimalPlaces
|
|
minSize = this.SafeNumber(response, 3) // quantityMinimum
|
|
}
|
|
var marketIdUpper interface{} = ToUpper(marketId)
|
|
var isPerp interface{} = (IsGreaterThanOrEqual(GetIndexOf(marketIdUpper, "PERP"), 0))
|
|
var marketIdWithoutPerp interface{} = Replace(marketIdUpper, "PERP", "")
|
|
var conflictingMarkets interface{} = this.SafeDict(this.Options, "conflictingMarkets", map[string]interface{} {})
|
|
var lowerCaseId interface{} = ToLower(marketIdWithoutPerp)
|
|
if IsTrue(InOp(conflictingMarkets, lowerCaseId)) {
|
|
var conflictingMarket interface{} = GetValue(conflictingMarkets, lowerCaseId)
|
|
baseId = GetValue(conflictingMarket, "base")
|
|
quoteId = GetValue(conflictingMarket, "quote")
|
|
if IsTrue(isPerp) {
|
|
settleId = GetValue(conflictingMarket, "quote")
|
|
}
|
|
} else {
|
|
var quoteCurrencies interface{} = this.HandleOption("fetchMarketsFromAPI", "quoteCurrencies", []interface{}{})
|
|
for i := 0; IsLessThan(i, GetArrayLength(quoteCurrencies)); i++ {
|
|
var quoteCurrency interface{} = GetValue(quoteCurrencies, i)
|
|
if IsTrue(EndsWith(marketIdWithoutPerp, quoteCurrency)) {
|
|
var quoteLength interface{} = this.ParseToInt(Multiply(OpNeg(1), GetArrayLength(quoteCurrency)))
|
|
baseId = Slice(marketIdWithoutPerp, 0, quoteLength)
|
|
quoteId = quoteCurrency
|
|
if IsTrue(isPerp) {
|
|
settleId = quoteCurrency // always same
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var base interface{} = this.SafeCurrencyCode(baseId)
|
|
var quote interface{} = this.SafeCurrencyCode(quoteId)
|
|
var settle interface{} = this.SafeCurrencyCode(settleId)
|
|
var symbol interface{} = Add(Add(base, "/"), quote)
|
|
if IsTrue(!IsEqual(settleId, nil)) {
|
|
symbol = Add(Add(symbol, ":"), settle)
|
|
swap = true
|
|
contractSize = tickSize // always same
|
|
linear = true // always linear
|
|
inverse = false
|
|
}
|
|
var typeVar interface{} = Ternary(IsTrue(swap), "swap", "spot")
|
|
return map[string]interface{} {
|
|
"id": marketId,
|
|
"symbol": symbol,
|
|
"base": base,
|
|
"quote": quote,
|
|
"settle": settle,
|
|
"baseId": baseId,
|
|
"quoteId": quoteId,
|
|
"settleId": settleId,
|
|
"type": typeVar,
|
|
"spot": !IsTrue(swap),
|
|
"margin": false,
|
|
"swap": swap,
|
|
"future": false,
|
|
"option": false,
|
|
"active": status,
|
|
"contract": swap,
|
|
"linear": linear,
|
|
"inverse": inverse,
|
|
"contractSize": contractSize,
|
|
"expiry": nil,
|
|
"expiryDatetime": nil,
|
|
"strike": nil,
|
|
"optionType": nil,
|
|
"precision": map[string]interface{} {
|
|
"price": tickSize,
|
|
"amount": amountPrecision,
|
|
},
|
|
"limits": map[string]interface{} {
|
|
"leverage": map[string]interface{} {
|
|
"min": nil,
|
|
"max": nil,
|
|
},
|
|
"amount": map[string]interface{} {
|
|
"min": minSize,
|
|
"max": nil,
|
|
},
|
|
"price": map[string]interface{} {
|
|
"min": nil,
|
|
"max": nil,
|
|
},
|
|
"cost": map[string]interface{} {
|
|
"min": nil,
|
|
"max": nil,
|
|
},
|
|
},
|
|
"created": nil,
|
|
"info": response,
|
|
}
|
|
}
|
|
/**
|
|
* @method
|
|
* @name gemini#fetchOrderBook
|
|
* @description fetches information on open orders with bid (buy) and ask (sell) prices, volumes and other data
|
|
* @see https://docs.gemini.com/rest-api/#current-order-book
|
|
* @param {string} symbol unified symbol of the market to fetch the order book for
|
|
* @param {int} [limit] the maximum amount of order book entries to return
|
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
* @returns {object} A dictionary of [order book structures]{@link https://docs.ccxt.com/#/?id=order-book-structure} indexed by market symbols
|
|
*/
|
|
func (this *gemini) FetchOrderBook(symbol interface{}, optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
limit := GetArg(optionalArgs, 0, nil)
|
|
_ = limit
|
|
params := GetArg(optionalArgs, 1, map[string]interface{} {})
|
|
_ = params
|
|
|
|
retRes8618 := (<-this.LoadMarkets())
|
|
PanicOnError(retRes8618)
|
|
var market interface{} = this.Market(symbol)
|
|
var request interface{} = map[string]interface{} {
|
|
"symbol": GetValue(market, "id"),
|
|
}
|
|
if IsTrue(!IsEqual(limit, nil)) {
|
|
AddElementToObject(request, "limit_bids", limit)
|
|
AddElementToObject(request, "limit_asks", limit)
|
|
}
|
|
|
|
response:= (<-this.PublicGetV1BookSymbol(this.Extend(request, params)))
|
|
PanicOnError(response)
|
|
|
|
ch <- this.ParseOrderBook(response, GetValue(market, "symbol"), nil, "bids", "asks", "price", "amount")
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
func (this *gemini) FetchTickerV1(symbol interface{}, optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
params := GetArg(optionalArgs, 0, map[string]interface{} {})
|
|
_ = params
|
|
|
|
retRes8758 := (<-this.LoadMarkets())
|
|
PanicOnError(retRes8758)
|
|
var market interface{} = this.Market(symbol)
|
|
var request interface{} = map[string]interface{} {
|
|
"symbol": GetValue(market, "id"),
|
|
}
|
|
|
|
response:= (<-this.PublicGetV1PubtickerSymbol(this.Extend(request, params)))
|
|
PanicOnError(response)
|
|
|
|
//
|
|
// {
|
|
// "bid":"9117.95",
|
|
// "ask":"9117.96",
|
|
// "volume":{
|
|
// "BTC":"1615.46861748",
|
|
// "USD":"14727307.57545006088",
|
|
// "timestamp":1594982700000
|
|
// },
|
|
// "last":"9115.23"
|
|
// }
|
|
//
|
|
ch <- this.ParseTicker(response, market)
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
func (this *gemini) FetchTickerV2(symbol interface{}, optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
params := GetArg(optionalArgs, 0, map[string]interface{} {})
|
|
_ = params
|
|
|
|
retRes8978 := (<-this.LoadMarkets())
|
|
PanicOnError(retRes8978)
|
|
var market interface{} = this.Market(symbol)
|
|
var request interface{} = map[string]interface{} {
|
|
"symbol": GetValue(market, "id"),
|
|
}
|
|
|
|
response:= (<-this.PublicGetV2TickerSymbol(this.Extend(request, params)))
|
|
PanicOnError(response)
|
|
|
|
//
|
|
// {
|
|
// "symbol":"BTCUSD",
|
|
// "open":"9080.58",
|
|
// "high":"9184.53",
|
|
// "low":"9063.56",
|
|
// "close":"9116.08",
|
|
// // Hourly prices descending for past 24 hours
|
|
// "changes":["9117.33","9105.69","9106.23","9120.35","9098.57","9114.53","9113.55","9128.01","9113.63","9133.49","9133.49","9137.75","9126.73","9103.91","9119.33","9123.04","9124.44","9117.57","9114.22","9102.33","9076.67","9074.72","9074.97","9092.05"],
|
|
// "bid":"9115.86",
|
|
// "ask":"9115.87"
|
|
// }
|
|
//
|
|
ch <- this.ParseTicker(response, market)
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
func (this *gemini) FetchTickerV1AndV2(symbol interface{}, optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
params := GetArg(optionalArgs, 0, map[string]interface{} {})
|
|
_ = params
|
|
var tickerPromiseA interface{} = this.FetchTickerV1(symbol, params)
|
|
var tickerPromiseB interface{} = this.FetchTickerV2(symbol, params)
|
|
tickerAtickerBVariable := (<-promiseAll([]interface{}{tickerPromiseA, tickerPromiseB}));
|
|
tickerA := GetValue(tickerAtickerBVariable,0);
|
|
tickerB := GetValue(tickerAtickerBVariable,1)
|
|
|
|
ch <- this.DeepExtend(tickerA, map[string]interface{} {
|
|
"open": GetValue(tickerB, "open"),
|
|
"high": GetValue(tickerB, "high"),
|
|
"low": GetValue(tickerB, "low"),
|
|
"change": GetValue(tickerB, "change"),
|
|
"percentage": GetValue(tickerB, "percentage"),
|
|
"average": GetValue(tickerB, "average"),
|
|
"info": GetValue(tickerB, "info"),
|
|
})
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
/**
|
|
* @method
|
|
* @name gemini#fetchTicker
|
|
* @description fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
|
|
* @see https://docs.gemini.com/rest-api/#ticker
|
|
* @see https://docs.gemini.com/rest-api/#ticker-v2
|
|
* @param {string} symbol unified symbol of the market to fetch the ticker for
|
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
* @param {object} [params.fetchTickerMethod] 'fetchTickerV2', 'fetchTickerV1' or 'fetchTickerV1AndV2' - 'fetchTickerV1' for original ccxt.gemini.fetchTicker - 'fetchTickerV1AndV2' for 2 api calls to get the result of both fetchTicker methods - default = 'fetchTickerV1'
|
|
* @returns {object} a [ticker structure]{@link https://docs.ccxt.com/#/?id=ticker-structure}
|
|
*/
|
|
func (this *gemini) FetchTicker(symbol interface{}, optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
params := GetArg(optionalArgs, 0, map[string]interface{} {})
|
|
_ = params
|
|
var method interface{} = this.SafeValue(this.Options, "fetchTickerMethod", "fetchTickerV1")
|
|
if IsTrue(IsEqual(method, "fetchTickerV1")) {
|
|
|
|
retRes94819 := (<-this.FetchTickerV1(symbol, params))
|
|
PanicOnError(retRes94819)
|
|
ch <- retRes94819
|
|
return nil
|
|
}
|
|
if IsTrue(IsEqual(method, "fetchTickerV2")) {
|
|
|
|
retRes95119 := (<-this.FetchTickerV2(symbol, params))
|
|
PanicOnError(retRes95119)
|
|
ch <- retRes95119
|
|
return nil
|
|
}
|
|
|
|
retRes95315 := (<-this.FetchTickerV1AndV2(symbol, params))
|
|
PanicOnError(retRes95315)
|
|
ch <- retRes95315
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
func (this *gemini) ParseTicker(ticker interface{}, optionalArgs ...interface{}) interface{} {
|
|
//
|
|
// fetchTickers
|
|
//
|
|
// {
|
|
// "pair": "BATUSD",
|
|
// "price": "0.20687",
|
|
// "percentChange24h": "0.0146"
|
|
// }
|
|
//
|
|
// fetchTickerV1
|
|
//
|
|
// {
|
|
// "bid":"9117.95",
|
|
// "ask":"9117.96",
|
|
// "volume":{
|
|
// "BTC":"1615.46861748",
|
|
// "USD":"14727307.57545006088",
|
|
// "timestamp":1594982700000
|
|
// },
|
|
// "last":"9115.23"
|
|
// }
|
|
//
|
|
// fetchTickerV2
|
|
//
|
|
// {
|
|
// "symbol":"BTCUSD",
|
|
// "open":"9080.58",
|
|
// "high":"9184.53",
|
|
// "low":"9063.56",
|
|
// "close":"9116.08",
|
|
// // Hourly prices descending for past 24 hours
|
|
// "changes":["9117.33","9105.69","9106.23","9120.35","9098.57","9114.53","9113.55","9128.01","9113.63","9133.49","9133.49","9137.75","9126.73","9103.91","9119.33","9123.04","9124.44","9117.57","9114.22","9102.33","9076.67","9074.72","9074.97","9092.05"],
|
|
// "bid":"9115.86",
|
|
// "ask":"9115.87"
|
|
// }
|
|
//
|
|
market := GetArg(optionalArgs, 0, nil)
|
|
_ = market
|
|
var volume interface{} = this.SafeValue(ticker, "volume", map[string]interface{} {})
|
|
var timestamp interface{} = this.SafeInteger(volume, "timestamp")
|
|
var symbol interface{} = nil
|
|
var marketId interface{} = this.SafeStringLower(ticker, "pair")
|
|
market = this.SafeMarket(marketId, market)
|
|
var baseId interface{} = nil
|
|
var quoteId interface{} = nil
|
|
var base interface{} = nil
|
|
var quote interface{} = nil
|
|
if IsTrue(IsTrue((!IsEqual(marketId, nil))) && IsTrue((IsEqual(market, nil)))) {
|
|
var idLength interface{} = Subtract(GetLength(marketId), 0)
|
|
if IsTrue(IsEqual(idLength, 7)) {
|
|
baseId = Slice(marketId, 0, 4)
|
|
quoteId = Slice(marketId, 4, 7)
|
|
} else {
|
|
baseId = Slice(marketId, 0, 3)
|
|
quoteId = Slice(marketId, 3, 6)
|
|
}
|
|
base = this.SafeCurrencyCode(baseId)
|
|
quote = this.SafeCurrencyCode(quoteId)
|
|
symbol = Add(Add(base, "/"), quote)
|
|
}
|
|
if IsTrue(IsTrue((IsEqual(symbol, nil))) && IsTrue((!IsEqual(market, nil)))) {
|
|
symbol = GetValue(market, "symbol")
|
|
baseId = this.SafeStringUpper(market, "baseId")
|
|
quoteId = this.SafeStringUpper(market, "quoteId")
|
|
}
|
|
var price interface{} = this.SafeString(ticker, "price")
|
|
var last interface{} = this.SafeString2(ticker, "last", "close", price)
|
|
var percentage interface{} = this.SafeString(ticker, "percentChange24h")
|
|
var open interface{} = this.SafeString(ticker, "open")
|
|
var baseVolume interface{} = this.SafeString(volume, baseId)
|
|
var quoteVolume interface{} = this.SafeString(volume, quoteId)
|
|
return this.SafeTicker(map[string]interface{} {
|
|
"symbol": symbol,
|
|
"timestamp": timestamp,
|
|
"datetime": this.Iso8601(timestamp),
|
|
"high": this.SafeString(ticker, "high"),
|
|
"low": this.SafeString(ticker, "low"),
|
|
"bid": this.SafeString(ticker, "bid"),
|
|
"bidVolume": nil,
|
|
"ask": this.SafeString(ticker, "ask"),
|
|
"askVolume": nil,
|
|
"vwap": nil,
|
|
"open": open,
|
|
"close": last,
|
|
"last": last,
|
|
"previousClose": nil,
|
|
"change": nil,
|
|
"percentage": percentage,
|
|
"average": nil,
|
|
"baseVolume": baseVolume,
|
|
"quoteVolume": quoteVolume,
|
|
"info": ticker,
|
|
}, market)
|
|
}
|
|
/**
|
|
* @method
|
|
* @name gemini#fetchTickers
|
|
* @description fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
|
|
* @see https://docs.gemini.com/rest-api/#price-feed
|
|
* @param {string[]|undefined} symbols unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
|
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
* @returns {object} a dictionary of [ticker structures]{@link https://docs.ccxt.com/#/?id=ticker-structure}
|
|
*/
|
|
func (this *gemini) FetchTickers(optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
symbols := GetArg(optionalArgs, 0, nil)
|
|
_ = symbols
|
|
params := GetArg(optionalArgs, 1, map[string]interface{} {})
|
|
_ = params
|
|
|
|
retRes10608 := (<-this.LoadMarkets())
|
|
PanicOnError(retRes10608)
|
|
|
|
response:= (<-this.PublicGetV1Pricefeed(params))
|
|
PanicOnError(response)
|
|
|
|
//
|
|
// [
|
|
// {
|
|
// "pair": "BATUSD",
|
|
// "price": "0.20687",
|
|
// "percentChange24h": "0.0146"
|
|
// },
|
|
// {
|
|
// "pair": "LINKETH",
|
|
// "price": "0.018",
|
|
// "percentChange24h": "0.0000"
|
|
// },
|
|
// ]
|
|
//
|
|
ch <- this.ParseTickers(response, symbols)
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
func (this *gemini) ParseTrade(trade interface{}, optionalArgs ...interface{}) interface{} {
|
|
//
|
|
// public fetchTrades
|
|
//
|
|
// {
|
|
// "timestamp":1601617445,
|
|
// "timestampms":1601617445144,
|
|
// "tid":14122489752,
|
|
// "price":"0.46476",
|
|
// "amount":"28.407209",
|
|
// "exchange":"gemini",
|
|
// "type":"buy"
|
|
// }
|
|
//
|
|
// private fetchTrades
|
|
//
|
|
// {
|
|
// "price":"3900.00",
|
|
// "amount":"0.00996",
|
|
// "timestamp":1638891173,
|
|
// "timestampms":1638891173518,
|
|
// "type":"Sell",
|
|
// "aggressor":false,
|
|
// "fee_currency":"EUR",
|
|
// "fee_amount":"0.00",
|
|
// "tid":73621746145,
|
|
// "order_id":"73621746059",
|
|
// "exchange":"gemini",
|
|
// "is_auction_fill":false,
|
|
// "is_clearing_fill":false,
|
|
// "symbol":"ETHEUR",
|
|
// "client_order_id":"1638891171610"
|
|
// }
|
|
//
|
|
market := GetArg(optionalArgs, 0, nil)
|
|
_ = market
|
|
var timestamp interface{} = this.SafeInteger(trade, "timestampms")
|
|
var id interface{} = this.SafeString(trade, "tid")
|
|
var orderId interface{} = this.SafeString(trade, "order_id")
|
|
var feeCurrencyId interface{} = this.SafeString(trade, "fee_currency")
|
|
var feeCurrencyCode interface{} = this.SafeCurrencyCode(feeCurrencyId)
|
|
var fee interface{} = map[string]interface{} {
|
|
"cost": this.SafeString(trade, "fee_amount"),
|
|
"currency": feeCurrencyCode,
|
|
}
|
|
var priceString interface{} = this.SafeString(trade, "price")
|
|
var amountString interface{} = this.SafeString(trade, "amount")
|
|
var side interface{} = this.SafeStringLower(trade, "type")
|
|
var symbol interface{} = this.SafeSymbol(nil, market)
|
|
return this.SafeTrade(map[string]interface{} {
|
|
"id": id,
|
|
"order": orderId,
|
|
"info": trade,
|
|
"timestamp": timestamp,
|
|
"datetime": this.Iso8601(timestamp),
|
|
"symbol": symbol,
|
|
"type": nil,
|
|
"side": side,
|
|
"takerOrMaker": nil,
|
|
"price": priceString,
|
|
"cost": nil,
|
|
"amount": amountString,
|
|
"fee": fee,
|
|
}, market)
|
|
}
|
|
/**
|
|
* @method
|
|
* @name gemini#fetchTrades
|
|
* @description get the list of most recent trades for a particular symbol
|
|
* @see https://docs.gemini.com/rest-api/#trade-history
|
|
* @param {string} symbol unified symbol of the market to fetch trades for
|
|
* @param {int} [since] timestamp in ms of the earliest trade to fetch
|
|
* @param {int} [limit] the maximum amount of trades to fetch
|
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
* @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=public-trades}
|
|
*/
|
|
func (this *gemini) FetchTrades(symbol interface{}, optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
since := GetArg(optionalArgs, 0, nil)
|
|
_ = since
|
|
limit := GetArg(optionalArgs, 1, nil)
|
|
_ = limit
|
|
params := GetArg(optionalArgs, 2, map[string]interface{} {})
|
|
_ = params
|
|
|
|
retRes11558 := (<-this.LoadMarkets())
|
|
PanicOnError(retRes11558)
|
|
var market interface{} = this.Market(symbol)
|
|
var request interface{} = map[string]interface{} {
|
|
"symbol": GetValue(market, "id"),
|
|
}
|
|
if IsTrue(!IsEqual(limit, nil)) {
|
|
AddElementToObject(request, "limit_trades", mathMin(limit, 500))
|
|
}
|
|
if IsTrue(!IsEqual(since, nil)) {
|
|
AddElementToObject(request, "timestamp", since)
|
|
}
|
|
|
|
response:= (<-this.PublicGetV1TradesSymbol(this.Extend(request, params)))
|
|
PanicOnError(response)
|
|
|
|
//
|
|
// [
|
|
// {
|
|
// "timestamp":1601617445,
|
|
// "timestampms":1601617445144,
|
|
// "tid":14122489752,
|
|
// "price":"0.46476",
|
|
// "amount":"28.407209",
|
|
// "exchange":"gemini",
|
|
// "type":"buy"
|
|
// },
|
|
// ]
|
|
//
|
|
ch <- this.ParseTrades(response, market, since, limit)
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
func (this *gemini) ParseBalance(response interface{}) interface{} {
|
|
var result interface{} = map[string]interface{} {
|
|
"info": response,
|
|
}
|
|
for i := 0; IsLessThan(i, GetArrayLength(response)); i++ {
|
|
var balance interface{} = GetValue(response, i)
|
|
var currencyId interface{} = this.SafeString(balance, "currency")
|
|
var code interface{} = this.SafeCurrencyCode(currencyId)
|
|
var account interface{} = this.Account()
|
|
AddElementToObject(account, "free", this.SafeString(balance, "available"))
|
|
AddElementToObject(account, "total", this.SafeString(balance, "amount"))
|
|
AddElementToObject(result, code, account)
|
|
}
|
|
return this.SafeBalance(result)
|
|
}
|
|
/**
|
|
* @method
|
|
* @name gemini#fetchTradingFees
|
|
* @description fetch the trading fees for multiple markets
|
|
* @see https://docs.gemini.com/rest-api/#get-notional-volume
|
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
* @returns {object} a dictionary of [fee structures]{@link https://docs.ccxt.com/#/?id=fee-structure} indexed by market symbols
|
|
*/
|
|
func (this *gemini) FetchTradingFees(optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
params := GetArg(optionalArgs, 0, map[string]interface{} {})
|
|
_ = params
|
|
|
|
retRes12068 := (<-this.LoadMarkets())
|
|
PanicOnError(retRes12068)
|
|
|
|
response:= (<-this.PrivatePostV1Notionalvolume(params))
|
|
PanicOnError(response)
|
|
//
|
|
// {
|
|
// "web_maker_fee_bps": 25,
|
|
// "web_taker_fee_bps": 35,
|
|
// "web_auction_fee_bps": 25,
|
|
// "api_maker_fee_bps": 10,
|
|
// "api_taker_fee_bps": 35,
|
|
// "api_auction_fee_bps": 20,
|
|
// "fix_maker_fee_bps": 10,
|
|
// "fix_taker_fee_bps": 35,
|
|
// "fix_auction_fee_bps": 20,
|
|
// "block_maker_fee_bps": 0,
|
|
// "block_taker_fee_bps": 50,
|
|
// "notional_30d_volume": 150.00,
|
|
// "last_updated_ms": 1551371446000,
|
|
// "date": "2019-02-28",
|
|
// "notional_1d_volume": [
|
|
// {
|
|
// "date": "2019-02-22",
|
|
// "notional_volume": 75.00
|
|
// },
|
|
// {
|
|
// "date": "2019-02-14",
|
|
// "notional_volume": 75.00
|
|
// }
|
|
// ]
|
|
// }
|
|
//
|
|
var makerBps interface{} = this.SafeString(response, "api_maker_fee_bps")
|
|
var takerBps interface{} = this.SafeString(response, "api_taker_fee_bps")
|
|
var makerString interface{} = Precise.StringDiv(makerBps, "10000")
|
|
var takerString interface{} = Precise.StringDiv(takerBps, "10000")
|
|
var maker interface{} = this.ParseNumber(makerString)
|
|
var taker interface{} = this.ParseNumber(takerString)
|
|
var result interface{} = map[string]interface{} {}
|
|
for i := 0; IsLessThan(i, GetArrayLength(this.Symbols)); i++ {
|
|
var symbol interface{} = GetValue(this.Symbols, i)
|
|
AddElementToObject(result, symbol, map[string]interface{} {
|
|
"info": response,
|
|
"symbol": symbol,
|
|
"maker": maker,
|
|
"taker": taker,
|
|
"percentage": true,
|
|
"tierBased": true,
|
|
})
|
|
}
|
|
|
|
ch <- result
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
/**
|
|
* @method
|
|
* @name gemini#fetchBalance
|
|
* @description query for balance and get the amount of funds available for trading or funds locked in orders
|
|
* @see https://docs.gemini.com/rest-api/#get-available-balances
|
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
* @returns {object} a [balance structure]{@link https://docs.ccxt.com/#/?id=balance-structure}
|
|
*/
|
|
func (this *gemini) FetchBalance(optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
params := GetArg(optionalArgs, 0, map[string]interface{} {})
|
|
_ = params
|
|
|
|
retRes12668 := (<-this.LoadMarkets())
|
|
PanicOnError(retRes12668)
|
|
|
|
response:= (<-this.PrivatePostV1Balances(params))
|
|
PanicOnError(response)
|
|
|
|
ch <- this.ParseBalance(response)
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
func (this *gemini) ParseOrder(order interface{}, optionalArgs ...interface{}) interface{} {
|
|
//
|
|
// createOrder (private)
|
|
//
|
|
// {
|
|
// "order_id":"106027397702",
|
|
// "id":"106027397702",
|
|
// "symbol":"etheur",
|
|
// "exchange":"gemini",
|
|
// "avg_execution_price":"2877.48",
|
|
// "side":"sell",
|
|
// "type":"exchange limit",
|
|
// "timestamp":"1650398122",
|
|
// "timestampms":1650398122308,
|
|
// "is_live":false,
|
|
// "is_cancelled":false,
|
|
// "is_hidden":false,
|
|
// "was_forced":false,
|
|
// "executed_amount":"0.014434",
|
|
// "client_order_id":"1650398121695",
|
|
// "options":[],
|
|
// "price":"2800.00",
|
|
// "original_amount":"0.014434",
|
|
// "remaining_amount":"0"
|
|
// }
|
|
//
|
|
// fetchOrder (private)
|
|
//
|
|
// {
|
|
// "order_id":"106028543717",
|
|
// "id":"106028543717",
|
|
// "symbol":"etheur",
|
|
// "exchange":"gemini",
|
|
// "avg_execution_price":"0.00",
|
|
// "side":"buy",
|
|
// "type":"exchange limit",
|
|
// "timestamp":"1650398446",
|
|
// "timestampms":1650398446375,
|
|
// "is_live":true,
|
|
// "is_cancelled":false,
|
|
// "is_hidden":false,
|
|
// "was_forced":false,
|
|
// "executed_amount":"0",
|
|
// "client_order_id":"1650398445709",
|
|
// "options":[],
|
|
// "price":"2000.00",
|
|
// "original_amount":"0.01",
|
|
// "remaining_amount":"0.01"
|
|
// }
|
|
//
|
|
// fetchOpenOrders (private)
|
|
//
|
|
// {
|
|
// "order_id":"106028543717",
|
|
// "id":"106028543717",
|
|
// "symbol":"etheur",
|
|
// "exchange":"gemini",
|
|
// "avg_execution_price":"0.00",
|
|
// "side":"buy",
|
|
// "type":"exchange limit",
|
|
// "timestamp":"1650398446",
|
|
// "timestampms":1650398446375,
|
|
// "is_live":true,
|
|
// "is_cancelled":false,
|
|
// "is_hidden":false,
|
|
// "was_forced":false,
|
|
// "executed_amount":"0",
|
|
// "client_order_id":"1650398445709",
|
|
// "options":[],
|
|
// "price":"2000.00",
|
|
// "original_amount":"0.01",
|
|
// "remaining_amount":"0.01"
|
|
// }
|
|
//
|
|
// cancelOrder (private)
|
|
//
|
|
// {
|
|
// "order_id":"106028543717",
|
|
// "id":"106028543717",
|
|
// "symbol":"etheur",
|
|
// "exchange":"gemini",
|
|
// "avg_execution_price":"0.00",
|
|
// "side":"buy",
|
|
// "type":"exchange limit",
|
|
// "timestamp":"1650398446",
|
|
// "timestampms":1650398446375,
|
|
// "is_live":false,
|
|
// "is_cancelled":true,
|
|
// "is_hidden":false,
|
|
// "was_forced":false,
|
|
// "executed_amount":"0",
|
|
// "client_order_id":"1650398445709",
|
|
// "reason":"Requested",
|
|
// "options":[],
|
|
// "price":"2000.00",
|
|
// "original_amount":"0.01",
|
|
// "remaining_amount":"0.01"
|
|
// }
|
|
//
|
|
market := GetArg(optionalArgs, 0, nil)
|
|
_ = market
|
|
var timestamp interface{} = this.SafeInteger(order, "timestampms")
|
|
var amount interface{} = this.SafeString(order, "original_amount")
|
|
var remaining interface{} = this.SafeString(order, "remaining_amount")
|
|
var filled interface{} = this.SafeString(order, "executed_amount")
|
|
var status interface{} = "closed"
|
|
if IsTrue(GetValue(order, "is_live")) {
|
|
status = "open"
|
|
}
|
|
if IsTrue(GetValue(order, "is_cancelled")) {
|
|
status = "canceled"
|
|
}
|
|
var price interface{} = this.SafeString(order, "price")
|
|
var average interface{} = this.SafeString(order, "avg_execution_price")
|
|
var typeVar interface{} = this.SafeString(order, "type")
|
|
if IsTrue(IsEqual(typeVar, "exchange limit")) {
|
|
typeVar = "limit"
|
|
} else if IsTrue(IsTrue(IsEqual(typeVar, "market buy")) || IsTrue(IsEqual(typeVar, "market sell"))) {
|
|
typeVar = "market"
|
|
} else {
|
|
typeVar = GetValue(order, "type")
|
|
}
|
|
var fee interface{} = nil
|
|
var marketId interface{} = this.SafeString(order, "symbol")
|
|
var symbol interface{} = this.SafeSymbol(marketId, market)
|
|
var id interface{} = this.SafeString(order, "order_id")
|
|
var side interface{} = this.SafeStringLower(order, "side")
|
|
var clientOrderId interface{} = this.SafeString(order, "client_order_id")
|
|
var optionsArray interface{} = this.SafeValue(order, "options", []interface{}{})
|
|
var option interface{} = this.SafeString(optionsArray, 0)
|
|
var timeInForce interface{} = "GTC"
|
|
var postOnly interface{} = false
|
|
if IsTrue(!IsEqual(option, nil)) {
|
|
if IsTrue(IsEqual(option, "immediate-or-cancel")) {
|
|
timeInForce = "IOC"
|
|
} else if IsTrue(IsEqual(option, "fill-or-kill")) {
|
|
timeInForce = "FOK"
|
|
} else if IsTrue(IsEqual(option, "maker-or-cancel")) {
|
|
timeInForce = "PO"
|
|
postOnly = true
|
|
}
|
|
}
|
|
return this.SafeOrder(map[string]interface{} {
|
|
"id": id,
|
|
"clientOrderId": clientOrderId,
|
|
"info": order,
|
|
"timestamp": timestamp,
|
|
"datetime": this.Iso8601(timestamp),
|
|
"lastTradeTimestamp": nil,
|
|
"status": status,
|
|
"symbol": symbol,
|
|
"type": typeVar,
|
|
"timeInForce": timeInForce,
|
|
"postOnly": postOnly,
|
|
"side": side,
|
|
"price": price,
|
|
"triggerPrice": nil,
|
|
"average": average,
|
|
"cost": nil,
|
|
"amount": amount,
|
|
"filled": filled,
|
|
"remaining": remaining,
|
|
"fee": fee,
|
|
"trades": nil,
|
|
}, market)
|
|
}
|
|
/**
|
|
* @method
|
|
* @name gemini#fetchOrder
|
|
* @description fetches information on an order made by the user
|
|
* @see https://docs.gemini.com/rest-api/#order-status
|
|
* @param {string} id order id
|
|
* @param {string} symbol unified symbol of the market the order was made in
|
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
* @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
*/
|
|
func (this *gemini) FetchOrder(id interface{}, optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
symbol := GetArg(optionalArgs, 0, nil)
|
|
_ = symbol
|
|
params := GetArg(optionalArgs, 1, map[string]interface{} {})
|
|
_ = params
|
|
|
|
retRes14478 := (<-this.LoadMarkets())
|
|
PanicOnError(retRes14478)
|
|
var request interface{} = map[string]interface{} {
|
|
"order_id": id,
|
|
}
|
|
|
|
response:= (<-this.PrivatePostV1OrderStatus(this.Extend(request, params)))
|
|
PanicOnError(response)
|
|
|
|
//
|
|
// {
|
|
// "order_id":"106028543717",
|
|
// "id":"106028543717",
|
|
// "symbol":"etheur",
|
|
// "exchange":"gemini",
|
|
// "avg_execution_price":"0.00",
|
|
// "side":"buy",
|
|
// "type":"exchange limit",
|
|
// "timestamp":"1650398446",
|
|
// "timestampms":1650398446375,
|
|
// "is_live":true,
|
|
// "is_cancelled":false,
|
|
// "is_hidden":false,
|
|
// "was_forced":false,
|
|
// "executed_amount":"0",
|
|
// "client_order_id":"1650398445709",
|
|
// "options":[],
|
|
// "price":"2000.00",
|
|
// "original_amount":"0.01",
|
|
// "remaining_amount":"0.01"
|
|
// }
|
|
//
|
|
ch <- this.ParseOrder(response)
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
/**
|
|
* @method
|
|
* @name gemini#fetchOpenOrders
|
|
* @description fetch all unfilled currently open orders
|
|
* @see https://docs.gemini.com/rest-api/#get-active-orders
|
|
* @param {string} symbol unified market symbol
|
|
* @param {int} [since] the earliest time in ms to fetch open orders for
|
|
* @param {int} [limit] the maximum number of open orders structures to retrieve
|
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
* @returns {Order[]} a list of [order structures]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
*/
|
|
func (this *gemini) FetchOpenOrders(optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
symbol := GetArg(optionalArgs, 0, nil)
|
|
_ = symbol
|
|
since := GetArg(optionalArgs, 1, nil)
|
|
_ = since
|
|
limit := GetArg(optionalArgs, 2, nil)
|
|
_ = limit
|
|
params := GetArg(optionalArgs, 3, map[string]interface{} {})
|
|
_ = params
|
|
|
|
retRes14908 := (<-this.LoadMarkets())
|
|
PanicOnError(retRes14908)
|
|
|
|
response:= (<-this.PrivatePostV1Orders(params))
|
|
PanicOnError(response)
|
|
//
|
|
// [
|
|
// {
|
|
// "order_id":"106028543717",
|
|
// "id":"106028543717",
|
|
// "symbol":"etheur",
|
|
// "exchange":"gemini",
|
|
// "avg_execution_price":"0.00",
|
|
// "side":"buy",
|
|
// "type":"exchange limit",
|
|
// "timestamp":"1650398446",
|
|
// "timestampms":1650398446375,
|
|
// "is_live":true,
|
|
// "is_cancelled":false,
|
|
// "is_hidden":false,
|
|
// "was_forced":false,
|
|
// "executed_amount":"0",
|
|
// "client_order_id":"1650398445709",
|
|
// "options":[],
|
|
// "price":"2000.00",
|
|
// "original_amount":"0.01",
|
|
// "remaining_amount":"0.01"
|
|
// }
|
|
// ]
|
|
//
|
|
var market interface{} = nil
|
|
if IsTrue(!IsEqual(symbol, nil)) {
|
|
market = this.Market(symbol) // throws on non-existent symbol
|
|
}
|
|
|
|
ch <- this.ParseOrders(response, market, since, limit)
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
/**
|
|
* @method
|
|
* @name gemini#createOrder
|
|
* @description create a trade order
|
|
* @see https://docs.gemini.com/rest-api/#new-order
|
|
* @param {string} symbol unified symbol of the market to create an order in
|
|
* @param {string} type must be 'limit'
|
|
* @param {string} side 'buy' or 'sell'
|
|
* @param {float} amount how much of currency you want to trade in units of base currency
|
|
* @param {float} [price] the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
|
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
* @returns {object} an [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
*/
|
|
func (this *gemini) CreateOrder(symbol interface{}, typeVar interface{}, side interface{}, amount interface{}, optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
price := GetArg(optionalArgs, 0, nil)
|
|
_ = price
|
|
params := GetArg(optionalArgs, 1, map[string]interface{} {})
|
|
_ = params
|
|
|
|
retRes15388 := (<-this.LoadMarkets())
|
|
PanicOnError(retRes15388)
|
|
if IsTrue(!IsEqual(typeVar, "limit")) {
|
|
panic(ExchangeError(Add(this.Id, " createOrder() allows limit orders only")))
|
|
}
|
|
var clientOrderId interface{} = this.SafeString2(params, "clientOrderId", "client_order_id")
|
|
params = this.Omit(params, []interface{}{"clientOrderId", "client_order_id"})
|
|
if IsTrue(IsEqual(clientOrderId, nil)) {
|
|
clientOrderId = ToString(this.Milliseconds())
|
|
}
|
|
var market interface{} = this.Market(symbol)
|
|
var amountString interface{} = this.AmountToPrecision(symbol, amount)
|
|
var priceString interface{} = this.PriceToPrecision(symbol, price)
|
|
var request interface{} = map[string]interface{} {
|
|
"client_order_id": clientOrderId,
|
|
"symbol": GetValue(market, "id"),
|
|
"amount": amountString,
|
|
"price": priceString,
|
|
"side": side,
|
|
"type": "exchange limit",
|
|
}
|
|
typeVar = this.SafeString(params, "type", typeVar)
|
|
params = this.Omit(params, "type")
|
|
var triggerPrice interface{} = this.SafeStringN(params, []interface{}{"triggerPrice", "stop_price", "stopPrice"})
|
|
params = this.Omit(params, []interface{}{"triggerPrice", "stop_price", "stopPrice", "type"})
|
|
if IsTrue(IsEqual(typeVar, "stopLimit")) {
|
|
panic(ArgumentsRequired(Add(Add(Add(this.Id, " createOrder() requires a triggerPrice parameter or a stop_price parameter for "), typeVar), " orders")))
|
|
}
|
|
if IsTrue(!IsEqual(triggerPrice, nil)) {
|
|
AddElementToObject(request, "stop_price", this.PriceToPrecision(symbol, triggerPrice))
|
|
AddElementToObject(request, "type", "exchange stop limit")
|
|
} else {
|
|
// No options can be applied to stop-limit orders at this time.
|
|
var timeInForce interface{} = this.SafeString(params, "timeInForce")
|
|
params = this.Omit(params, "timeInForce")
|
|
if IsTrue(!IsEqual(timeInForce, nil)) {
|
|
if IsTrue(IsTrue((IsEqual(timeInForce, "IOC"))) || IsTrue((IsEqual(timeInForce, "immediate-or-cancel")))) {
|
|
AddElementToObject(request, "options", []interface{}{"immediate-or-cancel"})
|
|
} else if IsTrue(IsTrue((IsEqual(timeInForce, "FOK"))) || IsTrue((IsEqual(timeInForce, "fill-or-kill")))) {
|
|
AddElementToObject(request, "options", []interface{}{"fill-or-kill"})
|
|
} else if IsTrue(IsEqual(timeInForce, "PO")) {
|
|
AddElementToObject(request, "options", []interface{}{"maker-or-cancel"})
|
|
}
|
|
}
|
|
var postOnly interface{} = this.SafeBool(params, "postOnly", false)
|
|
params = this.Omit(params, "postOnly")
|
|
if IsTrue(postOnly) {
|
|
AddElementToObject(request, "options", []interface{}{"maker-or-cancel"})
|
|
}
|
|
// allowing override for auction-only and indication-of-interest order options
|
|
var options interface{} = this.SafeString(params, "options")
|
|
if IsTrue(!IsEqual(options, nil)) {
|
|
AddElementToObject(request, "options", []interface{}{options})
|
|
}
|
|
}
|
|
|
|
response:= (<-this.PrivatePostV1OrderNew(this.Extend(request, params)))
|
|
PanicOnError(response)
|
|
|
|
//
|
|
// {
|
|
// "order_id":"106027397702",
|
|
// "id":"106027397702",
|
|
// "symbol":"etheur",
|
|
// "exchange":"gemini",
|
|
// "avg_execution_price":"2877.48",
|
|
// "side":"sell",
|
|
// "type":"exchange limit",
|
|
// "timestamp":"1650398122",
|
|
// "timestampms":1650398122308,
|
|
// "is_live":false,
|
|
// "is_cancelled":false,
|
|
// "is_hidden":false,
|
|
// "was_forced":false,
|
|
// "executed_amount":"0.014434",
|
|
// "client_order_id":"1650398121695",
|
|
// "options":[],
|
|
// "price":"2800.00",
|
|
// "original_amount":"0.014434",
|
|
// "remaining_amount":"0"
|
|
// }
|
|
//
|
|
ch <- this.ParseOrder(response)
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
/**
|
|
* @method
|
|
* @name gemini#cancelOrder
|
|
* @description cancels an open order
|
|
* @see https://docs.gemini.com/rest-api/#cancel-order
|
|
* @param {string} id order id
|
|
* @param {string} symbol unified symbol of the market the order was made in
|
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
* @returns {object} An [order structure]{@link https://docs.ccxt.com/#/?id=order-structure}
|
|
*/
|
|
func (this *gemini) CancelOrder(id interface{}, optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
symbol := GetArg(optionalArgs, 0, nil)
|
|
_ = symbol
|
|
params := GetArg(optionalArgs, 1, map[string]interface{} {})
|
|
_ = params
|
|
|
|
retRes16318 := (<-this.LoadMarkets())
|
|
PanicOnError(retRes16318)
|
|
var request interface{} = map[string]interface{} {
|
|
"order_id": id,
|
|
}
|
|
|
|
response:= (<-this.PrivatePostV1OrderCancel(this.Extend(request, params)))
|
|
PanicOnError(response)
|
|
|
|
//
|
|
// {
|
|
// "order_id":"106028543717",
|
|
// "id":"106028543717",
|
|
// "symbol":"etheur",
|
|
// "exchange":"gemini",
|
|
// "avg_execution_price":"0.00",
|
|
// "side":"buy",
|
|
// "type":"exchange limit",
|
|
// "timestamp":"1650398446",
|
|
// "timestampms":1650398446375,
|
|
// "is_live":false,
|
|
// "is_cancelled":true,
|
|
// "is_hidden":false,
|
|
// "was_forced":false,
|
|
// "executed_amount":"0",
|
|
// "client_order_id":"1650398445709",
|
|
// "reason":"Requested",
|
|
// "options":[],
|
|
// "price":"2000.00",
|
|
// "original_amount":"0.01",
|
|
// "remaining_amount":"0.01"
|
|
// }
|
|
//
|
|
ch <- this.ParseOrder(response)
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
/**
|
|
* @method
|
|
* @name gemini#fetchMyTrades
|
|
* @description fetch all trades made by the user
|
|
* @see https://docs.gemini.com/rest-api/#get-past-trades
|
|
* @param {string} symbol unified market symbol
|
|
* @param {int} [since] the earliest time in ms to fetch trades for
|
|
* @param {int} [limit] the maximum number of trades structures to retrieve
|
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
* @returns {Trade[]} a list of [trade structures]{@link https://docs.ccxt.com/#/?id=trade-structure}
|
|
*/
|
|
func (this *gemini) FetchMyTrades(optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
symbol := GetArg(optionalArgs, 0, nil)
|
|
_ = symbol
|
|
since := GetArg(optionalArgs, 1, nil)
|
|
_ = since
|
|
limit := GetArg(optionalArgs, 2, nil)
|
|
_ = limit
|
|
params := GetArg(optionalArgs, 3, map[string]interface{} {})
|
|
_ = params
|
|
if IsTrue(IsEqual(symbol, nil)) {
|
|
panic(ArgumentsRequired(Add(this.Id, " fetchMyTrades() requires a symbol argument")))
|
|
}
|
|
|
|
retRes16788 := (<-this.LoadMarkets())
|
|
PanicOnError(retRes16788)
|
|
var market interface{} = this.Market(symbol)
|
|
var request interface{} = map[string]interface{} {
|
|
"symbol": GetValue(market, "id"),
|
|
}
|
|
if IsTrue(!IsEqual(limit, nil)) {
|
|
AddElementToObject(request, "limit_trades", limit)
|
|
}
|
|
if IsTrue(!IsEqual(since, nil)) {
|
|
AddElementToObject(request, "timestamp", this.ParseToInt(Divide(since, 1000)))
|
|
}
|
|
|
|
response:= (<-this.PrivatePostV1Mytrades(this.Extend(request, params)))
|
|
PanicOnError(response)
|
|
|
|
ch <- this.ParseTrades(response, market, since, limit)
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
/**
|
|
* @method
|
|
* @name gemini#withdraw
|
|
* @description make a withdrawal
|
|
* @see https://docs.gemini.com/rest-api/#withdraw-crypto-funds
|
|
* @param {string} code unified currency code
|
|
* @param {float} amount the amount to withdraw
|
|
* @param {string} address the address to withdraw to
|
|
* @param {string} tag
|
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
* @returns {object} a [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure}
|
|
*/
|
|
func (this *gemini) Withdraw(code interface{}, amount interface{}, address interface{}, optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
tag := GetArg(optionalArgs, 0, nil)
|
|
_ = tag
|
|
params := GetArg(optionalArgs, 1, map[string]interface{} {})
|
|
_ = params
|
|
tagparamsVariable := this.HandleWithdrawTagAndParams(tag, params);
|
|
tag = GetValue(tagparamsVariable,0);
|
|
params = GetValue(tagparamsVariable,1)
|
|
this.CheckAddress(address)
|
|
|
|
retRes17088 := (<-this.LoadMarkets())
|
|
PanicOnError(retRes17088)
|
|
var currency interface{} = this.Currency(code)
|
|
var request interface{} = map[string]interface{} {
|
|
"currency": GetValue(currency, "id"),
|
|
"amount": amount,
|
|
"address": address,
|
|
}
|
|
|
|
response:= (<-this.PrivatePostV1WithdrawCurrency(this.Extend(request, params)))
|
|
PanicOnError(response)
|
|
//
|
|
// for BTC
|
|
// {
|
|
// "address":"mi98Z9brJ3TgaKsmvXatuRahbFRUFKRUdR",
|
|
// "amount":"1",
|
|
// "withdrawalId":"02176a83-a6b1-4202-9b85-1c1c92dd25c4",
|
|
// "message":"You have requested a transfer of 1 BTC to mi98Z9brJ3TgaKsmvXatuRahbFRUFKRUdR. This withdrawal will be sent to the blockchain within the next 60 seconds."
|
|
// }
|
|
//
|
|
// for ETH
|
|
// {
|
|
// "address":"0xA63123350Acc8F5ee1b1fBd1A6717135e82dBd28",
|
|
// "amount":"2.34567",
|
|
// "txHash":"0x28267179f92926d85c5516bqc063b2631935573d8915258e95d9572eedcc8cc"
|
|
// }
|
|
//
|
|
// for error (other variations of error messages are also expected)
|
|
// {
|
|
// "result":"error",
|
|
// "reason":"CryptoAddressWhitelistsNotEnabled",
|
|
// "message":"Cryptocurrency withdrawal address whitelists are not enabled for account 24. Please contact support@gemini.com for information on setting up a withdrawal address whitelist."
|
|
// }
|
|
//
|
|
var result interface{} = this.SafeString(response, "result")
|
|
if IsTrue(IsEqual(result, "error")) {
|
|
panic(ExchangeError(Add(Add(this.Id, " withdraw() failed: "), this.Json(response))))
|
|
}
|
|
|
|
ch <- this.ParseTransaction(response, currency)
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
func (this *gemini) Nonce() interface{} {
|
|
var nonceMethod interface{} = this.SafeString(this.Options, "nonce", "milliseconds")
|
|
if IsTrue(IsEqual(nonceMethod, "milliseconds")) {
|
|
return this.Milliseconds()
|
|
}
|
|
return this.Seconds()
|
|
}
|
|
/**
|
|
* @method
|
|
* @name gemini#fetchDepositsWithdrawals
|
|
* @description fetch history of deposits and withdrawals
|
|
* @see https://docs.gemini.com/rest-api/#transfers
|
|
* @param {string} [code] unified currency code for the currency of the deposit/withdrawals, default is undefined
|
|
* @param {int} [since] timestamp in ms of the earliest deposit/withdrawal, default is undefined
|
|
* @param {int} [limit] max number of deposit/withdrawals to return, default is undefined
|
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
* @returns {object} a list of [transaction structure]{@link https://docs.ccxt.com/#/?id=transaction-structure}
|
|
*/
|
|
func (this *gemini) FetchDepositsWithdrawals(optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
code := GetArg(optionalArgs, 0, nil)
|
|
_ = code
|
|
since := GetArg(optionalArgs, 1, nil)
|
|
_ = since
|
|
limit := GetArg(optionalArgs, 2, nil)
|
|
_ = limit
|
|
params := GetArg(optionalArgs, 3, map[string]interface{} {})
|
|
_ = params
|
|
|
|
retRes17668 := (<-this.LoadMarkets())
|
|
PanicOnError(retRes17668)
|
|
var request interface{} = map[string]interface{} {}
|
|
if IsTrue(!IsEqual(limit, nil)) {
|
|
AddElementToObject(request, "limit_transfers", limit)
|
|
}
|
|
if IsTrue(!IsEqual(since, nil)) {
|
|
AddElementToObject(request, "timestamp", since)
|
|
}
|
|
|
|
response:= (<-this.PrivatePostV1Transfers(this.Extend(request, params)))
|
|
PanicOnError(response)
|
|
|
|
ch <- this.ParseTransactions(response)
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
func (this *gemini) ParseTransaction(transaction interface{}, optionalArgs ...interface{}) interface{} {
|
|
//
|
|
// withdraw
|
|
//
|
|
// for BTC
|
|
// {
|
|
// "address":"mi98Z9brJ3TgaKsmvXatuRahbFRUFKRUdR",
|
|
// "amount":"1",
|
|
// "withdrawalId":"02176a83-a6b1-4202-9b85-1c1c92dd25c4",
|
|
// "message":"You have requested a transfer of 1 BTC to mi98Z9brJ3TgaKsmvXatuRahbFRUFKRUdR. This withdrawal will be sent to the blockchain within the next 60 seconds."
|
|
// }
|
|
//
|
|
// for ETH
|
|
// {
|
|
// "address":"0xA63123350Acc8F5ee1b1fBd1A6717135e82dBd28",
|
|
// "amount":"2.34567",
|
|
// "txHash":"0x28267179f92926d85c5516bqc063b2631935573d8915258e95d9572eedcc8cc"
|
|
// }
|
|
//
|
|
currency := GetArg(optionalArgs, 0, nil)
|
|
_ = currency
|
|
var timestamp interface{} = this.SafeInteger(transaction, "timestampms")
|
|
var currencyId interface{} = this.SafeString(transaction, "currency")
|
|
var code interface{} = this.SafeCurrencyCode(currencyId, currency)
|
|
var address interface{} = this.SafeString(transaction, "destination")
|
|
var typeVar interface{} = this.SafeStringLower(transaction, "type")
|
|
// if status field is available, then it's complete
|
|
var statusRaw interface{} = this.SafeString(transaction, "status")
|
|
var fee interface{} = nil
|
|
var feeAmount interface{} = this.SafeNumber(transaction, "feeAmount")
|
|
if IsTrue(!IsEqual(feeAmount, nil)) {
|
|
fee = map[string]interface{} {
|
|
"cost": feeAmount,
|
|
"currency": code,
|
|
}
|
|
}
|
|
return map[string]interface{} {
|
|
"info": transaction,
|
|
"id": this.SafeString2(transaction, "eid", "withdrawalId"),
|
|
"txid": this.SafeString(transaction, "txHash"),
|
|
"timestamp": timestamp,
|
|
"datetime": this.Iso8601(timestamp),
|
|
"network": nil,
|
|
"address": address,
|
|
"addressTo": nil,
|
|
"addressFrom": nil,
|
|
"tag": nil,
|
|
"tagTo": nil,
|
|
"tagFrom": nil,
|
|
"type": typeVar,
|
|
"amount": this.SafeNumber(transaction, "amount"),
|
|
"currency": code,
|
|
"status": this.ParseTransactionStatus(statusRaw),
|
|
"updated": nil,
|
|
"internal": nil,
|
|
"comment": this.SafeString(transaction, "message"),
|
|
"fee": fee,
|
|
}
|
|
}
|
|
func (this *gemini) ParseTransactionStatus(status interface{}) interface{} {
|
|
var statuses interface{} = map[string]interface{} {
|
|
"Advanced": "ok",
|
|
"Complete": "ok",
|
|
}
|
|
return this.SafeString(statuses, status, status)
|
|
}
|
|
func (this *gemini) ParseDepositAddress(depositAddress interface{}, optionalArgs ...interface{}) interface{} {
|
|
//
|
|
// {
|
|
// "address": "0xed6494Fe7c1E56d1bd6136e89268C51E32d9708B",
|
|
// "timestamp": "1636813923098",
|
|
// "addressVersion": "eV1" }
|
|
// }
|
|
//
|
|
currency := GetArg(optionalArgs, 0, nil)
|
|
_ = currency
|
|
var address interface{} = this.SafeString(depositAddress, "address")
|
|
var code interface{} = this.SafeCurrencyCode(nil, currency)
|
|
return map[string]interface{} {
|
|
"currency": code,
|
|
"network": nil,
|
|
"address": address,
|
|
"tag": nil,
|
|
"info": depositAddress,
|
|
}
|
|
}
|
|
/**
|
|
* @method
|
|
* @name gemini#fetchDepositAddress
|
|
* @see https://docs.gemini.com/rest-api/#get-deposit-addresses
|
|
* @description fetch the deposit address for a currency associated with this account
|
|
* @param {string} code unified currency code
|
|
* @param {object} [params] extra parameters specific to the endpoint
|
|
* @param {string} [params.network] *required* The chain of currency
|
|
* @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure}
|
|
*/
|
|
func (this *gemini) FetchDepositAddress(code interface{}, optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
params := GetArg(optionalArgs, 0, map[string]interface{} {})
|
|
_ = params
|
|
|
|
retRes18748 := (<-this.LoadMarkets())
|
|
PanicOnError(retRes18748)
|
|
|
|
groupedByNetwork:= (<-this.FetchDepositAddressesByNetwork(code, params))
|
|
PanicOnError(groupedByNetwork)
|
|
var networkCode interface{} = nil
|
|
networkCodeparamsVariable := this.HandleNetworkCodeAndParams(params);
|
|
networkCode = GetValue(networkCodeparamsVariable,0);
|
|
params = GetValue(networkCodeparamsVariable,1)
|
|
var networkGroup interface{} = this.IndexBy(this.SafeValue(groupedByNetwork, networkCode), "currency")
|
|
|
|
ch <- this.SafeValue(networkGroup, code)
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
/**
|
|
* @method
|
|
* @name gemini#fetchDepositAddressesByNetwork
|
|
* @description fetch a dictionary of addresses for a currency, indexed by network
|
|
* @see https://docs.gemini.com/rest-api/#get-deposit-addresses
|
|
* @param {string} code unified currency code of the currency for the deposit address
|
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
* @param {string} [params.network] *required* The chain of currency
|
|
* @returns {object} a dictionary of [address structures]{@link https://docs.ccxt.com/#/?id=address-structure} indexed by the network
|
|
*/
|
|
func (this *gemini) FetchDepositAddressesByNetwork(code interface{}, optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
params := GetArg(optionalArgs, 0, map[string]interface{} {})
|
|
_ = params
|
|
|
|
retRes18938 := (<-this.LoadMarkets())
|
|
PanicOnError(retRes18938)
|
|
var currency interface{} = this.Currency(code)
|
|
code = GetValue(currency, "code")
|
|
var networkCode interface{} = nil
|
|
networkCodeparamsVariable := this.HandleNetworkCodeAndParams(params);
|
|
networkCode = GetValue(networkCodeparamsVariable,0);
|
|
params = GetValue(networkCodeparamsVariable,1)
|
|
if IsTrue(IsEqual(networkCode, nil)) {
|
|
panic(ArgumentsRequired(Add(this.Id, " fetchDepositAddresses() requires a network parameter")))
|
|
}
|
|
var networkId interface{} = this.NetworkCodeToId(networkCode)
|
|
var request interface{} = map[string]interface{} {
|
|
"network": networkId,
|
|
}
|
|
|
|
response:= (<-this.PrivatePostV1AddressesNetwork(this.Extend(request, params)))
|
|
PanicOnError(response)
|
|
var results interface{} = this.ParseDepositAddresses(response, []interface{}{code}, false, map[string]interface{} {
|
|
"network": networkCode,
|
|
"currency": code,
|
|
})
|
|
|
|
ch <- this.GroupBy(results, "network")
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
func (this *gemini) Sign(path interface{}, optionalArgs ...interface{}) interface{} {
|
|
api := GetArg(optionalArgs, 0, "public")
|
|
_ = api
|
|
method := GetArg(optionalArgs, 1, "GET")
|
|
_ = method
|
|
params := GetArg(optionalArgs, 2, map[string]interface{} {})
|
|
_ = params
|
|
headers := GetArg(optionalArgs, 3, nil)
|
|
_ = headers
|
|
body := GetArg(optionalArgs, 4, nil)
|
|
_ = body
|
|
var url interface{} = Add("/", this.ImplodeParams(path, params))
|
|
var query interface{} = this.Omit(params, this.ExtractParams(path))
|
|
if IsTrue(IsEqual(api, "private")) {
|
|
this.CheckRequiredCredentials()
|
|
var apiKey interface{} = this.ApiKey
|
|
if IsTrue(IsLessThan(GetIndexOf(apiKey, "account"), 0)) {
|
|
panic(AuthenticationError(Add(this.Id, " sign() requires an account-key, master-keys are not-supported")))
|
|
}
|
|
var nonce interface{} = ToString(this.Nonce())
|
|
var request interface{} = this.Extend(map[string]interface{} {
|
|
"request": url,
|
|
"nonce": nonce,
|
|
}, query)
|
|
var payload interface{} = this.Json(request)
|
|
payload = this.StringToBase64(payload)
|
|
var signature interface{} = this.Hmac(this.Encode(payload), this.Encode(this.Secret), sha384)
|
|
headers = map[string]interface{} {
|
|
"Content-Type": "text/plain",
|
|
"X-GEMINI-APIKEY": this.ApiKey,
|
|
"X-GEMINI-PAYLOAD": payload,
|
|
"X-GEMINI-SIGNATURE": signature,
|
|
}
|
|
} else {
|
|
if IsTrue(GetArrayLength(ObjectKeys(query))) {
|
|
url = Add(url, Add("?", this.Urlencode(query)))
|
|
}
|
|
}
|
|
url = Add(GetValue(GetValue(this.Urls, "api"), api), url)
|
|
if IsTrue(IsTrue((IsEqual(method, "POST"))) || IsTrue((IsEqual(method, "DELETE")))) {
|
|
body = this.Json(query)
|
|
}
|
|
return map[string]interface{} {
|
|
"url": url,
|
|
"method": method,
|
|
"body": body,
|
|
"headers": headers,
|
|
}
|
|
}
|
|
func (this *gemini) HandleErrors(httpCode interface{}, reason interface{}, url interface{}, method interface{}, headers interface{}, body interface{}, response interface{}, requestHeaders interface{}, requestBody interface{}) interface{} {
|
|
if IsTrue(IsEqual(response, nil)) {
|
|
if IsTrue(IsString(body)) {
|
|
var feedback interface{} = Add(Add(this.Id, " "), body)
|
|
this.ThrowBroadlyMatchedException(GetValue(this.Exceptions, "broad"), body, feedback)
|
|
}
|
|
return nil // fallback to default error handler
|
|
}
|
|
//
|
|
// {
|
|
// "result": "error",
|
|
// "reason": "BadNonce",
|
|
// "message": "Out-of-sequence nonce <1234> precedes previously used nonce <2345>"
|
|
// }
|
|
//
|
|
var result interface{} = this.SafeString(response, "result")
|
|
if IsTrue(IsEqual(result, "error")) {
|
|
var reasonInner interface{} = this.SafeString(response, "reason")
|
|
var message interface{} = this.SafeString(response, "message")
|
|
var feedback interface{} = Add(Add(this.Id, " "), message)
|
|
this.ThrowExactlyMatchedException(GetValue(this.Exceptions, "exact"), reasonInner, feedback)
|
|
this.ThrowExactlyMatchedException(GetValue(this.Exceptions, "exact"), message, feedback)
|
|
this.ThrowBroadlyMatchedException(GetValue(this.Exceptions, "broad"), message, feedback)
|
|
panic(ExchangeError(feedback))
|
|
}
|
|
return nil
|
|
}
|
|
/**
|
|
* @method
|
|
* @name gemini#createDepositAddress
|
|
* @description create a currency deposit address
|
|
* @see https://docs.gemini.com/rest-api/#new-deposit-address
|
|
* @param {string} code unified currency code of the currency for the deposit address
|
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
* @returns {object} an [address structure]{@link https://docs.ccxt.com/#/?id=address-structure}
|
|
*/
|
|
func (this *gemini) CreateDepositAddress(code interface{}, optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
params := GetArg(optionalArgs, 0, map[string]interface{} {})
|
|
_ = params
|
|
|
|
retRes19838 := (<-this.LoadMarkets())
|
|
PanicOnError(retRes19838)
|
|
var currency interface{} = this.Currency(code)
|
|
var request interface{} = map[string]interface{} {
|
|
"currency": GetValue(currency, "id"),
|
|
}
|
|
|
|
response:= (<-this.PrivatePostV1DepositCurrencyNewAddress(this.Extend(request, params)))
|
|
PanicOnError(response)
|
|
var address interface{} = this.SafeString(response, "address")
|
|
this.CheckAddress(address)
|
|
|
|
ch <- map[string]interface{} {
|
|
"currency": code,
|
|
"address": address,
|
|
"tag": nil,
|
|
"info": response,
|
|
}
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
/**
|
|
* @method
|
|
* @name gemini#fetchOHLCV
|
|
* @description fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
|
* @see https://docs.gemini.com/rest-api/#candles
|
|
* @param {string} symbol unified symbol of the market to fetch OHLCV data for
|
|
* @param {string} timeframe the length of time each candle represents
|
|
* @param {int} [since] timestamp in ms of the earliest candle to fetch
|
|
* @param {int} [limit] the maximum amount of candles to fetch
|
|
* @param {object} [params] extra parameters specific to the exchange API endpoint
|
|
* @returns {int[][]} A list of candles ordered as timestamp, open, high, low, close, volume
|
|
*/
|
|
func (this *gemini) FetchOHLCV(symbol interface{}, optionalArgs ...interface{}) <- chan interface{} {
|
|
ch := make(chan interface{})
|
|
go func() interface{} {
|
|
defer close(ch)
|
|
defer ReturnPanicError(ch)
|
|
timeframe := GetArg(optionalArgs, 0, "1m")
|
|
_ = timeframe
|
|
since := GetArg(optionalArgs, 1, nil)
|
|
_ = since
|
|
limit := GetArg(optionalArgs, 2, nil)
|
|
_ = limit
|
|
params := GetArg(optionalArgs, 3, map[string]interface{} {})
|
|
_ = params
|
|
|
|
retRes20128 := (<-this.LoadMarkets())
|
|
PanicOnError(retRes20128)
|
|
var market interface{} = this.Market(symbol)
|
|
var timeframeId interface{} = this.SafeString(this.Timeframes, timeframe, timeframe)
|
|
var request interface{} = map[string]interface{} {
|
|
"timeframe": timeframeId,
|
|
"symbol": GetValue(market, "id"),
|
|
}
|
|
|
|
response:= (<-this.PublicGetV2CandlesSymbolTimeframe(this.Extend(request, params)))
|
|
PanicOnError(response)
|
|
|
|
//
|
|
// [
|
|
// [1591515000000,0.02509,0.02509,0.02509,0.02509,0],
|
|
// [1591514700000,0.02503,0.02509,0.02503,0.02509,44.6405],
|
|
// [1591514400000,0.02503,0.02503,0.02503,0.02503,0],
|
|
// ]
|
|
//
|
|
ch <- this.ParseOHLCVs(response, market, timeframe, since, limit)
|
|
return nil
|
|
|
|
}()
|
|
return ch
|
|
}
|
|
|
|
|
|
func (this *gemini) Init(userConfig map[string]interface{}) {
|
|
this.Exchange = Exchange{}
|
|
this.Exchange.InitParent(userConfig, this.Describe().(map[string]interface{}), this)
|
|
this.Exchange.DerivedExchange = this
|
|
}
|