1036 lines
40 KiB
Python
1036 lines
40 KiB
Python
# -*- coding: utf-8 -*-
|
||
|
||
# PLEASE DO NOT EDIT THIS FILE, IT IS GENERATED AND WILL BE OVERWRITTEN:
|
||
# https://github.com/ccxt/ccxt/blob/master/CONTRIBUTING.md#how-to-contribute-code
|
||
|
||
from ccxt.async_support.base.exchange import Exchange
|
||
from ccxt.abstract.btcturk import ImplicitAPI
|
||
import hashlib
|
||
import math
|
||
from ccxt.base.types import Any, Balances, Bool, Int, Market, Num, Order, OrderBook, OrderSide, OrderType, Str, Strings, Ticker, Tickers, Trade
|
||
from typing import List
|
||
from ccxt.base.errors import ExchangeError
|
||
from ccxt.base.errors import BadRequest
|
||
from ccxt.base.errors import InsufficientFunds
|
||
from ccxt.base.errors import InvalidOrder
|
||
from ccxt.base.decimal_to_precision import TICK_SIZE
|
||
from ccxt.base.precise import Precise
|
||
|
||
|
||
class btcturk(Exchange, ImplicitAPI):
|
||
|
||
def describe(self) -> Any:
|
||
return self.deep_extend(super(btcturk, self).describe(), {
|
||
'id': 'btcturk',
|
||
'name': 'BTCTurk',
|
||
'countries': ['TR'], # Turkey
|
||
'rateLimit': 100,
|
||
'pro': False,
|
||
'has': {
|
||
'CORS': True,
|
||
'spot': True,
|
||
'margin': False,
|
||
'swap': False,
|
||
'future': False,
|
||
'option': False,
|
||
'addMargin': False,
|
||
'borrowCrossMargin': False,
|
||
'borrowIsolatedMargin': False,
|
||
'borrowMargin': False,
|
||
'cancelOrder': True,
|
||
'closeAllPositions': False,
|
||
'closePosition': False,
|
||
'createDepositAddress': False,
|
||
'createOrder': True,
|
||
'createOrderWithTakeProfitAndStopLoss': False,
|
||
'createOrderWithTakeProfitAndStopLossWs': False,
|
||
'createPostOnlyOrder': False,
|
||
'createReduceOnlyOrder': False,
|
||
'fetchBalance': True,
|
||
'fetchBorrowInterest': False,
|
||
'fetchBorrowRate': False,
|
||
'fetchBorrowRateHistories': False,
|
||
'fetchBorrowRateHistory': False,
|
||
'fetchBorrowRates': False,
|
||
'fetchBorrowRatesPerSymbol': False,
|
||
'fetchCrossBorrowRate': False,
|
||
'fetchCrossBorrowRates': False,
|
||
'fetchCurrencies': False,
|
||
'fetchDepositAddress': False,
|
||
'fetchDepositAddresses': False,
|
||
'fetchDepositAddressesByNetwork': False,
|
||
'fetchFundingHistory': False,
|
||
'fetchFundingInterval': False,
|
||
'fetchFundingIntervals': False,
|
||
'fetchFundingRate': False,
|
||
'fetchFundingRateHistory': False,
|
||
'fetchFundingRates': False,
|
||
'fetchGreeks': False,
|
||
'fetchIndexOHLCV': False,
|
||
'fetchIsolatedBorrowRate': False,
|
||
'fetchIsolatedBorrowRates': False,
|
||
'fetchIsolatedPositions': False,
|
||
'fetchLeverage': False,
|
||
'fetchLeverages': False,
|
||
'fetchLeverageTiers': False,
|
||
'fetchLiquidations': False,
|
||
'fetchLongShortRatio': False,
|
||
'fetchLongShortRatioHistory': False,
|
||
'fetchMarginAdjustmentHistory': False,
|
||
'fetchMarginMode': False,
|
||
'fetchMarginModes': False,
|
||
'fetchMarketLeverageTiers': False,
|
||
'fetchMarkets': True,
|
||
'fetchMarkOHLCV': False,
|
||
'fetchMarkPrices': False,
|
||
'fetchMyLiquidations': False,
|
||
'fetchMySettlementHistory': False,
|
||
'fetchMyTrades': True,
|
||
'fetchOHLCV': True,
|
||
'fetchOpenInterest': False,
|
||
'fetchOpenInterestHistory': False,
|
||
'fetchOpenInterests': False,
|
||
'fetchOpenOrders': True,
|
||
'fetchOption': False,
|
||
'fetchOptionChain': False,
|
||
'fetchOrderBook': True,
|
||
'fetchOrders': True,
|
||
'fetchPosition': False,
|
||
'fetchPositionHistory': False,
|
||
'fetchPositionMode': False,
|
||
'fetchPositions': False,
|
||
'fetchPositionsForSymbol': False,
|
||
'fetchPositionsHistory': False,
|
||
'fetchPositionsRisk': False,
|
||
'fetchPremiumIndexOHLCV': False,
|
||
'fetchSettlementHistory': False,
|
||
'fetchTicker': True,
|
||
'fetchTickers': True,
|
||
'fetchTrades': True,
|
||
'fetchVolatilityHistory': False,
|
||
'reduceMargin': False,
|
||
'repayCrossMargin': False,
|
||
'repayIsolatedMargin': False,
|
||
'repayMargin': False,
|
||
'setLeverage': False,
|
||
'setMargin': False,
|
||
'setMarginMode': False,
|
||
'setPositionMode': False,
|
||
'ws': False,
|
||
},
|
||
'timeframes': {
|
||
'1m': 1,
|
||
'15m': 15,
|
||
'30m': 30,
|
||
'1h': 60,
|
||
'4h': 240,
|
||
'1d': '1 d',
|
||
'1w': '1 w',
|
||
'1y': '1 y',
|
||
},
|
||
'urls': {
|
||
'logo': 'https://github.com/user-attachments/assets/10e0a238-9f60-4b06-9dda-edfc7602f1d6',
|
||
'api': {
|
||
'public': 'https://api.btcturk.com/api/v2',
|
||
'private': 'https://api.btcturk.com/api/v1',
|
||
'graph': 'https://graph-api.btcturk.com/v1',
|
||
},
|
||
'www': 'https://www.btcturk.com',
|
||
'doc': 'https://github.com/BTCTrader/broker-api-docs',
|
||
},
|
||
'api': {
|
||
'public': {
|
||
'get': {
|
||
'orderbook': 1,
|
||
'ticker': 0.1,
|
||
'trades': 1, # ?last=COUNT(max 50)
|
||
'ohlc': 1,
|
||
'server/exchangeinfo': 1,
|
||
},
|
||
},
|
||
'private': {
|
||
'get': {
|
||
'users/balances': 1,
|
||
'openOrders': 1,
|
||
'allOrders': 1,
|
||
'users/transactions/trade': 1,
|
||
},
|
||
'post': {
|
||
'users/transactions/crypto': 1,
|
||
'users/transactions/fiat': 1,
|
||
'order': 1,
|
||
'cancelOrder': 1,
|
||
},
|
||
'delete': {
|
||
'order': 1,
|
||
},
|
||
},
|
||
'graph': {
|
||
'get': {
|
||
'ohlcs': 1,
|
||
'klines/history': 1,
|
||
},
|
||
},
|
||
},
|
||
'features': {
|
||
'spot': {
|
||
'sandbox': False,
|
||
'createOrder': {
|
||
'marginMode': False,
|
||
'triggerPrice': True,
|
||
'triggerPriceType': None,
|
||
'triggerDirection': False,
|
||
'stopLossPrice': False,
|
||
'takeProfitPrice': False,
|
||
'attachedStopLossTakeProfit': None,
|
||
'timeInForce': {
|
||
'IOC': False,
|
||
'FOK': False,
|
||
'PO': False,
|
||
'GTD': False,
|
||
},
|
||
'hedged': False,
|
||
'leverage': False,
|
||
'marketBuyRequiresPrice': False,
|
||
'marketBuyByCost': False,
|
||
'selfTradePrevention': False,
|
||
'trailing': False,
|
||
'iceberg': False,
|
||
},
|
||
'createOrders': None,
|
||
'fetchMyTrades': {
|
||
'marginMode': False,
|
||
'limit': 100,
|
||
'daysBack': 100000,
|
||
'untilDays': 30,
|
||
'symbolRequired': True,
|
||
},
|
||
'fetchOrder': None,
|
||
'fetchOpenOrders': {
|
||
'marginMode': False,
|
||
'limit': None,
|
||
'trigger': False,
|
||
'trailing': False,
|
||
'symbolRequired': True,
|
||
},
|
||
'fetchOrders': {
|
||
'marginMode': False,
|
||
'limit': 1000,
|
||
'daysBack': 100000,
|
||
'untilDays': 30,
|
||
'trigger': False,
|
||
'trailing': False,
|
||
'symbolRequired': True,
|
||
},
|
||
'fetchClosedOrders': None,
|
||
'fetchOHLCV': {
|
||
'limit': None,
|
||
},
|
||
},
|
||
'swap': {
|
||
'linear': None,
|
||
'inverse': None,
|
||
},
|
||
'future': {
|
||
'linear': None,
|
||
'inverse': None,
|
||
},
|
||
},
|
||
'fees': {
|
||
'trading': {
|
||
'maker': self.parse_number('0.0005'),
|
||
'taker': self.parse_number('0.0009'),
|
||
},
|
||
},
|
||
'exceptions': {
|
||
'exact': {
|
||
'FAILED_ORDER_WITH_OPEN_ORDERS': InsufficientFunds,
|
||
'FAILED_LIMIT_ORDER': InvalidOrder,
|
||
'FAILED_MARKET_ORDER': InvalidOrder,
|
||
},
|
||
},
|
||
'precisionMode': TICK_SIZE,
|
||
})
|
||
|
||
async def fetch_markets(self, params={}) -> List[Market]:
|
||
"""
|
||
retrieves data on all markets for btcturk
|
||
|
||
https://docs.btcturk.com/public-endpoints/exchange-info
|
||
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict[]: an array of objects representing market data
|
||
"""
|
||
response = await self.publicGetServerExchangeinfo(params)
|
||
#
|
||
# {
|
||
# "data": {
|
||
# "timeZone": "UTC",
|
||
# "serverTime": "1618826678404",
|
||
# "symbols": [
|
||
# {
|
||
# "id": "1",
|
||
# "name": "BTCTRY",
|
||
# "nameNormalized": "BTC_TRY",
|
||
# "status": "TRADING",
|
||
# "numerator": "BTC",
|
||
# "denominator": "TRY",
|
||
# "numeratorScale": "8",
|
||
# "denominatorScale": "2",
|
||
# "hasFraction": False,
|
||
# "filters": [
|
||
# {
|
||
# "filterType": "PRICE_FILTER",
|
||
# "minPrice": "0.0000000000001",
|
||
# "maxPrice": "10000000",
|
||
# "tickSize": "10",
|
||
# "minExchangeValue": "99.92",
|
||
# "minAmount": null,
|
||
# "maxAmount": null
|
||
# }
|
||
# ],
|
||
# "orderMethods": [
|
||
# "MARKET",
|
||
# "LIMIT",
|
||
# "STOP_MARKET",
|
||
# "STOP_LIMIT"
|
||
# ],
|
||
# "displayFormat": "#,###",
|
||
# "commissionFromNumerator": False,
|
||
# "order": "1000",
|
||
# "priceRounding": False
|
||
# },
|
||
# ...
|
||
# },
|
||
# ],
|
||
# }
|
||
#
|
||
data = self.safe_dict(response, 'data', {})
|
||
markets = self.safe_list(data, 'symbols', [])
|
||
return self.parse_markets(markets)
|
||
|
||
def parse_market(self, entry) -> Market:
|
||
id = self.safe_string(entry, 'name')
|
||
baseId = self.safe_string(entry, 'numerator')
|
||
quoteId = self.safe_string(entry, 'denominator')
|
||
base = self.safe_currency_code(baseId)
|
||
quote = self.safe_currency_code(quoteId)
|
||
filters = self.safe_list(entry, 'filters', [])
|
||
minPrice = None
|
||
maxPrice = None
|
||
minAmount = None
|
||
maxAmount = None
|
||
minCost = None
|
||
for j in range(0, len(filters)):
|
||
filter = filters[j]
|
||
filterType = self.safe_string(filter, 'filterType')
|
||
if filterType == 'PRICE_FILTER':
|
||
minPrice = self.safe_number(filter, 'minPrice')
|
||
maxPrice = self.safe_number(filter, 'maxPrice')
|
||
minAmount = self.safe_number(filter, 'minAmount')
|
||
maxAmount = self.safe_number(filter, 'maxAmount')
|
||
minCost = self.safe_number(filter, 'minExchangeValue')
|
||
status = self.safe_string(entry, 'status')
|
||
return {
|
||
'id': id,
|
||
'symbol': base + '/' + quote,
|
||
'base': base,
|
||
'quote': quote,
|
||
'settle': None,
|
||
'baseId': baseId,
|
||
'quoteId': quoteId,
|
||
'settleId': None,
|
||
'type': 'spot',
|
||
'spot': True,
|
||
'margin': False,
|
||
'swap': False,
|
||
'future': False,
|
||
'option': False,
|
||
'active': (status == 'TRADING'),
|
||
'contract': False,
|
||
'linear': None,
|
||
'inverse': None,
|
||
'contractSize': None,
|
||
'expiry': None,
|
||
'expiryDatetime': None,
|
||
'strike': None,
|
||
'optionType': None,
|
||
'precision': {
|
||
'amount': self.parse_number(self.parse_precision(self.safe_string(entry, 'numeratorScale'))),
|
||
'price': self.parse_number(self.parse_precision(self.safe_string(entry, 'denominatorScale'))),
|
||
},
|
||
'limits': {
|
||
'leverage': {
|
||
'min': None,
|
||
'max': None,
|
||
},
|
||
'amount': {
|
||
'min': minAmount,
|
||
'max': maxAmount,
|
||
},
|
||
'price': {
|
||
'min': minPrice,
|
||
'max': maxPrice,
|
||
},
|
||
'cost': {
|
||
'min': minCost,
|
||
'max': None,
|
||
},
|
||
},
|
||
'created': None,
|
||
'info': entry,
|
||
}
|
||
|
||
def parse_balance(self, response) -> Balances:
|
||
data = self.safe_list(response, 'data', [])
|
||
result: dict = {
|
||
'info': response,
|
||
'timestamp': None,
|
||
'datetime': None,
|
||
}
|
||
for i in range(0, len(data)):
|
||
entry = data[i]
|
||
currencyId = self.safe_string(entry, 'asset')
|
||
code = self.safe_currency_code(currencyId)
|
||
account = self.account()
|
||
account['total'] = self.safe_string(entry, 'balance')
|
||
account['free'] = self.safe_string(entry, 'free')
|
||
account['used'] = self.safe_string(entry, 'locked')
|
||
result[code] = account
|
||
return self.safe_balance(result)
|
||
|
||
async def fetch_balance(self, params={}) -> Balances:
|
||
"""
|
||
query for balance and get the amount of funds available for trading or funds locked in orders
|
||
|
||
https://docs.btcturk.com/private-endpoints/account-balance
|
||
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: a `balance structure <https://docs.ccxt.com/#/?id=balance-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
response = await self.privateGetUsersBalances(params)
|
||
#
|
||
# {
|
||
# "data": [
|
||
# {
|
||
# "asset": "TRY",
|
||
# "assetname": "Türk Lirası",
|
||
# "balance": "0",
|
||
# "locked": "0",
|
||
# "free": "0",
|
||
# "orderFund": "0",
|
||
# "requestFund": "0",
|
||
# "precision": 2
|
||
# }
|
||
# ]
|
||
# }
|
||
#
|
||
return self.parse_balance(response)
|
||
|
||
async def fetch_order_book(self, symbol: str, limit: Int = None, params={}) -> OrderBook:
|
||
"""
|
||
fetches information on open orders with bid(buy) and ask(sell) prices, volumes and other data
|
||
|
||
https://docs.btcturk.com/public-endpoints/orderbook
|
||
|
||
:param str symbol: unified symbol of the market to fetch the order book for
|
||
:param int [limit]: the maximum amount of order book entries to return
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: A dictionary of `order book structures <https://docs.ccxt.com/#/?id=order-book-structure>` indexed by market symbols
|
||
"""
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
request: dict = {
|
||
'pairSymbol': market['id'],
|
||
}
|
||
response = await self.publicGetOrderbook(self.extend(request, params))
|
||
# {
|
||
# "data": {
|
||
# "timestamp": 1618827901241,
|
||
# "bids": [
|
||
# [
|
||
# "460263.00",
|
||
# "0.04244000"
|
||
# ]
|
||
# ]
|
||
# }
|
||
# }
|
||
data = self.safe_dict(response, 'data', {})
|
||
timestamp = self.safe_integer(data, 'timestamp')
|
||
return self.parse_order_book(data, market['symbol'], timestamp, 'bids', 'asks', 0, 1)
|
||
|
||
def parse_ticker(self, ticker: dict, market: Market = None) -> Ticker:
|
||
#
|
||
# {
|
||
# "pair": "BTCTRY",
|
||
# "pairNormalized": "BTC_TRY",
|
||
# "timestamp": 1618826361234,
|
||
# "last": 462485,
|
||
# "high": 473976,
|
||
# "low": 444201,
|
||
# "bid": 461928,
|
||
# "ask": 462485,
|
||
# "open": 456915,
|
||
# "volume": 917.41368645,
|
||
# "average": 462868.29574589,
|
||
# "daily": 5570,
|
||
# "dailyPercent": 1.22,
|
||
# "denominatorSymbol": "TRY",
|
||
# "numeratorSymbol": "BTC",
|
||
# "order": 1000
|
||
# }
|
||
#
|
||
marketId = self.safe_string(ticker, 'pair')
|
||
market = self.safe_market(marketId, market)
|
||
symbol = market['symbol']
|
||
timestamp = self.safe_integer(ticker, 'timestamp')
|
||
last = self.safe_string(ticker, 'last')
|
||
return self.safe_ticker({
|
||
'symbol': symbol,
|
||
'timestamp': timestamp,
|
||
'datetime': self.iso8601(timestamp),
|
||
'high': self.safe_string(ticker, 'high'),
|
||
'low': self.safe_string(ticker, 'low'),
|
||
'bid': self.safe_string(ticker, 'bid'),
|
||
'bidVolume': None,
|
||
'ask': self.safe_string(ticker, 'ask'),
|
||
'askVolume': None,
|
||
'vwap': None,
|
||
'open': self.safe_string(ticker, 'open'),
|
||
'close': last,
|
||
'last': last,
|
||
'previousClose': None,
|
||
'change': self.safe_string(ticker, 'daily'),
|
||
'percentage': self.safe_string(ticker, 'dailyPercent'),
|
||
'average': self.safe_string(ticker, 'average'),
|
||
'baseVolume': self.safe_string(ticker, 'volume'),
|
||
'quoteVolume': None,
|
||
'info': ticker,
|
||
}, market)
|
||
|
||
async def fetch_tickers(self, symbols: Strings = None, params={}) -> Tickers:
|
||
"""
|
||
fetches price tickers for multiple markets, statistical information calculated over the past 24 hours for each market
|
||
|
||
https://docs.btcturk.com/public-endpoints/ticker
|
||
|
||
:param str[]|None symbols: unified symbols of the markets to fetch the ticker for, all market tickers are returned if not assigned
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: a dictionary of `ticker structures <https://docs.ccxt.com/#/?id=ticker-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
response = await self.publicGetTicker(params)
|
||
tickers = self.safe_list(response, 'data')
|
||
return self.parse_tickers(tickers, symbols)
|
||
|
||
async def fetch_ticker(self, symbol: str, params={}) -> Ticker:
|
||
"""
|
||
fetches a price ticker, a statistical calculation with the information calculated over the past 24 hours for a specific market
|
||
|
||
https://docs.btcturk.com/public-endpoints/ticker
|
||
|
||
:param str symbol: unified symbol of the market to fetch the ticker for
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: a `ticker structure <https://docs.ccxt.com/#/?id=ticker-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
tickers = await self.fetch_tickers([symbol], params)
|
||
return self.safe_value(tickers, symbol)
|
||
|
||
def parse_trade(self, trade: dict, market: Market = None) -> Trade:
|
||
#
|
||
# fetchTrades
|
||
# {
|
||
# "pair": "BTCUSDT",
|
||
# "pairNormalized": "BTC_USDT",
|
||
# "numerator": "BTC",
|
||
# "denominator": "USDT",
|
||
# "date": "1618916879083",
|
||
# "tid": "637545136790672520",
|
||
# "price": "55774",
|
||
# "amount": "0.27917100",
|
||
# "side": "buy"
|
||
# }
|
||
#
|
||
# fetchMyTrades
|
||
# {
|
||
# "price": "56000",
|
||
# "numeratorSymbol": "BTC",
|
||
# "denominatorSymbol": "USDT",
|
||
# "orderType": "buy",
|
||
# "orderId": "2606935102",
|
||
# "id": "320874372",
|
||
# "timestamp": "1618916479593",
|
||
# "amount": "0.00020000",
|
||
# "fee": "0",
|
||
# "tax": "0"
|
||
# }
|
||
#
|
||
timestamp = self.safe_integer_2(trade, 'date', 'timestamp')
|
||
id = self.safe_string_2(trade, 'tid', 'id')
|
||
order = self.safe_string(trade, 'orderId')
|
||
priceString = self.safe_string(trade, 'price')
|
||
amountString = Precise.string_abs(self.safe_string(trade, 'amount'))
|
||
marketId = self.safe_string(trade, 'pair')
|
||
symbol = self.safe_symbol(marketId, market)
|
||
side = self.safe_string_2(trade, 'side', 'orderType')
|
||
fee = None
|
||
feeAmountString = self.safe_string(trade, 'fee')
|
||
if feeAmountString is not None:
|
||
feeCurrency = self.safe_string(trade, 'denominatorSymbol')
|
||
fee = {
|
||
'cost': Precise.string_abs(feeAmountString),
|
||
'currency': self.safe_currency_code(feeCurrency),
|
||
}
|
||
return self.safe_trade({
|
||
'info': trade,
|
||
'id': id,
|
||
'order': order,
|
||
'timestamp': timestamp,
|
||
'datetime': self.iso8601(timestamp),
|
||
'symbol': symbol,
|
||
'type': None,
|
||
'side': side,
|
||
'takerOrMaker': None,
|
||
'price': priceString,
|
||
'amount': amountString,
|
||
'cost': None,
|
||
'fee': fee,
|
||
}, market)
|
||
|
||
async def fetch_trades(self, symbol: str, since: Int = None, limit: Int = None, params={}) -> List[Trade]:
|
||
"""
|
||
get the list of most recent trades for a particular symbol
|
||
|
||
https://docs.btcturk.com/public-endpoints/trades
|
||
|
||
:param str symbol: unified symbol of the market to fetch trades for
|
||
:param int [since]: timestamp in ms of the earliest trade to fetch
|
||
:param int [limit]: the maximum amount of trades to fetch
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=public-trades>`
|
||
"""
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
# maxCount = 50
|
||
request: dict = {
|
||
'pairSymbol': market['id'],
|
||
}
|
||
if limit is not None:
|
||
request['last'] = limit
|
||
response = await self.publicGetTrades(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "data": [
|
||
# {
|
||
# "pair": "BTCTRY",
|
||
# "pairNormalized": "BTC_TRY",
|
||
# "numerator": "BTC",
|
||
# "denominator": "TRY",
|
||
# "date": 1618828421497,
|
||
# "tid": "637544252214980918",
|
||
# "price": "462585.00",
|
||
# "amount": "0.01618411",
|
||
# "side": "sell"
|
||
# }
|
||
# ]
|
||
# }
|
||
#
|
||
data = self.safe_list(response, 'data')
|
||
return self.parse_trades(data, market, since, limit)
|
||
|
||
def parse_ohlcv(self, ohlcv, market: Market = None) -> list:
|
||
#
|
||
# {
|
||
# "timestamp": 1661990400,
|
||
# "high": 368388.0,
|
||
# "open": 368388.0,
|
||
# "low": 368388.0,
|
||
# "close": 368388.0,
|
||
# "volume": 0.00035208,
|
||
# }
|
||
#
|
||
return [
|
||
self.safe_timestamp(ohlcv, 'timestamp'),
|
||
self.safe_number(ohlcv, 'open'),
|
||
self.safe_number(ohlcv, 'high'),
|
||
self.safe_number(ohlcv, 'low'),
|
||
self.safe_number(ohlcv, 'close'),
|
||
self.safe_number(ohlcv, 'volume'),
|
||
]
|
||
|
||
async def fetch_ohlcv(self, symbol: str, timeframe: str = '1h', since: Int = None, limit: Int = None, params={}) -> List[list]:
|
||
"""
|
||
fetches historical candlestick data containing the open, high, low, and close price, and the volume of a market
|
||
|
||
https://docs.btcturk.com/public-endpoints/get-kline-data
|
||
|
||
:param str symbol: unified symbol of the market to fetch OHLCV data for
|
||
:param str timeframe: the length of time each candle represents
|
||
:param int [since]: timestamp in ms of the earliest candle to fetch
|
||
:param int [limit]: the maximum amount of candles to fetch
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:param int [params.until]: timestamp in ms of the latest candle to fetch
|
||
:returns int[][]: A list of candles ordered, open, high, low, close, volume
|
||
"""
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
request: dict = {
|
||
'symbol': market['id'],
|
||
'resolution': self.safe_value(self.timeframes, timeframe, timeframe), # allows the user to pass custom timeframes if needed
|
||
}
|
||
until = self.safe_integer(params, 'until', self.milliseconds())
|
||
request['to'] = self.parse_to_int((until / 1000))
|
||
if since is not None:
|
||
request['from'] = self.parse_to_int(since / 1000)
|
||
elif limit is None: # since will also be None
|
||
limit = 100 # default value
|
||
if limit is not None:
|
||
limit = min(limit, 11000) # max 11000 candles diapason can be covered
|
||
if timeframe == '1y': # difficult with leap years
|
||
raise BadRequest(self.id + ' fetchOHLCV() does not accept a limit parameter when timeframe == "1y"')
|
||
seconds = self.parse_timeframe(timeframe)
|
||
limitSeconds = seconds * (limit - 1)
|
||
if since is not None:
|
||
to = self.parse_to_int(since / 1000) + limitSeconds
|
||
request['to'] = min(request['to'], to)
|
||
else:
|
||
request['from'] = self.parse_to_int(0 / 1000) - limitSeconds
|
||
response = await self.graphGetKlinesHistory(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "s": "ok",
|
||
# "t": [
|
||
# 1661990400,
|
||
# 1661990520,
|
||
# ...
|
||
# ],
|
||
# "h": [
|
||
# 368388.0,
|
||
# 369090.0,
|
||
# ...
|
||
# ],
|
||
# "o": [
|
||
# 368388.0,
|
||
# 368467.0,
|
||
# ...
|
||
# ],
|
||
# "l": [
|
||
# 368388.0,
|
||
# 368467.0,
|
||
# ...
|
||
# ],
|
||
# "c": [
|
||
# 368388.0,
|
||
# 369090.0,
|
||
# ...
|
||
# ],
|
||
# "v": [
|
||
# 0.00035208,
|
||
# 0.2972395,
|
||
# ...
|
||
# ]
|
||
# }
|
||
#
|
||
return self.parse_ohlcvs(response, market, timeframe, since, limit)
|
||
|
||
def parse_ohlcvs(self, ohlcvs, market=None, timeframe='1m', since: Int = None, limit: Int = None, tail: Bool = False):
|
||
results = []
|
||
timestamp = self.safe_list(ohlcvs, 't', [])
|
||
high = self.safe_list(ohlcvs, 'h', [])
|
||
open = self.safe_list(ohlcvs, 'o', [])
|
||
low = self.safe_list(ohlcvs, 'l', [])
|
||
close = self.safe_list(ohlcvs, 'c', [])
|
||
volume = self.safe_list(ohlcvs, 'v', [])
|
||
for i in range(0, len(timestamp)):
|
||
ohlcv: dict = {
|
||
'timestamp': self.safe_integer(timestamp, i),
|
||
'high': self.safe_number(high, i),
|
||
'open': self.safe_number(open, i),
|
||
'low': self.safe_number(low, i),
|
||
'close': self.safe_number(close, i),
|
||
'volume': self.safe_number(volume, i),
|
||
}
|
||
results.append(self.parse_ohlcv(ohlcv, market))
|
||
sorted = self.sort_by(results, 0)
|
||
return self.filter_by_since_limit(sorted, since, limit, 0, tail)
|
||
|
||
async def create_order(self, symbol: str, type: OrderType, side: OrderSide, amount: float, price: Num = None, params={}):
|
||
"""
|
||
create a trade order
|
||
|
||
https://docs.btcturk.com/private-endpoints/submit-order
|
||
|
||
:param str symbol: unified symbol of the market to create an order in
|
||
:param str type: 'market' or 'limit'
|
||
:param str side: 'buy' or 'sell'
|
||
:param float amount: how much of currency you want to trade in units of base currency
|
||
:param float [price]: the price at which the order is to be fulfilled, in units of the quote currency, ignored in market orders
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: an `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
request: dict = {
|
||
'orderType': side,
|
||
'orderMethod': type,
|
||
'pairSymbol': market['id'],
|
||
'quantity': self.amount_to_precision(symbol, amount),
|
||
}
|
||
if type != 'market':
|
||
request['price'] = self.price_to_precision(symbol, price)
|
||
if 'clientOrderId' in params:
|
||
request['newClientOrderId'] = params['clientOrderId']
|
||
elif not ('newClientOrderId' in params):
|
||
request['newClientOrderId'] = self.uuid()
|
||
response = await self.privatePostOrder(self.extend(request, params))
|
||
data = self.safe_dict(response, 'data')
|
||
return self.parse_order(data, market)
|
||
|
||
async def cancel_order(self, id: str, symbol: Str = None, params={}):
|
||
"""
|
||
cancels an open order
|
||
|
||
https://docs.btcturk.com/private-endpoints/cancel-order
|
||
|
||
:param str id: order id
|
||
:param str symbol: not used by btcturk cancelOrder()
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns dict: An `order structure <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
request: dict = {
|
||
'id': id,
|
||
}
|
||
response = await self.privateDeleteOrder(self.extend(request, params))
|
||
#
|
||
# {
|
||
# "success": True,
|
||
# "message": "SUCCESS",
|
||
# "code": 0
|
||
# }
|
||
#
|
||
return self.safe_order({
|
||
'info': response,
|
||
})
|
||
|
||
async def fetch_open_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
||
"""
|
||
fetch all unfilled currently open orders
|
||
|
||
https://docs.btcturk.com/private-endpoints/open-orders
|
||
|
||
:param str symbol: unified market symbol
|
||
:param int [since]: the earliest time in ms to fetch open orders for
|
||
:param int [limit]: the maximum number of open orders structures to retrieve
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
request: dict = {}
|
||
market = None
|
||
if symbol is not None:
|
||
market = self.market(symbol)
|
||
request['pairSymbol'] = market['id']
|
||
response = await self.privateGetOpenOrders(self.extend(request, params))
|
||
data = self.safe_dict(response, 'data', {})
|
||
bids = self.safe_list(data, 'bids', [])
|
||
asks = self.safe_list(data, 'asks', [])
|
||
return self.parse_orders(self.array_concat(bids, asks), market, since, limit)
|
||
|
||
async def fetch_orders(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}) -> List[Order]:
|
||
"""
|
||
fetches information on multiple orders made by the user
|
||
|
||
https://docs.btcturk.com/private-endpoints/all-orders
|
||
|
||
:param str symbol: unified market symbol of the market orders were made in
|
||
:param int [since]: the earliest time in ms to fetch orders for
|
||
:param int [limit]: the maximum number of order structures to retrieve
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns Order[]: a list of `order structures <https://docs.ccxt.com/#/?id=order-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
market = self.market(symbol)
|
||
request: dict = {
|
||
'pairSymbol': market['id'],
|
||
}
|
||
if limit is not None:
|
||
# default 100 max 1000
|
||
request['last'] = limit
|
||
if since is not None:
|
||
request['startTime'] = int(math.floor(since / 1000))
|
||
response = await self.privateGetAllOrders(self.extend(request, params))
|
||
# {
|
||
# "data": [
|
||
# {
|
||
# "id": "2606012912",
|
||
# "price": "55000",
|
||
# "amount": "0.0003",
|
||
# "quantity": "0.0003",
|
||
# "stopPrice": "0",
|
||
# "pairSymbol": "BTCUSDT",
|
||
# "pairSymbolNormalized": "BTC_USDT",
|
||
# "type": "buy",
|
||
# "method": "limit",
|
||
# "orderClientId": "2ed187bd-59a8-4875-a212-1b793963b85c",
|
||
# "time": "1618913189253",
|
||
# "updateTime": "1618913189253",
|
||
# "status": "Untouched",
|
||
# "leftAmount": "0.0003000000000000"
|
||
# }
|
||
# ]
|
||
# }
|
||
data = self.safe_list(response, 'data')
|
||
return self.parse_orders(data, market, since, limit)
|
||
|
||
def parse_order_status(self, status: Str):
|
||
statuses: dict = {
|
||
'Untouched': 'open',
|
||
'Partial': 'open',
|
||
'Canceled': 'canceled',
|
||
'Closed': 'closed',
|
||
}
|
||
return self.safe_string(statuses, status, status)
|
||
|
||
def parse_order(self, order: dict, market: Market = None) -> Order:
|
||
#
|
||
# fetchOrders / fetchOpenOrders
|
||
# {
|
||
# "id": 2605984008,
|
||
# "price": "55000",
|
||
# "amount": "0.00050000",
|
||
# "quantity": "0.00050000",
|
||
# "stopPrice": "0",
|
||
# "pairSymbol": "BTCUSDT",
|
||
# "pairSymbolNormalized": "BTC_USDT",
|
||
# "type": "buy",
|
||
# "method": "limit",
|
||
# "orderClientId": "f479bdb6-0965-4f03-95b5-daeb7aa5a3a5",
|
||
# "time": 0,
|
||
# "updateTime": 1618913083543,
|
||
# "status": "Untouched",
|
||
# "leftAmount": "0.00050000"
|
||
# }
|
||
#
|
||
# createOrder
|
||
# {
|
||
# "id": "2606935102",
|
||
# "quantity": "0.0002",
|
||
# "price": "56000",
|
||
# "stopPrice": null,
|
||
# "newOrderClientId": "98e5c491-7ed9-462b-9666-93553180fb28",
|
||
# "type": "buy",
|
||
# "method": "limit",
|
||
# "pairSymbol": "BTCUSDT",
|
||
# "pairSymbolNormalized": "BTC_USDT",
|
||
# "datetime": "1618916479523"
|
||
# }
|
||
#
|
||
id = self.safe_string(order, 'id')
|
||
price = self.safe_string(order, 'price')
|
||
amountString = self.safe_string_2(order, 'amount', 'quantity')
|
||
amount = Precise.string_abs(amountString)
|
||
remaining = self.safe_string(order, 'leftAmount')
|
||
marketId = self.safe_string(order, 'pairSymbol')
|
||
symbol = self.safe_symbol(marketId, market)
|
||
side = self.safe_string(order, 'type')
|
||
type = self.safe_string(order, 'method')
|
||
clientOrderId = self.safe_string(order, 'orderClientId')
|
||
timestamp = self.safe_integer_2(order, 'updateTime', 'datetime')
|
||
rawStatus = self.safe_string(order, 'status')
|
||
status = self.parse_order_status(rawStatus)
|
||
return self.safe_order({
|
||
'info': order,
|
||
'id': id,
|
||
'price': price,
|
||
'amount': amount,
|
||
'remaining': remaining,
|
||
'filled': None,
|
||
'cost': None,
|
||
'average': None,
|
||
'status': status,
|
||
'side': side,
|
||
'type': type,
|
||
'clientOrderId': clientOrderId,
|
||
'timestamp': timestamp,
|
||
'datetime': self.iso8601(timestamp),
|
||
'symbol': symbol,
|
||
'fee': None,
|
||
}, market)
|
||
|
||
async def fetch_my_trades(self, symbol: Str = None, since: Int = None, limit: Int = None, params={}):
|
||
"""
|
||
fetch all trades made by the user
|
||
|
||
https://docs.btcturk.com/private-endpoints/user-transactions
|
||
|
||
:param str symbol: unified market symbol
|
||
:param int [since]: the earliest time in ms to fetch trades for
|
||
:param int [limit]: the maximum number of trades structures to retrieve
|
||
:param dict [params]: extra parameters specific to the exchange API endpoint
|
||
:returns Trade[]: a list of `trade structures <https://docs.ccxt.com/#/?id=trade-structure>`
|
||
"""
|
||
await self.load_markets()
|
||
market = None
|
||
if symbol is not None:
|
||
market = self.market(symbol)
|
||
response = await self.privateGetUsersTransactionsTrade()
|
||
#
|
||
# {
|
||
# "data": [
|
||
# {
|
||
# "price": "56000",
|
||
# "numeratorSymbol": "BTC",
|
||
# "denominatorSymbol": "USDT",
|
||
# "orderType": "buy",
|
||
# "orderId": "2606935102",
|
||
# "id": "320874372",
|
||
# "timestamp": "1618916479593",
|
||
# "amount": "0.00020000",
|
||
# "fee": "0",
|
||
# "tax": "0"
|
||
# }
|
||
# ],
|
||
# "success": True,
|
||
# "message": "SUCCESS",
|
||
# "code": "0"
|
||
# }
|
||
#
|
||
data = self.safe_list(response, 'data')
|
||
return self.parse_trades(data, market, since, limit)
|
||
|
||
def nonce(self):
|
||
return self.milliseconds()
|
||
|
||
def sign(self, path, api='public', method='GET', params={}, headers=None, body=None):
|
||
if self.id == 'btctrader':
|
||
raise ExchangeError(self.id + ' is an abstract base API for BTCExchange, BTCTurk')
|
||
url = self.urls['api'][api] + '/' + path
|
||
if (method == 'GET') or (method == 'DELETE'):
|
||
if params:
|
||
url += '?' + self.urlencode(params)
|
||
else:
|
||
body = self.json(params)
|
||
if api == 'private':
|
||
self.check_required_credentials()
|
||
nonce = str(self.nonce())
|
||
secret = self.base64_to_binary(self.secret)
|
||
auth = self.apiKey + nonce
|
||
headers = {
|
||
'X-PCK': self.apiKey,
|
||
'X-Stamp': nonce,
|
||
'X-Signature': self.hmac(self.encode(auth), secret, hashlib.sha256, 'base64'),
|
||
'Content-Type': 'application/json',
|
||
}
|
||
return {'url': url, 'method': method, 'body': body, 'headers': headers}
|
||
|
||
def handle_errors(self, code: int, reason: str, url: str, method: str, headers: dict, body: str, response, requestHeaders, requestBody):
|
||
errorCode = self.safe_string(response, 'code', '0')
|
||
message = self.safe_string(response, 'message')
|
||
output = body if (message is None) else message
|
||
self.throw_exactly_matched_exception(self.exceptions['exact'], message, self.id + ' ' + output)
|
||
if (errorCode != '0') and (errorCode != 'SUCCESS'):
|
||
raise ExchangeError(self.id + ' ' + output)
|
||
return None
|