infoway-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.
- infoway_sdk-0.1.0/.gitignore +9 -0
- infoway_sdk-0.1.0/PKG-INFO +150 -0
- infoway_sdk-0.1.0/README.md +125 -0
- infoway_sdk-0.1.0/README_CN.md +122 -0
- infoway_sdk-0.1.0/pyproject.toml +46 -0
- infoway_sdk-0.1.0/src/infoway/__init__.py +17 -0
- infoway_sdk-0.1.0/src/infoway/_http.py +84 -0
- infoway_sdk-0.1.0/src/infoway/_types.py +39 -0
- infoway_sdk-0.1.0/src/infoway/_version.py +1 -0
- infoway_sdk-0.1.0/src/infoway/client.py +57 -0
- infoway_sdk-0.1.0/src/infoway/exceptions.py +22 -0
- infoway_sdk-0.1.0/src/infoway/rest/__init__.py +0 -0
- infoway_sdk-0.1.0/src/infoway/rest/_market_data.py +44 -0
- infoway_sdk-0.1.0/src/infoway/rest/basic.py +29 -0
- infoway_sdk-0.1.0/src/infoway/rest/common.py +15 -0
- infoway_sdk-0.1.0/src/infoway/rest/crypto.py +15 -0
- infoway_sdk-0.1.0/src/infoway/rest/india.py +15 -0
- infoway_sdk-0.1.0/src/infoway/rest/japan.py +15 -0
- infoway_sdk-0.1.0/src/infoway/rest/market.py +27 -0
- infoway_sdk-0.1.0/src/infoway/rest/plate.py +27 -0
- infoway_sdk-0.1.0/src/infoway/rest/stock.py +15 -0
- infoway_sdk-0.1.0/src/infoway/rest/stock_info.py +33 -0
- infoway_sdk-0.1.0/src/infoway/ws/__init__.py +3 -0
- infoway_sdk-0.1.0/src/infoway/ws/client.py +195 -0
- infoway_sdk-0.1.0/tests/__init__.py +0 -0
- infoway_sdk-0.1.0/tests/conftest.py +7 -0
- infoway_sdk-0.1.0/tests/test_basic.py +38 -0
- infoway_sdk-0.1.0/tests/test_client.py +29 -0
- infoway_sdk-0.1.0/tests/test_crypto.py +33 -0
- infoway_sdk-0.1.0/tests/test_exceptions.py +27 -0
- infoway_sdk-0.1.0/tests/test_http.py +78 -0
- infoway_sdk-0.1.0/tests/test_market.py +37 -0
- infoway_sdk-0.1.0/tests/test_plate.py +37 -0
- infoway_sdk-0.1.0/tests/test_stock.py +74 -0
- infoway_sdk-0.1.0/tests/test_stock_info.py +49 -0
- infoway_sdk-0.1.0/tests/test_ws.py +54 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: infoway-sdk
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python SDK for Infoway real-time financial data API
|
|
5
|
+
Project-URL: Homepage, https://infoway.io
|
|
6
|
+
Project-URL: Documentation, https://docs.infoway.io
|
|
7
|
+
Project-URL: Repository, https://github.com/infoway-io/infoway-sdk-python
|
|
8
|
+
Author-email: Infoway <dev@infoway.io>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Keywords: crypto,finance,forex,market-data,real-time,stock,websocket
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Requires-Dist: httpx>=0.27
|
|
19
|
+
Requires-Dist: websockets>=12.0
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
22
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
23
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# Infoway SDK
|
|
27
|
+
|
|
28
|
+
[](https://pypi.org/project/infoway-sdk/)
|
|
29
|
+
[](https://pypi.org/project/infoway-sdk/)
|
|
30
|
+
[](https://opensource.org/licenses/MIT)
|
|
31
|
+
|
|
32
|
+
**English** | [中文](README_CN.md)
|
|
33
|
+
|
|
34
|
+
Official Python SDK for [Infoway](https://infoway.io) real-time financial data API. Full documentation at [docs.infoway.io](https://docs.infoway.io).
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install infoway-sdk
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Quick Start
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from infoway import InfowayClient, KlineType
|
|
46
|
+
|
|
47
|
+
client = InfowayClient(api_key="YOUR_API_KEY")
|
|
48
|
+
|
|
49
|
+
# Real-time trades
|
|
50
|
+
trades = client.stock.get_trade("AAPL.US")
|
|
51
|
+
|
|
52
|
+
# Daily K-lines for crypto
|
|
53
|
+
klines = client.crypto.get_kline("BTCUSDT", kline_type=KlineType.DAY, count=30)
|
|
54
|
+
|
|
55
|
+
# Market temperature
|
|
56
|
+
temp = client.market.get_temperature(market="HK,US")
|
|
57
|
+
|
|
58
|
+
# Sector/plate rankings
|
|
59
|
+
plates = client.plate.get_industry("HK", limit=10)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## WebSocket Streaming
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
import asyncio
|
|
66
|
+
from infoway.ws import InfowayWebSocket
|
|
67
|
+
|
|
68
|
+
async def main():
|
|
69
|
+
ws = InfowayWebSocket(api_key="YOUR_API_KEY", business="stock")
|
|
70
|
+
|
|
71
|
+
async def on_trade(msg):
|
|
72
|
+
print(f"Trade: {msg}")
|
|
73
|
+
|
|
74
|
+
ws.on_trade = on_trade
|
|
75
|
+
await ws.subscribe_trade("AAPL.US,TSLA.US")
|
|
76
|
+
await ws.connect()
|
|
77
|
+
|
|
78
|
+
asyncio.run(main())
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### WebSocket Features
|
|
82
|
+
|
|
83
|
+
- **Auto-reconnect** with exponential backoff (1s to 30s cap)
|
|
84
|
+
- **Heartbeat keepalive** at 30-second intervals
|
|
85
|
+
- **Auto-resubscribe** on reconnect -- no manual re-subscription needed
|
|
86
|
+
- Event callbacks: `on_trade`, `on_depth`, `on_kline`, `on_error`, `on_reconnect`, `on_disconnect`
|
|
87
|
+
|
|
88
|
+
## REST API Modules
|
|
89
|
+
|
|
90
|
+
| Module | Accessor | Description |
|
|
91
|
+
|--------|----------|-------------|
|
|
92
|
+
| Stock | `client.stock` | HK, US, CN equities -- trade, depth, K-line |
|
|
93
|
+
| Crypto | `client.crypto` | Cryptocurrency pairs -- trade, depth, K-line |
|
|
94
|
+
| Japan | `client.japan` | Japan market data -- trade, depth, K-line |
|
|
95
|
+
| India | `client.india` | India market data -- trade, depth, K-line |
|
|
96
|
+
| Common | `client.common` | Cross-market data -- trade, depth, K-line |
|
|
97
|
+
| Basic | `client.basic` | Symbol lists, trading days, trading hours, adjustment factors |
|
|
98
|
+
| Market | `client.market` | Market temperature, breadth, indexes, leaders, rank config |
|
|
99
|
+
| Plate | `client.plate` | Sector/industry/concept plates, members, charts |
|
|
100
|
+
| Stock Info | `client.stock_info` | Fundamentals -- valuation, ratings, company, panorama, events |
|
|
101
|
+
|
|
102
|
+
## Configuration
|
|
103
|
+
|
|
104
|
+
You can pass `api_key` directly or set it via environment variable:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
export INFOWAY_API_KEY="YOUR_API_KEY"
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
# Reads INFOWAY_API_KEY from environment automatically
|
|
112
|
+
client = InfowayClient()
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Client Options
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
client = InfowayClient(
|
|
119
|
+
api_key="YOUR_API_KEY",
|
|
120
|
+
base_url="https://data.infoway.io", # default
|
|
121
|
+
timeout=15.0, # request timeout in seconds
|
|
122
|
+
max_retries=3, # retry count for failed requests
|
|
123
|
+
)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Error Handling
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from infoway import InfowayClient, InfowayAPIError, InfowayAuthError, InfowayTimeoutError
|
|
130
|
+
|
|
131
|
+
client = InfowayClient(api_key="YOUR_API_KEY")
|
|
132
|
+
|
|
133
|
+
try:
|
|
134
|
+
trades = client.stock.get_trade("AAPL.US")
|
|
135
|
+
except InfowayAuthError:
|
|
136
|
+
print("Invalid API key")
|
|
137
|
+
except InfowayTimeoutError:
|
|
138
|
+
print("Request timed out")
|
|
139
|
+
except InfowayAPIError as e:
|
|
140
|
+
print(f"API error [{e.ret}]: {e.msg}")
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## License
|
|
144
|
+
|
|
145
|
+
MIT
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
Get your free API key at [infoway.io](https://infoway.io) -- 7-day free trial, no credit card required.
|
|
150
|
+
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Infoway SDK
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/infoway-sdk/)
|
|
4
|
+
[](https://pypi.org/project/infoway-sdk/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
**English** | [中文](README_CN.md)
|
|
8
|
+
|
|
9
|
+
Official Python SDK for [Infoway](https://infoway.io) real-time financial data API. Full documentation at [docs.infoway.io](https://docs.infoway.io).
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install infoway-sdk
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Quick Start
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from infoway import InfowayClient, KlineType
|
|
21
|
+
|
|
22
|
+
client = InfowayClient(api_key="YOUR_API_KEY")
|
|
23
|
+
|
|
24
|
+
# Real-time trades
|
|
25
|
+
trades = client.stock.get_trade("AAPL.US")
|
|
26
|
+
|
|
27
|
+
# Daily K-lines for crypto
|
|
28
|
+
klines = client.crypto.get_kline("BTCUSDT", kline_type=KlineType.DAY, count=30)
|
|
29
|
+
|
|
30
|
+
# Market temperature
|
|
31
|
+
temp = client.market.get_temperature(market="HK,US")
|
|
32
|
+
|
|
33
|
+
# Sector/plate rankings
|
|
34
|
+
plates = client.plate.get_industry("HK", limit=10)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## WebSocket Streaming
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import asyncio
|
|
41
|
+
from infoway.ws import InfowayWebSocket
|
|
42
|
+
|
|
43
|
+
async def main():
|
|
44
|
+
ws = InfowayWebSocket(api_key="YOUR_API_KEY", business="stock")
|
|
45
|
+
|
|
46
|
+
async def on_trade(msg):
|
|
47
|
+
print(f"Trade: {msg}")
|
|
48
|
+
|
|
49
|
+
ws.on_trade = on_trade
|
|
50
|
+
await ws.subscribe_trade("AAPL.US,TSLA.US")
|
|
51
|
+
await ws.connect()
|
|
52
|
+
|
|
53
|
+
asyncio.run(main())
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### WebSocket Features
|
|
57
|
+
|
|
58
|
+
- **Auto-reconnect** with exponential backoff (1s to 30s cap)
|
|
59
|
+
- **Heartbeat keepalive** at 30-second intervals
|
|
60
|
+
- **Auto-resubscribe** on reconnect -- no manual re-subscription needed
|
|
61
|
+
- Event callbacks: `on_trade`, `on_depth`, `on_kline`, `on_error`, `on_reconnect`, `on_disconnect`
|
|
62
|
+
|
|
63
|
+
## REST API Modules
|
|
64
|
+
|
|
65
|
+
| Module | Accessor | Description |
|
|
66
|
+
|--------|----------|-------------|
|
|
67
|
+
| Stock | `client.stock` | HK, US, CN equities -- trade, depth, K-line |
|
|
68
|
+
| Crypto | `client.crypto` | Cryptocurrency pairs -- trade, depth, K-line |
|
|
69
|
+
| Japan | `client.japan` | Japan market data -- trade, depth, K-line |
|
|
70
|
+
| India | `client.india` | India market data -- trade, depth, K-line |
|
|
71
|
+
| Common | `client.common` | Cross-market data -- trade, depth, K-line |
|
|
72
|
+
| Basic | `client.basic` | Symbol lists, trading days, trading hours, adjustment factors |
|
|
73
|
+
| Market | `client.market` | Market temperature, breadth, indexes, leaders, rank config |
|
|
74
|
+
| Plate | `client.plate` | Sector/industry/concept plates, members, charts |
|
|
75
|
+
| Stock Info | `client.stock_info` | Fundamentals -- valuation, ratings, company, panorama, events |
|
|
76
|
+
|
|
77
|
+
## Configuration
|
|
78
|
+
|
|
79
|
+
You can pass `api_key` directly or set it via environment variable:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
export INFOWAY_API_KEY="YOUR_API_KEY"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
# Reads INFOWAY_API_KEY from environment automatically
|
|
87
|
+
client = InfowayClient()
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Client Options
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
client = InfowayClient(
|
|
94
|
+
api_key="YOUR_API_KEY",
|
|
95
|
+
base_url="https://data.infoway.io", # default
|
|
96
|
+
timeout=15.0, # request timeout in seconds
|
|
97
|
+
max_retries=3, # retry count for failed requests
|
|
98
|
+
)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Error Handling
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from infoway import InfowayClient, InfowayAPIError, InfowayAuthError, InfowayTimeoutError
|
|
105
|
+
|
|
106
|
+
client = InfowayClient(api_key="YOUR_API_KEY")
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
trades = client.stock.get_trade("AAPL.US")
|
|
110
|
+
except InfowayAuthError:
|
|
111
|
+
print("Invalid API key")
|
|
112
|
+
except InfowayTimeoutError:
|
|
113
|
+
print("Request timed out")
|
|
114
|
+
except InfowayAPIError as e:
|
|
115
|
+
print(f"API error [{e.ret}]: {e.msg}")
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
MIT
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
Get your free API key at [infoway.io](https://infoway.io) -- 7-day free trial, no credit card required.
|
|
125
|
+
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Infoway SDK (中文)
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/infoway-sdk/)
|
|
4
|
+
[](https://pypi.org/project/infoway-sdk/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
[English](README.md) | **中文**
|
|
8
|
+
|
|
9
|
+
[Infoway](https://infoway.io) 实时金融数据 API 的官方 Python SDK。完整文档请访问 [docs.infoway.io](https://docs.infoway.io)。
|
|
10
|
+
|
|
11
|
+
## 安装
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install infoway-sdk
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 快速开始
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
from infoway import InfowayClient, KlineType
|
|
21
|
+
|
|
22
|
+
client = InfowayClient(api_key="YOUR_API_KEY")
|
|
23
|
+
|
|
24
|
+
# 实时成交数据
|
|
25
|
+
trades = client.stock.get_trade("AAPL.US")
|
|
26
|
+
|
|
27
|
+
# 加密货币日K线
|
|
28
|
+
klines = client.crypto.get_kline("BTCUSDT", kline_type=KlineType.DAY, count=30)
|
|
29
|
+
|
|
30
|
+
# 市场温度
|
|
31
|
+
temp = client.market.get_temperature(market="HK,US")
|
|
32
|
+
|
|
33
|
+
# 板块排行
|
|
34
|
+
plates = client.plate.get_industry("HK", limit=10)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## WebSocket 实时推送
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
import asyncio
|
|
41
|
+
from infoway.ws import InfowayWebSocket
|
|
42
|
+
|
|
43
|
+
async def main():
|
|
44
|
+
ws = InfowayWebSocket(api_key="YOUR_API_KEY", business="stock")
|
|
45
|
+
|
|
46
|
+
async def on_trade(msg):
|
|
47
|
+
print(f"成交: {msg}")
|
|
48
|
+
|
|
49
|
+
ws.on_trade = on_trade
|
|
50
|
+
await ws.subscribe_trade("AAPL.US,TSLA.US")
|
|
51
|
+
await ws.connect()
|
|
52
|
+
|
|
53
|
+
asyncio.run(main())
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 特性
|
|
57
|
+
|
|
58
|
+
- **自动重连** -- 指数退避策略(1秒至30秒上限)
|
|
59
|
+
- **心跳保活** -- 每30秒自动发送心跳
|
|
60
|
+
- **自动重新订阅** -- 重连后自动恢复所有订阅,无需手动处理
|
|
61
|
+
- 事件回调:`on_trade`、`on_depth`、`on_kline`、`on_error`、`on_reconnect`、`on_disconnect`
|
|
62
|
+
|
|
63
|
+
## REST API 模块
|
|
64
|
+
|
|
65
|
+
| 模块 | 访问方式 | 说明 |
|
|
66
|
+
|------|----------|------|
|
|
67
|
+
| Stock | `client.stock` | 港股、美股、A股 -- 成交、深度、K线 |
|
|
68
|
+
| Crypto | `client.crypto` | 加密货币 -- 成交、深度、K线 |
|
|
69
|
+
| Japan | `client.japan` | 日本市场 -- 成交、深度、K线 |
|
|
70
|
+
| India | `client.india` | 印度市场 -- 成交、深度、K线 |
|
|
71
|
+
| Common | `client.common` | 跨市场数据 -- 成交、深度、K线 |
|
|
72
|
+
| Basic | `client.basic` | 标的列表、交易日、交易时间、复权因子 |
|
|
73
|
+
| Market | `client.market` | 市场温度、市场宽度、指数、龙头股、排行配置 |
|
|
74
|
+
| Plate | `client.plate` | 行业/概念板块、成分股、板块图表 |
|
|
75
|
+
| Stock Info | `client.stock_info` | 基本面 -- 估值、评级、公司概况、全景、事件 |
|
|
76
|
+
|
|
77
|
+
## 环境变量
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
export INFOWAY_API_KEY="YOUR_API_KEY"
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
# 自动从环境变量读取 INFOWAY_API_KEY
|
|
85
|
+
client = InfowayClient()
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 客户端配置
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
client = InfowayClient(
|
|
92
|
+
api_key="YOUR_API_KEY",
|
|
93
|
+
base_url="https://data.infoway.io", # 默认值
|
|
94
|
+
timeout=15.0, # 请求超时(秒)
|
|
95
|
+
max_retries=3, # 失败重试次数
|
|
96
|
+
)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## 错误处理
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from infoway import InfowayClient, InfowayAPIError, InfowayAuthError, InfowayTimeoutError
|
|
103
|
+
|
|
104
|
+
client = InfowayClient(api_key="YOUR_API_KEY")
|
|
105
|
+
|
|
106
|
+
try:
|
|
107
|
+
trades = client.stock.get_trade("AAPL.US")
|
|
108
|
+
except InfowayAuthError:
|
|
109
|
+
print("API Key 无效")
|
|
110
|
+
except InfowayTimeoutError:
|
|
111
|
+
print("请求超时")
|
|
112
|
+
except InfowayAPIError as e:
|
|
113
|
+
print(f"API 错误 [{e.ret}]: {e.msg}")
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## 许可证
|
|
117
|
+
|
|
118
|
+
MIT
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
在 [infoway.io](https://infoway.io) 获取免费 API Key -- 7天免费试用,无需信用卡。
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "infoway-sdk"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Official Python SDK for Infoway real-time financial data API"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
authors = [{ name = "Infoway", email = "dev@infoway.io" }]
|
|
13
|
+
keywords = ["finance", "stock", "crypto", "forex", "market-data", "websocket", "real-time"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Topic :: Office/Business :: Financial :: Investment",
|
|
21
|
+
]
|
|
22
|
+
dependencies = [
|
|
23
|
+
"httpx>=0.27",
|
|
24
|
+
"websockets>=12.0",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
dev = [
|
|
29
|
+
"pytest>=8.0",
|
|
30
|
+
"pytest-asyncio>=0.23",
|
|
31
|
+
"respx>=0.21",
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
[project.urls]
|
|
35
|
+
Homepage = "https://infoway.io"
|
|
36
|
+
Documentation = "https://docs.infoway.io"
|
|
37
|
+
Repository = "https://github.com/infoway-io/infoway-sdk-python"
|
|
38
|
+
|
|
39
|
+
[tool.hatch.version]
|
|
40
|
+
path = "src/infoway/_version.py"
|
|
41
|
+
|
|
42
|
+
[tool.hatch.build.targets.wheel]
|
|
43
|
+
packages = ["src/infoway"]
|
|
44
|
+
|
|
45
|
+
[tool.pytest.ini_options]
|
|
46
|
+
asyncio_mode = "auto"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Infoway SDK — Official Python client for Infoway real-time financial data API."""
|
|
2
|
+
|
|
3
|
+
from infoway._version import __version__
|
|
4
|
+
from infoway.client import InfowayClient
|
|
5
|
+
from infoway.exceptions import InfowayAPIError, InfowayAuthError, InfowayTimeoutError
|
|
6
|
+
from infoway._types import KlineType, Business, WsCode
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"__version__",
|
|
10
|
+
"InfowayClient",
|
|
11
|
+
"InfowayAPIError",
|
|
12
|
+
"InfowayAuthError",
|
|
13
|
+
"InfowayTimeoutError",
|
|
14
|
+
"KlineType",
|
|
15
|
+
"Business",
|
|
16
|
+
"WsCode",
|
|
17
|
+
]
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Low-level HTTP client with retry and error handling."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
import time
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import httpx
|
|
11
|
+
|
|
12
|
+
from infoway.exceptions import InfowayAPIError, InfowayAuthError, InfowayTimeoutError
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger("infoway")
|
|
15
|
+
|
|
16
|
+
_DEFAULT_BASE_URL = "https://data.infoway.io"
|
|
17
|
+
_DEFAULT_TIMEOUT = 15.0
|
|
18
|
+
_DEFAULT_RETRIES = 3
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class HttpClient:
|
|
22
|
+
"""HTTP client wrapping httpx with auth, retry, and Infoway error handling."""
|
|
23
|
+
|
|
24
|
+
def __init__(
|
|
25
|
+
self,
|
|
26
|
+
api_key: str | None = None,
|
|
27
|
+
base_url: str = _DEFAULT_BASE_URL,
|
|
28
|
+
timeout: float = _DEFAULT_TIMEOUT,
|
|
29
|
+
max_retries: int = _DEFAULT_RETRIES,
|
|
30
|
+
):
|
|
31
|
+
self._api_key = api_key or os.getenv("INFOWAY_API_KEY", "")
|
|
32
|
+
self._base_url = base_url.rstrip("/")
|
|
33
|
+
self._timeout = timeout
|
|
34
|
+
self._max_retries = max_retries
|
|
35
|
+
self._client = httpx.Client(
|
|
36
|
+
base_url=self._base_url,
|
|
37
|
+
headers={"apiKey": self._api_key},
|
|
38
|
+
timeout=self._timeout,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
def get(self, path: str, params: dict[str, Any] | None = None) -> Any:
|
|
42
|
+
return self._request("GET", path, params=params)
|
|
43
|
+
|
|
44
|
+
def post(self, path: str, json: dict[str, Any] | None = None) -> Any:
|
|
45
|
+
return self._request("POST", path, json=json)
|
|
46
|
+
|
|
47
|
+
def _request(self, method: str, path: str, **kwargs: Any) -> Any:
|
|
48
|
+
last_exc: Exception | None = None
|
|
49
|
+
for attempt in range(self._max_retries):
|
|
50
|
+
try:
|
|
51
|
+
resp = self._client.request(method, path, **kwargs)
|
|
52
|
+
return self._handle_response(resp)
|
|
53
|
+
except (InfowayAPIError, InfowayAuthError):
|
|
54
|
+
raise
|
|
55
|
+
except httpx.TimeoutException as e:
|
|
56
|
+
last_exc = InfowayTimeoutError(str(e))
|
|
57
|
+
except httpx.HTTPError as e:
|
|
58
|
+
last_exc = e
|
|
59
|
+
logger.debug("Request failed (attempt %d/%d): %s", attempt + 1, self._max_retries, e)
|
|
60
|
+
if attempt < self._max_retries - 1:
|
|
61
|
+
time.sleep(min(2 ** attempt, 8))
|
|
62
|
+
raise last_exc
|
|
63
|
+
|
|
64
|
+
def _handle_response(self, resp: httpx.Response) -> Any:
|
|
65
|
+
if resp.status_code == 401:
|
|
66
|
+
raise InfowayAuthError()
|
|
67
|
+
data = resp.json()
|
|
68
|
+
ret = data.get("ret") or data.get("code", 200)
|
|
69
|
+
msg = data.get("msg", "")
|
|
70
|
+
trace_id = data.get("traceId")
|
|
71
|
+
if ret == 401:
|
|
72
|
+
raise InfowayAuthError(msg)
|
|
73
|
+
if ret != 200:
|
|
74
|
+
raise InfowayAPIError(ret=ret, msg=msg, trace_id=trace_id)
|
|
75
|
+
return data.get("data")
|
|
76
|
+
|
|
77
|
+
def close(self):
|
|
78
|
+
self._client.close()
|
|
79
|
+
|
|
80
|
+
def __enter__(self):
|
|
81
|
+
return self
|
|
82
|
+
|
|
83
|
+
def __exit__(self, *args: Any):
|
|
84
|
+
self.close()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Shared type definitions and enums."""
|
|
2
|
+
|
|
3
|
+
from enum import IntEnum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class KlineType(IntEnum):
|
|
7
|
+
MIN_1 = 1
|
|
8
|
+
MIN_5 = 2
|
|
9
|
+
MIN_15 = 3
|
|
10
|
+
MIN_30 = 4
|
|
11
|
+
HOUR_1 = 5
|
|
12
|
+
HOUR_2 = 6
|
|
13
|
+
HOUR_4 = 7
|
|
14
|
+
DAY = 8
|
|
15
|
+
WEEK = 9
|
|
16
|
+
MONTH = 10
|
|
17
|
+
QUARTER = 11
|
|
18
|
+
YEAR = 12
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Business(str):
|
|
22
|
+
STOCK = "stock"
|
|
23
|
+
JAPAN = "japan"
|
|
24
|
+
INDIA = "india"
|
|
25
|
+
CRYPTO = "crypto"
|
|
26
|
+
COMMON = "common"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class WsCode(IntEnum):
|
|
30
|
+
SUB_TRADE = 10000
|
|
31
|
+
PUSH_TRADE = 10001
|
|
32
|
+
UNSUB_TRADE = 10002
|
|
33
|
+
SUB_DEPTH = 10003
|
|
34
|
+
PUSH_DEPTH = 10004
|
|
35
|
+
UNSUB_DEPTH = 10005
|
|
36
|
+
SUB_KLINE = 10006
|
|
37
|
+
PUSH_KLINE = 10007
|
|
38
|
+
UNSUB_KLINE = 10008
|
|
39
|
+
HEARTBEAT = 10010
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Main entry point for the Infoway SDK."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from infoway._http import HttpClient
|
|
6
|
+
from infoway.rest.stock import StockClient
|
|
7
|
+
from infoway.rest.crypto import CryptoClient
|
|
8
|
+
from infoway.rest.japan import JapanClient
|
|
9
|
+
from infoway.rest.india import IndiaClient
|
|
10
|
+
from infoway.rest.common import CommonClient
|
|
11
|
+
from infoway.rest.basic import BasicClient
|
|
12
|
+
from infoway.rest.market import MarketClient
|
|
13
|
+
from infoway.rest.plate import PlateClient
|
|
14
|
+
from infoway.rest.stock_info import StockInfoClient
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class InfowayClient:
|
|
18
|
+
"""Infoway API client.
|
|
19
|
+
|
|
20
|
+
Usage::
|
|
21
|
+
|
|
22
|
+
from infoway import InfowayClient
|
|
23
|
+
|
|
24
|
+
client = InfowayClient(api_key="YOUR_API_KEY")
|
|
25
|
+
trades = client.stock.get_trade("AAPL.US")
|
|
26
|
+
klines = client.crypto.get_kline("BTCUSDT", kline_type=8, count=100)
|
|
27
|
+
temp = client.market.get_temperature(market="HK,US")
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self,
|
|
32
|
+
api_key: str | None = None,
|
|
33
|
+
base_url: str = "https://data.infoway.io",
|
|
34
|
+
timeout: float = 15.0,
|
|
35
|
+
max_retries: int = 3,
|
|
36
|
+
):
|
|
37
|
+
self._http = HttpClient(
|
|
38
|
+
api_key=api_key, base_url=base_url, timeout=timeout, max_retries=max_retries,
|
|
39
|
+
)
|
|
40
|
+
self.stock = StockClient(self._http)
|
|
41
|
+
self.crypto = CryptoClient(self._http)
|
|
42
|
+
self.japan = JapanClient(self._http)
|
|
43
|
+
self.india = IndiaClient(self._http)
|
|
44
|
+
self.common = CommonClient(self._http)
|
|
45
|
+
self.basic = BasicClient(self._http)
|
|
46
|
+
self.market = MarketClient(self._http)
|
|
47
|
+
self.plate = PlateClient(self._http)
|
|
48
|
+
self.stock_info = StockInfoClient(self._http)
|
|
49
|
+
|
|
50
|
+
def close(self):
|
|
51
|
+
self._http.close()
|
|
52
|
+
|
|
53
|
+
def __enter__(self):
|
|
54
|
+
return self
|
|
55
|
+
|
|
56
|
+
def __exit__(self, *args):
|
|
57
|
+
self.close()
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Infoway SDK exception types."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class InfowayTimeoutError(Exception):
|
|
5
|
+
"""Raised when a request times out."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class InfowayAPIError(Exception):
|
|
9
|
+
"""Raised when the API returns a non-success response."""
|
|
10
|
+
|
|
11
|
+
def __init__(self, ret: int, msg: str, trace_id: str | None = None):
|
|
12
|
+
self.ret = ret
|
|
13
|
+
self.msg = msg
|
|
14
|
+
self.trace_id = trace_id
|
|
15
|
+
super().__init__(f"[{ret}] {msg}" + (f" (trace: {trace_id})" if trace_id else ""))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class InfowayAuthError(InfowayAPIError):
|
|
19
|
+
"""Raised on 401 Unauthorized."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, msg: str = "Unauthorized"):
|
|
22
|
+
super().__init__(ret=401, msg=msg)
|
|
File without changes
|