python-okx 0.4.0__py3-none-any.whl → 0.4.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- okx/Account.py +53 -10
- okx/Convert.py +9 -3
- okx/DualInvest.py +59 -0
- okx/Funding.py +7 -6
- okx/Grid.py +6 -2
- okx/PublicData.py +20 -0
- okx/Trade.py +49 -12
- okx/TradingData.py +25 -0
- okx/__init__.py +1 -1
- okx/consts.py +19 -1
- okx/okxclient.py +10 -1
- okx/websocket/WsPrivateAsync.py +154 -18
- okx/websocket/WsPublicAsync.py +93 -15
- {python_okx-0.4.0.dist-info → python_okx-0.4.2.dist-info}/METADATA +97 -15
- {python_okx-0.4.0.dist-info → python_okx-0.4.2.dist-info}/RECORD +17 -16
- {python_okx-0.4.0.dist-info → python_okx-0.4.2.dist-info}/WHEEL +1 -1
- {python_okx-0.4.0.dist-info → python_okx-0.4.2.dist-info}/top_level.txt +0 -0
okx/Account.py
CHANGED
|
@@ -27,28 +27,30 @@ class AccountAPI(OkxClient):
|
|
|
27
27
|
params = {'instType': instType, 'instId': instId, 'posId': posId}
|
|
28
28
|
return self._request_with_params(GET, POSITION_INFO, params)
|
|
29
29
|
|
|
30
|
-
def position_builder(self, acctLv=None,inclRealPosAndEq=
|
|
31
|
-
simAsset=None):
|
|
30
|
+
def position_builder(self, acctLv=None, inclRealPosAndEq=None, lever=None, greeksType=None, simPos=None,
|
|
31
|
+
simAsset=None, idxVol=None):
|
|
32
32
|
params = {}
|
|
33
33
|
if acctLv is not None:
|
|
34
34
|
params['acctLv'] = acctLv
|
|
35
35
|
if inclRealPosAndEq is not None:
|
|
36
36
|
params['inclRealPosAndEq'] = inclRealPosAndEq
|
|
37
37
|
if lever is not None:
|
|
38
|
-
params['
|
|
38
|
+
params['lever'] = lever
|
|
39
39
|
if greeksType is not None:
|
|
40
|
-
params['
|
|
40
|
+
params['greeksType'] = greeksType
|
|
41
41
|
if simPos is not None:
|
|
42
42
|
params['simPos'] = simPos
|
|
43
43
|
if simAsset is not None:
|
|
44
44
|
params['simAsset'] = simAsset
|
|
45
|
+
if idxVol is not None:
|
|
46
|
+
params['idxVol'] = idxVol
|
|
45
47
|
return self._request_with_params(POST, POSITION_BUILDER, params)
|
|
46
48
|
|
|
47
49
|
# Get Bills Details (recent 7 days)
|
|
48
50
|
def get_account_bills(self, instType='', ccy='', mgnMode='', ctType='', type='', subType='', after='', before='',
|
|
49
|
-
limit=''):
|
|
51
|
+
limit='', begin='', end=''):
|
|
50
52
|
params = {'instType': instType, 'ccy': ccy, 'mgnMode': mgnMode, 'ctType': ctType, 'type': type,
|
|
51
|
-
'subType': subType, 'after': after, 'before': before, 'limit': limit}
|
|
53
|
+
'subType': subType, 'after': after, 'before': before, 'limit': limit, 'begin': begin, 'end': end}
|
|
52
54
|
return self._request_with_params(GET, BILLS_DETAIL, params)
|
|
53
55
|
|
|
54
56
|
# Get Bills Details (recent 3 months)
|
|
@@ -74,14 +76,18 @@ class AccountAPI(OkxClient):
|
|
|
74
76
|
return self._request_with_params(POST, SET_LEVERAGE, params)
|
|
75
77
|
|
|
76
78
|
# Get Maximum Tradable Size For Instrument
|
|
77
|
-
def get_max_order_size(self, instId, tdMode, ccy='', px=''):
|
|
79
|
+
def get_max_order_size(self, instId, tdMode, ccy='', px='', tradeQuoteCcy=None):
|
|
78
80
|
params = {'instId': instId, 'tdMode': tdMode, 'ccy': ccy, 'px': px}
|
|
81
|
+
if tradeQuoteCcy is not None:
|
|
82
|
+
params['tradeQuoteCcy'] = tradeQuoteCcy
|
|
79
83
|
return self._request_with_params(GET, MAX_TRADE_SIZE, params)
|
|
80
84
|
|
|
81
85
|
# Get Maximum Available Tradable Amount
|
|
82
|
-
def get_max_avail_size(self, instId, tdMode, ccy='', reduceOnly='', unSpotOffset='', quickMgnType=''):
|
|
86
|
+
def get_max_avail_size(self, instId, tdMode, ccy='', reduceOnly='', unSpotOffset='', quickMgnType='', tradeQuoteCcy=None):
|
|
83
87
|
params = {'instId': instId, 'tdMode': tdMode, 'ccy': ccy, 'reduceOnly': reduceOnly,
|
|
84
88
|
'unSpotOffset': unSpotOffset, 'quickMgnType': quickMgnType}
|
|
89
|
+
if tradeQuoteCcy is not None:
|
|
90
|
+
params['tradeQuoteCcy'] = tradeQuoteCcy
|
|
85
91
|
return self._request_with_params(GET, MAX_AVAIL_SIZE, params)
|
|
86
92
|
|
|
87
93
|
# Increase / Decrease margin
|
|
@@ -100,13 +106,17 @@ class AccountAPI(OkxClient):
|
|
|
100
106
|
return self._request_with_params(GET, GET_INSTRUMENTS, params)
|
|
101
107
|
|
|
102
108
|
# Get the maximum loan of isolated MARGIN
|
|
103
|
-
def get_max_loan(self, instId, mgnMode, mgnCcy=''):
|
|
109
|
+
def get_max_loan(self, instId, mgnMode, mgnCcy='', tradeQuoteCcy=None):
|
|
104
110
|
params = {'instId': instId, 'mgnMode': mgnMode, 'mgnCcy': mgnCcy}
|
|
111
|
+
if tradeQuoteCcy is not None:
|
|
112
|
+
params['tradeQuoteCcy'] = tradeQuoteCcy
|
|
105
113
|
return self._request_with_params(GET, MAX_LOAN, params)
|
|
106
114
|
|
|
107
115
|
# Get Fee Rates
|
|
108
|
-
def get_fee_rates(self, instType, instId='', uly='', category='', instFamily=''):
|
|
116
|
+
def get_fee_rates(self, instType, instId='', uly='', category='', instFamily='', groupId=None):
|
|
109
117
|
params = {'instType': instType, 'instId': instId, 'uly': uly, 'category': category, 'instFamily': instFamily}
|
|
118
|
+
if groupId is not None:
|
|
119
|
+
params['groupId'] = groupId
|
|
110
120
|
return self._request_with_params(GET, FEE_RATES, params)
|
|
111
121
|
|
|
112
122
|
# Get interest-accrued
|
|
@@ -323,3 +333,36 @@ class AccountAPI(OkxClient):
|
|
|
323
333
|
def spot_borrow_repay_history(self, ccy='', type='', after='', before='', limit=''):
|
|
324
334
|
params = {'ccy': ccy, 'type': type, 'after': after, 'before': before, 'limit': limit}
|
|
325
335
|
return self._request_with_params(GET, GET_BORROW_REPAY_HISTORY, params)
|
|
336
|
+
|
|
337
|
+
def set_auto_earn(self, ccy, action, earnType=None):
|
|
338
|
+
params = {'ccy': ccy, 'action': action}
|
|
339
|
+
if earnType is not None:
|
|
340
|
+
params['earnType'] = earnType
|
|
341
|
+
return self._request_with_params(POST, SET_AUTO_EARN, params)
|
|
342
|
+
|
|
343
|
+
# Set trading config (BROK-1724). type is required (e.g. 'stgyType');
|
|
344
|
+
# stgyType (0=general, 1=delta neutral) applies only when type == 'stgyType'.
|
|
345
|
+
def set_trading_config(self, type='', stgyType=''):
|
|
346
|
+
params = {'type': type}
|
|
347
|
+
if stgyType != '':
|
|
348
|
+
params['stgyType'] = stgyType
|
|
349
|
+
return self._request_with_params(POST, SET_TRADING_CONFIG, params)
|
|
350
|
+
|
|
351
|
+
# Pre-check whether the delta-neutral switch is allowed (BROK-1724)
|
|
352
|
+
def precheck_set_delta_neutral(self, stgyType=''):
|
|
353
|
+
params = {'stgyType': stgyType}
|
|
354
|
+
return self._request_with_params(GET, PRECHECK_SET_DELTA_NEUTRAL, params)
|
|
355
|
+
|
|
356
|
+
# Get the list of supported bill subtypes (BROK-1728)
|
|
357
|
+
def get_bill_type(self, type=''):
|
|
358
|
+
params = {}
|
|
359
|
+
if type != '':
|
|
360
|
+
params['type'] = type
|
|
361
|
+
return self._request_with_params(GET, BILL_TYPE, params)
|
|
362
|
+
|
|
363
|
+
# Apply for an asynchronous bill history archive by year + quarter (BROK-1728)
|
|
364
|
+
def apply_bills(self, year='', quarter='', type=''):
|
|
365
|
+
params = {'year': year, 'quarter': quarter}
|
|
366
|
+
if type != '':
|
|
367
|
+
params['type'] = type
|
|
368
|
+
return self._request_with_params(POST, BILLS_APPLY, params)
|
okx/Convert.py
CHANGED
|
@@ -10,16 +10,22 @@ class ConvertAPI(OkxClient):
|
|
|
10
10
|
params = {}
|
|
11
11
|
return self._request_with_params(GET, GET_CURRENCIES, params)
|
|
12
12
|
|
|
13
|
-
def get_currency_pair(self, fromCcy='', toCcy=''):
|
|
13
|
+
def get_currency_pair(self, fromCcy='', toCcy='', convertMode=None):
|
|
14
14
|
params = {"fromCcy": fromCcy, 'toCcy': toCcy}
|
|
15
|
+
if convertMode is not None:
|
|
16
|
+
params['convertMode'] = convertMode
|
|
15
17
|
return self._request_with_params(GET, GET_CURRENCY_PAIR, params)
|
|
16
18
|
|
|
17
|
-
def estimate_quote(self, baseCcy = '', quoteCcy = '', side = '', rfqSz = '', rfqSzCcy = '', clQReqId = '',tag=''):
|
|
19
|
+
def estimate_quote(self, baseCcy = '', quoteCcy = '', side = '', rfqSz = '', rfqSzCcy = '', clQReqId = '',tag='', convertMode=None):
|
|
18
20
|
params = {'baseCcy': baseCcy, 'quoteCcy': quoteCcy, 'side':side, 'rfqSz':rfqSz, 'rfqSzCcy':rfqSzCcy, 'clQReqId':clQReqId,'tag':tag}
|
|
21
|
+
if convertMode is not None:
|
|
22
|
+
params['convertMode'] = convertMode
|
|
19
23
|
return self._request_with_params(POST, ESTIMATE_QUOTE, params)
|
|
20
24
|
|
|
21
|
-
def convert_trade(self, quoteId = '', baseCcy = '', quoteCcy = '', side = '', sz = '', szCcy = '', clTReqId = '',tag=''):
|
|
25
|
+
def convert_trade(self, quoteId = '', baseCcy = '', quoteCcy = '', side = '', sz = '', szCcy = '', clTReqId = '',tag='', convertMode=None):
|
|
22
26
|
params = {'quoteId': quoteId, 'baseCcy': baseCcy, 'quoteCcy':quoteCcy, 'side':side, 'sz':sz, 'szCcy':szCcy, 'clTReqId':clTReqId,'tag':tag}
|
|
27
|
+
if convertMode is not None:
|
|
28
|
+
params['convertMode'] = convertMode
|
|
23
29
|
return self._request_with_params(POST, CONVERT_TRADE, params)
|
|
24
30
|
|
|
25
31
|
def get_convert_history(self, after = '', before = '', limit = '',tag=''):
|
okx/DualInvest.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from .okxclient import OkxClient
|
|
2
|
+
from .consts import (
|
|
3
|
+
GET,
|
|
4
|
+
POST,
|
|
5
|
+
DUAL_INVEST_CURRENCY_PAIRS,
|
|
6
|
+
DUAL_INVEST_PRODUCT_INFO,
|
|
7
|
+
DUAL_INVEST_REQUEST_QUOTE,
|
|
8
|
+
DUAL_INVEST_TRADE,
|
|
9
|
+
DUAL_INVEST_REQUEST_REDEEM_QUOTE,
|
|
10
|
+
DUAL_INVEST_REDEEM,
|
|
11
|
+
DUAL_INVEST_ORDER_STATE,
|
|
12
|
+
DUAL_INVEST_ORDER_HISTORY,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class DualInvestAPI(OkxClient):
|
|
17
|
+
def __init__(self, api_key='-1', api_secret_key='-1', passphrase='-1', use_server_time=None, flag='1', domain='https://www.okx.com', debug=False, proxy=None): # NOSONAR - '-1' is the SDK-wide "no credential supplied" sentinel (auth is gated on api_key != '-1' in OkxClient), not a hard-coded secret
|
|
18
|
+
OkxClient.__init__(self, api_key, api_secret_key, passphrase, use_server_time, flag, domain, debug, proxy)
|
|
19
|
+
|
|
20
|
+
# GET /api/v5/finance/sfp/dcd/currency-pair
|
|
21
|
+
def get_currency_pairs(self):
|
|
22
|
+
return self._request_without_params(GET, DUAL_INVEST_CURRENCY_PAIRS)
|
|
23
|
+
|
|
24
|
+
# GET /api/v5/finance/sfp/dcd/products
|
|
25
|
+
def get_product_info(self, baseCcy='', quoteCcy='', optType=''):
|
|
26
|
+
params = {'baseCcy': baseCcy, 'quoteCcy': quoteCcy, 'optType': optType}
|
|
27
|
+
return self._request_with_params(GET, DUAL_INVEST_PRODUCT_INFO, params)
|
|
28
|
+
|
|
29
|
+
# POST /api/v5/finance/sfp/dcd/quote
|
|
30
|
+
def request_quote(self, productId='', notionalSz='', notionalCcy=''):
|
|
31
|
+
params = {'productId': productId, 'notionalSz': notionalSz, 'notionalCcy': notionalCcy}
|
|
32
|
+
return self._request_with_params(POST, DUAL_INVEST_REQUEST_QUOTE, params)
|
|
33
|
+
|
|
34
|
+
# POST /api/v5/finance/sfp/dcd/trade
|
|
35
|
+
def trade(self, quoteId=''):
|
|
36
|
+
params = {'quoteId': quoteId}
|
|
37
|
+
return self._request_with_params(POST, DUAL_INVEST_TRADE, params)
|
|
38
|
+
|
|
39
|
+
# POST /api/v5/finance/sfp/dcd/redeem-quote
|
|
40
|
+
def request_redeem_quote(self, ordId=''):
|
|
41
|
+
params = {'ordId': ordId}
|
|
42
|
+
return self._request_with_params(POST, DUAL_INVEST_REQUEST_REDEEM_QUOTE, params)
|
|
43
|
+
|
|
44
|
+
# POST /api/v5/finance/sfp/dcd/redeem
|
|
45
|
+
def redeem(self, ordId='', quoteId=''):
|
|
46
|
+
params = {'ordId': ordId, 'quoteId': quoteId}
|
|
47
|
+
return self._request_with_params(POST, DUAL_INVEST_REDEEM, params)
|
|
48
|
+
|
|
49
|
+
# GET /api/v5/finance/sfp/dcd/order-status
|
|
50
|
+
def get_order_state(self, ordId=''):
|
|
51
|
+
params = {'ordId': ordId}
|
|
52
|
+
return self._request_with_params(GET, DUAL_INVEST_ORDER_STATE, params)
|
|
53
|
+
|
|
54
|
+
# GET /api/v5/finance/sfp/dcd/order-history
|
|
55
|
+
def get_order_history(self, ordId='', productId='', uly='', state='',
|
|
56
|
+
beginId='', endId='', begin='', end='', limit=''):
|
|
57
|
+
params = {'ordId': ordId, 'productId': productId, 'uly': uly, 'state': state,
|
|
58
|
+
'beginId': beginId, 'endId': endId, 'begin': begin, 'end': end, 'limit': limit}
|
|
59
|
+
return self._request_with_params(GET, DUAL_INVEST_ORDER_HISTORY, params)
|
okx/Funding.py
CHANGED
|
@@ -35,9 +35,11 @@ class FundingAPI(OkxClient):
|
|
|
35
35
|
return self._request_with_params(POST, FUNDS_TRANSFER, params)
|
|
36
36
|
|
|
37
37
|
# Withdrawal
|
|
38
|
-
def withdrawal(self, ccy, amt, dest, toAddr, chain='', areaCode='', clientId=''):
|
|
38
|
+
def withdrawal(self, ccy, amt, dest, toAddr, chain='', areaCode='', clientId='', toAddrType=None):
|
|
39
39
|
params = {'ccy': ccy, 'amt': amt, 'dest': dest, 'toAddr': toAddr, 'chain': chain,
|
|
40
40
|
'areaCode': areaCode, 'clientId': clientId}
|
|
41
|
+
if toAddrType is not None:
|
|
42
|
+
params['toAddrType'] = toAddrType
|
|
41
43
|
return self._request_with_params(POST, WITHDRAWAL_COIN, params)
|
|
42
44
|
|
|
43
45
|
# Get Deposit History
|
|
@@ -46,10 +48,7 @@ class FundingAPI(OkxClient):
|
|
|
46
48
|
'depId': depId, 'fromWdId': fromWdId}
|
|
47
49
|
return self._request_with_params(GET, DEPOSIT_HISTORY, params)
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
def get_withdrawal_history(self, ccy='', wdId='', state='', after='', before='', limit='',txId=''):
|
|
51
|
-
params = {'ccy': ccy, 'wdId': wdId, 'state': state, 'after': after, 'before': before, 'limit': limit,'txId':txId}
|
|
52
|
-
return self._request_with_params(GET, WITHDRAWAL_HISTORY, params)
|
|
51
|
+
|
|
53
52
|
|
|
54
53
|
# Get Currencies
|
|
55
54
|
def get_currencies(self, ccy=''):
|
|
@@ -113,7 +112,9 @@ class FundingAPI(OkxClient):
|
|
|
113
112
|
return self._request_with_params(GET, GET_DEPOSIT_WITHDrAW_STATUS, params)
|
|
114
113
|
|
|
115
114
|
#Get withdrawal history
|
|
116
|
-
def get_withdrawal_history(self, ccy='', wdId='', clientId='', txId='', type='', state='', after='', before
|
|
115
|
+
def get_withdrawal_history(self, ccy='', wdId='', clientId='', txId='', type='', state='', after='', before='', limit='', toAddrType=None):
|
|
117
116
|
params = {'ccy': ccy, 'wdId': wdId, 'clientId': clientId, 'txId': txId, 'type': type, 'state': state, 'after': after, 'before': before, 'limit': limit}
|
|
117
|
+
if toAddrType is not None:
|
|
118
|
+
params['toAddrType'] = toAddrType
|
|
118
119
|
return self._request_with_params(GET, GET_WITHDRAWAL_HISTORY, params)
|
|
119
120
|
|
okx/Grid.py
CHANGED
|
@@ -7,11 +7,13 @@ class GridAPI(OkxClient):
|
|
|
7
7
|
OkxClient.__init__(self, api_key, api_secret_key, passphrase, use_server_time, flag, domain, debug, proxy)
|
|
8
8
|
|
|
9
9
|
def grid_order_algo(self, instId='', algoOrdType='', maxPx='', minPx='', gridNum='', runType='', tpTriggerPx='',
|
|
10
|
-
slTriggerPx='', tag='', quoteSz='', baseSz='', sz='', direction='', lever='', basePos=''):
|
|
10
|
+
slTriggerPx='', tag='', quoteSz='', baseSz='', sz='', direction='', lever='', basePos='', tradeQuoteCcy=None):
|
|
11
11
|
params = {'instId': instId, 'algoOrdType': algoOrdType, 'maxPx': maxPx, 'minPx': minPx, 'gridNum': gridNum,
|
|
12
12
|
'runType': runType, 'tpTriggerPx': tpTriggerPx, 'slTriggerPx': slTriggerPx, 'tag': tag,
|
|
13
13
|
'quoteSz': quoteSz, 'baseSz': baseSz, 'sz': sz, 'direction': direction, 'lever': lever,
|
|
14
14
|
'basePos': basePos}
|
|
15
|
+
if tradeQuoteCcy is not None:
|
|
16
|
+
params['tradeQuoteCcy'] = tradeQuoteCcy
|
|
15
17
|
return self._request_with_params(POST, GRID_ORDER_ALGO, params)
|
|
16
18
|
|
|
17
19
|
def grid_amend_order_algo(self, algoId='', instId='', slTriggerPx='', tpTriggerPx=''):
|
|
@@ -79,11 +81,13 @@ class GridAPI(OkxClient):
|
|
|
79
81
|
|
|
80
82
|
# - Place recurring buy order
|
|
81
83
|
def place_recurring_buy_order(self, stgyName='', recurringList=[], period='', recurringDay='', recurringTime='',
|
|
82
|
-
timeZone='', amt='', investmentCcy='', tdMode='', algoClOrdId='', tag=''):
|
|
84
|
+
timeZone='', amt='', investmentCcy='', tdMode='', algoClOrdId='', tag='', tradeQuoteCcy=None):
|
|
83
85
|
params = {'stgyName': stgyName, 'recurringList': recurringList, 'period': period, 'recurringDay': recurringDay,
|
|
84
86
|
'recurringTime': recurringTime,
|
|
85
87
|
'timeZone': timeZone, 'amt': amt, 'investmentCcy': investmentCcy, 'tdMode': tdMode,
|
|
86
88
|
'algoClOrdId': algoClOrdId, 'tag': tag}
|
|
89
|
+
if tradeQuoteCcy is not None:
|
|
90
|
+
params['tradeQuoteCcy'] = tradeQuoteCcy
|
|
87
91
|
return self._request_with_params(POST, PLACE_RECURRING_BUY_ORDER, params)
|
|
88
92
|
|
|
89
93
|
# - Amend recurring buy order
|
okx/PublicData.py
CHANGED
|
@@ -122,3 +122,23 @@ class PublicAPI(OkxClient):
|
|
|
122
122
|
'optType': optType
|
|
123
123
|
}
|
|
124
124
|
return self._request_with_params(GET, GET_OPTION_TRADES, params)
|
|
125
|
+
|
|
126
|
+
# Get historical market data
|
|
127
|
+
def get_market_data_history(self, module, instType, dateAggrType, begin, end, instIdList=None, instFamilyList=None):
|
|
128
|
+
params = {
|
|
129
|
+
'module': module,
|
|
130
|
+
'instType': instType,
|
|
131
|
+
'dateAggrType': dateAggrType,
|
|
132
|
+
'begin': begin,
|
|
133
|
+
'end': end
|
|
134
|
+
}
|
|
135
|
+
if instIdList is not None:
|
|
136
|
+
params['instIdList'] = instIdList
|
|
137
|
+
if instFamilyList is not None:
|
|
138
|
+
params['instFamilyList'] = instFamilyList
|
|
139
|
+
return self._request_with_params(GET, MARKET_DATA_HISTORY, params)
|
|
140
|
+
|
|
141
|
+
# Get announcements (BROK-1730)
|
|
142
|
+
def get_announcements(self, annType='', page=''):
|
|
143
|
+
params = {'annType': annType, 'page': page}
|
|
144
|
+
return self._request_with_params(GET, ANNOUNCEMENTS, params)
|
okx/Trade.py
CHANGED
|
@@ -11,11 +11,20 @@ class TradeAPI(OkxClient):
|
|
|
11
11
|
OkxClient.__init__(self, api_key, api_secret_key, passphrase, use_server_time, flag, domain, debug, proxy)
|
|
12
12
|
|
|
13
13
|
# Place Order
|
|
14
|
-
def place_order(self, instId, tdMode, side, ordType, sz, ccy='', clOrdId='', tag='', posSide='', px='',
|
|
15
|
-
reduceOnly='', tgtCcy='', stpMode='', attachAlgoOrds=None, pxUsd='', pxVol='', banAmend=''
|
|
14
|
+
def place_order(self, instId, tdMode, side, ordType, sz, ccy='', clOrdId='', tag='', posSide='', px='', # NOSONAR - public OKX v5 place-order API surface; the parameter list mirrors the REST endpoint 1:1 and cannot be reduced without breaking backward compatibility
|
|
15
|
+
reduceOnly='', tgtCcy='', stpMode='', attachAlgoOrds=None, pxUsd='', pxVol='', banAmend='', tradeQuoteCcy=None, pxAmendType=None,
|
|
16
|
+
isElpTakerAccess=None, instIdCode=None):
|
|
16
17
|
params = {'instId': instId, 'tdMode': tdMode, 'side': side, 'ordType': ordType, 'sz': sz, 'ccy': ccy,
|
|
17
18
|
'clOrdId': clOrdId, 'tag': tag, 'posSide': posSide, 'px': px, 'reduceOnly': reduceOnly,
|
|
18
19
|
'tgtCcy': tgtCcy, 'stpMode': stpMode, 'pxUsd': pxUsd, 'pxVol': pxVol, 'banAmend': banAmend}
|
|
20
|
+
if tradeQuoteCcy is not None:
|
|
21
|
+
params['tradeQuoteCcy'] = tradeQuoteCcy
|
|
22
|
+
if pxAmendType is not None:
|
|
23
|
+
params['pxAmendType'] = pxAmendType
|
|
24
|
+
if isElpTakerAccess is not None:
|
|
25
|
+
params['isElpTakerAccess'] = isElpTakerAccess
|
|
26
|
+
if instIdCode is not None:
|
|
27
|
+
params['instIdCode'] = instIdCode
|
|
19
28
|
params['attachAlgoOrds'] = attachAlgoOrds
|
|
20
29
|
return self._request_with_params(POST, PLACR_ORDER, params)
|
|
21
30
|
|
|
@@ -33,13 +42,20 @@ class TradeAPI(OkxClient):
|
|
|
33
42
|
return self._request_with_params(POST, CANCEL_BATCH_ORDERS, orders_data)
|
|
34
43
|
|
|
35
44
|
# Amend Order
|
|
36
|
-
def amend_order(self, instId, cxlOnFail='', ordId='', clOrdId='', reqId='', newSz='', newPx='', newTpTriggerPx='',
|
|
45
|
+
def amend_order(self, instId, cxlOnFail='', ordId='', clOrdId='', reqId='', newSz='', newPx='', newTpTriggerPx='', # NOSONAR - public OKX v5 amend-order API surface; the parameter list mirrors the REST endpoint 1:1 and cannot be reduced without breaking backward compatibility
|
|
37
46
|
newTpOrdPx='', newSlTriggerPx='', newSlOrdPx='', newTpTriggerPxType='', newSlTriggerPxType='',
|
|
38
|
-
attachAlgoOrds='', newTriggerPx='', newOrdPx=''
|
|
47
|
+
attachAlgoOrds='', newTriggerPx='', newOrdPx='', pxAmendType=None,
|
|
48
|
+
newTpTriggerRatio=None, newSlTriggerRatio=None):
|
|
39
49
|
params = {'instId': instId, 'cxlOnFail': cxlOnFail, 'ordId': ordId, 'clOrdId': clOrdId, 'reqId': reqId,
|
|
40
50
|
'newSz': newSz, 'newPx': newPx, 'newTpTriggerPx': newTpTriggerPx, 'newTpOrdPx': newTpOrdPx,
|
|
41
51
|
'newSlTriggerPx': newSlTriggerPx, 'newSlOrdPx': newSlOrdPx, 'newTpTriggerPxType': newTpTriggerPxType,
|
|
42
52
|
'newSlTriggerPxType': newSlTriggerPxType, 'newTriggerPx': newTriggerPx, 'newOrdPx': newOrdPx}
|
|
53
|
+
if pxAmendType is not None:
|
|
54
|
+
params['pxAmendType'] = pxAmendType
|
|
55
|
+
if newTpTriggerRatio is not None:
|
|
56
|
+
params['newTpTriggerRatio'] = newTpTriggerRatio
|
|
57
|
+
if newSlTriggerRatio is not None:
|
|
58
|
+
params['newSlTriggerRatio'] = newSlTriggerRatio
|
|
43
59
|
params['attachAlgoOrds'] = attachAlgoOrds
|
|
44
60
|
return self._request_with_params(POST, AMEND_ORDER, params)
|
|
45
61
|
|
|
@@ -88,15 +104,16 @@ class TradeAPI(OkxClient):
|
|
|
88
104
|
return self._request_with_params(GET, ORDER_FILLS, params)
|
|
89
105
|
|
|
90
106
|
# Place Algo Order
|
|
91
|
-
def place_algo_order(self, instId='', tdMode='', side='', ordType='', sz='', ccy='',
|
|
107
|
+
def place_algo_order(self, instId='', tdMode='', side='', ordType='', sz='', ccy='', # NOSONAR - public OKX v5 place-algo-order API surface; the parameter list mirrors the REST endpoint 1:1 and cannot be reduced without breaking backward compatibility
|
|
92
108
|
posSide='', reduceOnly='', tpTriggerPx='',
|
|
93
109
|
tpOrdPx='', slTriggerPx='', slOrdPx='',
|
|
94
110
|
triggerPx='', orderPx='', tgtCcy='', pxVar='',
|
|
95
111
|
pxSpread='',
|
|
96
112
|
szLimit='', pxLimit='', timeInterval='', tpTriggerPxType='', slTriggerPxType='',
|
|
97
113
|
callbackRatio='', callbackSpread='', activePx='', tag='', triggerPxType='', closeFraction=''
|
|
98
|
-
, quickMgnType='', algoClOrdId='', tradeQuoteCcy=
|
|
99
|
-
, chaseType='', chaseVal='', maxChaseType='', maxChaseVal='', attachAlgoOrds=[]
|
|
114
|
+
, quickMgnType='', algoClOrdId='', tradeQuoteCcy=None, tpOrdKind='', cxlOnClosePos=''
|
|
115
|
+
, chaseType='', chaseVal='', maxChaseType='', maxChaseVal='', attachAlgoOrds=[], pxAmendType=None
|
|
116
|
+
, advanceOrdType=None, tpTriggerRatio=None, slTriggerRatio=None):
|
|
100
117
|
params = {'instId': instId, 'tdMode': tdMode, 'side': side, 'ordType': ordType, 'sz': sz, 'ccy': ccy,
|
|
101
118
|
'posSide': posSide, 'reduceOnly': reduceOnly, 'tpTriggerPx': tpTriggerPx, 'tpOrdPx': tpOrdPx,
|
|
102
119
|
'slTriggerPx': slTriggerPx, 'slOrdPx': slOrdPx, 'triggerPx': triggerPx, 'orderPx': orderPx,
|
|
@@ -105,14 +122,34 @@ class TradeAPI(OkxClient):
|
|
|
105
122
|
'pxSpread': pxSpread, 'tpTriggerPxType': tpTriggerPxType, 'slTriggerPxType': slTriggerPxType,
|
|
106
123
|
'callbackRatio': callbackRatio, 'callbackSpread': callbackSpread, 'activePx': activePx,
|
|
107
124
|
'tag': tag, 'triggerPxType': triggerPxType, 'closeFraction': closeFraction,
|
|
108
|
-
'quickMgnType': quickMgnType, 'algoClOrdId': algoClOrdId,
|
|
125
|
+
'quickMgnType': quickMgnType, 'algoClOrdId': algoClOrdId,
|
|
109
126
|
'tpOrdKind': tpOrdKind, 'cxlOnClosePos': cxlOnClosePos, 'chaseType': chaseType, 'chaseVal': chaseVal,
|
|
110
127
|
'maxChaseType': maxChaseType, 'maxChaseVal': maxChaseVal, 'attachAlgoOrds': attachAlgoOrds}
|
|
128
|
+
if tradeQuoteCcy is not None:
|
|
129
|
+
params['tradeQuoteCcy'] = tradeQuoteCcy
|
|
130
|
+
if pxAmendType is not None:
|
|
131
|
+
params['pxAmendType'] = pxAmendType
|
|
132
|
+
if advanceOrdType is not None:
|
|
133
|
+
params['advanceOrdType'] = advanceOrdType
|
|
134
|
+
if tpTriggerRatio is not None:
|
|
135
|
+
params['tpTriggerRatio'] = tpTriggerRatio
|
|
136
|
+
if slTriggerRatio is not None:
|
|
137
|
+
params['slTriggerRatio'] = slTriggerRatio
|
|
111
138
|
return self._request_with_params(POST, PLACE_ALGO_ORDER, params)
|
|
112
139
|
|
|
113
140
|
# Cancel Algo Order
|
|
114
|
-
def cancel_algo_order(self, params):
|
|
115
|
-
|
|
141
|
+
def cancel_algo_order(self, orders_data=None, params=None):
|
|
142
|
+
"""
|
|
143
|
+
Cancel algo orders — POST /api/v5/trade/cancel-algos.
|
|
144
|
+
|
|
145
|
+
:param orders_data: list of {'instId': str, 'algoId': str},
|
|
146
|
+
e.g. [{'instId': 'BTC-USDT', 'algoId': '590xxxx'}]
|
|
147
|
+
:param params: DEPRECATED alias for orders_data (kept for backward compatibility).
|
|
148
|
+
"""
|
|
149
|
+
data = orders_data if orders_data is not None else params
|
|
150
|
+
if data is None:
|
|
151
|
+
raise ValueError("orders_data is required: list of {'instId', 'algoId'}")
|
|
152
|
+
return self._request_with_params(POST, CANCEL_ALGOS, data)
|
|
116
153
|
|
|
117
154
|
# Get Algo Order List
|
|
118
155
|
def order_algos_list(self, ordType='', algoId='', instType='', instId='', after='', before='', limit=''):
|
|
@@ -177,11 +214,11 @@ class TradeAPI(OkxClient):
|
|
|
177
214
|
return self._request_with_params(GET, GET_ALGO_ORDER_DETAILS, params)
|
|
178
215
|
|
|
179
216
|
# Amend algo order
|
|
180
|
-
def amend_algo_order(self, instId='', algoId='', algoClOrdId='', cxlOnFail='', reqId='', newSz='',
|
|
217
|
+
def amend_algo_order(self, instId='', algoId='', algoClOrdId='', cxlOnFail='', reqId='', newSz='', newTriggerPx='', newOrdPx='',
|
|
181
218
|
newTpTriggerPx='', newTpOrdPx='', newSlTriggerPx='', newSlOrdPx='', newTpTriggerPxType='',
|
|
182
219
|
newSlTriggerPxType=''):
|
|
183
220
|
params = {'instId': instId, 'algoId': algoId, 'algoClOrdId': algoClOrdId, 'cxlOnFail': cxlOnFail,
|
|
184
|
-
'reqId': reqId, 'newSz': newSz, 'newTpTriggerPx': newTpTriggerPx, 'newTpOrdPx': newTpOrdPx,
|
|
221
|
+
'reqId': reqId, 'newSz': newSz, 'newTriggerPx': newTriggerPx, 'newOrdPx': newOrdPx, 'newTpTriggerPx': newTpTriggerPx, 'newTpOrdPx': newTpOrdPx,
|
|
185
222
|
'newSlTriggerPx': newSlTriggerPx, 'newSlOrdPx': newSlOrdPx,
|
|
186
223
|
'newTpTriggerPxType': newTpTriggerPxType, 'newSlTriggerPxType': newSlTriggerPxType}
|
|
187
224
|
return self._request_with_params(POST, AMEND_ALGO_ORDER, params)
|
okx/TradingData.py
CHANGED
|
@@ -47,6 +47,31 @@ class TradingDataAPI(OkxClient):
|
|
|
47
47
|
params = {'ccy': ccy, 'period': period}
|
|
48
48
|
return self._request_with_params(GET, TAKER_FLOW, params)
|
|
49
49
|
|
|
50
|
+
def get_open_interest_history(self, instId, period=None, begin=None, end=None, limit=None):
|
|
51
|
+
"""
|
|
52
|
+
Get contract open interest history
|
|
53
|
+
Retrieve the contract open interest statistics of futures and perp.
|
|
54
|
+
Rate limit: 10 requests per 2 seconds
|
|
55
|
+
Rate limit rule: IP + Instrument ID
|
|
56
|
+
|
|
57
|
+
:param instId: Instrument ID, e.g. BTC-USDT-SWAP. Only applicable to FUTURES, SWAP
|
|
58
|
+
:param period: Bar size, the default is 5m, e.g. [5m/15m/30m/1H/2H/4H]
|
|
59
|
+
:param begin: Return records newer than the requested ts
|
|
60
|
+
:param end: Pagination of data to return records earlier than the requested ts
|
|
61
|
+
:param limit: Number of results per request. The maximum is 100. The default is 100
|
|
62
|
+
:return: API response
|
|
63
|
+
"""
|
|
64
|
+
params = {'instId': instId}
|
|
65
|
+
if period is not None:
|
|
66
|
+
params['period'] = period
|
|
67
|
+
if begin is not None:
|
|
68
|
+
params['begin'] = begin
|
|
69
|
+
if end is not None:
|
|
70
|
+
params['end'] = end
|
|
71
|
+
if limit is not None:
|
|
72
|
+
params['limit'] = limit
|
|
73
|
+
return self._request_with_params(GET, CONTRACTS_OPEN_INTEREST_HISTORY, params)
|
|
74
|
+
|
|
50
75
|
|
|
51
76
|
|
|
52
77
|
|
okx/__init__.py
CHANGED
okx/consts.py
CHANGED
|
@@ -66,6 +66,11 @@ BORROWING_ORDERS_LIST='/api/v5/account/fixed-loan/borrowing-orders-list'
|
|
|
66
66
|
MANUAL_REBORROW_REPAY = '/api/v5/account/spot-manual-borrow-repay'
|
|
67
67
|
SET_AUTO_REPAY='/api/v5/account/set-auto-repay'
|
|
68
68
|
GET_BORROW_REPAY_HISTORY='/api/v5/account/spot-borrow-repay-history'
|
|
69
|
+
SET_AUTO_EARN='/api/v5/account/set-auto-earn'
|
|
70
|
+
SET_TRADING_CONFIG = '/api/v5/account/set-trading-config'
|
|
71
|
+
PRECHECK_SET_DELTA_NEUTRAL = '/api/v5/account/precheck-set-delta-neutral'
|
|
72
|
+
BILL_TYPE = '/api/v5/account/subtypes'
|
|
73
|
+
BILLS_APPLY = '/api/v5/account/bills-history-archive'
|
|
69
74
|
|
|
70
75
|
# Funding
|
|
71
76
|
NON_TRADABLE_ASSETS = '/api/v5/asset/non-tradable-assets'
|
|
@@ -81,7 +86,6 @@ BILLS_INFO = '/api/v5/asset/bills'
|
|
|
81
86
|
DEPOSIT_LIGHTNING = '/api/v5/asset/deposit-lightning'
|
|
82
87
|
WITHDRAWAL_LIGHTNING = '/api/v5/asset/withdrawal-lightning'
|
|
83
88
|
CANCEL_WITHDRAWAL = '/api/v5/asset/cancel-withdrawal'
|
|
84
|
-
WITHDRAWAL_HISTORY = '/api/v5/asset/withdrawal-history'
|
|
85
89
|
CONVERT_DUST_ASSETS = '/api/v5/asset/convert-dust-assets'
|
|
86
90
|
ASSET_VALUATION = '/api/v5/asset/asset-valuation'
|
|
87
91
|
GET_WITHDRAWAL_HISTORY = '/api/v5/asset/withdrawal-history'
|
|
@@ -130,6 +134,7 @@ INSURANCE_FUND = '/api/v5/public/insurance-fund'
|
|
|
130
134
|
CONVERT_CONTRACT_COIN = '/api/v5/public/convert-contract-coin'
|
|
131
135
|
GET_OPTION_TICKBANDS = '/api/v5/public/instrument-tick-bands'
|
|
132
136
|
GET_OPTION_TRADES = '/api/v5/public/option-trades'
|
|
137
|
+
MARKET_DATA_HISTORY = '/api/v5/public/market-data-history'
|
|
133
138
|
|
|
134
139
|
# Trading data
|
|
135
140
|
SUPPORT_COIN = '/api/v5/rubik/stat/trading-data/support-coin'
|
|
@@ -142,6 +147,8 @@ PUT_CALL_RATIO = '/api/v5/rubik/stat/option/open-interest-volume-ratio'
|
|
|
142
147
|
OPEN_INTEREST_VOLUME_EXPIRY = '/api/v5/rubik/stat/option/open-interest-volume-expiry'
|
|
143
148
|
INTEREST_VOLUME_STRIKE = '/api/v5/rubik/stat/option/open-interest-volume-strike'
|
|
144
149
|
TAKER_FLOW = '/api/v5/rubik/stat/option/taker-block-volume'
|
|
150
|
+
CONTRACTS_OPEN_INTEREST_HISTORY = '/api/v5/rubik/stat/contracts/open-interest-history'
|
|
151
|
+
ANNOUNCEMENTS = '/api/v5/support/announcements'
|
|
145
152
|
|
|
146
153
|
# Trade
|
|
147
154
|
PLACR_ORDER = '/api/v5/trade/order'
|
|
@@ -300,4 +307,15 @@ FINANCE_LOAN_INFO = '/api/v5/finance/flexible-loan/loan-info'
|
|
|
300
307
|
FINANCE_LOAN_HISTORY = '/api/v5/finance/flexible-loan/loan-history'
|
|
301
308
|
FINANCE_INTEREST_ACCRUED = '/api/v5/finance/flexible-loan/interest-accrued'
|
|
302
309
|
|
|
310
|
+
# Dual Investment (DCD) — under /api/v5/finance/sfp/dcd/
|
|
311
|
+
DUAL_INVEST_CURRENCY_PAIRS = '/api/v5/finance/sfp/dcd/currency-pair'
|
|
312
|
+
DUAL_INVEST_PRODUCT_INFO = '/api/v5/finance/sfp/dcd/products'
|
|
313
|
+
DUAL_INVEST_REQUEST_QUOTE = '/api/v5/finance/sfp/dcd/quote'
|
|
314
|
+
DUAL_INVEST_TRADE = '/api/v5/finance/sfp/dcd/trade'
|
|
315
|
+
DUAL_INVEST_REQUEST_REDEEM_QUOTE = '/api/v5/finance/sfp/dcd/redeem-quote'
|
|
316
|
+
DUAL_INVEST_REDEEM = '/api/v5/finance/sfp/dcd/redeem'
|
|
317
|
+
DUAL_INVEST_ORDER_STATE = '/api/v5/finance/sfp/dcd/order-status'
|
|
318
|
+
DUAL_INVEST_ORDER_HISTORY = '/api/v5/finance/sfp/dcd/order-history'
|
|
319
|
+
|
|
320
|
+
|
|
303
321
|
|
okx/okxclient.py
CHANGED
|
@@ -14,7 +14,16 @@ from . import consts as c, utils, exceptions
|
|
|
14
14
|
class OkxClient(Client):
|
|
15
15
|
|
|
16
16
|
def __init__(self, api_key='-1', api_secret_key='-1', passphrase='-1', use_server_time=None, flag='1',base_api=c.API_URL, debug=False, proxy=None):
|
|
17
|
-
|
|
17
|
+
# Compatible with different versions of httpx
|
|
18
|
+
# New versions (0.24.0+) use proxy, older versions use proxies
|
|
19
|
+
try:
|
|
20
|
+
super().__init__(base_url=base_api, http2=True, proxy=proxy)
|
|
21
|
+
except TypeError:
|
|
22
|
+
# Older versions of httpx use proxies parameter
|
|
23
|
+
if proxy:
|
|
24
|
+
super().__init__(base_url=base_api, http2=True, proxies={'http://': proxy, 'https://': proxy})
|
|
25
|
+
else:
|
|
26
|
+
super().__init__(base_url=base_api, http2=True)
|
|
18
27
|
self.API_KEY = api_key
|
|
19
28
|
self.API_SECRET_KEY = api_secret_key
|
|
20
29
|
self.PASSPHRASE = passphrase
|
okx/websocket/WsPrivateAsync.py
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import json
|
|
3
3
|
import logging
|
|
4
|
+
import warnings
|
|
5
|
+
|
|
6
|
+
from websockets.exceptions import ConnectionClosedError
|
|
4
7
|
|
|
5
8
|
from okx.websocket import WsUtils
|
|
6
9
|
from okx.websocket.WebSocketFactory import WebSocketFactory
|
|
@@ -9,7 +12,7 @@ logger = logging.getLogger(__name__)
|
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
class WsPrivateAsync:
|
|
12
|
-
def __init__(self, apiKey, passphrase, secretKey, url, useServerTime):
|
|
15
|
+
def __init__(self, apiKey, passphrase, secretKey, url, useServerTime=None, debug=False):
|
|
13
16
|
self.url = url
|
|
14
17
|
self.subscriptions = set()
|
|
15
18
|
self.callback = None
|
|
@@ -18,27 +21,49 @@ class WsPrivateAsync:
|
|
|
18
21
|
self.apiKey = apiKey
|
|
19
22
|
self.passphrase = passphrase
|
|
20
23
|
self.secretKey = secretKey
|
|
21
|
-
self.useServerTime =
|
|
24
|
+
self.useServerTime = False
|
|
25
|
+
self.websocket = None
|
|
26
|
+
self.debug = debug
|
|
27
|
+
|
|
28
|
+
# Set log level
|
|
29
|
+
if debug:
|
|
30
|
+
logger.setLevel(logging.DEBUG)
|
|
31
|
+
|
|
32
|
+
# Deprecation warning for useServerTime parameter
|
|
33
|
+
if useServerTime is not None:
|
|
34
|
+
warnings.warn("useServerTime parameter is deprecated. Please remove it.", DeprecationWarning)
|
|
22
35
|
|
|
23
36
|
async def connect(self):
|
|
24
37
|
self.websocket = await self.factory.connect()
|
|
25
38
|
|
|
26
39
|
async def consume(self):
|
|
27
|
-
|
|
28
|
-
|
|
40
|
+
try:
|
|
41
|
+
async for message in self.websocket:
|
|
42
|
+
if self.debug:
|
|
43
|
+
logger.debug("Received message: {%s}", message)
|
|
44
|
+
if self.callback:
|
|
45
|
+
self.callback(message)
|
|
46
|
+
except ConnectionClosedError as e:
|
|
47
|
+
logger.error(f"WebSocket connection closed: {e}")
|
|
29
48
|
if self.callback:
|
|
30
|
-
self.callback(
|
|
49
|
+
self.callback(json.dumps({"event": "error", "code": "ConnClosed", "msg": str(e)}))
|
|
50
|
+
raise
|
|
31
51
|
|
|
32
|
-
async def subscribe(self, params: list, callback):
|
|
52
|
+
async def subscribe(self, params: list, callback, id: str = None):
|
|
33
53
|
self.callback = callback
|
|
34
54
|
|
|
35
55
|
logRes = await self.login()
|
|
36
56
|
await asyncio.sleep(5)
|
|
37
57
|
if logRes:
|
|
38
|
-
|
|
58
|
+
payload_dict = {
|
|
39
59
|
"op": "subscribe",
|
|
40
60
|
"args": params
|
|
41
|
-
}
|
|
61
|
+
}
|
|
62
|
+
if id is not None:
|
|
63
|
+
payload_dict["id"] = id
|
|
64
|
+
payload = json.dumps(payload_dict)
|
|
65
|
+
if self.debug:
|
|
66
|
+
logger.debug(f"subscribe: {payload}")
|
|
42
67
|
await self.websocket.send(payload)
|
|
43
68
|
# await self.consume()
|
|
44
69
|
|
|
@@ -49,28 +74,139 @@ class WsPrivateAsync:
|
|
|
49
74
|
passphrase=self.passphrase,
|
|
50
75
|
secretKey=self.secretKey
|
|
51
76
|
)
|
|
77
|
+
if self.debug:
|
|
78
|
+
logger.debug(f"login: {loginPayload}")
|
|
52
79
|
await self.websocket.send(loginPayload)
|
|
53
80
|
return True
|
|
54
81
|
|
|
55
|
-
async def unsubscribe(self, params: list, callback):
|
|
82
|
+
async def unsubscribe(self, params: list, callback, id: str = None):
|
|
56
83
|
self.callback = callback
|
|
57
|
-
|
|
84
|
+
payload_dict = {
|
|
58
85
|
"op": "unsubscribe",
|
|
59
86
|
"args": params
|
|
60
|
-
}
|
|
61
|
-
|
|
87
|
+
}
|
|
88
|
+
if id is not None:
|
|
89
|
+
payload_dict["id"] = id
|
|
90
|
+
payload = json.dumps(payload_dict)
|
|
91
|
+
if self.debug:
|
|
92
|
+
logger.debug(f"unsubscribe: {payload}")
|
|
93
|
+
else:
|
|
94
|
+
logger.info(f"unsubscribe: {payload}")
|
|
62
95
|
await self.websocket.send(payload)
|
|
63
|
-
|
|
64
|
-
|
|
96
|
+
|
|
97
|
+
async def send(self, op: str, args: list, callback=None, id: str = None):
|
|
98
|
+
"""
|
|
99
|
+
Generic send method
|
|
100
|
+
:param op: Operation type
|
|
101
|
+
:param args: Parameter list
|
|
102
|
+
:param callback: Callback function
|
|
103
|
+
:param id: Optional request ID
|
|
104
|
+
"""
|
|
105
|
+
if callback:
|
|
106
|
+
self.callback = callback
|
|
107
|
+
payload_dict = {
|
|
108
|
+
"op": op,
|
|
109
|
+
"args": args
|
|
110
|
+
}
|
|
111
|
+
if id is not None:
|
|
112
|
+
payload_dict["id"] = id
|
|
113
|
+
payload = json.dumps(payload_dict)
|
|
114
|
+
if self.debug:
|
|
115
|
+
logger.debug(f"send: {payload}")
|
|
116
|
+
await self.websocket.send(payload)
|
|
117
|
+
|
|
118
|
+
async def place_order(self, args: list, callback=None, id: str = None):
|
|
119
|
+
"""
|
|
120
|
+
Place order
|
|
121
|
+
:param args: Order parameter list
|
|
122
|
+
:param callback: Callback function
|
|
123
|
+
:param id: Optional request ID
|
|
124
|
+
"""
|
|
125
|
+
if callback:
|
|
126
|
+
self.callback = callback
|
|
127
|
+
await self.send("order", args, id=id)
|
|
128
|
+
|
|
129
|
+
async def batch_orders(self, args: list, callback=None, id: str = None):
|
|
130
|
+
"""
|
|
131
|
+
Batch place orders
|
|
132
|
+
:param args: Batch order parameter list
|
|
133
|
+
:param callback: Callback function
|
|
134
|
+
:param id: Optional request ID
|
|
135
|
+
"""
|
|
136
|
+
if callback:
|
|
137
|
+
self.callback = callback
|
|
138
|
+
await self.send("batch-orders", args, id=id)
|
|
139
|
+
|
|
140
|
+
async def cancel_order(self, args: list, callback=None, id: str = None):
|
|
141
|
+
"""
|
|
142
|
+
Cancel order
|
|
143
|
+
:param args: Cancel order parameter list
|
|
144
|
+
:param callback: Callback function
|
|
145
|
+
:param id: Optional request ID
|
|
146
|
+
"""
|
|
147
|
+
if callback:
|
|
148
|
+
self.callback = callback
|
|
149
|
+
await self.send("cancel-order", args, id=id)
|
|
150
|
+
|
|
151
|
+
async def batch_cancel_orders(self, args: list, callback=None, id: str = None):
|
|
152
|
+
"""
|
|
153
|
+
Batch cancel orders
|
|
154
|
+
:param args: Batch cancel order parameter list
|
|
155
|
+
:param callback: Callback function
|
|
156
|
+
:param id: Optional request ID
|
|
157
|
+
"""
|
|
158
|
+
if callback:
|
|
159
|
+
self.callback = callback
|
|
160
|
+
await self.send("batch-cancel-orders", args, id=id)
|
|
161
|
+
|
|
162
|
+
async def amend_order(self, args: list, callback=None, id: str = None):
|
|
163
|
+
"""
|
|
164
|
+
Amend order
|
|
165
|
+
:param args: Amend order parameter list
|
|
166
|
+
:param callback: Callback function
|
|
167
|
+
:param id: Optional request ID
|
|
168
|
+
"""
|
|
169
|
+
if callback:
|
|
170
|
+
self.callback = callback
|
|
171
|
+
await self.send("amend-order", args, id=id)
|
|
172
|
+
|
|
173
|
+
async def batch_amend_orders(self, args: list, callback=None, id: str = None):
|
|
174
|
+
"""
|
|
175
|
+
Batch amend orders
|
|
176
|
+
:param args: Batch amend order parameter list
|
|
177
|
+
:param callback: Callback function
|
|
178
|
+
:param id: Optional request ID
|
|
179
|
+
"""
|
|
180
|
+
if callback:
|
|
181
|
+
self.callback = callback
|
|
182
|
+
await self.send("batch-amend-orders", args, id=id)
|
|
183
|
+
|
|
184
|
+
async def mass_cancel(self, args: list, callback=None, id: str = None):
|
|
185
|
+
"""
|
|
186
|
+
Mass cancel orders
|
|
187
|
+
Note: This method is for /ws/v5/business channel, rate limit: 1 request/second
|
|
188
|
+
:param args: Cancel parameter list, contains instType and instFamily
|
|
189
|
+
:param callback: Callback function
|
|
190
|
+
:param id: Optional request ID
|
|
191
|
+
"""
|
|
192
|
+
if callback:
|
|
193
|
+
self.callback = callback
|
|
194
|
+
await self.send("mass-cancel", args, id=id)
|
|
65
195
|
|
|
66
196
|
async def stop(self):
|
|
67
197
|
await self.factory.close()
|
|
68
|
-
self.loop.stop()
|
|
69
198
|
|
|
70
199
|
async def start(self):
|
|
71
|
-
|
|
200
|
+
if self.debug:
|
|
201
|
+
logger.debug("Connecting to WebSocket...")
|
|
202
|
+
else:
|
|
203
|
+
logger.info("Connecting to WebSocket...")
|
|
72
204
|
await self.connect()
|
|
73
|
-
self.loop.create_task(self.consume())
|
|
205
|
+
return self.loop.create_task(self.consume())
|
|
74
206
|
|
|
75
207
|
def stop_sync(self):
|
|
76
|
-
self.loop.
|
|
208
|
+
if self.loop.is_running():
|
|
209
|
+
future = asyncio.run_coroutine_threadsafe(self.stop(), self.loop)
|
|
210
|
+
future.result(timeout=10)
|
|
211
|
+
else:
|
|
212
|
+
self.loop.run_until_complete(self.stop())
|
okx/websocket/WsPublicAsync.py
CHANGED
|
@@ -2,54 +2,132 @@ import asyncio
|
|
|
2
2
|
import json
|
|
3
3
|
import logging
|
|
4
4
|
|
|
5
|
+
from websockets.exceptions import ConnectionClosedError
|
|
6
|
+
|
|
7
|
+
from okx.websocket import WsUtils
|
|
5
8
|
from okx.websocket.WebSocketFactory import WebSocketFactory
|
|
6
9
|
|
|
7
10
|
logger = logging.getLogger(__name__)
|
|
8
11
|
|
|
9
12
|
|
|
10
13
|
class WsPublicAsync:
|
|
11
|
-
def __init__(self, url):
|
|
14
|
+
def __init__(self, url, apiKey='', passphrase='', secretKey='', debug=False):
|
|
12
15
|
self.url = url
|
|
13
16
|
self.subscriptions = set()
|
|
14
17
|
self.callback = None
|
|
15
18
|
self.loop = asyncio.get_event_loop()
|
|
16
19
|
self.factory = WebSocketFactory(url)
|
|
20
|
+
self.websocket = None
|
|
21
|
+
self.debug = debug
|
|
22
|
+
# Credentials for business channel login
|
|
23
|
+
self.apiKey = apiKey
|
|
24
|
+
self.passphrase = passphrase
|
|
25
|
+
self.secretKey = secretKey
|
|
26
|
+
self.isLoggedIn = False
|
|
27
|
+
|
|
28
|
+
# Set log level
|
|
29
|
+
if debug:
|
|
30
|
+
logger.setLevel(logging.DEBUG)
|
|
17
31
|
|
|
18
32
|
async def connect(self):
|
|
19
33
|
self.websocket = await self.factory.connect()
|
|
20
34
|
|
|
21
35
|
async def consume(self):
|
|
22
|
-
|
|
23
|
-
|
|
36
|
+
try:
|
|
37
|
+
async for message in self.websocket:
|
|
38
|
+
if self.debug:
|
|
39
|
+
logger.debug("Received message: {%s}", message)
|
|
40
|
+
if self.callback:
|
|
41
|
+
self.callback(message)
|
|
42
|
+
except ConnectionClosedError as e:
|
|
43
|
+
logger.error(f"WebSocket connection closed: {e}")
|
|
24
44
|
if self.callback:
|
|
25
|
-
self.callback(
|
|
45
|
+
self.callback(json.dumps({"event": "error", "code": "ConnClosed", "msg": str(e)}))
|
|
46
|
+
raise
|
|
47
|
+
|
|
48
|
+
async def login(self):
|
|
49
|
+
"""
|
|
50
|
+
Login method for business channel that requires authentication (e.g. /ws/v5/business)
|
|
51
|
+
"""
|
|
52
|
+
if not self.apiKey or not self.secretKey or not self.passphrase:
|
|
53
|
+
raise ValueError("apiKey, secretKey and passphrase are required for login")
|
|
26
54
|
|
|
27
|
-
|
|
55
|
+
loginPayload = WsUtils.initLoginParams(
|
|
56
|
+
useServerTime=False,
|
|
57
|
+
apiKey=self.apiKey,
|
|
58
|
+
passphrase=self.passphrase,
|
|
59
|
+
secretKey=self.secretKey
|
|
60
|
+
)
|
|
61
|
+
if self.debug:
|
|
62
|
+
logger.debug(f"login: {loginPayload}")
|
|
63
|
+
await self.websocket.send(loginPayload)
|
|
64
|
+
self.isLoggedIn = True
|
|
65
|
+
return True
|
|
66
|
+
|
|
67
|
+
async def subscribe(self, params: list, callback, id: str = None):
|
|
28
68
|
self.callback = callback
|
|
29
|
-
|
|
69
|
+
payload_dict = {
|
|
30
70
|
"op": "subscribe",
|
|
31
71
|
"args": params
|
|
32
|
-
}
|
|
72
|
+
}
|
|
73
|
+
if id is not None:
|
|
74
|
+
payload_dict["id"] = id
|
|
75
|
+
payload = json.dumps(payload_dict)
|
|
76
|
+
if self.debug:
|
|
77
|
+
logger.debug(f"subscribe: {payload}")
|
|
33
78
|
await self.websocket.send(payload)
|
|
34
79
|
# await self.consume()
|
|
35
80
|
|
|
36
|
-
async def unsubscribe(self, params: list, callback):
|
|
81
|
+
async def unsubscribe(self, params: list, callback, id: str = None):
|
|
37
82
|
self.callback = callback
|
|
38
|
-
|
|
83
|
+
payload_dict = {
|
|
39
84
|
"op": "unsubscribe",
|
|
40
85
|
"args": params
|
|
41
|
-
}
|
|
42
|
-
|
|
86
|
+
}
|
|
87
|
+
if id is not None:
|
|
88
|
+
payload_dict["id"] = id
|
|
89
|
+
payload = json.dumps(payload_dict)
|
|
90
|
+
if self.debug:
|
|
91
|
+
logger.debug(f"unsubscribe: {payload}")
|
|
92
|
+
else:
|
|
93
|
+
logger.info(f"unsubscribe: {payload}")
|
|
94
|
+
await self.websocket.send(payload)
|
|
95
|
+
|
|
96
|
+
async def send(self, op: str, args: list, callback=None, id: str = None):
|
|
97
|
+
"""
|
|
98
|
+
Generic send method
|
|
99
|
+
:param op: Operation type
|
|
100
|
+
:param args: Parameter list
|
|
101
|
+
:param callback: Callback function
|
|
102
|
+
:param id: Optional request ID
|
|
103
|
+
"""
|
|
104
|
+
if callback:
|
|
105
|
+
self.callback = callback
|
|
106
|
+
payload_dict = {
|
|
107
|
+
"op": op,
|
|
108
|
+
"args": args
|
|
109
|
+
}
|
|
110
|
+
if id is not None:
|
|
111
|
+
payload_dict["id"] = id
|
|
112
|
+
payload = json.dumps(payload_dict)
|
|
113
|
+
if self.debug:
|
|
114
|
+
logger.debug(f"send: {payload}")
|
|
43
115
|
await self.websocket.send(payload)
|
|
44
116
|
|
|
45
117
|
async def stop(self):
|
|
46
118
|
await self.factory.close()
|
|
47
|
-
self.loop.stop()
|
|
48
119
|
|
|
49
120
|
async def start(self):
|
|
50
|
-
|
|
121
|
+
if self.debug:
|
|
122
|
+
logger.debug("Connecting to WebSocket...")
|
|
123
|
+
else:
|
|
124
|
+
logger.info("Connecting to WebSocket...")
|
|
51
125
|
await self.connect()
|
|
52
|
-
self.loop.create_task(self.consume())
|
|
126
|
+
return self.loop.create_task(self.consume())
|
|
53
127
|
|
|
54
128
|
def stop_sync(self):
|
|
55
|
-
self.loop.
|
|
129
|
+
if self.loop.is_running():
|
|
130
|
+
future = asyncio.run_coroutine_threadsafe(self.stop(), self.loop)
|
|
131
|
+
future.result(timeout=10)
|
|
132
|
+
else:
|
|
133
|
+
self.loop.run_until_complete(self.stop())
|
|
@@ -1,21 +1,42 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: python-okx
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: Python SDK for OKX
|
|
5
5
|
Home-page: https://okx.com/docs-v5/
|
|
6
6
|
Author: okxv5api
|
|
7
7
|
Author-email: api@okg.com
|
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
9
15
|
Classifier: License :: OSI Approved :: MIT License
|
|
10
16
|
Classifier: Operating System :: OS Independent
|
|
17
|
+
Requires-Python: >=3.7
|
|
11
18
|
Description-Content-Type: text/markdown
|
|
12
|
-
Requires-Dist:
|
|
13
|
-
Requires-Dist:
|
|
14
|
-
Requires-Dist:
|
|
15
|
-
Requires-Dist:
|
|
16
|
-
Requires-Dist:
|
|
17
|
-
Requires-Dist:
|
|
18
|
-
Requires-Dist:
|
|
19
|
+
Requires-Dist: httpx[http2]>=0.24.0
|
|
20
|
+
Requires-Dist: requests>=2.25.0
|
|
21
|
+
Requires-Dist: websockets>=10.0
|
|
22
|
+
Requires-Dist: certifi>=2021.0.0
|
|
23
|
+
Requires-Dist: loguru>=0.7.0
|
|
24
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
25
|
+
Requires-Dist: pytest>=7.0.0
|
|
26
|
+
Requires-Dist: pytest-asyncio>=0.21.0
|
|
27
|
+
Requires-Dist: pytest-cov>=4.0.0
|
|
28
|
+
Requires-Dist: ruff>=0.1.0
|
|
29
|
+
Requires-Dist: build>=1.0.0
|
|
30
|
+
Requires-Dist: twine>=4.0.0
|
|
31
|
+
Dynamic: author
|
|
32
|
+
Dynamic: author-email
|
|
33
|
+
Dynamic: classifier
|
|
34
|
+
Dynamic: description
|
|
35
|
+
Dynamic: description-content-type
|
|
36
|
+
Dynamic: home-page
|
|
37
|
+
Dynamic: requires-dist
|
|
38
|
+
Dynamic: requires-python
|
|
39
|
+
Dynamic: summary
|
|
19
40
|
|
|
20
41
|
### Overview
|
|
21
42
|
This is an unofficial Python wrapper for the [OKX exchange v5 API](https://www.okx.com/okx-api)
|
|
@@ -37,22 +58,83 @@ Make sure you update often and check the [Changelog](https://www.okx.com/docs-v5
|
|
|
37
58
|
- Websocket handling with reconnection and multiplexed connections
|
|
38
59
|
|
|
39
60
|
### Quick start
|
|
40
|
-
#### Prerequisites
|
|
41
61
|
|
|
42
|
-
|
|
62
|
+
#### Prerequisites
|
|
43
63
|
|
|
44
|
-
`
|
|
64
|
+
`python version:>=3.7`
|
|
45
65
|
|
|
46
|
-
#### Step 1:
|
|
66
|
+
#### Step 1: Register an account on OKX and apply for an API key
|
|
47
67
|
- Register for an account: https://www.okx.com/account/register
|
|
48
68
|
- Apply for an API key: https://www.okx.com/account/users/myApi
|
|
49
69
|
|
|
50
|
-
#### Step 2:
|
|
70
|
+
#### Step 2: Install python-okx
|
|
51
71
|
|
|
52
|
-
```
|
|
72
|
+
```bash
|
|
53
73
|
pip install python-okx
|
|
54
74
|
```
|
|
55
75
|
|
|
76
|
+
### API Credentials
|
|
77
|
+
|
|
78
|
+
#### Option 1: Hardcoded credentials
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
from okx import Account
|
|
82
|
+
|
|
83
|
+
account = Account.AccountAPI(
|
|
84
|
+
api_key="your-api-key-here",
|
|
85
|
+
api_secret_key="your-api-secret-here",
|
|
86
|
+
passphrase="your-passphrase-here",
|
|
87
|
+
flag="1", # 0 = live trading, 1 = demo trading
|
|
88
|
+
debug=False
|
|
89
|
+
)
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
#### Option 2: Using `.env` file (recommended)
|
|
93
|
+
|
|
94
|
+
Create a `.env` file in your project root:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
OKX_API_KEY=your-api-key-here
|
|
98
|
+
OKX_API_SECRET=your-api-secret-here
|
|
99
|
+
OKX_PASSPHRASE=your-passphrase-here
|
|
100
|
+
OKX_FLAG=1
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Then load it in your code:
|
|
104
|
+
|
|
105
|
+
```python
|
|
106
|
+
import os
|
|
107
|
+
from dotenv import load_dotenv
|
|
108
|
+
from okx import Account
|
|
109
|
+
|
|
110
|
+
load_dotenv()
|
|
111
|
+
|
|
112
|
+
account = Account.AccountAPI(
|
|
113
|
+
api_key=os.getenv('OKX_API_KEY'),
|
|
114
|
+
api_secret_key=os.getenv('OKX_API_SECRET'),
|
|
115
|
+
passphrase=os.getenv('OKX_PASSPHRASE'),
|
|
116
|
+
flag=os.getenv('OKX_FLAG', '1'),
|
|
117
|
+
debug=False
|
|
118
|
+
)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Development Setup
|
|
122
|
+
|
|
123
|
+
For contributors or local development:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
# Clone the repository
|
|
127
|
+
git clone https://github.com/okxapi/python-okx.git
|
|
128
|
+
cd python-okx
|
|
129
|
+
|
|
130
|
+
# Install dependencies
|
|
131
|
+
pip install -r requirements.txt
|
|
132
|
+
pip install -e .
|
|
133
|
+
|
|
134
|
+
# Run tests
|
|
135
|
+
pytest test/unit/ -v
|
|
136
|
+
```
|
|
137
|
+
|
|
56
138
|
#### Step 3: Run examples
|
|
57
139
|
|
|
58
140
|
- Fill in API credentials in the corresponding examples
|
|
@@ -1,21 +1,22 @@
|
|
|
1
|
-
okx/Account.py,sha256=
|
|
1
|
+
okx/Account.py,sha256=mK_q3P1ZcPI8iuZuZ5TrMIV4eOLQUP3O3Ts3vasLQMI,16143
|
|
2
2
|
okx/BlockTrading.py,sha256=A4OcuLuEHucAIhc8doPGr2o6dsCjVbv-a6YiTlzwdEw,4128
|
|
3
|
-
okx/Convert.py,sha256=
|
|
3
|
+
okx/Convert.py,sha256=AlFKMOnn6UPpFvak6iQSpRq6UtrBSBNaYwwZTRRBk6E,1881
|
|
4
4
|
okx/CopyTrading.py,sha256=Mso5GE9UQTJVrL4V3jIyU1QWBtzr-m32edrGDoRHk84,2751
|
|
5
|
+
okx/DualInvest.py,sha256=CTuvjI6KDgkB4trfjFZLiBNB9FO0r3gampPx3JkioE8,2782
|
|
5
6
|
okx/FDBroker.py,sha256=nUpcR_XtUcKa1Jk_B1OUZCjiv9ifQnxGRsLNHDxdGno,788
|
|
6
|
-
okx/Funding.py,sha256=
|
|
7
|
-
okx/Grid.py,sha256=
|
|
7
|
+
okx/Funding.py,sha256=lQcx1JUyebhbQGd978K_LuJUTi52Wi1GjrnI40i9Xrc,5040
|
|
8
|
+
okx/Grid.py,sha256=8rPEjojbyZ_kJdzvHMHVkVTp-xrdUC0SCQ2rmY35rPA,6911
|
|
8
9
|
okx/MarketData.py,sha256=4GO8Vdkh02GQFJeqC0k4ej3w49wkZroEOoVJ407_ju4,4827
|
|
9
|
-
okx/PublicData.py,sha256=
|
|
10
|
+
okx/PublicData.py,sha256=SkiHA3AiXhPHjZ4OK_k7YyAX6A5i3gbEWZDk5swibHE,5938
|
|
10
11
|
okx/SpreadTrading.py,sha256=Dy7VZ8WK4HyxhMvLY1G5RukRHI_T3d0fnrHZifv9Uyw,3227
|
|
11
12
|
okx/Status.py,sha256=hGnn-tw302xXCDWly7LssaG3-29HcHZRhrBFUuGrSS0,495
|
|
12
13
|
okx/SubAccount.py,sha256=BcwEzi_Y63k-rS9IffFKAJ2ywVl7o9J0U9sBzg87Lpw,3264
|
|
13
|
-
okx/Trade.py,sha256=
|
|
14
|
-
okx/TradingData.py,sha256=
|
|
15
|
-
okx/__init__.py,sha256=
|
|
16
|
-
okx/consts.py,sha256=
|
|
14
|
+
okx/Trade.py,sha256=nw93Zar92U5lIJ5WhPK6v-W8l_J_-sAY4ZvIx8Njp_U,13880
|
|
15
|
+
okx/TradingData.py,sha256=T_55BXEcEXj7Ti5eO1hfRo6fgdg5i2VxXYl4G3acgnE,3498
|
|
16
|
+
okx/__init__.py,sha256=_pOZk8eys0MjXeHo_Sk8gNJ8aujfqAwRu-iN7m7khSY,58
|
|
17
|
+
okx/consts.py,sha256=oPuecL5y3iBt6GfSubJPFIzE5gounrISJUrGK_q7At0,15745
|
|
17
18
|
okx/exceptions.py,sha256=ERuCnvnudJNBNACh3LNE3km5GJy0BGC5C3OKjgHXFaU,1182
|
|
18
|
-
okx/okxclient.py,sha256=
|
|
19
|
+
okx/okxclient.py,sha256=wywtgB73QelIlsQHFkOcRQsichdpm1n8AlYizB9S_rI,3040
|
|
19
20
|
okx/utils.py,sha256=yMlqB3OF6NDBrz7P6rEmD8PWe2xeqh0Gw8lceUFWy0A,1879
|
|
20
21
|
okx/Finance/EthStaking.py,sha256=hOl_i4VOBjejeyBboG1-37rhVnlSoOZ44e-Jzger-bQ,1612
|
|
21
22
|
okx/Finance/FlexibleLoan.py,sha256=L-j1O73fEIIiM_h8NvCT6NocUv6KAx2WEQU6F9GRzIw,2351
|
|
@@ -24,11 +25,11 @@ okx/Finance/SolStaking.py,sha256=VcEU4ukbU5bO8qe00DDfFJ0DUIZZS9ZO_-A2evJz5e0,160
|
|
|
24
25
|
okx/Finance/StakingDefi.py,sha256=eWyAAmMDht4B4N5VWf3bj3FxSBETMXaay-Luu4GN3ls,2250
|
|
25
26
|
okx/Finance/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
27
|
okx/websocket/WebSocketFactory.py,sha256=ba0ZNUggghZ_YlWYB7g8UCv6MewqVr9wn75YOtIU5kE,845
|
|
27
|
-
okx/websocket/WsPrivateAsync.py,sha256=
|
|
28
|
-
okx/websocket/WsPublicAsync.py,sha256=
|
|
28
|
+
okx/websocket/WsPrivateAsync.py,sha256=yssG16oniViPQhIUb52kPj_TmNhrulXY7GknkkjAZ7Q,6992
|
|
29
|
+
okx/websocket/WsPublicAsync.py,sha256=rOBVvO1Hm0SeIKGJYanom0Vs4FjH8wZHFNjsaXCJWJA,4269
|
|
29
30
|
okx/websocket/WsUtils.py,sha256=ynkCtfvkrwBUiCi-_yqp1xTn3hhVd_o5ITIhD1xLdBo,2199
|
|
30
31
|
okx/websocket/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
31
|
-
python_okx-0.4.
|
|
32
|
-
python_okx-0.4.
|
|
33
|
-
python_okx-0.4.
|
|
34
|
-
python_okx-0.4.
|
|
32
|
+
python_okx-0.4.2.dist-info/METADATA,sha256=YbPxhLxTFDkwS6p140kxhhWfX2VaeeumsS1m5sz_vNc,4912
|
|
33
|
+
python_okx-0.4.2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
34
|
+
python_okx-0.4.2.dist-info/top_level.txt,sha256=rrbhHZwiw4YlhG1LWWaAODvRxyZr3onxFnqwsHdikDc,4
|
|
35
|
+
python_okx-0.4.2.dist-info/RECORD,,
|
|
File without changes
|