mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-13 23:16:45 +00:00
* set limiter to first level mock data list and updated unit tests * address nested slices length limit * minor fix recording file and update unit tests * minor updates on unit tests * re-record mock files and minor fix on the unit tests ti adapt the mock data change * improve http recording limit value and fix issues with mock data in binance * added MockDataSliceLimit in request items and resolve minor unit test issues * resolve missed conflict * rename mock variables, resolve unit test issues, and other updates * minor fix to CheckJSON and update unit tests * minor unit test fix * further optimization on mock CheckJSON method, unit tests, and re-record poloniex * common and recording unit tests fix * minor linter issues fix * unit tests format fix * fix miscellaneous error * unit tests fix and minor docs update * re-record and reduce mock file size * indentation fix * minor assertion test fix * reverted log.Printf line in live testing * rename variables * update NewVCRServer unit test * replace string comparison with *net.OpError check * restructur net error test * exchanges/mock: Remove redundant error assertion message in TestNewVCRServer --------- Co-authored-by: Adrian Gallagher <adrian.gallagher@thrasher.io>
343 lines
11 KiB
Go
343 lines
11 KiB
Go
package bitflyer
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
|
|
"github.com/thrasher-corp/gocryptotrader/currency"
|
|
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
|
|
"github.com/thrasher-corp/gocryptotrader/exchanges/request"
|
|
)
|
|
|
|
const (
|
|
// Bitflyer chain analysis endpoints
|
|
// APIURL
|
|
chainAnalysis = "https://chainflyer.bitflyer.jp/v1/"
|
|
tradeBaseURL = "https://lightning.bitflyer.com/trade/"
|
|
|
|
// 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"
|
|
|
|
orders request.EndpointLimit = iota
|
|
lowVolume
|
|
)
|
|
|
|
// Exchange implements exchange.IBotExchange and contains additional specific api methods for interacting with Bitflyer
|
|
type Exchange struct {
|
|
exchange.Base
|
|
}
|
|
|
|
// GetLatestBlockCA returns the latest block information from bitflyer chain
|
|
// analysis system
|
|
func (e *Exchange) GetLatestBlockCA(ctx context.Context) (ChainAnalysisBlock, error) {
|
|
var resp ChainAnalysisBlock
|
|
return resp, e.SendHTTPRequest(ctx, exchange.ChainAnalysis, latestBlock, &resp)
|
|
}
|
|
|
|
// GetBlockCA returns block information by blockhash from bitflyer chain
|
|
// analysis system
|
|
func (e *Exchange) GetBlockCA(ctx context.Context, blockhash string) (ChainAnalysisBlock, error) {
|
|
var resp ChainAnalysisBlock
|
|
return resp, e.SendHTTPRequest(ctx, exchange.ChainAnalysis, blockByBlockHash+blockhash, &resp)
|
|
}
|
|
|
|
// GetBlockbyHeightCA returns the block information by height from bitflyer chain
|
|
// analysis system
|
|
func (e *Exchange) GetBlockbyHeightCA(ctx context.Context, height int64) (ChainAnalysisBlock, error) {
|
|
var resp ChainAnalysisBlock
|
|
return resp, e.SendHTTPRequest(ctx, exchange.ChainAnalysis, blockByBlockHeight+strconv.FormatInt(height, 10), &resp)
|
|
}
|
|
|
|
// GetTransactionByHashCA returns transaction information by txHash from
|
|
// bitflyer chain analysis system
|
|
func (e *Exchange) GetTransactionByHashCA(ctx context.Context, txHash string) (ChainAnalysisTransaction, error) {
|
|
var resp ChainAnalysisTransaction
|
|
return resp, e.SendHTTPRequest(ctx, exchange.ChainAnalysis, transaction+txHash, &resp)
|
|
}
|
|
|
|
// GetAddressInfoCA returns balance information for address by addressln string
|
|
// from bitflyer chain analysis system
|
|
func (e *Exchange) GetAddressInfoCA(ctx context.Context, addressln string) (ChainAnalysisAddress, error) {
|
|
var resp ChainAnalysisAddress
|
|
return resp, e.SendHTTPRequest(ctx, exchange.ChainAnalysis, address+addressln, &resp)
|
|
}
|
|
|
|
// GetMarkets returns market information
|
|
func (e *Exchange) GetMarkets(ctx context.Context) ([]MarketInfo, error) {
|
|
var resp []MarketInfo
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestSpot, pubGetMarkets, &resp)
|
|
}
|
|
|
|
// GetOrderBook returns market orderbook depth
|
|
func (e *Exchange) GetOrderBook(ctx context.Context, symbol string) (Orderbook, error) {
|
|
var resp Orderbook
|
|
v := url.Values{}
|
|
v.Set("product_code", symbol)
|
|
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestSpot, pubGetBoard+"?"+v.Encode(), &resp)
|
|
}
|
|
|
|
// GetTicker returns ticker information
|
|
func (e *Exchange) GetTicker(ctx context.Context, symbol string) (Ticker, error) {
|
|
var resp Ticker
|
|
v := url.Values{}
|
|
v.Set("product_code", symbol)
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestSpot, pubGetTicker+"?"+v.Encode(), &resp)
|
|
}
|
|
|
|
// GetExecutionHistory returns past trades that were executed on the market
|
|
func (e *Exchange) GetExecutionHistory(ctx context.Context, symbol string) ([]ExecutedTrade, error) {
|
|
var resp []ExecutedTrade
|
|
v := url.Values{}
|
|
v.Set("product_code", symbol)
|
|
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestSpot, pubGetExecutionHistory+"?"+v.Encode(), &resp)
|
|
}
|
|
|
|
// GetExchangeStatus returns exchange status information
|
|
func (e *Exchange) GetExchangeStatus(ctx context.Context) (string, error) {
|
|
resp := make(map[string]string)
|
|
err := e.SendHTTPRequest(ctx, exchange.RestSpot, pubGetHealth, &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 infinity
|
|
func (e *Exchange) GetChats(ctx context.Context, fromDate string) ([]ChatLog, error) {
|
|
var resp []ChatLog
|
|
v := url.Values{}
|
|
v.Set("from_date", fromDate)
|
|
return resp, e.SendHTTPRequest(ctx, exchange.RestSpot, pubGetChats+"?"+v.Encode(), &resp)
|
|
}
|
|
|
|
// GetPermissions returns current permissions for associated with your API
|
|
// keys
|
|
func (e *Exchange) GetPermissions() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// GetAccountBalance returnsthe full list of account funds
|
|
func (e *Exchange) GetAccountBalance() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// GetMarginStatus returns current margin status
|
|
func (e *Exchange) GetMarginStatus() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// GetCollateralAccounts returns a full list of collateralised accounts
|
|
func (e *Exchange) GetCollateralAccounts() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// GetCryptoDepositAddress returns an address for cryptocurrency deposits
|
|
func (e *Exchange) GetCryptoDepositAddress() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// GetDepositHistory returns a full history of deposits
|
|
func (e *Exchange) GetDepositHistory() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// GetTransactionHistory returns a full history of transactions
|
|
func (e *Exchange) GetTransactionHistory() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// GetBankAccSummary returns a full list of bank accounts assoc. with your keys
|
|
func (e *Exchange) GetBankAccSummary() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// GetCashDeposits returns a full list of cash deposits to the exchange
|
|
func (e *Exchange) GetCashDeposits() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// WithdrawFunds withdraws funds to a certain bank
|
|
func (e *Exchange) WithdrawFunds() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// GetDepositCancellationHistory returns the cancellation history of deposits
|
|
func (e *Exchange) GetDepositCancellationHistory() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// SendOrder creates new order
|
|
func (e *Exchange) SendOrder() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// CancelExistingOrder cancels an order
|
|
func (e *Exchange) CancelExistingOrder() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// SendParentOrder sends a special order
|
|
func (e *Exchange) SendParentOrder() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// CancelParentOrder cancels a special order
|
|
func (e *Exchange) CancelParentOrder() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// CancelAllExistingOrders cancels all orders on the exchange
|
|
func (e *Exchange) CancelAllExistingOrders() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// GetAllOrders returns a list of all orders
|
|
func (e *Exchange) GetAllOrders() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// GetParentOrders returns a list of all parent orders
|
|
func (e *Exchange) GetParentOrders() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// GetParentOrderDetails returns a detailing of a parent order
|
|
func (e *Exchange) GetParentOrderDetails() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// GetExecutions returns execution details
|
|
func (e *Exchange) GetExecutions() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// GetOpenInterestData returns a summary of open interest
|
|
func (e *Exchange) GetOpenInterestData() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// GetMarginChange returns collateral history
|
|
func (e *Exchange) GetMarginChange() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// GetTradingCommission returns trading commission
|
|
func (e *Exchange) GetTradingCommission() {
|
|
// Needs to be updated
|
|
}
|
|
|
|
// SendHTTPRequest sends an unauthenticated request
|
|
func (e *Exchange) SendHTTPRequest(ctx context.Context, ep exchange.URL, path string, result any) error {
|
|
endpoint, err := e.API.Endpoints.GetURL(ep)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
item := &request.Item{
|
|
Method: http.MethodGet,
|
|
Path: endpoint + path,
|
|
Result: result,
|
|
Verbose: e.Verbose,
|
|
HTTPDebugging: e.HTTPDebugging,
|
|
HTTPRecording: e.HTTPRecording,
|
|
HTTPMockDataSliceLimit: e.HTTPMockDataSliceLimit,
|
|
}
|
|
return e.SendPayload(ctx, request.UnAuth, func() (*request.Item, error) {
|
|
return item, nil
|
|
}, request.UnauthenticatedRequest)
|
|
}
|
|
|
|
// 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
|
|
// TODO: Fill out this function once API access is obtained
|
|
func (e *Exchange) SendAuthHTTPRequest() {
|
|
//nolint:gocritic // code example
|
|
// headers := make(map[string]string)
|
|
// headers["ACCESS-KEY"] = b.API.Credentials.Key
|
|
// headers["ACCESS-TIMESTAMP"] = strconv.FormatInt(time.Now().UnixNano(), 10)
|
|
}
|
|
|
|
// GetFee returns an estimate of fee based on type of transaction
|
|
// TODO: Figure out the weird fee structure. Do we use Bitcoin Easy Exchange,Lightning Spot,Bitcoin Market,Lightning FX/Futures ???
|
|
func (e *Exchange) GetFee(feeBuilder *exchange.FeeBuilder) (float64, error) {
|
|
var fee float64
|
|
|
|
switch feeBuilder.FeeType {
|
|
case exchange.CryptocurrencyTradeFee:
|
|
fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount)
|
|
case exchange.InternationalBankDepositFee:
|
|
fee = getDepositFee(feeBuilder.BankTransactionType, feeBuilder.FiatCurrency)
|
|
case exchange.InternationalBankWithdrawalFee:
|
|
fee = getWithdrawalFee(feeBuilder.BankTransactionType, feeBuilder.FiatCurrency, feeBuilder.Amount)
|
|
case exchange.OfflineTradeFee:
|
|
fee = calculateTradingFee(feeBuilder.PurchasePrice, feeBuilder.Amount)
|
|
}
|
|
if fee < 0 {
|
|
fee = 0
|
|
}
|
|
return fee, nil
|
|
}
|
|
|
|
// calculateTradingFee returns fee when performing a trade
|
|
func calculateTradingFee(price, amount float64) float64 {
|
|
// bitflyer has fee tiers, but does not disclose them via API, so the largest has to be assumed
|
|
return 0.0012 * price * amount
|
|
}
|
|
|
|
func getDepositFee(bankTransactionType exchange.InternationalBankTransactionType, c currency.Code) (fee float64) {
|
|
if bankTransactionType == exchange.WireTransfer {
|
|
if c.Item == currency.JPY.Item {
|
|
fee = 324
|
|
}
|
|
}
|
|
return fee
|
|
}
|
|
|
|
func getWithdrawalFee(bankTransactionType exchange.InternationalBankTransactionType, c currency.Code, amount float64) (fee float64) {
|
|
if bankTransactionType == exchange.WireTransfer {
|
|
if c.Item == currency.JPY.Item {
|
|
if amount < 30000 {
|
|
fee = 540
|
|
} else {
|
|
fee = 756
|
|
}
|
|
}
|
|
}
|
|
return fee
|
|
}
|