Added support for Bitflyer exchange

This commit is contained in:
Ryan O'Hara-Reid
2018-02-09 11:00:58 +11:00
parent 64a7bc206a
commit e57aa817dc
9 changed files with 994 additions and 4 deletions

View File

@@ -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 |

View File

@@ -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 {

View File

@@ -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 @@
}
}
]
}
}

View File

@@ -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":

View 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)
}

View 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)
}
}

View 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"`
}

View 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
}

View File

@@ -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 @@
}
}
]
}
}