dnse-sdk 0.1.0__tar.gz
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.
- dnse_sdk-0.1.0/PKG-INFO +60 -0
- dnse_sdk-0.1.0/README.md +52 -0
- dnse_sdk-0.1.0/dnse/__init__.py +4 -0
- dnse_sdk-0.1.0/dnse/client.py +291 -0
- dnse_sdk-0.1.0/dnse/common.py +103 -0
- dnse_sdk-0.1.0/dnse_sdk.egg-info/PKG-INFO +60 -0
- dnse_sdk-0.1.0/dnse_sdk.egg-info/SOURCES.txt +11 -0
- dnse_sdk-0.1.0/dnse_sdk.egg-info/dependency_links.txt +1 -0
- dnse_sdk-0.1.0/dnse_sdk.egg-info/top_level.txt +1 -0
- dnse_sdk-0.1.0/pyproject.toml +3 -0
- dnse_sdk-0.1.0/setup.cfg +22 -0
- dnse_sdk-0.1.0/setup.py +4 -0
dnse_sdk-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dnse-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: DNSE Python SDK
|
|
5
|
+
Author: DNSE
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
|
|
9
|
+
# DNSE Python SDK
|
|
10
|
+
|
|
11
|
+
This SDK provides a simple client for calling the DNSE APIs with HMAC signatures.
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
|
|
15
|
+
- Python 3.8+
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from dnse import DNSEClient
|
|
21
|
+
|
|
22
|
+
client = DNSEClient(
|
|
23
|
+
api_key="replace-with-api-key",
|
|
24
|
+
api_secret="replace-with-api-secret",
|
|
25
|
+
base_url="https://openapi.dnse.com.vn",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
status, body = client.get_accounts(dry_run=False)
|
|
29
|
+
print(status, body)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Dry run
|
|
33
|
+
|
|
34
|
+
Set `dry_run=True` on any API call to print the request and skip the network call.
|
|
35
|
+
|
|
36
|
+
## Examples
|
|
37
|
+
|
|
38
|
+
Run any example from the `sdk/python/trading-api` directory:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
python sdk/python/trading-api/get_accounts.py
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Available examples:
|
|
45
|
+
|
|
46
|
+
- cancel_order.py
|
|
47
|
+
- create_trading_token.py
|
|
48
|
+
- get_accounts.py
|
|
49
|
+
- get_balances.py
|
|
50
|
+
- get_deals.py
|
|
51
|
+
- get_loan_packages.py
|
|
52
|
+
- get_ohlc.py
|
|
53
|
+
- get_order_detail.py
|
|
54
|
+
- get_order_history.py
|
|
55
|
+
- get_orders.py
|
|
56
|
+
- get_ppse.py
|
|
57
|
+
- get_security_definition.py
|
|
58
|
+
- post_order.py
|
|
59
|
+
- put_order.py
|
|
60
|
+
- send_email_otp.py
|
dnse_sdk-0.1.0/README.md
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# DNSE Python SDK
|
|
2
|
+
|
|
3
|
+
This SDK provides a simple client for calling the DNSE APIs with HMAC signatures.
|
|
4
|
+
|
|
5
|
+
## Requirements
|
|
6
|
+
|
|
7
|
+
- Python 3.8+
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```python
|
|
12
|
+
from dnse import DNSEClient
|
|
13
|
+
|
|
14
|
+
client = DNSEClient(
|
|
15
|
+
api_key="replace-with-api-key",
|
|
16
|
+
api_secret="replace-with-api-secret",
|
|
17
|
+
base_url="https://openapi.dnse.com.vn",
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
status, body = client.get_accounts(dry_run=False)
|
|
21
|
+
print(status, body)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Dry run
|
|
25
|
+
|
|
26
|
+
Set `dry_run=True` on any API call to print the request and skip the network call.
|
|
27
|
+
|
|
28
|
+
## Examples
|
|
29
|
+
|
|
30
|
+
Run any example from the `sdk/python/trading-api` directory:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
python sdk/python/trading-api/get_accounts.py
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Available examples:
|
|
37
|
+
|
|
38
|
+
- cancel_order.py
|
|
39
|
+
- create_trading_token.py
|
|
40
|
+
- get_accounts.py
|
|
41
|
+
- get_balances.py
|
|
42
|
+
- get_deals.py
|
|
43
|
+
- get_loan_packages.py
|
|
44
|
+
- get_ohlc.py
|
|
45
|
+
- get_order_detail.py
|
|
46
|
+
- get_order_history.py
|
|
47
|
+
- get_orders.py
|
|
48
|
+
- get_ppse.py
|
|
49
|
+
- get_security_definition.py
|
|
50
|
+
- post_order.py
|
|
51
|
+
- put_order.py
|
|
52
|
+
- send_email_otp.py
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
from urllib import parse, request
|
|
5
|
+
|
|
6
|
+
from .common import build_signature, get_date_header_name
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class DNSEClient:
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
api_key,
|
|
13
|
+
api_secret,
|
|
14
|
+
base_url="https://openapi.dnse.com.vn",
|
|
15
|
+
algorithm="hmac-sha256",
|
|
16
|
+
hmac_nonce_enabled=True,
|
|
17
|
+
):
|
|
18
|
+
self._api_key = api_key
|
|
19
|
+
self._api_secret = api_secret
|
|
20
|
+
self._base_url = base_url.rstrip("/")
|
|
21
|
+
self._algorithm = algorithm
|
|
22
|
+
self._hmac_nonce_enabled = hmac_nonce_enabled
|
|
23
|
+
|
|
24
|
+
def get_accounts(self, dry_run=False):
|
|
25
|
+
return self._request("GET", "/accounts", dry_run=dry_run)
|
|
26
|
+
|
|
27
|
+
def get_balances(self, account_no, dry_run=False):
|
|
28
|
+
return self._request("GET", f"/accounts/{account_no}/balances", dry_run=dry_run)
|
|
29
|
+
|
|
30
|
+
def get_loan_packages(self, account_no, market_type, symbol=None, dry_run=False):
|
|
31
|
+
query = {"marketType": market_type}
|
|
32
|
+
if symbol:
|
|
33
|
+
query["symbol"] = symbol
|
|
34
|
+
return self._request(
|
|
35
|
+
"GET",
|
|
36
|
+
f"/accounts/{account_no}/loan-packages",
|
|
37
|
+
query=query,
|
|
38
|
+
dry_run=dry_run,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def get_deals(self, account_no, market_type, dry_run=False):
|
|
42
|
+
return self._request(
|
|
43
|
+
"GET",
|
|
44
|
+
f"/accounts/{account_no}/deals",
|
|
45
|
+
query={"marketType": market_type},
|
|
46
|
+
dry_run=dry_run,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def get_orders(self, account_no, market_type, order_category=None, dry_run=False):
|
|
50
|
+
query = {"marketType": market_type}
|
|
51
|
+
if order_category:
|
|
52
|
+
query["orderCategory"] = order_category
|
|
53
|
+
return self._request(
|
|
54
|
+
"GET",
|
|
55
|
+
f"/accounts/{account_no}/orders",
|
|
56
|
+
query=query,
|
|
57
|
+
dry_run=dry_run,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def get_order_detail(self, account_no, order_id, market_type, order_category=None, dry_run=False):
|
|
61
|
+
query = {"marketType": market_type}
|
|
62
|
+
if order_category:
|
|
63
|
+
query["orderCategory"] = order_category
|
|
64
|
+
return self._request(
|
|
65
|
+
"GET",
|
|
66
|
+
f"/accounts/{account_no}/orders/{order_id}",
|
|
67
|
+
query=query,
|
|
68
|
+
dry_run=dry_run,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def get_order_history(
|
|
72
|
+
self,
|
|
73
|
+
account_no,
|
|
74
|
+
market_type,
|
|
75
|
+
from_date=None,
|
|
76
|
+
to_date=None,
|
|
77
|
+
page_size=None,
|
|
78
|
+
page_index=None,
|
|
79
|
+
dry_run=False,
|
|
80
|
+
):
|
|
81
|
+
query = {"marketType": market_type}
|
|
82
|
+
if from_date:
|
|
83
|
+
query["from"] = from_date
|
|
84
|
+
if to_date:
|
|
85
|
+
query["to"] = to_date
|
|
86
|
+
if page_size is not None:
|
|
87
|
+
query["pageSize"] = page_size
|
|
88
|
+
if page_index is not None:
|
|
89
|
+
query["pageIndex"] = page_index
|
|
90
|
+
return self._request(
|
|
91
|
+
"GET",
|
|
92
|
+
f"/accounts/{account_no}/orders/history",
|
|
93
|
+
query=query,
|
|
94
|
+
dry_run=dry_run,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def get_ppse(self, account_no, market_type, symbol, price, loan_package_id, dry_run=False):
|
|
98
|
+
return self._request(
|
|
99
|
+
"GET",
|
|
100
|
+
f"/accounts/{account_no}/ppse",
|
|
101
|
+
query={
|
|
102
|
+
"marketType": market_type,
|
|
103
|
+
"symbol": symbol,
|
|
104
|
+
"price": str(price),
|
|
105
|
+
"loanPackageId": str(loan_package_id),
|
|
106
|
+
},
|
|
107
|
+
dry_run=dry_run,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
def get_security_definition(self, symbol, board_id=None, dry_run=False):
|
|
111
|
+
query = {}
|
|
112
|
+
if board_id:
|
|
113
|
+
query["boardId"] = board_id
|
|
114
|
+
return self._request(
|
|
115
|
+
"GET",
|
|
116
|
+
f"/price/secdef/{symbol}",
|
|
117
|
+
query=query if query else None,
|
|
118
|
+
dry_run=dry_run,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
def get_ohlc(self, bar_type, query=None, dry_run=False):
|
|
122
|
+
request_query = dict(query or {})
|
|
123
|
+
request_query["type"] = bar_type
|
|
124
|
+
return self._request(
|
|
125
|
+
"GET",
|
|
126
|
+
"/price/ohlc",
|
|
127
|
+
query=request_query,
|
|
128
|
+
dry_run=dry_run,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
def post_order(self, market_type, payload, trading_token, order_category="NORMAL", dry_run=False):
|
|
132
|
+
headers = {"trading-token": trading_token}
|
|
133
|
+
query = {"marketType": market_type}
|
|
134
|
+
if order_category:
|
|
135
|
+
query["orderCategory"] = order_category
|
|
136
|
+
return self._request(
|
|
137
|
+
"POST",
|
|
138
|
+
"/accounts/orders",
|
|
139
|
+
query=query,
|
|
140
|
+
body=payload,
|
|
141
|
+
headers=headers,
|
|
142
|
+
dry_run=dry_run,
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def put_order(
|
|
146
|
+
self,
|
|
147
|
+
account_no,
|
|
148
|
+
order_id,
|
|
149
|
+
market_type,
|
|
150
|
+
payload,
|
|
151
|
+
trading_token,
|
|
152
|
+
order_category=None,
|
|
153
|
+
dry_run=False,
|
|
154
|
+
):
|
|
155
|
+
headers = {"trading-token": trading_token}
|
|
156
|
+
query = {"marketType": market_type}
|
|
157
|
+
if order_category:
|
|
158
|
+
query["orderCategory"] = order_category
|
|
159
|
+
return self._request(
|
|
160
|
+
"PUT",
|
|
161
|
+
f"/accounts/{account_no}/orders/{order_id}",
|
|
162
|
+
query=query,
|
|
163
|
+
body=payload,
|
|
164
|
+
headers=headers,
|
|
165
|
+
dry_run=dry_run,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
def cancel_order(
|
|
169
|
+
self,
|
|
170
|
+
account_no,
|
|
171
|
+
order_id,
|
|
172
|
+
market_type,
|
|
173
|
+
trading_token,
|
|
174
|
+
order_category=None,
|
|
175
|
+
dry_run=False,
|
|
176
|
+
):
|
|
177
|
+
headers = {"trading-token": trading_token}
|
|
178
|
+
query = {"marketType": market_type}
|
|
179
|
+
if order_category:
|
|
180
|
+
query["orderCategory"] = order_category
|
|
181
|
+
return self._request(
|
|
182
|
+
"DELETE",
|
|
183
|
+
f"/accounts/{account_no}/orders/{order_id}",
|
|
184
|
+
query=query,
|
|
185
|
+
headers=headers,
|
|
186
|
+
dry_run=dry_run,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
def create_trading_token(self, otp_type, passcode, dry_run=False):
|
|
190
|
+
return self._request(
|
|
191
|
+
"POST",
|
|
192
|
+
"/registration/trading-token",
|
|
193
|
+
body={"otpType": otp_type, "passcode": passcode},
|
|
194
|
+
dry_run=dry_run,
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
def send_email_otp(self, dry_run=False):
|
|
198
|
+
return self._request(
|
|
199
|
+
"POST",
|
|
200
|
+
"/registration/send-email-otp",
|
|
201
|
+
dry_run=dry_run,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
def close_deal(self, deal_id, account_no, market_type, payload, trading_token, dry_run=False):
|
|
205
|
+
headers = {"trading-token": trading_token}
|
|
206
|
+
query = {"marketType": market_type}
|
|
207
|
+
return self._request(
|
|
208
|
+
"POST",
|
|
209
|
+
f"/accounts/{account_no}/deals/{deal_id}/close",
|
|
210
|
+
query=query,
|
|
211
|
+
body=payload,
|
|
212
|
+
headers=headers,
|
|
213
|
+
dry_run=dry_run,
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
def _request(self, method, path, query=None, body=None, headers=None, dry_run=False):
|
|
217
|
+
debug = os.getenv("DEBUG", "").lower() == "true"
|
|
218
|
+
url = self._build_url(path, query)
|
|
219
|
+
date_value, signature_header_value = self._signature_headers(method, path)
|
|
220
|
+
date_header_name = get_date_header_name()
|
|
221
|
+
|
|
222
|
+
data = None
|
|
223
|
+
if body is not None:
|
|
224
|
+
data = json.dumps(body).encode("utf-8")
|
|
225
|
+
|
|
226
|
+
req = request.Request(url, data=data, method=method)
|
|
227
|
+
req.add_header(date_header_name, date_value)
|
|
228
|
+
req.add_header("X-Signature", signature_header_value)
|
|
229
|
+
req.add_header("x-api-key", self._api_key)
|
|
230
|
+
|
|
231
|
+
if body is not None:
|
|
232
|
+
req.add_header("Content-Type", "application/json")
|
|
233
|
+
|
|
234
|
+
if headers:
|
|
235
|
+
for key, value in headers.items():
|
|
236
|
+
req.add_header(key, value)
|
|
237
|
+
|
|
238
|
+
if debug or dry_run:
|
|
239
|
+
prefix = "DRY RUN" if dry_run else "DEBUG"
|
|
240
|
+
print(f"{prefix} url:", url)
|
|
241
|
+
print(f"{prefix} method:", method)
|
|
242
|
+
print(f"{prefix} query_params:", query or {})
|
|
243
|
+
print(f"{prefix} headers:", dict(req.header_items()))
|
|
244
|
+
print(f"{prefix} body:", body)
|
|
245
|
+
|
|
246
|
+
if dry_run:
|
|
247
|
+
return None, None
|
|
248
|
+
|
|
249
|
+
try:
|
|
250
|
+
with request.urlopen(req) as resp:
|
|
251
|
+
body_text = resp.read().decode("utf-8")
|
|
252
|
+
return resp.status, body_text
|
|
253
|
+
except request.HTTPError as err:
|
|
254
|
+
body_text = err.read().decode("utf-8") if err.fp else ""
|
|
255
|
+
return err.code, body_text
|
|
256
|
+
|
|
257
|
+
def _build_url(self, path, query):
|
|
258
|
+
url = f"{self._base_url}{path}"
|
|
259
|
+
if query:
|
|
260
|
+
url = f"{url}?{parse.urlencode(query)}"
|
|
261
|
+
return url
|
|
262
|
+
|
|
263
|
+
def _signature_headers(self, method, path):
|
|
264
|
+
date_value = self._date_header()
|
|
265
|
+
nonce = None
|
|
266
|
+
if self._hmac_nonce_enabled:
|
|
267
|
+
import uuid
|
|
268
|
+
|
|
269
|
+
nonce = uuid.uuid4().hex
|
|
270
|
+
|
|
271
|
+
headers_list, signature = build_signature(
|
|
272
|
+
self._api_secret,
|
|
273
|
+
method,
|
|
274
|
+
path,
|
|
275
|
+
date_value,
|
|
276
|
+
self._algorithm,
|
|
277
|
+
nonce=nonce,
|
|
278
|
+
header_name=get_date_header_name(),
|
|
279
|
+
)
|
|
280
|
+
signature_header_value = (
|
|
281
|
+
f'Signature keyId="{self._api_key}",algorithm="{self._algorithm}",'
|
|
282
|
+
f'headers="{headers_list}",signature="{signature}"'
|
|
283
|
+
)
|
|
284
|
+
if nonce:
|
|
285
|
+
signature_header_value += f',nonce="{nonce}"'
|
|
286
|
+
return date_value, signature_header_value
|
|
287
|
+
|
|
288
|
+
def _date_header(self):
|
|
289
|
+
from datetime import datetime, timezone
|
|
290
|
+
|
|
291
|
+
return datetime.now(timezone.utc).strftime("%a, %d %b %Y %H:%M:%S %z")
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import base64
|
|
3
|
+
import hashlib
|
|
4
|
+
import hmac
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
from datetime import datetime, timezone
|
|
8
|
+
from urllib import parse, request
|
|
9
|
+
from uuid import uuid4
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_date_header_name():
|
|
13
|
+
return os.getenv("DATE_HEADER", "Date")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def build_signature(secret, method, path, date_value, algorithm, nonce=None, header_name=None):
|
|
17
|
+
header_name = header_name or get_date_header_name()
|
|
18
|
+
header_key = header_name.lower()
|
|
19
|
+
headers = f"(request-target) {header_key}"
|
|
20
|
+
signature_string = (
|
|
21
|
+
f"(request-target): {method.lower()} {path}\n" f"{header_key}: {date_value}"
|
|
22
|
+
)
|
|
23
|
+
if nonce:
|
|
24
|
+
signature_string += f"\nnonce: {nonce}"
|
|
25
|
+
|
|
26
|
+
if algorithm == "hmac-sha256":
|
|
27
|
+
digestmod = hashlib.sha256
|
|
28
|
+
elif algorithm == "hmac-sha384":
|
|
29
|
+
digestmod = hashlib.sha384
|
|
30
|
+
elif algorithm == "hmac-sha512":
|
|
31
|
+
digestmod = hashlib.sha512
|
|
32
|
+
else:
|
|
33
|
+
digestmod = hashlib.sha1
|
|
34
|
+
|
|
35
|
+
mac = hmac.new(secret.encode("utf-8"), signature_string.encode("utf-8"), digestmod)
|
|
36
|
+
encoded = base64.b64encode(mac.digest()).decode("utf-8")
|
|
37
|
+
escaped = parse.quote(encoded, safe="")
|
|
38
|
+
|
|
39
|
+
return headers, escaped
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def send_signed_request(
|
|
43
|
+
url,
|
|
44
|
+
method,
|
|
45
|
+
headers,
|
|
46
|
+
body,
|
|
47
|
+
api_key,
|
|
48
|
+
api_secret,
|
|
49
|
+
algorithm="hmac-sha256",
|
|
50
|
+
hmac_nonce_enabled=True,
|
|
51
|
+
):
|
|
52
|
+
debug = os.getenv("DEBUG", "").lower() == "true"
|
|
53
|
+
parsed = parse.urlparse(url)
|
|
54
|
+
path = parsed.path
|
|
55
|
+
date_value = datetime.now(timezone.utc).strftime("%a, %d %b %Y %H:%M:%S %z")
|
|
56
|
+
date_header_name = get_date_header_name()
|
|
57
|
+
|
|
58
|
+
nonce = uuid4().hex if hmac_nonce_enabled else None
|
|
59
|
+
headers_list, signature = build_signature(
|
|
60
|
+
api_secret,
|
|
61
|
+
method,
|
|
62
|
+
path,
|
|
63
|
+
date_value,
|
|
64
|
+
algorithm,
|
|
65
|
+
nonce=nonce,
|
|
66
|
+
header_name=date_header_name,
|
|
67
|
+
)
|
|
68
|
+
signature_header_value = (
|
|
69
|
+
f'Signature keyId="{api_key}",algorithm="{algorithm}",'
|
|
70
|
+
f'headers="{headers_list}",signature="{signature}"'
|
|
71
|
+
)
|
|
72
|
+
if nonce:
|
|
73
|
+
signature_header_value += f',nonce="{nonce}"'
|
|
74
|
+
|
|
75
|
+
data = None
|
|
76
|
+
if body is not None:
|
|
77
|
+
data = json.dumps(body).encode("utf-8")
|
|
78
|
+
headers.setdefault("Content-Type", "application/json")
|
|
79
|
+
|
|
80
|
+
req = request.Request(url, data=data, method=method)
|
|
81
|
+
req.add_header(date_header_name, date_value)
|
|
82
|
+
req.add_header("X-Signature", signature_header_value)
|
|
83
|
+
|
|
84
|
+
for key, value in headers.items():
|
|
85
|
+
req.add_header(key, value)
|
|
86
|
+
|
|
87
|
+
if debug:
|
|
88
|
+
query_params = parse.parse_qs(parsed.query)
|
|
89
|
+
print("DEBUG url:", url)
|
|
90
|
+
print("DEBUG method:", method)
|
|
91
|
+
print("DEBUG query_params:", query_params)
|
|
92
|
+
print("DEBUG headers:", dict(req.header_items()))
|
|
93
|
+
print("DEBUG body:", body)
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
with request.urlopen(req) as resp:
|
|
97
|
+
body_text = resp.read().decode("utf-8")
|
|
98
|
+
print(body_text)
|
|
99
|
+
except request.HTTPError as err:
|
|
100
|
+
body_text = err.read().decode("utf-8") if err.fp else ""
|
|
101
|
+
print(f"HTTP {err.code} {err.reason}")
|
|
102
|
+
if body_text:
|
|
103
|
+
print(body_text)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dnse-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: DNSE Python SDK
|
|
5
|
+
Author: DNSE
|
|
6
|
+
Requires-Python: >=3.8
|
|
7
|
+
Description-Content-Type: text/markdown
|
|
8
|
+
|
|
9
|
+
# DNSE Python SDK
|
|
10
|
+
|
|
11
|
+
This SDK provides a simple client for calling the DNSE APIs with HMAC signatures.
|
|
12
|
+
|
|
13
|
+
## Requirements
|
|
14
|
+
|
|
15
|
+
- Python 3.8+
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from dnse import DNSEClient
|
|
21
|
+
|
|
22
|
+
client = DNSEClient(
|
|
23
|
+
api_key="replace-with-api-key",
|
|
24
|
+
api_secret="replace-with-api-secret",
|
|
25
|
+
base_url="https://openapi.dnse.com.vn",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
status, body = client.get_accounts(dry_run=False)
|
|
29
|
+
print(status, body)
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Dry run
|
|
33
|
+
|
|
34
|
+
Set `dry_run=True` on any API call to print the request and skip the network call.
|
|
35
|
+
|
|
36
|
+
## Examples
|
|
37
|
+
|
|
38
|
+
Run any example from the `sdk/python/trading-api` directory:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
python sdk/python/trading-api/get_accounts.py
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Available examples:
|
|
45
|
+
|
|
46
|
+
- cancel_order.py
|
|
47
|
+
- create_trading_token.py
|
|
48
|
+
- get_accounts.py
|
|
49
|
+
- get_balances.py
|
|
50
|
+
- get_deals.py
|
|
51
|
+
- get_loan_packages.py
|
|
52
|
+
- get_ohlc.py
|
|
53
|
+
- get_order_detail.py
|
|
54
|
+
- get_order_history.py
|
|
55
|
+
- get_orders.py
|
|
56
|
+
- get_ppse.py
|
|
57
|
+
- get_security_definition.py
|
|
58
|
+
- post_order.py
|
|
59
|
+
- put_order.py
|
|
60
|
+
- send_email_otp.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dnse
|
dnse_sdk-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[metadata]
|
|
2
|
+
name = dnse-sdk
|
|
3
|
+
version = 0.1.0
|
|
4
|
+
description = DNSE Python SDK
|
|
5
|
+
long_description = file: README.md
|
|
6
|
+
long_description_content_type = text/markdown
|
|
7
|
+
author = DNSE
|
|
8
|
+
|
|
9
|
+
[options]
|
|
10
|
+
packages = find:
|
|
11
|
+
python_requires = >=3.8
|
|
12
|
+
include_package_data = True
|
|
13
|
+
|
|
14
|
+
[options.packages.find]
|
|
15
|
+
include =
|
|
16
|
+
dnse
|
|
17
|
+
dnse.*
|
|
18
|
+
|
|
19
|
+
[egg_info]
|
|
20
|
+
tag_build =
|
|
21
|
+
tag_date = 0
|
|
22
|
+
|
dnse_sdk-0.1.0/setup.py
ADDED