insight-proxy-client 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- insight_proxy/__init__.py +4 -0
- insight_proxy/client.py +183 -0
- insight_proxy/com/__init__.py +1 -0
- insight_proxy/com/insight/__init__.py +9 -0
- insight_proxy/com/insight/_contract.py +2 -0
- insight_proxy/com/insight/_session.py +179 -0
- insight_proxy/com/insight/market_service.py +43 -0
- insight_proxy/com/insight/playback.py +13 -0
- insight_proxy/com/insight/query.py +325 -0
- insight_proxy/com/insight/subscribe.py +55 -0
- insight_proxy/protocol.py +58 -0
- insight_proxy/typed_codec.py +160 -0
- insight_proxy_client-0.0.1.dist-info/METADATA +91 -0
- insight_proxy_client-0.0.1.dist-info/RECORD +16 -0
- insight_proxy_client-0.0.1.dist-info/WHEEL +5 -0
- insight_proxy_client-0.0.1.dist-info/top_level.txt +1 -0
insight_proxy/client.py
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""Thread-safe request/response multiplexing with a bounded event queue."""
|
|
2
|
+
import itertools
|
|
3
|
+
import queue
|
|
4
|
+
import socket
|
|
5
|
+
import ssl
|
|
6
|
+
import threading
|
|
7
|
+
|
|
8
|
+
from .protocol import frame_size, pack, unpack, restore_dates
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BridgeError(RuntimeError):
|
|
12
|
+
def __init__(self, code, message):
|
|
13
|
+
self.code = code
|
|
14
|
+
super().__init__(f"{code}: {message}")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class InsightClient:
|
|
18
|
+
def __init__(self, host="127.0.0.1", port=8765, *, token="", timeout=30,
|
|
19
|
+
event_queue_size=10000, ssl_context=None):
|
|
20
|
+
self.host, self.port = host, port
|
|
21
|
+
self.token, self.timeout = token, timeout
|
|
22
|
+
self.ssl_context = ssl_context
|
|
23
|
+
self._sock = None
|
|
24
|
+
self._reader = None
|
|
25
|
+
self._send_lock = threading.Lock()
|
|
26
|
+
self._lock = threading.Lock()
|
|
27
|
+
self._ids = itertools.count(1)
|
|
28
|
+
self._pending = {}
|
|
29
|
+
self._events = queue.Queue(maxsize=event_queue_size)
|
|
30
|
+
self._failure = None
|
|
31
|
+
|
|
32
|
+
def connect(self):
|
|
33
|
+
if self._sock is not None:
|
|
34
|
+
raise RuntimeError("Already connected; create a new client for reconnect")
|
|
35
|
+
sock = socket.create_connection((self.host, self.port), self.timeout)
|
|
36
|
+
try:
|
|
37
|
+
if self.ssl_context:
|
|
38
|
+
sock = self.ssl_context.wrap_socket(sock, server_hostname=self.host)
|
|
39
|
+
sock.settimeout(None)
|
|
40
|
+
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
|
41
|
+
self._sock = sock
|
|
42
|
+
self._reader = threading.Thread(target=self._read_loop, daemon=True)
|
|
43
|
+
self._reader.start()
|
|
44
|
+
self.request("hello", {"version": 1, "token": self.token})
|
|
45
|
+
return self
|
|
46
|
+
except BaseException:
|
|
47
|
+
sock.close()
|
|
48
|
+
self.close()
|
|
49
|
+
raise
|
|
50
|
+
|
|
51
|
+
def _exact(self, size):
|
|
52
|
+
chunks = bytearray()
|
|
53
|
+
while len(chunks) < size:
|
|
54
|
+
chunk = self._sock.recv(size - len(chunks))
|
|
55
|
+
if not chunk:
|
|
56
|
+
raise ConnectionError("Bridge disconnected; reconnect and resubscribe")
|
|
57
|
+
chunks.extend(chunk)
|
|
58
|
+
return bytes(chunks)
|
|
59
|
+
|
|
60
|
+
def _fail(self, error):
|
|
61
|
+
with self._lock:
|
|
62
|
+
if self._failure is None:
|
|
63
|
+
self._failure = error
|
|
64
|
+
pending, self._pending = self._pending, {}
|
|
65
|
+
for waiter in pending.values():
|
|
66
|
+
waiter.put(self._failure)
|
|
67
|
+
if self._sock:
|
|
68
|
+
try:
|
|
69
|
+
self._sock.shutdown(socket.SHUT_RDWR)
|
|
70
|
+
except OSError:
|
|
71
|
+
pass
|
|
72
|
+
self._sock.close()
|
|
73
|
+
|
|
74
|
+
def _read_loop(self):
|
|
75
|
+
try:
|
|
76
|
+
while True:
|
|
77
|
+
message = restore_dates(unpack(self._exact(frame_size(self._exact(4)))))
|
|
78
|
+
if message.get("type") == "response":
|
|
79
|
+
with self._lock:
|
|
80
|
+
waiter = self._pending.pop(message.get("id"), None)
|
|
81
|
+
if waiter:
|
|
82
|
+
waiter.put(message)
|
|
83
|
+
elif message.get("type") == "event":
|
|
84
|
+
try:
|
|
85
|
+
self._events.put_nowait(message)
|
|
86
|
+
except queue.Full:
|
|
87
|
+
raise BridgeError("slow_consumer", "Client event queue full; connection closed to signal a data gap")
|
|
88
|
+
else:
|
|
89
|
+
raise ValueError("Unknown server message")
|
|
90
|
+
except Exception as exc:
|
|
91
|
+
self._fail(exc)
|
|
92
|
+
|
|
93
|
+
def request(self, method, params=None, *, timeout=None):
|
|
94
|
+
request_id = next(self._ids)
|
|
95
|
+
waiter = queue.Queue()
|
|
96
|
+
frame = pack({"id": request_id, "method": method, "params": params or {}})
|
|
97
|
+
with self._lock:
|
|
98
|
+
if self._failure:
|
|
99
|
+
raise self._failure
|
|
100
|
+
if self._sock is None:
|
|
101
|
+
raise ConnectionError("Call connect() first")
|
|
102
|
+
self._pending[request_id] = waiter
|
|
103
|
+
try:
|
|
104
|
+
with self._send_lock:
|
|
105
|
+
self._sock.sendall(frame)
|
|
106
|
+
try:
|
|
107
|
+
reply = waiter.get(timeout=self.timeout if timeout is None else timeout)
|
|
108
|
+
except queue.Empty:
|
|
109
|
+
raise TimeoutError(f"{method} timed out; upstream execution may still finish") from None
|
|
110
|
+
if isinstance(reply, Exception):
|
|
111
|
+
raise reply
|
|
112
|
+
if not reply.get("ok"):
|
|
113
|
+
error = reply["error"]
|
|
114
|
+
raise BridgeError(error["code"], error["message"])
|
|
115
|
+
return reply.get("result")
|
|
116
|
+
finally:
|
|
117
|
+
with self._lock:
|
|
118
|
+
self._pending.pop(request_id, None)
|
|
119
|
+
|
|
120
|
+
def ping(self):
|
|
121
|
+
return self.request("ping")
|
|
122
|
+
|
|
123
|
+
def status(self):
|
|
124
|
+
return self.request("status")
|
|
125
|
+
|
|
126
|
+
def methods(self):
|
|
127
|
+
return self.request("methods")
|
|
128
|
+
|
|
129
|
+
def query(self, name, **params):
|
|
130
|
+
"""Returns {columns, records, dtypes}; datetime parameters are encoded automatically."""
|
|
131
|
+
return self.request("query", {"name": name, "kwargs": params})
|
|
132
|
+
|
|
133
|
+
def snapshot(self, symbols, datatype):
|
|
134
|
+
return self.query("get_gdp_recent_snap", htsc_code=symbols, data_type=datatype)
|
|
135
|
+
|
|
136
|
+
def static(self, symbols):
|
|
137
|
+
return self.query("get_gdp_static_by_id", htsc_code=symbols)
|
|
138
|
+
|
|
139
|
+
def subscribe(self, datatype, symbols=None):
|
|
140
|
+
"""symbols=None subscribes the entire datatype. Returns submitted, not confirmed."""
|
|
141
|
+
return self.request("subscribe", {"datatype": datatype, "symbols": symbols})
|
|
142
|
+
|
|
143
|
+
def unsubscribe(self, datatype, symbols=None):
|
|
144
|
+
return self.request("unsubscribe", {"datatype": datatype, "symbols": symbols})
|
|
145
|
+
|
|
146
|
+
def subscribe_tick(self, symbols):
|
|
147
|
+
"""Legacy SDK stock/futures/ETF tick subscription with explicit codes."""
|
|
148
|
+
return self.request("subscribe_tick", {"symbols": symbols})
|
|
149
|
+
|
|
150
|
+
def unsubscribe_tick(self, symbols):
|
|
151
|
+
return self.request("unsubscribe_tick", {"symbols": symbols})
|
|
152
|
+
|
|
153
|
+
def unsubscribe_all(self):
|
|
154
|
+
return self.request("unsubscribe_all")
|
|
155
|
+
|
|
156
|
+
def next_event(self, timeout=None):
|
|
157
|
+
# Check failure first: do not silently serve buffered stale data after a gap.
|
|
158
|
+
import time
|
|
159
|
+
deadline = None if timeout is None else time.monotonic() + timeout
|
|
160
|
+
while True:
|
|
161
|
+
if self._failure:
|
|
162
|
+
raise self._failure
|
|
163
|
+
wait = 0.2 if deadline is None else max(0, min(0.2, deadline - time.monotonic()))
|
|
164
|
+
try:
|
|
165
|
+
return self._events.get(timeout=wait)
|
|
166
|
+
except queue.Empty:
|
|
167
|
+
if deadline is not None and time.monotonic() >= deadline:
|
|
168
|
+
raise TimeoutError("No event received") from None
|
|
169
|
+
|
|
170
|
+
def events(self):
|
|
171
|
+
while True:
|
|
172
|
+
yield self.next_event()
|
|
173
|
+
|
|
174
|
+
def close(self):
|
|
175
|
+
self._fail(ConnectionError("Client closed"))
|
|
176
|
+
if self._reader and threading.current_thread() is not self._reader:
|
|
177
|
+
self._reader.join(timeout=2)
|
|
178
|
+
|
|
179
|
+
def __enter__(self):
|
|
180
|
+
return self.connect()
|
|
181
|
+
|
|
182
|
+
def __exit__(self, *_):
|
|
183
|
+
self.close()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Insight-compatible module namespace."""
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Insight 6.1.2 data API over TCP, for Python 3.9-3.12.
|
|
2
|
+
|
|
3
|
+
Change SDK imports to this package and replace login/initialization with connect().
|
|
4
|
+
An SDK-compatible connection exclusively owns the server's native login session.
|
|
5
|
+
"""
|
|
6
|
+
from ._session import Client, connect, close, SDKError
|
|
7
|
+
from . import query, subscribe, playback, market_service
|
|
8
|
+
|
|
9
|
+
__all__ = ['Client', 'connect', 'close', 'SDKError', 'query', 'subscribe', 'playback', 'market_service']
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"""Pinned public API contract, generated from SDK 6.1.2."""
|
|
2
|
+
CONTRACT = {'sdk_version': '6.1.2', 'modules': {'query': {'query_fin_info': '(query_type, params)', 'get_kline': "(htsc_code=None, time=None, frequency='daily', fq='pre')", 'get_derived': '(htsc_code=None, trading_day=None, type=None)', 'get_trade_distribution': '(htsc_code=None, trading_day=None)', 'get_chip_distribution': '(htsc_code=None, trading_day=None)', 'get_money_flow': '(htsc_code=None, trading_day=None)', 'get_change_summary': '(market=None, trading_day=None)', 'get_billboard': '(type=None, market=None)', 'get_stock_info': '(htsc_code=None, listing_date=None, listing_state=None)', 'get_all_stocks_info': '(listing_date=None, exchange=None, listing_state=None)', 'get_trading_days': '(exchange=None, trading_day=None, count=None)', 'get_industries': '(classified=None)', 'get_industry': "(htsc_code=None, classified='sw')", 'get_industry_stocks': "(industry_code=None, classified='sw')", 'get_new_share': '(htsc_code=None, book_start_date_online=None, listing_date=None)', 'get_daily_basic': '(htsc_code=None, trading_day=None)', 'get_stock_valuation': '(htsc_code=None, trading_day=None)', 'get_bond_info': '(htsc_code=None, secu_category_code=None, listing_date=None, issue_start_date=None, end_date=None)', 'get_all_bonds': '(exchange=None, secu_category_code=None, listing_date=None, issue_start_date=None, end_date=None)', 'get_repo_price': '(htsc_code=None, exchange=None, trading_day=None)', 'get_new_con_bond': '(htsc_code=None, exchange=None, book_start_date_online=None, listing_date=None, issue_date=None, convert_code=None)', 'get_bond_price': '(htsc_code=None, exchange=None, trading_day=None)', 'get_con_bond_redemption': '(htsc_code=None, exchange=None, register_date=None)', 'get_con_bond_2_shares_change': '(htsc_code=None, exchange=None, pub_date=None, convert_code=None)', 'get_con_bond_2_shares': '(htsc_code=None, pub_date=None, exer_begin_date=None, exer_end_date=None, convert_code=None, exchange=None)', 'get_income_statement': '(htsc_code=None, end_date=None, period=None)', 'get_balance_sheet': '(htsc_code=None, end_date=None, period=None)', 'get_cashflow_statement': '(htsc_code=None, end_date=None, period=None)', 'get_fin_indicator': '(htsc_code=None, end_date=None, period=None)', 'get_newest_stock_value': '(htsc_code=None, exchange=None)', 'get_company_info': '(htsc_code=None, name=None)', 'get_shareholder_num': '(htsc_code=None, name=None, end_date=None)', 'get_additional_share': '(htsc_code=None, listing_date=None)', 'get_allotment_share': '(htsc_code=None, ini_pub_date=None, is_allot_half_year=None)', 'get_capital_structure': '(htsc_code=None, end_date=None)', 'get_dividend': '(htsc_code=None, right_reg_date=None, ex_divi_date=None, divi_pay_date=None)', 'get_north_bound': '(htsc_code=None, trading_day=None)', 'get_margin_target': '(htsc_code=None, exchange=None)', 'get_margin_summary': '(htsc_code=None, trading_day=None)', 'get_margin_detail': '(exchange=None, trading_day=None)', 'get_shareholders_top10': '(htsc_code=None, change_date=None)', 'get_shareholders_floating_top10': '(htsc_code=None, change_date=None)', 'get_tick': '(htsc_code=None, security_type=None, trading_day=None)', 'get_adj_factor': '(htsc_code=None, begin_date=None)', 'get_locked_shares': '(htsc_code=None, listing_date=None)', 'get_frozen_shares': '(htsc_code=None, freezing_start_date=None)', 'get_hk_industry': '(htsc_code=None)', 'get_hk_industry_stocks': '(industry_code=None)', 'get_hk_daily_basic': '(htsc_code=None, trading_day=None)', 'get_hk_stock_valuation': '(htsc_code=None, trading_day=None)', 'get_hk_stock_basic_info': '(htsc_code=None, listing_date=None, listing_state=None)', 'get_hk_dividend': '(htsc_code=None, ex_divi_date=None)', 'get_hk_index_mapping': '(month=None)', 'get_main_product_info': '(htsc_code=None, product_code=None, product_level=None)', 'get_htsc_margin_target': '()', 'get_htsc_margin_by_id': '(htsc_code=None, data_type=None)', 'get_htsc_margin_by_type': '(data_type=None, security_type=None, is_async=False)', 'get_index_info': '(htsc_code=None, trading_day=None)', 'get_all_index': '(exchange=None, trading_day=None)', 'get_index_component': '(htsc_code=None, name=None, stock_code=None, trading_day=None)', 'get_index_component_pro': '(htsc_code=None, name=None, stock_code=None, trading_day=None)', 'get_factors': '(htsc_code=None, factor_name=None, trading_day=None)', 'get_fund_info': '(htsc_code=None, trading_day=None)', 'get_fund_target': '(htsc_code=None, exchange=None, end_date=None)', 'get_etf_component': '(htsc_code=None, pub_date=None, trading_day=None)', 'get_public_fund_portfolio': '(htsc_code=None, name=None, exchange=None, end_date=None)', 'get_etf_redemption': '(htsc_code=None, exchange=None, trading_day=None)', 'get_basic_info': '(htsc_code=None)', 'get_all_basic_info': '(security_type=None, exchange=None, today=True)', 'get_news': '(htsc_code=None, event_ids=None, pub_date=None)', 'find_edb_index': '(name=None)', 'edb': '(indexes=None, pub_date=None)', 'get_rpt_basicinfo_ht': '(id=None, report_code=None, time=None, title=None, language=None, is_valid=None)', 'get_rpt_stk_ht': '(report_code=None)', 'get_rpt_industry_ht': '(report_code=None)', 'get_rpt_author_ht': '(report_code=None)', 'get_rpt_annex_ht': '(report_code=None)', 'get_rpt_stkpredict_ht': '(report_code=None)', 'get_etf_info': '(query)', 'query_mdcontant': '(security_idsource_and_types, security_id_list)'}, 'subscribe': {'subscribe_tick_by_id': "(htsc_code, mode='coverage')", 'subscribe_tick_by_type': "(query=None, mode='coverage')", 'subscribe_kline_by_id': "(htsc_code=None, frequency=None, mode='coverage')", 'subscribe_kline_by_type': "(query=None, frequency=None, mode='coverage')", 'subscribe_trans_and_order_by_id': "(htsc_code=None, mode='coverage')", 'subscribe_trans_and_order_by_type': "(query=None, mode='coverage')", 'subscribe_htsc_margin_by_id': "(htsc_code=None, data_type=None, mode='coverage')", 'subscribe_htsc_margin_by_type': "(security_type=None, data_type=None, mode='coverage')", 'subscribe_derived': "(type=None, htsc_code=None, exchange=None, frequency='1min', mode='coverage')", 'subscribe_news_by_type': "(query=None, mode='coverage')", 'subscribe_news_by_id': "(htsc_code=None, mode='coverage')", 'sync': '()'}, 'playback': {'playback_tick': "(htsc_code=None, replay_time=None, fq='pre', timeout=600)", 'playback_trans_and_order': "(htsc_code=None, replay_time=None, fq='pre', timeout=600)"}}, 'sha256': {'query': '17976b193d25e5a05e8e3492b9f04ad54a93ddd1a700a30212b721cd09e3b4a7', 'subscribe': 'b7920962f688aac4cb8bedd8c5a60c80846a729b347eafc94719c5faba9913b0', 'playback': 'fdd2d88a9a67f564eae0c6a048916a4c25c0e1ca2bcd38ec03d24a3a5e2e2ddf'}, 'callbacks': ['on_subscribe_tick', 'on_subscribe_kline', 'on_subscribe_trans_and_order', 'on_subscribe_derived', 'on_subscribe_htsc_margin', 'on_subscribe_news', 'on_playback_tick', 'on_playback_trans_and_order', 'onPlaybackStatus', 'onPlaybackResponse', 'onPlaybackControlResponse', 'on_query_response', 'on_query_margin_response']}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import builtins
|
|
2
|
+
import functools
|
|
3
|
+
import importlib
|
|
4
|
+
import inspect
|
|
5
|
+
import logging
|
|
6
|
+
import queue
|
|
7
|
+
import threading
|
|
8
|
+
|
|
9
|
+
from ...client import InsightClient, BridgeError
|
|
10
|
+
from ...typed_codec import encode, decode
|
|
11
|
+
from ._contract import CONTRACT
|
|
12
|
+
from .market_service import market_service
|
|
13
|
+
|
|
14
|
+
LOG = logging.getLogger('insight_proxy.compat')
|
|
15
|
+
_default = None
|
|
16
|
+
_default_lock = threading.RLock()
|
|
17
|
+
_EXCEPTIONS = {name: getattr(builtins, name) for name in (
|
|
18
|
+
'ValueError', 'TypeError', 'KeyError', 'IndexError', 'RuntimeError', 'AttributeError',
|
|
19
|
+
'NotImplementedError', 'AssertionError', 'ZeroDivisionError', 'OverflowError',
|
|
20
|
+
'NameError', 'UnboundLocalError', 'TimeoutError', 'OSError', 'ConnectionError',
|
|
21
|
+
'PermissionError', 'FileNotFoundError', 'ImportError', 'ModuleNotFoundError')}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class SDKError(RuntimeError):
|
|
25
|
+
"""A vendor exception whose class is not part of the safe builtin allowlist."""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class _Namespace:
|
|
29
|
+
def __init__(self, client, module):
|
|
30
|
+
self._client, self._module = client, module
|
|
31
|
+
|
|
32
|
+
def __dir__(self):
|
|
33
|
+
return list(CONTRACT['modules'][self._module])
|
|
34
|
+
|
|
35
|
+
def __getattr__(self, name):
|
|
36
|
+
if name not in CONTRACT['modules'][self._module]:
|
|
37
|
+
raise AttributeError(name)
|
|
38
|
+
template = getattr(importlib.import_module('.' + self._module, __package__), name)
|
|
39
|
+
if name == 'sync':
|
|
40
|
+
return template
|
|
41
|
+
signature = inspect.signature(template)
|
|
42
|
+
|
|
43
|
+
@functools.wraps(template)
|
|
44
|
+
def invoke(*args, **kwargs):
|
|
45
|
+
bound = signature.bind(*args, **kwargs)
|
|
46
|
+
bound.apply_defaults()
|
|
47
|
+
return self._client._call(self._module, name, dict(bound.arguments))
|
|
48
|
+
return invoke
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Client:
|
|
52
|
+
"""Explicit session alternative to module-level connect/query/subscribe.
|
|
53
|
+
|
|
54
|
+
Callback exceptions are logged and placed in errors. Transport failures are
|
|
55
|
+
also placed there and close the session; neither is silently swallowed.
|
|
56
|
+
on_event receives bridge status/ack/error events, outside the SDK callback API.
|
|
57
|
+
"""
|
|
58
|
+
def __init__(self, marketservice=None, *, host='127.0.0.1', port=8765, token='123456',
|
|
59
|
+
timeout=30, ssl_context=None, event_queue_size=10000, on_event=None):
|
|
60
|
+
self.transport = InsightClient(host, port, token=token, timeout=timeout,
|
|
61
|
+
ssl_context=ssl_context, event_queue_size=event_queue_size)
|
|
62
|
+
self.marketservice = market_service() if marketservice is None else marketservice
|
|
63
|
+
self.on_event = on_event
|
|
64
|
+
self.errors = queue.Queue(maxsize=100)
|
|
65
|
+
self.query = _Namespace(self, 'query')
|
|
66
|
+
self.subscribe = _Namespace(self, 'subscribe')
|
|
67
|
+
self.playback = _Namespace(self, 'playback')
|
|
68
|
+
self._closed = threading.Event()
|
|
69
|
+
self._thread = None
|
|
70
|
+
self._connected = False
|
|
71
|
+
self.capabilities = None
|
|
72
|
+
|
|
73
|
+
def connect(self):
|
|
74
|
+
# Fail early with an actionable dependency message, before claiming a login.
|
|
75
|
+
try:
|
|
76
|
+
import pandas # noqa: F401
|
|
77
|
+
except ImportError as exc:
|
|
78
|
+
raise ImportError('Install insight-proxy-client (pandas >=1.5,<3)') from exc
|
|
79
|
+
try:
|
|
80
|
+
self.transport.connect()
|
|
81
|
+
self.capabilities = self.transport.request('sdk_open', {'sdk_version': '6.1.2', 'codec': 1})
|
|
82
|
+
if self.capabilities.get('modules') != CONTRACT['modules']:
|
|
83
|
+
raise BridgeError('incompatible_sdk', 'Server API contract differs from Insight 6.1.2')
|
|
84
|
+
self._connected = True
|
|
85
|
+
self._thread = threading.Thread(target=self._callbacks, name='insight-callbacks', daemon=True)
|
|
86
|
+
self._thread.start()
|
|
87
|
+
return self
|
|
88
|
+
except BaseException:
|
|
89
|
+
self.close()
|
|
90
|
+
raise
|
|
91
|
+
|
|
92
|
+
def _call(self, module, name, kwargs):
|
|
93
|
+
if not self._connected or self._closed.is_set():
|
|
94
|
+
raise ConnectionError('Call connect() first; create a new Client after close()')
|
|
95
|
+
timeout = self.transport.timeout
|
|
96
|
+
# Native playback is blocking and has its own public timeout argument.
|
|
97
|
+
if module == 'playback' and isinstance(kwargs.get('timeout'), (int, float)):
|
|
98
|
+
timeout = max(timeout, kwargs['timeout'] + 15)
|
|
99
|
+
result = self.transport.request('sdk_call', {'module': module, 'name': name,
|
|
100
|
+
'kwargs': encode(kwargs)}, timeout=timeout)
|
|
101
|
+
if 'exception' in result:
|
|
102
|
+
error = result['exception']
|
|
103
|
+
cls = _EXCEPTIONS.get(error['type'], SDKError)
|
|
104
|
+
raise cls(*decode(error['args']))
|
|
105
|
+
return decode(result['value'])
|
|
106
|
+
|
|
107
|
+
def _record_error(self, error):
|
|
108
|
+
try:
|
|
109
|
+
self.errors.put_nowait(error)
|
|
110
|
+
except queue.Full:
|
|
111
|
+
self.close()
|
|
112
|
+
|
|
113
|
+
def _callbacks(self):
|
|
114
|
+
while not self._closed.is_set():
|
|
115
|
+
try:
|
|
116
|
+
event = self.transport.next_event(timeout=0.2)
|
|
117
|
+
except TimeoutError:
|
|
118
|
+
continue
|
|
119
|
+
except Exception as exc:
|
|
120
|
+
if not self._closed.is_set():
|
|
121
|
+
self._record_error(exc)
|
|
122
|
+
LOG.error('Insight connection ended: %s', exc)
|
|
123
|
+
self.close()
|
|
124
|
+
break
|
|
125
|
+
try:
|
|
126
|
+
if event['event'] == 'sdk_callback':
|
|
127
|
+
name = event['data']['name']
|
|
128
|
+
if name not in CONTRACT['callbacks']:
|
|
129
|
+
raise ValueError('Unknown SDK callback: ' + str(name))
|
|
130
|
+
callback = getattr(self.marketservice, name, None)
|
|
131
|
+
if callback is not None:
|
|
132
|
+
callback(decode(event['data']['value']))
|
|
133
|
+
elif event['event'] == 'sdk_callback_error':
|
|
134
|
+
raise SDKError(event['data']['message'])
|
|
135
|
+
elif self.on_event:
|
|
136
|
+
self.on_event(event)
|
|
137
|
+
except Exception as exc:
|
|
138
|
+
self._record_error(exc)
|
|
139
|
+
LOG.exception('Insight callback failed')
|
|
140
|
+
|
|
141
|
+
def close(self):
|
|
142
|
+
self._closed.set()
|
|
143
|
+
self._connected = False
|
|
144
|
+
self.transport.close()
|
|
145
|
+
if self._thread and threading.current_thread() is not self._thread:
|
|
146
|
+
self._thread.join(timeout=2)
|
|
147
|
+
# The server resets the native login before another compatibility lease.
|
|
148
|
+
|
|
149
|
+
def __enter__(self):
|
|
150
|
+
return self.connect()
|
|
151
|
+
|
|
152
|
+
def __exit__(self, *_):
|
|
153
|
+
self.close()
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def connect(marketservice=None, **connection):
|
|
157
|
+
"""Initialize module-level APIs; credentials remain in the server config."""
|
|
158
|
+
global _default
|
|
159
|
+
with _default_lock:
|
|
160
|
+
if _default is not None and not _default._closed.is_set():
|
|
161
|
+
raise RuntimeError('A default connection is already active; call close() first')
|
|
162
|
+
_default = Client(marketservice, **connection).connect()
|
|
163
|
+
return _default
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def close():
|
|
167
|
+
global _default
|
|
168
|
+
with _default_lock:
|
|
169
|
+
if _default is not None:
|
|
170
|
+
_default.close()
|
|
171
|
+
_default = None
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def call(module, name, kwargs):
|
|
175
|
+
with _default_lock:
|
|
176
|
+
client = _default
|
|
177
|
+
if client is None:
|
|
178
|
+
raise ConnectionError('Call insight_proxy.com.insight.connect() first')
|
|
179
|
+
return client._call(module, name, kwargs)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Override these callbacks exactly as with Insight 6.1.2."""
|
|
2
|
+
class market_service:
|
|
3
|
+
def __init__(self):
|
|
4
|
+
pass
|
|
5
|
+
|
|
6
|
+
def on_subscribe_tick(self, result):
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
def on_subscribe_kline(self, result):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
def on_subscribe_trans_and_order(self, result):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
def on_subscribe_derived(self, result):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
def on_subscribe_htsc_margin(self, result):
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
def on_subscribe_news(self, result):
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
def on_playback_tick(self, result):
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
def on_playback_trans_and_order(self, result):
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
def onPlaybackStatus(self, status):
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
def onPlaybackResponse(self, response):
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
def onPlaybackControlResponse(self, response):
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
def on_query_response(self, result):
|
|
40
|
+
pass
|
|
41
|
+
|
|
42
|
+
def on_query_margin_response(self, result):
|
|
43
|
+
pass
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""Insight 6.1.2 signatures. Generated by tools/generate-compat.py."""
|
|
2
|
+
from ._session import call as _call
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def playback_tick(htsc_code=None, replay_time=None, fq='pre', timeout=600):
|
|
6
|
+
"""Socket equivalent of Insight 6.1.2 playback.playback_tick."""
|
|
7
|
+
return _call('playback', 'playback_tick', locals())
|
|
8
|
+
|
|
9
|
+
def playback_trans_and_order(htsc_code=None, replay_time=None, fq='pre', timeout=600):
|
|
10
|
+
"""Socket equivalent of Insight 6.1.2 playback.playback_trans_and_order."""
|
|
11
|
+
return _call('playback', 'playback_trans_and_order', locals())
|
|
12
|
+
|
|
13
|
+
__all__ = ['playback_tick', 'playback_trans_and_order']
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
"""Insight 6.1.2 signatures. Generated by tools/generate-compat.py."""
|
|
2
|
+
from ._session import call as _call
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def query_fin_info(query_type, params):
|
|
6
|
+
"""Socket equivalent of Insight 6.1.2 query.query_fin_info."""
|
|
7
|
+
return _call('query', 'query_fin_info', locals())
|
|
8
|
+
|
|
9
|
+
def get_kline(htsc_code=None, time=None, frequency='daily', fq='pre'):
|
|
10
|
+
"""Socket equivalent of Insight 6.1.2 query.get_kline."""
|
|
11
|
+
return _call('query', 'get_kline', locals())
|
|
12
|
+
|
|
13
|
+
def get_derived(htsc_code=None, trading_day=None, type=None):
|
|
14
|
+
"""Socket equivalent of Insight 6.1.2 query.get_derived."""
|
|
15
|
+
return _call('query', 'get_derived', locals())
|
|
16
|
+
|
|
17
|
+
def get_trade_distribution(htsc_code=None, trading_day=None):
|
|
18
|
+
"""Socket equivalent of Insight 6.1.2 query.get_trade_distribution."""
|
|
19
|
+
return _call('query', 'get_trade_distribution', locals())
|
|
20
|
+
|
|
21
|
+
def get_chip_distribution(htsc_code=None, trading_day=None):
|
|
22
|
+
"""Socket equivalent of Insight 6.1.2 query.get_chip_distribution."""
|
|
23
|
+
return _call('query', 'get_chip_distribution', locals())
|
|
24
|
+
|
|
25
|
+
def get_money_flow(htsc_code=None, trading_day=None):
|
|
26
|
+
"""Socket equivalent of Insight 6.1.2 query.get_money_flow."""
|
|
27
|
+
return _call('query', 'get_money_flow', locals())
|
|
28
|
+
|
|
29
|
+
def get_change_summary(market=None, trading_day=None):
|
|
30
|
+
"""Socket equivalent of Insight 6.1.2 query.get_change_summary."""
|
|
31
|
+
return _call('query', 'get_change_summary', locals())
|
|
32
|
+
|
|
33
|
+
def get_billboard(type=None, market=None):
|
|
34
|
+
"""Socket equivalent of Insight 6.1.2 query.get_billboard."""
|
|
35
|
+
return _call('query', 'get_billboard', locals())
|
|
36
|
+
|
|
37
|
+
def get_stock_info(htsc_code=None, listing_date=None, listing_state=None):
|
|
38
|
+
"""Socket equivalent of Insight 6.1.2 query.get_stock_info."""
|
|
39
|
+
return _call('query', 'get_stock_info', locals())
|
|
40
|
+
|
|
41
|
+
def get_all_stocks_info(listing_date=None, exchange=None, listing_state=None):
|
|
42
|
+
"""Socket equivalent of Insight 6.1.2 query.get_all_stocks_info."""
|
|
43
|
+
return _call('query', 'get_all_stocks_info', locals())
|
|
44
|
+
|
|
45
|
+
def get_trading_days(exchange=None, trading_day=None, count=None):
|
|
46
|
+
"""Socket equivalent of Insight 6.1.2 query.get_trading_days."""
|
|
47
|
+
return _call('query', 'get_trading_days', locals())
|
|
48
|
+
|
|
49
|
+
def get_industries(classified=None):
|
|
50
|
+
"""Socket equivalent of Insight 6.1.2 query.get_industries."""
|
|
51
|
+
return _call('query', 'get_industries', locals())
|
|
52
|
+
|
|
53
|
+
def get_industry(htsc_code=None, classified='sw'):
|
|
54
|
+
"""Socket equivalent of Insight 6.1.2 query.get_industry."""
|
|
55
|
+
return _call('query', 'get_industry', locals())
|
|
56
|
+
|
|
57
|
+
def get_industry_stocks(industry_code=None, classified='sw'):
|
|
58
|
+
"""Socket equivalent of Insight 6.1.2 query.get_industry_stocks."""
|
|
59
|
+
return _call('query', 'get_industry_stocks', locals())
|
|
60
|
+
|
|
61
|
+
def get_new_share(htsc_code=None, book_start_date_online=None, listing_date=None):
|
|
62
|
+
"""Socket equivalent of Insight 6.1.2 query.get_new_share."""
|
|
63
|
+
return _call('query', 'get_new_share', locals())
|
|
64
|
+
|
|
65
|
+
def get_daily_basic(htsc_code=None, trading_day=None):
|
|
66
|
+
"""Socket equivalent of Insight 6.1.2 query.get_daily_basic."""
|
|
67
|
+
return _call('query', 'get_daily_basic', locals())
|
|
68
|
+
|
|
69
|
+
def get_stock_valuation(htsc_code=None, trading_day=None):
|
|
70
|
+
"""Socket equivalent of Insight 6.1.2 query.get_stock_valuation."""
|
|
71
|
+
return _call('query', 'get_stock_valuation', locals())
|
|
72
|
+
|
|
73
|
+
def get_bond_info(htsc_code=None, secu_category_code=None, listing_date=None, issue_start_date=None, end_date=None):
|
|
74
|
+
"""Socket equivalent of Insight 6.1.2 query.get_bond_info."""
|
|
75
|
+
return _call('query', 'get_bond_info', locals())
|
|
76
|
+
|
|
77
|
+
def get_all_bonds(exchange=None, secu_category_code=None, listing_date=None, issue_start_date=None, end_date=None):
|
|
78
|
+
"""Socket equivalent of Insight 6.1.2 query.get_all_bonds."""
|
|
79
|
+
return _call('query', 'get_all_bonds', locals())
|
|
80
|
+
|
|
81
|
+
def get_repo_price(htsc_code=None, exchange=None, trading_day=None):
|
|
82
|
+
"""Socket equivalent of Insight 6.1.2 query.get_repo_price."""
|
|
83
|
+
return _call('query', 'get_repo_price', locals())
|
|
84
|
+
|
|
85
|
+
def get_new_con_bond(htsc_code=None, exchange=None, book_start_date_online=None, listing_date=None, issue_date=None, convert_code=None):
|
|
86
|
+
"""Socket equivalent of Insight 6.1.2 query.get_new_con_bond."""
|
|
87
|
+
return _call('query', 'get_new_con_bond', locals())
|
|
88
|
+
|
|
89
|
+
def get_bond_price(htsc_code=None, exchange=None, trading_day=None):
|
|
90
|
+
"""Socket equivalent of Insight 6.1.2 query.get_bond_price."""
|
|
91
|
+
return _call('query', 'get_bond_price', locals())
|
|
92
|
+
|
|
93
|
+
def get_con_bond_redemption(htsc_code=None, exchange=None, register_date=None):
|
|
94
|
+
"""Socket equivalent of Insight 6.1.2 query.get_con_bond_redemption."""
|
|
95
|
+
return _call('query', 'get_con_bond_redemption', locals())
|
|
96
|
+
|
|
97
|
+
def get_con_bond_2_shares_change(htsc_code=None, exchange=None, pub_date=None, convert_code=None):
|
|
98
|
+
"""Socket equivalent of Insight 6.1.2 query.get_con_bond_2_shares_change."""
|
|
99
|
+
return _call('query', 'get_con_bond_2_shares_change', locals())
|
|
100
|
+
|
|
101
|
+
def get_con_bond_2_shares(htsc_code=None, pub_date=None, exer_begin_date=None, exer_end_date=None, convert_code=None, exchange=None):
|
|
102
|
+
"""Socket equivalent of Insight 6.1.2 query.get_con_bond_2_shares."""
|
|
103
|
+
return _call('query', 'get_con_bond_2_shares', locals())
|
|
104
|
+
|
|
105
|
+
def get_income_statement(htsc_code=None, end_date=None, period=None):
|
|
106
|
+
"""Socket equivalent of Insight 6.1.2 query.get_income_statement."""
|
|
107
|
+
return _call('query', 'get_income_statement', locals())
|
|
108
|
+
|
|
109
|
+
def get_balance_sheet(htsc_code=None, end_date=None, period=None):
|
|
110
|
+
"""Socket equivalent of Insight 6.1.2 query.get_balance_sheet."""
|
|
111
|
+
return _call('query', 'get_balance_sheet', locals())
|
|
112
|
+
|
|
113
|
+
def get_cashflow_statement(htsc_code=None, end_date=None, period=None):
|
|
114
|
+
"""Socket equivalent of Insight 6.1.2 query.get_cashflow_statement."""
|
|
115
|
+
return _call('query', 'get_cashflow_statement', locals())
|
|
116
|
+
|
|
117
|
+
def get_fin_indicator(htsc_code=None, end_date=None, period=None):
|
|
118
|
+
"""Socket equivalent of Insight 6.1.2 query.get_fin_indicator."""
|
|
119
|
+
return _call('query', 'get_fin_indicator', locals())
|
|
120
|
+
|
|
121
|
+
def get_newest_stock_value(htsc_code=None, exchange=None):
|
|
122
|
+
"""Socket equivalent of Insight 6.1.2 query.get_newest_stock_value."""
|
|
123
|
+
return _call('query', 'get_newest_stock_value', locals())
|
|
124
|
+
|
|
125
|
+
def get_company_info(htsc_code=None, name=None):
|
|
126
|
+
"""Socket equivalent of Insight 6.1.2 query.get_company_info."""
|
|
127
|
+
return _call('query', 'get_company_info', locals())
|
|
128
|
+
|
|
129
|
+
def get_shareholder_num(htsc_code=None, name=None, end_date=None):
|
|
130
|
+
"""Socket equivalent of Insight 6.1.2 query.get_shareholder_num."""
|
|
131
|
+
return _call('query', 'get_shareholder_num', locals())
|
|
132
|
+
|
|
133
|
+
def get_additional_share(htsc_code=None, listing_date=None):
|
|
134
|
+
"""Socket equivalent of Insight 6.1.2 query.get_additional_share."""
|
|
135
|
+
return _call('query', 'get_additional_share', locals())
|
|
136
|
+
|
|
137
|
+
def get_allotment_share(htsc_code=None, ini_pub_date=None, is_allot_half_year=None):
|
|
138
|
+
"""Socket equivalent of Insight 6.1.2 query.get_allotment_share."""
|
|
139
|
+
return _call('query', 'get_allotment_share', locals())
|
|
140
|
+
|
|
141
|
+
def get_capital_structure(htsc_code=None, end_date=None):
|
|
142
|
+
"""Socket equivalent of Insight 6.1.2 query.get_capital_structure."""
|
|
143
|
+
return _call('query', 'get_capital_structure', locals())
|
|
144
|
+
|
|
145
|
+
def get_dividend(htsc_code=None, right_reg_date=None, ex_divi_date=None, divi_pay_date=None):
|
|
146
|
+
"""Socket equivalent of Insight 6.1.2 query.get_dividend."""
|
|
147
|
+
return _call('query', 'get_dividend', locals())
|
|
148
|
+
|
|
149
|
+
def get_north_bound(htsc_code=None, trading_day=None):
|
|
150
|
+
"""Socket equivalent of Insight 6.1.2 query.get_north_bound."""
|
|
151
|
+
return _call('query', 'get_north_bound', locals())
|
|
152
|
+
|
|
153
|
+
def get_margin_target(htsc_code=None, exchange=None):
|
|
154
|
+
"""Socket equivalent of Insight 6.1.2 query.get_margin_target."""
|
|
155
|
+
return _call('query', 'get_margin_target', locals())
|
|
156
|
+
|
|
157
|
+
def get_margin_summary(htsc_code=None, trading_day=None):
|
|
158
|
+
"""Socket equivalent of Insight 6.1.2 query.get_margin_summary."""
|
|
159
|
+
return _call('query', 'get_margin_summary', locals())
|
|
160
|
+
|
|
161
|
+
def get_margin_detail(exchange=None, trading_day=None):
|
|
162
|
+
"""Socket equivalent of Insight 6.1.2 query.get_margin_detail."""
|
|
163
|
+
return _call('query', 'get_margin_detail', locals())
|
|
164
|
+
|
|
165
|
+
def get_shareholders_top10(htsc_code=None, change_date=None):
|
|
166
|
+
"""Socket equivalent of Insight 6.1.2 query.get_shareholders_top10."""
|
|
167
|
+
return _call('query', 'get_shareholders_top10', locals())
|
|
168
|
+
|
|
169
|
+
def get_shareholders_floating_top10(htsc_code=None, change_date=None):
|
|
170
|
+
"""Socket equivalent of Insight 6.1.2 query.get_shareholders_floating_top10."""
|
|
171
|
+
return _call('query', 'get_shareholders_floating_top10', locals())
|
|
172
|
+
|
|
173
|
+
def get_tick(htsc_code=None, security_type=None, trading_day=None):
|
|
174
|
+
"""Socket equivalent of Insight 6.1.2 query.get_tick."""
|
|
175
|
+
return _call('query', 'get_tick', locals())
|
|
176
|
+
|
|
177
|
+
def get_adj_factor(htsc_code=None, begin_date=None):
|
|
178
|
+
"""Socket equivalent of Insight 6.1.2 query.get_adj_factor."""
|
|
179
|
+
return _call('query', 'get_adj_factor', locals())
|
|
180
|
+
|
|
181
|
+
def get_locked_shares(htsc_code=None, listing_date=None):
|
|
182
|
+
"""Socket equivalent of Insight 6.1.2 query.get_locked_shares."""
|
|
183
|
+
return _call('query', 'get_locked_shares', locals())
|
|
184
|
+
|
|
185
|
+
def get_frozen_shares(htsc_code=None, freezing_start_date=None):
|
|
186
|
+
"""Socket equivalent of Insight 6.1.2 query.get_frozen_shares."""
|
|
187
|
+
return _call('query', 'get_frozen_shares', locals())
|
|
188
|
+
|
|
189
|
+
def get_hk_industry(htsc_code=None):
|
|
190
|
+
"""Socket equivalent of Insight 6.1.2 query.get_hk_industry."""
|
|
191
|
+
return _call('query', 'get_hk_industry', locals())
|
|
192
|
+
|
|
193
|
+
def get_hk_industry_stocks(industry_code=None):
|
|
194
|
+
"""Socket equivalent of Insight 6.1.2 query.get_hk_industry_stocks."""
|
|
195
|
+
return _call('query', 'get_hk_industry_stocks', locals())
|
|
196
|
+
|
|
197
|
+
def get_hk_daily_basic(htsc_code=None, trading_day=None):
|
|
198
|
+
"""Socket equivalent of Insight 6.1.2 query.get_hk_daily_basic."""
|
|
199
|
+
return _call('query', 'get_hk_daily_basic', locals())
|
|
200
|
+
|
|
201
|
+
def get_hk_stock_valuation(htsc_code=None, trading_day=None):
|
|
202
|
+
"""Socket equivalent of Insight 6.1.2 query.get_hk_stock_valuation."""
|
|
203
|
+
return _call('query', 'get_hk_stock_valuation', locals())
|
|
204
|
+
|
|
205
|
+
def get_hk_stock_basic_info(htsc_code=None, listing_date=None, listing_state=None):
|
|
206
|
+
"""Socket equivalent of Insight 6.1.2 query.get_hk_stock_basic_info."""
|
|
207
|
+
return _call('query', 'get_hk_stock_basic_info', locals())
|
|
208
|
+
|
|
209
|
+
def get_hk_dividend(htsc_code=None, ex_divi_date=None):
|
|
210
|
+
"""Socket equivalent of Insight 6.1.2 query.get_hk_dividend."""
|
|
211
|
+
return _call('query', 'get_hk_dividend', locals())
|
|
212
|
+
|
|
213
|
+
def get_hk_index_mapping(month=None):
|
|
214
|
+
"""Socket equivalent of Insight 6.1.2 query.get_hk_index_mapping."""
|
|
215
|
+
return _call('query', 'get_hk_index_mapping', locals())
|
|
216
|
+
|
|
217
|
+
def get_main_product_info(htsc_code=None, product_code=None, product_level=None):
|
|
218
|
+
"""Socket equivalent of Insight 6.1.2 query.get_main_product_info."""
|
|
219
|
+
return _call('query', 'get_main_product_info', locals())
|
|
220
|
+
|
|
221
|
+
def get_htsc_margin_target():
|
|
222
|
+
"""Socket equivalent of Insight 6.1.2 query.get_htsc_margin_target."""
|
|
223
|
+
return _call('query', 'get_htsc_margin_target', locals())
|
|
224
|
+
|
|
225
|
+
def get_htsc_margin_by_id(htsc_code=None, data_type=None):
|
|
226
|
+
"""Socket equivalent of Insight 6.1.2 query.get_htsc_margin_by_id."""
|
|
227
|
+
return _call('query', 'get_htsc_margin_by_id', locals())
|
|
228
|
+
|
|
229
|
+
def get_htsc_margin_by_type(data_type=None, security_type=None, is_async=False):
|
|
230
|
+
"""Socket equivalent of Insight 6.1.2 query.get_htsc_margin_by_type."""
|
|
231
|
+
return _call('query', 'get_htsc_margin_by_type', locals())
|
|
232
|
+
|
|
233
|
+
def get_index_info(htsc_code=None, trading_day=None):
|
|
234
|
+
"""Socket equivalent of Insight 6.1.2 query.get_index_info."""
|
|
235
|
+
return _call('query', 'get_index_info', locals())
|
|
236
|
+
|
|
237
|
+
def get_all_index(exchange=None, trading_day=None):
|
|
238
|
+
"""Socket equivalent of Insight 6.1.2 query.get_all_index."""
|
|
239
|
+
return _call('query', 'get_all_index', locals())
|
|
240
|
+
|
|
241
|
+
def get_index_component(htsc_code=None, name=None, stock_code=None, trading_day=None):
|
|
242
|
+
"""Socket equivalent of Insight 6.1.2 query.get_index_component."""
|
|
243
|
+
return _call('query', 'get_index_component', locals())
|
|
244
|
+
|
|
245
|
+
def get_index_component_pro(htsc_code=None, name=None, stock_code=None, trading_day=None):
|
|
246
|
+
"""Socket equivalent of Insight 6.1.2 query.get_index_component_pro."""
|
|
247
|
+
return _call('query', 'get_index_component_pro', locals())
|
|
248
|
+
|
|
249
|
+
def get_factors(htsc_code=None, factor_name=None, trading_day=None):
|
|
250
|
+
"""Socket equivalent of Insight 6.1.2 query.get_factors."""
|
|
251
|
+
return _call('query', 'get_factors', locals())
|
|
252
|
+
|
|
253
|
+
def get_fund_info(htsc_code=None, trading_day=None):
|
|
254
|
+
"""Socket equivalent of Insight 6.1.2 query.get_fund_info."""
|
|
255
|
+
return _call('query', 'get_fund_info', locals())
|
|
256
|
+
|
|
257
|
+
def get_fund_target(htsc_code=None, exchange=None, end_date=None):
|
|
258
|
+
"""Socket equivalent of Insight 6.1.2 query.get_fund_target."""
|
|
259
|
+
return _call('query', 'get_fund_target', locals())
|
|
260
|
+
|
|
261
|
+
def get_etf_component(htsc_code=None, pub_date=None, trading_day=None):
|
|
262
|
+
"""Socket equivalent of Insight 6.1.2 query.get_etf_component."""
|
|
263
|
+
return _call('query', 'get_etf_component', locals())
|
|
264
|
+
|
|
265
|
+
def get_public_fund_portfolio(htsc_code=None, name=None, exchange=None, end_date=None):
|
|
266
|
+
"""Socket equivalent of Insight 6.1.2 query.get_public_fund_portfolio."""
|
|
267
|
+
return _call('query', 'get_public_fund_portfolio', locals())
|
|
268
|
+
|
|
269
|
+
def get_etf_redemption(htsc_code=None, exchange=None, trading_day=None):
|
|
270
|
+
"""Socket equivalent of Insight 6.1.2 query.get_etf_redemption."""
|
|
271
|
+
return _call('query', 'get_etf_redemption', locals())
|
|
272
|
+
|
|
273
|
+
def get_basic_info(htsc_code=None):
|
|
274
|
+
"""Socket equivalent of Insight 6.1.2 query.get_basic_info."""
|
|
275
|
+
return _call('query', 'get_basic_info', locals())
|
|
276
|
+
|
|
277
|
+
def get_all_basic_info(security_type=None, exchange=None, today=True):
|
|
278
|
+
"""Socket equivalent of Insight 6.1.2 query.get_all_basic_info."""
|
|
279
|
+
return _call('query', 'get_all_basic_info', locals())
|
|
280
|
+
|
|
281
|
+
def get_news(htsc_code=None, event_ids=None, pub_date=None):
|
|
282
|
+
"""Socket equivalent of Insight 6.1.2 query.get_news."""
|
|
283
|
+
return _call('query', 'get_news', locals())
|
|
284
|
+
|
|
285
|
+
def find_edb_index(name=None):
|
|
286
|
+
"""Socket equivalent of Insight 6.1.2 query.find_edb_index."""
|
|
287
|
+
return _call('query', 'find_edb_index', locals())
|
|
288
|
+
|
|
289
|
+
def edb(indexes=None, pub_date=None):
|
|
290
|
+
"""Socket equivalent of Insight 6.1.2 query.edb."""
|
|
291
|
+
return _call('query', 'edb', locals())
|
|
292
|
+
|
|
293
|
+
def get_rpt_basicinfo_ht(id=None, report_code=None, time=None, title=None, language=None, is_valid=None):
|
|
294
|
+
"""Socket equivalent of Insight 6.1.2 query.get_rpt_basicinfo_ht."""
|
|
295
|
+
return _call('query', 'get_rpt_basicinfo_ht', locals())
|
|
296
|
+
|
|
297
|
+
def get_rpt_stk_ht(report_code=None):
|
|
298
|
+
"""Socket equivalent of Insight 6.1.2 query.get_rpt_stk_ht."""
|
|
299
|
+
return _call('query', 'get_rpt_stk_ht', locals())
|
|
300
|
+
|
|
301
|
+
def get_rpt_industry_ht(report_code=None):
|
|
302
|
+
"""Socket equivalent of Insight 6.1.2 query.get_rpt_industry_ht."""
|
|
303
|
+
return _call('query', 'get_rpt_industry_ht', locals())
|
|
304
|
+
|
|
305
|
+
def get_rpt_author_ht(report_code=None):
|
|
306
|
+
"""Socket equivalent of Insight 6.1.2 query.get_rpt_author_ht."""
|
|
307
|
+
return _call('query', 'get_rpt_author_ht', locals())
|
|
308
|
+
|
|
309
|
+
def get_rpt_annex_ht(report_code=None):
|
|
310
|
+
"""Socket equivalent of Insight 6.1.2 query.get_rpt_annex_ht."""
|
|
311
|
+
return _call('query', 'get_rpt_annex_ht', locals())
|
|
312
|
+
|
|
313
|
+
def get_rpt_stkpredict_ht(report_code=None):
|
|
314
|
+
"""Socket equivalent of Insight 6.1.2 query.get_rpt_stkpredict_ht."""
|
|
315
|
+
return _call('query', 'get_rpt_stkpredict_ht', locals())
|
|
316
|
+
|
|
317
|
+
def get_etf_info(query):
|
|
318
|
+
"""Socket equivalent of Insight 6.1.2 query.get_etf_info."""
|
|
319
|
+
return _call('query', 'get_etf_info', locals())
|
|
320
|
+
|
|
321
|
+
def query_mdcontant(security_idsource_and_types, security_id_list):
|
|
322
|
+
"""Socket equivalent of Insight 6.1.2 query.query_mdcontant."""
|
|
323
|
+
return _call('query', 'query_mdcontant', locals())
|
|
324
|
+
|
|
325
|
+
__all__ = ['query_fin_info', 'get_kline', 'get_derived', 'get_trade_distribution', 'get_chip_distribution', 'get_money_flow', 'get_change_summary', 'get_billboard', 'get_stock_info', 'get_all_stocks_info', 'get_trading_days', 'get_industries', 'get_industry', 'get_industry_stocks', 'get_new_share', 'get_daily_basic', 'get_stock_valuation', 'get_bond_info', 'get_all_bonds', 'get_repo_price', 'get_new_con_bond', 'get_bond_price', 'get_con_bond_redemption', 'get_con_bond_2_shares_change', 'get_con_bond_2_shares', 'get_income_statement', 'get_balance_sheet', 'get_cashflow_statement', 'get_fin_indicator', 'get_newest_stock_value', 'get_company_info', 'get_shareholder_num', 'get_additional_share', 'get_allotment_share', 'get_capital_structure', 'get_dividend', 'get_north_bound', 'get_margin_target', 'get_margin_summary', 'get_margin_detail', 'get_shareholders_top10', 'get_shareholders_floating_top10', 'get_tick', 'get_adj_factor', 'get_locked_shares', 'get_frozen_shares', 'get_hk_industry', 'get_hk_industry_stocks', 'get_hk_daily_basic', 'get_hk_stock_valuation', 'get_hk_stock_basic_info', 'get_hk_dividend', 'get_hk_index_mapping', 'get_main_product_info', 'get_htsc_margin_target', 'get_htsc_margin_by_id', 'get_htsc_margin_by_type', 'get_index_info', 'get_all_index', 'get_index_component', 'get_index_component_pro', 'get_factors', 'get_fund_info', 'get_fund_target', 'get_etf_component', 'get_public_fund_portfolio', 'get_etf_redemption', 'get_basic_info', 'get_all_basic_info', 'get_news', 'find_edb_index', 'edb', 'get_rpt_basicinfo_ht', 'get_rpt_stk_ht', 'get_rpt_industry_ht', 'get_rpt_author_ht', 'get_rpt_annex_ht', 'get_rpt_stkpredict_ht', 'get_etf_info', 'query_mdcontant']
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Insight 6.1.2 signatures. Generated by tools/generate-compat.py."""
|
|
2
|
+
from ._session import call as _call
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def subscribe_tick_by_id(htsc_code, mode='coverage'):
|
|
6
|
+
"""Socket equivalent of Insight 6.1.2 subscribe.subscribe_tick_by_id."""
|
|
7
|
+
return _call('subscribe', 'subscribe_tick_by_id', locals())
|
|
8
|
+
|
|
9
|
+
def subscribe_tick_by_type(query=None, mode='coverage'):
|
|
10
|
+
"""Socket equivalent of Insight 6.1.2 subscribe.subscribe_tick_by_type."""
|
|
11
|
+
return _call('subscribe', 'subscribe_tick_by_type', locals())
|
|
12
|
+
|
|
13
|
+
def subscribe_kline_by_id(htsc_code=None, frequency=None, mode='coverage'):
|
|
14
|
+
"""Socket equivalent of Insight 6.1.2 subscribe.subscribe_kline_by_id."""
|
|
15
|
+
return _call('subscribe', 'subscribe_kline_by_id', locals())
|
|
16
|
+
|
|
17
|
+
def subscribe_kline_by_type(query=None, frequency=None, mode='coverage'):
|
|
18
|
+
"""Socket equivalent of Insight 6.1.2 subscribe.subscribe_kline_by_type."""
|
|
19
|
+
return _call('subscribe', 'subscribe_kline_by_type', locals())
|
|
20
|
+
|
|
21
|
+
def subscribe_trans_and_order_by_id(htsc_code=None, mode='coverage'):
|
|
22
|
+
"""Socket equivalent of Insight 6.1.2 subscribe.subscribe_trans_and_order_by_id."""
|
|
23
|
+
return _call('subscribe', 'subscribe_trans_and_order_by_id', locals())
|
|
24
|
+
|
|
25
|
+
def subscribe_trans_and_order_by_type(query=None, mode='coverage'):
|
|
26
|
+
"""Socket equivalent of Insight 6.1.2 subscribe.subscribe_trans_and_order_by_type."""
|
|
27
|
+
return _call('subscribe', 'subscribe_trans_and_order_by_type', locals())
|
|
28
|
+
|
|
29
|
+
def subscribe_htsc_margin_by_id(htsc_code=None, data_type=None, mode='coverage'):
|
|
30
|
+
"""Socket equivalent of Insight 6.1.2 subscribe.subscribe_htsc_margin_by_id."""
|
|
31
|
+
return _call('subscribe', 'subscribe_htsc_margin_by_id', locals())
|
|
32
|
+
|
|
33
|
+
def subscribe_htsc_margin_by_type(security_type=None, data_type=None, mode='coverage'):
|
|
34
|
+
"""Socket equivalent of Insight 6.1.2 subscribe.subscribe_htsc_margin_by_type."""
|
|
35
|
+
return _call('subscribe', 'subscribe_htsc_margin_by_type', locals())
|
|
36
|
+
|
|
37
|
+
def subscribe_derived(type=None, htsc_code=None, exchange=None, frequency='1min', mode='coverage'):
|
|
38
|
+
"""Socket equivalent of Insight 6.1.2 subscribe.subscribe_derived."""
|
|
39
|
+
return _call('subscribe', 'subscribe_derived', locals())
|
|
40
|
+
|
|
41
|
+
def subscribe_news_by_type(query=None, mode='coverage'):
|
|
42
|
+
"""Socket equivalent of Insight 6.1.2 subscribe.subscribe_news_by_type."""
|
|
43
|
+
return _call('subscribe', 'subscribe_news_by_type', locals())
|
|
44
|
+
|
|
45
|
+
def subscribe_news_by_id(htsc_code=None, mode='coverage'):
|
|
46
|
+
"""Socket equivalent of Insight 6.1.2 subscribe.subscribe_news_by_id."""
|
|
47
|
+
return _call('subscribe', 'subscribe_news_by_id', locals())
|
|
48
|
+
|
|
49
|
+
def sync():
|
|
50
|
+
"""Socket equivalent of Insight 6.1.2 subscribe.sync."""
|
|
51
|
+
line = input()
|
|
52
|
+
if line:
|
|
53
|
+
print("sync: input-->>" + str(line) + ",then exit this sync.")
|
|
54
|
+
|
|
55
|
+
__all__ = ['subscribe_tick_by_id', 'subscribe_tick_by_type', 'subscribe_kline_by_id', 'subscribe_kline_by_type', 'subscribe_trans_and_order_by_id', 'subscribe_trans_and_order_by_type', 'subscribe_htsc_margin_by_id', 'subscribe_htsc_margin_by_type', 'subscribe_derived', 'subscribe_news_by_type', 'subscribe_news_by_id', 'sync']
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Wire v1: 4 byte unsigned big endian length + UTF-8 JSON object."""
|
|
2
|
+
import datetime as dt
|
|
3
|
+
import json
|
|
4
|
+
import math
|
|
5
|
+
import struct
|
|
6
|
+
|
|
7
|
+
MAX_FRAME = 32 * 1024 * 1024
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def wire_value(value):
|
|
11
|
+
if isinstance(value, dt.datetime):
|
|
12
|
+
return {"$datetime": value.isoformat()}
|
|
13
|
+
if isinstance(value, dt.date):
|
|
14
|
+
return {"$datetime": dt.datetime.combine(value, dt.time()).isoformat()}
|
|
15
|
+
if isinstance(value, dict):
|
|
16
|
+
return {str(k): wire_value(v) for k, v in value.items()}
|
|
17
|
+
if isinstance(value, (list, tuple)):
|
|
18
|
+
return [wire_value(v) for v in value]
|
|
19
|
+
if isinstance(value, float) and not math.isfinite(value):
|
|
20
|
+
return None
|
|
21
|
+
if value is None or isinstance(value, (str, int, float, bool)):
|
|
22
|
+
return value
|
|
23
|
+
# numpy scalars, without requiring numpy on the client.
|
|
24
|
+
if hasattr(value, "item"):
|
|
25
|
+
return wire_value(value.item())
|
|
26
|
+
raise TypeError("Unsupported wire value: " + type(value).__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def restore_dates(value):
|
|
30
|
+
if isinstance(value, dict):
|
|
31
|
+
if set(value) == {"$datetime"}:
|
|
32
|
+
return dt.datetime.fromisoformat(value["$datetime"])
|
|
33
|
+
return {k: restore_dates(v) for k, v in value.items()}
|
|
34
|
+
if isinstance(value, list):
|
|
35
|
+
return [restore_dates(v) for v in value]
|
|
36
|
+
return value
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def pack(message):
|
|
40
|
+
payload = json.dumps(wire_value(message), ensure_ascii=False,
|
|
41
|
+
allow_nan=False, separators=(",", ":")).encode("utf-8")
|
|
42
|
+
if not 0 < len(payload) <= MAX_FRAME:
|
|
43
|
+
raise ValueError("Frame exceeds 32 MiB; narrow the query")
|
|
44
|
+
return struct.pack("!I", len(payload)) + payload
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def unpack(payload):
|
|
48
|
+
message = json.loads(payload, parse_constant=lambda _: (_ for _ in ()).throw(ValueError("Invalid JSON number")))
|
|
49
|
+
if not isinstance(message, dict):
|
|
50
|
+
raise ValueError("Message must be an object")
|
|
51
|
+
return message
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def frame_size(header):
|
|
55
|
+
size, = struct.unpack("!I", header)
|
|
56
|
+
if not 0 < size <= MAX_FRAME:
|
|
57
|
+
raise ValueError("Invalid frame length")
|
|
58
|
+
return size
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"""Versioned, non-executable JSON codec for SDK data; no pickle or eval.
|
|
2
|
+
|
|
3
|
+
Every container is tagged, so user dictionary keys cannot collide with tags.
|
|
4
|
+
Pandas is imported only when the compatibility API is used.
|
|
5
|
+
"""
|
|
6
|
+
import base64
|
|
7
|
+
import datetime as dt
|
|
8
|
+
from decimal import Decimal
|
|
9
|
+
import math
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def encode(value):
|
|
13
|
+
import numpy as np
|
|
14
|
+
import pandas as pd
|
|
15
|
+
if value is pd.NA:
|
|
16
|
+
return {'t': 'NA'}
|
|
17
|
+
if value is pd.NaT:
|
|
18
|
+
return {'t': 'NaT'}
|
|
19
|
+
if isinstance(value, pd.DataFrame):
|
|
20
|
+
return {'t': 'frame', 'index': encode(value.index), 'columns': encode(value.columns),
|
|
21
|
+
'series': [encode(value.iloc[:, i]) for i in range(len(value.columns))],
|
|
22
|
+
'attrs': encode(value.attrs)}
|
|
23
|
+
if isinstance(value, pd.Series):
|
|
24
|
+
return {'t': 'series', 'index': encode(value.index), 'name': encode(value.name),
|
|
25
|
+
'dtype': _dtype(value.dtype), 'values': encode(value.tolist()), 'attrs': encode(value.attrs)}
|
|
26
|
+
if isinstance(value, pd.MultiIndex):
|
|
27
|
+
return {'t': 'multiindex', 'levels': [encode(x) for x in value.levels],
|
|
28
|
+
'codes': [x.tolist() for x in value.codes], 'names': encode(value.names),
|
|
29
|
+
'sortorder': value.sortorder}
|
|
30
|
+
if isinstance(value, pd.RangeIndex):
|
|
31
|
+
return {'t': 'rangeindex', 'start': value.start, 'stop': value.stop,
|
|
32
|
+
'step': value.step, 'name': encode(value.name)}
|
|
33
|
+
if isinstance(value, pd.Index):
|
|
34
|
+
return {'t': 'index', 'values': encode(value.tolist()), 'dtype': _dtype(value.dtype),
|
|
35
|
+
'name': encode(value.name), 'freq': getattr(value, 'freqstr', None)}
|
|
36
|
+
if isinstance(value, pd.Timestamp):
|
|
37
|
+
return {'t': 'timestamp', 'v': value.isoformat(), 'tz': str(value.tz) if value.tz else None}
|
|
38
|
+
if isinstance(value, pd.Timedelta):
|
|
39
|
+
return {'t': 'timedelta_ns', 'v': value.value}
|
|
40
|
+
if isinstance(value, np.ndarray):
|
|
41
|
+
if value.dtype.hasobject:
|
|
42
|
+
return {'t': 'object_array', 'shape': list(value.shape), 'v': encode(value.ravel().tolist())}
|
|
43
|
+
if value.dtype.fields:
|
|
44
|
+
raise TypeError('Structured numpy arrays are not supported by codec v1')
|
|
45
|
+
return {'t': 'array', 'dtype': value.dtype.str, 'shape': list(value.shape),
|
|
46
|
+
'v': base64.b64encode(value.tobytes(order='C')).decode('ascii')}
|
|
47
|
+
if isinstance(value, np.generic):
|
|
48
|
+
return {'t': 'scalar', 'v': encode(np.asarray(value))}
|
|
49
|
+
if isinstance(value, dt.datetime):
|
|
50
|
+
return {'t': 'datetime', 'v': value.isoformat(), 'fold': value.fold}
|
|
51
|
+
if isinstance(value, dt.date):
|
|
52
|
+
return {'t': 'date', 'v': value.isoformat()}
|
|
53
|
+
if isinstance(value, dt.time):
|
|
54
|
+
return {'t': 'time', 'v': value.isoformat(), 'fold': value.fold}
|
|
55
|
+
if isinstance(value, dt.timedelta):
|
|
56
|
+
return {'t': 'timedelta', 'days': value.days, 'seconds': value.seconds, 'microseconds': value.microseconds}
|
|
57
|
+
if isinstance(value, Decimal):
|
|
58
|
+
return {'t': 'decimal', 'v': str(value)}
|
|
59
|
+
if isinstance(value, bytes):
|
|
60
|
+
return {'t': 'bytes', 'v': base64.b64encode(value).decode('ascii')}
|
|
61
|
+
if isinstance(value, dict):
|
|
62
|
+
return {'t': 'dict', 'v': [[encode(k), encode(v)] for k, v in value.items()]}
|
|
63
|
+
if isinstance(value, (list, tuple)):
|
|
64
|
+
return {'t': 'tuple' if isinstance(value, tuple) else 'list', 'v': [encode(v) for v in value]}
|
|
65
|
+
if isinstance(value, float) and not math.isfinite(value):
|
|
66
|
+
return {'t': 'float', 'v': 'nan' if math.isnan(value) else 'inf' if value > 0 else '-inf'}
|
|
67
|
+
if value is None or type(value) in (str, int, float, bool):
|
|
68
|
+
return value
|
|
69
|
+
raise TypeError('Unsupported SDK data type: ' + type(value).__name__)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _dtype(dtype):
|
|
73
|
+
import pandas as pd
|
|
74
|
+
if isinstance(dtype, pd.CategoricalDtype):
|
|
75
|
+
return {'category': encode(dtype.categories), 'ordered': dtype.ordered}
|
|
76
|
+
return str(dtype)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def _restore_dtype(dtype):
|
|
80
|
+
import pandas as pd
|
|
81
|
+
if isinstance(dtype, dict):
|
|
82
|
+
return pd.CategoricalDtype(decode(dtype['category']), ordered=dtype['ordered'])
|
|
83
|
+
return dtype
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def decode(value):
|
|
87
|
+
import numpy as np
|
|
88
|
+
import pandas as pd
|
|
89
|
+
if value is None or type(value) in (str, int, float, bool):
|
|
90
|
+
return value
|
|
91
|
+
if not isinstance(value, dict) or 't' not in value:
|
|
92
|
+
raise ValueError('Invalid SDK codec value')
|
|
93
|
+
tag = value['t']
|
|
94
|
+
if tag == 'dict':
|
|
95
|
+
return {decode(k): decode(v) for k, v in value['v']}
|
|
96
|
+
if tag in ('list', 'tuple'):
|
|
97
|
+
result = [decode(v) for v in value['v']]
|
|
98
|
+
return tuple(result) if tag == 'tuple' else result
|
|
99
|
+
if tag == 'frame':
|
|
100
|
+
columns, index = decode(value['columns']), decode(value['index'])
|
|
101
|
+
series = [decode(v) for v in value['series']]
|
|
102
|
+
if len(columns) != len(series) or any(not s.index.equals(index) for s in series):
|
|
103
|
+
raise ValueError('Invalid DataFrame axes')
|
|
104
|
+
result = pd.concat(series, axis=1) if series else pd.DataFrame(index=index)
|
|
105
|
+
result.index, result.columns = index, columns
|
|
106
|
+
result.attrs = decode(value['attrs'])
|
|
107
|
+
return result
|
|
108
|
+
if tag == 'series':
|
|
109
|
+
result = pd.Series(decode(value['values']), index=decode(value['index']),
|
|
110
|
+
dtype=_restore_dtype(value['dtype']), name=decode(value['name']))
|
|
111
|
+
result.attrs = decode(value['attrs'])
|
|
112
|
+
return result
|
|
113
|
+
if tag == 'multiindex':
|
|
114
|
+
return pd.MultiIndex(levels=[decode(x) for x in value['levels']], codes=value['codes'],
|
|
115
|
+
names=decode(value['names']), sortorder=value['sortorder'])
|
|
116
|
+
if tag == 'rangeindex':
|
|
117
|
+
return pd.RangeIndex(value['start'], value['stop'], value['step'], name=decode(value['name']))
|
|
118
|
+
if tag == 'index':
|
|
119
|
+
result = pd.Index(decode(value['values']), dtype=_restore_dtype(value['dtype']), name=decode(value['name']))
|
|
120
|
+
if value.get('freq') is not None:
|
|
121
|
+
result.freq = value['freq']
|
|
122
|
+
return result
|
|
123
|
+
if tag == 'timestamp':
|
|
124
|
+
result = pd.Timestamp(value['v'])
|
|
125
|
+
return result.tz_convert(value['tz']) if value['tz'] else result
|
|
126
|
+
if tag == 'timedelta_ns':
|
|
127
|
+
return pd.Timedelta(value['v'], unit='ns')
|
|
128
|
+
if tag == 'NaT':
|
|
129
|
+
return pd.NaT
|
|
130
|
+
if tag == 'NA':
|
|
131
|
+
return pd.NA
|
|
132
|
+
if tag == 'datetime':
|
|
133
|
+
return dt.datetime.fromisoformat(value['v']).replace(fold=value.get('fold', 0))
|
|
134
|
+
if tag == 'date':
|
|
135
|
+
return dt.date.fromisoformat(value['v'])
|
|
136
|
+
if tag == 'time':
|
|
137
|
+
return dt.time.fromisoformat(value['v']).replace(fold=value.get('fold', 0))
|
|
138
|
+
if tag == 'timedelta':
|
|
139
|
+
return dt.timedelta(days=value['days'], seconds=value['seconds'], microseconds=value['microseconds'])
|
|
140
|
+
if tag == 'decimal':
|
|
141
|
+
return Decimal(value['v'])
|
|
142
|
+
if tag == 'float':
|
|
143
|
+
if value['v'] not in ('nan', 'inf', '-inf'):
|
|
144
|
+
raise ValueError('Invalid non-finite float')
|
|
145
|
+
return float(value['v'])
|
|
146
|
+
if tag == 'bytes':
|
|
147
|
+
return base64.b64decode(value['v'], validate=True)
|
|
148
|
+
if tag == 'scalar':
|
|
149
|
+
return decode(value['v'])[()]
|
|
150
|
+
if tag == 'array':
|
|
151
|
+
dtype = np.dtype(value['dtype'])
|
|
152
|
+
if dtype.hasobject or dtype.fields:
|
|
153
|
+
raise ValueError('Unsafe numpy dtype')
|
|
154
|
+
return np.frombuffer(base64.b64decode(value['v'], validate=True), dtype=dtype).reshape(value['shape']).copy()
|
|
155
|
+
if tag == 'object_array':
|
|
156
|
+
data = decode(value['v'])
|
|
157
|
+
result = np.empty(len(data), dtype=object)
|
|
158
|
+
result[:] = data
|
|
159
|
+
return result.reshape(value['shape'])
|
|
160
|
+
raise ValueError('Unknown SDK codec tag: ' + str(tag))
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: insight-proxy-client
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Python client for insight_proxy with Insight 6.1.2 compatible data APIs
|
|
5
|
+
Classifier: Programming Language :: Python :: 3
|
|
6
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Requires-Python: >=3.9
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: pandas<3,>=1.5
|
|
14
|
+
Requires-Dist: numpy<3,>=1.23
|
|
15
|
+
|
|
16
|
+
# insight-proxy-client
|
|
17
|
+
|
|
18
|
+
Python client for the **insight_proxy** TCP service. Data APIs target **Insight 6.1.2**;
|
|
19
|
+
the import namespace is `insight_proxy.com.insight`.
|
|
20
|
+
This is an independent adapter, not the official Insight SDK.
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
```shell
|
|
25
|
+
python -m pip install insight-proxy-client==0.0.1
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Supports Python 3.9–3.12. pandas and NumPy are installed as dependencies.
|
|
29
|
+
The client does not require the vendor SDK or native DLLs. A separately deployed
|
|
30
|
+
insight_proxy server with Insight 6.1.2 and valid data access is required.
|
|
31
|
+
This package does not include the server executable or account credentials.
|
|
32
|
+
|
|
33
|
+
## Quick start
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from datetime import datetime
|
|
37
|
+
from insight_proxy.com.insight import Client
|
|
38
|
+
from insight_proxy.com.insight.market_service import market_service
|
|
39
|
+
|
|
40
|
+
class Market(market_service):
|
|
41
|
+
def on_subscribe_tick(self, result):
|
|
42
|
+
print(result)
|
|
43
|
+
|
|
44
|
+
def on_query_response(self, result):
|
|
45
|
+
print(result)
|
|
46
|
+
|
|
47
|
+
# Both Client() and Client(Market()) are supported.
|
|
48
|
+
# Defaults: host="127.0.0.1", port=8765, token="123456".
|
|
49
|
+
# The token must match the server configuration.
|
|
50
|
+
with Client(Market()) as client:
|
|
51
|
+
frame = client.query.get_kline(
|
|
52
|
+
"601688.SH",
|
|
53
|
+
[datetime(2026, 10, 19), datetime(2026, 10, 26, 23, 59, 59)],
|
|
54
|
+
frequency="daily", fq="none",
|
|
55
|
+
)
|
|
56
|
+
print(frame)
|
|
57
|
+
client.subscribe.subscribe_tick_by_id(["601688.SH"], mode="coverage")
|
|
58
|
+
input("Press Enter to close the subscription session.\n")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Construction does not open a connection. Use `client.connect()` / `client.close()`
|
|
62
|
+
or a `with` block. Override connection settings using keyword arguments:
|
|
63
|
+
`Client(Market(), host="127.0.0.1", port=8765, token="your-server-token")`.
|
|
64
|
+
Example dates are fixed historical values; select dates and instruments available
|
|
65
|
+
to your server account.
|
|
66
|
+
|
|
67
|
+
## Migrating SDK data calls
|
|
68
|
+
|
|
69
|
+
Replace `insight_python.com.insight` imports with `insight_proxy.com.insight`.
|
|
70
|
+
Replace SDK login and initialization with the proxy connection:
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from insight_proxy.com.insight import connect, close, query, subscribe, playback
|
|
74
|
+
|
|
75
|
+
connect() # or connect(Market(), host="...", port=8765, token="...")
|
|
76
|
+
try:
|
|
77
|
+
result = query.get_kline() # retains the SDK's missing-argument response
|
|
78
|
+
finally:
|
|
79
|
+
close()
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The compatibility layer exposes 80 query functions, 11 data subscription functions,
|
|
83
|
+
local `subscribe.sync()`, two playback functions and the `market_service` callbacks.
|
|
84
|
+
It preserves Insight 6.1.2 data function signatures, DataFrame/Series values,
|
|
85
|
+
column ordering, indexes, dtypes, datetime values, tuples and callback names.
|
|
86
|
+
SDK login/logging helpers and native gateway types are outside this client API.
|
|
87
|
+
One compatibility session exclusively owns the server's native SDK login.
|
|
88
|
+
Native SDK permissions and availability still determine which requests succeed.
|
|
89
|
+
|
|
90
|
+
Do not install the older combined `insight_proxy` distribution alongside this
|
|
91
|
+
client package in the same environment: they provide the same import namespace.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
insight_proxy/__init__.py,sha256=tF1vGxFCs6nJieYSvQR7B8ygocwtyE43VdhhgE3_-o4,147
|
|
2
|
+
insight_proxy/client.py,sha256=eFGaFVnIssNb5ojRcbcs6geEOlDagCT2L9blIWF7xXc,7047
|
|
3
|
+
insight_proxy/protocol.py,sha256=Qbs8t31I5MOZCAzSiVbU_koHvn7LRC-Q8B9_rKgn56U,2074
|
|
4
|
+
insight_proxy/typed_codec.py,sha256=dWvYqjBdo-PqPtY48JeNf0ndV0aSnFoPRnUyqBXN3Sg,7693
|
|
5
|
+
insight_proxy/com/__init__.py,sha256=lyzWltItjQ8xZ-YeffVvPEdwUJ6tOdIUJKGCptvpi9s,44
|
|
6
|
+
insight_proxy/com/insight/__init__.py,sha256=CgWUNP8zpC_T6eHRBuUP5R-vVOb4W8fMxxq0dzyEMvQ,454
|
|
7
|
+
insight_proxy/com/insight/_contract.py,sha256=lwYNV5pamrEA_2oMyK5hGEWZAmyJs-ZfNv7ZyICnUvE,7053
|
|
8
|
+
insight_proxy/com/insight/_session.py,sha256=c96hjWpy9nfNcjnxRNrxcIY4L7MB7sJYIZdWEzUCLT0,7333
|
|
9
|
+
insight_proxy/com/insight/market_service.py,sha256=fN2rmHKr_uGrZpZTGbGdR16FTT2Tc3u58A850yC4jDM,929
|
|
10
|
+
insight_proxy/com/insight/playback.py,sha256=fW1PaHsfPL0pHN3Gi56Nj8-g6LX0d3H32gV6wVn1lVw,615
|
|
11
|
+
insight_proxy/com/insight/query.py,sha256=JGunc3AAPtLW0_EKqQYDWZ_BePy-mi-sl9A1RXVr0LA,17556
|
|
12
|
+
insight_proxy/com/insight/subscribe.py,sha256=SNmFmNK1fzZPDyxO0CIN6ZmParBT4DwZ_vQHqOLpkec,3106
|
|
13
|
+
insight_proxy_client-0.0.1.dist-info/METADATA,sha256=vJix0U7BxBBm-Og6HUuvC1cH0d_ewjyFgnigXEvQEts,3626
|
|
14
|
+
insight_proxy_client-0.0.1.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
15
|
+
insight_proxy_client-0.0.1.dist-info/top_level.txt,sha256=PFcyp7boXpP2NXvMo99R4BWLIHbNd4zb4nqEOLKJOn8,14
|
|
16
|
+
insight_proxy_client-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
insight_proxy
|