mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-06-04 23:16:54 +00:00
(Exchange) Add FTX exchange support with implementation tutorial (#495)
* initial * wip * brokenwip * broken wipzzzz * more functions brokenwip NO API KEYS * broken wip * WIP * wip * WIP work in progress * WIP * WIP * wip * more wip * wip * ws wip * broken wip * adding new functions for websocket to work * trying to fix websocket issues * websocket bug fix wip * broken websocket implementation * WS unauth functions + brokenWS auth func * authentication problems * authentication problems fixed * data handling for websocket * websocket completed * remove verbose * minor error fix changes and testing * reorganising variable declarations and minor errors fixed * enabled exchanges updated * enabled exchanges fixed * remove keys * glorious nits * xdta n shazzy nitzzz * shazzy n thrasher nitz * nitz wip * broken wip * apichecker donee n make code better * apichecker donee n make code better * OB update * wip * wip * all nitz done * merge conflicts * go mod tidy * merge conflicts * PLEASE merge conflicts * new funcs added n binanceapi check update NO APIKEYS * basic tests * linter fixs * linter fixs * remove verbose * test errors fixed * remove comented code * minor changes * some tests fixed no apikeys * documentation work * documentation * wip * ryan nitz * nits addressed * unnecessary conversion * no fail * remove verbose * type field checking * broken * websocket nits fixed * some thangs * remove verbose * fix function * linter issues * test error fixed * nits * bumperino fixed * very small change * nits * errors fixing * errors fixing retry * linters * thrasher glorious nits * more changes * changes * 2 more changes to be addressed * 2 more changes to be addressed * issues addressed * whip * changes * missed change * changes * currency issues * changes * unsaved * int64 * HUGE * HUGE * NO NITS PLS * no more * YES * : * changes * PLEASE * n another one * thanks guys * ill believe in god if this ever ends * :D
This commit is contained in:
@@ -25,6 +25,10 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
|
||||
+ Please checkout individual exchange README for more information on
|
||||
implementation
|
||||
|
||||
## Guide for adding a new exchange
|
||||
|
||||
+ A guide on implementing API support for a new exchange can be found [here](../docs/ADD_NEW_EXCHANGE.md)
|
||||
|
||||
### Please click GoDocs chevron above to view current GoDoc information for this package
|
||||
|
||||
## Contribution
|
||||
|
||||
@@ -210,8 +210,8 @@ func (a *Alphapoint) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (a *Alphapoint) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (a *Alphapoint) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -409,8 +409,8 @@ func (b *Binance) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (b *Binance) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (b *Binance) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -424,8 +424,8 @@ func (b *Bitfinex) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (b *Bitfinex) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (b *Bitfinex) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -280,8 +280,8 @@ func (b *Bitflyer) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (b *Bitflyer) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (b *Bitflyer) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -311,8 +311,8 @@ func (b *Bithumb) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (b *Bithumb) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (b *Bithumb) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -421,8 +421,8 @@ func (b *Bitmex) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (b *Bitmex) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (b *Bitmex) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -362,8 +362,8 @@ func (b *Bitstamp) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (b *Bitstamp) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (b *Bitstamp) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -339,8 +339,8 @@ func (b *Bittrex) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (b *Bittrex) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (b *Bittrex) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -369,8 +369,8 @@ func (b *BTCMarkets) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (b *BTCMarkets) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (b *BTCMarkets) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -348,8 +348,8 @@ func (b *BTSE) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (b *BTSE) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (b *BTSE) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -385,8 +385,8 @@ func (c *CoinbasePro) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (c *CoinbasePro) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (c *CoinbasePro) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -461,8 +461,8 @@ func (c *Coinbene) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (c *Coinbene) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (c *Coinbene) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
|
||||
@@ -462,8 +462,8 @@ func (c *COINUT) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (c *COINUT) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (c *COINUT) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -211,10 +211,12 @@ func (e *Base) GetAssetTypes() asset.Items {
|
||||
}
|
||||
|
||||
// GetPairAssetType returns the associated asset type for the currency pair
|
||||
// This method is only useful for exchanges that have pair names with multiple delimiters (BTC-USD-0626)
|
||||
// Helpful if the exchange has only a single asset type but in that case the asset type can be hard coded
|
||||
func (e *Base) GetPairAssetType(c currency.Pair) (asset.Item, error) {
|
||||
assetTypes := e.GetAssetTypes()
|
||||
for i := range assetTypes {
|
||||
if e.GetEnabledPairs(assetTypes[i]).Contains(c, true) {
|
||||
if e.GetAvailablePairs(assetTypes[i]).Contains(c, true) {
|
||||
return assetTypes[i], nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1428,7 +1428,7 @@ func TestGetAssetType(t *testing.T) {
|
||||
b.CurrencyPairs.AssetTypes = asset.Items{asset.Spot}
|
||||
b.CurrencyPairs.Pairs = make(map[asset.Item]*currency.PairStore)
|
||||
b.CurrencyPairs.Pairs[asset.Spot] = ¤cy.PairStore{
|
||||
Enabled: currency.Pairs{
|
||||
Available: currency.Pairs{
|
||||
currency.NewPair(currency.BTC, currency.USD),
|
||||
},
|
||||
ConfigFormat: ¤cy.PairFormat{Delimiter: "-"},
|
||||
|
||||
@@ -115,6 +115,7 @@ type TradeHistory struct {
|
||||
Amount float64
|
||||
Exchange string
|
||||
Type string
|
||||
Side string
|
||||
Fee float64
|
||||
Description string
|
||||
}
|
||||
|
||||
@@ -348,8 +348,8 @@ func (e *EXMO) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (e *EXMO) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (e *EXMO) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
133
exchanges/ftx/README.md
Normal file
133
exchanges/ftx/README.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# GoCryptoTrader package Ftx
|
||||
|
||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/page-logo.png?raw=true" width="350px" height="350px" hspace="70">
|
||||
|
||||
|
||||
[](https://travis-ci.org/thrasher-corp/gocryptotrader)
|
||||
[](https://github.com/thrasher-corp/gocryptotrader/blob/master/LICENSE)
|
||||
[](https://godoc.org/github.com/thrasher-corp/gocryptotrader/exchanges/ftx)
|
||||
[](http://codecov.io/github/thrasher-corp/gocryptotrader?branch=master)
|
||||
[](https://goreportcard.com/report/github.com/thrasher-corp/gocryptotrader)
|
||||
|
||||
|
||||
This ftx package is part of the GoCryptoTrader codebase.
|
||||
|
||||
## This is still in active development
|
||||
|
||||
You can track ideas, planned features and what's in progress on this Trello board: [https://trello.com/b/ZAhMhpOy/gocryptotrader](https://trello.com/b/ZAhMhpOy/gocryptotrader).
|
||||
|
||||
Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader Slack](https://join.slack.com/t/gocryptotrader/shared_invite/enQtNTQ5NDAxMjA2Mjc5LTc5ZDE1ZTNiOGM3ZGMyMmY1NTAxYWZhODE0MWM5N2JlZDk1NDU0YTViYzk4NTk3OTRiMDQzNGQ1YTc4YmRlMTk)
|
||||
|
||||
## FTX Exchange
|
||||
|
||||
### Current Features
|
||||
|
||||
+ REST Support
|
||||
+ Websocket Support
|
||||
|
||||
### How to enable
|
||||
|
||||
+ [Enable via configuration](https://github.com/thrasher-corp/gocryptotrader/tree/master/config#enable-exchange-via-config-example)
|
||||
|
||||
+ Individual package example below:
|
||||
|
||||
```go
|
||||
// Exchanges will be abstracted out in further updates and examples will be
|
||||
// supplied then
|
||||
```
|
||||
|
||||
### How to do REST public/private calls
|
||||
|
||||
+ If enabled via "configuration".json file the exchange will be added to the
|
||||
IBotExchange array in the ```go var bot Bot``` and you will only be able to use
|
||||
the wrapper interface functions for accessing exchange data. View routines.go
|
||||
for an example of integration usage with GoCryptoTrader. Rudimentary example
|
||||
below:
|
||||
|
||||
main.go
|
||||
```go
|
||||
var f exchange.IBotExchange
|
||||
|
||||
for i := range bot.Exchanges {
|
||||
if bot.Exchanges[i].GetName() == "FTX" {
|
||||
f = bot.Exchanges[i]
|
||||
}
|
||||
}
|
||||
|
||||
// Public calls - wrapper functions
|
||||
|
||||
// Fetches current ticker information
|
||||
tick, err := f.FetchTicker()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Fetches current orderbook information
|
||||
ob, err := f.FetchOrderbook()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Private calls - wrapper functions - make sure your APIKEY and APISECRET are
|
||||
// set and AuthenticatedAPISupport is set to true
|
||||
|
||||
// Fetches current account information
|
||||
accountInfo, err := f.GetAccountInfo()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
```
|
||||
|
||||
+ If enabled via individually importing package, rudimentary example below:
|
||||
|
||||
```go
|
||||
// Public calls
|
||||
|
||||
// Fetches current ticker information
|
||||
ticker, err := f.GetTicker()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Fetches current orderbook information
|
||||
ob, err := f.GetOrderBook()
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Private calls - make sure your APIKEY and APISECRET are set and
|
||||
// AuthenticatedAPISupport is set to true
|
||||
|
||||
// GetUserInfo returns account info
|
||||
accountInfo, err := f.GetUserInfo(...)
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
|
||||
// Submits an order and the exchange and returns its tradeID
|
||||
tradeID, err := f.Trade(...)
|
||||
if err != nil {
|
||||
// Handle error
|
||||
}
|
||||
```
|
||||
|
||||
### Please click GoDocs chevron above to view current GoDoc information for this package
|
||||
|
||||
## Contribution
|
||||
|
||||
Please feel free to submit any pull requests or suggest any desired features to be added.
|
||||
|
||||
When submitting a PR, please abide by our coding guidelines:
|
||||
|
||||
+ Code must adhere to the official Go [formatting](https://golang.org/doc/effective_go.html#formatting) guidelines (i.e. uses [gofmt](https://golang.org/cmd/gofmt/)).
|
||||
+ Code must be documented adhering to the official Go [commentary](https://golang.org/doc/effective_go.html#commentary) guidelines.
|
||||
+ Code must adhere to our [coding style](https://github.com/thrasher-corp/gocryptotrader/blob/master/doc/coding_style.md).
|
||||
+ Pull requests need to be based on and opened against the `master` branch.
|
||||
|
||||
## Donations
|
||||
|
||||
<img src="https://github.com/thrasher-corp/gocryptotrader/blob/master/web/src/assets/donate.png?raw=true" hspace="70">
|
||||
|
||||
If this framework helped you in any way, or you would like to support the developers working on it, please donate Bitcoin to:
|
||||
|
||||
***bc1qk0jareu4jytc0cfrhr5wgshsq8282awpavfahc***
|
||||
959
exchanges/ftx/ftx.go
Normal file
959
exchanges/ftx/ftx.go
Normal file
@@ -0,0 +1,959 @@
|
||||
package ftx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/common/crypto"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
|
||||
)
|
||||
|
||||
// FTX is the overarching type across this package
|
||||
type FTX struct {
|
||||
exchange.Base
|
||||
WebsocketConn *wshandler.WebsocketConnection
|
||||
}
|
||||
|
||||
const (
|
||||
ftxAPIURL = "https://ftx.com/api"
|
||||
|
||||
// Public endpoints
|
||||
getMarkets = "/markets"
|
||||
getMarket = "/markets/"
|
||||
getOrderbook = "/markets/%s/orderbook?depth=%s"
|
||||
getTrades = "/markets/%s/trades?"
|
||||
getHistoricalData = "/markets/%s/candles?"
|
||||
getFutures = "/futures"
|
||||
getFuture = "/futures/"
|
||||
getFutureStats = "/futures/%s/stats"
|
||||
getFundingRates = "/funding_rates"
|
||||
getIndexWeights = "/indexes/%s/weights"
|
||||
getAllWalletBalances = "/wallet/all_balances"
|
||||
|
||||
// Authenticated endpoints
|
||||
getAccountInfo = "/account"
|
||||
getPositions = "/positions"
|
||||
setLeverage = "/account/leverage"
|
||||
getCoins = "/wallet/coins"
|
||||
getBalances = "/wallet/balances"
|
||||
getDepositAddress = "/wallet/deposit_address/"
|
||||
getDepositHistory = "/wallet/deposits"
|
||||
getWithdrawalHistory = "/wallet/withdrawals"
|
||||
withdrawRequest = "/wallet/withdrawals"
|
||||
getOpenOrders = "/orders?"
|
||||
getOrderHistory = "/orders/history?"
|
||||
getOpenTriggerOrders = "/conditional_orders?"
|
||||
getTriggerOrderTriggers = "/conditional_orders/%s/triggers"
|
||||
getTriggerOrderHistory = "/conditional_orders/history?"
|
||||
placeOrder = "/orders"
|
||||
placeTriggerOrder = "/conditional_orders"
|
||||
modifyOrder = "/orders/%s/modify"
|
||||
modifyOrderByClientID = "/orders/by_client_id/%s/modify"
|
||||
modifyTriggerOrder = "/conditional_orders/%s/modify"
|
||||
getOrderStatus = "/orders/"
|
||||
getOrderStatusByClientID = "/orders/by_client_id/"
|
||||
deleteOrder = "/orders/"
|
||||
deleteOrderByClientID = "/orders/by_client_id/"
|
||||
cancelTriggerOrder = "/conditional_orders/"
|
||||
getFills = "/fills?"
|
||||
getFundingPayments = "/funding_payments?"
|
||||
getLeveragedTokens = "/lt/tokens"
|
||||
getTokenInfo = "/lt/"
|
||||
getLTBalances = "/lt/balances"
|
||||
getLTCreations = "/lt/creations"
|
||||
requestLTCreation = "/lt/%s/create"
|
||||
getLTRedemptions = "/lt/redemptions"
|
||||
requestLTRedemption = "/lt/%s/redeem"
|
||||
getListQuotes = "/options/requests"
|
||||
getMyQuotesRequests = "/options/my_requests"
|
||||
createQuoteRequest = "/options/requests"
|
||||
deleteQuote = "/options/requests/"
|
||||
endpointQuote = "/options/requests/%s/quotes"
|
||||
getMyQuotes = "/options/my_quotes"
|
||||
deleteMyQuote = "/options/quotes/"
|
||||
acceptQuote = "/options/quotes/%s/accept"
|
||||
getOptionsInfo = "/options/account_info"
|
||||
getOptionsPositions = "/options/positions"
|
||||
getPublicOptionsTrades = "/options/trades"
|
||||
getOptionsFills = "/options/fills"
|
||||
requestOTCQuote = "/otc/quotes"
|
||||
getOTCQuoteStatus = "/otc/quotes/"
|
||||
acceptOTCQuote = "/otc/quotes/%s/accept"
|
||||
|
||||
// Other Consts
|
||||
trailingStopOrderType = "trailingStop"
|
||||
takeProfitOrderType = "takeProfit"
|
||||
closedStatus = "closed"
|
||||
spotString = "spot"
|
||||
futuresString = "future"
|
||||
|
||||
ratePeriod = time.Second
|
||||
rateLimit = 30
|
||||
)
|
||||
|
||||
// GetMarkets gets market data
|
||||
func (f *FTX) GetMarkets() ([]MarketData, error) {
|
||||
resp := struct {
|
||||
Data []MarketData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendHTTPRequest(ftxAPIURL+getMarkets, &resp)
|
||||
}
|
||||
|
||||
// GetMarket gets market data for a provided asset type
|
||||
func (f *FTX) GetMarket(marketName string) (MarketData, error) {
|
||||
resp := struct {
|
||||
Data MarketData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendHTTPRequest(ftxAPIURL+getMarket+marketName,
|
||||
&resp)
|
||||
}
|
||||
|
||||
// GetOrderbook gets orderbook for a given market with a given depth (default depth 20)
|
||||
func (f *FTX) GetOrderbook(marketName string, depth int64) (OrderbookData, error) {
|
||||
result := struct {
|
||||
Data TempOBData `json:"result"`
|
||||
}{}
|
||||
strDepth := strconv.FormatInt(depth, 10)
|
||||
var resp OrderbookData
|
||||
err := f.SendHTTPRequest(fmt.Sprintf(ftxAPIURL+getOrderbook, marketName, strDepth), &result)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
resp.MarketName = marketName
|
||||
for x := range result.Data.Asks {
|
||||
resp.Asks = append(resp.Asks, OData{Price: result.Data.Asks[x][0],
|
||||
Size: result.Data.Asks[x][1],
|
||||
})
|
||||
}
|
||||
for y := range result.Data.Bids {
|
||||
resp.Bids = append(resp.Bids, OData{Price: result.Data.Bids[y][0],
|
||||
Size: result.Data.Bids[y][1],
|
||||
})
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetTrades gets trades based on the conditions specified
|
||||
func (f *FTX) GetTrades(marketName string, startTime, endTime time.Time, limit int64) ([]TradeData, error) {
|
||||
strLimit := strconv.FormatInt(limit, 10)
|
||||
params := url.Values{}
|
||||
params.Set("limit", strLimit)
|
||||
resp := struct {
|
||||
Data []TradeData `json:"result"`
|
||||
}{}
|
||||
if !startTime.IsZero() && !endTime.IsZero() {
|
||||
if startTime.After(endTime) {
|
||||
return resp.Data, errors.New("startTime cannot be after endTime")
|
||||
}
|
||||
params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10))
|
||||
params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10))
|
||||
}
|
||||
return resp.Data, f.SendHTTPRequest(fmt.Sprintf(ftxAPIURL+getTrades, marketName)+params.Encode(),
|
||||
&resp)
|
||||
}
|
||||
|
||||
// GetHistoricalData gets historical OHLCV data for a given market pair
|
||||
func (f *FTX) GetHistoricalData(marketName, timeInterval, limit string, startTime, endTime time.Time) ([]OHLCVData, error) {
|
||||
resp := struct {
|
||||
Data []OHLCVData `json:"result"`
|
||||
}{}
|
||||
params := url.Values{}
|
||||
params.Set("resolution", timeInterval)
|
||||
if limit != "" {
|
||||
params.Set("limit", limit)
|
||||
}
|
||||
if !startTime.IsZero() && !endTime.IsZero() {
|
||||
if startTime.After(endTime) {
|
||||
return resp.Data, errors.New("startTime cannot be after endTime")
|
||||
}
|
||||
params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10))
|
||||
params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10))
|
||||
}
|
||||
return resp.Data, f.SendHTTPRequest(fmt.Sprintf(ftxAPIURL+getHistoricalData, marketName)+params.Encode(), &resp)
|
||||
}
|
||||
|
||||
// GetFutures gets data on futures
|
||||
func (f *FTX) GetFutures() ([]FuturesData, error) {
|
||||
resp := struct {
|
||||
Data []FuturesData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendHTTPRequest(ftxAPIURL+getFutures, &resp)
|
||||
}
|
||||
|
||||
// GetFuture gets data on a given future
|
||||
func (f *FTX) GetFuture(futureName string) (FuturesData, error) {
|
||||
resp := struct {
|
||||
Data FuturesData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendHTTPRequest(ftxAPIURL+getFuture+futureName, &resp)
|
||||
}
|
||||
|
||||
// GetFutureStats gets data on a given future's stats
|
||||
func (f *FTX) GetFutureStats(futureName string) (FutureStatsData, error) {
|
||||
resp := struct {
|
||||
Data FutureStatsData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendHTTPRequest(fmt.Sprintf(ftxAPIURL+getFutureStats, futureName), &resp)
|
||||
}
|
||||
|
||||
// GetFundingRates gets data on funding rates
|
||||
func (f *FTX) GetFundingRates() ([]FundingRatesData, error) {
|
||||
resp := struct {
|
||||
Data []FundingRatesData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendHTTPRequest(ftxAPIURL+getFundingRates, &resp)
|
||||
}
|
||||
|
||||
// GetIndexWeights gets index weights
|
||||
func (f *FTX) GetIndexWeights(index string) (IndexWeights, error) {
|
||||
var resp IndexWeights
|
||||
return resp, f.SendHTTPRequest(ftxAPIURL+fmt.Sprintf(getIndexWeights, index), &resp)
|
||||
}
|
||||
|
||||
// SendHTTPRequest sends an unauthenticated HTTP request
|
||||
func (f *FTX) SendHTTPRequest(path string, result interface{}) error {
|
||||
return f.SendPayload(context.Background(), &request.Item{
|
||||
Method: http.MethodGet,
|
||||
Path: path,
|
||||
Result: result,
|
||||
Verbose: f.Verbose,
|
||||
HTTPDebugging: f.HTTPDebugging,
|
||||
HTTPRecording: f.HTTPRecording,
|
||||
})
|
||||
}
|
||||
|
||||
// GetAccountInfo gets account info
|
||||
func (f *FTX) GetAccountInfo() (AccountInfoData, error) {
|
||||
resp := struct {
|
||||
Data AccountInfoData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getAccountInfo, nil, &resp)
|
||||
}
|
||||
|
||||
// GetPositions gets the users positions
|
||||
func (f *FTX) GetPositions() ([]PositionData, error) {
|
||||
resp := struct {
|
||||
Data []PositionData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getPositions, nil, &resp)
|
||||
}
|
||||
|
||||
// ChangeAccountLeverage changes default leverage used by account
|
||||
func (f *FTX) ChangeAccountLeverage(leverage float64) error {
|
||||
req := make(map[string]interface{})
|
||||
req["leverage"] = leverage
|
||||
return f.SendAuthHTTPRequest(http.MethodPost, setLeverage, req, nil)
|
||||
}
|
||||
|
||||
// GetCoins gets coins' data in the account wallet
|
||||
func (f *FTX) GetCoins() ([]WalletCoinsData, error) {
|
||||
resp := struct {
|
||||
Data []WalletCoinsData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getCoins, nil, &resp)
|
||||
}
|
||||
|
||||
// GetBalances gets balances of the account
|
||||
func (f *FTX) GetBalances() ([]BalancesData, error) {
|
||||
resp := struct {
|
||||
Data []BalancesData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getBalances, nil, &resp)
|
||||
}
|
||||
|
||||
// GetAllWalletBalances gets all wallets' balances
|
||||
func (f *FTX) GetAllWalletBalances() (AllWalletAccountData, error) {
|
||||
resp := struct {
|
||||
Data AllWalletAccountData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getAllWalletBalances, nil, &resp)
|
||||
}
|
||||
|
||||
// FetchDepositAddress gets deposit address for a given coin
|
||||
func (f *FTX) FetchDepositAddress(coin string) (DepositData, error) {
|
||||
resp := struct {
|
||||
Data DepositData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getDepositAddress+coin, nil, &resp)
|
||||
}
|
||||
|
||||
// FetchDepositHistory gets deposit history
|
||||
func (f *FTX) FetchDepositHistory() ([]TransactionData, error) {
|
||||
resp := struct {
|
||||
Data []TransactionData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getDepositHistory, nil, &resp)
|
||||
}
|
||||
|
||||
// FetchWithdrawalHistory gets withdrawal history
|
||||
func (f *FTX) FetchWithdrawalHistory() ([]TransactionData, error) {
|
||||
resp := struct {
|
||||
Data []TransactionData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getWithdrawalHistory, nil, &resp)
|
||||
}
|
||||
|
||||
// Withdraw sends a withdrawal request
|
||||
func (f *FTX) Withdraw(coin, address, tag, password, code string, size float64) (TransactionData, error) {
|
||||
req := make(map[string]interface{})
|
||||
req["coin"] = coin
|
||||
req["address"] = address
|
||||
req["size"] = size
|
||||
if code != "" {
|
||||
req["code"] = code
|
||||
}
|
||||
if tag != "" {
|
||||
req["tag"] = tag
|
||||
}
|
||||
if password != "" {
|
||||
req["password"] = password
|
||||
}
|
||||
resp := struct {
|
||||
Data TransactionData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, withdrawRequest, req, &resp)
|
||||
}
|
||||
|
||||
// GetOpenOrders gets open orders
|
||||
func (f *FTX) GetOpenOrders(marketName string) ([]OrderData, error) {
|
||||
params := url.Values{}
|
||||
if marketName != "" {
|
||||
params.Set("market", marketName)
|
||||
}
|
||||
resp := struct {
|
||||
Data []OrderData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOpenOrders+params.Encode(), nil, &resp)
|
||||
}
|
||||
|
||||
// FetchOrderHistory gets order history
|
||||
func (f *FTX) FetchOrderHistory(marketName string, startTime, endTime time.Time, limit string) ([]OrderData, error) {
|
||||
resp := struct {
|
||||
Data []OrderData `json:"result"`
|
||||
}{}
|
||||
params := url.Values{}
|
||||
if marketName != "" {
|
||||
params.Set("market", marketName)
|
||||
}
|
||||
if !startTime.IsZero() && !endTime.IsZero() {
|
||||
if startTime.After(endTime) {
|
||||
return resp.Data, errors.New("startTime cannot be after endTime")
|
||||
}
|
||||
params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10))
|
||||
params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10))
|
||||
}
|
||||
if limit != "" {
|
||||
params.Set("limit", limit)
|
||||
}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOrderHistory+params.Encode(), nil, &resp)
|
||||
}
|
||||
|
||||
// GetOpenTriggerOrders gets trigger orders that are currently open
|
||||
func (f *FTX) GetOpenTriggerOrders(marketName, orderType string) ([]TriggerOrderData, error) {
|
||||
params := url.Values{}
|
||||
if marketName != "" {
|
||||
params.Set("market", marketName)
|
||||
}
|
||||
if orderType != "" {
|
||||
params.Set("type", orderType)
|
||||
}
|
||||
resp := struct {
|
||||
Data []TriggerOrderData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOpenTriggerOrders+params.Encode(), nil, &resp)
|
||||
}
|
||||
|
||||
// GetTriggerOrderTriggers gets trigger orders that are currently open
|
||||
func (f *FTX) GetTriggerOrderTriggers(orderID string) ([]TriggerData, error) {
|
||||
resp := struct {
|
||||
Data []TriggerData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, fmt.Sprintf(getTriggerOrderTriggers, orderID), nil, &resp)
|
||||
}
|
||||
|
||||
// GetTriggerOrderHistory gets trigger orders that are currently open
|
||||
func (f *FTX) GetTriggerOrderHistory(marketName string, startTime, endTime time.Time, side, orderType, limit string) ([]TriggerOrderData, error) {
|
||||
resp := struct {
|
||||
Data []TriggerOrderData `json:"result"`
|
||||
}{}
|
||||
params := url.Values{}
|
||||
if marketName != "" {
|
||||
params.Set("market", marketName)
|
||||
}
|
||||
if !startTime.IsZero() && !endTime.IsZero() {
|
||||
if startTime.After(endTime) {
|
||||
return resp.Data, errors.New("startTime cannot be after endTime")
|
||||
}
|
||||
params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10))
|
||||
params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10))
|
||||
}
|
||||
if side != "" {
|
||||
params.Set("side", side)
|
||||
}
|
||||
if orderType != "" {
|
||||
params.Set("type", orderType)
|
||||
}
|
||||
if limit != "" {
|
||||
params.Set("limit", limit)
|
||||
}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getTriggerOrderHistory+params.Encode(), nil, &resp)
|
||||
}
|
||||
|
||||
// Order places an order
|
||||
func (f *FTX) Order(marketName, side, orderType, reduceOnly, ioc, postOnly, clientID string, price, size float64) (OrderData, error) {
|
||||
req := make(map[string]interface{})
|
||||
req["market"] = marketName
|
||||
req["side"] = side
|
||||
req["price"] = price
|
||||
req["type"] = orderType
|
||||
req["size"] = size
|
||||
if reduceOnly != "" {
|
||||
req["reduceOnly"] = reduceOnly
|
||||
}
|
||||
if ioc != "" {
|
||||
req["ioc"] = ioc
|
||||
}
|
||||
if postOnly != "" {
|
||||
req["postOnly"] = postOnly
|
||||
}
|
||||
if clientID != "" {
|
||||
req["clientID"] = clientID
|
||||
}
|
||||
resp := struct {
|
||||
Data OrderData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, placeOrder, req, &resp)
|
||||
}
|
||||
|
||||
// TriggerOrder places an order
|
||||
func (f *FTX) TriggerOrder(marketName, side, orderType, reduceOnly, retryUntilFilled string, size, triggerPrice, orderPrice, trailValue float64) (TriggerOrderData, error) {
|
||||
req := make(map[string]interface{})
|
||||
req["market"] = marketName
|
||||
req["side"] = side
|
||||
req["type"] = orderType
|
||||
req["size"] = size
|
||||
if reduceOnly != "" {
|
||||
req["reduceOnly"] = reduceOnly
|
||||
}
|
||||
if retryUntilFilled != "" {
|
||||
req["retryUntilFilled"] = retryUntilFilled
|
||||
}
|
||||
if orderType == order.Stop.Lower() || orderType == "" {
|
||||
req["triggerPrice"] = triggerPrice
|
||||
req["orderPrice"] = orderPrice
|
||||
}
|
||||
if orderType == trailingStopOrderType {
|
||||
req["trailValue"] = trailValue
|
||||
}
|
||||
if orderType == takeProfitOrderType {
|
||||
req["triggerPrice"] = triggerPrice
|
||||
req["orderPrice"] = orderPrice
|
||||
}
|
||||
resp := struct {
|
||||
Data TriggerOrderData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, placeTriggerOrder, req, &resp)
|
||||
}
|
||||
|
||||
// ModifyPlacedOrder modifies a placed order
|
||||
func (f *FTX) ModifyPlacedOrder(orderID, clientID string, price, size float64) (OrderData, error) {
|
||||
req := make(map[string]interface{})
|
||||
req["price"] = price
|
||||
req["size"] = size
|
||||
if clientID != "" {
|
||||
req["clientID"] = clientID
|
||||
}
|
||||
resp := struct {
|
||||
Data OrderData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(modifyOrder, orderID), req, &resp)
|
||||
}
|
||||
|
||||
// ModifyOrderByClientID modifies a placed order via clientOrderID
|
||||
func (f *FTX) ModifyOrderByClientID(clientOrderID, clientID string, price, size float64) (OrderData, error) {
|
||||
req := make(map[string]interface{})
|
||||
req["price"] = price
|
||||
req["size"] = size
|
||||
if clientID != "" {
|
||||
req["clientID"] = clientID
|
||||
}
|
||||
resp := struct {
|
||||
Data OrderData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(modifyOrderByClientID, clientOrderID), req, &resp)
|
||||
}
|
||||
|
||||
// ModifyTriggerOrder modifies an existing trigger order
|
||||
// Choices for ordertype include stop, trailingStop, takeProfit
|
||||
func (f *FTX) ModifyTriggerOrder(orderID, orderType string, size, triggerPrice, orderPrice, trailValue float64) (TriggerOrderData, error) {
|
||||
req := make(map[string]interface{})
|
||||
req["size"] = size
|
||||
if orderType == order.Stop.Lower() || orderType == "" {
|
||||
req["triggerPrice"] = triggerPrice
|
||||
req["orderPrice"] = orderPrice
|
||||
}
|
||||
if orderType == trailingStopOrderType {
|
||||
req["trailValue"] = trailValue
|
||||
}
|
||||
if orderType == takeProfitOrderType {
|
||||
req["triggerPrice"] = triggerPrice
|
||||
req["orderPrice"] = orderPrice
|
||||
}
|
||||
resp := struct {
|
||||
Data TriggerOrderData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(modifyTriggerOrder, orderID), req, &resp)
|
||||
}
|
||||
|
||||
// GetOrderStatus gets the order status of a given orderID
|
||||
func (f *FTX) GetOrderStatus(orderID string) (OrderData, error) {
|
||||
resp := struct {
|
||||
Data OrderData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOrderStatus+orderID, nil, &resp)
|
||||
}
|
||||
|
||||
// GetOrderStatusByClientID gets the order status of a given clientOrderID
|
||||
func (f *FTX) GetOrderStatusByClientID(clientOrderID string) (OrderData, error) {
|
||||
resp := struct {
|
||||
Data OrderData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOrderStatusByClientID+clientOrderID, nil, &resp)
|
||||
}
|
||||
|
||||
// DeleteOrder deletes an order
|
||||
func (f *FTX) DeleteOrder(orderID string) (string, error) {
|
||||
resp := struct {
|
||||
Data string `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, deleteOrder+orderID, nil, &resp)
|
||||
}
|
||||
|
||||
// DeleteOrderByClientID deletes an order
|
||||
func (f *FTX) DeleteOrderByClientID(clientID string) (string, error) {
|
||||
resp := struct {
|
||||
Data string `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, deleteOrderByClientID+clientID, nil, &resp)
|
||||
}
|
||||
|
||||
// DeleteTriggerOrder deletes an order
|
||||
func (f *FTX) DeleteTriggerOrder(orderID string) (string, error) {
|
||||
resp := struct {
|
||||
Data string `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodDelete, cancelTriggerOrder+orderID, nil, &resp)
|
||||
}
|
||||
|
||||
// GetFills gets fills' data
|
||||
func (f *FTX) GetFills(market, limit string, startTime, endTime time.Time) ([]FillsData, error) {
|
||||
resp := struct {
|
||||
Data []FillsData `json:"result"`
|
||||
}{}
|
||||
params := url.Values{}
|
||||
if market != "" {
|
||||
params.Set("market", market)
|
||||
}
|
||||
if limit != "" {
|
||||
params.Set("limit", limit)
|
||||
}
|
||||
if !startTime.IsZero() && !endTime.IsZero() {
|
||||
if startTime.After(endTime) {
|
||||
return resp.Data, errors.New("startTime cannot be after endTime")
|
||||
}
|
||||
params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10))
|
||||
params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10))
|
||||
}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getFills+params.Encode(), nil, &resp)
|
||||
}
|
||||
|
||||
// GetFundingPayments gets funding payments
|
||||
func (f *FTX) GetFundingPayments(startTime, endTime time.Time, future string) ([]FundingPaymentsData, error) {
|
||||
resp := struct {
|
||||
Data []FundingPaymentsData `json:"result"`
|
||||
}{}
|
||||
params := url.Values{}
|
||||
if !startTime.IsZero() && !endTime.IsZero() {
|
||||
if startTime.After(endTime) {
|
||||
return resp.Data, errors.New("startTime cannot be after endTime")
|
||||
}
|
||||
params.Set("start_time", strconv.FormatInt(startTime.Unix(), 10))
|
||||
params.Set("end_time", strconv.FormatInt(endTime.Unix(), 10))
|
||||
}
|
||||
if future != "" {
|
||||
params.Set("future", future)
|
||||
}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getFundingPayments+params.Encode(), nil, &resp)
|
||||
}
|
||||
|
||||
// ListLeveragedTokens lists leveraged tokens
|
||||
func (f *FTX) ListLeveragedTokens() ([]LeveragedTokensData, error) {
|
||||
resp := struct {
|
||||
Data []LeveragedTokensData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getLeveragedTokens, nil, &resp)
|
||||
}
|
||||
|
||||
// GetTokenInfo gets token info
|
||||
func (f *FTX) GetTokenInfo(tokenName string) ([]LeveragedTokensData, error) {
|
||||
resp := struct {
|
||||
Data []LeveragedTokensData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getTokenInfo+tokenName, nil, &resp)
|
||||
}
|
||||
|
||||
// ListLTBalances gets leveraged tokens' balances
|
||||
func (f *FTX) ListLTBalances() ([]LTBalanceData, error) {
|
||||
resp := struct {
|
||||
Data []LTBalanceData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getLTBalances, nil, &resp)
|
||||
}
|
||||
|
||||
// ListLTCreations lists the leveraged tokens' creation requests
|
||||
func (f *FTX) ListLTCreations() ([]LTCreationData, error) {
|
||||
resp := struct {
|
||||
Data []LTCreationData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getLTCreations, nil, &resp)
|
||||
}
|
||||
|
||||
// RequestLTCreation sends a request to create a leveraged token
|
||||
func (f *FTX) RequestLTCreation(tokenName string, size float64) (RequestTokenCreationData, error) {
|
||||
req := make(map[string]interface{})
|
||||
req["size"] = size
|
||||
resp := struct {
|
||||
Data RequestTokenCreationData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(requestLTCreation, tokenName), req, &resp)
|
||||
}
|
||||
|
||||
// ListLTRedemptions lists the leveraged tokens' redemption requests
|
||||
func (f *FTX) ListLTRedemptions() ([]LTRedemptionData, error) {
|
||||
resp := struct {
|
||||
Data []LTRedemptionData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getLTRedemptions, nil, &resp)
|
||||
}
|
||||
|
||||
// RequestLTRedemption sends a request to redeem a leveraged token
|
||||
func (f *FTX) RequestLTRedemption(tokenName string, size float64) (LTRedemptionRequestData, error) {
|
||||
req := make(map[string]interface{})
|
||||
req["size"] = size
|
||||
resp := struct {
|
||||
Data LTRedemptionRequestData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(requestLTRedemption, tokenName), req, &resp)
|
||||
}
|
||||
|
||||
// GetQuoteRequests gets a list of quote requests
|
||||
func (f *FTX) GetQuoteRequests() ([]QuoteRequestData, error) {
|
||||
resp := struct {
|
||||
Data []QuoteRequestData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getListQuotes, nil, &resp)
|
||||
}
|
||||
|
||||
// GetYourQuoteRequests gets a list of your quote requests
|
||||
func (f *FTX) GetYourQuoteRequests() ([]PersonalQuotesData, error) {
|
||||
resp := struct {
|
||||
Data []PersonalQuotesData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getMyQuotesRequests, nil, &resp)
|
||||
}
|
||||
|
||||
// CreateQuoteRequest sends a request to create a quote
|
||||
func (f *FTX) CreateQuoteRequest(underlying, optionType, side string, expiry int64, requestExpiry string, strike, size, limitPrice, counterParyID float64, hideLimitPrice bool) (CreateQuoteRequestData, error) {
|
||||
req := make(map[string]interface{})
|
||||
req["underlying"] = underlying
|
||||
req["type"] = optionType
|
||||
req["side"] = side
|
||||
req["strike"] = strike
|
||||
req["expiry"] = expiry
|
||||
req["size"] = size
|
||||
if limitPrice != 0 {
|
||||
req["limitPrice"] = limitPrice
|
||||
}
|
||||
if requestExpiry != "" {
|
||||
req["requestExpiry"] = requestExpiry
|
||||
}
|
||||
if counterParyID != 0 {
|
||||
req["counterParyID"] = counterParyID
|
||||
}
|
||||
req["hideLimitPrice"] = hideLimitPrice
|
||||
resp := struct {
|
||||
Data CreateQuoteRequestData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, createQuoteRequest, req, &resp)
|
||||
}
|
||||
|
||||
// DeleteQuote sends request to cancel a quote
|
||||
func (f *FTX) DeleteQuote(requestID string) (CancelQuoteRequestData, error) {
|
||||
resp := struct {
|
||||
Data CancelQuoteRequestData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodDelete, deleteQuote+requestID, nil, &resp)
|
||||
}
|
||||
|
||||
// GetQuotesForYourQuote gets a list of quotes for your quote
|
||||
func (f *FTX) GetQuotesForYourQuote(requestID string) (QuoteForQuoteData, error) {
|
||||
var resp QuoteForQuoteData
|
||||
return resp, f.SendAuthHTTPRequest(http.MethodGet, fmt.Sprintf(endpointQuote, requestID), nil, &resp)
|
||||
}
|
||||
|
||||
// MakeQuote makes a quote for a quote
|
||||
func (f *FTX) MakeQuote(requestID, price string) ([]QuoteForQuoteData, error) {
|
||||
params := url.Values{}
|
||||
params.Set("price", price)
|
||||
resp := struct {
|
||||
Data []QuoteForQuoteData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(endpointQuote, requestID), nil, &resp)
|
||||
}
|
||||
|
||||
// MyQuotes gets a list of my quotes for quotes
|
||||
func (f *FTX) MyQuotes() ([]QuoteForQuoteData, error) {
|
||||
resp := struct {
|
||||
Data []QuoteForQuoteData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getMyQuotes, nil, &resp)
|
||||
}
|
||||
|
||||
// DeleteMyQuote deletes my quote for quotes
|
||||
func (f *FTX) DeleteMyQuote(quoteID string) ([]QuoteForQuoteData, error) {
|
||||
resp := struct {
|
||||
Data []QuoteForQuoteData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodDelete, deleteMyQuote+quoteID, nil, &resp)
|
||||
}
|
||||
|
||||
// AcceptQuote accepts the quote for quote
|
||||
func (f *FTX) AcceptQuote(quoteID string) ([]QuoteForQuoteData, error) {
|
||||
resp := struct {
|
||||
Data []QuoteForQuoteData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(acceptQuote, quoteID), nil, &resp)
|
||||
}
|
||||
|
||||
// GetAccountOptionsInfo gets account's options' info
|
||||
func (f *FTX) GetAccountOptionsInfo() (AccountOptionsInfoData, error) {
|
||||
resp := struct {
|
||||
Data AccountOptionsInfoData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOptionsInfo, nil, &resp)
|
||||
}
|
||||
|
||||
// GetOptionsPositions gets options' positions
|
||||
func (f *FTX) GetOptionsPositions() ([]OptionsPositionsData, error) {
|
||||
resp := struct {
|
||||
Data []OptionsPositionsData `json:"result"`
|
||||
}{}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOptionsPositions, nil, &resp)
|
||||
}
|
||||
|
||||
// GetPublicOptionsTrades gets options' trades from public
|
||||
func (f *FTX) GetPublicOptionsTrades(startTime, endTime time.Time, limit string) ([]OptionsTradesData, error) {
|
||||
resp := struct {
|
||||
Data []OptionsTradesData `json:"result"`
|
||||
}{}
|
||||
req := make(map[string]interface{})
|
||||
if !startTime.IsZero() && !endTime.IsZero() {
|
||||
req["start_time"] = strconv.FormatInt(startTime.Unix(), 10)
|
||||
req["end_time"] = strconv.FormatInt(endTime.Unix(), 10)
|
||||
if startTime.After(endTime) {
|
||||
return resp.Data, errors.New("startTime cannot be after endTime")
|
||||
}
|
||||
}
|
||||
if limit != "" {
|
||||
req["limit"] = limit
|
||||
}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getPublicOptionsTrades, req, &resp)
|
||||
}
|
||||
|
||||
// GetOptionsFills gets fills data for options
|
||||
func (f *FTX) GetOptionsFills(startTime, endTime time.Time, limit string) ([]OptionFillsData, error) {
|
||||
resp := struct {
|
||||
Data []OptionFillsData `json:"result"`
|
||||
}{}
|
||||
req := make(map[string]interface{})
|
||||
if !startTime.IsZero() && !endTime.IsZero() {
|
||||
req["start_time"] = strconv.FormatInt(startTime.Unix(), 10)
|
||||
req["end_time"] = strconv.FormatInt(endTime.Unix(), 10)
|
||||
if startTime.After(endTime) {
|
||||
return resp.Data, errors.New("startTime cannot be after endTime")
|
||||
}
|
||||
}
|
||||
if limit != "" {
|
||||
req["limit"] = limit
|
||||
}
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOptionsFills, req, &resp)
|
||||
}
|
||||
|
||||
// SendAuthHTTPRequest sends an authenticated request
|
||||
func (f *FTX) SendAuthHTTPRequest(method, path string, data, result interface{}) error {
|
||||
ts := strconv.FormatInt(time.Now().UnixNano()/1000000, 10)
|
||||
var body io.Reader
|
||||
var hmac, payload []byte
|
||||
var err error
|
||||
if data != nil {
|
||||
payload, err = json.Marshal(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
body = bytes.NewBuffer(payload)
|
||||
sigPayload := ts + method + "/api" + path + string(payload)
|
||||
hmac = crypto.GetHMAC(crypto.HashSHA256, []byte(sigPayload), []byte(f.API.Credentials.Secret))
|
||||
} else {
|
||||
sigPayload := ts + method + "/api" + path
|
||||
hmac = crypto.GetHMAC(crypto.HashSHA256, []byte(sigPayload), []byte(f.API.Credentials.Secret))
|
||||
}
|
||||
headers := make(map[string]string)
|
||||
headers["FTX-KEY"] = f.API.Credentials.Key
|
||||
headers["FTX-SIGN"] = crypto.HexEncodeToString(hmac)
|
||||
headers["FTX-TS"] = ts
|
||||
headers["Content-Type"] = "application/json"
|
||||
return f.SendPayload(context.Background(), &request.Item{
|
||||
Method: method,
|
||||
Path: ftxAPIURL + path,
|
||||
Headers: headers,
|
||||
Body: body,
|
||||
Result: result,
|
||||
AuthRequest: true,
|
||||
Verbose: f.Verbose,
|
||||
HTTPDebugging: f.HTTPDebugging,
|
||||
HTTPRecording: f.HTTPRecording,
|
||||
})
|
||||
}
|
||||
|
||||
// GetFee returns an estimate of fee based on type of transaction
|
||||
func (f *FTX) GetFee(feeBuilder *exchange.FeeBuilder) (float64, error) {
|
||||
var fee float64
|
||||
switch feeBuilder.FeeType {
|
||||
case exchange.OfflineTradeFee:
|
||||
fee = getOfflineTradeFee(feeBuilder)
|
||||
default:
|
||||
feeData, err := f.GetAccountInfo()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch feeBuilder.IsMaker {
|
||||
case true:
|
||||
fee = feeData.MakerFee * feeBuilder.Amount * feeBuilder.PurchasePrice
|
||||
case false:
|
||||
fee = feeData.TakerFee * feeBuilder.Amount * feeBuilder.PurchasePrice
|
||||
}
|
||||
if fee < 0 {
|
||||
fee = 0
|
||||
}
|
||||
}
|
||||
return fee, nil
|
||||
}
|
||||
|
||||
// getOfflineTradeFee calculates the worst case-scenario trading fee
|
||||
func getOfflineTradeFee(feeBuilder *exchange.FeeBuilder) float64 {
|
||||
if feeBuilder.IsMaker {
|
||||
return 0.0002 * feeBuilder.PurchasePrice * feeBuilder.Amount
|
||||
}
|
||||
return 0.0007 * feeBuilder.PurchasePrice * feeBuilder.Amount
|
||||
}
|
||||
|
||||
func parseInterval(in time.Duration) (TimeInterval, error) {
|
||||
switch in {
|
||||
case kline.FifteenSecond:
|
||||
return TimeIntervalFifteenSeconds, nil
|
||||
case kline.OneMin:
|
||||
return TimeIntervalMinute, nil
|
||||
case kline.FiveMin:
|
||||
return TimeIntervalFiveMinutes, nil
|
||||
case kline.FifteenMin:
|
||||
return TimeIntervalFifteenMinutes, nil
|
||||
case kline.OneHour:
|
||||
return TimeIntervalHour, nil
|
||||
case kline.FourHour:
|
||||
return TimeIntervalFourHours, nil
|
||||
case kline.OneDay:
|
||||
return TimeIntervalDay, nil
|
||||
default:
|
||||
return TimeIntervalMinute, errInvalidInterval
|
||||
}
|
||||
}
|
||||
|
||||
func (f *FTX) compatibleOrderVars(orderSide, orderStatus, orderType string, amount, filledAmount, avgFillPrice float64) (OrderVars, error) {
|
||||
var resp OrderVars
|
||||
switch orderSide {
|
||||
case order.Buy.Lower():
|
||||
resp.Side = order.Buy
|
||||
case order.Sell.Lower():
|
||||
resp.Side = order.Sell
|
||||
}
|
||||
switch orderStatus {
|
||||
case strings.ToLower(order.New.String()):
|
||||
resp.Status = order.New
|
||||
case strings.ToLower(order.Open.String()):
|
||||
resp.Status = order.Open
|
||||
case closedStatus:
|
||||
if filledAmount != 0 && filledAmount != amount {
|
||||
resp.Status = order.PartiallyCancelled
|
||||
}
|
||||
if filledAmount == 0 {
|
||||
resp.Status = order.Cancelled
|
||||
}
|
||||
if filledAmount == amount {
|
||||
resp.Status = order.Filled
|
||||
}
|
||||
}
|
||||
var feeBuilder exchange.FeeBuilder
|
||||
feeBuilder.PurchasePrice = avgFillPrice
|
||||
feeBuilder.Amount = amount
|
||||
resp.OrderType = order.Market
|
||||
if strings.EqualFold(orderType, order.Limit.String()) {
|
||||
resp.OrderType = order.Limit
|
||||
feeBuilder.IsMaker = true
|
||||
}
|
||||
fee, err := f.GetFee(&feeBuilder)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
resp.Fee = fee
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// RequestForQuotes requests for otc quotes
|
||||
func (f *FTX) RequestForQuotes(base, quote string, amount float64) (RequestQuoteData, error) {
|
||||
resp := struct {
|
||||
Data RequestQuoteData `json:"result"`
|
||||
}{}
|
||||
req := make(map[string]interface{})
|
||||
req["fromCoin"] = base
|
||||
req["toCoin"] = quote
|
||||
req["size"] = amount
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodPost, requestOTCQuote, req, &resp)
|
||||
}
|
||||
|
||||
// GetOTCQuoteStatus gets quote status of a quote
|
||||
func (f *FTX) GetOTCQuoteStatus(marketName, quoteID string) ([]QuoteStatusData, error) {
|
||||
resp := struct {
|
||||
Data []QuoteStatusData `json:"result"`
|
||||
}{}
|
||||
params := url.Values{}
|
||||
params.Set("market", marketName)
|
||||
return resp.Data, f.SendAuthHTTPRequest(http.MethodGet, getOTCQuoteStatus+quoteID, params, &resp)
|
||||
}
|
||||
|
||||
// AcceptOTCQuote requests for otc quotes
|
||||
func (f *FTX) AcceptOTCQuote(quoteID string) error {
|
||||
return f.SendAuthHTTPRequest(http.MethodPost, fmt.Sprintf(acceptOTCQuote, quoteID), nil, nil)
|
||||
}
|
||||
1188
exchanges/ftx/ftx_test.go
Normal file
1188
exchanges/ftx/ftx_test.go
Normal file
File diff suppressed because it is too large
Load Diff
726
exchanges/ftx/ftx_types.go
Normal file
726
exchanges/ftx/ftx_types.go
Normal file
@@ -0,0 +1,726 @@
|
||||
package ftx
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
)
|
||||
|
||||
// MarketData stores market data
|
||||
type MarketData struct {
|
||||
Name string `json:"name"`
|
||||
BaseCurrency string `json:"baseCurrency"`
|
||||
QuoteCurrency string `json:"quoteCurrency"`
|
||||
MarketType string `json:"type"`
|
||||
Underlying string `json:"underlying"`
|
||||
Change1h float64 `json:"change1h"`
|
||||
Change24h float64 `json:"change24h"`
|
||||
ChangeBod float64 `json:"changeBod"`
|
||||
QuoteVolume24h float64 `json:"quoteVolume24h"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Ask float64 `json:"ask"`
|
||||
Bid float64 `json:"bid"`
|
||||
Last float64 `json:"last"`
|
||||
USDVolume24h float64 `json:"volumeUSD24h"`
|
||||
MinProvideSize float64 `json:"minProvideSize"`
|
||||
PriceIncrement float64 `json:"priceIncrement"`
|
||||
SizeIncrement float64 `json:"sizeIncrement"`
|
||||
Restricted bool `json:"restricted"`
|
||||
}
|
||||
|
||||
// OData stores orderdata in orderbook
|
||||
type OData struct {
|
||||
Price float64
|
||||
Size float64
|
||||
}
|
||||
|
||||
// OrderbookData stores orderbook data
|
||||
type OrderbookData struct {
|
||||
MarketName string
|
||||
Asks []OData
|
||||
Bids []OData
|
||||
}
|
||||
|
||||
// TempOBData stores orderbook data temporarily
|
||||
type TempOBData struct {
|
||||
Asks [][2]float64 `json:"asks"`
|
||||
Bids [][2]float64 `json:"bids"`
|
||||
}
|
||||
|
||||
// TradeData stores data from trades
|
||||
type TradeData struct {
|
||||
ID int64 `json:"id"`
|
||||
Liquidation bool `json:"liquidation"`
|
||||
Price float64 `json:"price"`
|
||||
Side string `json:"side"`
|
||||
Size float64 `json:"size"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
// OHLCVData stores historical OHLCV data
|
||||
type OHLCVData struct {
|
||||
Close float64 `json:"close"`
|
||||
High float64 `json:"high"`
|
||||
Low float64 `json:"low"`
|
||||
Open float64 `json:"open"`
|
||||
StartTime time.Time `json:"startTime"`
|
||||
Time float64 `json:"time"`
|
||||
Volume float64 `json:"volume"`
|
||||
}
|
||||
|
||||
// FuturesData stores data for futures
|
||||
type FuturesData struct {
|
||||
Ask float64 `json:"ask"`
|
||||
Bid float64 `json:"bid"`
|
||||
Change1h float64 `json:"change1h"`
|
||||
Change24h float64 `json:"change24h"`
|
||||
ChangeBod float64 `json:"changeBod"`
|
||||
VolumeUSD24h float64 `json:"volumeUsd24h"`
|
||||
Volume float64 `json:"volume"`
|
||||
Description string `json:"description"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Expired bool `json:"expired"`
|
||||
Expiry time.Time `json:"expiry"`
|
||||
ExpiryDescription string `json:"expiryDescription"`
|
||||
Group string `json:"group"`
|
||||
Index float64 `json:"index"`
|
||||
IMFFactor float64 `json:"imfFactor"`
|
||||
Last float64 `json:"last"`
|
||||
LowerBound float64 `json:"lowerBound"`
|
||||
MarginPrice float64 `json:"marginPrice"`
|
||||
Mark float64 `json:"mark"`
|
||||
MoveStart interface{} `json:"moveStart"`
|
||||
Name string `json:"name"`
|
||||
Perpetual bool `json:"perpetual"`
|
||||
PositionLimitWeight float64 `json:"positionLimitWeight"`
|
||||
PostOnly bool `json:"postOnly"`
|
||||
PriceIncrement float64 `json:"priceIncrement"`
|
||||
SizeIncrement float64 `json:"sizeIncrement"`
|
||||
Underlying string `json:"underlying"`
|
||||
UpperBound float64 `json:"upperBound"`
|
||||
FutureType string `json:"type"`
|
||||
}
|
||||
|
||||
// FutureStatsData stores data on futures stats
|
||||
type FutureStatsData struct {
|
||||
Volume float64 `json:"volume"`
|
||||
NextFundingRate float64 `json:"nextFundingRate"`
|
||||
NextFundingTime time.Time `json:"nextFundingTime"`
|
||||
ExpirationPrice float64 `json:"expirationPrice"`
|
||||
PredictedExpirationPrice float64 `json:"predictedExpirationPrice"`
|
||||
OpenInterest float64 `json:"openInterest"`
|
||||
StrikePrice float64 `json:"strikePrice"`
|
||||
}
|
||||
|
||||
// FundingRatesData stores data on funding rates
|
||||
type FundingRatesData struct {
|
||||
Future string `json:"future"`
|
||||
Rate float64 `json:"rate"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
// IndexWeights stores index weights' data
|
||||
type IndexWeights struct {
|
||||
Result map[string]float64 `json:"result"`
|
||||
}
|
||||
|
||||
// PositionData stores data of an open position
|
||||
type PositionData struct {
|
||||
Cost float64 `json:"cost"`
|
||||
EntryPrice float64 `json:"entryPrice"`
|
||||
Future string `json:"future"`
|
||||
InitialMarginRequirement float64 `json:"initialMarginRequirement"`
|
||||
LongOrderSize float64 `json:"longOrderSize"`
|
||||
MaintenanceMarginRequirement float64 `json:"maintenanceMarginRequirement"`
|
||||
NetSize float64 `json:"netSize"`
|
||||
OpenSize float64 `json:"openSize"`
|
||||
RealisedPnL float64 `json:"realisedPnL"`
|
||||
ShortOrderSide float64 `json:"shortOrderSide"`
|
||||
Side string `json:"side"`
|
||||
Size float64 `json:"size"`
|
||||
UnrealisedPnL float64 `json:"unrealisedPnL"`
|
||||
}
|
||||
|
||||
// AccountInfoData stores account data
|
||||
type AccountInfoData struct {
|
||||
BackstopProvider bool `json:"backstopProvider"`
|
||||
ChargeInterestOnNegativeUSD bool `json:"chargeInterestOnNegativeUsd"`
|
||||
Collateral float64 `json:"collateral"`
|
||||
FreeCollateral float64 `json:"freeCollateral"`
|
||||
InitialMarginRequirement float64 `json:"initialMarginRequirement"`
|
||||
Leverage float64 `json:"float64"`
|
||||
Liquidating bool `json:"liquidating"`
|
||||
MaintenanceMarginRequirement float64 `json:"maintenanceMarginRequirement"`
|
||||
MakerFee float64 `json:"makerFee"`
|
||||
MarginFraction float64 `json:"marginFraction"`
|
||||
OpenMarginFraction float64 `json:"openMarginFraction"`
|
||||
PositionLimit float64 `json:"positionLimit"`
|
||||
PositionLimitUsed float64 `json:"positionLimitUsed"`
|
||||
SpotLendingEnabled bool `json:"spotLendingEnabled"`
|
||||
SpotMarginEnabled bool `json:"spotMarginEnabled"`
|
||||
TakerFee float64 `json:"takerFee"`
|
||||
TotalAccountValue float64 `json:"totalAccountValue"`
|
||||
TotalPositionSize float64 `json:"totalPositionSize"`
|
||||
UseFTTCollateral bool `json:"useFttCollateral"`
|
||||
Username string `json:"username"`
|
||||
Positions []PositionData `json:"positions"`
|
||||
}
|
||||
|
||||
// WalletCoinsData stores data about wallet coins
|
||||
type WalletCoinsData struct {
|
||||
Bep2Asset interface{} `json:"bep2Asset"`
|
||||
CanConvert bool `json:"canConvert"`
|
||||
CanDeposit bool `json:"canDeposit"`
|
||||
CanWithdraw bool `json:"canWithdraw"`
|
||||
Collateral bool `json:"collateral"`
|
||||
CollateralWeight float64 `json:"collateralWeight"`
|
||||
CreditTo interface{} `json:"creditTo"`
|
||||
ERC20Contract interface{} `json:"erc20Contract"`
|
||||
Fiat bool `json:"fiat"`
|
||||
HasTag bool `json:"hasTag"`
|
||||
Hidden bool `json:"hidden"`
|
||||
IsETF bool `json:"isEtf"`
|
||||
IsToken bool `json:"isToken"`
|
||||
Methods []interface{}
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
// BalancesData stores balances data
|
||||
type BalancesData struct {
|
||||
Coin string `json:"coin"`
|
||||
Free float64 `json:"free"`
|
||||
Total float64 `json:"total"`
|
||||
}
|
||||
|
||||
// AllWalletAccountData stores account data on all WalletCoins
|
||||
type AllWalletAccountData struct {
|
||||
Main []BalancesData `json:"main"`
|
||||
BattleRoyale []BalancesData `json:"Battle Royale"`
|
||||
}
|
||||
|
||||
// DepositData stores deposit address data
|
||||
type DepositData struct {
|
||||
Address string `json:"address"`
|
||||
Tag string `json:"tag"`
|
||||
}
|
||||
|
||||
// TransactionData stores data about deposit history
|
||||
type TransactionData struct {
|
||||
Coin string `json:"coin"`
|
||||
Confirmations int64 `json:"conformations"`
|
||||
ConfirmedTime time.Time `json:"confirmedTime"`
|
||||
Fee float64 `json:"fee"`
|
||||
ID int64 `json:"id"`
|
||||
SentTime time.Time `json:"sentTime"`
|
||||
Size float64 `json:"size"`
|
||||
Status string `json:"status"`
|
||||
Time time.Time `json:"time"`
|
||||
TxID string `json:"txid"`
|
||||
}
|
||||
|
||||
// OrderData stores open order data
|
||||
type OrderData struct {
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
FilledSize float64 `json:"filledSize"`
|
||||
Future string `json:"future"`
|
||||
ID int64 `json:"id"`
|
||||
Market string `json:"market"`
|
||||
Price float64 `json:"price"`
|
||||
AvgFillPrice float64 `json:"avgFillPrice"`
|
||||
RemainingSize float64 `json:"remainingSize"`
|
||||
Side string `json:"side"`
|
||||
Size float64 `json:"size"`
|
||||
Status string `json:"status"`
|
||||
OrderType string `json:"type"`
|
||||
ReduceOnly bool `json:"reduceOnly"`
|
||||
IOC bool `json:"ioc"`
|
||||
PostOnly bool `json:"postOnly"`
|
||||
ClientID string `json:"clientId"`
|
||||
}
|
||||
|
||||
// TriggerOrderData stores trigger order data
|
||||
type TriggerOrderData struct {
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Error string `json:"error"`
|
||||
Future string `json:"future"`
|
||||
ID int64 `json:"id"`
|
||||
Market string `json:"market"`
|
||||
OrderID int64 `json:"orderId"`
|
||||
OrderPrice float64 `json:"orderPrice"`
|
||||
ReduceOnly bool `json:"reduceOnly"`
|
||||
Side string `json:"side"`
|
||||
Size float64 `json:"size"`
|
||||
Status string `json:"status"`
|
||||
TrailStart float64 `json:"trailStart"`
|
||||
TrailValue float64 `json:"trailvalue"`
|
||||
TriggerPrice float64 `json:"triggerPrice"`
|
||||
TriggeredAt string `json:"triggeredAt"`
|
||||
OrderType string `json:"type"`
|
||||
MarketOrLimit string `json:"orderType"`
|
||||
FilledSize float64 `json:"filledSize"`
|
||||
AvgFillPrice float64 `json:"avgFillPrice"`
|
||||
RetryUntilFilled bool `json:"retryUntilFilled"`
|
||||
}
|
||||
|
||||
// TriggerData stores trigger orders' trigger data
|
||||
type TriggerData struct {
|
||||
Error string `json:"error"`
|
||||
FilledSize float64 `json:"filledSize"`
|
||||
OrderSize float64 `json:"orderSize"`
|
||||
OrderID int64 `json:"orderId"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
// FillsData stores fills' data
|
||||
type FillsData struct {
|
||||
Fee float64 `json:"fee"`
|
||||
FeeRate float64 `json:"feeRate"`
|
||||
Future string `json:"future"`
|
||||
ID string `json:"id"`
|
||||
Liquidity string `json:"liquidity"`
|
||||
Market string `json:"market"`
|
||||
BaseCurrency string `json:"baseCurrency"`
|
||||
QuoteCurrency string `json:"quoteCurrency"`
|
||||
OrderID string `json:"orderID"`
|
||||
TradeID string `json:"tradeID"`
|
||||
Price float64 `json:"price"`
|
||||
Side string `json:"side"`
|
||||
Size float64 `json:"size"`
|
||||
Time time.Time `json:"time"`
|
||||
OrderType string `json:"type"`
|
||||
}
|
||||
|
||||
// FundingPaymentsData stores funding payments' data
|
||||
type FundingPaymentsData struct {
|
||||
Future string `json:"future"`
|
||||
ID string `json:"id"`
|
||||
Payment float64 `json:"payment"`
|
||||
Time time.Time `json:"time"`
|
||||
Rate float64 `json:"rate"`
|
||||
}
|
||||
|
||||
// LeveragedTokensData stores data of leveraged tokens
|
||||
type LeveragedTokensData struct {
|
||||
Basket map[string]interface{} `json:"basket"`
|
||||
Bep2AssetName string `json:"bep2AssetName"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Underlying string `json:"underlying"`
|
||||
Leverage float64 `json:"leverage"`
|
||||
Outstanding float64 `json:"outstanding"`
|
||||
PricePerShare float64 `json:"pricePerShare"`
|
||||
PositionPerShare float64 `json:"positionPerShare"`
|
||||
PositionsPerShare interface{} `json:"positionsPerShare"`
|
||||
TargetComponents []string `json:"targetComponents"`
|
||||
TotalCollateral float64 `json:"totalCollateral"`
|
||||
TotalNav float64 `json:"totalNav"`
|
||||
UnderlyingMark float64 `json:"underlyingMark"`
|
||||
ContactAddress string `json:"contactAddress"`
|
||||
Change1h float64 `json:"change1h"`
|
||||
Change24h float64 `json:"change24h"`
|
||||
ChangeBod float64 `json:"changeBod"`
|
||||
}
|
||||
|
||||
// LTBalanceData stores balances of leveraged tokens
|
||||
type LTBalanceData struct {
|
||||
Token string `json:"token"`
|
||||
Balance float64 `json:"balance"`
|
||||
}
|
||||
|
||||
// LTCreationData stores token creation requests' data
|
||||
type LTCreationData struct {
|
||||
ID string `json:"id"`
|
||||
Token string `json:"token"`
|
||||
RequestedSize float64 `json:"requestedSize"`
|
||||
Pending bool `json:"pending"`
|
||||
CreatedSize float64 `json:"createdize"`
|
||||
Price float64 `json:"price"`
|
||||
Cost float64 `json:"cost"`
|
||||
Fee float64 `json:"fee"`
|
||||
RequestedAt time.Time `json:"requestedAt"`
|
||||
FulfilledAt time.Time `json:"fulfilledAt"`
|
||||
}
|
||||
|
||||
// RequestTokenCreationData stores data of the token creation requested
|
||||
type RequestTokenCreationData struct {
|
||||
ID string `json:"id"`
|
||||
Token string `json:"token"`
|
||||
RequestedSize float64 `json:"requestedSize"`
|
||||
Cost float64 `json:"cost"`
|
||||
Pending bool `json:"pending"`
|
||||
RequestedAt time.Time `json:"requestedAt"`
|
||||
}
|
||||
|
||||
// LTRedemptionData stores data of the token redemption request
|
||||
type LTRedemptionData struct {
|
||||
ID int64 `json:"id"`
|
||||
Token string `json:"token"`
|
||||
Size float64 `json:"size"`
|
||||
Pending bool `json:"pending"`
|
||||
Price float64 `json:"price"`
|
||||
Proceeds float64 `json:"proceeds"`
|
||||
Fee float64 `json:"fee"`
|
||||
RequestedAt time.Time `json:"requestedAt"`
|
||||
FulfilledAt time.Time `json:"fulfilledAt"`
|
||||
}
|
||||
|
||||
// LTRedemptionRequestData stores redemption request data for a leveraged token
|
||||
type LTRedemptionRequestData struct {
|
||||
ID string `json:"id"`
|
||||
Token string `json:"token"`
|
||||
Size float64 `json:"size"`
|
||||
ProjectedProceeds float64 `json:"projectedProceeds"`
|
||||
Pending bool `json:"pending"`
|
||||
RequestedAt time.Time `json:"requestedAt"`
|
||||
}
|
||||
|
||||
// OptionData stores options' data
|
||||
type OptionData struct {
|
||||
Underlying string `json:"underlying"`
|
||||
OptionType string `json:"type"`
|
||||
Strike float64 `json:"strike"`
|
||||
Expiry time.Time `json:"expiry"`
|
||||
}
|
||||
|
||||
// QuoteRequestData stores option's quote request data
|
||||
type QuoteRequestData struct {
|
||||
ID int64 `json:"id"`
|
||||
Option OptionData `json:"option"`
|
||||
Side string `json:"side"`
|
||||
Size float64 `json:"size"`
|
||||
Time time.Time `json:"time"`
|
||||
RequestExpiry string `json:"requestExpiry"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
// QuoteData stores quote's data
|
||||
type QuoteData struct {
|
||||
Collateral float64 `json:"collateral"`
|
||||
ID int64 `json:"id"`
|
||||
Price float64 `json:"price"`
|
||||
QuoteExpiry string `json:"quoteExpiry"`
|
||||
Status string `json:"status"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
// PersonalQuotesData stores data of your quotes
|
||||
type PersonalQuotesData struct {
|
||||
ID int64 `json:"id"`
|
||||
Option OptionData `json:"option"`
|
||||
Side string `json:"side"`
|
||||
Size float64 `json:"size"`
|
||||
Time time.Time `json:"time"`
|
||||
RequestExpiry string `json:"requestExpiry"`
|
||||
Status string `json:"status"`
|
||||
HideLimitPrice bool `json:"hideLimitPrice"`
|
||||
LimitPrice float64 `json:"limitPrice"`
|
||||
Quotes []QuoteData `json:"quotes"`
|
||||
}
|
||||
|
||||
// CreateQuoteRequestData stores quote data of the request sent
|
||||
type CreateQuoteRequestData struct {
|
||||
ID int64 `json:"id"`
|
||||
Expiry time.Time `json:"expiry"`
|
||||
Strike float64 `json:"strike"`
|
||||
OptionType string `json:"type"`
|
||||
Underlying string `json:"underlying"`
|
||||
RequestExpiry string `json:"requestExpiry"`
|
||||
Side string `json:"side"`
|
||||
Size float64 `json:"size"`
|
||||
Status string `json:"status"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
// CancelQuoteRequestData stores cancel quote request data
|
||||
type CancelQuoteRequestData struct {
|
||||
ID int64 `json:"id"`
|
||||
Option OptionData `json:"option"`
|
||||
RequestExpiry string `json:"requestExpiry"`
|
||||
Side string `json:"side"`
|
||||
Size float64 `json:"size"`
|
||||
Status string `json:"status"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
// QuoteForQuoteData gets quote data for your quote
|
||||
type QuoteForQuoteData struct {
|
||||
Collateral float64 `json:"collateral"`
|
||||
ID int64 `json:"id"`
|
||||
Option OptionData `json:"option"`
|
||||
Price float64 `json:"price"`
|
||||
QuoteExpiry string `json:"quoteExpiry"`
|
||||
QuoterSide string `json:"quoterSide"`
|
||||
RequestID int64 `json:"requestID"`
|
||||
RequestSide string `json:"requestSide"`
|
||||
Size float64 `json:"size"`
|
||||
Status string `json:"status"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
// AccountOptionsInfoData stores account's options' info data
|
||||
type AccountOptionsInfoData struct {
|
||||
USDBalance float64 `json:"usdBalance"`
|
||||
LiquidationPrice float64 `json:"liquidationPrice"`
|
||||
Liquidating bool `json:"liquidating"`
|
||||
}
|
||||
|
||||
// OptionsPositionsData stores options positions' data
|
||||
type OptionsPositionsData struct {
|
||||
EntryPrice float64 `json:"entryPrice"`
|
||||
NetSize float64 `json:"netSize"`
|
||||
Option OptionData `json:"option"`
|
||||
Side string `json:"side"`
|
||||
Size float64 `json:"size"`
|
||||
PessimisticValuation float64 `json:"pessimisticValuation,omitempty"`
|
||||
PessimisticIndexPrice float64 `json:"pessimisticIndexPrice,omitempty"`
|
||||
}
|
||||
|
||||
// OptionsTradesData stores options' trades' data
|
||||
type OptionsTradesData struct {
|
||||
ID int64 `json:"id"`
|
||||
Option OptionData `json:"option"`
|
||||
Price float64 `json:"price"`
|
||||
Size float64 `json:"size"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
// OptionFillsData stores option's fills data
|
||||
type OptionFillsData struct {
|
||||
Fee float64 `json:"fee"`
|
||||
FeeRate float64 `json:"feeRate"`
|
||||
ID int64 `json:"id"`
|
||||
Liquidity string `json:"liquidity"`
|
||||
Option OptionData `json:"option"`
|
||||
Price float64 `json:"price"`
|
||||
QuoteID int64 `json:"quoteId"`
|
||||
Side string `json:"side"`
|
||||
Size float64 `json:"size"`
|
||||
Time string `json:"time"`
|
||||
}
|
||||
|
||||
// AuthenticationData stores authentication variables required
|
||||
type AuthenticationData struct {
|
||||
Key string `json:"key"`
|
||||
Sign string `json:"sign"`
|
||||
Time int64 `json:"time"`
|
||||
}
|
||||
|
||||
// Authenticate stores authentication variables required
|
||||
type Authenticate struct {
|
||||
Args AuthenticationData `json:"args"`
|
||||
Operation string `json:"op"`
|
||||
}
|
||||
|
||||
// WsResponseData stores basic ws response data on being subscribed to a channel successfully
|
||||
type WsResponseData struct {
|
||||
ResponseType string `json:"type"`
|
||||
Channel string `json:"channel"`
|
||||
Market string `json:"market"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
// WsTickerData stores ws ticker data
|
||||
type WsTickerData struct {
|
||||
Bid float64 `json:"bid"`
|
||||
Ask float64 `json:"ask"`
|
||||
BidSize float64 `json:"bidSize"`
|
||||
AskSize float64 `json:"askSize"`
|
||||
Last float64 `json:"last"`
|
||||
Time float64 `json:"time"`
|
||||
}
|
||||
|
||||
// WsTradeData stores ws trade data
|
||||
type WsTradeData struct {
|
||||
ID int64 `json:"id"`
|
||||
Price float64 `json:"price"`
|
||||
Size float64 `json:"size"`
|
||||
Side string `json:"side"`
|
||||
Liquidation bool `json:"liquidation"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
// WsOrderbookData stores ws orderbook data
|
||||
type WsOrderbookData struct {
|
||||
Action string `json:"action"`
|
||||
Bids [][2]float64 `json:"bids"`
|
||||
Asks [][2]float64 `json:"asks"`
|
||||
Time float64 `json:"time"`
|
||||
Checksum int64 `json:"checksum"`
|
||||
}
|
||||
|
||||
// WsOrders stores ws orders' data
|
||||
type WsOrders struct {
|
||||
ID int64 `json:"id"`
|
||||
ClientID string `json:"clientId"`
|
||||
Market string `json:"market"`
|
||||
OrderType string `json:"type"`
|
||||
Side string `json:"side"`
|
||||
Size float64 `json:"size"`
|
||||
Price float64 `json:"price"`
|
||||
ReduceOnly bool `json:"reduceOnly"`
|
||||
IOC bool `json:"ioc"`
|
||||
PostOnly bool `json:"postOnly"`
|
||||
Status string `json:"status"`
|
||||
FilledSize float64 `json:"filedSize"`
|
||||
RemainingSize float64 `json:"remainingSize"`
|
||||
AvgFillPrice float64 `json:"avgFillPrice"`
|
||||
}
|
||||
|
||||
// WsFills stores websocket fills' data
|
||||
type WsFills struct {
|
||||
Fee float64 `json:"fee"`
|
||||
FeeRate float64 `json:"feeRate"`
|
||||
Future string `json:"future"`
|
||||
ID int64 `json:"id"`
|
||||
Liquidity string `json:"liquidity"`
|
||||
Market string `json:"market"`
|
||||
OrderID int64 `json:"int64"`
|
||||
TradeID int64 `json:"tradeID"`
|
||||
Price float64 `json:"price"`
|
||||
Side string `json:"side"`
|
||||
Size float64 `json:"size"`
|
||||
Time time.Time `json:"time"`
|
||||
OrderType string `json:"orderType"`
|
||||
}
|
||||
|
||||
// WsSub has the data used to subscribe to a channel
|
||||
type WsSub struct {
|
||||
Channel string `json:"channel,omitempty"`
|
||||
Market string `json:"market,omitempty"`
|
||||
Operation string `json:"op,omitempty"`
|
||||
}
|
||||
|
||||
// WsTickerDataStore stores ws ticker data
|
||||
type WsTickerDataStore struct {
|
||||
Channel string `json:"channel"`
|
||||
Market string `json:"market"`
|
||||
MessageType string `json:"type"`
|
||||
Ticker WsTickerData `json:"data"`
|
||||
}
|
||||
|
||||
// WsOrderbookDataStore stores ws orderbook data
|
||||
type WsOrderbookDataStore struct {
|
||||
Channel string `json:"channel"`
|
||||
Market string `json:"market"`
|
||||
MessageType string `json:"type"`
|
||||
OBData WsOrderbookData `json:"data"`
|
||||
}
|
||||
|
||||
// WsTradeDataStore stores ws trades' data
|
||||
type WsTradeDataStore struct {
|
||||
Channel string `json:"channel"`
|
||||
Market string `json:"market"`
|
||||
MessageType string `json:"type"`
|
||||
TradeData []WsTradeData `json:"data"`
|
||||
}
|
||||
|
||||
// WsOrderDataStore stores ws orders' data
|
||||
type WsOrderDataStore struct {
|
||||
Channel string `json:"channel"`
|
||||
MessageType string `json:"type"`
|
||||
OrderData WsOrders `json:"data"`
|
||||
}
|
||||
|
||||
// WsFillsDataStore stores ws fills' data
|
||||
type WsFillsDataStore struct {
|
||||
Channel string `json:"channel"`
|
||||
MessageType string `json:"type"`
|
||||
FillsData WsFills `json:"fills"`
|
||||
}
|
||||
|
||||
// TimeInterval represents interval enum.
|
||||
type TimeInterval string
|
||||
|
||||
// Vars related to time intervals
|
||||
var (
|
||||
TimeIntervalFifteenSeconds = TimeInterval("15")
|
||||
TimeIntervalMinute = TimeInterval("60")
|
||||
TimeIntervalFiveMinutes = TimeInterval("300")
|
||||
TimeIntervalFifteenMinutes = TimeInterval("900")
|
||||
TimeIntervalHour = TimeInterval("3600")
|
||||
TimeIntervalFourHours = TimeInterval("14400")
|
||||
TimeIntervalDay = TimeInterval("86400")
|
||||
)
|
||||
|
||||
var errInvalidInterval = errors.New("invalid interval")
|
||||
|
||||
// OrderVars stores side, status and type for any order/trade
|
||||
type OrderVars struct {
|
||||
Side order.Side
|
||||
Status order.Status
|
||||
OrderType order.Type
|
||||
Fee float64
|
||||
}
|
||||
|
||||
// WsMarketsData stores websocket markets data
|
||||
type WsMarketsData struct {
|
||||
Data map[string]WsMarketsDataStorage `json:"data"`
|
||||
}
|
||||
|
||||
// WsMarketsDataStorage stores websocket markets data
|
||||
type WsMarketsDataStorage struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
PriceIncrement float64 `json:"priceIncrement,omitempty"`
|
||||
SizeIncrement float64 `json:"sizeIncrement,omitempty"`
|
||||
MarketType string `json:"marketType,omitempty"`
|
||||
BaseCurrency string `json:"baseCurrency,omitempty"`
|
||||
QuoteCurrency string `json:"quoteCurrency,omitempty"`
|
||||
Underlying string `json:"underlying,omitempty"`
|
||||
Restricted bool `json:"restricted,omitempty"`
|
||||
Future WsMarketsFutureData `json:"future,omitempty"`
|
||||
}
|
||||
|
||||
// WsMarketsFutureData stores websocket markets' future data
|
||||
type WsMarketsFutureData struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Underlying string `json:"underlying,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
MarketType string `json:"type,omitempty"`
|
||||
Expiry time.Time `json:"expiry,omitempty"`
|
||||
Perpetual bool `json:"perpetual,omitempty"`
|
||||
Expired bool `json:"expired,omitempty"`
|
||||
Enabled bool `json:"enabled,omitempty"`
|
||||
PostOnly bool `json:"postOnly,omitempty"`
|
||||
IMFFactor float64 `json:"imfFactor,omitempty"`
|
||||
UnderlyingDescription string `json:"underlyingDescription,omitempty"`
|
||||
ExpiryDescription string `json:"expiryDescription,omitempty"`
|
||||
MoveStart string `json:"moveStart,omitempty"`
|
||||
PositionLimitWeight float64 `json:"positionLimitWeight,omitempty"`
|
||||
Group string `json:"group,omitempty"`
|
||||
}
|
||||
|
||||
// WSMarkets stores websocket markets data
|
||||
type WSMarkets struct {
|
||||
Channel string `json:"channel"`
|
||||
MessageType string `json:"type"`
|
||||
Data WsMarketsData `json:"data"`
|
||||
Action string `json:"action"`
|
||||
}
|
||||
|
||||
// RequestQuoteData stores data on the requested quote
|
||||
type RequestQuoteData struct {
|
||||
QuoteID int64 `json:"quoteId"`
|
||||
}
|
||||
|
||||
// QuoteStatusData stores data of quotes' status
|
||||
type QuoteStatusData struct {
|
||||
BaseCoin string `json:"baseCoin"`
|
||||
Cost float64 `json:"cost"`
|
||||
Expired bool `json:"expired"`
|
||||
Filled bool `json:"filled"`
|
||||
FromCoin string `json:"fromCoin"`
|
||||
ID int64 `json:"id"`
|
||||
Price float64 `json:"price"`
|
||||
Proceeds float64 `json:"proceeds"`
|
||||
QuoteCoin string `json:"quoteCoin"`
|
||||
Side string `json:"side"`
|
||||
ToCoin string `json:"toCoin"`
|
||||
}
|
||||
|
||||
// AcceptQuote stores data of accepted quote
|
||||
type AcceptQuote struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
511
exchanges/ftx/ftx_websocket.go
Normal file
511
exchanges/ftx/ftx_websocket.go
Normal file
@@ -0,0 +1,511 @@
|
||||
package ftx
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/thrasher-corp/gocryptotrader/common/crypto"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wsorderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/log"
|
||||
)
|
||||
|
||||
const (
|
||||
ftxWSURL = "wss://ftx.com/ws/"
|
||||
ftxWebsocketTimer = 13 * time.Second
|
||||
wsTicker = "ticker"
|
||||
wsTrades = "trades"
|
||||
wsOrderbook = "orderbook"
|
||||
wsMarkets = "markets"
|
||||
wsFills = "fills"
|
||||
wsOrders = "orders"
|
||||
wsUpdate = "update"
|
||||
wsPartial = "partial"
|
||||
subscribe = "subscribe"
|
||||
unsubscribe = "unsubscribe"
|
||||
)
|
||||
|
||||
var obSuccess = make(map[currency.Pair]bool)
|
||||
|
||||
// WsConnect connects to a websocket feed
|
||||
func (f *FTX) WsConnect() error {
|
||||
if !f.Websocket.IsEnabled() || !f.IsEnabled() {
|
||||
return errors.New(wshandler.WebsocketNotEnabled)
|
||||
}
|
||||
var dialer websocket.Dialer
|
||||
err := f.WebsocketConn.Dial(&dialer, http.Header{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.WebsocketConn.SetupPingHandler(wshandler.WebsocketPingHandler{
|
||||
MessageType: websocket.PingMessage,
|
||||
Delay: ftxWebsocketTimer,
|
||||
})
|
||||
if f.Verbose {
|
||||
log.Debugf(log.ExchangeSys, "%s Connected to Websocket.\n", f.Name)
|
||||
}
|
||||
f.GenerateDefaultSubscriptions()
|
||||
go f.wsReadData()
|
||||
if f.GetAuthenticatedAPISupport(exchange.WebsocketAuthentication) {
|
||||
err := f.WsAuth()
|
||||
if err != nil {
|
||||
f.Websocket.DataHandler <- err
|
||||
f.Websocket.SetCanUseAuthenticatedEndpoints(false)
|
||||
}
|
||||
f.GenerateAuthSubscriptions()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// WsAuth sends an authentication message to receive auth data
|
||||
func (f *FTX) WsAuth() error {
|
||||
intNonce := time.Now().UnixNano() / 1000000
|
||||
strNonce := strconv.FormatInt(intNonce, 10)
|
||||
hmac := crypto.GetHMAC(
|
||||
crypto.HashSHA256,
|
||||
[]byte(strNonce+"websocket_login"),
|
||||
[]byte(f.API.Credentials.Secret),
|
||||
)
|
||||
sign := crypto.HexEncodeToString(hmac)
|
||||
req := Authenticate{Operation: "login",
|
||||
Args: AuthenticationData{
|
||||
Key: f.API.Credentials.Key,
|
||||
Sign: sign,
|
||||
Time: intNonce,
|
||||
},
|
||||
}
|
||||
return f.WebsocketConn.SendJSONMessage(req)
|
||||
}
|
||||
|
||||
// Subscribe sends a websocket message to receive data from the channel
|
||||
func (f *FTX) Subscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error {
|
||||
var sub WsSub
|
||||
switch channelToSubscribe.Channel {
|
||||
case wsFills, wsOrders, wsMarkets:
|
||||
sub.Operation = subscribe
|
||||
sub.Channel = channelToSubscribe.Channel
|
||||
default:
|
||||
a, err := f.GetPairAssetType(channelToSubscribe.Currency)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
sub.Operation = subscribe
|
||||
sub.Channel = channelToSubscribe.Channel
|
||||
sub.Market = f.FormatExchangeCurrency(channelToSubscribe.Currency, a).String()
|
||||
}
|
||||
return f.WebsocketConn.SendJSONMessage(sub)
|
||||
}
|
||||
|
||||
// GenerateDefaultSubscriptions generates default subscription
|
||||
func (f *FTX) GenerateDefaultSubscriptions() {
|
||||
var subscriptions []wshandler.WebsocketChannelSubscription
|
||||
subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{
|
||||
Channel: wsMarkets,
|
||||
})
|
||||
var channels = []string{wsTicker, wsTrades, wsOrderbook}
|
||||
for a := range f.CurrencyPairs.AssetTypes {
|
||||
pairs := f.GetEnabledPairs(f.CurrencyPairs.AssetTypes[a])
|
||||
for z := range pairs {
|
||||
newPair := currency.NewPairWithDelimiter(pairs[z].Base.String(), pairs[z].Quote.String(), "-")
|
||||
for x := range channels {
|
||||
subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{
|
||||
Channel: channels[x],
|
||||
Currency: newPair,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
f.Websocket.SubscribeToChannels(subscriptions)
|
||||
}
|
||||
|
||||
// GenerateAuthSubscriptions generates default subscription
|
||||
func (f *FTX) GenerateAuthSubscriptions() {
|
||||
var subscriptions []wshandler.WebsocketChannelSubscription
|
||||
var channels = []string{wsOrders, wsFills}
|
||||
for x := range channels {
|
||||
subscriptions = append(subscriptions, wshandler.WebsocketChannelSubscription{
|
||||
Channel: channels[x],
|
||||
})
|
||||
}
|
||||
f.Websocket.SubscribeToChannels(subscriptions)
|
||||
}
|
||||
|
||||
// wsReadData gets and passes on websocket messages for processing
|
||||
func (f *FTX) wsReadData() {
|
||||
f.Websocket.Wg.Add(1)
|
||||
|
||||
defer f.Websocket.Wg.Done()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-f.Websocket.ShutdownC:
|
||||
return
|
||||
|
||||
default:
|
||||
resp, err := f.WebsocketConn.ReadMessage()
|
||||
if err != nil {
|
||||
f.Websocket.ReadMessageErrors <- err
|
||||
return
|
||||
}
|
||||
f.Websocket.TrafficAlert <- struct{}{}
|
||||
err = f.wsHandleData(resp.Raw)
|
||||
if err != nil {
|
||||
f.Websocket.DataHandler <- err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func timestampFromFloat64(ts float64) time.Time {
|
||||
secs := int64(ts)
|
||||
nsecs := int64((ts - float64(secs)) * 1e9)
|
||||
return time.Unix(secs, nsecs).UTC()
|
||||
}
|
||||
|
||||
func (f *FTX) wsHandleData(respRaw []byte) error {
|
||||
var result map[string]interface{}
|
||||
err := json.Unmarshal(respRaw, &result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch result["type"] {
|
||||
case wsUpdate:
|
||||
var p currency.Pair
|
||||
var a asset.Item
|
||||
market, ok := result["market"]
|
||||
if ok {
|
||||
p = currency.NewPairFromString(market.(string))
|
||||
a, err = f.GetPairAssetType(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
switch result["channel"] {
|
||||
case wsTicker:
|
||||
var resultData WsTickerDataStore
|
||||
err = json.Unmarshal(respRaw, &resultData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.Websocket.DataHandler <- &ticker.Price{
|
||||
ExchangeName: f.Name,
|
||||
Bid: resultData.Ticker.Bid,
|
||||
Ask: resultData.Ticker.Ask,
|
||||
Last: resultData.Ticker.Last,
|
||||
LastUpdated: timestampFromFloat64(resultData.Ticker.Time),
|
||||
Pair: p,
|
||||
AssetType: a,
|
||||
}
|
||||
case wsOrderbook:
|
||||
var resultData WsOrderbookDataStore
|
||||
err = json.Unmarshal(respRaw, &resultData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(resultData.OBData.Asks) == 0 && len(resultData.OBData.Bids) == 0 {
|
||||
return nil
|
||||
}
|
||||
err = f.WsProcessUpdateOB(&resultData.OBData, p, a)
|
||||
if err != nil {
|
||||
f.wsResubToOB(p)
|
||||
return err
|
||||
}
|
||||
case wsTrades:
|
||||
var resultData WsTradeDataStore
|
||||
err = json.Unmarshal(respRaw, &resultData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for z := range resultData.TradeData {
|
||||
var oSide order.Side
|
||||
oSide, err = order.StringToOrderSide(resultData.TradeData[z].Side)
|
||||
if err != nil {
|
||||
f.Websocket.DataHandler <- order.ClassificationError{
|
||||
Exchange: f.Name,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
f.Websocket.DataHandler <- wshandler.TradeData{
|
||||
Timestamp: resultData.TradeData[z].Time,
|
||||
CurrencyPair: p,
|
||||
AssetType: a,
|
||||
Exchange: f.Name,
|
||||
Price: resultData.TradeData[z].Price,
|
||||
Amount: resultData.TradeData[z].Size,
|
||||
Side: oSide,
|
||||
}
|
||||
}
|
||||
case wsOrders:
|
||||
var resultData WsOrderDataStore
|
||||
err = json.Unmarshal(respRaw, &resultData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pair := currency.NewPairFromString(resultData.OrderData.Market)
|
||||
var assetType asset.Item
|
||||
assetType, err = f.GetPairAssetType(pair)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var oSide order.Side
|
||||
oSide, err = order.StringToOrderSide(resultData.OrderData.Side)
|
||||
if err != nil {
|
||||
f.Websocket.DataHandler <- order.ClassificationError{
|
||||
Exchange: f.Name,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
var resp order.Detail
|
||||
resp.Side = oSide
|
||||
resp.Amount = resultData.OrderData.Size
|
||||
resp.AssetType = assetType
|
||||
resp.ClientOrderID = resultData.OrderData.ClientID
|
||||
resp.Exchange = f.Name
|
||||
resp.ExecutedAmount = resultData.OrderData.FilledSize
|
||||
resp.ID = strconv.FormatInt(resultData.OrderData.ID, 10)
|
||||
resp.Pair = pair
|
||||
resp.RemainingAmount = resultData.OrderData.Size - resultData.OrderData.FilledSize
|
||||
var orderVars OrderVars
|
||||
orderVars, err = f.compatibleOrderVars(resultData.OrderData.Side,
|
||||
resultData.OrderData.Status,
|
||||
resultData.OrderData.OrderType,
|
||||
resultData.OrderData.FilledSize,
|
||||
resultData.OrderData.Size,
|
||||
resultData.OrderData.AvgFillPrice)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resp.Status = orderVars.Status
|
||||
resp.Side = orderVars.Side
|
||||
resp.Type = orderVars.OrderType
|
||||
resp.Fee = orderVars.Fee
|
||||
f.Websocket.DataHandler <- &resp
|
||||
case wsFills:
|
||||
var resultData WsFillsDataStore
|
||||
err = json.Unmarshal(respRaw, &resultData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.Websocket.DataHandler <- resultData.FillsData
|
||||
default:
|
||||
f.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: f.Name + wshandler.UnhandledMessage + string(respRaw)}
|
||||
}
|
||||
case wsPartial:
|
||||
switch result["channel"] {
|
||||
case "orderbook":
|
||||
var p currency.Pair
|
||||
var a asset.Item
|
||||
market, ok := result["market"]
|
||||
if ok {
|
||||
p = currency.NewPairFromString(market.(string))
|
||||
a, err = f.GetPairAssetType(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var resultData WsOrderbookDataStore
|
||||
err = json.Unmarshal(respRaw, &resultData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = f.WsProcessPartialOB(&resultData.OBData, p, a)
|
||||
if err != nil {
|
||||
f.wsResubToOB(p)
|
||||
return err
|
||||
}
|
||||
// reset obchecksum failure blockage for pair
|
||||
delete(obSuccess, p)
|
||||
case wsMarkets:
|
||||
var resultData WSMarkets
|
||||
err = json.Unmarshal(respRaw, &resultData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.Websocket.DataHandler <- resultData.Data
|
||||
}
|
||||
case "error":
|
||||
f.Websocket.DataHandler <- wshandler.UnhandledMessageWarning{Message: f.Name + wshandler.UnhandledMessage + string(respRaw)}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unsubscribe sends a websocket message to stop receiving data from the channel
|
||||
func (f *FTX) Unsubscribe(channelToSubscribe wshandler.WebsocketChannelSubscription) error {
|
||||
var unSub WsSub
|
||||
a, err := f.GetPairAssetType(channelToSubscribe.Currency)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
unSub.Operation = unsubscribe
|
||||
unSub.Channel = channelToSubscribe.Channel
|
||||
unSub.Market = f.FormatExchangeCurrency(channelToSubscribe.Currency, a).String()
|
||||
return f.WebsocketConn.SendJSONMessage(unSub)
|
||||
}
|
||||
|
||||
// WsProcessUpdateOB processes an update on the orderbook
|
||||
func (f *FTX) WsProcessUpdateOB(data *WsOrderbookData, p currency.Pair, a asset.Item) error {
|
||||
update := wsorderbook.WebsocketOrderbookUpdate{
|
||||
Asset: a,
|
||||
Pair: p,
|
||||
UpdateTime: timestampFromFloat64(data.Time),
|
||||
}
|
||||
|
||||
var err error
|
||||
for x := range data.Bids {
|
||||
update.Bids = append(update.Bids, orderbook.Item{
|
||||
Price: data.Bids[x][0],
|
||||
Amount: data.Bids[x][1],
|
||||
})
|
||||
}
|
||||
for x := range data.Asks {
|
||||
update.Asks = append(update.Asks, orderbook.Item{
|
||||
Price: data.Asks[x][0],
|
||||
Amount: data.Asks[x][1],
|
||||
})
|
||||
}
|
||||
|
||||
err = f.Websocket.Orderbook.Update(&update)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
updatedOb := f.Websocket.Orderbook.GetOrderbook(p, a)
|
||||
checksum := f.CalcUpdateOBChecksum(updatedOb)
|
||||
|
||||
if checksum != data.Checksum {
|
||||
log.Warnf(log.ExchangeSys, "%s checksum failure for item %s",
|
||||
f.Name,
|
||||
p)
|
||||
return errors.New("checksum failed")
|
||||
}
|
||||
f.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{
|
||||
Exchange: f.Name,
|
||||
Asset: a,
|
||||
Pair: p,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FTX) wsResubToOB(p currency.Pair) {
|
||||
if ok := obSuccess[p]; ok {
|
||||
return
|
||||
}
|
||||
|
||||
obSuccess[p] = true
|
||||
|
||||
channelToResubscribe := wshandler.WebsocketChannelSubscription{
|
||||
Channel: wsOrderbook,
|
||||
Currency: p,
|
||||
}
|
||||
f.Websocket.ResubscribeToChannel(channelToResubscribe)
|
||||
}
|
||||
|
||||
// WsProcessPartialOB creates an OB from websocket data
|
||||
func (f *FTX) WsProcessPartialOB(data *WsOrderbookData, p currency.Pair, a asset.Item) error {
|
||||
signedChecksum := f.CalcPartialOBChecksum(data)
|
||||
if signedChecksum != data.Checksum {
|
||||
return fmt.Errorf("%s channel: %s. Orderbook partial for %v checksum invalid",
|
||||
f.Name,
|
||||
a,
|
||||
p)
|
||||
}
|
||||
var bids, asks []orderbook.Item
|
||||
for x := range data.Bids {
|
||||
bids = append(bids, orderbook.Item{
|
||||
Price: data.Bids[x][0],
|
||||
Amount: data.Bids[x][1],
|
||||
})
|
||||
}
|
||||
for x := range data.Asks {
|
||||
asks = append(asks, orderbook.Item{
|
||||
Price: data.Asks[x][0],
|
||||
Amount: data.Asks[x][1],
|
||||
})
|
||||
}
|
||||
|
||||
newOrderBook := orderbook.Base{
|
||||
Asks: asks,
|
||||
Bids: bids,
|
||||
AssetType: a,
|
||||
LastUpdated: timestampFromFloat64(data.Time),
|
||||
Pair: p,
|
||||
ExchangeName: f.Name,
|
||||
}
|
||||
|
||||
if err := f.Websocket.Orderbook.LoadSnapshot(&newOrderBook); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f.Websocket.DataHandler <- wshandler.WebsocketOrderbookUpdate{
|
||||
Exchange: f.Name,
|
||||
Asset: a,
|
||||
Pair: p,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CalcPartialOBChecksum calculates checksum of partial OB data received from WS
|
||||
func (f *FTX) CalcPartialOBChecksum(data *WsOrderbookData) int64 {
|
||||
var checksum strings.Builder
|
||||
var price, amount string
|
||||
for i := 0; i < 100; i++ {
|
||||
if len(data.Bids)-1 >= i {
|
||||
price = checksumParseNumber(data.Bids[i][0])
|
||||
amount = checksumParseNumber(data.Bids[i][1])
|
||||
checksum.WriteString(price + ":" + amount + ":")
|
||||
}
|
||||
if len(data.Asks)-1 >= i {
|
||||
price = checksumParseNumber(data.Asks[i][0])
|
||||
amount = checksumParseNumber(data.Asks[i][1])
|
||||
checksum.WriteString(price + ":" + amount + ":")
|
||||
}
|
||||
}
|
||||
checksumStr := strings.TrimSuffix(checksum.String(), ":")
|
||||
return int64(crc32.ChecksumIEEE([]byte(checksumStr)))
|
||||
}
|
||||
|
||||
// CalcUpdateOBChecksum calculates checksum of update OB data received from WS
|
||||
func (f *FTX) CalcUpdateOBChecksum(data *orderbook.Base) int64 {
|
||||
var checksum strings.Builder
|
||||
var price, amount string
|
||||
for i := 0; i < 100; i++ {
|
||||
if len(data.Bids)-1 >= i {
|
||||
price = checksumParseNumber(data.Bids[i].Price)
|
||||
amount = checksumParseNumber(data.Bids[i].Amount)
|
||||
checksum.WriteString(price + ":" + amount + ":")
|
||||
}
|
||||
if len(data.Asks)-1 >= i {
|
||||
price = checksumParseNumber(data.Asks[i].Price)
|
||||
amount = checksumParseNumber(data.Asks[i].Amount)
|
||||
checksum.WriteString(price + ":" + amount + ":")
|
||||
}
|
||||
}
|
||||
checksumStr := strings.TrimSuffix(checksum.String(), ":")
|
||||
return int64(crc32.ChecksumIEEE([]byte(checksumStr)))
|
||||
}
|
||||
|
||||
func checksumParseNumber(num float64) string {
|
||||
modifier := byte('f')
|
||||
if num < 0.0001 {
|
||||
modifier = 'e'
|
||||
}
|
||||
r := strconv.FormatFloat(num, modifier, -1, 64)
|
||||
if strings.IndexByte(r, '.') == -1 && modifier != 'e' {
|
||||
r += ".0"
|
||||
}
|
||||
return r
|
||||
}
|
||||
873
exchanges/ftx/ftx_wrapper.go
Normal file
873
exchanges/ftx/ftx_wrapper.go
Normal file
@@ -0,0 +1,873 @@
|
||||
package ftx
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/config"
|
||||
"github.com/thrasher-corp/gocryptotrader/currency"
|
||||
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/kline"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/order"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/protocol"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/ticker"
|
||||
"github.com/thrasher-corp/gocryptotrader/exchanges/websocket/wshandler"
|
||||
"github.com/thrasher-corp/gocryptotrader/log"
|
||||
"github.com/thrasher-corp/gocryptotrader/portfolio/withdraw"
|
||||
)
|
||||
|
||||
// GetDefaultConfig returns a default exchange config
|
||||
func (f *FTX) GetDefaultConfig() (*config.ExchangeConfig, error) {
|
||||
f.SetDefaults()
|
||||
exchCfg := new(config.ExchangeConfig)
|
||||
exchCfg.Name = f.Name
|
||||
exchCfg.HTTPTimeout = exchange.DefaultHTTPTimeout
|
||||
exchCfg.BaseCurrencies = f.BaseCurrencies
|
||||
|
||||
err := f.SetupDefaults(exchCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if f.Features.Supports.RESTCapabilities.AutoPairUpdates {
|
||||
err = f.UpdateTradablePairs(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return exchCfg, nil
|
||||
}
|
||||
|
||||
// SetDefaults sets the basic defaults for FTX
|
||||
func (f *FTX) SetDefaults() {
|
||||
f.Name = "FTX"
|
||||
f.Enabled = true
|
||||
f.Verbose = true
|
||||
f.API.CredentialsValidator.RequiresKey = true
|
||||
f.API.CredentialsValidator.RequiresSecret = true
|
||||
f.CurrencyPairs = currency.PairsManager{
|
||||
AssetTypes: asset.Items{
|
||||
asset.Spot,
|
||||
asset.Futures,
|
||||
},
|
||||
}
|
||||
spot := currency.PairStore{
|
||||
RequestFormat: ¤cy.PairFormat{
|
||||
Uppercase: true,
|
||||
Delimiter: "/",
|
||||
},
|
||||
ConfigFormat: ¤cy.PairFormat{
|
||||
Uppercase: true,
|
||||
Delimiter: "/",
|
||||
},
|
||||
}
|
||||
futures := currency.PairStore{
|
||||
RequestFormat: ¤cy.PairFormat{
|
||||
Uppercase: true,
|
||||
Delimiter: "-",
|
||||
},
|
||||
ConfigFormat: ¤cy.PairFormat{
|
||||
Uppercase: true,
|
||||
Delimiter: "-",
|
||||
},
|
||||
}
|
||||
f.CurrencyPairs.Store(asset.Spot, spot)
|
||||
f.CurrencyPairs.Store(asset.Futures, futures)
|
||||
f.Features = exchange.Features{
|
||||
Supports: exchange.FeaturesSupported{
|
||||
REST: true,
|
||||
Websocket: true,
|
||||
RESTCapabilities: protocol.Features{
|
||||
TickerFetching: true,
|
||||
KlineFetching: true,
|
||||
TradeFetching: true,
|
||||
OrderbookFetching: true,
|
||||
AutoPairUpdates: true,
|
||||
AccountInfo: true,
|
||||
GetOrder: true,
|
||||
GetOrders: true,
|
||||
CancelOrders: true,
|
||||
CancelOrder: true,
|
||||
SubmitOrder: true,
|
||||
TradeFee: true,
|
||||
FiatDepositFee: true,
|
||||
FiatWithdrawalFee: true,
|
||||
CryptoWithdrawalFee: true,
|
||||
},
|
||||
WebsocketCapabilities: protocol.Features{
|
||||
OrderbookFetching: true,
|
||||
TradeFetching: true,
|
||||
Subscribe: true,
|
||||
Unsubscribe: true,
|
||||
GetOrders: true,
|
||||
GetOrder: true,
|
||||
},
|
||||
WithdrawPermissions: exchange.NoAPIWithdrawalMethods,
|
||||
},
|
||||
Enabled: exchange.FeaturesEnabled{
|
||||
AutoPairUpdates: true,
|
||||
},
|
||||
}
|
||||
|
||||
f.Requester = request.New(f.Name,
|
||||
common.NewHTTPClientWithTimeout(exchange.DefaultHTTPTimeout),
|
||||
request.WithLimiter(request.NewBasicRateLimit(ratePeriod, rateLimit)))
|
||||
|
||||
f.API.Endpoints.URLDefault = ftxAPIURL
|
||||
f.API.Endpoints.URL = f.API.Endpoints.URLDefault
|
||||
f.Websocket = wshandler.New()
|
||||
f.WebsocketResponseMaxLimit = exchange.DefaultWebsocketResponseMaxLimit
|
||||
f.WebsocketResponseCheckTimeout = exchange.DefaultWebsocketResponseCheckTimeout
|
||||
f.WebsocketOrderbookBufferLimit = exchange.DefaultWebsocketOrderbookBufferLimit
|
||||
}
|
||||
|
||||
// Setup takes in the supplied exchange configuration details and sets params
|
||||
func (f *FTX) Setup(exch *config.ExchangeConfig) error {
|
||||
if !exch.Enabled {
|
||||
f.SetEnabled(false)
|
||||
return nil
|
||||
}
|
||||
|
||||
err := f.SetupDefaults(exch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = f.Websocket.Setup(
|
||||
&wshandler.WebsocketSetup{
|
||||
Enabled: exch.Features.Enabled.Websocket,
|
||||
Verbose: exch.Verbose,
|
||||
AuthenticatedWebsocketAPISupport: exch.API.AuthenticatedWebsocketSupport,
|
||||
WebsocketTimeout: exch.WebsocketTrafficTimeout,
|
||||
DefaultURL: ftxWSURL,
|
||||
ExchangeName: exch.Name,
|
||||
RunningURL: exch.API.Endpoints.WebsocketURL,
|
||||
Connector: f.WsConnect,
|
||||
Subscriber: f.Subscribe,
|
||||
UnSubscriber: f.Unsubscribe,
|
||||
Features: &f.Features.Supports.WebsocketCapabilities,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f.WebsocketConn = &wshandler.WebsocketConnection{
|
||||
ExchangeName: f.Name,
|
||||
URL: f.Websocket.GetWebsocketURL(),
|
||||
ProxyURL: f.Websocket.GetProxyAddress(),
|
||||
Verbose: f.Verbose,
|
||||
ResponseCheckTimeout: exch.WebsocketResponseCheckTimeout,
|
||||
ResponseMaxLimit: exch.WebsocketResponseMaxLimit,
|
||||
}
|
||||
|
||||
f.Websocket.Orderbook.Setup(
|
||||
exch.WebsocketOrderbookBufferLimit,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
exch.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Start starts the FTX go routine
|
||||
func (f *FTX) Start(wg *sync.WaitGroup) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
f.Run()
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
|
||||
// Run implements the FTX wrapper
|
||||
func (f *FTX) Run() {
|
||||
if f.Verbose {
|
||||
log.Debugf(log.ExchangeSys,
|
||||
"%s Websocket: %s.",
|
||||
f.Name,
|
||||
common.IsEnabled(f.Websocket.IsEnabled()))
|
||||
f.PrintEnabledPairs()
|
||||
}
|
||||
|
||||
if !f.GetEnabledFeatures().AutoPairUpdates {
|
||||
return
|
||||
}
|
||||
|
||||
err := f.UpdateTradablePairs(false)
|
||||
if err != nil {
|
||||
log.Errorf(log.ExchangeSys,
|
||||
"%s failed to update tradable pairs. Err: %s",
|
||||
f.Name,
|
||||
err)
|
||||
}
|
||||
}
|
||||
|
||||
// FetchTradablePairs returns a list of the exchanges tradable pairs
|
||||
func (f *FTX) FetchTradablePairs(a asset.Item) ([]string, error) {
|
||||
if !f.SupportsAsset(a) {
|
||||
return nil, fmt.Errorf("asset type of %s is not supported by %s", a, f.Name)
|
||||
}
|
||||
markets, err := f.GetMarkets()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pairs []string
|
||||
switch a {
|
||||
case asset.Spot:
|
||||
for x := range markets {
|
||||
if markets[x].MarketType == spotString {
|
||||
pairs = append(pairs, markets[x].Name)
|
||||
}
|
||||
}
|
||||
case asset.Futures:
|
||||
for x := range markets {
|
||||
if markets[x].MarketType == futuresString {
|
||||
pairs = append(pairs, markets[x].Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
return pairs, nil
|
||||
}
|
||||
|
||||
// UpdateTradablePairs updates the exchanges available pairs and stores
|
||||
// them in the exchanges config
|
||||
func (f *FTX) UpdateTradablePairs(forceUpdate bool) error {
|
||||
for x := range f.CurrencyPairs.AssetTypes {
|
||||
pairs, err := f.FetchTradablePairs(f.CurrencyPairs.AssetTypes[x])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = f.UpdatePairs(currency.NewPairsFromStrings(pairs),
|
||||
f.CurrencyPairs.AssetTypes[x], false, forceUpdate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (f *FTX) UpdateTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) {
|
||||
allPairs := f.GetEnabledPairs(assetType)
|
||||
if !allPairs.Contains(p, true) {
|
||||
allPairs = append(allPairs, p)
|
||||
}
|
||||
markets, err := f.GetMarkets()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for a := range allPairs {
|
||||
for x := range markets {
|
||||
if markets[x].Name != f.FormatExchangeCurrency(allPairs[a], assetType).String() {
|
||||
continue
|
||||
}
|
||||
var resp ticker.Price
|
||||
resp.Pair = currency.NewPairFromString(markets[x].Name)
|
||||
resp.Last = markets[x].Last
|
||||
resp.Bid = markets[x].Bid
|
||||
resp.Ask = markets[x].Ask
|
||||
resp.LastUpdated = time.Now()
|
||||
err = ticker.ProcessTicker(f.Name, &resp, assetType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return ticker.GetTicker(f.Name, p, assetType)
|
||||
}
|
||||
|
||||
// FetchTicker returns the ticker for a currency pair
|
||||
func (f *FTX) FetchTicker(p currency.Pair, assetType asset.Item) (*ticker.Price, error) {
|
||||
tickerNew, err := ticker.GetTicker(f.Name, p, assetType)
|
||||
if err != nil {
|
||||
return f.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tickerNew, nil
|
||||
}
|
||||
|
||||
// FetchOrderbook returns orderbook base on the currency pair
|
||||
func (f *FTX) FetchOrderbook(currency currency.Pair, assetType asset.Item) (*orderbook.Base, error) {
|
||||
ob, err := orderbook.Get(f.Name, currency, assetType)
|
||||
if err != nil {
|
||||
return f.UpdateOrderbook(currency, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (f *FTX) UpdateOrderbook(p currency.Pair, assetType asset.Item) (*orderbook.Base, error) {
|
||||
orderBook := new(orderbook.Base)
|
||||
tempResp, err := f.GetOrderbook(f.FormatExchangeCurrency(p, assetType).String(), 0)
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
for x := range tempResp.Bids {
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{
|
||||
Amount: tempResp.Bids[x].Size,
|
||||
Price: tempResp.Bids[x].Price})
|
||||
}
|
||||
for y := range tempResp.Asks {
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{
|
||||
Amount: tempResp.Asks[y].Size,
|
||||
Price: tempResp.Asks[y].Price})
|
||||
}
|
||||
orderBook.Pair = p
|
||||
orderBook.ExchangeName = f.Name
|
||||
orderBook.AssetType = assetType
|
||||
err = orderBook.Process()
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
return orderbook.Get(f.Name, p, assetType)
|
||||
}
|
||||
|
||||
// UpdateAccountInfo retrieves balances for all enabled currencies
|
||||
func (f *FTX) UpdateAccountInfo() (account.Holdings, error) {
|
||||
var resp account.Holdings
|
||||
data, err := f.GetBalances()
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
var acc account.SubAccount
|
||||
for i := range data {
|
||||
c := currency.NewCode(data[i].Coin)
|
||||
hold := data[i].Total - data[i].Free
|
||||
total := data[i].Total
|
||||
acc.Currencies = append(acc.Currencies,
|
||||
account.Balance{CurrencyName: c,
|
||||
TotalValue: total,
|
||||
Hold: hold})
|
||||
}
|
||||
resp.Accounts = append(resp.Accounts, acc)
|
||||
resp.Exchange = f.Name
|
||||
|
||||
err = account.Process(&resp)
|
||||
if err != nil {
|
||||
return account.Holdings{}, err
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// FetchAccountInfo retrieves balances for all enabled currencies
|
||||
func (f *FTX) FetchAccountInfo() (account.Holdings, error) {
|
||||
acc, err := account.GetHoldings(f.Name)
|
||||
if err != nil {
|
||||
return f.UpdateAccountInfo()
|
||||
}
|
||||
|
||||
return acc, nil
|
||||
}
|
||||
|
||||
// GetFundingHistory returns funding history, deposits and
|
||||
// withdrawals
|
||||
func (f *FTX) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
var resp []exchange.FundHistory
|
||||
depositData, err := f.FetchDepositHistory()
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
for x := range depositData {
|
||||
var tempData exchange.FundHistory
|
||||
tempData.Fee = depositData[x].Fee
|
||||
tempData.Timestamp = depositData[x].Time
|
||||
tempData.ExchangeName = f.Name
|
||||
tempData.CryptoTxID = depositData[x].TxID
|
||||
tempData.Status = depositData[x].Status
|
||||
tempData.Amount = depositData[x].Size
|
||||
tempData.Currency = depositData[x].Coin
|
||||
tempData.TransferID = strconv.FormatInt(depositData[x].ID, 10)
|
||||
resp = append(resp, tempData)
|
||||
}
|
||||
withdrawalData, err := f.FetchWithdrawalHistory()
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
for y := range withdrawalData {
|
||||
var tempData exchange.FundHistory
|
||||
tempData.Fee = depositData[y].Fee
|
||||
tempData.Timestamp = depositData[y].Time
|
||||
tempData.ExchangeName = f.Name
|
||||
tempData.CryptoTxID = depositData[y].TxID
|
||||
tempData.Status = depositData[y].Status
|
||||
tempData.Amount = depositData[y].Size
|
||||
tempData.Currency = depositData[y].Coin
|
||||
tempData.TransferID = strconv.FormatInt(depositData[y].ID, 10)
|
||||
resp = append(resp, tempData)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (f *FTX) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
marketName := f.FormatExchangeCurrency(p, assetType).String()
|
||||
var resp []exchange.TradeHistory
|
||||
trades, err := f.GetTrades(marketName, time.Unix(timestampStart.Unix(), 0), time.Unix(timestampEnd.Unix(), 0), 100)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for {
|
||||
var tempResp exchange.TradeHistory
|
||||
if len(trades) > 0 {
|
||||
tempResp.Amount = trades[0].Size
|
||||
tempResp.Price = trades[0].Price
|
||||
tempResp.Exchange = f.Name
|
||||
tempResp.Timestamp = trades[0].Time
|
||||
tempResp.TID = strconv.FormatInt(trades[0].ID, 10)
|
||||
tempResp.Side = trades[0].Side
|
||||
resp = append(resp, tempResp)
|
||||
}
|
||||
for y := 1; y < len(trades); y++ {
|
||||
tempResp.Amount = trades[y].Size
|
||||
tempResp.Price = trades[y].Price
|
||||
tempResp.Exchange = f.Name
|
||||
tempResp.Timestamp = trades[y].Time
|
||||
tempResp.TID = strconv.FormatInt(trades[y].ID, 10)
|
||||
tempResp.Side = trades[y].Side
|
||||
resp = append(resp, tempResp)
|
||||
}
|
||||
if len(trades) != 100 {
|
||||
break
|
||||
}
|
||||
trades, err = f.GetTrades(marketName, time.Unix(timestampStart.Unix(), 0), time.Unix(trades[len(trades)-1].Time.Unix(), 0), 100)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// SubmitOrder submits a new order
|
||||
func (f *FTX) SubmitOrder(s *order.Submit) (order.SubmitResponse, error) {
|
||||
var resp order.SubmitResponse
|
||||
if err := s.Validate(); err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
if s.Side == order.Sell {
|
||||
s.Side = order.Ask
|
||||
}
|
||||
if s.Side == order.Buy {
|
||||
s.Side = order.Bid
|
||||
}
|
||||
|
||||
tempResp, err := f.Order(f.FormatExchangeCurrency(s.Pair, s.AssetType).String(),
|
||||
s.Side.String(),
|
||||
s.Type.String(),
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
s.ClientOrderID,
|
||||
s.Price,
|
||||
s.Amount)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
resp.IsOrderPlaced = true
|
||||
resp.OrderID = strconv.FormatInt(tempResp.ID, 10)
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ModifyOrder will allow of changing orderbook placement and limit to
|
||||
// market conversion
|
||||
func (f *FTX) ModifyOrder(action *order.Modify) (string, error) {
|
||||
if action.TriggerPrice != 0 {
|
||||
a, err := f.ModifyTriggerOrder(action.ID,
|
||||
action.Type.String(),
|
||||
action.Amount,
|
||||
action.TriggerPrice,
|
||||
action.Price,
|
||||
0)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strconv.FormatInt(a.ID, 10), err
|
||||
}
|
||||
var o OrderData
|
||||
var err error
|
||||
switch action.ID {
|
||||
case "":
|
||||
o, err = f.ModifyOrderByClientID(action.ClientOrderID, action.ClientOrderID, action.Price, action.Amount)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
default:
|
||||
o, err = f.ModifyPlacedOrder(action.ID, action.ClientOrderID, action.Price, action.Amount)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
return strconv.FormatInt(o.ID, 10), err
|
||||
}
|
||||
|
||||
// CancelOrder cancels an order by its corresponding ID number
|
||||
func (f *FTX) CancelOrder(order *order.Cancel) error {
|
||||
_, err := f.DeleteOrder(order.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
// CancelAllOrders cancels all orders associated with a currency pair
|
||||
func (f *FTX) CancelAllOrders(orderCancellation *order.Cancel) (order.CancelAllResponse, error) {
|
||||
var resp order.CancelAllResponse
|
||||
tempMap := make(map[string]string)
|
||||
orders, err := f.GetOpenOrders(f.FormatExchangeCurrency(orderCancellation.Pair, orderCancellation.AssetType).String())
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
for x := range orders {
|
||||
_, err := f.DeleteOrder(strconv.FormatInt(orders[x].ID, 10))
|
||||
if err != nil {
|
||||
tempMap[strconv.FormatInt(orders[x].ID, 10)] = "Cancellation Failed"
|
||||
continue
|
||||
}
|
||||
tempMap[strconv.FormatInt(orders[x].ID, 10)] = "Success"
|
||||
}
|
||||
resp.Status = tempMap
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetCompatible gets compatible variables for order vars
|
||||
func (s *OrderData) GetCompatible(f *FTX) (OrderVars, error) {
|
||||
var resp OrderVars
|
||||
switch s.Side {
|
||||
case order.Buy.Lower():
|
||||
resp.Side = order.Buy
|
||||
case order.Sell.Lower():
|
||||
resp.Side = order.Sell
|
||||
}
|
||||
switch s.Status {
|
||||
case strings.ToLower(order.New.String()):
|
||||
resp.Status = order.New
|
||||
case strings.ToLower(order.Open.String()):
|
||||
resp.Status = order.Open
|
||||
case closedStatus:
|
||||
if s.FilledSize != 0 && s.FilledSize != s.Size {
|
||||
resp.Status = order.PartiallyCancelled
|
||||
}
|
||||
if s.FilledSize == 0 {
|
||||
resp.Status = order.Cancelled
|
||||
}
|
||||
if s.FilledSize == s.Size {
|
||||
resp.Status = order.Filled
|
||||
}
|
||||
}
|
||||
var feeBuilder exchange.FeeBuilder
|
||||
feeBuilder.PurchasePrice = s.AvgFillPrice
|
||||
feeBuilder.Amount = s.Size
|
||||
resp.OrderType = order.Market
|
||||
if strings.EqualFold(s.OrderType, order.Limit.String()) {
|
||||
resp.OrderType = order.Limit
|
||||
feeBuilder.IsMaker = true
|
||||
}
|
||||
fee, err := f.GetFee(&feeBuilder)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
resp.Fee = fee
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetOrderInfo returns information on a current open order
|
||||
func (f *FTX) GetOrderInfo(orderID string) (order.Detail, error) {
|
||||
var resp order.Detail
|
||||
orderData, err := f.GetOrderStatus(orderID)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
p := currency.NewPairFromString(orderData.Market)
|
||||
assetType, err := f.GetPairAssetType(p)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
resp.ID = strconv.FormatInt(orderData.ID, 10)
|
||||
resp.Amount = orderData.Size
|
||||
resp.ClientOrderID = orderData.ClientID
|
||||
resp.Date = orderData.CreatedAt
|
||||
resp.Exchange = f.Name
|
||||
resp.ExecutedAmount = orderData.Size - orderData.RemainingSize
|
||||
resp.Pair = p
|
||||
resp.AssetType = assetType
|
||||
resp.Price = orderData.Price
|
||||
resp.RemainingAmount = orderData.RemainingSize
|
||||
orderVars, err := orderData.GetCompatible(f)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
resp.Status = orderVars.Status
|
||||
resp.Side = orderVars.Side
|
||||
resp.Type = orderVars.OrderType
|
||||
resp.Fee = orderVars.Fee
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetDepositAddress returns a deposit address for a specified currency
|
||||
func (f *FTX) GetDepositAddress(cryptocurrency currency.Code, _ string) (string, error) {
|
||||
a, err := f.FetchDepositAddress(cryptocurrency.String())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return a.Address, nil
|
||||
}
|
||||
|
||||
// WithdrawCryptocurrencyFunds returns a withdrawal ID when a withdrawal is
|
||||
// submitted
|
||||
func (f *FTX) WithdrawCryptocurrencyFunds(withdrawRequest *withdraw.Request) (*withdraw.ExchangeResponse, error) {
|
||||
var address, addressTag string
|
||||
if withdrawRequest.Crypto != nil {
|
||||
address = withdrawRequest.Crypto.Address
|
||||
addressTag = withdrawRequest.Crypto.AddressTag
|
||||
}
|
||||
resp := withdraw.ExchangeResponse{}
|
||||
a, err := f.Withdraw(withdrawRequest.Currency.String(),
|
||||
address,
|
||||
addressTag,
|
||||
withdrawRequest.TradePassword,
|
||||
strconv.FormatInt(withdrawRequest.OneTimePassword, 10),
|
||||
withdrawRequest.Amount)
|
||||
if err != nil {
|
||||
return &resp, err
|
||||
}
|
||||
resp.ID = strconv.FormatInt(a.ID, 10)
|
||||
resp.Status = a.Status
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
// WithdrawFiatFunds returns a withdrawal ID when a withdrawal is
|
||||
// submitted
|
||||
func (f *FTX) WithdrawFiatFunds(_ *withdraw.Request) (*withdraw.ExchangeResponse, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// WithdrawFiatFundsToInternationalBank returns a withdrawal ID when a
|
||||
// withdrawal is submitted
|
||||
func (f *FTX) WithdrawFiatFundsToInternationalBank(_ *withdraw.Request) (*withdraw.ExchangeResponse, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetWebsocket returns a pointer to the exchange websocket
|
||||
func (f *FTX) GetWebsocket() (*wshandler.Websocket, error) {
|
||||
return f.Websocket, nil
|
||||
}
|
||||
|
||||
// GetActiveOrders retrieves any orders that are active/open
|
||||
func (f *FTX) GetActiveOrders(getOrdersRequest *order.GetOrdersRequest) ([]order.Detail, error) {
|
||||
var resp []order.Detail
|
||||
for x := range getOrdersRequest.Pairs {
|
||||
assetType, err := f.GetPairAssetType(getOrdersRequest.Pairs[x])
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
var tempResp order.Detail
|
||||
orderData, err := f.GetOpenOrders(f.FormatExchangeCurrency(getOrdersRequest.Pairs[x], assetType).String())
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
for y := range orderData {
|
||||
tempResp.ID = strconv.FormatInt(orderData[y].ID, 10)
|
||||
tempResp.Amount = orderData[y].Size
|
||||
tempResp.AssetType = assetType
|
||||
tempResp.ClientOrderID = orderData[y].ClientID
|
||||
tempResp.Date = orderData[y].CreatedAt
|
||||
tempResp.Exchange = f.Name
|
||||
tempResp.ExecutedAmount = orderData[y].Size - orderData[y].RemainingSize
|
||||
tempResp.Pair = currency.NewPairFromString(orderData[y].Market)
|
||||
tempResp.Price = orderData[y].Price
|
||||
tempResp.RemainingAmount = orderData[y].RemainingSize
|
||||
var orderVars OrderVars
|
||||
orderVars, err = f.compatibleOrderVars(orderData[y].Side,
|
||||
orderData[y].Status,
|
||||
orderData[y].OrderType,
|
||||
orderData[y].FilledSize,
|
||||
orderData[y].Size,
|
||||
orderData[y].AvgFillPrice)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
tempResp.Status = orderVars.Status
|
||||
tempResp.Side = orderVars.Side
|
||||
tempResp.Type = orderVars.OrderType
|
||||
tempResp.Fee = orderVars.Fee
|
||||
resp = append(resp, tempResp)
|
||||
}
|
||||
triggerOrderData, err := f.GetOpenTriggerOrders(f.FormatExchangeCurrency(getOrdersRequest.Pairs[x], assetType).String(), getOrdersRequest.Type.String())
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
for z := range triggerOrderData {
|
||||
tempResp.ID = strconv.FormatInt(triggerOrderData[z].ID, 10)
|
||||
tempResp.Amount = triggerOrderData[z].Size
|
||||
tempResp.AssetType = assetType
|
||||
tempResp.Date = triggerOrderData[z].CreatedAt
|
||||
tempResp.Exchange = f.Name
|
||||
tempResp.ExecutedAmount = triggerOrderData[z].FilledSize
|
||||
tempResp.Pair = currency.NewPairFromString(triggerOrderData[z].Market)
|
||||
tempResp.Price = triggerOrderData[z].AvgFillPrice
|
||||
tempResp.RemainingAmount = triggerOrderData[z].Size - triggerOrderData[z].FilledSize
|
||||
tempResp.TriggerPrice = triggerOrderData[z].TriggerPrice
|
||||
orderVars, err := f.compatibleOrderVars(triggerOrderData[z].Side,
|
||||
triggerOrderData[z].Status,
|
||||
triggerOrderData[z].OrderType,
|
||||
triggerOrderData[z].FilledSize,
|
||||
triggerOrderData[z].Size,
|
||||
triggerOrderData[z].AvgFillPrice)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
tempResp.Status = orderVars.Status
|
||||
tempResp.Side = orderVars.Side
|
||||
tempResp.Type = orderVars.OrderType
|
||||
tempResp.Fee = orderVars.Fee
|
||||
resp = append(resp, tempResp)
|
||||
}
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetOrderHistory retrieves account order information
|
||||
// Can Limit response to specific order status
|
||||
func (f *FTX) GetOrderHistory(getOrdersRequest *order.GetOrdersRequest) ([]order.Detail, error) {
|
||||
var resp []order.Detail
|
||||
for x := range getOrdersRequest.Pairs {
|
||||
var tempResp order.Detail
|
||||
assetType, err := f.GetPairAssetType(getOrdersRequest.Pairs[x])
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
orderData, err := f.FetchOrderHistory(f.FormatExchangeCurrency(getOrdersRequest.Pairs[x], assetType).String(),
|
||||
getOrdersRequest.StartTicks, getOrdersRequest.EndTicks, "")
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
for y := range orderData {
|
||||
tempResp.ID = strconv.FormatInt(orderData[y].ID, 10)
|
||||
tempResp.Amount = orderData[y].Size
|
||||
tempResp.AssetType = assetType
|
||||
tempResp.ClientOrderID = orderData[y].ClientID
|
||||
tempResp.Date = orderData[y].CreatedAt
|
||||
tempResp.Exchange = f.Name
|
||||
tempResp.ExecutedAmount = orderData[y].Size - orderData[y].RemainingSize
|
||||
tempResp.Pair = currency.NewPairFromString(orderData[y].Market)
|
||||
tempResp.Price = orderData[y].Price
|
||||
tempResp.RemainingAmount = orderData[y].RemainingSize
|
||||
var orderVars OrderVars
|
||||
orderVars, err = f.compatibleOrderVars(orderData[y].Side,
|
||||
orderData[y].Status,
|
||||
orderData[y].OrderType,
|
||||
orderData[y].FilledSize,
|
||||
orderData[y].Size,
|
||||
orderData[y].AvgFillPrice)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
tempResp.Status = orderVars.Status
|
||||
tempResp.Side = orderVars.Side
|
||||
tempResp.Type = orderVars.OrderType
|
||||
tempResp.Fee = orderVars.Fee
|
||||
resp = append(resp, tempResp)
|
||||
}
|
||||
triggerOrderData, err := f.GetTriggerOrderHistory(f.FormatExchangeCurrency(getOrdersRequest.Pairs[x], assetType).String(),
|
||||
getOrdersRequest.StartTicks, getOrdersRequest.EndTicks, strings.ToLower(getOrdersRequest.Side.String()), strings.ToLower(getOrdersRequest.Type.String()), "")
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
for z := range triggerOrderData {
|
||||
tempResp.ID = strconv.FormatInt(triggerOrderData[z].ID, 10)
|
||||
tempResp.Amount = triggerOrderData[z].Size
|
||||
tempResp.AssetType = assetType
|
||||
tempResp.Date = triggerOrderData[z].CreatedAt
|
||||
tempResp.Exchange = f.Name
|
||||
tempResp.ExecutedAmount = triggerOrderData[z].FilledSize
|
||||
tempResp.Pair = currency.NewPairFromString(triggerOrderData[z].Market)
|
||||
tempResp.Price = triggerOrderData[z].AvgFillPrice
|
||||
tempResp.RemainingAmount = triggerOrderData[z].Size - triggerOrderData[z].FilledSize
|
||||
tempResp.TriggerPrice = triggerOrderData[z].TriggerPrice
|
||||
orderVars, err := f.compatibleOrderVars(triggerOrderData[z].Side,
|
||||
triggerOrderData[z].Status,
|
||||
triggerOrderData[z].OrderType,
|
||||
triggerOrderData[z].FilledSize,
|
||||
triggerOrderData[z].Size,
|
||||
triggerOrderData[z].AvgFillPrice)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
tempResp.Status = orderVars.Status
|
||||
tempResp.Side = orderVars.Side
|
||||
tempResp.Type = orderVars.OrderType
|
||||
tempResp.Fee = orderVars.Fee
|
||||
resp = append(resp, tempResp)
|
||||
}
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetFeeByType returns an estimate of fee based on the type of transaction
|
||||
func (f *FTX) GetFeeByType(feeBuilder *exchange.FeeBuilder) (float64, error) {
|
||||
return f.GetFee(feeBuilder)
|
||||
}
|
||||
|
||||
// SubscribeToWebsocketChannels appends to ChannelsToSubscribe
|
||||
// which lets websocket.manageSubscriptions handle subscribing
|
||||
func (f *FTX) SubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error {
|
||||
f.Websocket.SubscribeToChannels(channels)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnsubscribeToWebsocketChannels removes from ChannelsToSubscribe
|
||||
// which lets websocket.manageSubscriptions handle unsubscribing
|
||||
func (f *FTX) UnsubscribeToWebsocketChannels(channels []wshandler.WebsocketChannelSubscription) error {
|
||||
f.Websocket.RemoveSubscribedChannels(channels)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetSubscriptions returns a copied list of subscriptions
|
||||
func (f *FTX) GetSubscriptions() ([]wshandler.WebsocketChannelSubscription, error) {
|
||||
return f.Websocket.GetSubscriptions(), nil
|
||||
}
|
||||
|
||||
// AuthenticateWebsocket sends an authentication message to the websocket
|
||||
func (f *FTX) AuthenticateWebsocket() error {
|
||||
return f.WsAuth()
|
||||
}
|
||||
|
||||
// ValidateCredentials validates current credentials used for wrapper
|
||||
// functionality
|
||||
func (f *FTX) ValidateCredentials() error {
|
||||
_, err := f.UpdateAccountInfo()
|
||||
return f.CheckTransientError(err)
|
||||
}
|
||||
|
||||
// GetHistoricCandles returns candles between a time period for a set time interval
|
||||
func (f *FTX) GetHistoricCandles(pair currency.Pair, a asset.Item, start, end time.Time, interval time.Duration) (kline.Item, error) {
|
||||
intervalToString, err := parseInterval(interval)
|
||||
if err != nil {
|
||||
return kline.Item{}, err
|
||||
}
|
||||
var resp kline.Item
|
||||
ohlcData, err := f.GetHistoricalData(f.FormatExchangeCurrency(pair, a).String(),
|
||||
string(intervalToString), "", start, end)
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
resp.Exchange = f.Name
|
||||
resp.Asset = a
|
||||
resp.Pair = pair
|
||||
for x := range ohlcData {
|
||||
var tempData kline.Candle
|
||||
tempData.Open = ohlcData[x].Open
|
||||
tempData.High = ohlcData[x].High
|
||||
tempData.Low = ohlcData[x].Low
|
||||
tempData.Close = ohlcData[x].Close
|
||||
tempData.Volume = ohlcData[x].Volume
|
||||
tempData.Time = ohlcData[x].StartTime
|
||||
resp.Candles = append(resp.Candles, tempData)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
@@ -408,8 +408,8 @@ func (g *Gateio) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (g *Gateio) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (g *Gateio) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -330,8 +330,8 @@ func (g *Gemini) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (g *Gemini) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (g *Gemini) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -386,8 +386,8 @@ func (h *HitBTC) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (h *HitBTC) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (h *HitBTC) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -498,8 +498,8 @@ func (h *HUOBI) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (h *HUOBI) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (h *HUOBI) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ type IBotExchange interface {
|
||||
GetAuthenticatedAPISupport(endpoint uint8) bool
|
||||
SetPairs(pairs currency.Pairs, a asset.Item, enabled bool) error
|
||||
GetAssetTypes() asset.Items
|
||||
GetExchangeHistory(p currency.Pair, a asset.Item) ([]TradeHistory, error)
|
||||
GetExchangeHistory(p currency.Pair, a asset.Item, startTime, endTime time.Time) ([]TradeHistory, error)
|
||||
SupportsAutoPairUpdates() bool
|
||||
SupportsRESTTickerBatchUpdates() bool
|
||||
GetFeeByType(f *FeeBuilder) (float64, error)
|
||||
|
||||
@@ -304,8 +304,8 @@ func (i *ItBit) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (i *ItBit) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (i *ItBit) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -9,19 +9,20 @@ import (
|
||||
|
||||
// Consts here define basic time intervals
|
||||
const (
|
||||
OneMin = time.Minute
|
||||
ThreeMin = 3 * time.Minute
|
||||
FiveMin = 5 * time.Minute
|
||||
FifteenMin = 15 * time.Minute
|
||||
ThirtyMin = 30 * time.Minute
|
||||
OneHour = 1 * time.Hour
|
||||
TwoHour = 2 * time.Hour
|
||||
FourHour = 4 * time.Hour
|
||||
SixHour = 6 * time.Hour
|
||||
TwelveHour = 12 * time.Hour
|
||||
OneDay = 24 * time.Hour
|
||||
ThreeDay = 72 * time.Hour
|
||||
OneWeek = 168 * time.Hour
|
||||
FifteenSecond = 15 * time.Second
|
||||
OneMin = time.Minute
|
||||
ThreeMin = 3 * time.Minute
|
||||
FiveMin = 5 * time.Minute
|
||||
FifteenMin = 15 * time.Minute
|
||||
ThirtyMin = 30 * time.Minute
|
||||
OneHour = 1 * time.Hour
|
||||
TwoHour = 2 * time.Hour
|
||||
FourHour = 4 * time.Hour
|
||||
SixHour = 6 * time.Hour
|
||||
TwelveHour = 12 * time.Hour
|
||||
OneDay = 24 * time.Hour
|
||||
ThreeDay = 72 * time.Hour
|
||||
OneWeek = 168 * time.Hour
|
||||
)
|
||||
|
||||
// Item holds all the relevant information for internal kline elements
|
||||
|
||||
@@ -450,8 +450,8 @@ func (k *Kraken) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (k *Kraken) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (k *Kraken) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -335,8 +335,8 @@ func (l *LakeBTC) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (l *LakeBTC) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (l *LakeBTC) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -304,8 +304,8 @@ func (l *Lbank) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (l *Lbank) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (l *Lbank) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
|
||||
@@ -285,8 +285,8 @@ func (l *LocalBitcoins) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (l *LocalBitcoins) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (l *LocalBitcoins) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-corp/gocryptotrader/common"
|
||||
"github.com/thrasher-corp/gocryptotrader/config"
|
||||
@@ -261,8 +262,8 @@ func (o *OKGroup) GetFundingHistory() (resp []exchange.FundHistory, err error) {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (o *OKGroup) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (o *OKGroup) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ type Submit struct {
|
||||
ID string
|
||||
AccountID string
|
||||
ClientID string
|
||||
ClientOrderID string
|
||||
WalletAddress string
|
||||
Type Type
|
||||
Side Side
|
||||
@@ -83,6 +84,7 @@ type Modify struct {
|
||||
Exchange string
|
||||
InternalOrderID string
|
||||
ID string
|
||||
ClientOrderID string
|
||||
AccountID string
|
||||
ClientID string
|
||||
WalletAddress string
|
||||
@@ -122,6 +124,7 @@ type Detail struct {
|
||||
Exchange string
|
||||
InternalOrderID string
|
||||
ID string
|
||||
ClientOrderID string
|
||||
AccountID string
|
||||
ClientID string
|
||||
WalletAddress string
|
||||
@@ -145,6 +148,7 @@ type Cancel struct {
|
||||
Amount float64
|
||||
Exchange string
|
||||
ID string
|
||||
ClientOrderID string
|
||||
AccountID string
|
||||
ClientID string
|
||||
WalletAddress string
|
||||
|
||||
@@ -370,8 +370,8 @@ func (p *Poloniex) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (p *Poloniex) GetExchangeHistory(currencyPair currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (p *Poloniex) GetExchangeHistory(currencyPair currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ var Exchanges = []string{
|
||||
"coinbene",
|
||||
"coinut",
|
||||
"exmo",
|
||||
"ftx",
|
||||
"gateio",
|
||||
"gemini",
|
||||
"hitbtc",
|
||||
|
||||
@@ -321,8 +321,8 @@ func (y *Yobit) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (y *Yobit) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (y *Yobit) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
@@ -374,8 +374,8 @@ func (z *ZB) GetFundingHistory() ([]exchange.FundHistory, error) {
|
||||
return nil, common.ErrFunctionNotSupported
|
||||
}
|
||||
|
||||
// GetExchangeHistory returns historic trade data since exchange opening.
|
||||
func (z *ZB) GetExchangeHistory(p currency.Pair, assetType asset.Item) ([]exchange.TradeHistory, error) {
|
||||
// GetExchangeHistory returns historic trade data within the timeframe provided.
|
||||
func (z *ZB) GetExchangeHistory(p currency.Pair, assetType asset.Item, timestampStart, timestampEnd time.Time) ([]exchange.TradeHistory, error) {
|
||||
return nil, common.ErrNotYetImplemented
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user