mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
Added support for Bitflyer exchange
This commit is contained in:
@@ -22,6 +22,7 @@ Join our slack to discuss all things related to GoCryptoTrader! [GoCryptoTrader
|
||||
| ANXPRO | Yes | No | NA |
|
||||
| Binance| Yes | No | NA |
|
||||
| Bitfinex | Yes | Yes | NA |
|
||||
| Bitflyer | Yes | No | NA |
|
||||
| Bithumb | Yes | NA | NA |
|
||||
| Bitstamp | Yes | Yes | NA |
|
||||
| Bittrex | Yes | No | NA |
|
||||
|
||||
@@ -89,7 +89,7 @@ func TestGetEnabledExchanges(t *testing.T) {
|
||||
}
|
||||
|
||||
exchanges := cfg.GetEnabledExchanges()
|
||||
if len(exchanges) != 25 {
|
||||
if len(exchanges) != 26 {
|
||||
t.Error(
|
||||
"Test failed. TestGetEnabledExchanges. Enabled exchanges value mismatch",
|
||||
)
|
||||
@@ -141,7 +141,7 @@ func TestGetDisabledExchanges(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCountEnabledExchanges(t *testing.T) {
|
||||
defaultEnabledExchanges := 25
|
||||
defaultEnabledExchanges := 26
|
||||
GetConfigEnabledExchanges := GetConfig()
|
||||
err := GetConfigEnabledExchanges.LoadConfig(ConfigTestFile)
|
||||
if err != nil {
|
||||
|
||||
@@ -122,6 +122,29 @@
|
||||
"Uppercase": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Bitflyer",
|
||||
"Enabled": true,
|
||||
"Verbose": false,
|
||||
"Websocket": false,
|
||||
"UseSandbox": false,
|
||||
"RESTPollingDelay": 10,
|
||||
"AuthenticatedAPISupport": false,
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"AvailablePairs": "BTC_JPY,FXBTC_JPY,ETH_BTC,BCH_BTC",
|
||||
"EnabledPairs": "BTC_JPY,ETH_BTC,BCH_BTC",
|
||||
"BaseCurrencies": "JPY",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true,
|
||||
"Delimiter": "_"
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": true,
|
||||
"Delimiter": "_"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Bithumb",
|
||||
"Enabled": true,
|
||||
@@ -615,4 +638,4 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/anx"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/binance"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/bitfinex"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/bitflyer"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/bithumb"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/bitstamp"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/bittrex"
|
||||
@@ -136,6 +137,8 @@ func LoadExchange(name string) error {
|
||||
exch = new(binance.Binance)
|
||||
case "bitfinex":
|
||||
exch = new(bitfinex.Bitfinex)
|
||||
case "bitflyer":
|
||||
exch = new(bitflyer.Bitflyer)
|
||||
case "bithumb":
|
||||
exch = new(bithumb.Bithumb)
|
||||
case "bitstamp":
|
||||
|
||||
355
exchanges/bitflyer/bitflyer.go
Normal file
355
exchanges/bitflyer/bitflyer.go
Normal file
@@ -0,0 +1,355 @@
|
||||
package bitflyer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
const (
|
||||
// Bitflyer chain analysis endpoints
|
||||
// APIURL
|
||||
chainAnalysis = "https://chainflyer.bitflyer.jp/v1/"
|
||||
|
||||
// Public endpoints for chain analysis
|
||||
latestBlock = "block/latest"
|
||||
blockByBlockHash = "block/"
|
||||
blockByBlockHeight = "block/height/"
|
||||
transaction = "tx/"
|
||||
address = "address/"
|
||||
|
||||
// APIURL
|
||||
japanURL = "https://api.bitflyer.jp/v1"
|
||||
usURL = "https://api.bitflyer.com/v1"
|
||||
europeURL = "https://api.bitflyer.com/v1"
|
||||
|
||||
// Public Endpoints
|
||||
pubGetMarkets = "/getmarkets/"
|
||||
pubGetBoard = "/getboard"
|
||||
pubGetTicker = "/getticker"
|
||||
pubGetExecutionHistory = "/getexecutions"
|
||||
pubGetHealth = "/gethealth"
|
||||
pubGetChats = "/getchats"
|
||||
|
||||
// Autheticated Endpoints
|
||||
privGetPermissions = "/me/getpermissions"
|
||||
privGetBalance = "/me/getbalance"
|
||||
privMarginStatus = "/me/getcollateral"
|
||||
privGetCollateralAcc = "/me/getcollateralaccounts"
|
||||
privGetDepositAddress = "/me/getaddresses"
|
||||
privDepositHistory = "/me/getcoinins"
|
||||
privTransactionHistory = "/me/getcoinouts"
|
||||
privBankAccSummary = "/me/getbankaccounts"
|
||||
privGetDeposits = "/me/getdeposits"
|
||||
privWithdraw = "/me/withdraw"
|
||||
privDepositCancellationHistory = "/me/getwithdrawals"
|
||||
privSendOrder = "/me/sendchildorder"
|
||||
privCancelOrder = "/me/cancelchildorder"
|
||||
privParentOrder = "/me/sendparentorder"
|
||||
privCancelParentOrder = "/me/cancelparentorder"
|
||||
privCancelOrders = "/me/cancelallchildorders"
|
||||
privListOrders = "/me/getchildorders"
|
||||
privListParentOrders = "/me/getparentorders"
|
||||
privParentOrderDetails = "/me/getparentorder"
|
||||
privExecutions = "/me/getexecutions"
|
||||
privOpenInterest = "/me/getpositions"
|
||||
privMarginChange = "/me/getcollateralhistory"
|
||||
privTradingCommission = "/me/gettradingcommission"
|
||||
)
|
||||
|
||||
// Bitflyer is the overarching type across this package
|
||||
type Bitflyer struct {
|
||||
exchange.Base
|
||||
}
|
||||
|
||||
// SetDefaults sets the basic defaults for Bitflyer
|
||||
func (b *Bitflyer) SetDefaults() {
|
||||
b.Name = "Bitflyer"
|
||||
b.Enabled = false
|
||||
b.Verbose = false
|
||||
b.Websocket = false
|
||||
b.RESTPollingDelay = 10
|
||||
b.RequestCurrencyPairFormat.Delimiter = "_"
|
||||
b.RequestCurrencyPairFormat.Uppercase = true
|
||||
b.ConfigCurrencyPairFormat.Delimiter = "_"
|
||||
b.ConfigCurrencyPairFormat.Uppercase = true
|
||||
b.AssetTypes = []string{ticker.Spot}
|
||||
}
|
||||
|
||||
// Setup takes in the supplied exchange configuration details and sets params
|
||||
func (b *Bitflyer) Setup(exch config.ExchangeConfig) {
|
||||
if !exch.Enabled {
|
||||
b.SetEnabled(false)
|
||||
} else {
|
||||
b.Enabled = true
|
||||
b.AuthenticatedAPISupport = exch.AuthenticatedAPISupport
|
||||
b.SetAPIKeys(exch.APIKey, exch.APISecret, "", false)
|
||||
b.RESTPollingDelay = exch.RESTPollingDelay
|
||||
b.Verbose = exch.Verbose
|
||||
b.Websocket = exch.Websocket
|
||||
b.APIUrl = japanURL
|
||||
b.BaseCurrencies = common.SplitStrings(exch.BaseCurrencies, ",")
|
||||
b.AvailablePairs = common.SplitStrings(exch.AvailablePairs, ",")
|
||||
b.EnabledPairs = common.SplitStrings(exch.EnabledPairs, ",")
|
||||
err := b.SetCurrencyPairFormat()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
err = b.SetAssetTypes()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetLatestBlockCA returns the latest block information from bitflyer chain
|
||||
// analysis system
|
||||
func (b *Bitflyer) GetLatestBlockCA() (ChainAnalysisBlock, error) {
|
||||
var resp ChainAnalysisBlock
|
||||
path := fmt.Sprintf("%s%s", chainAnalysis, latestBlock)
|
||||
|
||||
return resp, common.SendHTTPGetRequest(path, true, b.Verbose, &resp)
|
||||
}
|
||||
|
||||
// GetBlockCA returns block information by blockhash from bitflyer chain
|
||||
// analysis system
|
||||
func (b *Bitflyer) GetBlockCA(blockhash string) (ChainAnalysisBlock, error) {
|
||||
var resp ChainAnalysisBlock
|
||||
path := fmt.Sprintf("%s%s%s", chainAnalysis, blockByBlockHash, blockhash)
|
||||
|
||||
return resp, common.SendHTTPGetRequest(path, true, b.Verbose, &resp)
|
||||
}
|
||||
|
||||
// GetBlockbyHeightCA returns the block information by height from bitflyer chain
|
||||
// analysis system
|
||||
func (b *Bitflyer) GetBlockbyHeightCA(height int64) (ChainAnalysisBlock, error) {
|
||||
var resp ChainAnalysisBlock
|
||||
path := fmt.Sprintf("%s%s%s", chainAnalysis, blockByBlockHeight, strconv.FormatInt(height, 10))
|
||||
|
||||
return resp, common.SendHTTPGetRequest(path, true, b.Verbose, &resp)
|
||||
}
|
||||
|
||||
// GetTransactionByHashCA returns transaction information by txHash from
|
||||
// bitflyer chain analysis system
|
||||
func (b *Bitflyer) GetTransactionByHashCA(txHash string) (ChainAnalysisTransaction, error) {
|
||||
var resp ChainAnalysisTransaction
|
||||
path := fmt.Sprintf("%s%s%s", chainAnalysis, transaction, txHash)
|
||||
|
||||
return resp, common.SendHTTPGetRequest(path, true, b.Verbose, &resp)
|
||||
}
|
||||
|
||||
// GetAddressInfoCA returns balance information for address by addressln string
|
||||
// from bitflyer chain analysis system
|
||||
func (b *Bitflyer) GetAddressInfoCA(addressln string) (ChainAnalysisAddress, error) {
|
||||
var resp ChainAnalysisAddress
|
||||
path := fmt.Sprintf("%s%s%s", chainAnalysis, address, addressln)
|
||||
|
||||
return resp, common.SendHTTPGetRequest(path, true, b.Verbose, &resp)
|
||||
}
|
||||
|
||||
// GetMarkets returns market information
|
||||
func (b *Bitflyer) GetMarkets() ([]MarketInfo, error) {
|
||||
var resp []MarketInfo
|
||||
path := fmt.Sprintf("%s%s", b.APIUrl, pubGetMarkets)
|
||||
|
||||
return resp, common.SendHTTPGetRequest(path, true, b.Verbose, &resp)
|
||||
}
|
||||
|
||||
// GetOrderBook returns market orderbook depth
|
||||
func (b *Bitflyer) GetOrderBook(symbol string) (Orderbook, error) {
|
||||
var resp Orderbook
|
||||
v := url.Values{}
|
||||
v.Set("product_code", symbol)
|
||||
path := fmt.Sprintf("%s%s?%s", japanURL, pubGetBoard, v.Encode())
|
||||
|
||||
return resp, common.SendHTTPGetRequest(path, true, b.Verbose, &resp)
|
||||
}
|
||||
|
||||
// GetTicker returns ticker information
|
||||
func (b *Bitflyer) GetTicker(symbol string) (Ticker, error) {
|
||||
var resp Ticker
|
||||
v := url.Values{}
|
||||
v.Set("product_code", symbol)
|
||||
path := fmt.Sprintf("%s%s?%s", japanURL, pubGetTicker, v.Encode())
|
||||
|
||||
return resp, common.SendHTTPGetRequest(path, true, b.Verbose, &resp)
|
||||
}
|
||||
|
||||
// GetExecutionHistory returns past trades that were executed on the market
|
||||
func (b *Bitflyer) GetExecutionHistory(symbol string) ([]ExecutedTrade, error) {
|
||||
var resp []ExecutedTrade
|
||||
v := url.Values{}
|
||||
v.Set("product_code", symbol)
|
||||
path := fmt.Sprintf("%s%s?%s", japanURL, pubGetExecutionHistory, v.Encode())
|
||||
|
||||
return resp, common.SendHTTPGetRequest(path, true, b.Verbose, &resp)
|
||||
}
|
||||
|
||||
// GetExchangeStatus returns exchange status information
|
||||
func (b *Bitflyer) GetExchangeStatus() (string, error) {
|
||||
resp := make(map[string]string)
|
||||
|
||||
path := fmt.Sprintf("%s%s", b.APIUrl, pubGetHealth)
|
||||
|
||||
err := common.SendHTTPGetRequest(path, true, b.Verbose, &resp)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
switch resp["status"] {
|
||||
case "BUSY":
|
||||
return "the exchange is experiencing high traffic", nil
|
||||
case "VERY BUSY":
|
||||
return "the exchange is experiencing heavy traffic", nil
|
||||
case "SUPER BUSY":
|
||||
return "the exchange is experiencing extremely heavy traffic. There is a possibility that orders will fail or be processed after a delay.", nil
|
||||
case "STOP":
|
||||
return "STOP", errors.New("the exchange has been stopped. Orders will not be accepted")
|
||||
}
|
||||
|
||||
return "NORMAL", nil
|
||||
}
|
||||
|
||||
// GetChats returns trollbox chat log
|
||||
// Note: returns vary from instant to infinty
|
||||
func (b *Bitflyer) GetChats(FromDate string) ([]ChatLog, error) {
|
||||
var resp []ChatLog
|
||||
v := url.Values{}
|
||||
v.Set("from_date", FromDate)
|
||||
path := fmt.Sprintf("%s%s?%s", b.APIUrl, pubGetChats, v.Encode())
|
||||
|
||||
return resp, common.SendHTTPGetRequest(path, true, b.Verbose, &resp)
|
||||
}
|
||||
|
||||
// GetPermissions returns current permissions for associated with your API
|
||||
// keys
|
||||
func (b *Bitflyer) GetPermissions() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// GetAccountBalance returnsthe full list of account funds
|
||||
func (b *Bitflyer) GetAccountBalance() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// GetMarginStatus returns current margin status
|
||||
func (b *Bitflyer) GetMarginStatus() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// GetCollateralAccounts returns a full list of collateralised accounts
|
||||
func (b *Bitflyer) GetCollateralAccounts() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// GetDepositAddress returns an address for cryptocurrency deposits
|
||||
func (b *Bitflyer) GetDepositAddress() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// GetDepositHistory returns a full history of deposits
|
||||
func (b *Bitflyer) GetDepositHistory() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// GetTransactionHistory returns a full history of transactions
|
||||
func (b *Bitflyer) GetTransactionHistory() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// GetBankAccSummary returns a full list of bank accounts assoc. with your keys
|
||||
func (b *Bitflyer) GetBankAccSummary() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// GetCashDeposits returns a full list of cash deposits to the exchange
|
||||
func (b *Bitflyer) GetCashDeposits() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// WithdrawFunds withdraws funds to a certain bank
|
||||
func (b *Bitflyer) WithdrawFunds() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// GetDepositCancellationHistory returns the cancellation history of deposits
|
||||
func (b *Bitflyer) GetDepositCancellationHistory() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// SendOrder creates new order
|
||||
func (b *Bitflyer) SendOrder() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// CancelOrder cancels an order
|
||||
func (b *Bitflyer) CancelOrder() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// SendParentOrder sends a special order
|
||||
func (b *Bitflyer) SendParentOrder() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// CancelParentOrder cancels a special order
|
||||
func (b *Bitflyer) CancelParentOrder() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// CancelAllOrders cancels all orders on the exchange
|
||||
func (b *Bitflyer) CancelAllOrders() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// GetAllOrders returns a list of all orders
|
||||
func (b *Bitflyer) GetAllOrders() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// GetParentOrders returns a list of all parent orders
|
||||
func (b *Bitflyer) GetParentOrders() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// GetParentOrderDetails returns a detailing of a parent order
|
||||
func (b *Bitflyer) GetParentOrderDetails() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// GetExecutions returns execution details
|
||||
func (b *Bitflyer) GetExecutions() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// GetOpenInterest returns a summary of open interest
|
||||
func (b *Bitflyer) GetOpenInterest() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// GetMarginChange returns collateral history
|
||||
func (b *Bitflyer) GetMarginChange() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// GetTradingCommission returns trading commission
|
||||
func (b *Bitflyer) GetTradingCommission() {
|
||||
// Needs to be updated
|
||||
}
|
||||
|
||||
// SendAuthHTTPRequest sends an authenticated HTTP request
|
||||
// Note: HTTP not done due to incorrect account privileges, please open a PR
|
||||
// if you have access and update the authenticated requests
|
||||
func (b *Bitflyer) SendAuthHTTPRequest(path string, params url.Values, result interface{}) {
|
||||
headers := make(map[string]string)
|
||||
headers["ACCESS-KEY"] = b.APIKey
|
||||
headers["ACCESS-TIMESTAMP"] = strconv.FormatInt(int64(time.Now().UnixNano()), 10)
|
||||
}
|
||||
173
exchanges/bitflyer/bitflyer_test.go
Normal file
173
exchanges/bitflyer/bitflyer_test.go
Normal file
@@ -0,0 +1,173 @@
|
||||
package bitflyer
|
||||
|
||||
import (
|
||||
"log"
|
||||
"testing"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/config"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
)
|
||||
|
||||
// Please supply your own keys here for due diligence testing
|
||||
const (
|
||||
testAPIKey = ""
|
||||
testAPISecret = ""
|
||||
)
|
||||
|
||||
var b Bitflyer
|
||||
|
||||
func TestSetDefaults(t *testing.T) {
|
||||
b.SetDefaults()
|
||||
}
|
||||
|
||||
func TestSetup(t *testing.T) {
|
||||
cfg := config.GetConfig()
|
||||
cfg.LoadConfig("../../testdata/configtest.json")
|
||||
bitflyerConfig, err := cfg.GetExchangeConfig("Bitflyer")
|
||||
if err != nil {
|
||||
t.Error("Test Failed - bitflyer Setup() init error")
|
||||
}
|
||||
|
||||
bitflyerConfig.AuthenticatedAPISupport = true
|
||||
bitflyerConfig.APIKey = testAPIKey
|
||||
bitflyerConfig.APISecret = testAPISecret
|
||||
|
||||
b.Setup(bitflyerConfig)
|
||||
}
|
||||
|
||||
func TestGetLatestBlockCA(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetLatestBlockCA()
|
||||
if err != nil {
|
||||
t.Error("test failed - Bitflyer - GetLatestBlockCA() error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBlockCA(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetBlockCA("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f")
|
||||
if err != nil {
|
||||
t.Error("test failed - Bitflyer - GetBlockCA() error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetBlockbyHeightCA(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetBlockbyHeightCA(0)
|
||||
if err != nil {
|
||||
t.Error("test failed - Bitflyer - GetBlockbyHeightCA() error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTransactionByHashCA(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetTransactionByHashCA("0562d1f063cd4127053d838b165630445af5e480ceb24e1fd9ecea52903cb772")
|
||||
if err != nil {
|
||||
t.Error("test failed - Bitflyer - GetTransactionByHashCA() error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAddressInfoCA(t *testing.T) {
|
||||
t.Parallel()
|
||||
v, err := b.GetAddressInfoCA("1F5zVDgNjorJ51oGebSvNCrSAHpwGkUdDB")
|
||||
if err != nil {
|
||||
t.Error("test failed - Bitflyer - GetAddressInfoCA() error:", err)
|
||||
}
|
||||
if v.UnconfirmedBalance == 0 || v.ConfirmedBalance == 0 {
|
||||
log.Println("WARNING!: Donation wallet is empty :( - please consider donating")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetMarkets(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetMarkets()
|
||||
if err != nil {
|
||||
t.Error("test failed - Bitflyer - GetMarkets() error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrderBook(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetOrderBook("BTC_JPY")
|
||||
if err != nil {
|
||||
t.Error("test failed - Bitflyer - GetOrderBook() error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTicker(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetTicker("BTC_JPY")
|
||||
if err != nil {
|
||||
t.Error("test failed - Bitflyer - GetTicker() error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetExecutionHistory(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetExecutionHistory("BTC_JPY")
|
||||
if err != nil {
|
||||
t.Error("test failed - Bitflyer - GetExecutionHistory() error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetExchangeStatus(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, err := b.GetExchangeStatus()
|
||||
if err != nil {
|
||||
t.Error("test failed - Bitflyer - GetExchangeStatus() error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
// func TestGetChats(t *testing.T) {
|
||||
// t.Parallel()
|
||||
// time := time.Now().Format(time.RFC3339)
|
||||
// _, err := b.GetChats(time)
|
||||
// if err != nil {
|
||||
// t.Error("test failed - Bitflyer - GetChats() error:", err)
|
||||
// }
|
||||
// }
|
||||
|
||||
func TestUpdateTicker(t *testing.T) {
|
||||
t.Parallel()
|
||||
p := pair.NewCurrencyPairFromString("BTC_JPY")
|
||||
_, err := b.UpdateTicker(p, "SPOT")
|
||||
if err != nil {
|
||||
t.Error("test failed - Bitflyer - UpdateTicker() error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateOrderbook(t *testing.T) {
|
||||
t.Parallel()
|
||||
p := pair.NewCurrencyPairFromString("BTC_JPY")
|
||||
_, err := b.UpdateOrderbook(p, "SPOT")
|
||||
if err != nil {
|
||||
t.Error("test failed - Bitflyer - UpdateOrderbook() error:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckFXString(t *testing.T) {
|
||||
t.Parallel()
|
||||
p := pair.NewCurrencyPairDelimiter("FXBTC_JPY", "_")
|
||||
p = b.CheckFXString(p)
|
||||
if p.GetFirstCurrency().String() != "FX_BTC" {
|
||||
t.Error("test failed - Bitflyer - CheckFXString() error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTickerPrice(t *testing.T) {
|
||||
t.Parallel()
|
||||
var p pair.CurrencyPair
|
||||
|
||||
currencies := b.GetAvailableCurrencies()
|
||||
for _, pair := range currencies {
|
||||
if pair.Pair().String() == "FXBTC_JPY" {
|
||||
p = pair
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
_, err := b.GetTickerPrice(p, b.AssetTypes[0])
|
||||
if err != nil {
|
||||
t.Error("test failed - Bitflyer - GetTickerPrice() error", err)
|
||||
}
|
||||
}
|
||||
280
exchanges/bitflyer/bitflyer_types.go
Normal file
280
exchanges/bitflyer/bitflyer_types.go
Normal file
@@ -0,0 +1,280 @@
|
||||
package bitflyer
|
||||
|
||||
// ChainAnalysisBlock holds block information from the bitcoin network
|
||||
type ChainAnalysisBlock struct {
|
||||
BlockHash string `json:"block_hash"`
|
||||
Height int64 `json:"height"`
|
||||
IsMain bool `json:"is_main"`
|
||||
Version float64 `json:"version"`
|
||||
PreviousBlock string `json:"prev_block"`
|
||||
MerkleRoot string `json:"merkle_root"`
|
||||
Timestamp string `json:"timestamp"`
|
||||
Bits int64 `json:"bits"`
|
||||
Nonce int64 `json:"nonce"`
|
||||
TxNum int64 `json:"txnum"`
|
||||
TotalFees float64 `json:"total_fees"`
|
||||
TxHashes []string `json:"tx_hashes"`
|
||||
}
|
||||
|
||||
// ChainAnalysisTransaction holds transaction data from the bitcoin network
|
||||
type ChainAnalysisTransaction struct {
|
||||
TxHash string `json:"tx_hash"`
|
||||
BlockHeight int64 `json:"block_height"`
|
||||
Confirmations int64 `json:"confirmed"`
|
||||
Fees float64 `json:"fees"`
|
||||
Size int64 `json:"size"`
|
||||
ReceivedDate string `json:"received_date"`
|
||||
Version float64 `json:"version"`
|
||||
LockTime int64 `json:"lock_time"`
|
||||
Inputs []struct {
|
||||
PrevHash string `json:"prev_hash"`
|
||||
PrevIndex int `json:"prev_index"`
|
||||
Value int64 `json:"value"`
|
||||
Script string `json:"script"`
|
||||
Address string `json:"address"`
|
||||
Sequence int64 `json:"sequence"`
|
||||
} `json:"inputs"`
|
||||
Outputs []struct {
|
||||
Value int64 `json:"value"`
|
||||
Script string `json:"script"`
|
||||
Address string `json:"address"`
|
||||
} `json:"outputs"`
|
||||
}
|
||||
|
||||
// ChainAnalysisAddress holds address information from the bitcoin network
|
||||
type ChainAnalysisAddress struct {
|
||||
Address string `json:"address"`
|
||||
UnconfirmedBalance float64 `json:"unconfirmed_balance"`
|
||||
ConfirmedBalance float64 `json:"confirmed_balance"`
|
||||
}
|
||||
|
||||
// MarketInfo holds market information returned from bitflyer
|
||||
type MarketInfo struct {
|
||||
ProductCode string `json:"product_code"`
|
||||
Alias string `json:"alias"`
|
||||
}
|
||||
|
||||
// Orderbook holds orderbook information
|
||||
type Orderbook struct {
|
||||
MidPrice float64 `json:"mid_price"`
|
||||
Bids []struct {
|
||||
Price float64 `json:"price"`
|
||||
Size float64 `json:"size"`
|
||||
} `json:"bids"`
|
||||
Asks []struct {
|
||||
Price float64 `json:"price"`
|
||||
Size float64 `json:"size"`
|
||||
} `json:"asks"`
|
||||
}
|
||||
|
||||
// Ticker holds ticker information
|
||||
type Ticker struct {
|
||||
ProductCode string `json:"product_code"`
|
||||
TimeStamp string `json:"timestamp"`
|
||||
TickID int64 `json:"tick_id"`
|
||||
BestBid float64 `json:"best_bid"`
|
||||
BestAsk float64 `json:"best_ask"`
|
||||
BestBidSize float64 `json:"best_bid_size"`
|
||||
BestAskSize float64 `json:"best_ask_size"`
|
||||
TotalBidDepth float64 `json:"total_bid_depth"`
|
||||
TotalAskDepth float64 `json:"total_ask_depth"`
|
||||
Last float64 `json:"ltp"`
|
||||
Volume float64 `json:"volume"`
|
||||
VolumeByProduct float64 `json:"volume_by_product"`
|
||||
}
|
||||
|
||||
// ExecutedTrade holds past trade information
|
||||
type ExecutedTrade struct {
|
||||
ID int64 `json:"id"`
|
||||
Side string `json:"side"`
|
||||
Price float64 `json:"price"`
|
||||
Size float64 `json:"size"`
|
||||
ExecDate string `json:"exec_date"`
|
||||
BuyAcceptedID string `json:"buy_child_order_acceptance_id"`
|
||||
SellAcceptedID string `json:"sell_child_order_acceptance_id"`
|
||||
}
|
||||
|
||||
// ChatLog holds chat log information
|
||||
type ChatLog struct {
|
||||
Nickname string `json:"nickname"`
|
||||
Message string `json:"message"`
|
||||
Date string `json:"date"`
|
||||
}
|
||||
|
||||
// AccountBalance holds account balance information
|
||||
type AccountBalance struct {
|
||||
CurrencyCode string `json:"currency_code"`
|
||||
Amount float64 `json:"amount"`
|
||||
Available float64 `json:"available"`
|
||||
}
|
||||
|
||||
// MarginStatus holds margin status information
|
||||
type MarginStatus struct {
|
||||
Collateral float64 `json:"collateral"`
|
||||
OpenPosPNL float64 `json:"open_position_pnl"`
|
||||
RequiredCollateral float64 `json:"require_collateral"`
|
||||
KeepRate float64 `json:"keep_rate"`
|
||||
}
|
||||
|
||||
// CollateralAccounts holds collateral balances
|
||||
type CollateralAccounts struct {
|
||||
CurrencyCode string `json:"currency_code"`
|
||||
Amount float64 `json:"amount"`
|
||||
}
|
||||
|
||||
// DepositAddress hold depositing address information
|
||||
type DepositAddress struct {
|
||||
Type string `json:"type"`
|
||||
CurrencyCode string `json:"currency_code"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
// DepositHistory holds deposit history information
|
||||
type DepositHistory struct {
|
||||
ID int64 `json:"id"`
|
||||
OrderID int64 `json:"order_id"`
|
||||
CurrencyCode string `json:"currency_code"`
|
||||
Amount float64 `json:"amount"`
|
||||
Address string `json:"address"`
|
||||
TXHash string `json:"tx_hash"`
|
||||
Status string `json:"status"`
|
||||
EventDate string `json:"event_date"`
|
||||
}
|
||||
|
||||
// TransactionHistory holds prior transaction history data
|
||||
type TransactionHistory struct {
|
||||
ID int64 `json:"id"`
|
||||
OrderID int64 `json:"order_id"`
|
||||
CurrencyCode string `json:"currency_code"`
|
||||
Amount float64 `json:"amount"`
|
||||
Address string `json:"address"`
|
||||
TXHash string `json:"tx_hash"`
|
||||
Fee float64 `json:"fee"`
|
||||
AdditionalFee float64 `json:"additional_fee"`
|
||||
Status string `json:"status"`
|
||||
EventDate string `json:"event_date"`
|
||||
}
|
||||
|
||||
// BankAccount holds bank account information
|
||||
type BankAccount struct {
|
||||
ID int64 `json:"id"`
|
||||
IsVerified bool `json:"is_verified"`
|
||||
BankName string `json:"bank_name"`
|
||||
BranchName string `json:"branch_name"`
|
||||
AccountType string `json:"account_type"`
|
||||
AccountNumber int `json:"account_number"`
|
||||
AccountName string `json:"account_name"`
|
||||
}
|
||||
|
||||
// CashDeposit holds cash deposit information
|
||||
type CashDeposit struct {
|
||||
ID int64 `json:"id"`
|
||||
OrderID string `json:"order_id"`
|
||||
CurrencyCode string `json:"currency_code"`
|
||||
Amount float64 `json:"amount"`
|
||||
Status string `json:"status"`
|
||||
EventDate string `json:"event_date"`
|
||||
}
|
||||
|
||||
// CancellationHistory cancellation history
|
||||
type CancellationHistory struct {
|
||||
ID int64 `json:"id"`
|
||||
OrderID string `json:"order_id"`
|
||||
CurrencyCode string `json:"currency_code"`
|
||||
Amount float64 `json:"amount"`
|
||||
Status string `json:"status"`
|
||||
EventDate string `json:"event_date"`
|
||||
}
|
||||
|
||||
// Orders holds order full order information
|
||||
type Orders struct {
|
||||
ID int64 `json:"id"`
|
||||
ChildOrderID string `json:"child_order_id"`
|
||||
ProductCode string `json:"product_code"`
|
||||
Side string `json:"side"`
|
||||
ChildOrderType string `json:"child_order_type"`
|
||||
Price float64 `json:"price"`
|
||||
AveragePrice float64 `json:"average_price"`
|
||||
Size float64 `json:"size"`
|
||||
ChildOrderState string `json:"child_order_state"`
|
||||
ExpireDate string `json:"expire_date"`
|
||||
ChildOrderDate string `json:"child_order_date"`
|
||||
ChildOrderAcceptanceID string `json:"child_order_acceptance_id"`
|
||||
OutstandingSize float64 `json:"outstanding_size"`
|
||||
CancelSize float64 `json:"cancel_size"`
|
||||
ExecutedSize float64 `json:"executed_size"`
|
||||
TotalCommission float64 `json:"total_commission"`
|
||||
}
|
||||
|
||||
// ParentOrders holds order full order information
|
||||
type ParentOrders struct {
|
||||
ID int64 `json:"id"`
|
||||
ParentOrderID string `json:"parent_order_id"`
|
||||
ProductCode string `json:"product_code"`
|
||||
Side string `json:"side"`
|
||||
ParentOrderType string `json:"parent_order_type"`
|
||||
Price float64 `json:"price"`
|
||||
AveragePrice float64 `json:"average_price"`
|
||||
Size float64 `json:"size"`
|
||||
ParentOrderState string `json:"parent_order_state"`
|
||||
ExpireDate string `json:"expire_date"`
|
||||
ParentOrderDate string `json:"parent_order_date"`
|
||||
ParentOrderAcceptanceID string `json:"parent_order_acceptance_id"`
|
||||
OutstandingSize float64 `json:"outstanding_size"`
|
||||
CancelSize float64 `json:"cancel_size"`
|
||||
ExecutedSize float64 `json:"executed_size"`
|
||||
TotalCommission float64 `json:"total_commission"`
|
||||
}
|
||||
|
||||
// ParentOrderDetail holds detailed information about an order
|
||||
type ParentOrderDetail struct {
|
||||
ID int64 `json:"id"`
|
||||
ParentOrderID string `json:"parent_order_id"`
|
||||
OrderMethod string `json:"order_method"`
|
||||
MinutesToExpire float64 `json:"minute_to_expire"`
|
||||
Parameters []struct {
|
||||
ProductCode string `json:"product_code"`
|
||||
ConditionType string `json:"condition_type"`
|
||||
Side string `json:"side"`
|
||||
Price float64 `json:"price"`
|
||||
Size float64 `json:"size"`
|
||||
TriggerPrice float64 `json:"trigger_price"`
|
||||
Offset float64 `json:"offset"`
|
||||
} `json:"parameters"`
|
||||
}
|
||||
|
||||
// Executions holds past executed trade details
|
||||
type Executions struct {
|
||||
ID int64 `json:"id"`
|
||||
ChildOrderID string `json:"child_order_id"`
|
||||
Side string `json:"side"`
|
||||
Price float64 `json:"price"`
|
||||
Size float64 `json:"size"`
|
||||
Commission float64 `json:"commission"`
|
||||
ExecDate string `json:"exec_date"`
|
||||
ChildOrderAcceptanceID string `json:"child_order_acceptance_id"`
|
||||
}
|
||||
|
||||
// OpenInterest holds open interest information
|
||||
type OpenInterest struct {
|
||||
ProductCode string `json:"product_code"`
|
||||
Side string `json:"side"`
|
||||
Price float64 `json:"price"`
|
||||
Size float64 `json:"size"`
|
||||
Commission float64 `json:"commission"`
|
||||
SwapPointAccumulate float64 `json:"swap_point_accumulate"`
|
||||
RequiredCollateral float64 `json:"require_collateral"`
|
||||
OpenDate string `json:"open_date"`
|
||||
Leverage float64 `json:"leverage"`
|
||||
PNL float64 `json:"pnl"`
|
||||
}
|
||||
|
||||
// CollateralHistory holds collateral history data
|
||||
type CollateralHistory struct {
|
||||
ID int64 `json:"id"`
|
||||
CurrencyCode string `json:"currency_code"`
|
||||
Change float64 `json:"change"`
|
||||
Amount float64 `json:"amount"`
|
||||
Reason string `json:"reason_code"`
|
||||
Date string `json:"date"`
|
||||
}
|
||||
132
exchanges/bitflyer/bitflyer_wrapper.go
Normal file
132
exchanges/bitflyer/bitflyer_wrapper.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package bitflyer
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
|
||||
"github.com/thrasher-/gocryptotrader/common"
|
||||
"github.com/thrasher-/gocryptotrader/currency/pair"
|
||||
exchange "github.com/thrasher-/gocryptotrader/exchanges"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/orderbook"
|
||||
"github.com/thrasher-/gocryptotrader/exchanges/ticker"
|
||||
)
|
||||
|
||||
// Start starts the Bitfinex go routine
|
||||
func (b *Bitflyer) Start() {
|
||||
go b.Run()
|
||||
}
|
||||
|
||||
// Run implements the Bitfinex wrapper
|
||||
func (b *Bitflyer) Run() {
|
||||
if b.Verbose {
|
||||
log.Printf("%s Websocket: %s.", b.GetName(), common.IsEnabled(b.Websocket))
|
||||
log.Printf("%s polling delay: %ds.\n", b.GetName(), b.RESTPollingDelay)
|
||||
log.Printf("%s %d currencies enabled: %s.\n", b.GetName(), len(b.EnabledPairs), b.EnabledPairs)
|
||||
}
|
||||
|
||||
marketInfo, err := b.GetMarkets()
|
||||
if err != nil {
|
||||
log.Printf("%s Failed to get available symbols.\n", b.GetName())
|
||||
} else {
|
||||
var exchangeProducts []string
|
||||
|
||||
for _, info := range marketInfo {
|
||||
exchangeProducts = append(exchangeProducts, info.ProductCode)
|
||||
}
|
||||
|
||||
err = b.UpdateAvailableCurrencies(exchangeProducts, false)
|
||||
if err != nil {
|
||||
log.Printf("%s Failed to get config.\n", b.GetName())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateTicker updates and returns the ticker for a currency pair
|
||||
func (b *Bitflyer) UpdateTicker(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
var tickerPrice ticker.Price
|
||||
|
||||
p = b.CheckFXString(p)
|
||||
|
||||
tickerNew, err := b.GetTicker(p.Pair().String())
|
||||
if err != nil {
|
||||
return tickerPrice, err
|
||||
}
|
||||
|
||||
tickerPrice.Pair = p
|
||||
tickerPrice.Ask = tickerNew.BestAsk
|
||||
tickerPrice.Bid = tickerNew.BestBid
|
||||
// tickerPrice.Low
|
||||
tickerPrice.Last = tickerNew.Last
|
||||
tickerPrice.Volume = tickerNew.Volume
|
||||
// tickerPrice.High
|
||||
ticker.ProcessTicker(b.GetName(), p, tickerPrice, assetType)
|
||||
return ticker.GetTicker(b.Name, p, assetType)
|
||||
}
|
||||
|
||||
// GetTickerPrice returns the ticker for a currency pair
|
||||
func (b *Bitflyer) GetTickerPrice(p pair.CurrencyPair, assetType string) (ticker.Price, error) {
|
||||
tick, err := ticker.GetTicker(b.GetName(), p, ticker.Spot)
|
||||
if err != nil {
|
||||
return b.UpdateTicker(p, assetType)
|
||||
}
|
||||
return tick, nil
|
||||
}
|
||||
|
||||
// CheckFXString upgrades currency pair if needed
|
||||
func (b *Bitflyer) CheckFXString(p pair.CurrencyPair) pair.CurrencyPair {
|
||||
if common.StringContains(p.GetFirstCurrency().String(), "FX") {
|
||||
p.FirstCurrency = "FX_BTC"
|
||||
return p
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// GetOrderbookEx returns the orderbook for a currency pair
|
||||
func (b *Bitflyer) GetOrderbookEx(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
ob, err := orderbook.GetOrderbook(b.GetName(), p, assetType)
|
||||
if err != nil {
|
||||
return b.UpdateOrderbook(p, assetType)
|
||||
}
|
||||
return ob, nil
|
||||
}
|
||||
|
||||
// UpdateOrderbook updates and returns the orderbook for a currency pair
|
||||
func (b *Bitflyer) UpdateOrderbook(p pair.CurrencyPair, assetType string) (orderbook.Base, error) {
|
||||
var orderBook orderbook.Base
|
||||
|
||||
p = b.CheckFXString(p)
|
||||
|
||||
orderbookNew, err := b.GetOrderBook(p.Pair().String())
|
||||
if err != nil {
|
||||
return orderBook, err
|
||||
}
|
||||
|
||||
for x := range orderbookNew.Asks {
|
||||
orderBook.Asks = append(orderBook.Asks, orderbook.Item{Price: orderbookNew.Asks[x].Price, Amount: orderbookNew.Asks[x].Size})
|
||||
}
|
||||
|
||||
for x := range orderbookNew.Bids {
|
||||
orderBook.Bids = append(orderBook.Bids, orderbook.Item{Price: orderbookNew.Bids[x].Price, Amount: orderbookNew.Bids[x].Size})
|
||||
}
|
||||
|
||||
orderbook.ProcessOrderbook(b.GetName(), p, orderBook, assetType)
|
||||
return orderbook.GetOrderbook(b.Name, p, assetType)
|
||||
}
|
||||
|
||||
// GetExchangeAccountInfo retrieves balances for all enabled currencies on the
|
||||
// Bitfinex exchange
|
||||
func (b *Bitflyer) GetExchangeAccountInfo() (exchange.AccountInfo, error) {
|
||||
var response exchange.AccountInfo
|
||||
response.ExchangeName = b.GetName()
|
||||
// accountBalance, err := b.GetAccountBalance()
|
||||
// if err != nil {
|
||||
// return response, err
|
||||
// }
|
||||
if !b.Enabled {
|
||||
return response, errors.New("exchange not enabled")
|
||||
}
|
||||
|
||||
// implement once authenticated requests are introduced
|
||||
|
||||
return response, nil
|
||||
}
|
||||
25
testdata/configtest.json
vendored
25
testdata/configtest.json
vendored
@@ -122,6 +122,29 @@
|
||||
"Uppercase": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Bitflyer",
|
||||
"Enabled": true,
|
||||
"Verbose": false,
|
||||
"Websocket": false,
|
||||
"UseSandbox": false,
|
||||
"RESTPollingDelay": 10,
|
||||
"AuthenticatedAPISupport": false,
|
||||
"APIKey": "Key",
|
||||
"APISecret": "Secret",
|
||||
"AvailablePairs": "BTC_JPY,FXBTC_JPY,ETH_BTC,BCH_BTC",
|
||||
"EnabledPairs": "BTC_JPY,ETH_BTC,BCH_BTC",
|
||||
"BaseCurrencies": "JPY",
|
||||
"AssetTypes": "SPOT",
|
||||
"ConfigCurrencyPairFormat": {
|
||||
"Uppercase": true,
|
||||
"Delimiter": "_"
|
||||
},
|
||||
"RequestCurrencyPairFormat": {
|
||||
"Uppercase": true,
|
||||
"Delimiter": "_"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Name": "Bithumb",
|
||||
"Enabled": true,
|
||||
@@ -615,4 +638,4 @@
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user