├── .gitignore ├── LICENSE.txt ├── MANIFEST.in ├── README.md ├── btcturk_api ├── __init__.py ├── client.py ├── constants.py ├── exceptions.py └── properties.py ├── docs ├── Makefile ├── make.bat └── source │ ├── account_endpoints.rst │ ├── btcturk_api.rst │ ├── common_errors.rst │ ├── conf.py │ ├── exceptions.rst │ ├── index.rst │ ├── modules.rst │ ├── public_endpoints.rst │ ├── quickstart.rst │ └── trade_operations.rst ├── release_notes.md ├── requirements.txt └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | # Byte-compiled / optimized / DLL files 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | pip-wheel-metadata/ 25 | share/python-wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | MANIFEST 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .nox/ 45 | .coverage 46 | .coverage.* 47 | .cache 48 | nosetests.xml 49 | coverage.xml 50 | *.cover 51 | *.py,cover 52 | .hypothesis/ 53 | .pytest_cache/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | db.sqlite3-journal 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 98 | __pypackages__/ 99 | 100 | # Celery stuff 101 | celerybeat-schedule 102 | celerybeat.pid 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | 134 | # pytype static type analyzer 135 | .pytype/ 136 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) Ömer Miraç Baydemir 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outlier-1/btcturkapi-python/54a82711467e73fa328e66a9658856068ebb2f71/MANIFEST.in -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BTCTurk API - Python Wrapper 2 | 3 | btcturkapi-python is a wrapper library built around Btcturk's REST api implementation. 4 | 5 | It provides more abstract version of REST Client for traders using Btcturk as a cryptocurrency platform. 6 | 7 | ## Documentation 8 | 9 | Check out the [documentation](https://btcturkapi-python.readthedocs.io/en/latest/) for learning how to use this library and further reference 10 | 11 | 12 | ## Table of Contents 13 | 14 | * [Features](#features) 15 | * [Quickstart](#Quickstart) 16 | * [Requirements](#Requirements) 17 | * [Installation](#Installation) 18 | * [Usage](#Usage) 19 | * [License](#License) 20 | * [Contact](#contact) 21 | * [Donation](#Donation) 22 | 23 | 24 | 25 | 26 | ## Features 27 | 28 | * Monitor cryptocurrency prices. 29 | * Place buy, sell orders with stop, limit, or market methods. 30 | * List and cancel open orders. 31 | * Get crypto/fiat transaction history. 32 | 33 | 34 | 35 | ## Quickstart 36 | 37 | In order to use this library for your projects, you can checkout installation and usage sections 38 | 39 | ### Requirements 40 | 41 | * BTCTurk API Credentials (For Authentication Necessary Operations) 42 | ``` 43 | https://www.btcturk.com/ApiAccess 44 | ``` 45 | You can go to link above and create your api credentials. 46 | 47 | 48 | ### Installation 49 | 50 | You can install this library via pip. 51 | 52 | It is the best practice using a virtual environment for your project, keep that in mind. 53 | 54 | Run this command through terminal: 55 | 56 | ```sh 57 | pip install btcturk-api 58 | ``` 59 | Since the project's current version doesn't have support for websockets and any advanced features, dependencies are simple and you should not encounter any installation error. 60 | 61 | 62 | ## Usage 63 | 64 | After installing the library, just import the client and you are good to go. You can use any feature btcturk api provides without dealing with preparing requests, handling responses etc. 65 | 66 | ```py 67 | from btcturk_api.client import Client 68 | ``` 69 | You can use public endpoints without providing an api key/secret pair. 70 | 71 | ```py 72 | >>> my_client = Client() 73 | >>> my_client.get_server_time() 74 | {'serverTime': 1613292018131, 'serverTime2': '2021-02-14T08:40:18.1308832+00:00'} 75 | ``` 76 | If you have an api key/secret pair, you can use account endpoints and trading operations 77 | 78 | ```py 79 | >>> my_client = Client(api_key='', api_secret='') 80 | >>> my_client.get_account_balance() 81 | [ 82 | { 83 | 'asset': 'TRY', 84 | 'assetname': 'Türk Lirası', 85 | 'balance': '100.00', 86 | 'locked': '0', 87 | 'free': '100.00', 88 | 'orderFund': '0', 89 | 'requestFund': '0', 90 | 'precision': 2 91 | }, 92 | {...} 93 | ] 94 | ``` 95 | 96 | ## License 97 | 98 | You can use this library in your personal projects free of charge. Detailed information is in LICENSE.txt file. 99 | 100 | 101 | ## Contact 102 | 103 | Miraç Baydemir - omermirac59@gmail.com 104 | 105 | Project Link: [https://github.com/outlier-1/btcturkapi-python/](https://github.com/outlier-1/btcturkapi-python/) 106 | 107 | 108 | ## Donation 109 | 110 | Bitcoin Address - '34FSjtdwTSB21uVDcptgJ8kPHHimMSCGxq' 111 | 112 | -------------------------------------------------------------------------------- /btcturk_api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/outlier-1/btcturkapi-python/54a82711467e73fa328e66a9658856068ebb2f71/btcturk_api/__init__.py -------------------------------------------------------------------------------- /btcturk_api/client.py: -------------------------------------------------------------------------------- 1 | from btcturk_api.properties import authentication_required 2 | from btcturk_api.exceptions import ( 3 | BadRequestError, 4 | InternalServerError, 5 | BTCTurkAuthenticationError, 6 | BadRespondError, 7 | ) 8 | from btcturk_api.constants import ( 9 | CRYPTO_SYMBOLS, 10 | CURRENCY_SYMBOLS, 11 | DEPOSIT_OR_WITHDRAWAL, 12 | TRADE_TYPES, 13 | ) 14 | 15 | from decimal import Decimal, ROUND_DOWN, InvalidOperation 16 | 17 | import base64 18 | import hashlib 19 | import hmac 20 | import json 21 | 22 | import requests 23 | 24 | import time 25 | import datetime as dt 26 | 27 | import uuid 28 | 29 | 30 | class Client: 31 | """ API Client Class 32 | 33 | Methods 34 | ------- 35 | get_exchange_info: 36 | Method for getting exchange info of any given pair 37 | 38 | get_server_time: 39 | Gets Current Server Time 40 | 41 | tick: 42 | Gets price related information of any given pair 43 | 44 | get_ohlc_data: 45 | Gets daily OHLC data for given pair 46 | 47 | get_order_book: 48 | Gets the order book of given pair 49 | 50 | get_trades: 51 | Gets a list of Trades for given pair 52 | 53 | get_account_balance: 54 | Gets the list of balances which user have 55 | 56 | get_trade_history: 57 | Gets the history of user's trades. 58 | 59 | get_crypto_history: 60 | Gets the history of user's crypto transactions. 61 | 62 | get_fiat_history: 63 | Gets the history of user's fiat transactions. 64 | 65 | get_open_orders: 66 | Get's list of user's open orders for given pair 67 | 68 | get_all_orders: 69 | Get's users all orders for given pair 70 | 71 | cancel_order: 72 | Deletes The Order 73 | 74 | submit_market_order: 75 | Submits an order in type of 'market order' 76 | 77 | submit_limit_order: 78 | Submits an order in type of 'limit order' 79 | 80 | submit_stop_order: 81 | Submits an order in type of 'stop order' 82 | """ 83 | 84 | API_BASE = "https://api.btcturk.com" 85 | API_ENDPOINT_AUTH = "/api/v1/" 86 | API_ENDPOINT_NON_AUTH = "/api/v2/" 87 | API_ENDPOINT_TRANSACTIONS = "/api/v1/users/transactions/" 88 | 89 | def __init__(self, api_key: str = None, api_secret: str = None): 90 | """ Creates a Client Object with given API Keys 91 | 92 | If user specifies both api_key and secret_key, constructor will try to authenticate the user 93 | by updating session headers and sending a request to btcturk api with given credentials information. 94 | 95 | Parameters 96 | ---------- 97 | api_key 98 | 99 | api_secret 100 | 101 | """ 102 | self.api_key = api_key 103 | self.api_secret = api_secret 104 | self.session = self._init_session() 105 | self.scale_limits = self._get_scale_limits() 106 | 107 | self.authenticated = False 108 | 109 | if api_key and api_secret: 110 | self._authenticate() 111 | 112 | def _format_unit(self, unit, scale): 113 | """ Rounds down the unit to given decimal points 114 | 115 | Parameters 116 | ---------- 117 | unit : float, mandatory 118 | It might be a price or quantity 119 | 120 | scale: int, mandatory 121 | Specifies the precision of decimal points 122 | 123 | Returns 124 | ------- 125 | Decimal 126 | Decimal object of formatted unit 127 | 128 | Raises 129 | ------ 130 | ValueError 131 | If user doesn't provide a valid float input 132 | 133 | """ 134 | try: 135 | scale_in_decimal_form = Decimal(str(1 / (10 ** scale))) if scale != 0 else Decimal(str(0)) 136 | formatted_unit = Decimal(str(unit)).quantize(scale_in_decimal_form, rounding=ROUND_DOWN) 137 | return formatted_unit 138 | except InvalidOperation as e: 139 | raise ValueError(f"Error occurred while trying to format your input '{unit}'. " 140 | f"Make sure you're entering a valid float number") 141 | 142 | def _get_scale_limits(self): 143 | """ Gets Currency Scales For Price and Amount 144 | 145 | Returns 146 | ------- 147 | dict 148 | Each object in this dictionary has this format:: 149 | 150 | { 151 | 'exchangeName': { 152 | 'price_scale': , 153 | 'amount_scale': , 154 | 'has_fraction': True/False 155 | }, 156 | 'exchangeName2': {...} 157 | } 158 | 159 | Raises 160 | ------ 161 | BadRespondError 162 | If response doesn't have proper dictionary keys 163 | 164 | """ 165 | 166 | request_url = self._create_public_endpoint_url("server/exchangeinfo") 167 | response = self.session.get(url=request_url) 168 | self._handle_response(response) 169 | 170 | try: 171 | scale_limits = {} 172 | for scale_info in response.json()["data"]["symbols"]: 173 | name = scale_info["name"] 174 | name_normalized = scale_info["nameNormalized"] 175 | price_scale = scale_info["denominatorScale"] 176 | amount_scale = scale_info["numeratorScale"] 177 | has_fraction = scale_info["hasFraction"] 178 | 179 | scale_limits[name] = { 180 | "price_scale": price_scale, 181 | "amount_scale": amount_scale, 182 | "has_fraction": has_fraction, 183 | } 184 | scale_limits[name_normalized] = scale_limits[name] 185 | 186 | return scale_limits 187 | except KeyError: 188 | raise BadRespondError( 189 | "Server didn't respond properly when requesting scale limits." 190 | ) 191 | 192 | @staticmethod 193 | def _init_session(): 194 | """ Initializes a session object with necessary headers 195 | 196 | Returns 197 | ------- 198 | requests.Session 199 | Session instance with some headers 200 | """ 201 | 202 | session = requests.session() 203 | headers = {"Content-Type": "application/json"} 204 | session.headers.update(headers) 205 | return session 206 | 207 | def _create_public_endpoint_url(self, endpoint): 208 | """ Constructs Public Endpoint Url 209 | 210 | Parameters 211 | ---------- 212 | endpoint : str, optional 213 | 214 | Returns 215 | ------- 216 | str 217 | url with format https://api.btcturk.com/api/v2/ 218 | """ 219 | return f"{self.API_BASE}{self.API_ENDPOINT_NON_AUTH}{endpoint}" 220 | 221 | def _create_auth_endpoint_url(self, endpoint): 222 | """ Constructs Auth Required Endpoint Url 223 | 224 | Parameters 225 | ---------- 226 | endpoint : optional 227 | 228 | Returns 229 | ------- 230 | str 231 | url with format https://api.btcturk.com/api/v1/ 232 | """ 233 | return f"{self.API_BASE}{self.API_ENDPOINT_AUTH}{endpoint}" 234 | 235 | def _create_signature(self): 236 | """ Creates HMAC-SHA256 encoded message 237 | The HMAC-SHA256 code generated using a private key that contains a timestamp as nonce and api_key 238 | 239 | Returns 240 | ------- 241 | bytes 242 | HMAC-SHA256 code 243 | """ 244 | api_secret = base64.b64decode(self.api_secret) 245 | stamp = str(int(time.time()) * 1000) 246 | data = "{}{}".format(self.api_key, stamp).encode("utf-8") 247 | signature = hmac.new(api_secret, data, hashlib.sha256).digest() 248 | signature = base64.b64encode(signature) 249 | return signature 250 | 251 | def _update_session_headers(self, **kwargs): 252 | """ Updates Client's session's headers with correct signature 253 | 254 | This is important because before each call to authentication required endpoints, 255 | HMAC-SHA256 message, which is time dependent, should be in headers for authorization. 256 | 257 | Parameters 258 | ---------- 259 | kwargs : kwargs 260 | any key, value pair that will be added to header 261 | 262 | """ 263 | signature = self._create_signature() 264 | headers = { 265 | "X-Stamp": str(int(time.time()) * 1000), 266 | "X-Signature": str(signature.decode("utf-8")), 267 | } 268 | 269 | for key, value in kwargs: 270 | headers[key] = value 271 | 272 | self.session.headers.update(headers) 273 | 274 | # Signature bug: report to btcturk-trader github page 275 | def _authenticate(self): 276 | """ Authenticates the Client 277 | 278 | Authenticates the clients by using api_key and api_sec attributes 279 | We need to provide 3 parameters for authentication: 280 | 281 | "X-PCK": API Public Key 282 | "X-Stamp": Nonce 283 | "X-Signature": Signature 284 | 285 | Nonce is current timestamp in milliseconds 286 | 287 | Signature is a HMAC-SHA256 encoded message. The HMAC-SHA256 code must be generated using a private key 288 | that contains a timestamp as nonce and your API key 289 | 290 | If authentication succeed, updates the session's header. raises BTCTurkAuthenticationError otherwise 291 | 292 | Raises 293 | ------ 294 | BTCTurkAuthenticationError 295 | Authentication Error with Response Message 296 | """ 297 | 298 | url = self._create_auth_endpoint_url("users/balances") 299 | signature = self._create_signature() 300 | headers = { 301 | "X-PCK": self.api_key, 302 | "X-Stamp": str(int(time.time()) * 1000), 303 | "X-Signature": str(signature.decode("utf-8")), 304 | } 305 | response = requests.get(url, headers=headers) 306 | 307 | if response.status_code == 200: 308 | # Authentication successful, update session header 309 | self.authenticated = True 310 | self.session.headers.update({"X-PCK": self.api_key}) 311 | else: 312 | raise BTCTurkAuthenticationError(response) 313 | 314 | @staticmethod 315 | def _handle_response(response): 316 | """ Handles Incoming Responses 317 | 318 | Looks for bad responses and raises proper exceptions 319 | 320 | Parameters 321 | ---------- 322 | response : requests.Response, mandatory 323 | 324 | Raises 325 | ---------- 326 | BadRequestError 327 | If response code has 4xx format 328 | 329 | InternalServerError 330 | If response code has 5xx format 331 | """ 332 | 333 | status = response.status_code 334 | if status // 100 == 4: 335 | raise BadRequestError(response) 336 | 337 | if status // 100 == 5: 338 | raise InternalServerError(response) 339 | 340 | def _get(self, url, params=None): 341 | """ Wrapper for HTTP Get Method 342 | 343 | Before returning any object, it gives the response to handler, handler checks for any client or server 344 | based errors have occurred or not. If didn't occur, returns the data section of response.json() 345 | 346 | Parameters 347 | ---------- 348 | url : str, mandatory 349 | Destination URL 350 | 351 | params : dict, optional 352 | request parameters 353 | 354 | Returns 355 | ------- 356 | dict 357 | Response's data section 358 | """ 359 | 360 | response = self.session.get(url=url, params=params) 361 | self._handle_response(response) 362 | return response.json()["data"] 363 | 364 | def _post(self, url, params=None): 365 | """ Wrapper for HTTP Post Method 366 | 367 | Before returning any object, it gives the response to handler, handler checks for any client or server 368 | based errors have occurred or not. If didn't occur, returns the data section of response.json() 369 | 370 | Parameters 371 | ---------- 372 | url : str, mandatory 373 | Destination URL 374 | 375 | params : dict, optional 376 | request parameters 377 | 378 | Returns 379 | ------- 380 | dict 381 | Response's data section 382 | """ 383 | 384 | response = self.session.post(url=url, data=json.dumps(params)) 385 | self._handle_response(response) 386 | 387 | return response.json()["data"] 388 | 389 | def _delete(self, url, params=None): 390 | """ Wrapper for HTTP Delete Method 391 | 392 | Parameters 393 | ---------- 394 | url : mandatory 395 | Destination URL 396 | 397 | params : optional 398 | request parameters 399 | 400 | Returns 401 | ------- 402 | bool 403 | Success value if there is no exception raised by handler 404 | """ 405 | response = self.session.delete(url=url, params=params, data=json.dumps(params)) 406 | self._handle_response(response) 407 | 408 | return response.json()["success"] 409 | 410 | # ############# PUBLIC ENDPOINT IMPLEMENTATIONS ############# # 411 | 412 | def get_exchange_info(self, symbol_list=None): 413 | """ Method for getting exchange info of any given pair 414 | 415 | Parameters 416 | ---------- 417 | symbol_list : list, optional 418 | In format of ['BTCUSDT', 'XRPUSDT', 'ETHTRY' ...] 419 | 420 | Returns 421 | ------- 422 | list 423 | If symbol_list is None, list of data dictionaries of all pairs. 424 | Otherwise, that list filtered down to given symbol list 425 | 426 | Example of Response Format 427 | 428 | .. code-block:: python 429 | 430 | [ 431 | { 432 | 'id': '', 433 | 'name': '', 434 | 'nameNormalized': '', 435 | 'status': '', 436 | 'numerator': '', 437 | 'denominator': '', 438 | 'numeratorScale': '', 439 | 'denominatorScale': '', 440 | 'hasFraction': '', 441 | 'filters': '' 442 | 'orderMethods': ['MARKET', 'LIMIT', 'STOP_MARKET', 'STOP_LIMIT'], # IMPORTANT 443 | 'displayFormat': '#,###', 444 | 'commissionFromNumerator': False, 'order': 999, 445 | 'priceRounding': False 446 | }, 447 | ... 448 | ] 449 | """ 450 | 451 | request_url = self._create_public_endpoint_url("server/exchangeinfo") 452 | exchange_list = self._get(request_url)["symbols"] 453 | 454 | if not symbol_list: 455 | return exchange_list 456 | 457 | filtered_list = list( 458 | filter(lambda symbol: symbol["name"] in symbol_list, exchange_list) 459 | ) 460 | return filtered_list 461 | 462 | def get_server_time(self): 463 | """ Gets Current Server Time 464 | 465 | Returns 466 | ------- 467 | dictionary 468 | Example of Response Format 469 | 470 | .. code-block:: python 471 | 472 | { 473 | 'serverTime': '', 474 | 'serverTime2': '', 475 | } 476 | """ 477 | 478 | request_url = self._create_public_endpoint_url("server/time") 479 | response = self.session.get(url=request_url) 480 | self._handle_response(response) 481 | response_as_json = response.json() 482 | 483 | return response_as_json 484 | 485 | def tick(self, pair=None, **kwargs): 486 | """ Gets price related information of any given pair 487 | 488 | If you specify kwargs, the other parameters will be **overridden.** 489 | Only keyword arguments you specified will be used to construct a query. 490 | Therefore, it is your choice to use kwargs. 491 | 492 | But i strongly discourage you to use that for avoiding any invalid requests 493 | 494 | Parameters 495 | ---------- 496 | pair : str, optional 497 | pair symbol like 'BTC_TRY', 'ETH_BTC', ... 498 | 499 | kwargs 500 | 501 | Returns 502 | ------- 503 | list 504 | If pair is set, a list of data dictionary with given pair, (length=1) 505 | Otherwise, a list of data dictionaries of all pairs. 506 | 507 | Example of Response Format 508 | 509 | .. code-block:: python 510 | 511 | [ 512 | { 513 | 'pairSymbol': '', 514 | 'pairSymbolNormalized': '', 515 | 'timestamp': '' 516 | 'last': '', 517 | 'high': '', 518 | 'low': '', 519 | 'bid': '', 520 | 'ask': '', 521 | 'open': '', 522 | 'volume': '', 523 | 'average': '', 524 | 'daily': '', 525 | 'dailyPercent': '', 526 | 'denominatorSymbol': '', 527 | 'numeratorSymbol': '', 528 | }, 529 | ... 530 | ] 531 | """ 532 | 533 | request_url = self._create_public_endpoint_url("ticker") 534 | params = kwargs if kwargs else {} 535 | 536 | if pair: 537 | return self._get(request_url, {"pairSymbol": pair}) 538 | return self._get(request_url, params) 539 | 540 | def get_ohlc_data(self, pair=None, last=10, **kwargs): 541 | """ Gets daily OHLC data for given pair 542 | 543 | If you specify kwargs, the other parameters will be **overridden.** 544 | Only keyword arguments you specified will be used to construct a query. 545 | Therefore, it is your choice to use kwargs. 546 | 547 | But i strongly discourage you to use that for avoiding any invalid requests 548 | 549 | Parameters 550 | ---------- 551 | pair : str, optional 552 | pair symbol like 'BTC_TRY', 'ETH_BTC', ... 553 | 554 | last : int, optional 555 | number of days 556 | 557 | kwargs 558 | 559 | Returns 560 | ------- 561 | list 562 | a list of data dictionary for given pair 563 | 564 | Example of Response Format 565 | 566 | .. code-block:: python 567 | 568 | [ 569 | { 570 | 'pairSymbol': '', 571 | 'pairSymbolNormalized': '', 572 | 'time': '' 573 | 'open': '', 574 | 'high': '', 575 | 'low': '', 576 | 'close': '', 577 | 'volume': '', 578 | 'average': '', 579 | 'dailyChangeAmount': '', 580 | 'dailyChangePercentage': '', 581 | }, 582 | ... 583 | ] 584 | """ 585 | 586 | request_url = self._create_public_endpoint_url("ohlc") 587 | params = kwargs if kwargs else {} 588 | 589 | if pair: 590 | return self._get(request_url, {"pairSymbol": pair, "last": last}) 591 | return self._get(request_url, params) 592 | 593 | def get_order_book(self, pair=None, limit=100, **kwargs): 594 | """ Gets the order book of given pair 595 | 596 | If you specify kwargs, the other parameters will be **overridden**. 597 | Only keyword arguments you specified will be used to construct a query. 598 | Therefore, it is your choice to use kwargs. 599 | 600 | But i strongly discourage you to use that for avoiding any invalid requests 601 | 602 | Parameters 603 | ---------- 604 | pair : str, mandatory 605 | pair symbol like 'BTC_TRY', 'ETH_BTC', ... 606 | 607 | limit : int, optional 608 | default 100 max 1000 609 | 610 | kwargs 611 | 612 | Returns 613 | ------- 614 | dict 615 | data dictionary 616 | 617 | Example of Response Format 618 | 619 | .. code-block:: python 620 | 621 | [ 622 | { 623 | 'timestamp': '', 624 | 'bids': '', 625 | 'asks': '', 626 | }, 627 | ... 628 | ] 629 | """ 630 | 631 | request_url = self._create_public_endpoint_url("orderbook") 632 | params = kwargs if kwargs else {"pairSymbol": pair, "limit": limit} 633 | 634 | return self._get(request_url, params) 635 | 636 | def get_trades(self, pair=None, last=50, **kwargs): 637 | """ Gets a list of Trades for given pair 638 | 639 | If you specify kwargs, the other parameters will be **overridden.** 640 | Only keyword arguments you specified will be used to construct a query. 641 | Therefore, it is your choice to use kwargs. 642 | 643 | But i strongly discourage you to use that for avoiding any invalid requests 644 | 645 | Parameters 646 | ---------- 647 | pair : str, mandatory 648 | pair symbol like 'BTC_TRY', 'ETH_BTC'.. 649 | 650 | last : int, optional 651 | default 50 max 1000 652 | 653 | Returns 654 | ------- 655 | dict 656 | Data dictionary 657 | 658 | Example of Response Format 659 | 660 | .. code-block:: python 661 | 662 | { 663 | 'pair': '', 664 | 'pairNormalized': '', 665 | 'numerator': '', 666 | 'denominator': '', 667 | 'date': '', 668 | 'tid': '', 669 | 'price': '', 670 | 'amount': '', 671 | }, 672 | """ 673 | 674 | request_url = self._create_public_endpoint_url("trades") 675 | params = kwargs if kwargs else {"pairSymbol": pair, "last": last} 676 | 677 | return self._get(request_url, params=params) 678 | 679 | # AUTHENTICATION REQUIRED GET ENDPOINT IMPLEMENTATIONS 680 | 681 | @authentication_required 682 | def get_account_balance(self, assets=None): 683 | """ Gets the list of balances which user have 684 | 685 | Parameters 686 | ---------- 687 | assets: optional 688 | List of assets like ['BTC', 'TRY', ...] 689 | 690 | Returns 691 | ------- 692 | list 693 | Example of Response Format 694 | 695 | .. code-block:: python 696 | 697 | [ 698 | { 699 | 'asset': 'EUR', 700 | 'assetname': 'Euro', 701 | 'balance': '0', 702 | 'locked': '0', 703 | 'free': '0', 704 | 'orderFund': '0', 705 | 'requestFund': '0', 706 | 'precision': 2 707 | }, 708 | ... 709 | ] 710 | """ 711 | 712 | url = self._create_auth_endpoint_url("users/balances") 713 | self._update_session_headers() 714 | balance_list = self._get(url) 715 | 716 | if not assets: 717 | return balance_list 718 | 719 | assets = [asset.upper() for asset in assets] 720 | 721 | filtered_balance_list = list( 722 | filter(lambda bl: bl["asset"].upper() in assets, balance_list) 723 | ) 724 | 725 | return filtered_balance_list 726 | 727 | @authentication_required 728 | def get_trade_history( 729 | self, trade_type=None, symbol=None, start_date=None, end_date=None, **kwargs 730 | ): 731 | """ Gets the history of user's trades. 732 | 733 | If trade_type not specified, both 'buy' and 'sell' types will be used 734 | 735 | If symbol not specified, all crypto symbols will be used 736 | 737 | If start_date not specified, it will get trades for last 30 days. 738 | 739 | If you specify kwargs, the other parameters will be **overridden.** 740 | Only keyword arguments you specified will be used to construct a query. 741 | Therefore, it is your choice to use kwargs. 742 | 743 | But i strongly discourage you to use that for avoiding any invalid requests 744 | 745 | Parameters 746 | ---------- 747 | trade_type : list, optional 748 | ["buy", "sell"], ["buy"] or ["sell"] 749 | 750 | symbol : list -> str, optional 751 | ["btc", "try", ...etc.], 752 | 753 | start_date : timestamp, optional 754 | 755 | end_date : timestamp, optional 756 | 757 | kwargs 758 | 759 | Returns 760 | ------- 761 | list 762 | List of trade data dictionaries, 763 | 764 | Example of Response Format 765 | 766 | .. code-block:: python 767 | 768 | [ 769 | { 770 | 'price': '', 771 | 'numeratorSymbol': '', 772 | 'denominatorSymbol': '', 773 | 'orderType': '', 774 | 'id': '', 775 | 'timestamp': '', 776 | 'amount': '', 777 | 'fee': '', 778 | 'tax': '' 779 | }, 780 | ... 781 | ] 782 | """ 783 | 784 | if not start_date: 785 | last_30_days_timestamp = dt.datetime.timestamp( 786 | dt.datetime.today() - dt.timedelta(days=30) 787 | ) 788 | start_date = int(last_30_days_timestamp * 1000) 789 | 790 | if not end_date: 791 | end_date = int(time.time() * 1000) 792 | 793 | if not symbol: 794 | symbol = CRYPTO_SYMBOLS 795 | 796 | if not trade_type: 797 | trade_type = TRADE_TYPES 798 | 799 | request_url = self.API_BASE + self.API_ENDPOINT_TRANSACTIONS + "trade" 800 | params = ( 801 | kwargs 802 | if kwargs 803 | else { 804 | "type": trade_type, 805 | "symbol": symbol, 806 | "startDate": start_date, 807 | "endDate": end_date, 808 | } 809 | ) 810 | 811 | self._update_session_headers() 812 | history = self._get(request_url, params) 813 | return history 814 | 815 | @authentication_required 816 | def get_crypto_history( 817 | self, 818 | symbol=None, 819 | transaction_type=None, 820 | start_date=None, 821 | end_date=None, 822 | **kwargs, 823 | ): 824 | """ Gets the history of user's crypto transactions. 825 | 826 | If symbol not specified, all crypto symbols will be used 827 | 828 | If transaction_type not specified, both 'withdrawal' and 'deposit' types will be used 829 | 830 | If start_date not specified, it will get trades for last 30 days. 831 | 832 | If you specify kwargs, the other parameters will be **overridden**. Only keyword arguments you specified 833 | will be used to construct a query. Therefore, it is your choice to use kwargs. 834 | 835 | But i strongly discourage you to use that for avoiding any invalid requests 836 | 837 | Parameters 838 | ---------- 839 | symbol : list, optional 840 | ["btc", "try", ...etc.] 841 | 842 | transaction_type : list , optional 843 | ["deposit", "withdrawal"], ["deposit"] or ["withdrawal"] 844 | 845 | start_date : timestamp, optional 846 | 847 | end_date : timestamp, optional 848 | 849 | kwargs 850 | 851 | Returns 852 | ------- 853 | list 854 | List of trade data dictionaries, 855 | 856 | Example of Response Format 857 | 858 | .. code-block:: python 859 | 860 | [ 861 | { 862 | 'balanceType': '', 863 | 'currencySymbol': '', 864 | 'id': '', 865 | 'timestamp': '', 866 | 'funds': '', 867 | 'orderFund': '', 868 | 'fee': '', 869 | 'tax': 870 | }, 871 | ... 872 | ] 873 | """ 874 | 875 | if not start_date: 876 | last_30_days_timestamp = dt.datetime.timestamp( 877 | dt.datetime.today() - dt.timedelta(days=30) 878 | ) 879 | start_date = int(last_30_days_timestamp * 1000) 880 | 881 | if not end_date: 882 | end_date = int(time.time() * 1000) 883 | 884 | if not symbol: 885 | symbol = CRYPTO_SYMBOLS 886 | 887 | if not transaction_type: 888 | transaction_type = DEPOSIT_OR_WITHDRAWAL 889 | 890 | request_url = self.API_BASE + self.API_ENDPOINT_TRANSACTIONS + "crypto" 891 | params = ( 892 | kwargs 893 | if kwargs 894 | else { 895 | "type": transaction_type, 896 | "symbol": symbol, 897 | "startDate": start_date, 898 | "endDate": end_date, 899 | } 900 | ) 901 | 902 | self._update_session_headers() 903 | history = self._get(request_url, params) 904 | return history 905 | 906 | @authentication_required 907 | def get_fiat_history( 908 | self, 909 | balance_types=None, 910 | currency_symbols=None, 911 | start_date=None, 912 | end_date=None, 913 | **kwargs, 914 | ): 915 | """ Gets the history of user's fiat transactions. 916 | 917 | If balance_types not specified, both 'withdrawal' and 'deposit' types will be used 918 | 919 | If currency_symbols not specified, all currency symbols will be used 920 | 921 | If start_date not specified, it will get trades for last 30 days. 922 | 923 | If you specify kwargs, the other parameters will be **overridden**. Only keyword arguments you specified 924 | will be used to construct a query. Therefore, it is your choice to use kwargs. 925 | 926 | But i strongly discourage you to use that for avoiding any invalid requests 927 | 928 | Parameters 929 | ---------- 930 | balance_types : list, optional 931 | ["buy", "sell"] 932 | 933 | currency_symbols : list, optional 934 | ["try", ...etc.] 935 | 936 | start_date : timestamp, optional 937 | 938 | end_date : timestamp, optional 939 | 940 | kwargs 941 | 942 | Returns 943 | ------- 944 | list 945 | List of trade data dictionaries, 946 | 947 | Example of Response Format 948 | 949 | .. code-block:: python 950 | 951 | [ 952 | { 953 | 'balanceType': '', 954 | 'currencySymbol': '', 955 | 'id': '', 956 | 'timestamp': '', 957 | 'funds': '', 958 | 'orderFund': '', 959 | 'fee': '', 960 | 'tax': 961 | }, 962 | ... 963 | ] 964 | """ 965 | if not start_date: 966 | last_30_days_timestamp = dt.datetime.timestamp( 967 | dt.datetime.today() - dt.timedelta(days=30) 968 | ) 969 | start_date = int(last_30_days_timestamp * 1000) 970 | 971 | if not end_date: 972 | end_date = int(time.time() * 1000) 973 | 974 | if not balance_types: 975 | balance_types = DEPOSIT_OR_WITHDRAWAL 976 | 977 | if not currency_symbols: 978 | currency_symbols = CURRENCY_SYMBOLS 979 | 980 | request_url = self.API_BASE + self.API_ENDPOINT_TRANSACTIONS + "fiat" 981 | params = ( 982 | kwargs 983 | if kwargs 984 | else { 985 | "balanceTypes": balance_types, 986 | "currencySymbols": currency_symbols, 987 | "startDate": start_date, 988 | "endDate": end_date, 989 | } 990 | ) 991 | 992 | self._update_session_headers() 993 | history = self._get(request_url, params) 994 | return history 995 | 996 | @authentication_required 997 | def get_open_orders(self, pair=None, **kwargs): 998 | """ Get's list of user's open orders for given pair 999 | 1000 | If you specify kwargs, the other parameters will be **overridden**. 1001 | Only keyword arguments you specified will be used to construct a query. 1002 | Therefore, it is your choice to use kwargs. 1003 | 1004 | But i strongly discourage you to use that for avoiding any invalid requests 1005 | 1006 | Parameters 1007 | ---------- 1008 | pair : str, optional 1009 | if not set returns all pairs open orders 1010 | 1011 | kwargs 1012 | 1013 | Returns 1014 | ------- 1015 | dict 1016 | Data dictionary 1017 | 1018 | Example of Response Format 1019 | 1020 | .. code-block:: python 1021 | 1022 | { 1023 | 'id': '', 1024 | 'price': '', 1025 | 'amount': '', 1026 | 'pairsymbol': '', 1027 | 'pairSymbolNormalized': '', 1028 | 'type': '', 1029 | 'method': '', 1030 | 'orderClientId': , 1031 | 'time': '', 1032 | 'updateTime': '', 1033 | 'status': 1034 | }, 1035 | """ 1036 | 1037 | request_url = self._create_auth_endpoint_url("openOrders") 1038 | params = kwargs if kwargs else {"pairSymbol": pair} 1039 | 1040 | self._update_session_headers() 1041 | orders = self._get(request_url, params) 1042 | return orders 1043 | 1044 | @authentication_required 1045 | def get_all_orders( 1046 | self, 1047 | order_id: int = 0, 1048 | pair_symbol=None, 1049 | start_date=None, 1050 | end_date=None, 1051 | page=None, 1052 | limit=100, 1053 | **kwargs, 1054 | ): 1055 | 1056 | """ Get's users all orders for given pair 1057 | 1058 | If you specify kwargs, the other parameters will be **overridden**. 1059 | Only keyword arguments you specified will be used to construct a query. 1060 | 1061 | 1062 | Therefore, it is your choice to use kwargs. 1063 | 1064 | But i strongly discourage you to use that for avoiding any invalid requests 1065 | 1066 | If start_date not specified, it will get orders for last 30 days. 1067 | 1068 | Parameters 1069 | ---------- 1070 | order_id: int, optional 1071 | If orderId set, it will return all orders greater than or equals to this order id 1072 | 1073 | pair_symbol: str, mandatory 1074 | Like BTC_TRY, XRP_USDT.. 1075 | 1076 | start_date: int, optional 1077 | start date as timestamp in milliseconds 1078 | 1079 | end_date: int, optional 1080 | end date as timestamp in milliseconds 1081 | 1082 | page: int, optional 1083 | page number 1084 | 1085 | limit: int, optional 1086 | limit number 1087 | 1088 | kwargs 1089 | 1090 | Returns 1091 | ------- 1092 | list 1093 | List of data dictionaries 1094 | 1095 | Example Of Response Format 1096 | 1097 | .. code-block:: python 1098 | 1099 | [ 1100 | { 1101 | 'id': '', 1102 | 'price': '', 1103 | 'amount': '', 1104 | 'quantity': '', 1105 | 'pairsymbol': '', 1106 | 'pairSymbolNormalized': '', 1107 | 'type': '', 1108 | 'method': '', 1109 | 'orderClientId': '', 1110 | 'time': '', 1111 | 'updateTime': '', 1112 | 'status': '', 1113 | 1114 | }, 1115 | ... 1116 | ] 1117 | """ 1118 | request_url = self._create_auth_endpoint_url("allOrders") 1119 | 1120 | if not start_date: 1121 | last_30_days_timestamp = dt.datetime.timestamp( 1122 | dt.datetime.today() - dt.timedelta(days=30) 1123 | ) 1124 | start_date = int(last_30_days_timestamp * 1000) 1125 | 1126 | if not end_date: 1127 | end_date = int(time.time() * 1000) 1128 | 1129 | order_id = order_id - 1 if order_id > 0 else 0 1130 | 1131 | payload = { 1132 | "orderId": order_id, 1133 | "pairSymbol": pair_symbol, 1134 | "startDate": start_date, 1135 | "endDate": end_date, 1136 | "page": page, 1137 | "limit": limit, 1138 | } 1139 | params = kwargs if kwargs else payload 1140 | 1141 | self._update_session_headers() 1142 | orders = self._get(request_url, params) 1143 | return orders 1144 | 1145 | # AUTHENTICATION REQUIRED ORDER IMPLEMENTATIONS 1146 | @authentication_required 1147 | def cancel_order(self, order_id=None): 1148 | """ Deletes The Order 1149 | 1150 | Parameters 1151 | ---------- 1152 | order_id : int, mandatory 1153 | 1154 | Returns 1155 | ------- 1156 | bool 1157 | Success value if there is no exception raised by handler 1158 | """ 1159 | request_url = self._create_auth_endpoint_url("order") 1160 | params = {"id": order_id} 1161 | 1162 | self._update_session_headers() 1163 | return self._delete(request_url, params) 1164 | 1165 | @authentication_required 1166 | def submit_market_order( 1167 | self, quantity=0.0, order_type=None, pair_symbol=None, new_order_client_id=None 1168 | ): 1169 | """ Submits an order in type of 'market order' 1170 | 1171 | Parameters 1172 | ---------- 1173 | quantity : float, mandatory 1174 | Mandatory for market or limit orders. 1175 | 1176 | order_type : str, mandatory 1177 | 'buy' or 'sell' 1178 | 1179 | pair_symbol : str, mandatory 1180 | 1181 | new_order_client_id : str, optional 1182 | 1183 | Returns 1184 | ------- 1185 | dict 1186 | Dictionary of order information 1187 | 1188 | Example of Response Format 1189 | 1190 | .. code-block:: python 1191 | 1192 | { 1193 | 'id': '', 1194 | 'datetime': '', 1195 | 'type': '', 1196 | 'method': '', 1197 | 'price': '', 1198 | 'stopPrice': '', 1199 | 'quantity': '', 1200 | 'pairSymbol': '', 1201 | 'pairSymbolNormalized': '', 1202 | 'newOrderClientId': '', 1203 | }, 1204 | 1205 | Raises 1206 | ------- 1207 | ValueError 1208 | If wrong pair_symbol entered or file cache for scales hasn't been updated 1209 | """ 1210 | if not new_order_client_id: 1211 | new_order_client_id = str(uuid.uuid1()) 1212 | 1213 | amount_scale = self.scale_limits[pair_symbol.upper()]["amount_scale"] 1214 | has_fraction = self.scale_limits[pair_symbol.upper()]["has_fraction"] 1215 | price_scale = ( 1216 | self.scale_limits[pair_symbol.upper()]["price_scale"] if has_fraction else 0 1217 | ) 1218 | 1219 | scale = amount_scale if order_type.lower() == "sell" else price_scale 1220 | 1221 | formatted_qty = str(self._format_unit(unit=quantity, scale=scale)) 1222 | 1223 | params = { 1224 | "quantity": formatted_qty, 1225 | "newOrderClientId": new_order_client_id, 1226 | "orderMethod": "market", 1227 | "orderType": order_type, 1228 | "pairSymbol": pair_symbol, 1229 | } 1230 | 1231 | return self._submit_order(params) 1232 | 1233 | @authentication_required 1234 | def submit_limit_order( 1235 | self, 1236 | quantity=0.0, 1237 | price=0.0, 1238 | order_type=None, 1239 | pair_symbol=None, 1240 | new_order_client_id=None, 1241 | ): 1242 | """ Submits an order in type of 'limit order' 1243 | 1244 | Parameters 1245 | ---------- 1246 | quantity : float, mandatory 1247 | Mandatory for market or limit orders. 1248 | 1249 | price : float, mandatory 1250 | Price field will be ignored for market orders. 1251 | 1252 | order_type : str, mandatory 1253 | 'buy' or 'sell' 1254 | 1255 | pair_symbol : str, mandatory 1256 | Like 'BTC_TRY', 'XRP_USDT'.. 1257 | 1258 | new_order_client_id : str, optional 1259 | 1260 | Returns 1261 | ------- 1262 | dict 1263 | Dictionary of order information 1264 | 1265 | Example of Response Format 1266 | 1267 | .. code-block:: python 1268 | 1269 | { 1270 | 'id': '', 1271 | 'datetime': '', 1272 | 'type': '', 1273 | 'method': '', 1274 | 'price': '', 1275 | 'stopPrice': '', 1276 | 'quantity': '', 1277 | 'pairSymbol': '', 1278 | 'pairSymbolNormalized': '', 1279 | 'newOrderClientId': '', 1280 | }, 1281 | 1282 | Raises 1283 | ------- 1284 | ValueError 1285 | If wrong pair_symbol entered 1286 | """ 1287 | 1288 | if not new_order_client_id: 1289 | new_order_client_id = str(uuid.uuid1()) 1290 | 1291 | scale = self.scale_limits[pair_symbol.upper()] 1292 | amount_scale, price_scale = scale["amount_scale"], scale["price_scale"] 1293 | has_fraction = self.scale_limits[pair_symbol.upper()]["has_fraction"] 1294 | 1295 | price_scale = price_scale if has_fraction else 0 1296 | 1297 | formatted_qty = str(self._format_unit(unit=quantity, scale=amount_scale)) 1298 | formatted_price = str(self._format_unit(unit=price, scale=price_scale)) 1299 | 1300 | params = { 1301 | "quantity": formatted_qty, 1302 | "price": formatted_price, 1303 | "newOrderClientId": new_order_client_id, 1304 | "orderMethod": "limit", 1305 | "orderType": order_type, 1306 | "pairSymbol": pair_symbol, 1307 | } 1308 | 1309 | return self._submit_order(params) 1310 | 1311 | @authentication_required 1312 | def submit_stop_order( 1313 | self, 1314 | stop_price=0.0, 1315 | quantity=0.0, 1316 | price=0.0, 1317 | order_type=None, 1318 | order_method=None, 1319 | pair_symbol=None, 1320 | new_order_client_id=None, 1321 | ): 1322 | """ Submits an order in type of 'stop order' 1323 | 1324 | Parameters 1325 | ---------- 1326 | stop_price: float, mandatory 1327 | For stop orders 1328 | 1329 | quantity : float, mandatory 1330 | Mandatory for market or limit orders. 1331 | 1332 | price : float, mandatory 1333 | Price field will be ignored for market orders. 1334 | 1335 | order_type : str, mandatory 1336 | 'buy' or 'sell' 1337 | 1338 | order_method: str, mandatory 1339 | Either 'stopLimit' or 'stopMarket' 1340 | 1341 | pair_symbol : str, mandatory 1342 | 1343 | new_order_client_id : str, optional 1344 | 1345 | Returns 1346 | ------- 1347 | dict 1348 | Dictionary of order information 1349 | 1350 | Example of Response Format 1351 | 1352 | .. code-block:: python 1353 | 1354 | { 1355 | 'id': '', 1356 | 'datetime': '', 1357 | 'type': '', 1358 | 'method': '', 1359 | 'price': '', 1360 | 'stopPrice': '', 1361 | 'quantity': '', 1362 | 'pairSymbol': '', 1363 | 'pairSymbolNormalized': '', 1364 | 'newOrderClientId': '', 1365 | }, 1366 | 1367 | Raises 1368 | ------ 1369 | ValueError 1370 | If wrong pair_symbol entered 1371 | """ 1372 | 1373 | if not new_order_client_id: 1374 | new_order_client_id = str(uuid.uuid1()) 1375 | 1376 | scale = self.scale_limits[pair_symbol.upper()] 1377 | amount_scale, price_scale = scale["amount_scale"], scale["price_scale"] 1378 | 1379 | has_fraction = self.scale_limits[pair_symbol.upper()]["has_fraction"] 1380 | price_scale = price_scale if has_fraction else 0 1381 | 1382 | formatted_qty = str(self._format_unit(unit=quantity, scale=amount_scale)) 1383 | formatted_price = str(self._format_unit(unit=price, scale=price_scale)) 1384 | formatted_stop_price = str(self._format_unit(unit=stop_price, scale=price_scale)) 1385 | 1386 | params = { 1387 | "quantity": formatted_qty, 1388 | "price": formatted_price, 1389 | "stopPrice": formatted_stop_price, 1390 | "newOrderClientId": new_order_client_id, 1391 | "orderMethod": order_method, 1392 | "orderType": order_type, 1393 | "pairSymbol": pair_symbol, 1394 | } 1395 | return self._submit_order(params) 1396 | 1397 | @authentication_required 1398 | def _submit_order(self, params=None, **kwargs): 1399 | """ Submits order for either keyword arguments, or parameter dictionary 1400 | 1401 | Parameters 1402 | ---------- 1403 | params : dict 1404 | dictionary of request parameters 1405 | 1406 | kwargs 1407 | 1408 | Returns 1409 | ------- 1410 | dict 1411 | 1412 | Dictionary of order information 1413 | 1414 | Example of Response Format 1415 | 1416 | .. code-block:: python 1417 | 1418 | { 1419 | 'id': '', 1420 | 'datetime': '', 1421 | 'type': '', 1422 | 'method': '', 1423 | 'price': '', 1424 | 'stopPrice': '', 1425 | 'quantity': '', 1426 | 'pairSymbol': '', 1427 | 'pairSymbolNormalized': '', 1428 | 'newOrderClientId': '', 1429 | }, 1430 | """ 1431 | 1432 | request_url = self._create_auth_endpoint_url("order") 1433 | self._update_session_headers() 1434 | 1435 | if kwargs: 1436 | return self._post(request_url, kwargs) 1437 | return self._post(request_url, params) 1438 | -------------------------------------------------------------------------------- /btcturk_api/constants.py: -------------------------------------------------------------------------------- 1 | CURRENCY_SYMBOLS = ["TRY"] 2 | CRYPTO_SYMBOLS = [ 3 | "BTC", 4 | "EOS", 5 | "ETH", 6 | "DASH", 7 | "ATOM", 8 | "USDT", 9 | "LTC", 10 | "XLM", 11 | "XRP", 12 | "NEO", 13 | "LINK", 14 | ] 15 | TRADE_TYPES = ["buy", "sell"] 16 | DEPOSIT_OR_WITHDRAWAL = ["deposit", "withdrawal"] 17 | -------------------------------------------------------------------------------- /btcturk_api/exceptions.py: -------------------------------------------------------------------------------- 1 | from requests import Response, get 2 | from datetime import datetime 3 | 4 | 5 | class BadRequestError(Exception): 6 | def __init__(self, response): 7 | self.response = response 8 | self.errors = { 9 | 400: InvalidRequestParameterError, 10 | 401: BTCTurkAuthenticationError, 11 | 404: UrlNotFoundError, 12 | 422: RequestProcessingError, 13 | 429: RequestLimitExceededError, 14 | } 15 | 16 | try: 17 | exception_class = self.errors[response.status_code] 18 | raise exception_class(response) 19 | except KeyError: 20 | raise UnknownError(response) 21 | 22 | 23 | class InvalidRequestParameterError(Exception): 24 | def __init__(self, response): 25 | self.response = response 26 | self.error_message = response.json()["message"] 27 | 28 | def __str__(self): 29 | return f"Invalid Request Parameters. Error Message: {self.error_message}" 30 | 31 | 32 | class BTCTurkAuthenticationError(Exception): 33 | def __init__(self, response: Response): 34 | self.error_msg = "Authentication Error.\n" 35 | 36 | if response.content: 37 | server_response_message = response.json()["message"] 38 | self.error_msg += f"Server Response: {server_response_message}\n" 39 | if server_response_message == "Unauthorized - Invalid Nonce": 40 | self._add_helper_msg_for_invalid_nonce() 41 | super().__init__(self.error_msg) 42 | 43 | def _add_helper_msg_for_invalid_nonce(self): 44 | self.error_msg += ( 45 | "You have encountered Invalid Nonce Error.\nThis usually happens when your client's time and " 46 | "server's time are inconsistent.\n" 47 | ) 48 | client_time = datetime.now() 49 | server_time = None 50 | try: 51 | server_time_response = get(url="https://api.btcturk.com/api/v2/server/time") 52 | server_time = datetime.fromtimestamp( 53 | server_time_response.json()["serverTime"] / 1000 54 | ) 55 | except Exception as e: 56 | self.error_msg += "Couldn't get server's time." 57 | 58 | if not server_time: 59 | return self.error_msg 60 | 61 | if client_time < server_time: 62 | self.error_msg += f"Your computer's time is behind of servers time by {server_time - client_time}" 63 | else: 64 | self.error_msg += f"Your computer's time is ahead of servers time by {client_time - server_time}" 65 | return self.error_msg 66 | 67 | def __str__(self): 68 | return f"Authentication Failed. Error Message: {self.error_msg}" 69 | 70 | 71 | class UrlNotFoundError(Exception): 72 | def __init__(self, response): 73 | super().__init__( 74 | f"There is something wrong with this API address: " 75 | f"{response.url}\n The endpoint may be changed. " 76 | f"Check the github page for further information" 77 | ) 78 | 79 | 80 | class RequestProcessingError(Exception): 81 | def __init__(self, response): 82 | super().__init__("Request could not be processed.\n" f"{response.content}") 83 | 84 | 85 | class RequestLimitExceededError(Exception): 86 | def __init__(self, response): 87 | super().__init__("Request limit has been exceeded.\n" f"{response.content}") 88 | 89 | 90 | class InternalServerError(Exception): 91 | def __init__(self, response): 92 | super().__init__("Internal Server Error!\n" f"{response.content}") 93 | 94 | 95 | class UnknownError(Exception): 96 | def __init__(self, response): 97 | super().__init__(f"Unknown Response Code: {response.status_code}") 98 | 99 | 100 | class BadRespondError(Exception): 101 | def __init__(self, message): 102 | super().__init__(message) 103 | -------------------------------------------------------------------------------- /btcturk_api/properties.py: -------------------------------------------------------------------------------- 1 | from btcturk_api.exceptions import BTCTurkAuthenticationError 2 | from requests import Response 3 | import functools 4 | 5 | 6 | def authentication_required(func): 7 | @functools.wraps(func) 8 | def wrapper_decorator(*args, **kwargs): 9 | if not args[0].authenticated: 10 | raise BTCTurkAuthenticationError(response=Response()) 11 | value = func(*args, **kwargs) 12 | return value 13 | 14 | return wrapper_decorator 15 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = source 9 | BUILDDIR = build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/source/account_endpoints.rst: -------------------------------------------------------------------------------- 1 | Account Related Endpoints 2 | ========================= 3 | 4 | 5 | Get Account Balance 6 | ******************* 7 | 8 | You can use this method for accessing your account balance information. 9 | 10 | Method returns all balances by default, if you want specific assets, you should give list of assets as 'assets' parameter. 11 | 12 | Here's an example, returning BTC and ETH balance information: 13 | 14 | .. code-block:: python 15 | 16 | >>> my_client.get_account_balance(assets=['BTC', 'ETH']) 17 | [ 18 | { 19 | 'asset': 'BTC', 20 | 'assetname': 'Bitcoin', 21 | 'balance': '0.0003670154826363', 22 | 'locked': '0', 23 | 'free': '0.0003670154826363', 24 | 'orderFund': '0', 25 | 'requestFund': '0', 26 | 'precision': 8 27 | }, 28 | { 29 | 'asset': 'ETH', 30 | 'assetname': 'Ethereum', 31 | 'balance': '0', 32 | 'locked': '0', 33 | 'free': '0', 34 | 'orderFund': '0', 35 | 'requestFund': '0', 36 | 'precision': 8 37 | } 38 | ] 39 | 40 | If you want to know more about this response and method's usage, check detailed information by clicking here. 41 | 42 | Get Trade History 43 | ***************** 44 | Usage Scenerio: Users want to access their trade history for last **90** days. But only **buy** trades needed and they want only 45 | bitcoin and ripple trades. 46 | 47 | .. code-block:: python 48 | 49 | >>> from datetime import datetime, timedelta 50 | >>> start_date = (datetime.now() - timedelta(days=90)).timestamp() 51 | >>> start_date = int(start_date * 1000) # Timestamp should be in milliseconds 52 | >>> my_client.get_trade_history( 53 | trade_type=['buy'], 54 | symbol=['btc', 'xrp'], 55 | start_date=start_date 56 | ) 57 | 58 | Get Crypto History 59 | ****************** 60 | Scenerio: Users want to access their trade history for last **90** days. But only **buy** orders needed and they want only 61 | bitcoin and ripple trades. 62 | 63 | .. code-block:: python 64 | 65 | >>> from datetime import datetime, timedelta 66 | >>> start_date = (datetime.now() - timedelta(days=90)).timestamp() 67 | >>> start_date = int(start_date * 1000) # Timestamp should be in milliseconds 68 | >>> my_client.get_trade_history( 69 | trade_type=['buy'], 70 | symbol=['btc', 'xrp'], 71 | start_date=start_date 72 | ) 73 | 74 | 75 | Get Fiat History 76 | ***************** 77 | Scenerio: Users want to access their trade history for last **90** days. But only **buy** orders needed and they want only 78 | bitcoin and ripple trades. 79 | 80 | .. code-block:: python 81 | 82 | >>> from datetime import datetime, timedelta 83 | >>> start_date = (datetime.now() - timedelta(days=90)).timestamp() 84 | >>> start_date = int(start_date * 1000) # Timestamp should be in milliseconds 85 | >>> my_client.get_trade_history( 86 | trade_type=['buy'], 87 | symbol=['btc', 'xrp'], 88 | start_date=start_date 89 | ) 90 | 91 | Get Open Orders 92 | ***************** 93 | Usage Scenario: Users want to get information about their open orders for all cryptocurrencies 94 | 95 | .. code-block:: python 96 | 97 | >>> my_client.get_open_orders() 98 | 99 | Get All Orders 100 | ***************** 101 | Usage Scenario: Users want to get their all ripple orders for last **6 hours**: 102 | 103 | .. code-block:: python 104 | 105 | >>> from datetime import datetime, timedelta 106 | >>> start_date = (datetime.now() - timedelta(hours=6)).timestamp() 107 | >>> start_date = int(start_date * 1000) # Timestamp should be in milliseconds 108 | >>> my_client.get_all_orders( 109 | pair_symbol='XRP_TRY', 110 | start_date=start_date 111 | ) 112 | -------------------------------------------------------------------------------- /docs/source/btcturk_api.rst: -------------------------------------------------------------------------------- 1 | BTCTurk API REFERENCE 2 | ===================== 3 | 4 | btcturk\_api.client module 5 | -------------------------- 6 | 7 | .. automodule:: btcturk_api.client 8 | :members: 9 | :undoc-members: 10 | :show-inheritance: 11 | 12 | -------------------------------------------------------------------------------- /docs/source/common_errors.rst: -------------------------------------------------------------------------------- 1 | Common Errors & Solutions 2 | ========================= 3 | 4 | In this section, you will find common errors that you may encounter while using the client. 5 | 6 | BTCTurkAuthenticationError 7 | ************************** 8 | One of the most common errors that you may encounter is BTCTurkAuthenticationError which means Server has unable to 9 | recognize your identity. Thus, you can't authenticate. 10 | 11 | Wrong API Credentials 12 | --------------------- 13 | - When you are creating your key/secret pair, you provide your machine's public IPv4 address. Make sure that you are providing 14 | the correct address. In addition to that, if you are using this client on a Virtual Machine, you must get credentials for 15 | that machine's public IPv4 address, always keep that in mind. 16 | 17 | - When you are doing copy/paste, you may miss some characters, always check when you paste your credentials. 18 | 19 | - Make sure you have authorized your credentials with correct permissions (buy-sell / see balance etc.) 20 | 21 | Invalid Nonce 22 | ------------- 23 | In order to authenticate, your machine's time and Btcturk Server's time must match. If it doesn't, you will get an 24 | authentication error with Invalid Nonce message. Again, if you are using a virtual machine, make sure these times match. 25 | 26 | You can check Btcturk's server time by creating a client without key/secret pair and calling `client.get_server_time()` 27 | method. 28 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | import sys 15 | 16 | sys.path.insert(0, os.path.abspath("../../")) 17 | 18 | # -- Project information ----------------------------------------------------- 19 | 20 | project = 'BTCTURK API - PYTHON' 21 | copyright = '2020 - 2021, Mirac Baydemir' 22 | author = 'Mirac Baydemir' 23 | 24 | # The full version, including alpha/beta/rc tags 25 | release = '1.6.0' 26 | 27 | 28 | # -- General configuration --------------------------------------------------- 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = [ 34 | 'numpydoc', 35 | 'sphinx_rtd_theme', 36 | ] 37 | 38 | # Add any paths that contain templates here, relative to this directory. 39 | templates_path = ['_templates'] 40 | 41 | # List of patterns, relative to source directory, that match files and 42 | # directories to ignore when looking for source files. 43 | # This pattern also affects html_static_path and html_extra_path. 44 | exclude_patterns = [] 45 | 46 | 47 | # -- Options for HTML output ------------------------------------------------- 48 | 49 | # The theme to use for HTML and HTML Help pages. See the documentation for 50 | # a list of builtin themes. 51 | # 52 | html_theme = 'sphinx_rtd_theme' 53 | 54 | # Add any paths that contain custom static files (such as style sheets) here, 55 | # relative to this directory. They are copied after the builtin static files, 56 | # so a file named "default.css" will overwrite the builtin "default.css". 57 | html_static_path = ['_static'] -------------------------------------------------------------------------------- /docs/source/exceptions.rst: -------------------------------------------------------------------------------- 1 | Exceptions 2 | ========== 3 | 4 | InvalidRequestParameterError 5 | **************************** 6 | When you provide invalid parameters, you get this exception. For proper usage of parameters, check BTCTurk API section 7 | of this documentation and learn more about the method you want to use. 8 | 9 | BTCTurkAuthenticationError 10 | ************************** 11 | When server can't authenticate you, this exception will be raised. In Exceptions sections, reasons of that explained. 12 | 13 | RequestLimitExceededError 14 | **************************** 15 | When you exceed Btcturk's api response limits, this exception will be raised. Check Btcturk's documentation for api limits. 16 | 17 | InternalServerError 18 | ******************* 19 | Raised for 5xx errors. If response message is html instead of json. -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | .. BTCTURK API - PYTHON documentation master file, created by 2 | sphinx-quickstart on Sun Feb 14 09:26:52 2021. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to BTCTURK API - PYTHON's documentation! 7 | ================================================ 8 | .. toctree:: 9 | :maxdepth: 2 10 | :caption: Contents: 11 | 12 | quickstart 13 | public_endpoints 14 | account_endpoints 15 | trade_operations 16 | exceptions 17 | common_errors 18 | btcturk_api 19 | 20 | 21 | Indices and tables 22 | ================== 23 | 24 | * :ref:`genindex` 25 | 26 | -------------------------------------------------------------------------------- /docs/source/modules.rst: -------------------------------------------------------------------------------- 1 | btcturk_api 2 | =========== 3 | 4 | .. toctree:: 5 | :maxdepth: 4 6 | 7 | btcturk_api 8 | -------------------------------------------------------------------------------- /docs/source/public_endpoints.rst: -------------------------------------------------------------------------------- 1 | Public Endpoints 2 | ================ 3 | 4 | Get Server Time 5 | *************** 6 | 7 | If you are encountering BTCTurkAuthenticationError, reason might be time inconsistency between server and your machine. 8 | You can get server's time with this method: 9 | 10 | .. code-block:: python 11 | 12 | >>> my_client.get_server_time() 13 | { 14 | 'serverTime': 1613313462021, 15 | 'serverTime2': '2021-02-14T14:37:42.0214779+00:00' 16 | } 17 | 18 | Get Exchange Info 19 | ***************** 20 | 21 | .. code-block:: python 22 | 23 | >>> my_client.get_exchange_info() 24 | [ 25 | { 26 | 'id': 1, 27 | 'name': 'BTCTRY', 28 | 'nameNormalized': 'BTC_TRY', 29 | 'status': 'TRADING', 30 | 'numerator': 'BTC', 31 | 'denominator': 'TRY', 32 | 'numeratorScale': 8, 33 | 'denominatorScale': 2, 34 | 'hasFraction': False, 35 | 'filters': [{'filterType': 'PRICE_FILTER', 'minPrice': '0.0000000000001', 'maxPrice': '10000000', ....], 36 | 'orderMethods': ['MARKET', 'LIMIT', 'STOP_MARKET', 'STOP_LIMIT'], 37 | 'displayFormat': '#,###', 38 | 'commissionFromNumerator': False, 39 | 'order': 1000, 40 | 'priceRounding': False 41 | }, 42 | {...} 43 | ] 44 | 45 | Get Ticker 46 | ********** 47 | 48 | .. code-block:: python 49 | 50 | >>> my_client.tick() 51 | { 52 | 'pair': 'BTCTRY', 53 | 'pairNormalized': 'BTC_TRY', 54 | 'timestamp': 1613313834273, 55 | 'last': 350000.0, 56 | 'high': 354975.0, 57 | 'low': 332565.0, 58 | 'bid': 350000.0, 59 | 'ask': 350904.0, 60 | 'open': 332775.0, 61 | 'volume': 1718.94206874, 62 | 'average': 344569.69406522, 63 | 'daily': 18129.0, 64 | 'dailyPercent': 5.18, 65 | 'denominatorSymbol': 'TRY', 66 | 'numeratorSymbol': 'BTC', 67 | 'order': 1000 68 | } 69 | 70 | Get OHLC Data 71 | ************* 72 | 73 | .. code-block:: python 74 | 75 | >>> my_client.get_ohlc_data(pair='BTC_USDT') 76 | 77 | [ 78 | { 79 | 'pairSymbol': 'BTCUSDT', 80 | 'pairNormalized': 'BTC_USDT', 81 | 'time': 1615766400000, 82 | 'open': '59125', 83 | 'high': '60575', 84 | 'low': '58804', 85 | 'close': '60396', 86 | 'volume': '17.989352800214', 87 | 'average': '59608.66', 88 | 'dailyChangeAmount': '1271', 89 | 'dailyChangePercentage': '2.15', 90 | }, 91 | ... 92 | ] 93 | 94 | Get Order book 95 | ************** 96 | 97 | .. code-block:: python 98 | 99 | >>> my_client.get_order_book(pair='BTCTRY', limit=1) 100 | {'timestamp': 1613315997466.0, 101 | 'bids': [['349600.00', '0.00518551']], 102 | 'asks': [['349830.00', '10.62911645']] 103 | } 104 | 105 | Get Trades 106 | ********** 107 | 108 | .. code-block:: python 109 | 110 | >>> my_client.get_trades(pair='BTCTRY') 111 | [ 112 | { 113 | 'pair': 'BTCTRY', 114 | 'pairNormalized': 'BTC_TRY', 115 | 'numerator': 'BTC', 116 | 'denominator': 'TRY', 117 | 'date': 1613316100877, 118 | 'tid': '637489129008759423', 119 | 'price': '349000.00', 120 | 'amount': '0.00500000', 121 | 'side': 'sell' 122 | }, 123 | {....} 124 | ] 125 | 126 | -------------------------------------------------------------------------------- /docs/source/quickstart.rst: -------------------------------------------------------------------------------- 1 | Quickstart 2 | ========== 3 | btcturkapi-python is a wrapper library built around Btcturk's REST api implementation. 4 | 5 | It provides more abstract version of REST Client for traders using Btcturk as a cryptocurrency platform. 6 | 7 | 8 | Installation 9 | ************ 10 | 11 | You can install this library via pip. 12 | 13 | It is the best practice using a virtual environment for your project, keep that in mind. 14 | 15 | Run this command through terminal: 16 | 17 | .. code-block:: bash 18 | 19 | $ pip install btcturk-api 20 | 21 | Since the project's current version doesn't have support for websockets and any advanced features, dependencies are simple and you should not encounter any installation error. 22 | 23 | Usage 24 | ***** 25 | 26 | After installing the library, just import the client and you are good to go. You can use any feature btcturk api provides without dealing with preparing requests, handling responses etc. 27 | 28 | .. code-block:: python 29 | 30 | >>> from btcturk_api.client import Client 31 | 32 | You can use public endpoints without providing an api key/secret pair. 33 | 34 | .. code-block:: python 35 | 36 | >>> my_client = Client() 37 | >>> my_client.get_server_time() 38 | {'serverTime': 1613292018131, 'serverTime2': '2021-02-14T08:40:18.1308832+00:00'} 39 | 40 | If you have an api key/secret pair, you can use *account endpoints* and *trading operations* 41 | 42 | .. code-block:: python 43 | 44 | >>> my_client = Client(api_key='', api_secret='') 45 | >>> my_client.get_account_balance() 46 | [ 47 | { 48 | 'asset': 'TRY', 49 | 'assetname': 'Türk Lirası', 50 | 'balance': '0.8462753436459292', 51 | 'locked': '0', 52 | 'free': '0.8462753436459292', 53 | 'orderFund': '0', 54 | 'requestFund': '0', 55 | 'precision': 2 56 | }, 57 | {...} 58 | ] 59 | 60 | -------------------------------------------------------------------------------- /docs/source/trade_operations.rst: -------------------------------------------------------------------------------- 1 | Trade Operations 2 | ================ 3 | 4 | Important Note About Orders 5 | *************************** 6 | We are going to talk about concepts like quantity, price in next subsections of this chapter. 7 | 8 | Users usually misinterpret those concepts and get various scaling errors, let's dive in these concepts one by one. 9 | 10 | Numerator / Denominator 11 | ----------------------- 12 | These concepts will be used to explain trades in a more clear way. In a trade pair such as **BTC/USDT**, 13 | first one is Numerator, second one is Denominator. In this example Numerator is **BTC**, Denominator is **USDT**. 14 | 15 | Quantity 16 | -------- 17 | In market orders, since price is determined by current market value you only provide a **quantity parameter** for your trade. 18 | 19 | If you are **buying**, denominator will be used as quantity unit, but if you are selling numerator becomes the quantity. 20 | 21 | If you are confused with these terms, don't worry! Here's a few cases which includes might want to sell or buy bitcoin. 22 | After looking these cases, you will understand why people misinterprets the concept quantity and what actually means. 23 | 24 | **Quick Example** 25 | 26 | I want to trade Bitcoin with USDT. 27 | 28 | **Case 1:** 29 | 30 | I want to buy bitcoin. 31 | 32 | **Wrong Approach**: 33 | 34 | I want to buy 0.05 bitcoin, so this is my quantity. I can call submit market order method with 'quantity=0.05' 35 | 36 | **WRONG!** This is misinterpreted by beginner client users all the time. 37 | 38 | If you provide 0.05 as quantity for BTC/USDT pair, **you tell the api that you want to buy bitcoin for worth of 0.05 USDT**, 39 | you will get either min_total_value error or scaling error if you do that. 40 | 41 | Don't forget, since you are buying, denominator in this pair should be your quantity. Thus, USDT! 42 | 43 | **Correct Approach:** 44 | 45 | I'm going to buy Bitcoin with my USDT with market price, i want to trade my 100 USDT with bitcoin, so my quantity is 100. 46 | 47 | **Case 2:** 48 | 49 | I want to sell bitcoin. 50 | 51 | Selling should not be confusing. In BTC/USDT pair, if you want to sell 0.05 Bitcoin, your quantity is 0.05. Pretty 52 | straightforward 53 | 54 | Price 55 | ----- 56 | Price is pretty straightforward too. It is the value of cryptocurrency in stock exchange. 57 | 58 | You can only use price parameter with stop orders and limit orders. 59 | 60 | 61 | Submit Market Order / Code Examples 62 | *********************************** 63 | 64 | **Usage Example 1:** 65 | 66 | - Pair: XRP/USDT 67 | - Goal: Buying XRP for 100 USDT 68 | 69 | .. code-block:: python 70 | 71 | >>> my_client.submit_market_order( 72 | quantity=100.0, 73 | order_type='buy', 74 | pair_symbol='XRP_USDT' 75 | ) 76 | 77 | **Usage Example 2:** 78 | 79 | - Pair: ETH/TRY 80 | - Goal: Selling 1250 ETH exchange of TRY 81 | 82 | 83 | .. code-block:: python 84 | 85 | >>> my_client.submit_market_order( 86 | quantity=1250.0, 87 | order_type='sell', 88 | pair_symbol='ETH_TRY' 89 | ) 90 | 91 | Submit Limit Order / Code Examples 92 | ********************************** 93 | **Usage Example 1:** 94 | 95 | - Pair: XRP/USDT 96 | - Goal: Place a Limit Buy Order for 400 XRP with price of 0.16 USDT 97 | 98 | .. code-block:: python 99 | 100 | >>> my_client.submit_limit_order( 101 | quantity=400.0, 102 | price=0.16, 103 | order_type='buy', 104 | pair_symbol='XRP_USDT' 105 | ) 106 | 107 | **Usage Example 2:** 108 | 109 | - Pair: ETH/TRY 110 | - Goal: Place a Limit Sell Order for 1250 ETH with price of 1950 USDT 111 | 112 | .. code-block:: python 113 | 114 | >>> my_client.submit_limit_order( 115 | quantity=1250.0, 116 | price=1950, 117 | order_type='sell', 118 | pair_symbol='ETH_USDT' 119 | ) 120 | 121 | Submit Stop Limit Order / Code Examples 122 | *************************************** 123 | **Usage Example 1:** 124 | 125 | - Pair: BTC/USDT 126 | - Goal: If Bitcoin price hits 50.000 USDT, we're going to place a limit buy order with quantity=0.05 and price=50.500 127 | 128 | .. code-block:: python 129 | 130 | >>> my_client.submit_limit_order( 131 | quantity=0.05, 132 | price=50500, 133 | stop_price=50000 134 | order_type='buy', 135 | pair_symbol='BTC_USDT' 136 | ) 137 | 138 | **Usage Example 2:** 139 | 140 | - Pair: BTC/USDT 141 | - Goal: If Bitcoin price drops 40.000 USDT, we're going to place a limit sell order with quantity=0.05 and price=39.500 142 | 143 | .. code-block:: python 144 | 145 | >>> my_client.submit_limit_order( 146 | quantity=0.05, 147 | price=39500, 148 | stop_price=40000 149 | order_type='sell', 150 | pair_symbol='BTC_USDT' 151 | ) 152 | -------------------------------------------------------------------------------- /release_notes.md: -------------------------------------------------------------------------------- 1 | # BTCTurk-Python API Release Notes 2 | 3 | ## v1.8.1 4 | 5 | ### Bug Fixes 6 | * Changed the way about formatting price and quantity metrics. It doesn't rounds up while formatting anymore. 7 | 8 | ## v1.7.1 9 | 10 | ### New Features 11 | * Added a method 'get_ohlc_data' for accessing Open - High - Low - Close data. (daily) 12 | * Updated documentation, now it has title for 'Get OHLC Data' in Public Endpoints Section. 13 | 14 | ## v1.6.1 15 | 16 | * Bug causing the client reference documentation not to show properly has been fixed. 17 | 18 | ## v1.6.0 19 | 20 | ### New Features 21 | * Added a nice, detailed documentation. 22 | 23 | ### Bug Fixes 24 | * Known bug causing scaling errors has been fixed. -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | alabaster==0.7.12 2 | appdirs==1.4.4 3 | attrs==20.3.0 4 | Babel==2.9.0 5 | black==19.10b0 6 | certifi==2020.12.5 7 | cffi==1.14.5 8 | chardet==4.0.0 9 | click==7.1.2 10 | colorama==0.4.4 11 | cryptography==3.4.5 12 | docutils==0.16 13 | idna==2.10 14 | imagesize==1.2.0 15 | Jinja2==2.11.3 16 | MarkupSafe==1.1.1 17 | msal==1.9.0 18 | numpydoc==1.1.0 19 | packaging==20.9 20 | pathspec==0.8.1 21 | pycparser==2.20 22 | Pygments==2.7.4 23 | PyJWT==2.0.1 24 | pyparsing==2.4.7 25 | pytz==2021.1 26 | regex==2020.11.13 27 | requests==2.25.1 28 | snowballstemmer==2.1.0 29 | Sphinx==3.4.3 30 | sphinx-rtd-theme==0.5.1 31 | sphinxcontrib-applehelp==1.0.2 32 | sphinxcontrib-devhelp==1.0.2 33 | sphinxcontrib-htmlhelp==1.0.3 34 | sphinxcontrib-jsmath==1.0.1 35 | sphinxcontrib-qthelp==1.0.3 36 | sphinxcontrib-serializinghtml==1.1.4 37 | toml==0.10.2 38 | typed-ast==1.4.2 39 | urllib3==1.26.4 40 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r") as fh: 4 | long_description = fh.read() 5 | 6 | setuptools.setup( 7 | name="btcturk_api", 8 | version="1.8.1", 9 | author="Ömer Miraç Baydemir", 10 | author_email="omermirac59@gmail.com", 11 | description="BTCTurk Rest API Python Implementation", 12 | long_description=long_description, 13 | long_description_content_type="text/markdown", 14 | url="https://github.com/outlier-1/btcturkapi-python", 15 | packages=['btcturk_api'], 16 | include_package_data=True, 17 | install_requires=['requests'], 18 | classifiers=[ 19 | "Programming Language :: Python :: 3", 20 | "Operating System :: OS Independent", 21 | ], 22 | python_requires='>=3.6', 23 | ) 24 | --------------------------------------------------------------------------------