dnse-sdk-openapi 0.0.1__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_openapi-0.0.1/PKG-INFO +120 -0
- dnse_sdk_openapi-0.0.1/README.md +114 -0
- dnse_sdk_openapi-0.0.1/broker-api/get_list_care_by.py +22 -0
- dnse_sdk_openapi-0.0.1/dnse/__init__.py +4 -0
- dnse_sdk_openapi-0.0.1/dnse/client.py +408 -0
- dnse_sdk_openapi-0.0.1/dnse/common.py +103 -0
- dnse_sdk_openapi-0.0.1/dnse_sdk_openapi.egg-info/PKG-INFO +120 -0
- dnse_sdk_openapi-0.0.1/dnse_sdk_openapi.egg-info/SOURCES.txt +52 -0
- dnse_sdk_openapi-0.0.1/dnse_sdk_openapi.egg-info/dependency_links.txt +1 -0
- dnse_sdk_openapi-0.0.1/dnse_sdk_openapi.egg-info/top_level.txt +6 -0
- dnse_sdk_openapi-0.0.1/marketdata-api/get_instruments.py +22 -0
- dnse_sdk_openapi-0.0.1/marketdata-api/get_latest_trade.py +22 -0
- dnse_sdk_openapi-0.0.1/marketdata-api/get_ohlc.py +31 -0
- dnse_sdk_openapi-0.0.1/marketdata-api/get_security_definition.py +22 -0
- dnse_sdk_openapi-0.0.1/marketdata-api/get_trades.py +22 -0
- dnse_sdk_openapi-0.0.1/marketdata-api/get_working_dates.py +22 -0
- dnse_sdk_openapi-0.0.1/pyproject.toml +12 -0
- dnse_sdk_openapi-0.0.1/setup.cfg +4 -0
- dnse_sdk_openapi-0.0.1/trading-api/cancel_order.py +29 -0
- dnse_sdk_openapi-0.0.1/trading-api/close_position.py +27 -0
- dnse_sdk_openapi-0.0.1/trading-api/create_trading_token.py +26 -0
- dnse_sdk_openapi-0.0.1/trading-api/get_accounts.py +22 -0
- dnse_sdk_openapi-0.0.1/trading-api/get_balances.py +22 -0
- dnse_sdk_openapi-0.0.1/trading-api/get_close_price.py +22 -0
- dnse_sdk_openapi-0.0.1/trading-api/get_execution_detail.py +28 -0
- dnse_sdk_openapi-0.0.1/trading-api/get_loan_packages.py +27 -0
- dnse_sdk_openapi-0.0.1/trading-api/get_order_detail.py +28 -0
- dnse_sdk_openapi-0.0.1/trading-api/get_order_history.py +30 -0
- dnse_sdk_openapi-0.0.1/trading-api/get_orders.py +27 -0
- dnse_sdk_openapi-0.0.1/trading-api/get_position_by_id.py +26 -0
- dnse_sdk_openapi-0.0.1/trading-api/get_positions.py +26 -0
- dnse_sdk_openapi-0.0.1/trading-api/get_ppse.py +29 -0
- dnse_sdk_openapi-0.0.1/trading-api/post_order.py +38 -0
- dnse_sdk_openapi-0.0.1/trading-api/put_order.py +35 -0
- dnse_sdk_openapi-0.0.1/trading-api/send_email_otp.py +22 -0
- dnse_sdk_openapi-0.0.1/websocket-marketdata/expected_price.py +53 -0
- dnse_sdk_openapi-0.0.1/websocket-marketdata/foreign_investor.py +51 -0
- dnse_sdk_openapi-0.0.1/websocket-marketdata/market_index.py +52 -0
- dnse_sdk_openapi-0.0.1/websocket-marketdata/ohlc.py +55 -0
- dnse_sdk_openapi-0.0.1/websocket-marketdata/ohlc_closed.py +55 -0
- dnse_sdk_openapi-0.0.1/websocket-marketdata/order.py +51 -0
- dnse_sdk_openapi-0.0.1/websocket-marketdata/quote.py +50 -0
- dnse_sdk_openapi-0.0.1/websocket-marketdata/sec_def.py +52 -0
- dnse_sdk_openapi-0.0.1/websocket-marketdata/trade.py +52 -0
- dnse_sdk_openapi-0.0.1/websocket-marketdata/trade_extra.py +51 -0
- dnse_sdk_openapi-0.0.1/websocket-marketdata/trading_websocket/__init__.py +33 -0
- dnse_sdk_openapi-0.0.1/websocket-marketdata/trading_websocket/_version.py +3 -0
- dnse_sdk_openapi-0.0.1/websocket-marketdata/trading_websocket/auth.py +59 -0
- dnse_sdk_openapi-0.0.1/websocket-marketdata/trading_websocket/client.py +790 -0
- dnse_sdk_openapi-0.0.1/websocket-marketdata/trading_websocket/connection.py +151 -0
- dnse_sdk_openapi-0.0.1/websocket-marketdata/trading_websocket/encoding.py +78 -0
- dnse_sdk_openapi-0.0.1/websocket-marketdata/trading_websocket/exceptions.py +38 -0
- dnse_sdk_openapi-0.0.1/websocket-marketdata/trading_websocket/models.py +525 -0
- dnse_sdk_openapi-0.0.1/websocket-marketdata/trading_websocket/py.typed +0 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dnse-sdk-openapi
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: DNSE OpenAPI SDK
|
|
5
|
+
Description-Content-Type: text/markdown
|
|
6
|
+
|
|
7
|
+
# DNSE OpenAPI Python SDK
|
|
8
|
+
|
|
9
|
+
Official Python SDK for integrating with DNSE OpenAPI.
|
|
10
|
+
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
- [Overview](#overview)
|
|
14
|
+
- [Installation](#installation)
|
|
15
|
+
- [Usage](#usage)
|
|
16
|
+
- [Dry Run](#dry-run)
|
|
17
|
+
- [Examples](#examples)
|
|
18
|
+
|
|
19
|
+
### Overview
|
|
20
|
+
|
|
21
|
+
DNSE OpenAPI is an API-first trading platform that enables developers to integrate brokerage, trading, margin, and market data services
|
|
22
|
+
into their own applications.
|
|
23
|
+
|
|
24
|
+
The DNSE Python SDK provides a lightweight client for securely interacting with DNSE OpenAPI REST endpoints. It handles request
|
|
25
|
+
signing, authentication, and communication details, allowing developers to focus on building trading systems, automation strategies,
|
|
26
|
+
and investment applications.
|
|
27
|
+
|
|
28
|
+
### Installation
|
|
29
|
+
|
|
30
|
+
#### Requirements
|
|
31
|
+
|
|
32
|
+
- Python 3.8+
|
|
33
|
+
|
|
34
|
+
#### Install from PyPI
|
|
35
|
+
|
|
36
|
+
```console
|
|
37
|
+
pip install openapi-sdk
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Upgrade
|
|
41
|
+
|
|
42
|
+
### Usage
|
|
43
|
+
|
|
44
|
+
Create a `DNSEClient` instance with your API credentials:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from dnse import DNSEClient
|
|
48
|
+
|
|
49
|
+
client = DNSEClient(
|
|
50
|
+
api_key="your_api_key",
|
|
51
|
+
api_secret="your_api_secret",
|
|
52
|
+
base_url="https://openapi.dnse.com.vn",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
status, body = client.get_accounts(dry_run=False)
|
|
56
|
+
print(status, body)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Dry Run
|
|
60
|
+
|
|
61
|
+
Set `dry_run=True` to preview the request without sending it to DNSE servers. No network call will be executed.
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
client.get_accounts(dry_run=True)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Examples
|
|
68
|
+
|
|
69
|
+
Run any example from the `sdk/python/examples` directory:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
python sdk/python/api/get_accounts.py
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### Trading API
|
|
76
|
+
|
|
77
|
+
| Function | Description |
|
|
78
|
+
|---------------------------|-----------------------------------------------------------------------------------------------------------------------|
|
|
79
|
+
| `get_accounts.py` | Demonstrates how to retrieve all trading sub-accounts managed under the account corresponding to the API Key. |
|
|
80
|
+
| `get_balances.py` | Demonstrates how to retrieve asset balances of a trading sub-account. |
|
|
81
|
+
| `get_loan_packages.py` | Demonstrates how to retrieve available loan package codes. It is necessary for placing an order. |
|
|
82
|
+
| `get_ppse.py` | Demonstrates how to retrieve buying power and selling power before placing an order. |
|
|
83
|
+
| `get_orders.py` | Demonstrates how to retrieve intraday order book. |
|
|
84
|
+
| `get_order_detail.py` | Demonstrates how to retrieve detailed information of a specific order (by ID). |
|
|
85
|
+
| `get_order_history.py` | Demonstrates how to retrieve historical orders. |
|
|
86
|
+
| `get_execution_detail.py` | Demonstrates how to retrieve detailed execution information of an order. |
|
|
87
|
+
| `get_positions.py` | Demonstrates how to retrieve current holding positions. |
|
|
88
|
+
| `get_positions_by_id.py` | Demonstrates how to retrieve detailed information of a specific position (by ID). |
|
|
89
|
+
| `close_position.py` | Demonstrates how to close an existing position (by ID). |
|
|
90
|
+
| `send_email_otp.py` | Demonstrates how to request an OTP sent to your registered email. The OTP is required for generating a trading token. |
|
|
91
|
+
| `create_trading_token.py` | Demonstrates how to generate a Trading Token required for order placement. |
|
|
92
|
+
| `post_order.py` | Demonstrates how to submit a new trading order. |
|
|
93
|
+
| `cancel_order.py` | Demonstrates how to cancel an existing order. |
|
|
94
|
+
| `replace_order.py` | Demonstrates how to modify an existing order. |
|
|
95
|
+
|
|
96
|
+
#### Market Data API
|
|
97
|
+
|
|
98
|
+
| Function | Description |
|
|
99
|
+
|------------------------------|--------------------------------------------------------------------------------------------|
|
|
100
|
+
| `get_security_definition.py` | Demonstrates how to retrieve security definition and instrument details. |
|
|
101
|
+
| `get_instruments.py` | Demonstrates how to retrieve the list of available trading instruments and their metadata. |
|
|
102
|
+
| `get_trades.py` | Demonstrates how to retrieve historical trade data for a specific instrument. |
|
|
103
|
+
| `get_latest_trade.py` | Demonstrates how to retrieve the most recent trade for a specific instrument. |
|
|
104
|
+
| `get_ohlc.py` | Demonstrates how to retrieve OHLC (Open, High, Low, Close) data for a given time range. |
|
|
105
|
+
| `get_close_price.py` | Demonstrates how to retrieve the latest closing price of a specific instrument. |
|
|
106
|
+
| `get_working_dates.py` | Demonstrates how to retrieve trading working dates. |
|
|
107
|
+
|
|
108
|
+
### WebSocket Market Data
|
|
109
|
+
|
|
110
|
+
| Function | Description |
|
|
111
|
+
|-----------------------|--------------------------------------------------------------------------------------|
|
|
112
|
+
| `sec_def.py` | Demonstrates how to receive real-time security definition updates. |
|
|
113
|
+
| `quote.py` | Demonstrates how to receive real-time best bid and ask prices. |
|
|
114
|
+
| `trade.py` | Demonstrates how to receive real-time trade (tick) data. |
|
|
115
|
+
| `trade_extra.py` | Demonstrates how to receive real-time trade data with additional aggregated metrics. |
|
|
116
|
+
| `ohlc.py` | Demonstrates how to receive real-time OHLC data. |
|
|
117
|
+
| `ohlc_closed.py` | Demonstrates how to receive completed OHLC candle data. |
|
|
118
|
+
| `expected_price.py` | Demonstrates how to receive expected price data during ATO and ATC sessions. |
|
|
119
|
+
| `foreign_investor.py` | Demonstrates how to receive foreign investor trading data. |
|
|
120
|
+
| `market_index.py` | Demonstrates how to receive market index data. |
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# DNSE OpenAPI Python SDK
|
|
2
|
+
|
|
3
|
+
Official Python SDK for integrating with DNSE OpenAPI.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Overview](#overview)
|
|
8
|
+
- [Installation](#installation)
|
|
9
|
+
- [Usage](#usage)
|
|
10
|
+
- [Dry Run](#dry-run)
|
|
11
|
+
- [Examples](#examples)
|
|
12
|
+
|
|
13
|
+
### Overview
|
|
14
|
+
|
|
15
|
+
DNSE OpenAPI is an API-first trading platform that enables developers to integrate brokerage, trading, margin, and market data services
|
|
16
|
+
into their own applications.
|
|
17
|
+
|
|
18
|
+
The DNSE Python SDK provides a lightweight client for securely interacting with DNSE OpenAPI REST endpoints. It handles request
|
|
19
|
+
signing, authentication, and communication details, allowing developers to focus on building trading systems, automation strategies,
|
|
20
|
+
and investment applications.
|
|
21
|
+
|
|
22
|
+
### Installation
|
|
23
|
+
|
|
24
|
+
#### Requirements
|
|
25
|
+
|
|
26
|
+
- Python 3.8+
|
|
27
|
+
|
|
28
|
+
#### Install from PyPI
|
|
29
|
+
|
|
30
|
+
```console
|
|
31
|
+
pip install openapi-sdk
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Upgrade
|
|
35
|
+
|
|
36
|
+
### Usage
|
|
37
|
+
|
|
38
|
+
Create a `DNSEClient` instance with your API credentials:
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from dnse import DNSEClient
|
|
42
|
+
|
|
43
|
+
client = DNSEClient(
|
|
44
|
+
api_key="your_api_key",
|
|
45
|
+
api_secret="your_api_secret",
|
|
46
|
+
base_url="https://openapi.dnse.com.vn",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
status, body = client.get_accounts(dry_run=False)
|
|
50
|
+
print(status, body)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Dry Run
|
|
54
|
+
|
|
55
|
+
Set `dry_run=True` to preview the request without sending it to DNSE servers. No network call will be executed.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
client.get_accounts(dry_run=True)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Examples
|
|
62
|
+
|
|
63
|
+
Run any example from the `sdk/python/examples` directory:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
python sdk/python/api/get_accounts.py
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
#### Trading API
|
|
70
|
+
|
|
71
|
+
| Function | Description |
|
|
72
|
+
|---------------------------|-----------------------------------------------------------------------------------------------------------------------|
|
|
73
|
+
| `get_accounts.py` | Demonstrates how to retrieve all trading sub-accounts managed under the account corresponding to the API Key. |
|
|
74
|
+
| `get_balances.py` | Demonstrates how to retrieve asset balances of a trading sub-account. |
|
|
75
|
+
| `get_loan_packages.py` | Demonstrates how to retrieve available loan package codes. It is necessary for placing an order. |
|
|
76
|
+
| `get_ppse.py` | Demonstrates how to retrieve buying power and selling power before placing an order. |
|
|
77
|
+
| `get_orders.py` | Demonstrates how to retrieve intraday order book. |
|
|
78
|
+
| `get_order_detail.py` | Demonstrates how to retrieve detailed information of a specific order (by ID). |
|
|
79
|
+
| `get_order_history.py` | Demonstrates how to retrieve historical orders. |
|
|
80
|
+
| `get_execution_detail.py` | Demonstrates how to retrieve detailed execution information of an order. |
|
|
81
|
+
| `get_positions.py` | Demonstrates how to retrieve current holding positions. |
|
|
82
|
+
| `get_positions_by_id.py` | Demonstrates how to retrieve detailed information of a specific position (by ID). |
|
|
83
|
+
| `close_position.py` | Demonstrates how to close an existing position (by ID). |
|
|
84
|
+
| `send_email_otp.py` | Demonstrates how to request an OTP sent to your registered email. The OTP is required for generating a trading token. |
|
|
85
|
+
| `create_trading_token.py` | Demonstrates how to generate a Trading Token required for order placement. |
|
|
86
|
+
| `post_order.py` | Demonstrates how to submit a new trading order. |
|
|
87
|
+
| `cancel_order.py` | Demonstrates how to cancel an existing order. |
|
|
88
|
+
| `replace_order.py` | Demonstrates how to modify an existing order. |
|
|
89
|
+
|
|
90
|
+
#### Market Data API
|
|
91
|
+
|
|
92
|
+
| Function | Description |
|
|
93
|
+
|------------------------------|--------------------------------------------------------------------------------------------|
|
|
94
|
+
| `get_security_definition.py` | Demonstrates how to retrieve security definition and instrument details. |
|
|
95
|
+
| `get_instruments.py` | Demonstrates how to retrieve the list of available trading instruments and their metadata. |
|
|
96
|
+
| `get_trades.py` | Demonstrates how to retrieve historical trade data for a specific instrument. |
|
|
97
|
+
| `get_latest_trade.py` | Demonstrates how to retrieve the most recent trade for a specific instrument. |
|
|
98
|
+
| `get_ohlc.py` | Demonstrates how to retrieve OHLC (Open, High, Low, Close) data for a given time range. |
|
|
99
|
+
| `get_close_price.py` | Demonstrates how to retrieve the latest closing price of a specific instrument. |
|
|
100
|
+
| `get_working_dates.py` | Demonstrates how to retrieve trading working dates. |
|
|
101
|
+
|
|
102
|
+
### WebSocket Market Data
|
|
103
|
+
|
|
104
|
+
| Function | Description |
|
|
105
|
+
|-----------------------|--------------------------------------------------------------------------------------|
|
|
106
|
+
| `sec_def.py` | Demonstrates how to receive real-time security definition updates. |
|
|
107
|
+
| `quote.py` | Demonstrates how to receive real-time best bid and ask prices. |
|
|
108
|
+
| `trade.py` | Demonstrates how to receive real-time trade (tick) data. |
|
|
109
|
+
| `trade_extra.py` | Demonstrates how to receive real-time trade data with additional aggregated metrics. |
|
|
110
|
+
| `ohlc.py` | Demonstrates how to receive real-time OHLC data. |
|
|
111
|
+
| `ohlc_closed.py` | Demonstrates how to receive completed OHLC candle data. |
|
|
112
|
+
| `expected_price.py` | Demonstrates how to receive expected price data during ATO and ATC sessions. |
|
|
113
|
+
| `foreign_investor.py` | Demonstrates how to receive foreign investor trading data. |
|
|
114
|
+
| `market_index.py` | Demonstrates how to receive market index data. |
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
|
|
6
|
+
|
|
7
|
+
from dnse import DNSEClient
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main():
|
|
11
|
+
client = DNSEClient(
|
|
12
|
+
api_key="replace-with-api-key",
|
|
13
|
+
api_secret="replace-with-api-secret",
|
|
14
|
+
base_url="https://openapi.dnse.com.vn",
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
status, body = client.get_list_care_by(dry_run=False)
|
|
18
|
+
print(status, body)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
if __name__ == "__main__":
|
|
22
|
+
main()
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
from urllib import parse
|
|
5
|
+
import urllib3
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
from .common import build_signature, get_date_header_name
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DNSEClient:
|
|
12
|
+
def __init__(
|
|
13
|
+
self,
|
|
14
|
+
api_key,
|
|
15
|
+
api_secret,
|
|
16
|
+
base_url="https://openapi.dnse.com.vn",
|
|
17
|
+
algorithm="hmac-sha256",
|
|
18
|
+
hmac_nonce_enabled=True,
|
|
19
|
+
):
|
|
20
|
+
self._api_key = api_key
|
|
21
|
+
self._api_secret = api_secret
|
|
22
|
+
self._base_url = base_url.rstrip("/")
|
|
23
|
+
self._algorithm = algorithm
|
|
24
|
+
self._hmac_nonce_enabled = hmac_nonce_enabled
|
|
25
|
+
|
|
26
|
+
# Tạo PoolManager 1 lần duy nhất, tái sử dụng suốt vòng đời object
|
|
27
|
+
self._http = urllib3.PoolManager(
|
|
28
|
+
num_pools=10, # Số lượng connection pools
|
|
29
|
+
maxsize=10, # Số connections tối đa mỗi pool
|
|
30
|
+
block=False, # Không block khi pool đầy
|
|
31
|
+
timeout=urllib3.Timeout(connect=30.0, read=60.0),
|
|
32
|
+
# cert_reqs = 'CERT_NONE', # Không yêu cầu certificate
|
|
33
|
+
# assert_hostname = False # Không kiểm tra hostname
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
def get_accounts(self, dry_run=False):
|
|
37
|
+
return self._request("GET", "/accounts", dry_run=dry_run)
|
|
38
|
+
|
|
39
|
+
def get_balances(self, account_no, dry_run=False):
|
|
40
|
+
return self._request("GET", f"/accounts/{account_no}/balances", dry_run=dry_run)
|
|
41
|
+
|
|
42
|
+
def get_loan_packages(self, account_no, market_type, symbol=None, dry_run=False):
|
|
43
|
+
query = {"marketType": market_type}
|
|
44
|
+
if symbol:
|
|
45
|
+
query["symbol"] = symbol
|
|
46
|
+
return self._request(
|
|
47
|
+
"GET",
|
|
48
|
+
f"/accounts/{account_no}/loan-packages",
|
|
49
|
+
query=query,
|
|
50
|
+
dry_run=dry_run,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def get_positions(self, account_no, market_type, dry_run=False):
|
|
54
|
+
return self._request(
|
|
55
|
+
"GET",
|
|
56
|
+
f"/accounts/{account_no}/positions",
|
|
57
|
+
query={"marketType": market_type},
|
|
58
|
+
dry_run=dry_run,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def get_position_by_id(self, market_type, position_id, dry_run=False):
|
|
62
|
+
return self._request(
|
|
63
|
+
"GET",
|
|
64
|
+
f"/accounts/positions/{position_id}",
|
|
65
|
+
query={"marketType": market_type},
|
|
66
|
+
dry_run=dry_run,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def get_orders(self, account_no, market_type, order_category=None, dry_run=False):
|
|
70
|
+
query = {"marketType": market_type}
|
|
71
|
+
if order_category:
|
|
72
|
+
query["orderCategory"] = order_category
|
|
73
|
+
return self._request(
|
|
74
|
+
"GET",
|
|
75
|
+
f"/accounts/{account_no}/orders",
|
|
76
|
+
query=query,
|
|
77
|
+
dry_run=dry_run,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
def get_order_detail(self, account_no, order_id, market_type, order_category=None, dry_run=False):
|
|
81
|
+
query = {"marketType": market_type}
|
|
82
|
+
if order_category:
|
|
83
|
+
query["orderCategory"] = order_category
|
|
84
|
+
return self._request(
|
|
85
|
+
"GET",
|
|
86
|
+
f"/accounts/{account_no}/orders/{order_id}",
|
|
87
|
+
query=query,
|
|
88
|
+
dry_run=dry_run,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def get_execution_detail(self, account_no, order_id, market_type, order_category="NORMAL", dry_run=False):
|
|
92
|
+
query = {"marketType": market_type}
|
|
93
|
+
if order_category:
|
|
94
|
+
query["orderCategory"] = order_category
|
|
95
|
+
return self._request(
|
|
96
|
+
"GET",
|
|
97
|
+
f"/accounts/{account_no}/executions/{order_id}",
|
|
98
|
+
query=query,
|
|
99
|
+
dry_run=dry_run,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
def get_order_history(
|
|
103
|
+
self,
|
|
104
|
+
account_no,
|
|
105
|
+
market_type,
|
|
106
|
+
from_date=None,
|
|
107
|
+
to_date=None,
|
|
108
|
+
page_size=None,
|
|
109
|
+
page_index=None,
|
|
110
|
+
dry_run=False,
|
|
111
|
+
):
|
|
112
|
+
query = {"marketType": market_type}
|
|
113
|
+
if from_date:
|
|
114
|
+
query["from"] = from_date
|
|
115
|
+
if to_date:
|
|
116
|
+
query["to"] = to_date
|
|
117
|
+
if page_size is not None:
|
|
118
|
+
query["pageSize"] = page_size
|
|
119
|
+
if page_index is not None:
|
|
120
|
+
query["pageIndex"] = page_index
|
|
121
|
+
return self._request(
|
|
122
|
+
"GET",
|
|
123
|
+
f"/accounts/{account_no}/orders/history",
|
|
124
|
+
query=query,
|
|
125
|
+
dry_run=dry_run,
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
def get_ppse(self, account_no, market_type, symbol, price, loan_package_id, dry_run=False):
|
|
129
|
+
return self._request(
|
|
130
|
+
"GET",
|
|
131
|
+
f"/accounts/{account_no}/ppse",
|
|
132
|
+
query={
|
|
133
|
+
"marketType": market_type,
|
|
134
|
+
"symbol": symbol,
|
|
135
|
+
"price": str(price),
|
|
136
|
+
"loanPackageId": str(loan_package_id),
|
|
137
|
+
},
|
|
138
|
+
dry_run=dry_run,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
def get_security_definition(self, symbol, board_id=None, dry_run=False):
|
|
142
|
+
query = {}
|
|
143
|
+
if board_id:
|
|
144
|
+
query["boardId"] = board_id
|
|
145
|
+
return self._request(
|
|
146
|
+
"GET",
|
|
147
|
+
f"/price/{symbol}/secdef",
|
|
148
|
+
query=query if query else None,
|
|
149
|
+
dry_run=dry_run,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
def get_ohlc(self, bar_type, query=None, dry_run=False):
|
|
153
|
+
request_query = dict(query or {})
|
|
154
|
+
request_query["type"] = bar_type
|
|
155
|
+
return self._request(
|
|
156
|
+
"GET",
|
|
157
|
+
"/price/ohlc",
|
|
158
|
+
query=request_query,
|
|
159
|
+
dry_run=dry_run,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
def get_trades(self, symbol, board_id=None, from_date=None, to_date=None, limit=None, order = None, next_page_token=None, dry_run=False):
|
|
163
|
+
query = {}
|
|
164
|
+
if board_id is not None:
|
|
165
|
+
query["boardId"] = board_id
|
|
166
|
+
if from_date is not None:
|
|
167
|
+
query["from"] = from_date
|
|
168
|
+
if to_date is not None:
|
|
169
|
+
query["to"] = to_date
|
|
170
|
+
if limit is not None:
|
|
171
|
+
query["limit"] = limit
|
|
172
|
+
if order is not None:
|
|
173
|
+
query["order"] = order
|
|
174
|
+
if next_page_token is not None:
|
|
175
|
+
query["nextPageToken"] = next_page_token
|
|
176
|
+
return self._request(
|
|
177
|
+
"GET",
|
|
178
|
+
f"/price/{symbol}/trades",
|
|
179
|
+
query=query if query else None,
|
|
180
|
+
dry_run=dry_run,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
def get_instruments(self, symbol=None, market_id=None, security_group_id=None, index_name=None, limit=None, page=None, dry_run=False):
|
|
184
|
+
query = {}
|
|
185
|
+
if symbol is not None:
|
|
186
|
+
query["symbol"] = symbol
|
|
187
|
+
if market_id is not None:
|
|
188
|
+
query["marketId"] = market_id
|
|
189
|
+
if security_group_id is not None:
|
|
190
|
+
query["securityGroupId"] = security_group_id
|
|
191
|
+
if index_name is not None:
|
|
192
|
+
query["indexName"] = index_name
|
|
193
|
+
if limit is not None:
|
|
194
|
+
query["limit"] = limit
|
|
195
|
+
if page is not None:
|
|
196
|
+
query["page"] = page
|
|
197
|
+
return self._request(
|
|
198
|
+
"GET",
|
|
199
|
+
f"/instruments",
|
|
200
|
+
query=query if query else None,
|
|
201
|
+
dry_run=dry_run,
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
def get_latest_trade(self, symbol, board_id=None, dry_run=False):
|
|
205
|
+
query = {}
|
|
206
|
+
if board_id is not None:
|
|
207
|
+
query["boardId"] = board_id
|
|
208
|
+
return self._request(
|
|
209
|
+
"GET",
|
|
210
|
+
f"/price/{symbol}/trades/latest",
|
|
211
|
+
query=query if query else None,
|
|
212
|
+
dry_run=dry_run,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
def get_close_price(self, symbol, board_id=None, dry_run=False):
|
|
216
|
+
query = {}
|
|
217
|
+
if board_id is not None:
|
|
218
|
+
query["boardId"] = board_id
|
|
219
|
+
return self._request(
|
|
220
|
+
"GET",
|
|
221
|
+
f"/price/{symbol}/close",
|
|
222
|
+
query=query if query else None,
|
|
223
|
+
dry_run=dry_run,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
def get_working_dates(self, dry_run=False):
|
|
227
|
+
return self._request(
|
|
228
|
+
"GET",
|
|
229
|
+
f"/market/working-dates",
|
|
230
|
+
dry_run=dry_run,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
def get_list_care_by(self, dry_run=False):
|
|
234
|
+
return self._request(
|
|
235
|
+
"GET",
|
|
236
|
+
f"/brokers/accounts/care-by",
|
|
237
|
+
dry_run=dry_run,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
def post_order(self, market_type, payload, trading_token, order_category="NORMAL", dry_run=False):
|
|
241
|
+
headers = {"trading-token": trading_token}
|
|
242
|
+
query = {"marketType": market_type}
|
|
243
|
+
if order_category:
|
|
244
|
+
query["orderCategory"] = order_category
|
|
245
|
+
return self._request(
|
|
246
|
+
"POST",
|
|
247
|
+
"/accounts/orders",
|
|
248
|
+
query=query,
|
|
249
|
+
body=payload,
|
|
250
|
+
headers=headers,
|
|
251
|
+
dry_run=dry_run,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
def put_order(
|
|
255
|
+
self,
|
|
256
|
+
account_no,
|
|
257
|
+
order_id,
|
|
258
|
+
market_type,
|
|
259
|
+
payload,
|
|
260
|
+
trading_token,
|
|
261
|
+
order_category=None,
|
|
262
|
+
dry_run=False,
|
|
263
|
+
):
|
|
264
|
+
headers = {"trading-token": trading_token}
|
|
265
|
+
query = {"marketType": market_type}
|
|
266
|
+
if order_category:
|
|
267
|
+
query["orderCategory"] = order_category
|
|
268
|
+
return self._request(
|
|
269
|
+
"PUT",
|
|
270
|
+
f"/accounts/{account_no}/orders/{order_id}",
|
|
271
|
+
query=query,
|
|
272
|
+
body=payload,
|
|
273
|
+
headers=headers,
|
|
274
|
+
dry_run=dry_run,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
def cancel_order(
|
|
278
|
+
self,
|
|
279
|
+
account_no,
|
|
280
|
+
order_id,
|
|
281
|
+
market_type,
|
|
282
|
+
trading_token,
|
|
283
|
+
order_category=None,
|
|
284
|
+
dry_run=False,
|
|
285
|
+
):
|
|
286
|
+
headers = {"trading-token": trading_token}
|
|
287
|
+
query = {"marketType": market_type}
|
|
288
|
+
if order_category:
|
|
289
|
+
query["orderCategory"] = order_category
|
|
290
|
+
return self._request(
|
|
291
|
+
"DELETE",
|
|
292
|
+
f"/accounts/{account_no}/orders/{order_id}",
|
|
293
|
+
query=query,
|
|
294
|
+
headers=headers,
|
|
295
|
+
dry_run=dry_run,
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
def create_trading_token(self, otp_type, passcode, dry_run=False):
|
|
299
|
+
return self._request(
|
|
300
|
+
"POST",
|
|
301
|
+
"/registration/trading-token",
|
|
302
|
+
body={"otpType": otp_type, "passcode": passcode},
|
|
303
|
+
dry_run=dry_run,
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
def send_email_otp(self, dry_run=False):
|
|
307
|
+
return self._request(
|
|
308
|
+
"POST",
|
|
309
|
+
"/registration/send-email-otp",
|
|
310
|
+
dry_run=dry_run,
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
def close_position(self, position_id, market_type, trading_token, dry_run=False):
|
|
314
|
+
headers = {"trading-token": trading_token}
|
|
315
|
+
query = {"marketType": market_type}
|
|
316
|
+
return self._request(
|
|
317
|
+
"POST",
|
|
318
|
+
f"/accounts/positions/{position_id}/close",
|
|
319
|
+
query=query,
|
|
320
|
+
headers=headers,
|
|
321
|
+
dry_run=dry_run,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
def _request(self, method, path, query=None, body=None, headers=None, dry_run=False):
|
|
325
|
+
debug = os.getenv("DEBUG", "").lower() == "true"
|
|
326
|
+
url = self._build_url(path, query)
|
|
327
|
+
date_value, signature_header_value = self._signature_headers(method, path)
|
|
328
|
+
date_header_name = get_date_header_name()
|
|
329
|
+
|
|
330
|
+
# Build headers dict
|
|
331
|
+
req_headers = {
|
|
332
|
+
date_header_name: date_value,
|
|
333
|
+
"X-Signature": signature_header_value,
|
|
334
|
+
"x-api-key": self._api_key,
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if body is not None:
|
|
338
|
+
req_headers["Content-Type"] = "application/json"
|
|
339
|
+
|
|
340
|
+
if headers:
|
|
341
|
+
req_headers.update(headers)
|
|
342
|
+
|
|
343
|
+
# Prepare body data
|
|
344
|
+
data = None
|
|
345
|
+
if body is not None:
|
|
346
|
+
data = json.dumps(body)
|
|
347
|
+
|
|
348
|
+
if debug or dry_run:
|
|
349
|
+
prefix = "DRY RUN" if dry_run else "DEBUG"
|
|
350
|
+
print(f"{prefix} url:", url)
|
|
351
|
+
print(f"{prefix} method:", method)
|
|
352
|
+
print(f"{prefix} query_params:", query or {})
|
|
353
|
+
print(f"{prefix} headers:", req_headers)
|
|
354
|
+
print(f"{prefix} body:", body)
|
|
355
|
+
|
|
356
|
+
if dry_run:
|
|
357
|
+
return None, None
|
|
358
|
+
|
|
359
|
+
try:
|
|
360
|
+
resp = self._http.request(
|
|
361
|
+
method,
|
|
362
|
+
url,
|
|
363
|
+
body=data,
|
|
364
|
+
headers=req_headers,
|
|
365
|
+
)
|
|
366
|
+
body_text = resp.data.decode("utf-8")
|
|
367
|
+
return resp.status, body_text
|
|
368
|
+
except urllib3.exceptions.HTTPError as err:
|
|
369
|
+
if hasattr(err, 'response') and err.response:
|
|
370
|
+
body_text = err.response.data.decode("utf-8") if err.response.data else ""
|
|
371
|
+
return err.response.status, body_text
|
|
372
|
+
raise
|
|
373
|
+
|
|
374
|
+
def _build_url(self, path, query):
|
|
375
|
+
url = f"{self._base_url}{path}"
|
|
376
|
+
if query:
|
|
377
|
+
url = f"{url}?{parse.urlencode(query)}"
|
|
378
|
+
return url
|
|
379
|
+
|
|
380
|
+
def _signature_headers(self, method, path):
|
|
381
|
+
date_value = self._date_header()
|
|
382
|
+
nonce = None
|
|
383
|
+
if self._hmac_nonce_enabled:
|
|
384
|
+
import uuid
|
|
385
|
+
|
|
386
|
+
nonce = uuid.uuid4().hex
|
|
387
|
+
|
|
388
|
+
headers_list, signature = build_signature(
|
|
389
|
+
self._api_secret,
|
|
390
|
+
method,
|
|
391
|
+
path,
|
|
392
|
+
date_value,
|
|
393
|
+
self._algorithm,
|
|
394
|
+
nonce=nonce,
|
|
395
|
+
header_name=get_date_header_name(),
|
|
396
|
+
)
|
|
397
|
+
signature_header_value = (
|
|
398
|
+
f'Signature keyId="{self._api_key}",algorithm="{self._algorithm}",'
|
|
399
|
+
f'headers="{headers_list}",signature="{signature}"'
|
|
400
|
+
)
|
|
401
|
+
if nonce:
|
|
402
|
+
signature_header_value += f',nonce="{nonce}"'
|
|
403
|
+
return date_value, signature_header_value
|
|
404
|
+
|
|
405
|
+
def _date_header(self):
|
|
406
|
+
from datetime import datetime, timezone
|
|
407
|
+
|
|
408
|
+
return datetime.now(timezone.utc).strftime("%a, %d %b %Y %H:%M:%S %z")
|