├── .gitignore ├── LICENSE ├── __init__.py ├── binance_f ├── __init__.py ├── base │ ├── __init__.py │ ├── printobject.py │ └── printtime.py ├── constant │ ├── __init__.py │ ├── system.py │ └── test.py ├── exception │ ├── __init__.py │ └── binanceapiexception.py ├── impl │ ├── __init__.py │ ├── restapiinvoker.py │ ├── restapirequest.py │ ├── restapirequestimpl.py │ ├── utils │ │ ├── __init__.py │ │ ├── apisignature.py │ │ ├── channelparser.py │ │ ├── channels.py │ │ ├── inputchecker.py │ │ ├── jsonwrapper.py │ │ ├── timeservice.py │ │ └── urlparamsbuilder.py │ ├── websocketconnection.py │ ├── websocketrequest.py │ ├── websocketrequestimpl.py │ └── websocketwatchdog.py ├── model │ ├── __init__.py │ ├── accountinformation.py │ ├── accountupdate.py │ ├── aggregatetrade.py │ ├── aggregatetradeevent.py │ ├── balance.py │ ├── candlestick.py │ ├── candlestickevent.py │ ├── codeandmsg.py │ ├── constant.py │ ├── diffdepthevent.py │ ├── exchangeinformation.py │ ├── fundingrate.py │ ├── income.py │ ├── leverage.py │ ├── liquidationorder.py │ ├── liquidationorderevent.py │ ├── listenkeyexpired.py │ ├── markprice.py │ ├── markpriceevent.py │ ├── message.py │ ├── mytrade.py │ ├── openinterest.py │ ├── order.py │ ├── orderbook.py │ ├── orderbookevent.py │ ├── orderupdate.py │ ├── position.py │ ├── positionmargin.py │ ├── positionmarginhistory.py │ ├── positionmode.py │ ├── symbolbooktickerevent.py │ ├── symbolminitickerevent.py │ ├── symbolorderbook.py │ ├── symbolprice.py │ ├── symboltickerevent.py │ ├── tickerpricechangestatistics.py │ └── trade.py ├── requestclient.py └── subscriptionclient.py ├── doc └── README.md ├── exchanges.py ├── main_thread.py ├── requirements.txt ├── strategys.py └── utils ├── callib.py └── logger.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Distribution / packaging 2 | .idea/ 3 | __pycache__/ 4 | .history/ 5 | .ipynb_checkpoints/ 6 | .logs/ 7 | logs/* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 emmm~ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudthink/openquant/9eae105158eaafa6ec3b43cff0c547768883f3be/__init__.py -------------------------------------------------------------------------------- /binance_f/__init__.py: -------------------------------------------------------------------------------- 1 | from binance_f.requestclient import RequestClient 2 | from binance_f.subscriptionclient import SubscriptionClient 3 | -------------------------------------------------------------------------------- /binance_f/base/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudthink/openquant/9eae105158eaafa6ec3b43cff0c547768883f3be/binance_f/base/__init__.py -------------------------------------------------------------------------------- /binance_f/base/printobject.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | BASIC_DATA_TYPE = (int, str, float) 4 | BASIC_DATA_TYPE_BOOL = (bool) 5 | 6 | TYPE_BASIC = "type_basic" 7 | TYPE_BOOL = "type_bool" 8 | TYPE_OBJECT = "type_object" 9 | TYPE_LIST = "type_list" 10 | TYPE_DICT = "type_dict" 11 | TYPE_UNDEFINED = "type_undefined" 12 | 13 | 14 | class TypeCheck: 15 | @staticmethod 16 | def is_list(obj): 17 | return type(obj) == list and isinstance(obj, list) 18 | 19 | @staticmethod 20 | def is_dict(obj): 21 | return type(obj) == dict and isinstance(obj, dict) 22 | 23 | @staticmethod 24 | def is_object(obj): 25 | return isinstance(obj, object) 26 | 27 | @staticmethod 28 | def is_basic(obj): 29 | return isinstance(obj, BASIC_DATA_TYPE) 30 | 31 | @staticmethod 32 | def is_bool(obj): 33 | return isinstance(obj, bool) 34 | 35 | @staticmethod 36 | def get_obj_type(obj): 37 | if TypeCheck.is_basic(obj): 38 | return TYPE_BASIC 39 | elif TypeCheck.is_bool(obj): 40 | return TYPE_BOOL 41 | elif TypeCheck.is_list(obj): 42 | return TYPE_LIST 43 | elif TypeCheck.is_dict(obj): 44 | return TYPE_DICT 45 | elif TypeCheck.is_object(obj): 46 | return TYPE_OBJECT 47 | else: 48 | return TYPE_UNDEFINED 49 | 50 | 51 | class PrintBasic: 52 | @staticmethod 53 | def print_basic(data, name=None): 54 | if name and len(name): 55 | print(str(name) + " : " + str(data)) 56 | else: 57 | print(str(data)) 58 | 59 | @staticmethod 60 | def print_basic_bool(data, name=None): 61 | bool_desc = "True" 62 | if not data: 63 | bool_desc = "False" 64 | 65 | if name and len(name): 66 | print(str(name) + " : " + str(bool_desc)) 67 | else: 68 | print(str(bool_desc)) 69 | 70 | @staticmethod 71 | def print_obj(obj): 72 | if not obj: 73 | return -1 74 | 75 | members = [attr for attr in dir(obj) if not callable(attr) and not attr.startswith("__")] 76 | for member_def in members: 77 | val_str = str(getattr(obj, member_def)) 78 | print(member_def + ":" + val_str) 79 | return 0 80 | 81 | 82 | class PrintList: 83 | @staticmethod 84 | def print_list_data(obj): 85 | if not obj: 86 | print("object is None") 87 | return -1 88 | 89 | if TypeCheck.get_obj_type(obj) == TYPE_LIST: 90 | for idx, row in enumerate(obj): 91 | PrintBasic.print_basic(row) 92 | else: 93 | return -2 94 | 95 | return 0 96 | 97 | @staticmethod 98 | def print_origin_object(obj): 99 | if not obj: 100 | print("object is None") 101 | return -1 102 | obj_type = TypeCheck.get_obj_type(obj) 103 | 104 | if obj_type == TYPE_BASIC: 105 | PrintBasic.print_basic(obj) 106 | elif obj_type == TYPE_BOOL: 107 | PrintBasic.print_basic_bool(obj) 108 | elif obj_type == TYPE_OBJECT: 109 | PrintBasic.print_obj(obj) 110 | else: 111 | return 1 112 | 113 | return 0 114 | 115 | @staticmethod 116 | def print_object_list(obj_list): 117 | if not obj_list: 118 | return -1 119 | 120 | obj_type = TypeCheck.get_obj_type(obj_list) 121 | if obj_type != TYPE_LIST: 122 | return -2 123 | 124 | print ("data count : ", (len(obj_list))) 125 | print ("\n") 126 | for idx, row in enumerate(obj_list): 127 | print("data number " + (str(idx)) + " :") 128 | PrintList.print_origin_object(row) 129 | print("\n") 130 | print("\n\n") 131 | 132 | return 0 133 | 134 | @staticmethod 135 | def print_object_dict(obj_dict): 136 | if not obj_dict: 137 | return -1 138 | 139 | obj_type = TypeCheck.get_obj_type(obj_dict) 140 | if obj_type != TYPE_DICT: 141 | return -2 142 | 143 | print ("data count : ", (len(obj_dict))) 144 | print ("\n") 145 | for key, row in obj_dict.items(): 146 | PrintBasic.print_basic(str(key) + " :") 147 | PrintList.print_origin_object(row) 148 | print("\n") 149 | print("\n\n") 150 | 151 | return 0 152 | 153 | 154 | class PrintMix: 155 | @staticmethod 156 | def print_data(data): 157 | if not data: 158 | print (sys._getframe().f_code.co_name + " none data") 159 | return -1 160 | 161 | obj_type = TypeCheck.get_obj_type(data) 162 | 163 | if obj_type == TYPE_BASIC: 164 | PrintBasic.print_basic(data) 165 | elif obj_type == TYPE_BOOL: 166 | PrintBasic.print_basic_bool(data) 167 | elif obj_type == TYPE_LIST: 168 | PrintList.print_object_list(data) 169 | elif obj_type == TYPE_DICT: 170 | PrintList.print_object_dict(data) 171 | elif obj_type == TYPE_OBJECT: 172 | PrintList.print_origin_object(data) 173 | else: 174 | print (sys._getframe().f_code.co_name + " enter unknown") 175 | return -2 176 | 177 | return 0 178 | 179 | 180 | if __name__ == "__main__": 181 | """ 182 | from binance_f.model.symbol import Symbol 183 | 184 | symbol_1 = Symbol() 185 | symbol_1.amount_precision = 10009 186 | symbol_1.symbol = "btcusdt" 187 | 188 | symbol_2 = Symbol() 189 | symbol_2.amount_precision = 28 190 | symbol_2.symbol = "htusdt" 191 | 192 | symbol_3 = Symbol() 193 | symbol_3.amount_precision = 26 194 | symbol_3.symbol = "eosusdt" 195 | 196 | symbol_list = [symbol_1, symbol_2, symbol_3] 197 | symbol_dict = {"one": symbol_1, "two": symbol_2, "three": symbol_3} 198 | PrintMix.print_data(symbol_list) 199 | PrintMix.print_data(symbol_dict) 200 | 201 | print(type(symbol_list) == list) 202 | print(type(symbol_dict) == dict) 203 | print(type(symbol_list) == object) 204 | print(isinstance(symbol_list, list)) 205 | print(isinstance(symbol_list, object)) 206 | print(isinstance(symbol_dict, dict)) 207 | print(isinstance(symbol_dict, object)) 208 | """ 209 | 210 | a=['s', 'h', 'i'] 211 | PrintList.print_list_data(a) 212 | -------------------------------------------------------------------------------- /binance_f/base/printtime.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | import time 5 | 6 | 7 | 8 | 9 | class PrintDate: 10 | @staticmethod 11 | def timestamp_to_date(ts_minsecond): 12 | try: 13 | ts_minsecond = int(ts_minsecond) 14 | time_local = time.localtime(int(ts_minsecond / 1000)) 15 | dt = time.strftime("%Y-%m-%d %H:%M:%S", time_local) 16 | print("ping " + str(ts_minsecond) + ":" + dt) 17 | except Exception as e: 18 | print(e) 19 | 20 | if __name__ == "__main__": 21 | ping_ts = 1569319465421 22 | PrintDate.timestamp_to_date(ping_ts) 23 | PrintDate.timestamp_to_date(int(ping_ts), ("ping " + str(ping_ts))) 24 | 25 | -------------------------------------------------------------------------------- /binance_f/constant/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudthink/openquant/9eae105158eaafa6ec3b43cff0c547768883f3be/binance_f/constant/__init__.py -------------------------------------------------------------------------------- /binance_f/constant/system.py: -------------------------------------------------------------------------------- 1 | 2 | class WebSocketDefine: 3 | Uri = "wss://stream.binancefuture.com/ws" 4 | # Uri = "wss://fstream.binance.com/ws" 5 | 6 | class RestApiDefine: 7 | Url = "https://testnet.binancefuture.com" 8 | #Url = "https://fapi.binance.com" 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /binance_f/constant/test.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | p_api_key='' 4 | p_secret_key='' 5 | 6 | if(os.path.exists("binance_f/privateconfig.py")): 7 | from binance_f.privateconfig import * 8 | g_api_key = p_api_key 9 | g_secret_key = p_secret_key 10 | else: 11 | g_api_key = p_api_key 12 | g_secret_key = p_secret_key 13 | 14 | 15 | g_account_id = 12345678 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /binance_f/exception/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudthink/openquant/9eae105158eaafa6ec3b43cff0c547768883f3be/binance_f/exception/__init__.py -------------------------------------------------------------------------------- /binance_f/exception/binanceapiexception.py: -------------------------------------------------------------------------------- 1 | 2 | class BinanceApiException(Exception): 3 | 4 | RUNTIME_ERROR = "RuntimeError" 5 | INPUT_ERROR = "InputError" 6 | KEY_MISSING = "KeyMissing" 7 | SYS_ERROR = "SysError" 8 | SUBSCRIPTION_ERROR = "SubscriptionError" 9 | ENV_ERROR = "EnvironmentError" 10 | EXEC_ERROR = "ExecuteError" 11 | 12 | def __init__(self, error_code, error_message): 13 | self.error_code = error_code 14 | self.error_message = error_message 15 | -------------------------------------------------------------------------------- /binance_f/impl/__init__.py: -------------------------------------------------------------------------------- 1 | from binance_f.impl.restapirequest import RestApiRequest 2 | -------------------------------------------------------------------------------- /binance_f/impl/restapiinvoker.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import aiohttp 3 | from binance_f.exception.binanceapiexception import BinanceApiException 4 | from binance_f.impl.utils import * 5 | # from binance_f.base.logger.debugobject import * 6 | 7 | from utils import logger 8 | #logger.initlogger(log_level="ERROR", log_path=None, logfile_name=None, clear=False, backup_count=0) 9 | import traceback 10 | 11 | def check_response(json_wrapper): 12 | if json_wrapper.contain_key("success"): 13 | success = json_wrapper.get_boolean("success") 14 | if success is False: 15 | err_code = json_wrapper.get_int_or_default("code", "") 16 | err_msg = json_wrapper.get_string_or_default("msg", "") 17 | if err_code == "": 18 | raise BinanceApiException(BinanceApiException.EXEC_ERROR, "[Executing] " + err_msg) 19 | else: 20 | raise BinanceApiException(BinanceApiException.EXEC_ERROR, "[Executing] " + str(err_code) + ": " + err_msg) 21 | elif json_wrapper.contain_key("code"): 22 | code = json_wrapper.get_int("code") 23 | msg = json_wrapper.get_string_or_default("msg", "") 24 | if code != 200: 25 | #continue 26 | raise BinanceApiException(BinanceApiException.EXEC_ERROR, "[Executing] " + str(code) + ": " + msg) 27 | 28 | def get_limits_usage(response): 29 | limits = {} 30 | limits_headers = ["X-MBX-USED-WEIGHT-", "X-MBX-ORDER-COUNT-" ] # Limit headers to catch 31 | for key,value in response.headers.items(): 32 | if any([key.startswith(h) for h in limits_headers]): 33 | limits[key] = value 34 | return limits 35 | 36 | def call_sync(request): 37 | if request.method == "GET": 38 | response = requests.get(request.host + request.url, headers=request.header) 39 | limits = get_limits_usage(response) 40 | json_wrapper = parse_json_from_string(response.text) 41 | logger.debug(response.text) 42 | check_response(json_wrapper) 43 | return (request.json_parser(json_wrapper),limits) 44 | elif request.method == "POST": 45 | response = requests.post(request.host + request.url, headers=request.header) 46 | limits = get_limits_usage(response) 47 | json_wrapper = parse_json_from_string(response.text) 48 | logger.debug(response.text) 49 | check_response(json_wrapper) 50 | return (request.json_parser(json_wrapper),limits) 51 | elif request.method == "DELETE": 52 | response = requests.delete(request.host + request.url, headers=request.header) 53 | limits = get_limits_usage(response) 54 | json_wrapper = parse_json_from_string(response.text) 55 | logger.debug(response.text) 56 | check_response(json_wrapper) 57 | return (request.json_parser(json_wrapper),limits) 58 | elif request.method == "PUT": 59 | response = requests.put(request.host + request.url, headers=request.header) 60 | limits = get_limits_usage(response) 61 | json_wrapper = parse_json_from_string(response.text) 62 | logger.debug(response.text) 63 | check_response(json_wrapper) 64 | return (request.json_parser(json_wrapper),limits) 65 | 66 | 67 | -------------------------------------------------------------------------------- /binance_f/impl/restapirequest.py: -------------------------------------------------------------------------------- 1 | 2 | class RestApiRequest(object): 3 | 4 | def __init__(self): 5 | self.method = "" 6 | self.url = "" 7 | self.host = "" 8 | self.post_body = "" 9 | self.header = dict() 10 | self.json_parser = None 11 | self.header.update({"client_SDK_Version": "binance_futures-1.0.1-py3.7"}) 12 | 13 | -------------------------------------------------------------------------------- /binance_f/impl/restapirequestimpl.py: -------------------------------------------------------------------------------- 1 | from binance_f.impl import RestApiRequest 2 | from binance_f.impl.utils.urlparamsbuilder import UrlParamsBuilder 3 | from binance_f.impl.utils.apisignature import create_signature 4 | from binance_f.impl.utils.apisignature import create_signature_with_query 5 | from binance_f.impl.utils.inputchecker import * 6 | from binance_f.impl.utils.timeservice import * 7 | from binance_f.model import * 8 | # For develop 9 | from binance_f.base.printobject import * 10 | 11 | from utils import logger 12 | #logger.initLogger(log_level="DEBUG", log_path=None, logfile_name=None, clear=False, backup_count=0) 13 | 14 | def print(*args): 15 | logger.debug(*args) 16 | 17 | class RestApiRequestImpl(object): 18 | 19 | def __init__(self, api_key, secret_key, server_url="https://fapi.binance.com"): 20 | self.__api_key = api_key 21 | self.__secret_key = secret_key 22 | self.__server_url = server_url 23 | 24 | def __create_request_by_get(self, url, builder): 25 | request = RestApiRequest() 26 | request.method = "GET" 27 | request.host = self.__server_url 28 | request.header.update({'Content-Type': 'application/json'}) 29 | request.url = url + "?" + builder.build_url() 30 | return request 31 | 32 | def __create_request_by_get_with_apikey(self, url, builder): 33 | request = RestApiRequest() 34 | request.method = "GET" 35 | request.host = self.__server_url 36 | request.header.update({'Content-Type': 'application/json'}) 37 | request.header.update({"X-MBX-APIKEY": self.__api_key}) 38 | request.url = url + "?" + builder.build_url() 39 | # For develop 40 | print("====== Request ======") 41 | print(request) 42 | #PrintMix.print_data(request) 43 | print("=====================") 44 | return request 45 | 46 | def __create_request_by_post_with_signature(self, url, builder): 47 | request = RestApiRequest() 48 | request.method = "POST" 49 | request.host = self.__server_url 50 | builder.put_url("recvWindow", 60000) 51 | builder.put_url("timestamp", str(get_current_timestamp() - 1000)) 52 | create_signature(self.__secret_key, builder) 53 | request.header.update({'Content-Type': 'application/json'}) 54 | request.header.update({"X-MBX-APIKEY": self.__api_key}) 55 | request.post_body = builder.post_map 56 | request.url = url + "?" + builder.build_url() 57 | # For develop 58 | print("====== Request ======") 59 | print(request) 60 | #PrintMix.print_data(request) 61 | print("=====================") 62 | return request 63 | 64 | def __create_request_by_delete_with_signature(self, url, builder): 65 | request = RestApiRequest() 66 | request.method = "DELETE" 67 | request.host = self.__server_url 68 | builder.put_url("recvWindow", 60000) 69 | builder.put_url("timestamp", str(get_current_timestamp() - 1000)) 70 | create_signature(self.__secret_key, builder) 71 | request.header.update({'Content-Type': 'application/json'}) 72 | request.header.update({"X-MBX-APIKEY": self.__api_key}) 73 | request.url = url + "?" + builder.build_url() 74 | # For develop 75 | print("====== Request ======") 76 | print(request) 77 | #PrintMix.print_data(request) 78 | print("=====================") 79 | return request 80 | 81 | def __create_request_by_get_with_signature(self, url, builder): 82 | request = RestApiRequest() 83 | request.method = "GET" 84 | request.host = self.__server_url 85 | builder.put_url("recvWindow", 60000) 86 | builder.put_url("timestamp", str(get_current_timestamp() - 1000)) 87 | create_signature(self.__secret_key, builder) 88 | request.header.update({"Content-Type": "application/x-www-form-urlencoded"}) 89 | request.header.update({"X-MBX-APIKEY": self.__api_key}) 90 | request.url = url + "?" + builder.build_url() 91 | # For develop 92 | print("====== Request ======") 93 | print(request) 94 | #PrintMix.print_data(request) 95 | print("=====================") 96 | return request 97 | 98 | def __create_request_by_put_with_signature(self, url, builder): 99 | request = RestApiRequest() 100 | request.method = "PUT" 101 | request.host = self.__server_url 102 | builder.put_url("recvWindow", 60000) 103 | builder.put_url("timestamp", str(get_current_timestamp() - 1000)) 104 | create_signature(self.__secret_key, builder) 105 | request.header.update({'Content-Type': 'application/json'}) 106 | request.header.update({"X-MBX-APIKEY": self.__api_key}) 107 | request.url = url + "?" + builder.build_url() 108 | # For develop 109 | print("====== Request ======") 110 | print(request) 111 | #PrintMix.print_data(request) 112 | print("=====================") 113 | return request 114 | 115 | def get_servertime(self): 116 | builder = UrlParamsBuilder() 117 | request = self.__create_request_by_get("/fapi/v1/time", builder) 118 | 119 | def parse(json_wrapper): 120 | result = json_wrapper.get_int("serverTime") 121 | return result 122 | 123 | request.json_parser = parse 124 | return request 125 | 126 | def get_exchange_information(self): 127 | builder = UrlParamsBuilder() 128 | request = self.__create_request_by_get("/fapi/v1/exchangeInfo", builder) 129 | 130 | def parse(json_wrapper): 131 | result = ExchangeInformation.json_parse(json_wrapper) 132 | return result 133 | 134 | request.json_parser = parse 135 | return request 136 | 137 | def get_order_book(self, symbol, limit): 138 | check_should_not_none(symbol, "symbol") 139 | builder = UrlParamsBuilder() 140 | builder.put_url("symbol", symbol) 141 | builder.put_url("limit", limit) 142 | 143 | request = self.__create_request_by_get("/fapi/v1/depth", builder) 144 | 145 | def parse(json_wrapper): 146 | result = OrderBook.json_parse(json_wrapper) 147 | return result 148 | 149 | request.json_parser = parse 150 | return request 151 | 152 | def get_recent_trades_list(self, symbol, limit): 153 | check_should_not_none(symbol, "symbol") 154 | builder = UrlParamsBuilder() 155 | builder.put_url("symbol", symbol) 156 | builder.put_url("limit", limit) 157 | 158 | request = self.__create_request_by_get("/fapi/v1/trades", builder) 159 | 160 | def parse(json_wrapper): 161 | result = list() 162 | data_list = json_wrapper.convert_2_array() 163 | for item in data_list.get_items(): 164 | element = Trade.json_parse(item) 165 | result.append(element) 166 | return result 167 | 168 | request.json_parser = parse 169 | return request 170 | 171 | def get_old_trade_lookup(self, symbol, limit, fromId): 172 | check_should_not_none(symbol, "symbol") 173 | builder = UrlParamsBuilder() 174 | builder.put_url("symbol", symbol) 175 | builder.put_url("limit", limit) 176 | builder.put_url("fromId", fromId) 177 | 178 | request = self.__create_request_by_get_with_apikey("/fapi/v1/historicalTrades", builder) 179 | 180 | def parse(json_wrapper): 181 | result = list() 182 | data_list = json_wrapper.convert_2_array() 183 | for item in data_list.get_items(): 184 | element = Trade.json_parse(item) 185 | result.append(element) 186 | return result 187 | 188 | request.json_parser = parse 189 | return request 190 | 191 | def get_aggregate_trades_list(self, symbol, fromId, startTime, endTime, limit): 192 | check_should_not_none(symbol, "symbol") 193 | builder = UrlParamsBuilder() 194 | builder.put_url("symbol", symbol) 195 | builder.put_url("fromId", fromId) 196 | builder.put_url("startTime", startTime) 197 | builder.put_url("endTime", endTime) 198 | builder.put_url("limit", limit) 199 | 200 | request = self.__create_request_by_get("/fapi/v1/aggTrades", builder) 201 | 202 | def parse(json_wrapper): 203 | aggregate_trades_list = list() 204 | data_list = json_wrapper.convert_2_array() 205 | for item in data_list.get_items(): 206 | trade = AggregateTrade.json_parse(item) 207 | aggregate_trades_list.append(trade) 208 | return aggregate_trades_list 209 | 210 | request.json_parser = parse 211 | return request 212 | 213 | def get_candlestick_data(self, symbol, interval, startTime, endTime, limit): 214 | check_should_not_none(symbol, "symbol") 215 | check_should_not_none(symbol, "interval") 216 | builder = UrlParamsBuilder() 217 | builder.put_url("symbol", symbol) 218 | builder.put_url("interval", interval) 219 | builder.put_url("startTime", startTime) 220 | builder.put_url("endTime", endTime) 221 | builder.put_url("limit", limit) 222 | 223 | request = self.__create_request_by_get("/fapi/v1/klines", builder) 224 | 225 | def parse(json_wrapper): 226 | result = list() 227 | data_list = json_wrapper.convert_2_array() 228 | for item in data_list.get_items(): 229 | element = Candlestick.json_parse(item) 230 | result.append(element) 231 | return result 232 | 233 | request.json_parser = parse 234 | return request 235 | 236 | def get_mark_price(self, symbol): 237 | check_should_not_none(symbol, "symbol") 238 | builder = UrlParamsBuilder() 239 | builder.put_url("symbol", symbol) 240 | 241 | request = self.__create_request_by_get("/fapi/v1/premiumIndex", builder) 242 | 243 | def parse(json_wrapper): 244 | result = MarkPrice.json_parse(json_wrapper) 245 | return result 246 | 247 | request.json_parser = parse 248 | return request 249 | 250 | def get_funding_rate(self, symbol, startTime, endTime, limit): 251 | check_should_not_none(symbol, "symbol") 252 | builder = UrlParamsBuilder() 253 | builder.put_url("symbol", symbol) 254 | builder.put_url("startTime", startTime) 255 | builder.put_url("endTime", endTime) 256 | builder.put_url("limit", limit) 257 | 258 | request = self.__create_request_by_get("/fapi/v1/fundingRate", builder) 259 | 260 | def parse(json_wrapper): 261 | result = list() 262 | data_list = json_wrapper.convert_2_array() 263 | for item in data_list.get_items(): 264 | element = FundingRate.json_parse(item) 265 | result.append(element) 266 | return result 267 | 268 | request.json_parser = parse 269 | return request 270 | 271 | def get_ticker_price_change_statistics(self, symbol): 272 | builder = UrlParamsBuilder() 273 | builder.put_url("symbol", symbol) 274 | 275 | request = self.__create_request_by_get("/fapi/v1/ticker/24hr", builder) 276 | 277 | def parse(json_wrapper): 278 | result = list() 279 | 280 | if symbol: 281 | element = TickerPriceChangeStatistics.json_parse(json_wrapper) 282 | result.append(element) 283 | else: 284 | data_list = json_wrapper.convert_2_array() 285 | for item in data_list.get_items(): 286 | element = TickerPriceChangeStatistics.json_parse(item) 287 | result.append(element) 288 | 289 | return result 290 | 291 | request.json_parser = parse 292 | return request 293 | 294 | def get_symbol_price_ticker(self, symbol): 295 | builder = UrlParamsBuilder() 296 | builder.put_url("symbol", symbol) 297 | 298 | request = self.__create_request_by_get("/fapi/v1/ticker/price", builder) 299 | 300 | def parse(json_wrapper): 301 | result = list() 302 | if symbol: 303 | element = SymbolPrice.json_parse(json_wrapper) 304 | result.append(element) 305 | else: 306 | data_list = json_wrapper.convert_2_array() 307 | for item in data_list.get_items(): 308 | element = SymbolPrice.json_parse(item) 309 | result.append(element) 310 | return result 311 | 312 | request.json_parser = parse 313 | return request 314 | 315 | def get_symbol_orderbook_ticker(self, symbol): 316 | builder = UrlParamsBuilder() 317 | builder.put_url("symbol", symbol) 318 | 319 | request = self.__create_request_by_get("/fapi/v1/ticker/bookTicker", builder) 320 | 321 | def parse(json_wrapper): 322 | result = list() 323 | if symbol: 324 | element = SymbolOrderBook.json_parse(json_wrapper) 325 | result.append(element) 326 | else: 327 | data_list = json_wrapper.convert_2_array() 328 | for item in data_list.get_items(): 329 | element = SymbolOrderBook.json_parse(item) 330 | result.append(element) 331 | return result 332 | 333 | request.json_parser = parse 334 | return request 335 | 336 | 337 | def get_open_interest(self, symbol): 338 | builder = UrlParamsBuilder() 339 | builder.put_url("symbol", symbol) 340 | 341 | request = self.__create_request_by_get("/fapi/v1/openInterest", builder) 342 | 343 | def parse(json_wrapper): 344 | result = list() 345 | element = OpenInterest.json_parse(json_wrapper) 346 | result.append(element) 347 | return element 348 | 349 | request.json_parser = parse 350 | return request 351 | 352 | 353 | def get_liquidation_orders(self, symbol, startTime, endTime, limit): 354 | builder = UrlParamsBuilder() 355 | builder.put_url("symbol", symbol) 356 | builder.put_url("startTime", startTime) 357 | builder.put_url("endTime", endTime) 358 | builder.put_url("limit", limit) 359 | 360 | request = self.__create_request_by_get("/fapi/v1/allForceOrders", builder) 361 | 362 | def parse(json_wrapper): 363 | result = list() 364 | data_list = json_wrapper.convert_2_array() 365 | for item in data_list.get_items(): 366 | element = LiquidationOrder.json_parse(item) 367 | result.append(element) 368 | return result 369 | 370 | request.json_parser = parse 371 | return request 372 | 373 | 374 | def change_position_mode(self, dualSidePosition): 375 | check_should_not_none(dualSidePosition, "dualSidePosition") 376 | builder = UrlParamsBuilder() 377 | builder.put_url("dualSidePosition", dualSidePosition) 378 | 379 | request = self.__create_request_by_post_with_signature("/fapi/v1/positionSide/dual", builder) 380 | 381 | def parse(json_wrapper): 382 | result = CodeMsg.json_parse(json_wrapper) 383 | return result 384 | 385 | request.json_parser = parse 386 | return request 387 | 388 | 389 | def get_position_mode(self): 390 | 391 | request = self.__create_request_by_get_with_signature("/fapi/v1/positionSide/dual", builder) 392 | 393 | def parse(json_wrapper): 394 | result = PositionMode.json_parse(json_wrapper) 395 | return result 396 | 397 | request.json_parser = parse 398 | return request 399 | 400 | 401 | 402 | def post_order(self, symbol, side, ordertype, 403 | timeInForce, quantity, reduceOnly, price, newClientOrderId, stopPrice, workingType, closePosition, positionSide, callbackRate, activationPrice, newOrderRespType): 404 | check_should_not_none(symbol, "symbol") 405 | check_should_not_none(side, "side") 406 | check_should_not_none(ordertype, "ordertype") 407 | builder = UrlParamsBuilder() 408 | builder.put_url("symbol", symbol) 409 | builder.put_url("side", side) 410 | builder.put_url("type", ordertype) 411 | builder.put_url("timeInForce", timeInForce) 412 | builder.put_url("quantity", quantity) 413 | builder.put_url("reduceOnly", reduceOnly) 414 | builder.put_url("price", price) 415 | builder.put_url("newClientOrderId", newClientOrderId) 416 | builder.put_url("stopPrice", stopPrice) 417 | builder.put_url("workingType", workingType) 418 | builder.put_url("closePosition", closePosition) 419 | builder.put_url("positionSide", positionSide) 420 | builder.put_url("callbackRate", callbackRate) 421 | builder.put_url("activationPrice", activationPrice) 422 | builder.put_url("newOrderRespType", newOrderRespType) 423 | 424 | 425 | request = self.__create_request_by_post_with_signature("/fapi/v1/order", builder) 426 | 427 | def parse(json_wrapper): 428 | result = Order.json_parse(json_wrapper) 429 | return result 430 | 431 | request.json_parser = parse 432 | return request 433 | 434 | def get_order(self, symbol, orderId, origClientOrderId): 435 | check_should_not_none(symbol, "symbol") 436 | builder = UrlParamsBuilder() 437 | builder.put_url("symbol", symbol) 438 | builder.put_url("orderId", orderId) 439 | builder.put_url("origClientOrderId", origClientOrderId) 440 | 441 | request = self.__create_request_by_get_with_signature("/fapi/v1/order", builder) 442 | 443 | def parse(json_wrapper): 444 | result = Order.json_parse(json_wrapper) 445 | return result 446 | 447 | request.json_parser = parse 448 | return request 449 | 450 | def cancel_order(self, symbol, orderId, origClientOrderId): 451 | check_should_not_none(symbol, "symbol") 452 | builder = UrlParamsBuilder() 453 | builder.put_url("symbol", symbol) 454 | builder.put_url("orderId", orderId) 455 | builder.put_url("origClientOrderId", origClientOrderId) 456 | 457 | request = self.__create_request_by_delete_with_signature("/fapi/v1/order", builder) 458 | 459 | def parse(json_wrapper): 460 | result = Order.json_parse(json_wrapper) 461 | return result 462 | 463 | request.json_parser = parse 464 | return request 465 | 466 | def cancel_all_orders(self, symbol): 467 | check_should_not_none(symbol, "symbol") 468 | builder = UrlParamsBuilder() 469 | builder.put_url("symbol", symbol) 470 | 471 | request = self.__create_request_by_delete_with_signature("/fapi/v1/allOpenOrders", builder) 472 | 473 | def parse(json_wrapper): 474 | result = CodeMsg.json_parse(json_wrapper) 475 | return result 476 | 477 | request.json_parser = parse 478 | return request 479 | 480 | def cancel_list_orders(self, symbol, orderIdList, origClientOrderIdList): 481 | check_should_not_none(symbol, "symbol") 482 | builder = UrlParamsBuilder() 483 | builder.put_url("symbol", symbol) 484 | builder.put_url("orderIdList", orderIdList) 485 | builder.put_url("origClientOrderIdList", origClientOrderIdList) 486 | request = self.__create_request_by_delete_with_signature("/fapi/v1/batchOrders", builder) 487 | 488 | def parse(json_wrapper): 489 | result = list() 490 | data_list = json_wrapper.convert_2_array() 491 | for item in data_list.get_items(): 492 | if item.contain_key("code"): 493 | element = Msg.json_parse(item) 494 | else: 495 | element = Order.json_parse(item) 496 | result.append(element) 497 | return result 498 | 499 | request.json_parser = parse 500 | return request 501 | 502 | def get_open_orders(self, symbol): 503 | builder = UrlParamsBuilder() 504 | builder.put_url("symbol", symbol) 505 | 506 | request = self.__create_request_by_get_with_signature("/fapi/v1/openOrders", builder) 507 | 508 | def parse(json_wrapper): 509 | result = list() 510 | data_list = json_wrapper.convert_2_array() 511 | for item in data_list.get_items(): 512 | element = Order.json_parse(item) 513 | result.append(element) 514 | return result 515 | 516 | request.json_parser = parse 517 | return request 518 | 519 | def get_all_orders(self, symbol, orderId, startTime, endTime, limit): 520 | check_should_not_none(symbol, "symbol") 521 | builder = UrlParamsBuilder() 522 | builder.put_url("symbol", symbol) 523 | builder.put_url("orderId", orderId) 524 | builder.put_url("startTime", startTime) 525 | builder.put_url("endTime", endTime) 526 | builder.put_url("limit", limit) 527 | 528 | request = self.__create_request_by_get_with_signature("/fapi/v1/allOrders", builder) 529 | 530 | def parse(json_wrapper): 531 | result = list() 532 | data_list = json_wrapper.convert_2_array() 533 | for item in data_list.get_items(): 534 | element = Order.json_parse(item) 535 | result.append(element) 536 | return result 537 | 538 | request.json_parser = parse 539 | return request 540 | 541 | def get_balance(self): 542 | builder = UrlParamsBuilder() 543 | 544 | request = self.__create_request_by_get_with_signature("/fapi/v1/balance", builder) 545 | 546 | def parse(json_wrapper): 547 | result = list() 548 | data_list = json_wrapper.convert_2_array() 549 | for item in data_list.get_items(): 550 | element = Balance.json_parse(item) 551 | result.append(element) 552 | return result 553 | 554 | request.json_parser = parse 555 | return request 556 | 557 | def get_account_information(self): 558 | builder = UrlParamsBuilder() 559 | 560 | request = self.__create_request_by_get_with_signature("/fapi/v1/account", builder) 561 | 562 | def parse(json_wrapper): 563 | result = AccountInformation.json_parse(json_wrapper) 564 | return result 565 | 566 | request.json_parser = parse 567 | return request 568 | 569 | def change_initial_leverage(self, symbol, leverage): 570 | check_should_not_none(symbol, "symbol") 571 | check_should_not_none(leverage, "leverage") 572 | builder = UrlParamsBuilder() 573 | builder.put_url("symbol", symbol) 574 | builder.put_url("leverage", leverage) 575 | 576 | request = self.__create_request_by_post_with_signature("/fapi/v1/leverage", builder) 577 | 578 | def parse(json_wrapper): 579 | result = Leverage.json_parse(json_wrapper) 580 | return result 581 | 582 | request.json_parser = parse 583 | return request 584 | 585 | def change_margin_type(self, symbol, marginType): 586 | check_should_not_none(symbol, "symbol") 587 | check_should_not_none(marginType, "marginType") 588 | builder = UrlParamsBuilder() 589 | builder.put_url("symbol", symbol) 590 | builder.put_url("marginType", marginType) 591 | 592 | request = self.__create_request_by_post_with_signature("/fapi/v1/marginType", builder) 593 | 594 | def parse(json_wrapper): 595 | result = CodeMsg.json_parse(json_wrapper) 596 | return result 597 | 598 | request.json_parser = parse 599 | return request 600 | 601 | def change_position_margin(self, symbol, amount, type): 602 | check_should_not_none(symbol, "symbol") 603 | check_should_not_none(amount, "amount") 604 | check_should_not_none(type, "type") 605 | builder = UrlParamsBuilder() 606 | builder.put_url("symbol", symbol) 607 | builder.put_url("amount", amount) 608 | builder.put_url("type", type) 609 | 610 | request = self.__create_request_by_post_with_signature("/fapi/v1/positionMargin", builder) 611 | 612 | def parse(json_wrapper): 613 | result = PositionMargin.json_parse(json_wrapper) 614 | return result 615 | 616 | request.json_parser = parse 617 | return request 618 | 619 | def get_position_margin_change_history(self, symbol, type, startTime, endTime, limit): 620 | check_should_not_none(symbol, "symbol") 621 | builder = UrlParamsBuilder() 622 | builder.put_url("symbol", symbol) 623 | builder.put_url("type", type) 624 | builder.put_url("startTime", startTime) 625 | builder.put_url("endTime", endTime) 626 | builder.put_url("limit", limit) 627 | 628 | request = self.__create_request_by_get_with_signature("/fapi/v1/positionMargin/history", builder) 629 | 630 | def parse(json_wrapper): 631 | result = list() 632 | data_list = json_wrapper.convert_2_array() 633 | for item in data_list.get_items(): 634 | element = PositionMarginHist.json_parse(item) 635 | result.append(element) 636 | return result 637 | 638 | request.json_parser = parse 639 | return request 640 | 641 | 642 | def get_position(self): 643 | builder = UrlParamsBuilder() 644 | 645 | request = self.__create_request_by_get_with_signature("/fapi/v1/positionRisk", builder) 646 | 647 | def parse(json_wrapper): 648 | result = list() 649 | data_list = json_wrapper.convert_2_array() 650 | for item in data_list.get_items(): 651 | element = Position.json_parse(item) 652 | result.append(element) 653 | return result 654 | 655 | request.json_parser = parse 656 | return request 657 | 658 | def get_account_trades(self, symbol, startTime, endTime, fromId, limit): 659 | check_should_not_none(symbol, "symbol") 660 | builder = UrlParamsBuilder() 661 | builder.put_url("symbol", symbol) 662 | builder.put_url("startTime", startTime) 663 | builder.put_url("endTime", endTime) 664 | builder.put_url("fromId", fromId) 665 | builder.put_url("limit", limit) 666 | 667 | request = self.__create_request_by_get_with_signature("/fapi/v1/userTrades", builder) 668 | 669 | def parse(json_wrapper): 670 | result = list() 671 | data_list = json_wrapper.convert_2_array() 672 | for item in data_list.get_items(): 673 | element = MyTrade.json_parse(item) 674 | result.append(element) 675 | return result 676 | 677 | request.json_parser = parse 678 | return request 679 | 680 | def get_income_history(self, symbol, incomeType, startTime, endTime, limit): 681 | builder = UrlParamsBuilder() 682 | builder.put_url("symbol", symbol) 683 | builder.put_url("incomeType", incomeType) 684 | builder.put_url("startTime", startTime) 685 | builder.put_url("endTime", endTime) 686 | builder.put_url("limit", limit) 687 | 688 | request = self.__create_request_by_get_with_signature("/fapi/v1/income", builder) 689 | 690 | def parse(json_wrapper): 691 | result = list() 692 | data_list = json_wrapper.convert_2_array() 693 | for item in data_list.get_items(): 694 | element = Income.json_parse(item) 695 | result.append(element) 696 | return result 697 | 698 | request.json_parser = parse 699 | return request 700 | 701 | def start_user_data_stream(self): 702 | builder = UrlParamsBuilder() 703 | 704 | request = self.__create_request_by_post_with_signature("/fapi/v1/listenKey", builder) 705 | 706 | def parse(json_wrapper): 707 | result = json_wrapper.get_string("listenKey") 708 | return result 709 | 710 | request.json_parser = parse 711 | return request 712 | 713 | def keep_user_data_stream(self): 714 | builder = UrlParamsBuilder() 715 | 716 | request = self.__create_request_by_put_with_signature("/fapi/v1/listenKey", builder) 717 | 718 | def parse(json_wrapper): 719 | result = "OK" 720 | return result 721 | 722 | request.json_parser = parse 723 | return request 724 | 725 | def close_user_data_stream(self): 726 | builder = UrlParamsBuilder() 727 | 728 | request = self.__create_request_by_delete_with_signature("/fapi/v1/listenKey", builder) 729 | 730 | def parse(json_wrapper): 731 | result = "OK" 732 | return result 733 | 734 | request.json_parser = parse 735 | return request 736 | 737 | -------------------------------------------------------------------------------- /binance_f/impl/utils/__init__.py: -------------------------------------------------------------------------------- 1 | import json 2 | from binance_f.impl.utils.jsonwrapper import JsonWrapper 3 | 4 | 5 | def parse_json_from_string(value): 6 | value = value.replace("False","false") 7 | value = value.replace("True","true") 8 | return JsonWrapper(json.loads(value)) 9 | -------------------------------------------------------------------------------- /binance_f/impl/utils/apisignature.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import hashlib 3 | import hmac 4 | import datetime 5 | from urllib import parse 6 | import urllib.parse 7 | from binance_f.exception.binanceapiexception import BinanceApiException 8 | 9 | 10 | def create_signature(secret_key, builder): 11 | if secret_key is None or secret_key == "": 12 | raise BinanceApiException(BinanceApiException.KEY_MISSING, "Secret key are required") 13 | 14 | # keys = builder.param_map.keys() 15 | # query_string = '&'.join(['%s=%s' % (key, parse.quote(builder.param_map[key], safe='')) for key in keys]) 16 | query_string = builder.build_url() 17 | signature = hmac.new(secret_key.encode(), msg=query_string.encode(), digestmod=hashlib.sha256).hexdigest() 18 | builder.put_url("signature", signature) 19 | 20 | 21 | def create_signature_with_query(secret_key, query): 22 | if secret_key is None or secret_key == "": 23 | raise BinanceApiException(BinanceApiException.KEY_MISSING, "Secret key are required") 24 | 25 | signature = hmac.new(secret_key.encode(), msg=query.encode(), digestmod=hashlib.sha256).hexdigest() 26 | 27 | return signature 28 | 29 | 30 | def utc_now(): 31 | return datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%S') 32 | -------------------------------------------------------------------------------- /binance_f/impl/utils/channelparser.py: -------------------------------------------------------------------------------- 1 | class ChannelParser: 2 | def __init__(self, input): 3 | fields = input.split(".") 4 | if len(fields) >= 2: 5 | self.symbol = fields[1] 6 | -------------------------------------------------------------------------------- /binance_f/impl/utils/channels.py: -------------------------------------------------------------------------------- 1 | import json 2 | from binance_f.impl.utils.timeservice import get_current_timestamp 3 | from binance_f.model import DepthStep 4 | 5 | 6 | def aggregate_trade_channel(symbol): 7 | channel = dict() 8 | channel["params"] = list() 9 | channel["params"].append(symbol + "@aggTrade") 10 | channel["id"] = get_current_timestamp() 11 | channel["method"] = "SUBSCRIBE" 12 | return json.dumps(channel) 13 | 14 | def mark_price_channel(symbol): 15 | channel = dict() 16 | channel["params"] = list() 17 | channel["params"].append(symbol + "@markPrice") 18 | channel["id"] = get_current_timestamp() 19 | channel["method"] = "SUBSCRIBE" 20 | return json.dumps(channel) 21 | 22 | def kline_channel(symbol, interval): 23 | channel = dict() 24 | channel["params"] = list() 25 | channel["params"].append(symbol + "@kline_" + interval) 26 | channel["id"] = get_current_timestamp() 27 | channel["method"] = "SUBSCRIBE" 28 | return json.dumps(channel) 29 | 30 | def trade_channel(symbol): 31 | channel = dict() 32 | channel["params"] = list() 33 | channel["params"].append(symbol + "@trade") 34 | channel["id"] = get_current_timestamp() 35 | channel["method"] = "SUBSCRIBE" 36 | return json.dumps(channel) 37 | 38 | def symbol_miniticker_channel(symbol): 39 | channel = dict() 40 | channel["params"] = list() 41 | channel["params"].append(symbol + "@miniTicker") 42 | channel["id"] = get_current_timestamp() 43 | channel["method"] = "SUBSCRIBE" 44 | return json.dumps(channel) 45 | 46 | def all_miniticker_channel(): 47 | channel = dict() 48 | channel["params"] = list() 49 | channel["params"].append("!miniTicker@arr") 50 | channel["id"] = get_current_timestamp() 51 | channel["method"] = "SUBSCRIBE" 52 | return json.dumps(channel) 53 | 54 | def symbol_ticker_channel(symbol): 55 | channel = dict() 56 | channel["params"] = list() 57 | channel["params"].append(symbol + "@ticker") 58 | channel["id"] = get_current_timestamp() 59 | channel["method"] = "SUBSCRIBE" 60 | return json.dumps(channel) 61 | 62 | def all_ticker_channel(): 63 | channel = dict() 64 | channel["params"] = list() 65 | channel["params"].append("!ticker@arr") 66 | channel["id"] = get_current_timestamp() 67 | channel["method"] = "SUBSCRIBE" 68 | return json.dumps(channel) 69 | 70 | def symbol_bookticker_channel(symbol): 71 | channel = dict() 72 | channel["params"] = list() 73 | channel["params"].append(symbol + "@bookTicker") 74 | channel["id"] = get_current_timestamp() 75 | channel["method"] = "SUBSCRIBE" 76 | return json.dumps(channel) 77 | 78 | def all_bookticker_channel(): 79 | channel = dict() 80 | channel["params"] = list() 81 | channel["params"].append("!bookTicker") 82 | channel["id"] = get_current_timestamp() 83 | channel["method"] = "SUBSCRIBE" 84 | return json.dumps(channel) 85 | 86 | def symbol_liquidation_channel(symbol): 87 | channel = dict() 88 | channel["params"] = list() 89 | channel["params"].append(symbol + "@forceOrder") 90 | channel["id"] = get_current_timestamp() 91 | channel["method"] = "SUBSCRIBE" 92 | return json.dumps(channel) 93 | 94 | def all_liquidation_channel(symbol): 95 | channel = dict() 96 | channel["params"] = list() 97 | channel["params"].append("!forceOrder@arr") 98 | channel["id"] = get_current_timestamp() 99 | channel["method"] = "SUBSCRIBE" 100 | return json.dumps(channel) 101 | 102 | def book_depth_channel(symbol, limit, update_time): 103 | channel = dict() 104 | channel["params"] = list() 105 | channel["params"].append(symbol + "@depth" + str(limit) + str(update_time)) 106 | channel["id"] = get_current_timestamp() 107 | channel["method"] = "SUBSCRIBE" 108 | return json.dumps(channel) 109 | 110 | def diff_depth_channel(symbol, update_time): 111 | channel = dict() 112 | channel["params"] = list() 113 | channel["params"].append(symbol + "@depth" + update_time) 114 | channel["id"] = get_current_timestamp() 115 | channel["method"] = "SUBSCRIBE" 116 | return json.dumps(channel) 117 | 118 | def user_data_channel(listenKey): 119 | channel = dict() 120 | channel["params"] = list() 121 | channel["params"].append(listenKey) 122 | channel["id"] = get_current_timestamp() 123 | channel["method"] = "SUBSCRIBE" 124 | return json.dumps(channel) 125 | -------------------------------------------------------------------------------- /binance_f/impl/utils/inputchecker.py: -------------------------------------------------------------------------------- 1 | import re 2 | import time 3 | from binance_f.exception.binanceapiexception import BinanceApiException 4 | 5 | reg_ex = "[ _`~!@#$%^&*()+=|{}':;',\\[\\].<>/?~!@#¥%……&*()——+|{}【】‘;:”“’。,、?]|\n|\t" 6 | 7 | 8 | def check_symbol(symbol): 9 | if not isinstance(symbol, str): 10 | raise BinanceApiException(BinanceApiException.INPUT_ERROR, "[Input] symbol must be string") 11 | if re.match(reg_ex, symbol): 12 | raise BinanceApiException(BinanceApiException.INPUT_ERROR, "[Input] " + symbol + " is invalid symbol") 13 | 14 | 15 | def check_symbol_list(symbols): 16 | if not isinstance(symbols, list): 17 | raise BinanceApiException(BinanceApiException.INPUT_ERROR, "[Input] symbols in subscription is not a list") 18 | for symbol in symbols: 19 | check_symbol(symbol) 20 | 21 | 22 | def check_currency(currency): 23 | if not isinstance(currency, str): 24 | raise BinanceApiException(BinanceApiException.INPUT_ERROR, "[Input] currency must be string") 25 | if re.match(reg_ex, currency) is not None: 26 | raise BinanceApiException(BinanceApiException.INPUT_ERROR, "[Input] " + currency + " is invalid currency") 27 | 28 | 29 | def check_range(value, min_value, max_value, name): 30 | if value is None: 31 | return 32 | if min_value > value or value > max_value: 33 | raise BinanceApiException(BinanceApiException.INPUT_ERROR, 34 | "[Input] " + name + " is out of bound. " + str(value) + " is not in [" + str( 35 | min_value) + "," + str(max_value) + "]") 36 | 37 | 38 | def check_should_not_none(value, name): 39 | if value is None: 40 | raise BinanceApiException(BinanceApiException.INPUT_ERROR, "[Input] " + name + " should not be null") 41 | 42 | 43 | def check_should_none(value, name): 44 | if value is not None: 45 | raise BinanceApiException(BinanceApiException.INPUT_ERROR, "[Input] " + name + " should be null") 46 | 47 | 48 | def check_list(list_value, min_value, max_value, name): 49 | if list_value is None: 50 | return 51 | if len(list_value) > max_value: 52 | raise BinanceApiException(BinanceApiException.INPUT_ERROR, 53 | "[Input] " + name + " is out of bound, the max size is " + str(max_value)) 54 | if len(list_value) < min_value: 55 | raise BinanceApiException(BinanceApiException.INPUT_ERROR, 56 | "[Input] " + name + " should contain " + str(min_value) + " item(s) at least") 57 | 58 | 59 | def greater_or_equal(value, base, name): 60 | if value is not None and value < base: 61 | raise BinanceApiException(BinanceApiException.INPUT_ERROR, 62 | "[Input] " + name + " should be greater than " + base) 63 | 64 | 65 | def format_date(value, name): 66 | if value is None: 67 | return None 68 | if not isinstance(value, str): 69 | raise BinanceApiException(BinanceApiException.INPUT_ERROR, "[Input] " + name + " must be string") 70 | try: 71 | new_time = time.strptime(value, "%Y-%m-%d") 72 | return time.strftime("%Y-%m-%d", new_time) 73 | except: 74 | raise BinanceApiException(BinanceApiException.INPUT_ERROR, "[Input] " + name + " is not invalid date format") 75 | -------------------------------------------------------------------------------- /binance_f/impl/utils/jsonwrapper.py: -------------------------------------------------------------------------------- 1 | import json 2 | from binance_f.exception.binanceapiexception import BinanceApiException 3 | 4 | class JsonWrapper: 5 | def __init__(self, json_object): 6 | self.json_object = json_object 7 | 8 | def __check_mandatory_field(self, name): 9 | if name not in self.json_object: 10 | raise BinanceApiException(BinanceApiException.RUNTIME_ERROR, 11 | "[Json] Get json item field: " + name + " does not exist") 12 | 13 | def contain_key(self, name): 14 | if name in self.json_object: 15 | return True 16 | else: 17 | return False 18 | 19 | def get_boolean(self, name): 20 | self.__check_mandatory_field(name) 21 | return bool(self.json_object[name]) 22 | 23 | def get_string(self, name): 24 | self.__check_mandatory_field(name) 25 | return str(self.json_object[name]) 26 | 27 | def get_int(self, name): 28 | self.__check_mandatory_field(name) 29 | return int(self.json_object[name]) 30 | 31 | def get_string_or_default(self, name, default): 32 | if self.contain_key(name): 33 | return str(self.json_object[name]) 34 | else: 35 | return default 36 | 37 | def get_int_or_default(self, name, default): 38 | if self.contain_key(name): 39 | return int(self.json_object[name]) 40 | else: 41 | return default 42 | 43 | def get_float(self, name): 44 | self.__check_mandatory_field(name) 45 | return float(self.json_object[name]) 46 | 47 | def get_float_or_default(self, name, default): 48 | if self.contain_key(name): 49 | return float(self.json_object[name]) 50 | else: 51 | return default 52 | 53 | def get_object(self, name): 54 | self.__check_mandatory_field(name) 55 | return JsonWrapper(self.json_object[name]) 56 | 57 | def get_object_or_default(self, name, defalut_value): 58 | if name not in self.json_object: 59 | return defalut_value 60 | else: 61 | return JsonWrapper(self.json_object[name]) 62 | 63 | def get_array(self, name): 64 | self.__check_mandatory_field(name) 65 | return JsonWrapperArray(self.json_object[name]) 66 | 67 | def convert_2_array(self): 68 | return JsonWrapperArray(self.json_object) 69 | 70 | def convert_2_dict(self): 71 | items = dict() 72 | for item in self.json_object: 73 | name = item 74 | items[name] = self.json_object[name] 75 | return items 76 | 77 | def convert_2_list(self): 78 | items = list() 79 | for item in self.json_object: 80 | items.append(item) 81 | return items 82 | 83 | 84 | 85 | class JsonWrapperArray: 86 | def __init__(self, json_object): 87 | self.json_object = json_object 88 | 89 | def get_items(self): 90 | items = list() 91 | for item in self.json_object: 92 | items.append(JsonWrapper(item)) 93 | return items 94 | 95 | def get_items_as_array(self): 96 | items = list() 97 | for item in self.json_object: 98 | items.append(JsonWrapperArray(item)) 99 | return items 100 | 101 | def get_float_at(self, index): 102 | return float(self.json_object[index]) 103 | 104 | def get_items_as_string(self): 105 | items = list() 106 | for item in self.json_object: 107 | items.append(str(item)) 108 | return items 109 | 110 | def get_array_at(self, index): 111 | return JsonWrapperArray(self.json_object[index]) 112 | 113 | def get_object_at(self, index): 114 | return JsonWrapper(self.json_object[index]) 115 | 116 | 117 | -------------------------------------------------------------------------------- /binance_f/impl/utils/timeservice.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | def get_current_timestamp(): 5 | return int(round(time.time() * 1000)) 6 | 7 | 8 | def convert_cst_in_second_to_utc(time_in_second): 9 | if time_in_second > 946656000: 10 | return (time_in_second - 8 * 60 * 60) * 1000 11 | else: 12 | return 0 13 | 14 | 15 | def convert_cst_in_millisecond_to_utc(time_in_ms): 16 | if time_in_ms > 946656000000: 17 | return time_in_ms - 8 * 60 * 60 * 1000 18 | else: 19 | return 0 20 | 21 | -------------------------------------------------------------------------------- /binance_f/impl/utils/urlparamsbuilder.py: -------------------------------------------------------------------------------- 1 | import json 2 | import urllib.parse 3 | 4 | 5 | class UrlParamsBuilder(object): 6 | 7 | def __init__(self): 8 | self.param_map = dict() 9 | self.post_map = dict() 10 | 11 | def put_url(self, name, value): 12 | if value is not None: 13 | if isinstance(value, list): 14 | self.param_map[name] = json.dumps(value) 15 | elif isinstance(value, float): 16 | self.param_map[name] = ('%f' % (value))[slice(0, 16)].rstrip('0').rstrip('.') 17 | #print(self.param_map[name]) 18 | else: 19 | self.param_map[name] = str(value) 20 | def put_post(self, name, value): 21 | if value is not None: 22 | if isinstance(value, list): 23 | self.post_map[name] = value 24 | else: 25 | self.post_map[name] = str(value) 26 | 27 | def build_url(self): 28 | if len(self.param_map) == 0: 29 | return "" 30 | encoded_param = urllib.parse.urlencode(self.param_map) 31 | return encoded_param 32 | 33 | def build_url_to_json(self): 34 | return json.dumps(self.param_map) 35 | -------------------------------------------------------------------------------- /binance_f/impl/websocketconnection.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import websocket 3 | import gzip 4 | import ssl 5 | import logging 6 | from urllib import parse 7 | import urllib.parse 8 | 9 | from binance_f.base.printtime import PrintDate 10 | from binance_f.impl.utils.timeservice import get_current_timestamp 11 | from binance_f.impl.utils.urlparamsbuilder import UrlParamsBuilder 12 | from binance_f.impl.utils.apisignature import create_signature 13 | from binance_f.exception.binanceapiexception import BinanceApiException 14 | from binance_f.impl.utils import * 15 | from binance_f.base.printobject import * 16 | from binance_f.model.constant import * 17 | # Key: ws, Value: connection 18 | websocket_connection_handler = dict() 19 | 20 | 21 | def on_message(ws, message): 22 | websocket_connection = websocket_connection_handler[ws] 23 | websocket_connection.on_message(message) 24 | return 25 | 26 | 27 | def on_error(ws, error): 28 | websocket_connection = websocket_connection_handler[ws] 29 | websocket_connection.on_failure(error) 30 | 31 | 32 | def on_close(ws): 33 | websocket_connection = websocket_connection_handler[ws] 34 | websocket_connection.on_close() 35 | 36 | 37 | def on_open(ws): 38 | websocket_connection = websocket_connection_handler[ws] 39 | websocket_connection.on_open(ws) 40 | 41 | 42 | connection_id = 0 43 | 44 | 45 | class ConnectionState: 46 | IDLE = 0 47 | CONNECTED = 1 48 | CLOSED_ON_ERROR = 2 49 | 50 | 51 | def websocket_func(*args): 52 | connection_instance = args[0] 53 | connection_instance.ws = websocket.WebSocketApp(connection_instance.url, 54 | on_message=on_message, 55 | on_error=on_error, 56 | on_close=on_close) 57 | global websocket_connection_handler 58 | websocket_connection_handler[connection_instance.ws] = connection_instance 59 | connection_instance.logger.info("[Sub][" + str(connection_instance.id) + "] Connecting...") 60 | connection_instance.delay_in_second = -1 61 | connection_instance.ws.on_open = on_open 62 | connection_instance.ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE}) 63 | connection_instance.logger.info("[Sub][" + str(connection_instance.id) + "] Connection event loop down") 64 | if connection_instance.state == ConnectionState.CONNECTED: 65 | connection_instance.state = ConnectionState.IDLE 66 | 67 | 68 | class WebsocketConnection: 69 | 70 | def __init__(self, api_key, secret_key, uri, watch_dog, request): 71 | self.__thread = None 72 | self.url = uri 73 | self.__api_key = api_key 74 | self.__secret_key = secret_key 75 | self.request = request 76 | self.__watch_dog = watch_dog 77 | self.delay_in_second = -1 78 | self.ws = None 79 | self.last_receive_time = 0 80 | self.logger = logging.getLogger("binance-futures") 81 | self.state = ConnectionState.IDLE 82 | global connection_id 83 | connection_id += 1 84 | self.id = connection_id 85 | 86 | def in_delay_connection(self): 87 | return self.delay_in_second != -1 88 | 89 | def re_connect_in_delay(self, delay_in_second): 90 | if self.ws is not None: 91 | self.ws.close() 92 | self.ws = None 93 | self.delay_in_second = delay_in_second 94 | self.logger.warning("[Sub][" + str(self.id) + "] Reconnecting after " 95 | + str(self.delay_in_second) + " seconds later") 96 | 97 | def re_connect(self): 98 | if self.delay_in_second != 0: 99 | self.delay_in_second -= 1 100 | self.logger.warning("In delay connection: " + str(self.delay_in_second)) 101 | else: 102 | self.connect() 103 | 104 | def connect(self): 105 | if self.state == ConnectionState.CONNECTED: 106 | self.logger.info("[Sub][" + str(self.id) + "] Already connected") 107 | else: 108 | self.__thread = threading.Thread(target=websocket_func, args=[self]) 109 | self.__thread.start() 110 | 111 | def send(self, data): 112 | self.ws.send(data) 113 | 114 | def close(self): 115 | self.ws.close() 116 | del websocket_connection_handler[self.ws] 117 | self.__watch_dog.on_connection_closed(self) 118 | self.logger.error("[Sub][" + str(self.id) + "] Closing normally") 119 | 120 | def on_open(self, ws): 121 | self.logger.info("[Sub][" + str(self.id) + "] Connected to server") 122 | self.ws = ws 123 | self.last_receive_time = get_current_timestamp() 124 | self.state = ConnectionState.CONNECTED 125 | self.__watch_dog.on_connection_created(self) 126 | if self.request.subscription_handler is not None: 127 | self.request.subscription_handler(self) 128 | return 129 | 130 | def on_error(self, error_message): 131 | if self.request.error_handler is not None: 132 | print('error') 133 | exception = BinanceApiException(BinanceApiException.SUBSCRIPTION_ERROR, error_message) 134 | self.request.error_handler(exception) 135 | self.logger.error("[Sub][" + str(self.id) + "] " + str(error_message)) 136 | 137 | def on_failure(self, error): 138 | print('on_failure') 139 | self.on_error("Unexpected error: " + str(error)) 140 | self.close_on_error() 141 | 142 | def on_message(self, message): 143 | self.last_receive_time = get_current_timestamp() 144 | json_wrapper = parse_json_from_string(message) 145 | 146 | if json_wrapper.contain_key("status") and json_wrapper.get_string("status") != "ok": 147 | error_code = json_wrapper.get_string_or_default("err-code", "Unknown error") 148 | error_msg = json_wrapper.get_string_or_default("err-msg", "Unknown error") 149 | self.on_error(error_code + ": " + error_msg) 150 | elif json_wrapper.contain_key("err-code") and json_wrapper.get_int("err-code") != 0: 151 | error_code = json_wrapper.get_string_or_default("err-code", "Unknown error") 152 | error_msg = json_wrapper.get_string_or_default("err-msg", "Unknown error") 153 | self.on_error(error_code + ": " + error_msg) 154 | elif json_wrapper.contain_key("result") and json_wrapper.contain_key("id"): 155 | self.__on_receive_response(json_wrapper) 156 | else: 157 | self.__on_receive_payload(json_wrapper) 158 | 159 | def __on_receive_response(self, json_wrapper): 160 | res = None 161 | try: 162 | res = json_wrapper.get_int("id") 163 | except Exception as e: 164 | self.on_error("Failed to parse server's response: " + str(e)) 165 | 166 | try: 167 | if self.request.update_callback is not None: 168 | self.request.update_callback(SubscribeMessageType.RESPONSE, res) 169 | except Exception as e: 170 | self.on_error("Process error: " + str(e) 171 | + " You should capture the exception in your error handler") 172 | 173 | def __on_receive_payload(self, json_wrapper): 174 | res = None 175 | try: 176 | if self.request.json_parser is not None: 177 | res = self.request.json_parser(json_wrapper) 178 | except Exception as e: 179 | self.on_error("Failed to parse server's response: " + str(e)) 180 | 181 | try: 182 | if self.request.update_callback is not None: 183 | self.request.update_callback(SubscribeMessageType.PAYLOAD, res) 184 | except Exception as e: 185 | self.on_error("Process error: " + str(e) 186 | + " You should capture the exception in your error handler") 187 | 188 | if self.request.auto_close: 189 | self.close() 190 | 191 | def __process_ping_on_trading_line(self, ping_ts): 192 | self.send("{\"op\":\"pong\",\"ts\":" + str(ping_ts) + "}") 193 | return 194 | 195 | def __process_ping_on_market_line(self, ping_ts): 196 | self.send("{\"pong\":" + str(ping_ts) + "}") 197 | return 198 | 199 | def close_on_error(self): 200 | if self.ws is not None: 201 | self.ws.close() 202 | self.state = ConnectionState.CLOSED_ON_ERROR 203 | self.logger.error("[Sub][" + str(self.id) + "] Connection is closing due to error") 204 | -------------------------------------------------------------------------------- /binance_f/impl/websocketrequest.py: -------------------------------------------------------------------------------- 1 | class WebsocketRequest(object): 2 | 3 | def __init__(self): 4 | self.subscription_handler = None 5 | self.auto_close = False # close connection after receive data, for subscribe set False, for request set True 6 | self.is_trading = False 7 | self.error_handler = None 8 | self.json_parser = None 9 | self.update_callback = None 10 | -------------------------------------------------------------------------------- /binance_f/impl/websocketrequestimpl.py: -------------------------------------------------------------------------------- 1 | import time 2 | from binance_f.impl.websocketrequest import WebsocketRequest 3 | from binance_f.impl.utils.channels import * 4 | from binance_f.impl.utils.channelparser import ChannelParser 5 | from binance_f.impl.utils.timeservice import * 6 | from binance_f.impl.utils.inputchecker import * 7 | from binance_f.model import * 8 | # For develop 9 | from binance_f.base.printobject import * 10 | 11 | 12 | class WebsocketRequestImpl(object): 13 | 14 | def __init__(self, api_key): 15 | self.__api_key = api_key 16 | 17 | def subscribe_aggregate_trade_event(self, symbol, callback, error_handler=None): 18 | check_should_not_none(symbol, "symbol") 19 | check_should_not_none(callback, "callback") 20 | 21 | def subscription_handler(connection): 22 | connection.send(aggregate_trade_channel(symbol)) 23 | time.sleep(0.01) 24 | 25 | def json_parse(json_wrapper): 26 | result = AggregateTradeEvent.json_parse(json_wrapper) 27 | return result 28 | 29 | request = WebsocketRequest() 30 | request.subscription_handler = subscription_handler 31 | request.json_parser = json_parse 32 | request.update_callback = callback 33 | request.error_handler = error_handler 34 | 35 | return request 36 | 37 | def subscribe_mark_price_event(self, symbol, callback, error_handler=None): 38 | check_should_not_none(symbol, "symbol") 39 | check_should_not_none(callback, "callback") 40 | 41 | def subscription_handler(connection): 42 | connection.send(mark_price_channel(symbol)) 43 | time.sleep(0.01) 44 | 45 | def json_parse(json_wrapper): 46 | result = MarkPriceEvent.json_parse(json_wrapper) 47 | return result 48 | 49 | request = WebsocketRequest() 50 | request.subscription_handler = subscription_handler 51 | request.json_parser = json_parse 52 | request.update_callback = callback 53 | request.error_handler = error_handler 54 | 55 | return request 56 | 57 | def subscribe_candlestick_event(self, symbol, interval, callback, error_handler=None): 58 | check_should_not_none(symbol, "symbol") 59 | check_should_not_none(interval, "interval") 60 | check_should_not_none(callback, "callback") 61 | 62 | def subscription_handler(connection): 63 | connection.send(kline_channel(symbol, interval)) 64 | time.sleep(0.01) 65 | 66 | def json_parse(json_wrapper): 67 | result = CandlestickEvent.json_parse(json_wrapper) 68 | return result 69 | 70 | request = WebsocketRequest() 71 | request.subscription_handler = subscription_handler 72 | request.json_parser = json_parse 73 | request.update_callback = callback 74 | request.error_handler = error_handler 75 | 76 | return request 77 | 78 | def subscribe_symbol_miniticker_event(self, symbol, callback, error_handler=None): 79 | check_should_not_none(symbol, "symbol") 80 | check_should_not_none(callback, "callback") 81 | 82 | def subscription_handler(connection): 83 | connection.send(symbol_miniticker_channel(symbol)) 84 | time.sleep(0.01) 85 | 86 | def json_parse(json_wrapper): 87 | result = SymbolMiniTickerEvent.json_parse(json_wrapper) 88 | return result 89 | 90 | request = WebsocketRequest() 91 | request.subscription_handler = subscription_handler 92 | request.json_parser = json_parse 93 | request.update_callback = callback 94 | request.error_handler = error_handler 95 | 96 | return request 97 | 98 | def subscribe_all_miniticker_event(self, callback, error_handler=None): 99 | check_should_not_none(callback, "callback") 100 | 101 | def subscription_handler(connection): 102 | connection.send(all_miniticker_channel()) 103 | time.sleep(0.01) 104 | 105 | def json_parse(json_wrapper): 106 | result = list() 107 | data_list = json_wrapper.convert_2_array() 108 | for item in data_list.get_items(): 109 | element = SymbolMiniTickerEvent.json_parse(item) 110 | result.append(element) 111 | return result 112 | 113 | request = WebsocketRequest() 114 | request.subscription_handler = subscription_handler 115 | request.json_parser = json_parse 116 | request.update_callback = callback 117 | request.error_handler = error_handler 118 | 119 | return request 120 | 121 | def subscribe_symbol_ticker_event(self, symbol, callback, error_handler=None): 122 | check_should_not_none(symbol, "symbol") 123 | check_should_not_none(callback, "callback") 124 | 125 | def subscription_handler(connection): 126 | connection.send(symbol_ticker_channel(symbol)) 127 | time.sleep(0.01) 128 | 129 | def json_parse(json_wrapper): 130 | result = SymbolTickerEvent.json_parse(json_wrapper) 131 | return result 132 | 133 | request = WebsocketRequest() 134 | request.subscription_handler = subscription_handler 135 | request.json_parser = json_parse 136 | request.update_callback = callback 137 | request.error_handler = error_handler 138 | 139 | return request 140 | 141 | def subscribe_all_ticker_event(self, callback, error_handler=None): 142 | check_should_not_none(callback, "callback") 143 | 144 | def subscription_handler(connection): 145 | connection.send(all_ticker_channel()) 146 | time.sleep(0.01) 147 | 148 | def json_parse(json_wrapper): 149 | result = list() 150 | data_list = json_wrapper.convert_2_array() 151 | for item in data_list.get_items(): 152 | ticker_event_obj = SymbolTickerEvent.json_parse(item) 153 | result.append(ticker_event_obj) 154 | return result 155 | 156 | request = WebsocketRequest() 157 | request.subscription_handler = subscription_handler 158 | request.json_parser = json_parse 159 | request.update_callback = callback 160 | request.error_handler = error_handler 161 | 162 | return request 163 | 164 | def subscribe_symbol_bookticker_event(self, symbol, callback, error_handler=None): 165 | check_should_not_none(symbol, "symbol") 166 | check_should_not_none(callback, "callback") 167 | 168 | def subscription_handler(connection): 169 | connection.send(symbol_bookticker_channel(symbol)) 170 | time.sleep(0.01) 171 | 172 | def json_parse(json_wrapper): 173 | result = SymbolBookTickerEvent.json_parse(json_wrapper) 174 | return result 175 | 176 | request = WebsocketRequest() 177 | request.subscription_handler = subscription_handler 178 | request.json_parser = json_parse 179 | request.update_callback = callback 180 | request.error_handler = error_handler 181 | 182 | return request 183 | 184 | def subscribe_all_bookticker_event(self, callback, error_handler=None): 185 | check_should_not_none(callback, "callback") 186 | 187 | def subscription_handler(connection): 188 | connection.send(all_bookticker_channel()) 189 | time.sleep(0.01) 190 | 191 | def json_parse(json_wrapper): 192 | result = SymbolBookTickerEvent.json_parse(json_wrapper) 193 | return result 194 | 195 | request = WebsocketRequest() 196 | request.subscription_handler = subscription_handler 197 | request.json_parser = json_parse 198 | request.update_callback = callback 199 | request.error_handler = error_handler 200 | 201 | return request 202 | 203 | def subscribe_symbol_liquidation_event(self, symbol, callback, error_handler=None): 204 | check_should_not_none(symbol, "symbol") 205 | check_should_not_none(callback, "callback") 206 | 207 | def subscription_handler(connection): 208 | connection.send(symbol_liquidation_channel(symbol)) 209 | time.sleep(0.01) 210 | 211 | def json_parse(json_wrapper): 212 | result = LiquidationOrderEvent.json_parse(json_wrapper) 213 | return result 214 | 215 | request = WebsocketRequest() 216 | request.subscription_handler = subscription_handler 217 | request.json_parser = json_parse 218 | request.update_callback = callback 219 | request.error_handler = error_handler 220 | 221 | return request 222 | 223 | def subscribe_all_liquidation_event(self, callback, error_handler=None): 224 | check_should_not_none(callback, "callback") 225 | 226 | def subscription_handler(connection): 227 | connection.send(all_liquidation_channel()) 228 | time.sleep(0.01) 229 | 230 | def json_parse(json_wrapper): 231 | result = LiquidationOrderEvent.json_parse(json_wrapper) 232 | return result 233 | 234 | request = WebsocketRequest() 235 | request.subscription_handler = subscription_handler 236 | request.json_parser = json_parse 237 | request.update_callback = callback 238 | request.error_handler = error_handler 239 | 240 | return request 241 | 242 | def subscribe_book_depth_event(self, symbol, limit, update_time, callback, error_handler=None): 243 | check_should_not_none(symbol, "symbol") 244 | check_should_not_none(limit, "limit") 245 | check_should_not_none(callback, "callback") 246 | #print(update_time) 247 | def subscription_handler(connection): 248 | connection.send(book_depth_channel(symbol, limit, update_time)) 249 | time.sleep(0.01) 250 | 251 | def json_parse(json_wrapper): 252 | result = OrderBookEvent.json_parse(json_wrapper) 253 | return result 254 | 255 | request = WebsocketRequest() 256 | request.subscription_handler = subscription_handler 257 | request.json_parser = json_parse 258 | request.update_callback = callback 259 | request.error_handler = error_handler 260 | 261 | return request 262 | 263 | def subscribe_diff_depth_event(self, symbol, update_time, callback, error_handler=None): 264 | check_should_not_none(symbol, "symbol") 265 | check_should_not_none(callback, "callback") 266 | 267 | def subscription_handler(connection): 268 | connection.send(diff_depth_channel(symbol, update_time)) 269 | time.sleep(0.01) 270 | 271 | def json_parse(json_wrapper): 272 | result = DiffDepthEvent.json_parse(json_wrapper) 273 | return result 274 | 275 | request = WebsocketRequest() 276 | request.subscription_handler = subscription_handler 277 | request.json_parser = json_parse 278 | request.update_callback = callback 279 | request.error_handler = error_handler 280 | 281 | return request 282 | 283 | 284 | def subscribe_user_data_event(self, listenKey, callback, error_handler=None): 285 | check_should_not_none(listenKey, "listenKey") 286 | check_should_not_none(callback, "callback") 287 | 288 | def subscription_handler(connection): 289 | connection.send(user_data_channel(listenKey)) 290 | time.sleep(0.01) 291 | 292 | def json_parse(json_wrapper): 293 | print("event type: ", json_wrapper.get_string("e")) 294 | print(json_wrapper) 295 | if(json_wrapper.get_string("e") == "ACCOUNT_UPDATE"): 296 | result = AccountUpdate.json_parse(json_wrapper) 297 | elif(json_wrapper.get_string("e") == "ORDER_TRADE_UPDATE"): 298 | result = OrderUpdate.json_parse(json_wrapper) 299 | elif(json_wrapper.get_string("e") == "listenKeyExpired"): 300 | result = ListenKeyExpired.json_parse(json_wrapper) 301 | return result 302 | 303 | request = WebsocketRequest() 304 | request.subscription_handler = subscription_handler 305 | request.json_parser = json_parse 306 | request.update_callback = callback 307 | request.error_handler = error_handler 308 | 309 | return request 310 | 311 | 312 | -------------------------------------------------------------------------------- /binance_f/impl/websocketwatchdog.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import logging 3 | from apscheduler.schedulers.blocking import BlockingScheduler 4 | from binance_f.impl.websocketconnection import ConnectionState 5 | from binance_f.impl.utils.timeservice import get_current_timestamp 6 | 7 | 8 | def watch_dog_job(*args): 9 | watch_dog_instance = args[0] 10 | for connection in watch_dog_instance.connection_list: 11 | if connection.state == ConnectionState.CONNECTED: 12 | if watch_dog_instance.is_auto_connect: 13 | ts = get_current_timestamp() - connection.last_receive_time 14 | if ts > watch_dog_instance.receive_limit_ms: 15 | watch_dog_instance.logger.warning("[Sub][" + str(connection.id) + "] No response from server") 16 | connection.re_connect_in_delay(watch_dog_instance.connection_delay_failure) 17 | elif connection.in_delay_connection(): 18 | watch_dog_instance.logger.warning("[Sub] call re_connect") 19 | connection.re_connect() 20 | pass 21 | elif connection.state == ConnectionState.CLOSED_ON_ERROR: 22 | if watch_dog_instance.is_auto_connect: 23 | connection.re_connect_in_delay(watch_dog_instance.connection_delay_failure) 24 | pass 25 | 26 | 27 | class WebSocketWatchDog(threading.Thread): 28 | mutex = threading.Lock() 29 | connection_list = list() 30 | 31 | def __init__(self, is_auto_connect=True, receive_limit_ms=60000, connection_delay_failure=15): 32 | threading.Thread.__init__(self) 33 | self.is_auto_connect = is_auto_connect 34 | self.receive_limit_ms = receive_limit_ms 35 | self.connection_delay_failure = connection_delay_failure 36 | self.logger = logging.getLogger("binance-client") 37 | self.scheduler = BlockingScheduler() 38 | self.scheduler.add_job(watch_dog_job, "interval", max_instances=10, seconds=1, args=[self]) 39 | self.start() 40 | 41 | def run(self): 42 | self.scheduler.start() 43 | 44 | def on_connection_created(self, connection): 45 | self.mutex.acquire() 46 | self.connection_list.append(connection) 47 | self.mutex.release() 48 | 49 | def on_connection_closed(self, connection): 50 | self.mutex.acquire() 51 | self.connection_list.remove(connection) 52 | self.mutex.release() 53 | -------------------------------------------------------------------------------- /binance_f/model/__init__.py: -------------------------------------------------------------------------------- 1 | from binance_f.model.constant import * 2 | from binance_f.model.message import Msg 3 | from binance_f.model.exchangeinformation import ExchangeInformation 4 | from binance_f.model.orderbook import OrderBook 5 | from binance_f.model.trade import Trade 6 | from binance_f.model.aggregatetrade import AggregateTrade 7 | from binance_f.model.candlestick import Candlestick 8 | from binance_f.model.markprice import MarkPrice 9 | from binance_f.model.openinterest import OpenInterest 10 | from binance_f.model.fundingrate import FundingRate 11 | from binance_f.model.tickerpricechangestatistics import TickerPriceChangeStatistics 12 | from binance_f.model.symbolprice import SymbolPrice 13 | from binance_f.model.symbolorderbook import SymbolOrderBook 14 | from binance_f.model.liquidationorder import LiquidationOrder 15 | from binance_f.model.aggregatetradeevent import AggregateTradeEvent 16 | from binance_f.model.markpriceevent import MarkPriceEvent 17 | from binance_f.model.candlestickevent import CandlestickEvent 18 | from binance_f.model.symbolminitickerevent import SymbolMiniTickerEvent 19 | from binance_f.model.symboltickerevent import SymbolTickerEvent 20 | from binance_f.model.symbolbooktickerevent import SymbolBookTickerEvent 21 | from binance_f.model.liquidationorderevent import LiquidationOrderEvent 22 | from binance_f.model.orderbookevent import OrderBookEvent 23 | from binance_f.model.diffdepthevent import DiffDepthEvent 24 | from binance_f.model.order import Order 25 | from binance_f.model.balance import Balance 26 | from binance_f.model.accountinformation import AccountInformation 27 | from binance_f.model.leverage import Leverage 28 | from binance_f.model.codeandmsg import CodeMsg 29 | from binance_f.model.positionmode import PositionMode 30 | from binance_f.model.positionmargin import PositionMargin 31 | from binance_f.model.positionmarginhistory import PositionMarginHist 32 | from binance_f.model.position import Position 33 | from binance_f.model.mytrade import MyTrade 34 | from binance_f.model.income import Income 35 | from binance_f.model.accountupdate import AccountUpdate 36 | from binance_f.model.orderupdate import OrderUpdate 37 | from binance_f.model.listenkeyexpired import ListenKeyExpired 38 | -------------------------------------------------------------------------------- /binance_f/model/accountinformation.py: -------------------------------------------------------------------------------- 1 | class Asset: 2 | 3 | def __init__(self): 4 | self.asset = "" 5 | self.initialMargin = 0.0 6 | self.maintMargin = 0.0 7 | self.marginBalance = 0.0 8 | self.maxWithdrawAmount = 0.0 9 | self.openOrderInitialMargin = 0.0 10 | self.positionInitialMargin = 0.0 11 | self.unrealizedProfit = 0.0 12 | self.walletBalance = 0.0 13 | 14 | @staticmethod 15 | def json_parse(json_data): 16 | result = Asset() 17 | result.asset = json_data.get_string("asset") 18 | result.initialMargin = json_data.get_float("initialMargin") 19 | result.maintMargin = json_data.get_float("maintMargin") 20 | result.marginBalance = json_data.get_float("marginBalance") 21 | result.maxWithdrawAmount = json_data.get_float("maxWithdrawAmount") 22 | result.openOrderInitialMargin = json_data.get_float("openOrderInitialMargin") 23 | result.positionInitialMargin = json_data.get_float("positionInitialMargin") 24 | result.unrealizedProfit = json_data.get_float("unrealizedProfit") 25 | return result 26 | 27 | 28 | class Position: 29 | 30 | def __init__(self): 31 | self.initialMargin = 0.0 32 | self.maintMargin = 0.0 33 | self.openOrderInitialMargin = 0.0 34 | self.positionInitialMargin = 0.0 35 | self.symbol = "" 36 | self.leverage = 0.0 37 | self.unrealizedProfit = 0.0 38 | self.isolated = False 39 | self.positionSide = "" 40 | 41 | @staticmethod 42 | def json_parse(json_data): 43 | result = Position() 44 | result.initialMargin = json_data.get_float("initialMargin") 45 | result.maintMargin = json_data.get_float("maintMargin") 46 | result.leverage = json_data.get_float("leverage") 47 | result.openOrderInitialMargin = json_data.get_float("openOrderInitialMargin") 48 | result.positionInitialMargin = json_data.get_float("positionInitialMargin") 49 | result.symbol = json_data.get_string("symbol") 50 | result.unrealizedProfit = json_data.get_float("unrealizedProfit") 51 | result.isolated = json_data.get_boolean("isolated") 52 | result.positionSide = json_data.get_string("positionSide") 53 | return result 54 | 55 | 56 | class AccountInformation: 57 | def __init__(self): 58 | self.canDeposit = False 59 | self.canTrade = False 60 | self.canWithdraw = False 61 | self.feeTier = 0 62 | self.maxWithdrawAmount = 0.0 63 | self.totalInitialMargin = 0.0 64 | self.totalMaintMargin = 0.0 65 | self.totalMarginBalance = 0.0 66 | self.totalOpenOrderInitialMargin = 0.0 67 | self.totalPositionInitialMargin = 0.0 68 | self.totalUnrealizedProfit = 0.0 69 | self.totalWalletBalance = 0.0 70 | self.updateTime = 0 71 | self.assets = list() 72 | self.positions = list() 73 | 74 | @staticmethod 75 | def json_parse(json_data): 76 | result = AccountInformation() 77 | result.canDeposit = json_data.get_boolean("canDeposit") 78 | result.canTrade = json_data.get_boolean("canTrade") 79 | result.canWithdraw = json_data.get_boolean("canWithdraw") 80 | result.feeTier = json_data.get_float("feeTier") 81 | result.maxWithdrawAmount = json_data.get_float("maxWithdrawAmount") 82 | result.totalInitialMargin = json_data.get_float("totalInitialMargin") 83 | result.totalMaintMargin = json_data.get_float("totalMaintMargin") 84 | result.totalMarginBalance = json_data.get_float("totalMarginBalance") 85 | result.totalOpenOrderInitialMargin = json_data.get_float("totalOpenOrderInitialMargin") 86 | result.totalPositionInitialMargin = json_data.get_float("totalPositionInitialMargin") 87 | result.totalUnrealizedProfit = json_data.get_float("totalUnrealizedProfit") 88 | result.totalWalletBalance = json_data.get_float("totalWalletBalance") 89 | result.updateTime = json_data.get_int("updateTime") 90 | 91 | element_list = list() 92 | data_list = json_data.get_array("assets") 93 | for item in data_list.get_items(): 94 | element = Asset.json_parse(item) 95 | element_list.append(element) 96 | result.assets = element_list 97 | 98 | element_list = list() 99 | data_list = json_data.get_array("positions") 100 | for item in data_list.get_items(): 101 | element = Position.json_parse(item) 102 | element_list.append(element) 103 | result.positions = element_list 104 | 105 | return result 106 | -------------------------------------------------------------------------------- /binance_f/model/accountupdate.py: -------------------------------------------------------------------------------- 1 | class Balance: 2 | 3 | def __init__(self): 4 | self.asset = "" 5 | self.walletBalance = 0.0 6 | self.crossWallet = 0.0 7 | 8 | @staticmethod 9 | def json_parse(json_data): 10 | result = Balance() 11 | result.asset = json_data.get_string("a") 12 | result.walletBalance = json_data.get_float("wb") 13 | result.crossWallet = json_data.get_float("cw") 14 | return result 15 | 16 | 17 | class Position: 18 | 19 | def __init__(self): 20 | self.symbol = "" 21 | self.amount = 0.0 22 | self.entryPrice = 0.0 23 | self.preFee = 0.0 24 | self.unrealizedPnl = 0.0 25 | self.marginType = "" 26 | self.isolatedWallet = 0.0 27 | self.positionSide = "" 28 | 29 | @staticmethod 30 | def json_parse(json_data): 31 | result = Position() 32 | result.symbol = json_data.get_string("s") 33 | result.amount = json_data.get_float("pa") 34 | result.entryPrice = json_data.get_float("ep") 35 | result.preFee = json_data.get_float("cr") 36 | result.unrealizedPnl = json_data.get_float("up") 37 | result.marginType = json_data.get_string("mt") 38 | result.isolatedWallet = json_data.get_float("iw") 39 | result.positionSide = json_data.get_string("ps") 40 | return result 41 | 42 | 43 | class AccountUpdate: 44 | def __init__(self): 45 | self.eventType = "" 46 | self.eventTime = 0 47 | self.transactionTime = 0 48 | self.balances = list() 49 | self.positions = list() 50 | 51 | @staticmethod 52 | def json_parse(json_data): 53 | result = AccountUpdate() 54 | result.eventType = json_data.get_string("e") 55 | result.eventTime = json_data.get_int("E") 56 | result.transactionTime = json_data.get_int("T") 57 | 58 | data_group = json_data.get_object("a") 59 | 60 | element_list = list() 61 | data_list = data_group.get_array("B") 62 | for item in data_list.get_items(): 63 | element = Balance.json_parse(item) 64 | element_list.append(element) 65 | result.balances = element_list 66 | 67 | if data_group.contain_key("P"): 68 | element_list = list() 69 | data_list = data_group.get_array("P") 70 | for item in data_list.get_items(): 71 | element = Position.json_parse(item) 72 | element_list.append(element) 73 | result.positions = element_list 74 | return result 75 | -------------------------------------------------------------------------------- /binance_f/model/aggregatetrade.py: -------------------------------------------------------------------------------- 1 | class AggregateTrade: 2 | 3 | def __init__(self): 4 | self.id = None 5 | self.price = 0.0 6 | self.qty = 0.0 7 | self.firstId = None 8 | self.lastId = None 9 | self.time = 0 10 | self.isBuyerMaker = False 11 | 12 | @staticmethod 13 | def json_parse(json_data): 14 | trade = AggregateTrade() 15 | trade.id = json_data.get_int("a") 16 | trade.price = json_data.get_float("p") 17 | trade.qty = json_data.get_float("q") 18 | trade.firstId = json_data.get_int("f") 19 | trade.lastId = json_data.get_int("l") 20 | trade.time = json_data.get_int("T") 21 | trade.isBuyerMaker = json_data.get_boolean("m") 22 | 23 | return trade -------------------------------------------------------------------------------- /binance_f/model/aggregatetradeevent.py: -------------------------------------------------------------------------------- 1 | class AggregateTradeEvent: 2 | 3 | def __init__(self): 4 | self.eventType = "" 5 | self.eventTime = 0 6 | self.symbol = "" 7 | self.id = None 8 | self.price = 0.0 9 | self.qty = 0.0 10 | self.firstId = None 11 | self.lastId = None 12 | self.time = 0 13 | self.isBuyerMaker = False 14 | 15 | @staticmethod 16 | def json_parse(json_wrapper): 17 | result = AggregateTradeEvent() 18 | result.eventType = json_wrapper.get_string("e") 19 | result.eventTime = json_wrapper.get_int("E") 20 | result.symbol = json_wrapper.get_string("s") 21 | result.id = json_wrapper.get_int("a") 22 | result.price = json_wrapper.get_float("p") 23 | result.qty = json_wrapper.get_float("q") 24 | result.firstId = json_wrapper.get_int("f") 25 | result.lastId = json_wrapper.get_int("l") 26 | result.time = json_wrapper.get_int("T") 27 | result.isBuyerMaker = json_wrapper.get_boolean("m") 28 | return result -------------------------------------------------------------------------------- /binance_f/model/balance.py: -------------------------------------------------------------------------------- 1 | class Balance: 2 | 3 | def __init__(self): 4 | self.asset = "" 5 | self.accountAlias = "" 6 | self.balance = 0.0 7 | self.withdrawAvailable = 0.0 8 | 9 | @staticmethod 10 | def json_parse(json_data): 11 | result = Balance() 12 | result.asset = json_data.get_string("asset") 13 | result.accountAlias = json_data.get_string("accountAlias") 14 | result.balance = json_data.get_float("balance") 15 | result.withdrawAvailable = json_data.get_float("withdrawAvailable") 16 | 17 | return result -------------------------------------------------------------------------------- /binance_f/model/candlestick.py: -------------------------------------------------------------------------------- 1 | class Candlestick: 2 | 3 | def __init__(self): 4 | self.openTime = 0 5 | self.open = 0.0 6 | self.high = 0.0 7 | self.low = 0.0 8 | self.close = 0.0 9 | self.volume = 0.0 10 | self.closeTime = 0 11 | self.quoteAssetVolume = 0.0 12 | self.numTrades = 0 13 | self.takerBuyBaseAssetVolume = 0.0 14 | self.takerBuyQuoteAssetVolume = 0.0 15 | self.ignore = 0.0 16 | 17 | @staticmethod 18 | def json_parse(json_data): 19 | result = Candlestick() 20 | val = json_data.convert_2_list() 21 | result.openTime = val[0] 22 | result.open = val[1] 23 | result.high = val[2] 24 | result.low = val[3] 25 | result.close = val[4] 26 | result.volume = val[5] 27 | result.closeTime = val[6] 28 | result.quoteAssetVolume = val[7] 29 | result.numTrades = val[8] 30 | result.takerBuyBaseAssetVolume = val[9] 31 | result.takerBuyQuoteAssetVolume = val[10] 32 | result.ignore = val[11] 33 | 34 | return result -------------------------------------------------------------------------------- /binance_f/model/candlestickevent.py: -------------------------------------------------------------------------------- 1 | class Candlestick: 2 | 3 | def __init__(self): 4 | self.startTime = 0 5 | self.closeTime = 0 6 | self.symbol = "" 7 | self.interval = "" 8 | self.firstTradeId = 0 9 | self.lastTradeId = 0 10 | self.open = 0.0 11 | self.close = 0.0 12 | self.high = 0.0 13 | self.low = 0.0 14 | self.volume = 0.0 15 | self.numTrades = 0 16 | self.isClosed = False 17 | self.quoteAssetVolume = 0.0 18 | self.takerBuyBaseAssetVolume = 0.0 19 | self.takerBuyQuoteAssetVolume = 0.0 20 | self.ignore = 0.0 21 | 22 | @staticmethod 23 | def json_parse(json_data): 24 | data_obj = Candlestick() 25 | data_obj.startTime = json_data.get_int("t") 26 | data_obj.closeTime = json_data.get_int("T") 27 | data_obj.symbol = json_data.get_string("s") 28 | data_obj.interval = json_data.get_string("i") 29 | data_obj.firstTradeId = json_data.get_int("f") 30 | data_obj.lastTradeId = json_data.get_int("L") 31 | data_obj.open = json_data.get_float("o") 32 | data_obj.close = json_data.get_float("c") 33 | data_obj.high = json_data.get_float("h") 34 | data_obj.low = json_data.get_float("l") 35 | data_obj.volume = json_data.get_float("v") 36 | data_obj.numTrades = json_data.get_int("n") 37 | data_obj.isClosed = json_data.get_boolean("x") 38 | data_obj.quoteAssetVolume = json_data.get_float("q") 39 | data_obj.takerBuyBaseAssetVolume = json_data.get_float("V") 40 | data_obj.takerBuyQuoteAssetVolume = json_data.get_float("Q") 41 | data_obj.ignore = json_data.get_int("B") 42 | 43 | return data_obj 44 | 45 | 46 | class CandlestickEvent: 47 | 48 | def __init__(self): 49 | self.eventType = "" 50 | self.eventTime = 0 51 | self.symbol = "" 52 | self.data = Candlestick() 53 | 54 | @staticmethod 55 | def json_parse(json_wrapper): 56 | candlestick_event = CandlestickEvent() 57 | candlestick_event.eventType = json_wrapper.get_string("e") 58 | candlestick_event.eventTime = json_wrapper.get_int("E") 59 | candlestick_event.symbol = json_wrapper.get_string("s") 60 | data = Candlestick.json_parse(json_wrapper.get_object("k")) 61 | candlestick_event.data = data 62 | return candlestick_event 63 | 64 | -------------------------------------------------------------------------------- /binance_f/model/codeandmsg.py: -------------------------------------------------------------------------------- 1 | class CodeMsg: 2 | 3 | def __init__(self): 4 | self.code = 0 5 | self.msg = "" 6 | 7 | @staticmethod 8 | def json_parse(json_data): 9 | result = CodeMsg() 10 | result.code = json_data.get_int("code") 11 | result.msg = json_data.get_string("msg") 12 | 13 | return result 14 | -------------------------------------------------------------------------------- /binance_f/model/constant.py: -------------------------------------------------------------------------------- 1 | class CandlestickInterval: 2 | MIN1 = "1m" 3 | MIN3 = "3m" 4 | MIN5 = "5m" 5 | MIN15 = "15m" 6 | MIN30 = "30m" 7 | HOUR1 = "1h" 8 | HOUR2 = "2h" 9 | HOUR4 = "4h" 10 | HOUR6 = "6h" 11 | HOUR8 = "8h" 12 | HOUR12 = "12h" 13 | DAY1 = "1d" 14 | DAY3 = "3d" 15 | WEEK1 = "1w" 16 | MON1 = "1m" 17 | INVALID = None 18 | 19 | 20 | class OrderSide: 21 | BUY = "BUY" 22 | SELL = "SELL" 23 | INVALID = None 24 | 25 | 26 | class TimeInForce: 27 | ''' 28 | GTC - Good Till Cancel 成交为止 29 | IOC - Immediate or Cancel 无法立即成交(吃单)的部分就撤销 30 | FOK - Fill or Kill 无法全部立即成交就撤销 31 | GTX - Good Till Crossing 无法成为挂单方就撤销 32 | ''' 33 | GTC = "GTC" 34 | IOC = "IOC" 35 | FOK = "FOK" 36 | GTX = "GTX" 37 | INVALID = None 38 | 39 | 40 | class TradeDirection: 41 | BUY = "buy" 42 | SELL = "sell" 43 | INVALID = None 44 | 45 | 46 | class OrderType: 47 | LIMIT = "LIMIT" 48 | MARKET = "MARKET" 49 | STOP = "STOP" 50 | STOP_MARKET = "STOP_MARKET" 51 | TAKE_PROFIT = "TAKE_PROFIT" 52 | TAKE_PROFIT_MARKET = "TAKE_PROFIT_MARKET" 53 | TRAILING_STOP_MARKET = "TRAILING_STOP_MARKET" 54 | INVALID = None 55 | 56 | class OrderRespType: 57 | ACK = "ACK" 58 | RESULT = "RESULT" 59 | INVALID = None 60 | 61 | 62 | class MatchRole: 63 | MAKER = "maker" 64 | TAKER = "taker" 65 | 66 | class DepthStep: 67 | STEP0 = "step0" 68 | STEP1 = "step1" 69 | STEP2 = "step2" 70 | STEP3 = "step3" 71 | STEP4 = "step4" 72 | STEP5 = "step5" 73 | 74 | 75 | class SubscribeMessageType: 76 | RESPONSE = "response" 77 | PAYLOAD = "payload" 78 | 79 | 80 | class TransferType: 81 | ROLL_IN = "ROLL_IN" 82 | ROLL_OUT = "ROLL_OUT" 83 | INVALID = None 84 | 85 | class WorkingType: 86 | MARK_PRICE = "MARK_PRICE" 87 | CONTRACT_PRICE = "CONTRACT_PRICE" 88 | INVALID = None 89 | 90 | 91 | class FuturesMarginType: 92 | ISOLATED = "ISOLATED" 93 | CROSSED = "CROSSED" 94 | 95 | class PositionSide: 96 | BOTH = "BOTH" 97 | LONG = "LONG" 98 | SHORT = "SHORT" 99 | INVALID = None 100 | 101 | 102 | 103 | class IncomeType: 104 | TRANSFER = "TRANSFER" 105 | WELCOME_BONUS = "WELCOME_BONUS" 106 | REALIZED_PNL = "REALIZED_PNL" 107 | FUNDING_FEE = "FUNDING_FEE" 108 | COMMISSION = "COMMISSION" 109 | INSURANCE_CLEAR = "INSURANCE_CLEAR" 110 | INVALID = None 111 | 112 | class UpdateTime: 113 | NORMAL = "" 114 | FAST = "@100ms" 115 | REALTIME = "@0ms" 116 | INVALID = "" 117 | -------------------------------------------------------------------------------- /binance_f/model/diffdepthevent.py: -------------------------------------------------------------------------------- 1 | class Order: 2 | 3 | def __init__(self): 4 | self.price = 0.0 5 | self.qty = 0.0 6 | 7 | 8 | class DiffDepthEvent: 9 | 10 | def __init__(self): 11 | self.eventType = "" 12 | self.eventTime = 0 13 | self.transactionTime = 0 14 | self.symbol = "" 15 | self.firstUpdateId = None 16 | self.finalUpdateId = None 17 | self.lastUpdateIdInlastStream = 0 18 | self.bids = list() 19 | self.asks = list() 20 | 21 | @staticmethod 22 | def json_parse(json_data): 23 | order_book = DiffDepthEvent() 24 | order_book.eventType = json_data.get_string("e") 25 | order_book.eventTime = json_data.get_int("E") 26 | order_book.transactionTime = json_data.get_int("T") 27 | order_book.symbol = json_data.get_string("s") 28 | order_book.firstUpdateId = json_data.get_int("U") 29 | order_book.finalUpdateId = json_data.get_int("u") 30 | order_book.lastUpdateIdInlastStream = json_data.get_int("pu") 31 | 32 | list_array = json_data.get_array("b") 33 | bid_list = list() 34 | for item in list_array.get_items(): 35 | order = Order() 36 | val = item.convert_2_list() 37 | order.price = val[0] 38 | order.qty = val[1] 39 | bid_list.append(order) 40 | order_book.bids = bid_list 41 | 42 | list_array = json_data.get_array("a") 43 | ask_list = list() 44 | for item in list_array.get_items(): 45 | order = Order() 46 | val = item.convert_2_list() 47 | order.price = val[0] 48 | order.qty = val[1] 49 | ask_list.append(order) 50 | order_book.asks = ask_list 51 | 52 | return order_book -------------------------------------------------------------------------------- /binance_f/model/exchangeinformation.py: -------------------------------------------------------------------------------- 1 | class RateLimit: 2 | 3 | def __init__(self): 4 | self.rateLimitType = "" 5 | self.interval = "" 6 | self.intervalNum = 0 7 | self.limit = 0 8 | 9 | 10 | class ExchangeFilter: 11 | 12 | def __init__(self): 13 | self.filterType = "" 14 | self.maxOrders = 0 15 | 16 | 17 | class Symbol: 18 | 19 | def __init__(self): 20 | self.symbol = "" 21 | self.status = "" 22 | self.maintMarginPercent = 0.0 23 | self.requiredMarginPercent = 0.0 24 | self.baseAsset = "" 25 | self.quoteAsset = "" 26 | self.pricePrecision = None 27 | self.quantityPrecision = None 28 | self.baseAssetPrecision = None 29 | self.quotePrecision = None 30 | self.orderTypes = list() 31 | self.timeInForce = list() 32 | self.filters = list() 33 | 34 | 35 | 36 | 37 | class ExchangeInformation: 38 | 39 | def __init__(self): 40 | self.timezone = "" 41 | self.serverTime = 0 42 | self.rateLimits = list() 43 | self.exchangeFilters = list() 44 | self.symbols = list() 45 | 46 | @staticmethod 47 | def json_parse(json_data): 48 | result = ExchangeInformation() 49 | result.timezone = json_data.get_string("timezone") 50 | result.serverTime = json_data.get_int("serverTime") 51 | 52 | data_list = json_data.get_array("rateLimits") 53 | element_list = list() 54 | for item in data_list.get_items(): 55 | element = RateLimit() 56 | element.rateLimitType = item.get_string("rateLimitType") 57 | element.interval = item.get_string("interval") 58 | element.intervalNum = item.get_int("intervalNum") 59 | element.limit = item.get_int("limit") 60 | 61 | element_list.append(element) 62 | result.rateLimits = element_list 63 | 64 | data_list = json_data.get_array("exchangeFilters") 65 | element_list = list() 66 | for item in data_list.get_items(): 67 | element = ExchangeFilter() 68 | element.filterType = item.get_string("filterType") 69 | if element.filterType == "EXCHANGE_MAX_NUM_ORDERS": 70 | element.maxNumOrders = item.get_int("maxNumOrders") 71 | elif element.filterType == "EXCHANGE_MAX_ALGO_ORDERS": 72 | element.maxNumAlgoOrders = item.get_int("maxNumAlgoOrders") 73 | 74 | element_list.append(element) 75 | result.exchangeFilters = element_list 76 | 77 | data_list = json_data.get_array("symbols") 78 | element_list = list() 79 | for item in data_list.get_items(): 80 | element = Symbol() 81 | element.symbol = item.get_string("symbol") 82 | element.status = item.get_string("status") 83 | element.maintMarginPercent = item.get_float("maintMarginPercent") 84 | element.requiredMarginPercent = item.get_float("requiredMarginPercent") 85 | element.baseAsset = item.get_string("baseAsset") 86 | element.quoteAsset = item.get_string("quoteAsset") 87 | element.pricePrecision = item.get_int("pricePrecision") 88 | element.quantityPrecision = item.get_int("quantityPrecision") 89 | element.baseAssetPrecision = item.get_int("baseAssetPrecision") 90 | element.quotePrecision = item.get_int("quotePrecision") 91 | element.orderTypes = item.get_object("orderTypes").convert_2_list() 92 | element.timeInForce = item.get_object("timeInForce").convert_2_list() 93 | 94 | val_list = item.get_array("filters") 95 | filter_list = list() 96 | for jtem in val_list.get_items(): 97 | filter_list.append(jtem.convert_2_dict()) 98 | element.filters = filter_list 99 | 100 | element_list.append(element) 101 | result.symbols = element_list 102 | 103 | return result 104 | 105 | -------------------------------------------------------------------------------- /binance_f/model/fundingrate.py: -------------------------------------------------------------------------------- 1 | class FundingRate: 2 | 3 | def __init__(self): 4 | self.symbol = "" 5 | self.fundingRate = 0.0 6 | self.fundingTime = 0 7 | 8 | @staticmethod 9 | def json_parse(json_data): 10 | result = FundingRate() 11 | result.symbol = json_data.get_string("symbol") 12 | result.fundingRate = json_data.get_float("fundingRate") 13 | result.fundingTime = json_data.get_int("fundingTime") 14 | 15 | return result 16 | -------------------------------------------------------------------------------- /binance_f/model/income.py: -------------------------------------------------------------------------------- 1 | class Income: 2 | 3 | def __init__(self): 4 | self.symbol = "" 5 | self.incomeType = "" 6 | self.income = 0.0 7 | self.asset = "" 8 | self.time = 0 9 | 10 | @staticmethod 11 | def json_parse(json_data): 12 | result = Income() 13 | result.symbol = json_data.get_string("symbol") 14 | result.incomeType = json_data.get_string("incomeType") 15 | result.income = json_data.get_float("income") 16 | result.asset = json_data.get_string("asset") 17 | result.time = json_data.get_int("time") 18 | 19 | return result 20 | -------------------------------------------------------------------------------- /binance_f/model/leverage.py: -------------------------------------------------------------------------------- 1 | class Leverage: 2 | 3 | def __init__(self): 4 | self.leverage = 0.0 5 | self.symbol = 0.0 6 | self.symbol = "" 7 | 8 | @staticmethod 9 | def json_parse(json_data): 10 | result = Leverage() 11 | result.leverage = json_data.get_float("leverage") 12 | result.maxNotionalValue = json_data.get_float("maxNotionalValue") 13 | result.symbol = json_data.get_string("symbol") 14 | 15 | return result -------------------------------------------------------------------------------- /binance_f/model/liquidationorder.py: -------------------------------------------------------------------------------- 1 | class LiquidationOrder: 2 | 3 | def __init__(self): 4 | self.symbol = "" 5 | self.price = 0.0 6 | self.origQty = 0.0 7 | self.executedQty = 0.0 8 | self.averagePrice = 0.0 9 | self.timeInForce = "" 10 | self.type = "" 11 | self.side = "" 12 | self.time = 0 13 | 14 | @staticmethod 15 | def json_parse(json_data): 16 | result = LiquidationOrder() 17 | result.symbol = json_data.get_string("symbol") 18 | result.price = json_data.get_float("price") 19 | result.origQty = json_data.get_float("origQty") 20 | result.executedQty = json_data.get_float("executedQty") 21 | result.averagePrice = json_data.get_float("averagePrice") 22 | result.timeInForce = json_data.get_string("timeInForce") 23 | result.type = json_data.get_string("symbol") 24 | result.side = json_data.get_string("side") 25 | result.time = json_data.get_int("time") 26 | 27 | return result 28 | -------------------------------------------------------------------------------- /binance_f/model/liquidationorderevent.py: -------------------------------------------------------------------------------- 1 | class LiquidationOrder: 2 | 3 | def __init__(self): 4 | self.symbol = "" 5 | self.side = "" 6 | self.type = "" 7 | self.timeInForce = "" 8 | self.origQty = 0.0 9 | self.price = 0.0 10 | self.averagePrice = 0.0 11 | self.orderStatus = "" 12 | self.lastFilledQty = 0.0 13 | self.lastFilledAccumulatedQty = 0.0 14 | self.time = 0 15 | 16 | 17 | class LiquidationOrderEvent: 18 | 19 | def __init__(self): 20 | self.eventType = "" 21 | self.eventTime = 0 22 | self.data = None 23 | 24 | @staticmethod 25 | def json_parse(json_wrapper): 26 | result = LiquidationOrderEvent() 27 | result.eventType = json_wrapper.get_string("e") 28 | result.eventTime = json_wrapper.get_int("E") 29 | data = json_wrapper.get_object("o") 30 | element = LiquidationOrder() 31 | element.symbol = data.get_string("s") 32 | element.side = data.get_string("S") 33 | element.type = data.get_string("o") 34 | element.timeInForce = data.get_string("f") 35 | element.origQty = data.get_float("q") 36 | element.price = data.get_float("p") 37 | element.averagePrice = data.get_float("ap") 38 | element.orderStatus = data.get_string("X") 39 | element.lastFilledQty = data.get_float("l") 40 | element.lastFilledAccumulatedQty = data.get_float("z") 41 | element.time = data.get_int("T") 42 | result.data = element 43 | return result -------------------------------------------------------------------------------- /binance_f/model/listenkeyexpired.py: -------------------------------------------------------------------------------- 1 | class ListenKeyExpired: 2 | def __init__(self): 3 | self.eventType = "" 4 | self.eventTime = 0 5 | 6 | @staticmethod 7 | def json_parse(json_data): 8 | result = ListenKeyExpired() 9 | result.eventType = json_data.get_string("e") 10 | result.eventTime = json_data.get_int("E") 11 | 12 | return result 13 | -------------------------------------------------------------------------------- /binance_f/model/markprice.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class MarkPrice: 4 | 5 | def __init__(self): 6 | self.symbol = "" 7 | self.markPrice = 0.0 8 | self.lastFundingRate = 0.0 9 | self.nextFundingTime = 0 10 | self.time = 0 11 | 12 | @staticmethod 13 | def json_parse(json_data): 14 | result = MarkPrice() 15 | result.symbol = json_data.get_string("symbol") 16 | result.markPrice = json_data.get_float("markPrice") 17 | result.lastFundingRate = json_data.get_float("lastFundingRate") 18 | result.nextFundingTime = json_data.get_int("nextFundingTime") 19 | result.time = json_data.get_int("time") 20 | 21 | return result 22 | -------------------------------------------------------------------------------- /binance_f/model/markpriceevent.py: -------------------------------------------------------------------------------- 1 | class MarkPriceEvent: 2 | 3 | def __init__(self): 4 | self.eventType = "" 5 | self.eventTime = 0 6 | self.symbol = "" 7 | self.markPrice = 0.0 8 | self.fundingRate = 0.0 9 | self.nextFundingTime = 0 10 | 11 | @staticmethod 12 | def json_parse(json_data): 13 | result = MarkPriceEvent() 14 | result.eventType = json_data.get_string("e") 15 | result.eventTime = json_data.get_int("E") 16 | result.symbol = json_data.get_string("s") 17 | result.markPrice = json_data.get_float("p") 18 | result.fundingRate = json_data.get_float("r") 19 | result.nextFundingTime = json_data.get_int("T") 20 | return result 21 | -------------------------------------------------------------------------------- /binance_f/model/message.py: -------------------------------------------------------------------------------- 1 | class Msg: 2 | 3 | def __init__(self): 4 | self.code = 0 5 | self.msg = "" 6 | 7 | @staticmethod 8 | def json_parse(json_data): 9 | result = Msg() 10 | result.code = json_data.get_int("code") 11 | result.msg = json_data.get_string("msg") 12 | 13 | return result 14 | -------------------------------------------------------------------------------- /binance_f/model/mytrade.py: -------------------------------------------------------------------------------- 1 | class MyTrade: 2 | 3 | def __init__(self): 4 | self.isBuyer = False 5 | self.commission = 0.0 6 | self.commissionAsset = "" 7 | self.counterPartyId = None 8 | self.id = None 9 | self.isMaker = False 10 | self.orderId = None 11 | self.price = 0.0 12 | self.qty = 0.0 13 | self.quoteQty = 0.0 14 | self.realizedPnl = 0.0 15 | self.side = "" 16 | self.symbol = "" 17 | self.time = 0 18 | 19 | @staticmethod 20 | def json_parse(json_data): 21 | result = MyTrade() 22 | result.isBuyer = json_data.get_boolean("buyer") 23 | result.commission = json_data.get_float("commission") 24 | result.commissionAsset = json_data.get_string("commissionAsset") 25 | result.counterPartyId = json_data.get_int_or_default("counterPartyId", None) 26 | result.id = json_data.get_int("id") 27 | result.isMaker = json_data.get_boolean("maker") 28 | result.orderId = json_data.get_int("orderId") 29 | result.price = json_data.get_float("price") 30 | result.qty = json_data.get_float("qty") 31 | result.quoteQty = json_data.get_float("quoteQty") 32 | result.realizedPnl = json_data.get_float("realizedPnl") 33 | result.side = json_data.get_string("side") 34 | result.symbol = json_data.get_string("symbol") 35 | result.time = json_data.get_int("time") 36 | 37 | return result -------------------------------------------------------------------------------- /binance_f/model/openinterest.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class OpenInterest: 4 | 5 | def __init__(self): 6 | self.symbol = "" 7 | self.openInterest = 0.0 8 | 9 | @staticmethod 10 | def json_parse(json_data): 11 | result = OpenInterest() 12 | result.symbol = json_data.get_string("symbol") 13 | result.openInterest = json_data.get_float("openInterest") 14 | 15 | return result 16 | -------------------------------------------------------------------------------- /binance_f/model/order.py: -------------------------------------------------------------------------------- 1 | class Order: 2 | 3 | def __init__(self): 4 | self.clientOrderId = "" 5 | self.cumQuote = 0.0 6 | self.executedQty = None 7 | self.orderId = None 8 | self.origQty = None 9 | self.price = None 10 | self.reduceOnly = False 11 | self.side = None 12 | self.status = None 13 | self.stopPrice = None 14 | self.symbol = "" 15 | self.timeInForce = None 16 | self.type = None 17 | self.updateTime = 0 18 | self.workingType = "" 19 | self.avgPrice = 0.0 20 | self.origType = "" 21 | self.positionSide = "" 22 | self.activatePrice = None 23 | self.priceRate = None 24 | self.closePosition = None 25 | 26 | @staticmethod 27 | def json_parse(json_data): 28 | result = Order() 29 | result.clientOrderId = json_data.get_string("clientOrderId") 30 | result.cumQuote = json_data.get_float("cumQuote") 31 | result.executedQty = json_data.get_float_or_default("executedQty", None) 32 | result.orderId = json_data.get_int("orderId") 33 | result.origQty = json_data.get_float("origQty") 34 | result.price = json_data.get_float("price") 35 | result.reduceOnly = json_data.get_boolean("reduceOnly") 36 | result.side = json_data.get_string("side") 37 | result.status = json_data.get_string("status") 38 | result.stopPrice = json_data.get_float("stopPrice") 39 | result.symbol = json_data.get_string("symbol") 40 | result.timeInForce = json_data.get_string("timeInForce") 41 | result.type = json_data.get_string("type") 42 | result.updateTime = json_data.get_int("updateTime") 43 | result.workingType = json_data.get_string("workingType") 44 | result.avgPrice = json_data.get_float("avgPrice") 45 | result.origType = json_data.get_string("origType") 46 | result.positionSide = json_data.get_string("positionSide") 47 | result.activatePrice = json_data.get_float_or_default("activatePrice", None) 48 | result.priceRate = json_data.get_float_or_default("priceRate", None) 49 | result.closePosition = json_data.get_boolean("closePosition") 50 | 51 | return result 52 | -------------------------------------------------------------------------------- /binance_f/model/orderbook.py: -------------------------------------------------------------------------------- 1 | class Order: 2 | 3 | def __init__(self): 4 | self.price = 0.0 5 | self.qty = 0.0 6 | 7 | 8 | class OrderBook: 9 | 10 | def __init__(self): 11 | self.lastUpdateId = 0 12 | self.bids = list() 13 | self.asks = list() 14 | 15 | @staticmethod 16 | def json_parse(json_data): 17 | order_book = OrderBook() 18 | order_book.lastUpdateId = json_data.get_int("lastUpdateId") 19 | 20 | list_array = json_data.get_array("bids") 21 | bid_list = list() 22 | for item in list_array.get_items(): 23 | order = Order() 24 | val = item.convert_2_list() 25 | order.price = val[0] 26 | order.qty = val[1] 27 | bid_list.append(order) 28 | order_book.bids = bid_list 29 | 30 | list_array = json_data.get_array("asks") 31 | ask_list = list() 32 | for item in list_array.get_items(): 33 | order = Order() 34 | val = item.convert_2_list() 35 | order.price = val[0] 36 | order.qty = val[1] 37 | ask_list.append(order) 38 | order_book.asks = ask_list 39 | 40 | return order_book -------------------------------------------------------------------------------- /binance_f/model/orderbookevent.py: -------------------------------------------------------------------------------- 1 | class Order: 2 | 3 | def __init__(self): 4 | self.price = 0.0 5 | self.qty = 0.0 6 | 7 | 8 | class OrderBookEvent: 9 | 10 | def __init__(self): 11 | self.eventType = "" 12 | self.eventTime = "" 13 | self.transactionTime = "" 14 | self.symbol = "" 15 | self.firstUpdateId = 0 16 | self.lastUpdateId = 0 17 | self.lastUpdateIdInlastStream = 0 18 | self.bids = list() 19 | self.asks = list() 20 | 21 | @staticmethod 22 | def json_parse(json_data): 23 | result = OrderBookEvent() 24 | result.eventType = json_data.get_string("e") 25 | result.eventTime = json_data.get_int("E") 26 | result.transactionTime = json_data.get_int("T") 27 | result.symbol = json_data.get_string("s") 28 | result.firstUpdateId = json_data.get_int("U") 29 | result.lastUpdateId = json_data.get_int("u") 30 | result.lastUpdateIdInlastStream = json_data.get_int("pu") 31 | 32 | list_array = json_data.get_array("b") 33 | bid_list = list() 34 | for item in list_array.get_items(): 35 | order = Order() 36 | val = item.convert_2_list() 37 | order.price = val[0] 38 | order.qty = val[1] 39 | bid_list.append(order) 40 | result.bids = bid_list 41 | 42 | list_array = json_data.get_array("a") 43 | ask_list = list() 44 | for item in list_array.get_items(): 45 | order = Order() 46 | val = item.convert_2_list() 47 | order.price = val[0] 48 | order.qty = val[1] 49 | ask_list.append(order) 50 | result.asks = ask_list 51 | 52 | return result -------------------------------------------------------------------------------- /binance_f/model/orderupdate.py: -------------------------------------------------------------------------------- 1 | class OrderUpdate: 2 | def __init__(self): 3 | self.eventType = "" 4 | self.eventTime = 0 5 | self.transactionTime = 0 6 | self.symbol = "" 7 | self.clientOrderId = "" 8 | self.side = None 9 | self.type = None 10 | self.timeInForce = None 11 | self.origQty = 0.0 12 | self.price = 0.0 13 | self.avgPrice = 0.0 14 | self.stopPrice = 0.0 15 | self.executionType = "" 16 | self.orderStatus = "" 17 | self.orderId = None 18 | self.lastFilledQty = 0.0 19 | self.cumulativeFilledQty = 0.0 20 | self.lastFilledPrice = 0.0 21 | self.commissionAsset = None 22 | self.commissionAmount = 0 23 | self.orderTradeTime = 0 24 | self.tradeID = None 25 | self.bidsNotional = 0.0 26 | self.asksNotional = 0.0 27 | self.isMarkerSide = None 28 | self.isReduceOnly = None 29 | self.workingType = 0.0 30 | self.isClosePosition = None 31 | self.activationPrice = 0.0 32 | self.callbackRate = 0.0 33 | self.positionSide = None 34 | 35 | 36 | @staticmethod 37 | def json_parse(json_data): 38 | result = OrderUpdate() 39 | result.eventType = json_data.get_string("e") 40 | result.eventTime = json_data.get_int("E") 41 | result.transactionTime = json_data.get_int("T") 42 | 43 | data_group = json_data.get_object("o") 44 | result.symbol = data_group.get_string("s") 45 | result.clientOrderId = data_group.get_string("c") 46 | result.side = data_group.get_string("S") 47 | result.type = data_group.get_string("o") 48 | result.timeInForce = data_group.get_string("f") 49 | result.origQty = data_group.get_float("q") 50 | result.price = data_group.get_float("p") 51 | result.avgPrice = data_group.get_float("ap") 52 | result.stopPrice = data_group.get_float("sp") 53 | result.executionType = data_group.get_string("x") 54 | result.orderStatus = data_group.get_string("X") 55 | result.orderId = data_group.get_int("i") 56 | result.lastFilledQty = data_group.get_float("l") 57 | result.cumulativeFilledQty = data_group.get_float("z") 58 | result.lastFilledPrice = data_group.get_float("L") 59 | result.commissionAsset = data_group.get_string_or_default("N", None) 60 | result.commissionAmount = data_group.get_float_or_default("n", None) 61 | result.orderTradeTime = data_group.get_int("T") 62 | result.tradeID = data_group.get_int("t") 63 | result.bidsNotional = data_group.get_float("b") 64 | result.asksNotional = data_group.get_float("a") 65 | result.isMarkerSide = data_group.get_boolean("m") 66 | result.isReduceOnly = data_group.get_boolean("R") 67 | result.workingType = data_group.get_string("wt") 68 | result.isClosePosition = data_group.get_boolean("cp") 69 | result.activationPrice = data_group.get_float_or_default("AP", None) 70 | result.callbackRate = data_group.get_float_or_default("cr", None) 71 | result.positionSide = data_group.get_string("ps") 72 | 73 | return result 74 | -------------------------------------------------------------------------------- /binance_f/model/position.py: -------------------------------------------------------------------------------- 1 | class Position: 2 | 3 | def __init__(self): 4 | self.entryPrice = 0.0 5 | self.isAutoAddMargin = False 6 | self.leverage = 0.0 7 | self.maxNotionalValue = 0.0 8 | self.liquidationPrice = 0.0 9 | self.markPrice = 0.0 10 | self.positionAmt = 0.0 11 | self.symbol = "" 12 | self.unrealizedProfit = 0.0 13 | self.marginType = "" 14 | self.isolatedMargin = 0.0 15 | self.positionSide = "" 16 | 17 | 18 | @staticmethod 19 | def json_parse(json_data): 20 | result = Position() 21 | result.entryPrice = json_data.get_float("entryPrice") 22 | result.isAutoAddMargin = json_data.get_boolean("isAutoAddMargin") 23 | result.leverage = json_data.get_float("leverage") 24 | result.maxNotionalValue = json_data.get_float("maxNotionalValue") 25 | result.liquidationPrice = json_data.get_float("liquidationPrice") 26 | result.markPrice = json_data.get_float("markPrice") 27 | result.positionAmt = json_data.get_float("positionAmt") 28 | result.symbol = json_data.get_string("symbol") 29 | result.unRealizedProfit = json_data.get_float("unRealizedProfit") 30 | result.marginType = json_data.get_string("marginType") 31 | result.isolatedMargin = json_data.get_float("isolatedMargin") 32 | result.positionSide = json_data.get_string("positionSide") 33 | return result 34 | -------------------------------------------------------------------------------- /binance_f/model/positionmargin.py: -------------------------------------------------------------------------------- 1 | class PositionMargin: 2 | 3 | def __init__(self): 4 | self.code = 0 5 | self.msg = "" 6 | self.amount = 0.0 7 | self.type = 0 8 | 9 | @staticmethod 10 | def json_parse(json_data): 11 | result = PositionMargin() 12 | result.code = json_data.get_int("code") 13 | result.msg = json_data.get_string("msg") 14 | result.amout = json_data.get_float("amount") 15 | result.type = json_data.get_int("type") 16 | 17 | return result 18 | -------------------------------------------------------------------------------- /binance_f/model/positionmarginhistory.py: -------------------------------------------------------------------------------- 1 | class PositionMarginHist: 2 | 3 | def __init__(self): 4 | self.amount = 0.0 5 | self.asset = "" 6 | self.symbol = "" 7 | self.time = 0 8 | self.type = 0 9 | 10 | @staticmethod 11 | def json_parse(json_data): 12 | result = PositionMarginHist() 13 | result.amount = json_data.get_float("amount") 14 | result.asset = json_data.get_string("asset") 15 | result.symbol = json_data.get_string("symbol") 16 | result.time = json_data.get_int("time") 17 | result.type = json_data.get_int("type") 18 | 19 | return result 20 | -------------------------------------------------------------------------------- /binance_f/model/positionmode.py: -------------------------------------------------------------------------------- 1 | class PositionMode: 2 | 3 | def __init__(self): 4 | self.dualSidePosition = None 5 | 6 | @staticmethod 7 | def json_parse(json_data): 8 | result = PositionMode() 9 | result.dualSidePosition = json_data.get_boolean("dualSidePosition") 10 | 11 | return result 12 | -------------------------------------------------------------------------------- /binance_f/model/symbolbooktickerevent.py: -------------------------------------------------------------------------------- 1 | class SymbolBookTickerEvent: 2 | 3 | def __init__(self): 4 | self.orderBookUpdateId = None 5 | self.symbol = "" 6 | self.bestBidPrice = 0.0 7 | self.bestBidQty = 0.0 8 | self.bestAskPrice = 0.0 9 | self.bestAskQty = 0.0 10 | 11 | @staticmethod 12 | def json_parse(json_wrapper): 13 | ticker_event = SymbolBookTickerEvent() 14 | ticker_event.orderBookUpdateId = json_wrapper.get_int("u") 15 | ticker_event.symbol = json_wrapper.get_string("s") 16 | ticker_event.bestBidPrice = json_wrapper.get_float("b") 17 | ticker_event.bestBidQty = json_wrapper.get_float("B") 18 | ticker_event.bestAskPrice = json_wrapper.get_float("a") 19 | ticker_event.bestAskQty = json_wrapper.get_float("A") 20 | return ticker_event -------------------------------------------------------------------------------- /binance_f/model/symbolminitickerevent.py: -------------------------------------------------------------------------------- 1 | class SymbolMiniTickerEvent: 2 | 3 | def __init__(self): 4 | self.eventType = "" 5 | self.eventTime = 0 6 | self.symbol = "" 7 | self.open = 0.0 8 | self.close = 0.0 9 | self.high = 0.0 10 | self.low = 0.0 11 | self.totalTradedBaseAssetVolume = 0.0 12 | self.totalTradedQuoteAssetVolume = 0.0 13 | 14 | @staticmethod 15 | def json_parse(json_wrapper): 16 | result = SymbolMiniTickerEvent() 17 | result.eventType = json_wrapper.get_string("e") 18 | result.eventTime = json_wrapper.get_int("E") 19 | result.symbol = json_wrapper.get_string("s") 20 | result.open = json_wrapper.get_float("o") 21 | result.close = json_wrapper.get_float("c") 22 | result.high = json_wrapper.get_float("h") 23 | result.low = json_wrapper.get_float("l") 24 | result.totalTradedBaseAssetVolume = json_wrapper.get_float("v") 25 | result.totalTradedQuoteAssetVolume = json_wrapper.get_float("q") 26 | return result -------------------------------------------------------------------------------- /binance_f/model/symbolorderbook.py: -------------------------------------------------------------------------------- 1 | class SymbolOrderBook: 2 | 3 | def __init__(self): 4 | self.symbol = "" 5 | self.bidPrice = 0.0 6 | self.bidQty = 0.0 7 | self.askPrice = 0.0 8 | self.askQty = 0.0 9 | 10 | @staticmethod 11 | def json_parse(json_data): 12 | result = SymbolOrderBook() 13 | result.symbol = json_data.get_string("symbol") 14 | result.bidPrice = json_data.get_float("bidPrice") 15 | result.bidQty = json_data.get_float("bidQty") 16 | result.askPrice = json_data.get_float("askPrice") 17 | result.askQty = json_data.get_float("askQty") 18 | return result -------------------------------------------------------------------------------- /binance_f/model/symbolprice.py: -------------------------------------------------------------------------------- 1 | class SymbolPrice: 2 | 3 | def __init__(self): 4 | self.symbol = "" 5 | self.price = 0.0 6 | 7 | @staticmethod 8 | def json_parse(json_data): 9 | result = SymbolPrice() 10 | result.symbol = json_data.get_string("symbol") 11 | result.price = json_data.get_float("price") 12 | return result -------------------------------------------------------------------------------- /binance_f/model/symboltickerevent.py: -------------------------------------------------------------------------------- 1 | class SymbolTickerEvent: 2 | 3 | def __init__(self): 4 | self.eventType = "" 5 | self.eventTime = 0 6 | self.symbol = "" 7 | self.priceChange = 0.0 8 | self.priceChangePercent = 0.0 9 | self.weightedAvgPrice = 0.0 10 | self.lastPrice = 0.0 11 | self.lastQty = 0.0 12 | self.open = 0.0 13 | self.high = 0.0 14 | self.low = 0.0 15 | self.totalTradedBaseAssetVolume = 0.0 16 | self.totalTradedQuoteAssetVolume = 0.0 17 | self.openTime = 0 18 | self.closeTime = 0 19 | self.firstId = None 20 | self.lastId = None 21 | self.count = 0 22 | 23 | @staticmethod 24 | def json_parse(json_wrapper): 25 | ticker_event = SymbolTickerEvent() 26 | ticker_event.eventType = json_wrapper.get_string("e") 27 | ticker_event.eventTime = json_wrapper.get_int("E") 28 | ticker_event.symbol = json_wrapper.get_string("s") 29 | ticker_event.priceChange = json_wrapper.get_float("p") 30 | ticker_event.priceChangePercent = json_wrapper.get_float("P") 31 | ticker_event.weightedAvgPrice = json_wrapper.get_float("w") 32 | ticker_event.lastPrice = json_wrapper.get_float("c") 33 | ticker_event.lastQty = json_wrapper.get_float("Q") 34 | ticker_event.open = json_wrapper.get_float("o") 35 | ticker_event.high = json_wrapper.get_float("h") 36 | ticker_event.low = json_wrapper.get_float("l") 37 | ticker_event.totalTradedBaseAssetVolume = json_wrapper.get_float("v") 38 | ticker_event.totalTradedQuoteAssetVolume = json_wrapper.get_float("q") 39 | ticker_event.openTime = json_wrapper.get_int("O") 40 | ticker_event.closeTime = json_wrapper.get_int("C") 41 | ticker_event.firstId = json_wrapper.get_int("F") 42 | ticker_event.lastId = json_wrapper.get_int("L") 43 | ticker_event.count = json_wrapper.get_int("n") 44 | return ticker_event -------------------------------------------------------------------------------- /binance_f/model/tickerpricechangestatistics.py: -------------------------------------------------------------------------------- 1 | class TickerPriceChangeStatistics: 2 | 3 | def __init__(self): 4 | self.symbol = "" 5 | self.priceChange = 0.0 6 | self.priceChangePercent = 0.0 7 | self.weightedAvgPrice = 0.0 8 | self.lastPrice = 0.0 9 | self.lastQty = 0.0 10 | self.bidPrice = 0.0 11 | self.askPrice = 0.0 12 | self.openPrice = 0.0 13 | self.highPrice = 0.0 14 | self.lowPrice = 0.0 15 | self.volume = 0.0 16 | self.quoteVolume = 0.0 17 | self.openTime = 0 18 | self.closeTime = 0 19 | self.firstId = None 20 | self.lastId = None 21 | self.count = None 22 | 23 | @staticmethod 24 | def json_parse(json_data): 25 | result = TickerPriceChangeStatistics() 26 | result.symbol = json_data.get_string("symbol") 27 | result.priceChange = json_data.get_float("priceChange") 28 | result.priceChangePercent = json_data.get_float("priceChangePercent") 29 | result.weightedAvgPrice = json_data.get_float("weightedAvgPrice") 30 | result.lastPrice = json_data.get_float("lastPrice") 31 | result.lastQty = json_data.get_float("lastQty") 32 | result.openPrice = json_data.get_float("openPrice") 33 | result.highPrice = json_data.get_float("highPrice") 34 | result.lowPrice = json_data.get_float("lowPrice") 35 | result.volume = json_data.get_float("volume") 36 | result.quoteVolume = json_data.get_float("quoteVolume") 37 | result.openTime = json_data.get_int("openTime") 38 | result.closeTime = json_data.get_int("closeTime") 39 | result.firstId = json_data.get_int("firstId") 40 | result.lastId = json_data.get_int("lastId") 41 | result.count = json_data.get_int("count") 42 | return result -------------------------------------------------------------------------------- /binance_f/model/trade.py: -------------------------------------------------------------------------------- 1 | class Trade: 2 | 3 | def __init__(self): 4 | self.id = None 5 | self.price = 0.0 6 | self.qty = 0.0 7 | self.quoteQty = 0.0 8 | self.time = 0 9 | self.isBuyerMaker = False 10 | 11 | @staticmethod 12 | def json_parse(json_data): 13 | result = Trade() 14 | result.id = json_data.get_int("id") 15 | result.price = json_data.get_float("price") 16 | result.qty = json_data.get_float("qty") 17 | result.quoteQty = json_data.get_float("quoteQty") 18 | result.time = json_data.get_int("time") 19 | result.isBuyerMaker = json_data.get_boolean("isBuyerMaker") 20 | 21 | return result -------------------------------------------------------------------------------- /binance_f/requestclient.py: -------------------------------------------------------------------------------- 1 | from binance_f.constant.system import RestApiDefine 2 | from binance_f.impl.restapirequestimpl import RestApiRequestImpl 3 | from binance_f.impl.restapiinvoker import call_sync 4 | from binance_f.model.constant import * 5 | 6 | 7 | class RequestClient(object): 8 | 9 | def __init__(self, **kwargs): 10 | """ 11 | Create the request client instance. 12 | :param kwargs: The option of request connection. 13 | api_key: The public key applied from Binance. 14 | secret_key: The private key applied from Binance. 15 | server_url: The URL name like "https://api.binance.com". 16 | """ 17 | api_key = None 18 | secret_key = None 19 | url = RestApiDefine.Url 20 | if "api_key" in kwargs: 21 | api_key = kwargs["api_key"] 22 | if "secret_key" in kwargs: 23 | secret_key = kwargs["secret_key"] 24 | if "url" in kwargs: 25 | url = kwargs["url"] 26 | try: 27 | self.request_impl = RestApiRequestImpl(api_key, secret_key, url) 28 | except Exception: 29 | pass 30 | self.limits = {} 31 | 32 | def refresh_limits(self,limits): 33 | for k,v in limits.items(): 34 | #print(k,v) 35 | self.limits[k] = v 36 | 37 | def get_servertime(self) -> any: 38 | """ 39 | Check Server Time 40 | 41 | GET /fapi/v1/time 42 | 43 | Test connectivity to the Rest API and get the current server time. 44 | """ 45 | response = call_sync(self.request_impl.get_servertime()) 46 | self.refresh_limits(response[1]) 47 | return response[0] 48 | 49 | def get_exchange_information(self) -> any: 50 | """ 51 | Exchange Information (MARKET_DATA) 52 | 53 | GET /fapi/v1/exchangeInfo 54 | 55 | Current exchange trading rules and symbol information 56 | """ 57 | response = call_sync(self.request_impl.get_exchange_information()) 58 | self.refresh_limits(response[1]) 59 | return response[0] 60 | 61 | def get_order_book(self, symbol: 'str', limit: 'int' = None) -> any: 62 | """ 63 | Order Book (MARKET_DATA) 64 | 65 | GET /fapi/v1/depth 66 | 67 | Adjusted based on the limit: 68 | """ 69 | response = call_sync(self.request_impl.get_order_book(symbol, limit)) 70 | self.refresh_limits(response[1]) 71 | return response[0] 72 | 73 | def get_recent_trades_list(self, symbol: 'str', limit: 'int' = None) -> any: 74 | """ 75 | Recent Trades List (MARKET_DATA) 76 | 77 | GET /fapi/v1/trades 78 | 79 | Get recent trades (up to last 500). 80 | """ 81 | response = call_sync(self.request_impl.get_recent_trades_list(symbol, limit)) 82 | self.refresh_limits(response[1]) 83 | return response[0] 84 | 85 | def get_old_trade_lookup(self, symbol: 'str', limit: 'int' = None, fromId: 'long' = None) -> any: 86 | """ 87 | Old Trades Lookup (MARKET_DATA) 88 | 89 | GET /fapi/v1/historicalTrades 90 | 91 | Get older market historical trades. 92 | """ 93 | response = call_sync(self.request_impl.get_old_trade_lookup(symbol, limit, fromId)) 94 | self.refresh_limits(response[1]) 95 | return response[0] 96 | 97 | def get_aggregate_trades_list(self, symbol: 'str', fromId: 'long' = None, 98 | startTime: 'long' = None, endTime: 'long' = None, limit: 'int' = None) -> any: 99 | """ 100 | Compressed/Aggregate Trades List (MARKET_DATA) 101 | 102 | GET /fapi/v1/aggTrades 103 | 104 | Get compressed, aggregate trades. Trades that fill at the time, from the same order, 105 | with the same price will have the quantity aggregated. 106 | """ 107 | response = call_sync(self.request_impl.get_aggregate_trades_list(symbol, fromId, startTime, endTime, limit)) 108 | self.refresh_limits(response[1]) 109 | return response[0] 110 | 111 | def get_candlestick_data(self, symbol: 'str', interval: 'CandlestickInterval', 112 | startTime: 'long' = None, endTime: 'long' = None, limit: 'int' = None) -> any: 113 | """ 114 | Kline/Candlestick Data (MARKET_DATA) 115 | 116 | GET /fapi/v1/klines 117 | 118 | Kline/candlestick bars for a symbol. Klines are uniquely identified by their open time. 119 | """ 120 | response = call_sync(self.request_impl.get_candlestick_data(symbol, interval, startTime, endTime, limit)) 121 | self.refresh_limits(response[1]) 122 | return response[0] 123 | 124 | def get_mark_price(self, symbol: 'str') -> any: 125 | """ 126 | Mark Price (MARKET_DATA) 127 | 128 | GET /fapi/v1/premiumIndex 129 | 130 | Mark Price and Funding Rate 131 | """ 132 | response = call_sync(self.request_impl.get_mark_price(symbol)) 133 | self.refresh_limits(response[1]) 134 | return response[0] 135 | 136 | def get_funding_rate(self, symbol: 'str', startTime: 'long' = None, endTime: 'str' = None, limit: 'int' = None) -> any: 137 | """ 138 | Get Funding Rate History (MARKET_DATA) 139 | 140 | GET /fapi/v1/fundingRate 141 | """ 142 | response = call_sync(self.request_impl.get_funding_rate(symbol, startTime, endTime, limit)) 143 | self.refresh_limits(response[1]) 144 | return response[0] 145 | 146 | def get_ticker_price_change_statistics(self, symbol: 'str' = None) -> any: 147 | """ 148 | 24hr Ticker Price Change Statistics (MARKET_DATA) 149 | 150 | GET /fapi/v1/ticker/24hr 151 | 152 | 24 hour rolling window price change statistics. 153 | Careful when accessing this with no symbol. 154 | """ 155 | response = call_sync(self.request_impl.get_ticker_price_change_statistics(symbol)) 156 | self.refresh_limits(response[1]) 157 | return response[0] 158 | 159 | def get_symbol_price_ticker(self, symbol: 'str' = None) -> any: 160 | """ 161 | Symbol Price Ticker (MARKET_DATA) 162 | 163 | GET /fapi/v1/ticker/price 164 | 165 | Latest price for a symbol or symbols. 166 | """ 167 | response = call_sync(self.request_impl.get_symbol_price_ticker(symbol)) 168 | self.refresh_limits(response[1]) 169 | return response[0] 170 | 171 | def get_symbol_orderbook_ticker(self, symbol: 'str' = None) -> any: 172 | """ 173 | Symbol Order Book Ticker (MARKET_DATA) 174 | 175 | GET /fapi/v1/ticker/bookTicker 176 | 177 | Best price/qty on the order book for a symbol or symbols. 178 | """ 179 | response = call_sync(self.request_impl.get_symbol_orderbook_ticker(symbol)) 180 | self.refresh_limits(response[1]) 181 | return response[0] 182 | 183 | def get_liquidation_orders(self, symbol: 'str' = None, startTime: 'long' = None, endTime: 'str' = None, 184 | limit: 'int' = None) -> any: 185 | """ 186 | Get all Liquidation Orders (MARKET_DATA) 187 | 188 | GET /fapi/v1/allForceOrders 189 | """ 190 | response = call_sync(self.request_impl.get_liquidation_orders(symbol, startTime, endTime, limit)) 191 | self.refresh_limits(response[1]) 192 | return response[0] 193 | 194 | def get_open_interest(self, symbol: 'str') -> any: 195 | """ 196 | Symbol Open Interest (MARKET_DATA) 197 | 198 | GET /fapi/v1/openInterest 199 | 200 | Get present open interest of a specific symbol. 201 | """ 202 | response = call_sync(self.request_impl.get_open_interest(symbol)) 203 | self.refresh_limits(response[1]) 204 | return response[0] 205 | 206 | 207 | def change_position_mode(self, dualSidePosition: 'boolean' = None) -> any: 208 | """ 209 | Change Current Position Mode (TRADE) 210 | 211 | POST /fapi/v1/positionSide/dual (HMAC SHA256) 212 | 213 | Change user's position mode (Hedge Mode or One-way Mode ) on EVERY symbol 214 | """ 215 | response = call_sync(self.request_impl.change_position_mode(dualSidePosition)) 216 | self.refresh_limits(response[1]) 217 | return response[0] 218 | 219 | 220 | def get_position_mode(self) -> any: 221 | """ 222 | Get Current Position Mode (USER_DATA) 223 | 224 | GET /fapi/v1/positionSide/dual (HMAC SHA256) 225 | 226 | Get user's position mode (Hedge Mode or One-way Mode ) on EVERY symbol 227 | """ 228 | response = call_sync(self.request_impl.get_position_mode()) 229 | self.refresh_limits(response[1]) 230 | return response[0] 231 | 232 | 233 | def post_order(self, symbol: 'str', side: 'OrderSide', ordertype: 'OrderType', 234 | timeInForce: 'TimeInForce' = TimeInForce.INVALID, quantity: 'float' = None, 235 | reduceOnly: 'boolean' = None, price: 'float' = None, 236 | newClientOrderId: 'str' = None, stopPrice: 'float' = None, 237 | workingType: 'WorkingType' = WorkingType.INVALID, closePosition: 'boolean' = None, 238 | positionSide: 'PositionSide' = PositionSide.INVALID, callbackRate: 'float' = None, 239 | activationPrice: 'float' = None, newOrderRespType: 'OrderRespType' = OrderRespType.INVALID) -> any: 240 | """ 241 | New Order (TRADE) 242 | 243 | POST /fapi/v1/order (HMAC SHA256) 244 | 245 | Send in a new order. 246 | """ 247 | #print(symbol, side, ordertype, quantity, price) 248 | response = call_sync(self.request_impl.post_order(symbol, side, ordertype, 249 | timeInForce, quantity, reduceOnly, price, newClientOrderId, stopPrice, workingType, closePosition, positionSide, callbackRate, activationPrice, newOrderRespType)) 250 | self.refresh_limits(response[1]) 251 | return response[0] 252 | 253 | def get_order(self, symbol: 'str', orderId: 'long' = None, origClientOrderId: 'str' = None) -> any: 254 | """ 255 | Query Order (USER_DATA) 256 | 257 | GET /fapi/v1/order (HMAC SHA256) 258 | 259 | Check an order's status. 260 | """ 261 | response = call_sync(self.request_impl.get_order(symbol, orderId, origClientOrderId)) 262 | self.refresh_limits(response[1]) 263 | return response[0] 264 | 265 | def cancel_order(self, symbol: 'str', orderId: 'long' = None, origClientOrderId: 'str' = None) -> any: 266 | """ 267 | Cancel Order (TRADE) 268 | 269 | DELETE /fapi/v1/order (HMAC SHA256) 270 | 271 | Cancel an active order. 272 | """ 273 | response = call_sync(self.request_impl.cancel_order(symbol, orderId, origClientOrderId)) 274 | self.refresh_limits(response[1]) 275 | return response[0] 276 | 277 | 278 | def cancel_all_orders(self, symbol: 'str') -> any: 279 | """ 280 | Cancel All Open Orders (TRADE) 281 | 282 | DELETE /fapi/v1/allOpenOrders (HMAC SHA256) 283 | """ 284 | response = call_sync(self.request_impl.cancel_all_orders(symbol)) 285 | self.refresh_limits(response[1]) 286 | return response[0] 287 | 288 | 289 | def cancel_list_orders(self, symbol: 'str', orderIdList: 'list' = None, origClientOrderIdList: 'list' = None) -> any: 290 | """ 291 | Cancel Multiple Orders (TRADE) 292 | 293 | DELETE /fapi/v1/batchOrders (HMAC SHA256) 294 | """ 295 | response = call_sync(self.request_impl.cancel_list_orders(symbol, orderIdList, origClientOrderIdList)) 296 | self.refresh_limits(response[1]) 297 | return response[0] 298 | 299 | def get_open_orders(self, symbol: 'str' = None) -> any: 300 | """ 301 | Current Open Orders (USER_DATA) 302 | 303 | GET /fapi/v1/openOrders (HMAC SHA256) 304 | 305 | Get all open orders on a symbol. Careful when accessing this with no symbol. 306 | """ 307 | response = call_sync(self.request_impl.get_open_orders(symbol)) 308 | self.refresh_limits(response[1]) 309 | return response[0] 310 | 311 | def get_all_orders(self, symbol: 'str', orderId: 'long' = None, startTime: 'long' = None, 312 | endTime: 'long' = None, limit: 'int' = None) -> any: 313 | """ 314 | All Orders (USER_DATA) 315 | 316 | GET /fapi/v1/allOrders (HMAC SHA256) 317 | 318 | Get all account orders; active, canceled, or filled. 319 | """ 320 | response = call_sync(self.request_impl.get_all_orders(symbol, orderId, startTime, endTime, limit)) 321 | self.refresh_limits(response[1]) 322 | return response[0] 323 | 324 | def get_balance(self) -> any: 325 | """ 326 | Future Account Balance (USER_DATA) 327 | 328 | Get /fapi/v1/balance (HMAC SHA256) 329 | """ 330 | response = call_sync(self.request_impl.get_balance()) 331 | self.refresh_limits(response[1]) 332 | return response[0] 333 | 334 | def get_account_information(self) -> any: 335 | """ 336 | Account Information (USER_DATA) 337 | 338 | GET /fapi/v1/account (HMAC SHA256) 339 | 340 | Get current account information. 341 | """ 342 | response = call_sync(self.request_impl.get_account_information()) 343 | self.refresh_limits(response[1]) 344 | return response[0] 345 | 346 | def change_initial_leverage(self, symbol: 'str', leverage: 'int') -> any: 347 | """ 348 | Change Initial Leverage (TRADE) 349 | 350 | POST /fapi/v1/leverage (HMAC SHA256) 351 | 352 | Change user's initial leverage of specific symbol market. 353 | """ 354 | response = call_sync(self.request_impl.change_initial_leverage(symbol, leverage)) 355 | self.refresh_limits(response[1]) 356 | return response[0] 357 | 358 | def change_margin_type(self, symbol: 'str', marginType: 'FuturesMarginType') -> any: 359 | """ 360 | Change Margin Type (TRADE) 361 | 362 | POST /fapi/v1/marginType (HMAC SHA256) 363 | """ 364 | response = call_sync(self.request_impl.change_margin_type(symbol, marginType)) 365 | self.refresh_limits(response[1]) 366 | return response[0] 367 | 368 | def change_position_margin(self, symbol: 'str', amount: 'float', type: 'int') -> any: 369 | """ 370 | Modify Isolated Position Margin (TRADE) 371 | 372 | POST /fapi/v1/positionMargin (HMAC SHA256) 373 | """ 374 | response = call_sync(self.request_impl.change_position_margin(symbol, amount, type)) 375 | self.refresh_limits(response[1]) 376 | return response[0] 377 | 378 | def get_position_margin_change_history(self, symbol: 'str', type: 'int' = None, startTime: 'int' = None, endTime: 'int' = None, limit :'int' = None) -> any: 379 | """ 380 | Get Position Margin Change History (TRADE) 381 | 382 | GET /fapi/v1/positionMargin/history (HMAC SHA256) 383 | """ 384 | response = call_sync(self.request_impl.get_position_margin_change_history(symbol, type, startTime, endTime, limit)) 385 | self.refresh_limits(response[1]) 386 | return response[0] 387 | 388 | def get_position(self) -> any: 389 | """ 390 | Position Information (USER_DATA) 391 | 392 | GET /fapi/v1/positionRisk (HMAC SHA256) Get current account information. 393 | """ 394 | response = call_sync(self.request_impl.get_position()) 395 | self.refresh_limits(response[1]) 396 | return response[0] 397 | 398 | def get_account_trades(self, symbol: 'str', startTime: 'long' = None, endTime: 'long' = None, 399 | fromId: 'long' = None, limit: 'int' = None) -> any: 400 | """ 401 | Account Trade List (USER_DATA) 402 | 403 | GET /fapi/v1/userTrades (HMAC SHA256) 404 | 405 | Get trades for a specific account and symbol. 406 | """ 407 | response = call_sync(self.request_impl.get_account_trades(symbol, startTime, endTime, fromId, limit)) 408 | self.refresh_limits(response[1]) 409 | return response[0] 410 | 411 | def get_income_history(self, symbol: 'str' = None, incomeType: 'IncomeType' = IncomeType.INVALID, 412 | startTime: 'long' = None, endTime: 'long' = None, limit: 'int' = None) -> any: 413 | """ 414 | Get Income History(USER_DATA) 415 | 416 | GET /fapi/v1/income (HMAC SHA256) 417 | """ 418 | response = call_sync(self.request_impl.get_income_history(symbol, incomeType, startTime, endTime, limit)) 419 | self.refresh_limits(response[1]) 420 | return response[0] 421 | 422 | def start_user_data_stream(self) -> any: 423 | """ 424 | Start User Data Stream (USER_STREAM) 425 | 426 | POST /fapi/v1/listenKey (HMAC SHA256) 427 | 428 | Start a new user data stream. The stream will close after 60 minutes unless a keepalive is sent. 429 | If the account has an active listenKey, 430 | that listenKey will be returned and its validity will be extended for 60 minutes. 431 | """ 432 | response = call_sync(self.request_impl.start_user_data_stream()) 433 | self.refresh_limits(response[1]) 434 | return response[0] 435 | 436 | def keep_user_data_stream(self) -> any: 437 | """ 438 | Keepalive User Data Stream (USER_STREAM) 439 | 440 | PUT /fapi/v1/listenKey (HMAC SHA256) 441 | 442 | Keepalive a user data stream to prevent a time out. User data streams will close after 60 minutes. 443 | It's recommended to send a ping about every 60 minutes. 444 | """ 445 | response = call_sync(self.request_impl.keep_user_data_stream()) 446 | self.refresh_limits(response[1]) 447 | return response[0] 448 | 449 | def close_user_data_stream(self) -> any: 450 | """ 451 | Close User Data Stream (USER_STREAM) 452 | 453 | DELETE /fapi/v1/listenKey (HMAC SHA256) 454 | 455 | Close out a user data stream. 456 | """ 457 | response = call_sync(self.request_impl.close_user_data_stream()) 458 | self.refresh_limits(response[1]) 459 | return response[0] 460 | -------------------------------------------------------------------------------- /binance_f/subscriptionclient.py: -------------------------------------------------------------------------------- 1 | import urllib.parse 2 | 3 | from binance_f.constant.system import WebSocketDefine 4 | from binance_f.impl.websocketrequestimpl import WebsocketRequestImpl 5 | from binance_f.impl.websocketconnection import WebsocketConnection 6 | from binance_f.impl.websocketwatchdog import WebSocketWatchDog 7 | from binance_f.impl.restapirequestimpl import RestApiRequestImpl 8 | from binance_f.model import * 9 | from binance_f.model.constant import * 10 | 11 | # For develop 12 | from binance_f.base.printobject import * 13 | 14 | class SubscriptionClient(object): 15 | 16 | def __init__(self, **kwargs): 17 | """ 18 | Create the subscription client to subscribe the update from server. 19 | 20 | :param kwargs: The option of subscription connection. 21 | api_key: The public key applied from Binance. 22 | secret_key: The private key applied from Binance. 23 | uri: Set the URI for subscription. 24 | is_auto_connect: When the connection lost is happening on the subscription line, specify whether the client 25 | reconnect to server automatically. The connection lost means: 26 | Caused by network problem 27 | The connection close triggered by server (happened every 24 hours) 28 | No any message can be received from server within a specified time, see receive_limit_ms 29 | receive_limit_ms: Set the receive limit in millisecond. If no message is received within this limit time, 30 | the connection will be disconnected. 31 | connection_delay_failure: If auto reconnect is enabled, specify the delay time before reconnect. 32 | """ 33 | api_key = None 34 | secret_key = None 35 | if "api_key" in kwargs: 36 | api_key = kwargs["api_key"] 37 | if "secret_key" in kwargs: 38 | secret_key = kwargs["secret_key"] 39 | self.__api_key = api_key 40 | self.__secret_key = secret_key 41 | self.websocket_request_impl = WebsocketRequestImpl(self.__api_key) 42 | self.connections = list() 43 | self.uri = WebSocketDefine.Uri 44 | is_auto_connect = True 45 | receive_limit_ms = 60000 46 | connection_delay_failure = 1 47 | if "uri" in kwargs: 48 | self.uri = kwargs["uri"] 49 | if "is_auto_connect" in kwargs: 50 | is_auto_connect = kwargs["is_auto_connect"] 51 | if "receive_limit_ms" in kwargs: 52 | receive_limit_ms = kwargs["receive_limit_ms"] 53 | if "connection_delay_failure" in kwargs: 54 | connection_delay_failure = kwargs["connection_delay_failure"] 55 | self.__watch_dog = WebSocketWatchDog(is_auto_connect, receive_limit_ms, connection_delay_failure) 56 | 57 | def __create_connection(self, request): 58 | connection = WebsocketConnection(self.__api_key, self.__secret_key, self.uri, self.__watch_dog, request) 59 | self.connections.append(connection) 60 | connection.connect() 61 | 62 | def unsubscribe_all(self): 63 | for conn in self.connections: 64 | conn.close() 65 | self.connections.clear() 66 | 67 | def subscribe_aggregate_trade_event(self, symbol: 'str', callback, error_handler=None): 68 | """ 69 | Aggregate Trade Streams 70 | 71 | The Aggregate Trade Streams push trade information that is aggregated for a single taker order every 100 milliseconds. 72 | 73 | Stream Name: @aggTrade 74 | """ 75 | request = self.websocket_request_impl.subscribe_aggregate_trade_event(symbol, callback, error_handler) 76 | self.__create_connection(request) 77 | 78 | def subscribe_mark_price_event(self, symbol: 'str', callback, error_handler=None): 79 | """ 80 | Mark Price Stream 81 | 82 | Mark price for a single symbol pushed every 3 secends. 83 | 84 | Stream Name: @markPrice 85 | """ 86 | request = self.websocket_request_impl.subscribe_mark_price_event(symbol, callback, error_handler) 87 | self.__create_connection(request) 88 | 89 | def subscribe_candlestick_event(self, symbol: 'str', interval: 'CandlestickInterval', callback, 90 | error_handler=None): 91 | """ 92 | Kline/Candlestick Streams 93 | 94 | The Kline/Candlestick Stream push updates to the current klines/candlestick every 250 milliseconds (if existing). 95 | 96 | Stream Name: @kline_ 97 | """ 98 | request = self.websocket_request_impl.subscribe_candlestick_event(symbol, interval, callback, error_handler) 99 | self.__create_connection(request) 100 | 101 | def subscribe_symbol_miniticker_event(self, symbol: 'str', callback, error_handler=None): 102 | """ 103 | Individual Symbol Mini Ticker Stream 104 | 105 | 24hr rolling window mini-ticker statistics for a single symbol pushed every 3 seconds. These are NOT the statistics of the UTC day, 106 | but a 24hr rolling window from requestTime to 24hrs before. 107 | 108 | Stream Name: @miniTicker 109 | """ 110 | request = self.websocket_request_impl.subscribe_symbol_miniticker_event(symbol, callback, error_handler) 111 | self.__create_connection(request) 112 | 113 | def subscribe_all_miniticker_event(self, callback, error_handler=None): 114 | """ 115 | All Market Mini Tickers Stream 116 | 117 | 24hr rolling window mini-ticker statistics for all symbols pushed every 3 seconds. 118 | These are NOT the statistics of the UTC day, but a 24hr rolling window from requestTime to 24hrs before. 119 | Note that only tickers that have changed will be present in the array. 120 | 121 | Stream Name: !miniTicker@arr 122 | """ 123 | request = self.websocket_request_impl.subscribe_all_miniticker_event(callback, error_handler) 124 | self.__create_connection(request) 125 | 126 | def subscribe_symbol_ticker_event(self, symbol: 'str', callback, error_handler=None): 127 | """ 128 | Individual Symbol Ticker Streams 129 | 130 | 24hr rollwing window ticker statistics for a single symbol pushed every 3 seconds. These are NOT the statistics of the UTC day, 131 | but a 24hr rolling window from requestTime to 24hrs before. 132 | 133 | Stream Name: @ticker 134 | """ 135 | request = self.websocket_request_impl.subscribe_symbol_ticker_event(symbol, callback, error_handler) 136 | self.__create_connection(request) 137 | 138 | def subscribe_all_ticker_event(self, callback, error_handler=None): 139 | """ 140 | All Market Tickers Stream 141 | 142 | 24hr rollwing window ticker statistics for all symbols pushed every 3 seconds. These are NOT the statistics of the UTC day, but a 24hr rolling window from requestTime to 24hrs before. 143 | Note that only tickers that have changed will be present in the array. 144 | 145 | Stream Name: !ticker@arr 146 | """ 147 | request = self.websocket_request_impl.subscribe_all_ticker_event(callback, error_handler) 148 | self.__create_connection(request) 149 | 150 | def subscribe_symbol_bookticker_event(self, symbol: 'str', callback, error_handler=None): 151 | """ 152 | Individual Symbol Book Ticker Streams 153 | 154 | Pushes any update to the best bid or ask's price or quantity in real-time for a specified symbol. 155 | 156 | Stream Name: @bookTicker 157 | """ 158 | request = self.websocket_request_impl.subscribe_symbol_bookticker_event(symbol, callback, error_handler) 159 | self.__create_connection(request) 160 | 161 | def subscribe_all_bookticker_event(self, callback, error_handler=None): 162 | """ 163 | All Book Tickers Stream 164 | 165 | Pushes any update to the best bid or ask's price or quantity in real-time for all symbols. 166 | 167 | Stream Name: !bookTicker 168 | """ 169 | request = self.websocket_request_impl.subscribe_all_bookticker_event(callback, error_handler) 170 | self.__create_connection(request) 171 | 172 | def subscribe_symbol_liquidation_event(self, symbol: 'str', callback, error_handler=None): 173 | """ 174 | Liquidation Order Streams 175 | 176 | The Liquidation Order Streams push force liquidation order information for specific symbol 177 | 178 | Stream Name: @forceOrder 179 | """ 180 | request = self.websocket_request_impl.subscribe_symbol_liquidation_event(symbol, callback, error_handler) 181 | self.__create_connection(request) 182 | 183 | def subscribe_all_liquidation_event(self, callback, error_handler=None): 184 | """ 185 | All Market Liquidation Order Streams 186 | 187 | The All Liquidation Order Streams push force liquidation order information for all symbols in the market. 188 | 189 | Stream Name: !forceOrder@arr 190 | """ 191 | request = self.websocket_request_impl.subscribe_all_liquidation_event(callback, error_handler) 192 | self.__create_connection(request) 193 | 194 | def subscribe_book_depth_event(self, symbol: 'str', limit: 'int', callback, error_handler=None, update_time: 'UpdateTime' = UpdateTime.INVALID): 195 | """ 196 | Partial Book Depth Streams 197 | 198 | Top bids and asks, Valid are 5, 10, or 20. 199 | 200 | Stream Names: @depth OR @depth@100ms. 201 | """ 202 | print(update_time) 203 | request = self.websocket_request_impl.subscribe_book_depth_event(symbol, limit, update_time, callback, error_handler) 204 | self.__create_connection(request) 205 | 206 | def subscribe_diff_depth_event(self, symbol: 'str', callback, error_handler=None, update_time: 'UpdateTime' = UpdateTime.INVALID): 207 | """ 208 | Diff. Depth Stream 209 | 210 | Bids and asks, pushed every 250 milliseconds or 100 milliseconds(if existing) 211 | 212 | Stream Name: @depth OR @depth@100ms 213 | """ 214 | request = self.websocket_request_impl.subscribe_diff_depth_event(symbol, update_time, callback, error_handler) 215 | self.__create_connection(request) 216 | 217 | def subscribe_user_data_event(self, listenKey: 'str', callback, error_handler=None): 218 | """ 219 | User Data Streams 220 | """ 221 | request = self.websocket_request_impl.subscribe_user_data_event(listenKey, callback, error_handler) 222 | self.__create_connection(request) 223 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudthink/openquant/9eae105158eaafa6ec3b43cff0c547768883f3be/doc/README.md -------------------------------------------------------------------------------- /exchanges.py: -------------------------------------------------------------------------------- 1 | # coding = utf-8 2 | """ 3 | Datetime : 2020/5/23 22:36 4 | Author : cloudthink 5 | Info : 6 | """ 7 | 8 | from binance_f import RequestClient 9 | from binance_f.constant.test import * 10 | from binance_f.base.printobject import * 11 | 12 | import datetime, time 13 | from utils import logger 14 | 15 | from binance_f.model.constant import * 16 | 17 | 18 | class Binance_futures: 19 | def __init__(self, api_key=None, secret_key=None, init_balance=0, leverage=1): 20 | self.api_key = api_key 21 | self.secret_key = secret_key 22 | 23 | # self.init_balance=init_balance 24 | self.leverage = leverage 25 | logger.warn('now leverage:', self.leverage) 26 | request_client = RequestClient(api_key=g_api_key, secret_key=g_secret_key) 27 | self.request_client = request_client 28 | 29 | # 初始化变量 None? 30 | self.get_account_information_result = 0 31 | self.get_position_result = 0 32 | self.get_symbol_price_ticker_result = 0 33 | 34 | self.get_candlestick_data_result = 0 35 | self.get_symbol_orderbook_ticker_result = 0 36 | 37 | self.position_info_list = [] 38 | self.depth_ticker_dict = {} 39 | 40 | if init_balance != 0: 41 | self.origin_balance = init_balance 42 | else: 43 | self.origin_balance = 0 44 | self.margin_balance = 0.0000001 45 | self.total_margin_ratio = 0 46 | self.old_total_balance = 0 47 | # 初始化交易对基础信息 48 | self.exchange_info = {} 49 | self.io_get_exchange_info() 50 | 51 | self.position_info_long = {} 52 | self.position_info_short = {} 53 | 54 | def io_get_exchange_info(self): 55 | result = self.request_client.get_exchange_information() 56 | 57 | for one_info in result.symbols: 58 | self.exchange_info[one_info.symbol] = one_info 59 | if one_info.symbol == 'BTCUSDT': 60 | print(one_info.filters[1]) 61 | 62 | def io_get_depth_ticker(self, symbol_='BTCUSDT'): 63 | ''' 64 | 返回当前最优的挂单(最高买单,最低卖单) 65 | :return: 66 | ''' 67 | 68 | result = self.request_client.get_symbol_orderbook_ticker(symbol=symbol_) 69 | self.get_symbol_orderbook_ticker_result = result[0] 70 | logger.info(symbol_, result[0].askPrice, result[0].bidPrice) 71 | 72 | self.depth_ticker_dict[symbol_] = {} 73 | self.depth_ticker_dict[symbol_]['ask1_price'] = result[0].askPrice 74 | self.depth_ticker_dict[symbol_]['ask1_amount'] = result[0].askQty 75 | self.depth_ticker_dict[symbol_]['bid1_price'] = result[0].bidPrice 76 | self.depth_ticker_dict[symbol_]['bid1_amount'] = result[0].bidQty 77 | 78 | def io_get_depth(self, symbol_='BTCUSDT', limit_=5): 79 | ''' 80 | 深度信息 81 | :return: 82 | ''' 83 | 84 | result = self.request_client.get_order_book(symbol=symbol_) 85 | 86 | def io_order(self, symbol, type, price, amount): 87 | open_timeinforce = TimeInForce.IOC 88 | cover_timeinforce = TimeInForce.IOC 89 | 90 | # 价格精度 91 | price_precision = int(self.exchange_info[symbol].pricePrecision) 92 | # 下单量精度 93 | amount_precision = int(self.exchange_info[symbol].quantityPrecision) 94 | # 最小下单量 95 | min_open_amount = float(self.exchange_info[symbol].filters[1]['minQty']) 96 | 97 | if (float(amount) < min_open_amount) or (float(amount) == 0) or (float(price) == 0): 98 | logger.debug('want trade amount litter than min trade amount! do nothing') 99 | return 100 | 101 | price = round(float(price), price_precision) 102 | amount = round(float(amount), amount_precision) 103 | # logger.warn('--->:',symbol,type,price,amount)#,price_precision,amount_precision,min_open_amount,float(amount) 133 | low:9197.65 134 | numTrades:23 135 | open:9197.67 136 | openTime:1590163380000 137 | quoteAssetVolume:3967182.22146 138 | takerBuyBaseAssetVolume:379.770 139 | takerBuyQuoteAssetVolume:3493517.83026 140 | volume:431.250 141 | 142 | :param symbol_: 143 | :param interval_: 144 | :param limit_: 145 | :return: 146 | ''' 147 | result = self.request_client.get_candlestick_data(symbol=symbol, interval=interval, startTime=None, endTime=None, limit=limit_) 148 | self.get_candlestick_data_result = result 149 | 150 | open_list = [] 151 | close_list = [] 152 | high_list = [] 153 | low_list = [] 154 | change_list = [] 155 | for one_k_data in result: 156 | open = float(one_k_data.open) 157 | close = float(one_k_data.close) 158 | high = float(one_k_data.high) 159 | low = float(one_k_data.low) 160 | change = abs(high - low) 161 | 162 | open_list.append(open) 163 | close_list.append(close) 164 | high_list.append(high) 165 | low_list.append(low) 166 | change_list.append(change) 167 | 168 | # print(open_list, close_list, high_list, low_list, change_list) 169 | return open_list, close_list, high_list, low_list, change_list 170 | 171 | def io_get_ticker_price(self): 172 | result = self.request_client.get_symbol_price_ticker() 173 | self.get_symbol_price_ticker_result = result 174 | # 使用字典,方便快速检索现价 175 | self.ticker_price_dict = {} 176 | for one_price in result: 177 | self.ticker_price_dict[one_price.symbol] = one_price.price 178 | logger.info(self.ticker_price_dict) 179 | 180 | def io_get_position_info(self, trade_symbols=None): 181 | ''' 182 | "entryPrice": "0.00000", // 开仓均价或持仓均价 183 | "marginType": "isolated", // 逐仓模式或全仓模式 184 | "isAutoAddMargin": "false", 185 | "isolatedMargin": "0.00000000", // 逐仓保证金 186 | "leverage": "10", // 当前杠杆倍数 187 | "liquidationPrice": "0", // 参考强平价格 188 | "markPrice": "6679.50671178", // 当前标记价格 189 | "maxNotionalValue": "20000000", // 当前杠杆倍数允许的名义价值上限 190 | "positionAmt": "0.000", // 头寸数量,符号代表多空方向, 正数为多,负数为空 191 | "symbol": "BTCUSDT", // 交易对 192 | "unRealizedProfit": "0.00000000", // 持仓未实现盈亏 193 | "positionSide": "BOTH", // 持仓方向 194 | :param trade_symbols: 195 | :return: 196 | ''' 197 | 198 | result = self.request_client.get_position() 199 | self.get_position_result = result 200 | 201 | # print('len(result):',len(trade_symbols)) 202 | for one_position in result: 203 | one_symbol = one_position.symbol 204 | # 不指定交易对时全部返回,指定时只返回指定的持仓信息 205 | # 过滤持仓数量为0的 206 | if float(one_position.positionAmt == 0): 207 | continue 208 | if trade_symbols == None or (one_symbol in trade_symbols): 209 | self.one_position_info = {} 210 | self.one_position_info[one_symbol] = {} 211 | self.one_position_info[one_symbol]['symbol'] = one_symbol 212 | self.one_position_info[one_symbol]['hold_price'] = float(one_position.entryPrice) 213 | self.one_position_info[one_symbol]['margin_type'] = one_position.marginType 214 | self.one_position_info[one_symbol]['leverage'] = float(one_position.leverage) 215 | self.one_position_info[one_symbol]['liquidation_price'] = float(one_position.liquidationPrice) 216 | self.one_position_info[one_symbol]['mark_price'] = float(one_position.markPrice) 217 | self.one_position_info[one_symbol]['position_amt'] = float(one_position.positionAmt) 218 | self.one_position_info[one_symbol]['unRealized_profit'] = float(one_position.unRealizedProfit) 219 | self.one_position_info[one_symbol]['position_side'] = one_position.positionSide 220 | 221 | # # 持仓收益率 空仓数量为负,自动转换 222 | self.one_position_info[one_symbol]['position_profit_rate'] = \ 223 | (self.one_position_info[one_symbol]['position_amt'] / abs(self.one_position_info[one_symbol]['position_amt'])) \ 224 | * (float(one_position.markPrice) - float(one_position.entryPrice)) / float(one_position.entryPrice) \ 225 | * float(one_position.leverage) 226 | 227 | if one_position.positionSide == 'LONG': 228 | 229 | long_margin = float(one_position.positionAmt) * float(one_position.markPrice) / float(one_position.leverage) 230 | # 每个方向的margin_rate= long_margin/ 持仓币种数量*2 231 | self.one_position_info[one_symbol]['long_margin_rate'] = long_margin / (self.margin_balance / len(trade_symbols) / 2) 232 | 233 | self.position_info_long[one_symbol] = self.one_position_info[one_symbol] 234 | elif one_position.positionSide == 'SHORT': 235 | 236 | short_margin = float(one_position.positionAmt) * float(one_position.markPrice) / float(one_position.leverage) 237 | self.one_position_info[one_symbol]['short_margin_rate'] = short_margin / (self.margin_balance / len(trade_symbols) / 2) 238 | # print(self.one_position_info) 239 | 240 | self.position_info_short[one_symbol] = self.one_position_info[one_symbol] 241 | self.position_info_list.append(self.one_position_info[one_symbol]) 242 | 243 | # print(self.position_info_list) 244 | 245 | def io_get_account_info(self): 246 | ''' 247 | print("canDeposit: ", result.canDeposit) 248 | print("canWithdraw: ", result.canWithdraw) 249 | print("feeTier: ", result.feeTier) 250 | print("maxWithdrawAmount: ", result.maxWithdrawAmount) 251 | print("totalInitialMargin: ", result.totalInitialMargin) 252 | print("totalMaintMargin: ", result.totalMaintMargin) 253 | print("totalMarginBalance: ", result.totalMarginBalance) 254 | print("totalOpenOrderInitialMargin: ", result.totalOpenOrderInitialMargin) 255 | print("totalPositionInitialMargin: ", result.totalPositionInitialMargin) 256 | print("totalUnrealizedProfit: ", result.totalUnrealizedProfit) 257 | print("totalWalletBalance: ", result.totalWalletBalance) 258 | print("updateTime: ", result.updateTime) 259 | print("=== Assets ===") 260 | PrintMix.print_data(result.assets) 261 | print("==============") 262 | print("=== Positions ===") 263 | PrintMix.print_data(result.positions) 264 | print("==============") 265 | :return: none 266 | ''' 267 | 268 | result = self.request_client.get_account_information() 269 | 270 | self.get_account_information_result = result 271 | 272 | if self.origin_balance == 0: 273 | # 初始保证金 274 | self.origin_balance = result.totalWalletBalance 275 | 276 | self.total_balance = result.totalWalletBalance 277 | # 当前可用保证金 278 | self.margin_balance = result.totalMarginBalance 279 | self.total_unrealised_profit = result.totalUnrealizedProfit 280 | # 保证金比率 281 | self.total_margin_ratio = 1 - (float(result.totalWalletBalance) / float(result.totalMarginBalance)) 282 | 283 | self.profit = (self.total_balance - self.origin_balance) 284 | self.profit_ratio = (self.total_balance - self.origin_balance) / self.origin_balance 285 | 286 | if self.old_total_balance != self.total_balance: 287 | logger.warn('@@ Now status: total_balance:', round(self.total_balance, 6), ',total_unrealised_profit:', round(self.total_unrealised_profit, 6)) 288 | # logger.warn('@@ Now status: total_margin_ratio:',round(self.total_margin_ratio*100,6),'%, profit:',round(self.profit,6),', profit_ratio:',round(self.profit_ratio*100,6),'%') 289 | # logger.warn('@@ Now status: total_balance:', round(self.total_balance,6), ',total_unrealised_profit:', round(self.total_unrealised_profit,6)) 290 | logger.warn('@@ Now status: total_margin_ratio:', round(self.total_margin_ratio * 100, 6), '%, profit:', round(self.profit, 6), ', profit_ratio:', round(self.profit_ratio * 100, 6), '%') 291 | self.old_total_balance = self.total_balance 292 | 293 | self.assets = result.assets 294 | self.positions = result.positions 295 | self.old_total_balance = self.total_balance 296 | return 0 297 | # print(result) 298 | 299 | # ex = Binance_futures() 300 | # ex.io_get_account_info() 301 | 302 | # class Exchange: 303 | # def __init__(self,exchagneroundame): 304 | # if exchagneroundame=='binance_futures': 305 | # self.exchange=Binance_futures() 306 | -------------------------------------------------------------------------------- /main_thread.py: -------------------------------------------------------------------------------- 1 | # coding = utf-8 2 | 3 | ''' 4 | v1.0.1 5 | ''' 6 | main_verison='1.0.2' 7 | 8 | import asyncio 9 | import time, datetime, json, re, os, random 10 | import os, socks, socket 11 | import pandas as pd 12 | import numpy as np 13 | import threading 14 | import random 15 | import logging 16 | 17 | from utils.callib import * 18 | from utils import logger 19 | 20 | isdebug=True 21 | 22 | if isdebug: 23 | debug_level='INFO' 24 | log_path=None 25 | logfile_name=None 26 | else: 27 | debug_level = 'DEBUG' 28 | log_path = './logs' 29 | logfile_name = 'log_'+ main_verison + '_'+str(datetime.datetime.now())[-6:]+'.txt' 30 | logger.initLogger(log_level=debug_level, log_path=log_path, logfile_name=logfile_name, clear=False, backup_count=0) 31 | 32 | from exchanges import * 33 | from strategys import * 34 | 35 | class trade_obj: 36 | ''' 37 | 单交易所,多币种策略 ,移除fmz的函数 38 | #rateLimits: 39 | 带权重请求每分钟最多2400,平均每秒40权重 40 | [map[interval:MINUTE intervalNum:1 limit:2400 rateLimitType:REQUEST_WEIGHT] 41 | 每秒最多下单20 42 | map[interval:MINUTE intervalNum:1 limit:1200 rateLimitType:ORDERS]] 43 | ''' 44 | 45 | def __init__(self, exchange_name=None, trade_symbols=None, init_balance=0 ,leverage=75): 46 | 47 | self.trade_symbols = trade_symbols 48 | self.init_balance=init_balance 49 | 50 | # 初始化交易所对象 51 | self.exchange = Binance_futures(init_balance=self.init_balance,leverage=leverage) 52 | self.account = 0 53 | self.postion = 0 54 | 55 | # 初始化最新价 56 | self.exchange.io_get_ticker_price() 57 | 58 | self._now_datas = {} 59 | for one_symbol in trade_symbols: 60 | # 初始化交易对储存,并设置杠杆率 61 | self.exchange.request_client.change_initial_leverage(symbol=one_symbol, leverage=leverage) 62 | self._now_datas[one_symbol] = {} 63 | 64 | # 策略选择器 65 | self.strategy = Strategys(strategy_name='1', exchange=self.exchange) 66 | pass 67 | 68 | def update_account(self): 69 | # 无返回值,不阻塞,自休眠时间,没有锁 70 | logger.debug('update_account start:', self.exchange.get_account_information_result) 71 | self.exchange.io_get_account_info() 72 | logger.debug('update_account done:', self.exchange.get_account_information_result) 73 | # 休眠500ms 74 | time.sleep(0.1) 75 | return 0 76 | 77 | def update_postion(self): 78 | # 无返回值,不阻塞,自休眠时间,没有锁 79 | # 是否必须更新持仓信息之后才能平仓,加锁 80 | logger.debug('update_postion start:', self.exchange.get_position_result) 81 | self.exchange.io_get_position_info(self.trade_symbols) 82 | logger.debug('update_postion done:', self.exchange.get_position_result) 83 | return 0 84 | 85 | def update_ticker_price(self): 86 | # 无返回值,不阻塞,自休眠时间,没有锁 87 | logger.debug('update_ticker_price start:', self.exchange.ticker_price_dict) 88 | self.exchange.io_get_ticker_price() 89 | logger.debug('update_ticker_price done:', self.exchange.ticker_price_dict) 90 | return 0 91 | 92 | def on_cover(self): 93 | ''' 94 | 平仓条件判断+平仓 95 | :return: 96 | ''' 97 | 98 | def on_open(self): 99 | ''' 100 | 开仓条件加判断 101 | :return: 102 | ''' 103 | 104 | def on_tick(self, symbol): 105 | # 防止限制频率,随机sleep 106 | 107 | time.sleep(random.random()/10) 108 | ''' 109 | 对单个symbol处理的主入口,阻塞(必须按顺序),或者开新的线程处理平仓 110 | 流程: 111 | 112 | 计算指标 113 | 获取depth,或者最优挂单? 114 | 115 | 平仓判断 116 | 买入判断 117 | 下单 118 | :param symbol: 119 | :return: 120 | ''' 121 | # logger.info('on_tick start', symbol) 122 | # 更新k线,本函数内会阻塞 123 | open_list, close_list, high_list, low_list, change_list = self.exchange.io_get_klines(symbol) 124 | 125 | # logger.debug('ticker_now_price:', self.exchange.ticker_price_dict[symbol], 'kline_now_price:', close_list[-1]) 126 | # 指标计算 127 | # MA n 128 | 129 | 130 | # 更新策略所需参数 131 | self.strategy.update(strategy_name='1', symbol=symbol, trade_symbols=self.trade_symbols,exchange=self.exchange, datas=self._now_datas) 132 | # 交易完后更新或新增参数 133 | #self.strategy.on_cover('1') 134 | # 多线程下单 135 | pass 136 | 137 | 138 | def stop_loss(self): 139 | pass 140 | 141 | def run(self): 142 | #print('\n############ main thread start ############\n') 143 | logger.info('!!!!! init success , main thread is runing , start time:',datetime.datetime.now()) 144 | ''' 145 | 主入口 146 | 因为交易所的有些api可以获取多个symbol的数据,所以有两种协程方案: 147 | 1.不同symbol互不干扰,每个启动一个协程 148 | 2.同一获取行情信息,下单等逻辑启用协程 149 | 150 | 方案1会发很多重复的请求,所以用方案2 151 | 循环顺序: 152 | 更新行情(获取最新价,初始化) 153 | 154 | 更新账户信息 155 | 更新持仓信息 156 | 更新可以统一获取的行情(获取最新价,获取k线) 157 | for i in trade_symbols: 158 | # 开新线程,获取需要独自获取的行情 159 | 止损 160 | 交易(获取depth) 161 | 162 | 更新状态 163 | 164 | : 165 | return: 166 | ''' 167 | pass 168 | 169 | if __name__ == '__main__': 170 | 171 | #代理设置 172 | if socket.gethostname() == 'DESKTOP-****': 173 | print(socket.gethostname()) 174 | socks.set_default_proxy(socks.SOCKS5, '127.0.0.1', 1088) 175 | socket.socket = socks.socksocket 176 | 177 | #交易设置 178 | trade_symbols = ['BTCUSDT', 'ETHUSDT', 'BCHUSDT','ETCUSDT','LINKUSDT', 'EOSUSDT','XRPUSDT','IOSTUSDT'] 179 | init_balance = 99999 180 | set_leverage = 10 181 | 182 | _trade = trade_obj(trade_symbols=trade_symbols, init_balance=init_balance, leverage=set_leverage) 183 | _trade.run() 184 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | sympy~=1.5.1 2 | pandas~=0.24.2 3 | numpy~=1.16.4 4 | requests~=2.22.0 5 | aiohttp~=2.3.10 6 | APScheduler~=3.6.3 7 | websocket~=0.2.1 8 | websocket-client~=0.56.0 9 | pysocks -------------------------------------------------------------------------------- /strategys.py: -------------------------------------------------------------------------------- 1 | # coding = utf-8 2 | """ 3 | Datetime : 2020/5/23 15:48 4 | Author : cloudthink 5 | Info : 策略可替换模块,可以方便的在这里修改和添加策略 6 | """ 7 | import random,time,datetime 8 | from utils import logger, tools, callib 9 | 10 | 11 | class Strategys: 12 | ''' 13 | 必须保持更新,可能需要每个on_tick实例化并调用 14 | ''' 15 | 16 | def __init__(self, strategy_name=None, symbol=None, exchange=None, datas=None): 17 | self._symbol = symbol 18 | self._strategy_name = strategy_name 19 | self._exchange = exchange 20 | self._datas = datas 21 | 22 | def update(self, strategy_name=None, symbol=None, trade_symbols=None,exchange=None, datas=None): 23 | ''' 24 | 运行时更新策略? 25 | 更新exchange和trade 26 | :return: 27 | ''' 28 | self._symbol = symbol 29 | self._trade_symbols=trade_symbols 30 | self._trade_symbols_num=len(self._trade_symbols) 31 | self._strategy_name = strategy_name 32 | self._exchange = exchange 33 | # 只负责接受数据,不负责解析,解析放在具体的on_open和on_close函数 34 | self._datas = datas 35 | # print(self._datas[self._symbol]['kline_close_price']) 36 | 37 | def on_open(self, open_strategy_name='1',symbol_=None): 38 | 39 | 40 | return self._datas 41 | 42 | def on_cover(self, cover_strategy_name='1',symbol_=None): 43 | 44 | return 0 45 | # return self._datas 46 | -------------------------------------------------------------------------------- /utils/callib.py: -------------------------------------------------------------------------------- 1 | # coding = utf-8 2 | """ 3 | Datetime : 2020/5/23 22:36 4 | Author : cloudthink 5 | Info : 更新为python3版本适配 6 | """ 7 | 8 | import math 9 | import threading 10 | import numpy as np 11 | import pandas as pd 12 | 13 | 14 | class MyThread(threading.Thread): 15 | ''' 16 | 有返回值的线程,不阻塞 17 | ''' 18 | def __init__(self, func, args=()): 19 | super(MyThread, self).__init__() 20 | self.func = func 21 | self.args = args 22 | 23 | def run(self): 24 | # if self.func(*self.args): 25 | self.result = self.func(*self.args) 26 | 27 | def get_result(self): 28 | try: 29 | return self.result 30 | except: 31 | return {} 32 | 33 | class MyThread_void(threading.Thread): 34 | ''' 35 | 无返回值的线程,不阻塞 36 | ''' 37 | 38 | def __init__(self, func, args=()): 39 | super(MyThread_void, self).__init__() 40 | self.func = func 41 | self.args = args 42 | 43 | def run(self): 44 | # if self.func(*self.args): 45 | self.func(*self.args) 46 | 47 | 48 | 49 | class Std: 50 | @staticmethod 51 | def _skip(arr, period): 52 | k = 0 53 | for j in range(0, len(arr)): 54 | if arr[j] is not None: 55 | k+=1 56 | if k == period: 57 | break 58 | return j 59 | 60 | @staticmethod 61 | def _sum(arr, num): 62 | s = 0.0 63 | for i in range(0, num): 64 | if arr[i] is not None: 65 | s += arr[i] 66 | return s 67 | 68 | @staticmethod 69 | def _avg(arr, num): 70 | if len(arr) == 0: 71 | return 0 72 | s = 0.0 73 | n = 0 74 | for i in range(0, min(len(arr), num)): 75 | if arr[i] is not None: 76 | s += arr[i] 77 | n += 1 78 | if n == 0: 79 | return 0 80 | return s / n 81 | 82 | @staticmethod 83 | def _zeros(n): 84 | return [0.0] * n 85 | 86 | @staticmethod 87 | def _set(arr, start, end, value): 88 | for i in range(start, min(len(arr), end)): 89 | arr[i] = value 90 | 91 | @staticmethod 92 | def _diff(a, b): 93 | d = [None] * len(b) 94 | for i in range(0, len(b)): 95 | if a[i] is not None and b[i] is not None: 96 | d[i] = a[i] - b[i] 97 | return d 98 | 99 | @staticmethod 100 | def _move_diff(a): 101 | d = [None] * (len(a)-1) 102 | for i in range(1, len(a)): 103 | d[i-1] = a[i] - a[i-1] 104 | return d 105 | 106 | @staticmethod 107 | def _cmp(arr, start, end, cmpFunc): 108 | v = arr[start] 109 | for i in range(start, end): 110 | v = cmpFunc(arr[i], v) 111 | return v 112 | 113 | @staticmethod 114 | def _filt(records, n, attr, iv, cmpFunc): 115 | if len(records) < 2: 116 | return None 117 | v = iv 118 | pos = 0 119 | if n != 0: 120 | pos = len(records) - min(len(records)-1, n) - 1 121 | for i in range(len(records)-2, pos-1, -1): 122 | if records[i] is not None: 123 | if attr is not None: 124 | v = cmpFunc(v, records[i][attr]) 125 | else: 126 | v = cmpFunc(v, records[i]) 127 | return v 128 | 129 | @staticmethod 130 | def _ticks(records): 131 | if len(records) == 0: 132 | return [] 133 | #else: 134 | # return records 135 | 136 | #if 'Close' not in records[0]: 137 | # return records 138 | 139 | ticks = [None] * len(records) 140 | for i in range(0, len(records)): 141 | ticks[i] = records[i]#['Close'] 142 | return ticks 143 | 144 | @staticmethod 145 | def _sma(S, period): 146 | R = Std._zeros(len(S)) 147 | j = Std._skip(S, period) 148 | Std._set(R, 0, j, None) 149 | if j < len(S): 150 | s = 0 151 | for i in range(j, len(S)): 152 | if i == j: 153 | s = Std._sum(S, i+1) 154 | else: 155 | s += S[i] - S[i-period] 156 | R[i] = s / period 157 | return R 158 | 159 | @staticmethod 160 | def _smma(S, period): 161 | R = Std._zeros(len(S)) 162 | j = Std._skip(S, period) 163 | Std._set(R, 0, j, None) 164 | if j < len(S): 165 | R[j] = Std._avg(S, j+1) 166 | for i in range(j+1, len(S)): 167 | R[i] = (R[i-1] * (period-1) + S[i]) / period 168 | return R 169 | 170 | @staticmethod 171 | def _ema(S, period): 172 | R = Std._zeros(len(S)) 173 | multiplier = 2.0 / (period + 1) 174 | j = Std._skip(S, period) 175 | Std._set(R, 0, j, None) 176 | if j < len(S): 177 | R[j] = Std._avg(S, j+1) 178 | for i in range(j+1, len(S)): 179 | R[i] = ((S[i] - R[i-1] ) * multiplier) + R[i-1] 180 | return R 181 | 182 | class TA: 183 | @staticmethod 184 | def Highest(records, n, attr=None): 185 | return Std._filt(records, n, attr, 5e-324, max) 186 | 187 | @staticmethod 188 | def Lowest(records, n, attr=None): 189 | return Std._filt(records, n, attr, 1.7976931348623157e+308, min) 190 | 191 | @staticmethod 192 | def MA(records, period=9): 193 | return Std._sma(Std._ticks(records), period) 194 | 195 | @staticmethod 196 | def SMA(records, period=9): 197 | return Std._sma(Std._ticks(records), period) 198 | 199 | @staticmethod 200 | def EMA(records, period=9): 201 | return Std._ema(Std._ticks(records), period) 202 | 203 | @staticmethod 204 | def MACD(records, fastEMA=12, slowEMA=26, signalEMA=9): 205 | ticks = Std._ticks(records) 206 | slow = Std._ema(ticks, slowEMA) 207 | fast = Std._ema(ticks, fastEMA) 208 | # DIF 209 | dif = Std._diff(fast, slow) 210 | # DEA 211 | signal = Std._ema(dif, signalEMA) 212 | histogram = Std._diff(dif, signal) 213 | return [ dif, signal, histogram] 214 | 215 | @staticmethod 216 | def BOLL(records, period=20, multiplier=2): 217 | S = Std._ticks(records) 218 | j = period - 1 219 | while j < len(S) and (S[j] is None): 220 | j+=1 221 | UP = Std._zeros(len(S)) 222 | MB = Std._zeros(len(S)) 223 | DN = Std._zeros(len(S)) 224 | Std._set(UP, 0, j, None) 225 | Std._set(MB, 0, j, None) 226 | Std._set(DN, 0, j, None) 227 | n = 0.0 228 | for i in range(j, len(S)): 229 | if i == j: 230 | for k in range(0, period): 231 | n += S[k] 232 | else: 233 | n = n + S[i] - S[i - period] 234 | ma = n / period 235 | d = 0 236 | for k in range(i+1-period, i+1): 237 | d += (S[k] - ma) * (S[k] - ma) 238 | stdev = math.sqrt(d / period) 239 | up = ma + (multiplier * stdev) 240 | dn = ma - (multiplier * stdev) 241 | UP[i] = up 242 | MB[i] = ma 243 | DN[i] = dn 244 | return [UP, MB, DN] 245 | 246 | @staticmethod 247 | def KDJ(records, n=9, k=3, d=3): 248 | RSV = Std._zeros(len(records)) 249 | Std._set(RSV, 0, n - 1, None) 250 | K = Std._zeros(len(records)) 251 | D = Std._zeros(len(records)) 252 | J = Std._zeros(len(records)) 253 | 254 | hs = Std._zeros(len(records)) 255 | ls = Std._zeros(len(records)) 256 | for i in range(0, len(records)): 257 | hs[i] = records[i]['High'] 258 | ls[i] = records[i]['Low'] 259 | 260 | for i in range(0, len(records)): 261 | if i >= (n - 1): 262 | c = records[i]['Close'] 263 | h = Std._cmp(hs, i - (n - 1), i + 1, max) 264 | l = Std._cmp(ls, i - (n - 1), i + 1, min) 265 | RSV[i] = 100 * ((c - l) / (h - l)) 266 | K[i] = float(1 * RSV[i] + (k - 1) * K[i - 1]) / k 267 | D[i] = float(1 * K[i] + (d - 1) * D[i - 1]) / d 268 | else: 269 | K[i] = D[i] = 50.0 270 | RSV[i] = 0.0 271 | J[i] = 3 * K[i] - 2 * D[i] 272 | # remove prefix 273 | for i in range(0, n-1): 274 | K[i] = D[i] = J[i] = None 275 | return [K, D, J] 276 | 277 | @staticmethod 278 | def RSI(records, period=14): 279 | n = period 280 | rsi = Std._zeros(len(records)) 281 | Std._set(rsi, 0, len(rsi), None) 282 | if len(records) < n: 283 | return rsi 284 | 285 | ticks = Std._ticks(records) 286 | deltas = Std._move_diff(ticks) 287 | seed = deltas[:n] 288 | up = 0.0 289 | down = 0.0 290 | for i in range(0, len(seed)): 291 | if seed[i] >= 0: 292 | up += seed[i] 293 | else: 294 | down += seed[i] 295 | up /= n 296 | down /= n 297 | down = -down 298 | if down != 0: 299 | rs = up / down 300 | else: 301 | rs = 0 302 | rsi[n] = 100 - 100 / (1 + rs) 303 | delta = 0.0 304 | upval = 0.0 305 | downval = 0.0 306 | for i in range(n+1, len(ticks)): 307 | delta = deltas[i - 1] 308 | if delta > 0: 309 | upval = delta 310 | downval = 0.0 311 | else: 312 | upval = 0.0 313 | downval = -delta 314 | up = (up * (n - 1) + upval) / n 315 | down = (down * (n - 1) + downval) / n 316 | rs = up / down 317 | rsi[i] = 100 - 100 / (1 + rs) 318 | return rsi 319 | @staticmethod 320 | def OBV(records): 321 | if len(records) == 0: 322 | return [] 323 | 324 | if 'Close' not in records[0]: 325 | raise ("TA.OBV argument must KLine") 326 | 327 | R = Std._zeros(len(records)) 328 | for i in range(0, len(records)): 329 | if i == 0: 330 | R[i] = records[i]['Volume'] 331 | elif records[i]['Close'] >= records[i - 1]['Close']: 332 | R[i] = R[i - 1] + records[i]['Volume'] 333 | else: 334 | R[i] = R[i - 1] - records[i]['Volume'] 335 | return R 336 | 337 | @staticmethod 338 | def ATR(records, period=14): 339 | if len(records) == 0: 340 | return [] 341 | if 'Close' not in records[0]: 342 | raise ("TA.ATR argument must KLine") 343 | 344 | R = Std._zeros(len(records)) 345 | m = 0.0 346 | n = 0.0 347 | for i in range(0, len(records)): 348 | TR = 0 349 | if i == 0: 350 | TR = records[i]['High'] - records[i]['Low'] 351 | else: 352 | TR = max(records[i]['High'] - records[i]['Low'], abs(records[i]['High'] - records[i - 1]['Close']), abs(records[i - 1]['Close'] - records[i]['Low'])) 353 | m += TR 354 | if i < period: 355 | n = m / (i + 1) 356 | else: 357 | n = (((period - 1) * n) + TR) / period 358 | R[i] = n 359 | return R 360 | 361 | @staticmethod 362 | def Alligator(records, jawLength=13, teethLength=8, lipsLength=5): 363 | ticks = [] 364 | for i in range(0, len(records)): 365 | ticks.append((records[i]['High'] + records[i]['Low']) / 2) 366 | return [ 367 | [None]*8+Std._smma(ticks, jawLength), # // jaw (blue) 368 | [None]*5+Std._smma(ticks, teethLength), # teeth (red) 369 | [None]*3+Std._smma(ticks, lipsLength) # lips (green) 370 | ] 371 | 372 | @staticmethod 373 | def CMF(records, periods=20): 374 | ret = [] 375 | sumD = 0.0 376 | sumV = 0.0 377 | arrD = [] 378 | arrV = [] 379 | for i in range(0, len(records)): 380 | d = 0.0 381 | if records[i]['High'] != records[i]['Low']: 382 | d = (2 * records[i]['Close'] - records[i]['Low'] - records[i]['High']) / (records[i]['High'] - records[i]['Low']) * records[i]['Volume'] 383 | arrD.append(d) 384 | arrV.append(records[i]['Volume']) 385 | sumD += d 386 | sumV += records[i]['Volume'] 387 | if i >= periods: 388 | sumD -= arrD.pop(0) 389 | sumV -= arrV.pop(0) 390 | ret.append(sumD / sumV) 391 | return ret 392 | -------------------------------------------------------------------------------- /utils/logger.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | import os 4 | import sys 5 | import shutil 6 | import logging 7 | import traceback 8 | from logging.handlers import TimedRotatingFileHandler 9 | 10 | initialized = False 11 | 12 | 13 | 14 | def initLogger(log_level="DEBUG", log_path=None, logfile_name=None, clear=False, backup_count=0): 15 | #强制模式开关 16 | log_level="WARN" # 17 | #log_path = './logs' 18 | #logfile_name = None#'log_3' + '.txt' 19 | 20 | """ 初始化日志输出 21 | @param log_level 日志级别 DEBUG/INFO 22 | 'CRITICAL': CRITICAL, 23 | 'FATAL': FATAL, 24 | 'ERROR': ERROR, 25 | 'WARN': WARNING, 26 | 'WARNING': WARNING, 27 | 'INFO': INFO, 28 | 'DEBUG': DEBUG, 29 | 'NOTSET': NOTSET, 30 | @param log_path 日志输出路径 31 | @param logfile_name 日志文件名 32 | @param clear 初始化的时候,是否清理之前的日志文件 33 | @param backup_count 保存按天分割的日志文件个数,默认0为永久保存所有日志文件 34 | """ 35 | global initialized 36 | if initialized: 37 | return 38 | logger = logging.getLogger() 39 | logger.setLevel(log_level) 40 | if logfile_name: 41 | if clear and os.path.isdir(log_path): 42 | shutil.rmtree(log_path) 43 | if not os.path.isdir(log_path): 44 | os.makedirs(log_path) 45 | logfile = os.path.join(log_path, logfile_name) 46 | print("init logger ... save to file:", logfile) 47 | handler_file = TimedRotatingFileHandler(logfile, "midnight", backupCount=backup_count, encoding='utf-8') 48 | handler_console = logging.StreamHandler() 49 | 50 | fmt_str = "%(levelname)1.1s [%(asctime)s] %(message)s" 51 | fmt = logging.Formatter(fmt=fmt_str, datefmt=None) 52 | 53 | handler_file.setFormatter(fmt) 54 | handler_file.setLevel('DEBUG') 55 | 56 | handler_console.setFormatter(fmt) 57 | 58 | logger.addHandler(handler_file) 59 | logger.addHandler(handler_console) 60 | else: 61 | print("init logger ... just output to console") 62 | handler = logging.StreamHandler() 63 | 64 | fmt_str = "%(levelname)1.1s [%(asctime)s] %(message)s" 65 | fmt = logging.Formatter(fmt=fmt_str, datefmt=None) 66 | handler.setFormatter(fmt) 67 | logger.addHandler(handler) 68 | 69 | initialized = True 70 | 71 | 72 | def info(*args, **kwargs): 73 | func_name, kwargs = _log_msg_header(*args, **kwargs) 74 | logging.info(_log(func_name, *args, **kwargs)) 75 | 76 | 77 | def warn(*args, **kwargs): 78 | msg_header, kwargs = _log_msg_header(*args, **kwargs) 79 | logging.warning(_log(msg_header, *args, **kwargs)) 80 | 81 | 82 | def debug(*args, **kwargs): 83 | msg_header, kwargs = _log_msg_header(*args, **kwargs) 84 | logging.debug(_log(msg_header, *args, **kwargs)) 85 | 86 | 87 | def error(*args, **kwargs): 88 | logging.error("*" * 60) 89 | msg_header, kwargs = _log_msg_header(*args, **kwargs) 90 | logging.error(_log(msg_header, *args, **kwargs)) 91 | logging.error("*" * 60) 92 | 93 | 94 | def exception(*args, **kwargs): 95 | logging.error("*" * 60) 96 | msg_header, kwargs = _log_msg_header(*args, **kwargs) 97 | logging.error(_log(msg_header, *args, **kwargs)) 98 | # exc_info = sys.exc_info() 99 | # traceback.print_exception(*exc_info) 100 | logging.error(traceback.format_exc()) 101 | logging.error("*" * 60) 102 | 103 | 104 | def _log(msg_header, *args, **kwargs): 105 | _log_msg = msg_header 106 | for l in args: 107 | if type(l) == tuple: 108 | ps = str(l) 109 | else: 110 | try: 111 | ps = "%r" % l 112 | except: 113 | ps = str(l) 114 | if type(l) == str: 115 | _log_msg += ps[1:-1] + " " 116 | else: 117 | _log_msg += ps + " " 118 | if len(kwargs) > 0: 119 | _log_msg += str(kwargs) 120 | return _log_msg 121 | 122 | 123 | def _log_msg_header(*args, **kwargs): 124 | """ 打印日志的message头 125 | @param kwargs["caller"] 调用的方法所属类对象 126 | * NOTE: logger.xxx(... caller=self) for instance method 127 | logger.xxx(... caller=cls) for @classmethod 128 | """ 129 | cls_name = "" 130 | func_name = sys._getframe().f_back.f_back.f_code.co_name 131 | session_id = "-" 132 | try: 133 | _caller = kwargs.get("caller", None) 134 | if _caller: 135 | if not hasattr(_caller, "__name__"): 136 | _caller = _caller.__class__ 137 | cls_name = _caller.__name__ 138 | del kwargs["caller"] 139 | except: 140 | pass 141 | finally: 142 | msg_header = "[{session_id}] [{cls_name}.{func_name}] ".format(cls_name=cls_name, func_name=func_name, 143 | session_id=session_id) 144 | return msg_header, kwargs --------------------------------------------------------------------------------