fugle-marketdata 0.1.1a0__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.
- fugle_marketdata-0.1.1a0/LICENSE +21 -0
- fugle_marketdata-0.1.1a0/PKG-INFO +96 -0
- fugle_marketdata-0.1.1a0/README.md +71 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/__init__.py +4 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/client_factory.py +8 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/constants.py +11 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/rest/__init__.py +2 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/rest/base_rest.py +22 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/rest/factory.py +38 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/rest/futopt/__init__.py +2 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/rest/futopt/client.py +13 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/rest/futopt/intraday.py +20 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/rest/stock/__init__.py +1 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/rest/stock/client.py +21 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/rest/stock/historical.py +11 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/rest/stock/intraday.py +26 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/rest/stock/snapshot.py +15 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/websocket/__init__.py +1 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/websocket/client.py +102 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/websocket/factory.py +40 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/websocket/futopt/__init__.py +1 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/websocket/futopt/client.py +4 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/websocket/stock/__init__.py +1 -0
- fugle_marketdata-0.1.1a0/fugle_marketdata/websocket/stock/client.py +4 -0
- fugle_marketdata-0.1.1a0/pyproject.toml +26 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Fortuna Intelligence Co., Ltd.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: fugle-marketdata
|
|
3
|
+
Version: 0.1.1a0
|
|
4
|
+
Summary: Fugle Realtime API 1.0 client library for Python
|
|
5
|
+
Home-page: https://github.com/fugle-dev/fugle-realtime-py#readme
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: fugle,realtime,stock
|
|
8
|
+
Author: Fortuna Intelligence Co., Ltd.
|
|
9
|
+
Author-email: development@fugle.tw
|
|
10
|
+
Requires-Python: >=3.7,<4.0
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Requires-Dist: pyee (>=9.0.4,<10.0.0)
|
|
19
|
+
Requires-Dist: requests (>=2.28.2,<3.0.0)
|
|
20
|
+
Requires-Dist: websocket-client (>=1.5.1,<2.0.0)
|
|
21
|
+
Project-URL: Documentation, https://developer.fugle.tw
|
|
22
|
+
Project-URL: Repository, https://github.com/fugle-dev/fugle-realtime-py
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
[![PyPI version][pypi-image]][pypi-url]
|
|
26
|
+
[![Python version][python-image]][python-url]
|
|
27
|
+
[![Build Status][action-image]][action-url]
|
|
28
|
+
# Fugle MarketData
|
|
29
|
+
|
|
30
|
+
[![PyPI version][pypi-image]][pypi-url]
|
|
31
|
+
[![Python version][python-image]][python-url]
|
|
32
|
+
[![Build Status][action-image]][action-url]
|
|
33
|
+
|
|
34
|
+
> Fugle MarketData API client library for Node.js
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
$ pip install fugle-marketdata
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Importing
|
|
43
|
+
|
|
44
|
+
```py
|
|
45
|
+
from fugle_marketdata import WebSocketClient, RestClient
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Usage
|
|
50
|
+
|
|
51
|
+
The library is an isomorphic Python client that supports REST API and WebSocket.
|
|
52
|
+
|
|
53
|
+
### REST API
|
|
54
|
+
|
|
55
|
+
```py
|
|
56
|
+
|
|
57
|
+
# def main():
|
|
58
|
+
# client = RestClient(api_key=TOKEN)
|
|
59
|
+
# stock = client.stock
|
|
60
|
+
# print(stock.intraday.quote(symbol="2330"))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
client = RestClient(api_key = 'YOUR_API_KEY')
|
|
64
|
+
stock = client.stock # Stock REST API client
|
|
65
|
+
print(stock.intraday.quote(symbol="2330"))
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### WebSocket API
|
|
69
|
+
|
|
70
|
+
```py
|
|
71
|
+
|
|
72
|
+
from fugle_marketdata import WebSocketClient, RestClient
|
|
73
|
+
import asyncio
|
|
74
|
+
|
|
75
|
+
def handle_message(message):
|
|
76
|
+
print(message)
|
|
77
|
+
|
|
78
|
+
async def main():
|
|
79
|
+
client = WebSocketClient(api_key = 'YOUR_API_KEY')
|
|
80
|
+
stock = client.stock
|
|
81
|
+
stock.on("message", handle_message)
|
|
82
|
+
await stock.connect()
|
|
83
|
+
stock.subscribe({
|
|
84
|
+
"channel": 'trades',
|
|
85
|
+
"symbol": '2330'
|
|
86
|
+
})
|
|
87
|
+
if __name__ == "__main__":
|
|
88
|
+
asyncio.run(main())
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## License
|
|
93
|
+
|
|
94
|
+
[MIT](LICENSE)
|
|
95
|
+
|
|
96
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
[![PyPI version][pypi-image]][pypi-url]
|
|
2
|
+
[![Python version][python-image]][python-url]
|
|
3
|
+
[![Build Status][action-image]][action-url]
|
|
4
|
+
# Fugle MarketData
|
|
5
|
+
|
|
6
|
+
[![PyPI version][pypi-image]][pypi-url]
|
|
7
|
+
[![Python version][python-image]][python-url]
|
|
8
|
+
[![Build Status][action-image]][action-url]
|
|
9
|
+
|
|
10
|
+
> Fugle MarketData API client library for Node.js
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
$ pip install fugle-marketdata
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Importing
|
|
19
|
+
|
|
20
|
+
```py
|
|
21
|
+
from fugle_marketdata import WebSocketClient, RestClient
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
The library is an isomorphic Python client that supports REST API and WebSocket.
|
|
28
|
+
|
|
29
|
+
### REST API
|
|
30
|
+
|
|
31
|
+
```py
|
|
32
|
+
|
|
33
|
+
# def main():
|
|
34
|
+
# client = RestClient(api_key=TOKEN)
|
|
35
|
+
# stock = client.stock
|
|
36
|
+
# print(stock.intraday.quote(symbol="2330"))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
client = RestClient(api_key = 'YOUR_API_KEY')
|
|
40
|
+
stock = client.stock # Stock REST API client
|
|
41
|
+
print(stock.intraday.quote(symbol="2330"))
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### WebSocket API
|
|
45
|
+
|
|
46
|
+
```py
|
|
47
|
+
|
|
48
|
+
from fugle_marketdata import WebSocketClient, RestClient
|
|
49
|
+
import asyncio
|
|
50
|
+
|
|
51
|
+
def handle_message(message):
|
|
52
|
+
print(message)
|
|
53
|
+
|
|
54
|
+
async def main():
|
|
55
|
+
client = WebSocketClient(api_key = 'YOUR_API_KEY')
|
|
56
|
+
stock = client.stock
|
|
57
|
+
stock.on("message", handle_message)
|
|
58
|
+
await stock.connect()
|
|
59
|
+
stock.subscribe({
|
|
60
|
+
"channel": 'trades',
|
|
61
|
+
"symbol": '2330'
|
|
62
|
+
})
|
|
63
|
+
if __name__ == "__main__":
|
|
64
|
+
asyncio.run(main())
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## License
|
|
69
|
+
|
|
70
|
+
[MIT](LICENSE)
|
|
71
|
+
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
class ClientFactory:
|
|
2
|
+
def __init__(self, **options):
|
|
3
|
+
self.options = options
|
|
4
|
+
print(options)
|
|
5
|
+
if not self.options.get('api_key') and not self.options.get('bearer_token'):
|
|
6
|
+
raise TypeError('One of the "api_key" or "bearer_token" options must be specified')
|
|
7
|
+
if self.options.get('api_key') and self.options.get('bearer_token'):
|
|
8
|
+
raise TypeError('One and only one of the "api_key" or "bearer_token" options must be specified')
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
FUGLE_MARKETDATA_API_REST_BASE_URL = 'https://api.fugle.tw/marketdata'
|
|
2
|
+
FUGLE_MARKETDATA_API_WEBSOCKET_BASE_URL = 'wss://api.fugle.tw/marketdata'
|
|
3
|
+
FUGLE_MARKETDATA_API_VERSION = 'v1.0'
|
|
4
|
+
|
|
5
|
+
CONNECT_EVENT = 'connect'
|
|
6
|
+
DISCONNECT_EVENT = 'disconnect'
|
|
7
|
+
MESSAGE_EVENT = 'message'
|
|
8
|
+
ERROR_EVENT = 'error'
|
|
9
|
+
AUTHENTICATED_EVENT = 'authenticated'
|
|
10
|
+
UNAUTHENTICATED_EVENT = 'unauthenticated'
|
|
11
|
+
UNAUTHENTICATED_MESSAGE = 'Invalid authentication credentials'
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from urllib.parse import urlencode
|
|
2
|
+
import requests
|
|
3
|
+
|
|
4
|
+
class BaseRest(object):
|
|
5
|
+
def __init__(self, **config):
|
|
6
|
+
self.config = config
|
|
7
|
+
|
|
8
|
+
def request(self, path, **params):
|
|
9
|
+
baseUrl = self.config['base_url']
|
|
10
|
+
headers = {}
|
|
11
|
+
if self.config.get('api_key'):
|
|
12
|
+
headers['X-API-KEY'] = self.config['api_key']
|
|
13
|
+
if self.config.get('bearer_token'):
|
|
14
|
+
headers['Authorization'] = f"Bearer {self.config['bearer_token']}"
|
|
15
|
+
|
|
16
|
+
endpoint = path if (path.startswith('/')) else '/' + path
|
|
17
|
+
|
|
18
|
+
if len(params) == 0:
|
|
19
|
+
query = ''
|
|
20
|
+
else:
|
|
21
|
+
query = '?' + urlencode(params)
|
|
22
|
+
return requests.get(baseUrl + endpoint + query, headers=headers).json()
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from fugle_marketdata.client_factory import ClientFactory
|
|
2
|
+
from ..constants import FUGLE_MARKETDATA_API_REST_BASE_URL, FUGLE_MARKETDATA_API_VERSION
|
|
3
|
+
from .stock import RestStockClient
|
|
4
|
+
from .futopt import RestFutOptClient
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RestClientFactory(ClientFactory):
|
|
8
|
+
def __init__(self, **options):
|
|
9
|
+
super().__init__(**options)
|
|
10
|
+
self.__clients = {}
|
|
11
|
+
self.options = options
|
|
12
|
+
|
|
13
|
+
@property
|
|
14
|
+
def stock(self):
|
|
15
|
+
return self.get_client('stock')
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def futopt(self):
|
|
19
|
+
return self.get_client('futopt')
|
|
20
|
+
|
|
21
|
+
def get_client(self, type):
|
|
22
|
+
|
|
23
|
+
base_url = f"{FUGLE_MARKETDATA_API_REST_BASE_URL}/{FUGLE_MARKETDATA_API_VERSION}/{type}"
|
|
24
|
+
|
|
25
|
+
if type in self.__clients:
|
|
26
|
+
return self.__clients[type]
|
|
27
|
+
|
|
28
|
+
if type == 'stock':
|
|
29
|
+
client = RestStockClient(base_url=base_url, **self.options)
|
|
30
|
+
|
|
31
|
+
elif type == 'futopt':
|
|
32
|
+
client = RestFutOptClient(base_url=base_url, **self.options)
|
|
33
|
+
|
|
34
|
+
else:
|
|
35
|
+
None
|
|
36
|
+
|
|
37
|
+
self.__clients[type] = client
|
|
38
|
+
return client
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from ..base_rest import BaseRest
|
|
2
|
+
|
|
3
|
+
class Intraday(BaseRest):
|
|
4
|
+
def contracts(self, **params):
|
|
5
|
+
return self.request(f"intraday/contracts", **params)
|
|
6
|
+
|
|
7
|
+
def products(self, **params):
|
|
8
|
+
return self.request(f"intraday/products", **params)
|
|
9
|
+
|
|
10
|
+
def ticker(self, **params):
|
|
11
|
+
symbol = params.pop('symbol')
|
|
12
|
+
return self.request(f"intraday/ticker/{symbol}", **params)
|
|
13
|
+
|
|
14
|
+
def quote(self, **params):
|
|
15
|
+
symbol = params.pop('symbol')
|
|
16
|
+
return self.request(f"intraday/quote/{symbol}", **params)
|
|
17
|
+
|
|
18
|
+
def candles(self, **params):
|
|
19
|
+
symbol = params.pop('symbol')
|
|
20
|
+
return self.request(f"intraday/candles/{symbol}", **params)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .client import RestStockClient
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from .intraday import Intraday
|
|
2
|
+
from .historical import Historical
|
|
3
|
+
from .snapshot import Snapshot
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class RestStockClient:
|
|
7
|
+
def __init__(self, **config):
|
|
8
|
+
# config: base_url, api_key?, bearer_token?
|
|
9
|
+
self.config = config
|
|
10
|
+
|
|
11
|
+
@property
|
|
12
|
+
def intraday(self):
|
|
13
|
+
return Intraday(**self.config)
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def historical(self):
|
|
17
|
+
return Historical(**self.config)
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def snapshot(self):
|
|
21
|
+
return Snapshot(**self.config)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from ..base_rest import BaseRest
|
|
2
|
+
|
|
3
|
+
class Historical(BaseRest):
|
|
4
|
+
def candles(self, **params):
|
|
5
|
+
symbol = params.pop('symbol')
|
|
6
|
+
return self.request(f"historical/candles/{symbol}", **params)
|
|
7
|
+
|
|
8
|
+
def stats(self, **params):
|
|
9
|
+
symbol = params.pop('symbol')
|
|
10
|
+
return self.request(f"historical/stats/{symbol}", **params)
|
|
11
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from ..base_rest import BaseRest
|
|
2
|
+
|
|
3
|
+
class Intraday(BaseRest):
|
|
4
|
+
def tickers(self, **params):
|
|
5
|
+
return self.request(f"intraday/tickers", **params)
|
|
6
|
+
|
|
7
|
+
def ticker(self, **params):
|
|
8
|
+
symbol = params.pop('symbol')
|
|
9
|
+
return self.request(f"intraday/ticker/{symbol}", **params)
|
|
10
|
+
|
|
11
|
+
def quote(self, **params):
|
|
12
|
+
symbol = params.pop('symbol')
|
|
13
|
+
return self.request(f"intraday/quote/{symbol}", **params)
|
|
14
|
+
|
|
15
|
+
def candles(self, **params):
|
|
16
|
+
symbol = params.pop('symbol')
|
|
17
|
+
return self.request(f"intraday/candles/{symbol}", **params)
|
|
18
|
+
|
|
19
|
+
def trades(self, **params):
|
|
20
|
+
symbol = params.pop('symbol')
|
|
21
|
+
return self.request(f"intraday/trades/{symbol}", **params)
|
|
22
|
+
|
|
23
|
+
def volumes(self, **params):
|
|
24
|
+
symbol = params.pop('symbol')
|
|
25
|
+
return self.request(f"intraday/volumes/{symbol}", **params)
|
|
26
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from ..base_rest import BaseRest
|
|
2
|
+
|
|
3
|
+
class Snapshot(BaseRest):
|
|
4
|
+
def quotes(self, **params):
|
|
5
|
+
market = params.pop('market')
|
|
6
|
+
return self.request(f"snapshot/quotes/{market}", **params)
|
|
7
|
+
|
|
8
|
+
def movers(self, **params):
|
|
9
|
+
market = params.pop('market')
|
|
10
|
+
return self.request(f"snapshot/movers/{market}", **params)
|
|
11
|
+
|
|
12
|
+
def actives(self, **params):
|
|
13
|
+
market = params.pop('market')
|
|
14
|
+
return self.request(f"snapshot/actives/{market}", **params)
|
|
15
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .factory import WebSocketClientFactory
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
import websocket
|
|
4
|
+
from pyee import EventEmitter
|
|
5
|
+
|
|
6
|
+
from ..constants import (
|
|
7
|
+
CONNECT_EVENT,
|
|
8
|
+
DISCONNECT_EVENT,
|
|
9
|
+
MESSAGE_EVENT,
|
|
10
|
+
ERROR_EVENT,
|
|
11
|
+
AUTHENTICATED_EVENT,
|
|
12
|
+
UNAUTHENTICATED_EVENT,
|
|
13
|
+
UNAUTHENTICATED_MESSAGE
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
class WebSocketClient():
|
|
17
|
+
def __init__(self, **config):
|
|
18
|
+
self.config = config
|
|
19
|
+
# config: base_url, api_key?, bearer_token?
|
|
20
|
+
self.ee = EventEmitter()
|
|
21
|
+
self.ee.on(CONNECT_EVENT, self.__authenticate)
|
|
22
|
+
self.__ws = websocket.WebSocketApp(
|
|
23
|
+
self.config.get('base_url'),
|
|
24
|
+
on_open=self.on_open,
|
|
25
|
+
on_close=self.on_close,
|
|
26
|
+
on_error=self.on_error,
|
|
27
|
+
on_message=self.on_message)
|
|
28
|
+
self.loop = asyncio.get_event_loop()
|
|
29
|
+
self.future = self.loop.create_future()
|
|
30
|
+
|
|
31
|
+
def subscribe(self, params):
|
|
32
|
+
message = {
|
|
33
|
+
"event": "subscribe",
|
|
34
|
+
"data": params
|
|
35
|
+
}
|
|
36
|
+
self.__send(message)
|
|
37
|
+
|
|
38
|
+
def unsubscribe(self, params):
|
|
39
|
+
message = {
|
|
40
|
+
"event": "unsubscribe",
|
|
41
|
+
"data": params
|
|
42
|
+
}
|
|
43
|
+
self.__send(message)
|
|
44
|
+
|
|
45
|
+
def __authenticate(self, ws):
|
|
46
|
+
if self.config.get('api_key'):
|
|
47
|
+
auth_info = {
|
|
48
|
+
'event': 'auth',
|
|
49
|
+
'data': {
|
|
50
|
+
'apikey': self.config['api_key']
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
elif self.config.get('bearer_token'):
|
|
54
|
+
auth_info = {
|
|
55
|
+
'event': 'auth',
|
|
56
|
+
'data': {
|
|
57
|
+
'token': self.config['bearer_token']
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else:
|
|
61
|
+
None # error
|
|
62
|
+
|
|
63
|
+
self.__send(auth_info)
|
|
64
|
+
|
|
65
|
+
def __send(self, message):
|
|
66
|
+
print(message)
|
|
67
|
+
self.__ws.send(json.dumps(message))
|
|
68
|
+
|
|
69
|
+
def on_open(self, ws):
|
|
70
|
+
self.ee.emit(CONNECT_EVENT, ws)
|
|
71
|
+
|
|
72
|
+
def on_close(self, close_status_code, close_msg):
|
|
73
|
+
self.ee.emit(DISCONNECT_EVENT, close_msg)
|
|
74
|
+
|
|
75
|
+
def on_message(self, ws, data):
|
|
76
|
+
message = json.loads(data)
|
|
77
|
+
self.ee.emit(MESSAGE_EVENT, data)
|
|
78
|
+
if message['event'] == AUTHENTICATED_EVENT:
|
|
79
|
+
self.ee.emit(AUTHENTICATED_EVENT, message)
|
|
80
|
+
self.loop.call_soon_threadsafe(self.future.set_result, 'success')
|
|
81
|
+
elif message['event'] == ERROR_EVENT:
|
|
82
|
+
if message['data'] and message['data']['message'] == UNAUTHENTICATED_MESSAGE:
|
|
83
|
+
self.ee.emit(UNAUTHENTICATED_EVENT, message)
|
|
84
|
+
self.loop.call_soon_threadsafe(self.future.set_exception, 'login fail')
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def on_error(self, ws, error):
|
|
88
|
+
self.ee.emit("error", ws, error)
|
|
89
|
+
|
|
90
|
+
def on(self, event, listener):
|
|
91
|
+
self.ee.on(event, listener)
|
|
92
|
+
|
|
93
|
+
def off(self, event, listener):
|
|
94
|
+
self.ee.off(event, listener)
|
|
95
|
+
|
|
96
|
+
async def connect(self):
|
|
97
|
+
self.loop.run_in_executor(None, self.__ws.run_forever)
|
|
98
|
+
await self.future
|
|
99
|
+
|
|
100
|
+
def disconnect(self):
|
|
101
|
+
if self.__ws is not None:
|
|
102
|
+
self.__ws.close()
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from fugle_marketdata.client_factory import ClientFactory
|
|
2
|
+
from .futopt import WebSocketFutOptClient
|
|
3
|
+
from .stock import WebSocketStockClient
|
|
4
|
+
from ..constants import FUGLE_MARKETDATA_API_WEBSOCKET_BASE_URL, FUGLE_MARKETDATA_API_VERSION
|
|
5
|
+
|
|
6
|
+
class WebSocketClientFactory(ClientFactory):
|
|
7
|
+
def __init__(self, **options):
|
|
8
|
+
super().__init__(**options)
|
|
9
|
+
self.__clients = {}
|
|
10
|
+
self.options = options
|
|
11
|
+
|
|
12
|
+
@property
|
|
13
|
+
def stock(self):
|
|
14
|
+
return self.get_client('stock')
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def futopt(self):
|
|
18
|
+
return self.get_client('futopt')
|
|
19
|
+
|
|
20
|
+
def get_client(self, type):
|
|
21
|
+
|
|
22
|
+
base_url = f"{FUGLE_MARKETDATA_API_WEBSOCKET_BASE_URL}/{FUGLE_MARKETDATA_API_VERSION}/{type}/streaming"
|
|
23
|
+
|
|
24
|
+
if type in self.__clients:
|
|
25
|
+
return self.__clients[type]
|
|
26
|
+
|
|
27
|
+
if type == 'stock':
|
|
28
|
+
client = WebSocketStockClient(base_url=base_url, **self.options)
|
|
29
|
+
elif type == 'futopt' :
|
|
30
|
+
client = WebSocketFutOptClient(base_url=base_url, **self.options)
|
|
31
|
+
else:
|
|
32
|
+
None
|
|
33
|
+
|
|
34
|
+
self.__clients[type] = client
|
|
35
|
+
return client
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .client import WebSocketFutOptClient
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .client import WebSocketStockClient
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "fugle-marketdata"
|
|
3
|
+
version = "0.1.1a0"
|
|
4
|
+
description = "Fugle Realtime API 1.0 client library for Python"
|
|
5
|
+
authors = ["Fortuna Intelligence Co., Ltd. <development@fugle.tw>"]
|
|
6
|
+
license = "MIT"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
homepage = "https://github.com/fugle-dev/fugle-realtime-py#readme"
|
|
9
|
+
repository = "https://github.com/fugle-dev/fugle-realtime-py"
|
|
10
|
+
documentation = "https://developer.fugle.tw"
|
|
11
|
+
keywords = ["fugle", "realtime", "stock"]
|
|
12
|
+
|
|
13
|
+
[tool.poetry.dependencies]
|
|
14
|
+
python = "^3.7"
|
|
15
|
+
requests = "^2.28.2"
|
|
16
|
+
websocket-client = "^1.5.1"
|
|
17
|
+
pyee = "^9.0.4"
|
|
18
|
+
|
|
19
|
+
[tool.poetry.dev-dependencies]
|
|
20
|
+
pytest = "^7.2.0"
|
|
21
|
+
pytest-mock = "^3.6.1"
|
|
22
|
+
pytest-cov = "^2.12.1"
|
|
23
|
+
|
|
24
|
+
[build-system]
|
|
25
|
+
requires = ["poetry-core>=1.0.0"]
|
|
26
|
+
build-backend = "poetry.core.masonry.api"
|