tradingapi 0.3.10__tar.gz → 0.3.11__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.
- {tradingapi-0.3.10 → tradingapi-0.3.11}/PKG-INFO +1 -1
- {tradingapi-0.3.10 → tradingapi-0.3.11}/pyproject.toml +1 -1
- tradingapi-0.3.11/tests/test_fivepaisa_stream_reconnect.py +135 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi/fivepaisa.py +86 -43
- tradingapi-0.3.11/tradingapi/ib.py +864 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi/market_data_exchanges.py +1 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi/utils.py +34 -1
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi.egg-info/PKG-INFO +1 -1
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi.egg-info/SOURCES.txt +2 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/README.md +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/setup.cfg +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tests/test_broker_side_terminal_order.py +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tests/test_calculate_delta_realtime_quotes.py +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tests/test_find_option_with_delta.py +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi/__init__.py +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi/allocation.py +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi/attribution.py +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi/broker_base.py +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi/config/commissions_20241216.yaml +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi/config/config_sample.yaml +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi/config.py +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi/dhan.py +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi/error_handling.py +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi/exceptions.py +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi/flattrade.py +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi/globals.py +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi/icicidirect.py +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi/icicidirect_generate_session.py +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi/proxy_utils.py +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi/shoonya.py +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi/span.py +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi.egg-info/dependency_links.txt +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi.egg-info/entry_points.txt +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi.egg-info/requires.txt +0 -0
- {tradingapi-0.3.10 → tradingapi-0.3.11}/tradingapi.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import sys
|
|
3
|
+
import threading
|
|
4
|
+
import time
|
|
5
|
+
from unittest.mock import patch
|
|
6
|
+
|
|
7
|
+
sys.path.insert(0, "/home/psharma/onedrive/code/tradingapi2")
|
|
8
|
+
|
|
9
|
+
from tradingapi.broker_base import Brokers
|
|
10
|
+
from tradingapi.fivepaisa import FivePaisa
|
|
11
|
+
from tradingapi import fivepaisa as fivepaisa_module
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class _SocketState:
|
|
15
|
+
connected = True
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class _WebSocket:
|
|
19
|
+
def __init__(self, sent_requests):
|
|
20
|
+
self.sock = _SocketState()
|
|
21
|
+
self.on_open = None
|
|
22
|
+
self.sent_requests = sent_requests
|
|
23
|
+
|
|
24
|
+
def send(self, payload):
|
|
25
|
+
self.sent_requests.append(json.loads(payload))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class _DelayedStreamingApi:
|
|
29
|
+
def __init__(self, connect_started, allow_connect, stop_stream):
|
|
30
|
+
self.ws = None
|
|
31
|
+
self.connect_started = connect_started
|
|
32
|
+
self.allow_connect = allow_connect
|
|
33
|
+
self.stop_stream = stop_stream
|
|
34
|
+
self.connect_calls = 0
|
|
35
|
+
self.sent_requests = []
|
|
36
|
+
|
|
37
|
+
def Request_Feed(self, feed_type, operation, symbols):
|
|
38
|
+
return {"feed": feed_type, "operation": operation, "symbols": symbols}
|
|
39
|
+
|
|
40
|
+
def connect(self, _request):
|
|
41
|
+
self.connect_calls += 1
|
|
42
|
+
self.connect_started.set()
|
|
43
|
+
assert self.allow_connect.wait(timeout=2)
|
|
44
|
+
self.ws = _WebSocket(self.sent_requests)
|
|
45
|
+
|
|
46
|
+
def error_data(self, callback):
|
|
47
|
+
self.error_callback = callback
|
|
48
|
+
|
|
49
|
+
def receive_data(self, callback):
|
|
50
|
+
callback(self.ws, json.dumps({"Token": 1, "LastRate": 100.0}))
|
|
51
|
+
self.stop_stream.wait(timeout=2)
|
|
52
|
+
|
|
53
|
+
def close_data(self):
|
|
54
|
+
if self.ws is not None:
|
|
55
|
+
self.ws.sock.connected = False
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _make_broker(api, fresh_login):
|
|
59
|
+
broker = object.__new__(FivePaisa)
|
|
60
|
+
broker.api = api
|
|
61
|
+
broker.broker = Brokers.FIVEPAISA
|
|
62
|
+
broker.account_key = broker.broker.name
|
|
63
|
+
broker.subscribe_thread = None
|
|
64
|
+
broker.subscribed_symbols = []
|
|
65
|
+
broker._stream_reconnect_lock = threading.Lock()
|
|
66
|
+
broker._stream_reconnect_serial_lock = threading.Lock()
|
|
67
|
+
broker._stream_subscriptions_lock = threading.Lock()
|
|
68
|
+
broker._stream_reconnect_active = False
|
|
69
|
+
broker._suppress_stream_reconnect = False
|
|
70
|
+
broker._last_stream_tick_ts = time.time() - 1
|
|
71
|
+
broker._fp_susertoken_path = "/tmp/fivepaisa-token"
|
|
72
|
+
broker._fp_restore_session_from_token = lambda _path: True
|
|
73
|
+
broker._fp_fresh_login = fresh_login
|
|
74
|
+
broker._wait_for_stream_request_rate_limit = lambda: None
|
|
75
|
+
broker.map_exchange_for_api = lambda _symbol, _exchange: "N"
|
|
76
|
+
broker.map_exchange_for_db = lambda _symbol, exchange: exchange
|
|
77
|
+
broker.exchange_mappings = {
|
|
78
|
+
"N": {
|
|
79
|
+
"symbol_map": {"TEST": 1, "TEST2": 2},
|
|
80
|
+
"symbol_map_reversed": {1: "TEST", 2: "TEST2"},
|
|
81
|
+
"exchangetype_map": {"TEST": "C", "TEST2": "C"},
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return broker
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_reconnect_waits_for_startup_and_coalesces_queued_callers():
|
|
88
|
+
connect_started = threading.Event()
|
|
89
|
+
allow_connect = threading.Event()
|
|
90
|
+
stop_stream = threading.Event()
|
|
91
|
+
api = _DelayedStreamingApi(connect_started, allow_connect, stop_stream)
|
|
92
|
+
fresh_login_calls = []
|
|
93
|
+
broker = _make_broker(api, lambda path: fresh_login_calls.append(path))
|
|
94
|
+
errors = []
|
|
95
|
+
|
|
96
|
+
def start_stream(symbol):
|
|
97
|
+
try:
|
|
98
|
+
broker.start_quotes_streaming("s", [symbol], exchange="NSE")
|
|
99
|
+
except Exception as exc:
|
|
100
|
+
errors.append(exc)
|
|
101
|
+
|
|
102
|
+
first_caller = threading.Thread(target=start_stream, args=("TEST",))
|
|
103
|
+
second_caller = threading.Thread(target=start_stream, args=("TEST2",))
|
|
104
|
+
with patch.object(fivepaisa_module.config, "get", return_value="/tmp/fivepaisa-token"):
|
|
105
|
+
first_caller.start()
|
|
106
|
+
assert connect_started.wait(timeout=2)
|
|
107
|
+
second_caller.start()
|
|
108
|
+
time.sleep(0.05)
|
|
109
|
+
allow_connect.set()
|
|
110
|
+
first_caller.join(timeout=2)
|
|
111
|
+
second_caller.join(timeout=2)
|
|
112
|
+
broker.start_quotes_streaming("s", ["TEST"], exchange="NSE")
|
|
113
|
+
|
|
114
|
+
stop_stream.set()
|
|
115
|
+
if broker.subscribe_thread is not None:
|
|
116
|
+
broker.subscribe_thread.join(timeout=2)
|
|
117
|
+
|
|
118
|
+
assert not first_caller.is_alive()
|
|
119
|
+
assert not second_caller.is_alive()
|
|
120
|
+
assert not errors
|
|
121
|
+
assert api.connect_calls == 1
|
|
122
|
+
assert fresh_login_calls == ["/tmp/fivepaisa-token"]
|
|
123
|
+
assert broker.subscribed_symbols == ["TEST", "TEST2"]
|
|
124
|
+
assert api.sent_requests == [
|
|
125
|
+
{
|
|
126
|
+
"feed": "mf",
|
|
127
|
+
"operation": "s",
|
|
128
|
+
"symbols": [{"Exch": "N", "ExchType": "C", "ScripCode": 2}],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"feed": "mf",
|
|
132
|
+
"operation": "s",
|
|
133
|
+
"symbols": [{"Exch": "N", "ExchType": "C", "ScripCode": 1}],
|
|
134
|
+
},
|
|
135
|
+
]
|
|
@@ -351,6 +351,7 @@ class FivePaisa(BrokerBase):
|
|
|
351
351
|
self._fp_fresh_login: Optional[Callable[[str], None]] = None
|
|
352
352
|
self._stream_reconnect_lock = threading.Lock()
|
|
353
353
|
self._stream_reconnect_serial_lock = threading.Lock()
|
|
354
|
+
self._stream_subscriptions_lock = threading.Lock()
|
|
354
355
|
self._stream_reconnect_active = False
|
|
355
356
|
self._suppress_stream_reconnect = False
|
|
356
357
|
self._last_stream_tick_ts = 0.0
|
|
@@ -3392,24 +3393,31 @@ class FivePaisa(BrokerBase):
|
|
|
3392
3393
|
context = create_error_context(date_string=date_string, date_string_type=type(date_string))
|
|
3393
3394
|
raise ValidationError("Invalid date string", context)
|
|
3394
3395
|
|
|
3396
|
+
def _stale_market_open_timestamp() -> str:
|
|
3397
|
+
now_ist = get_tradingapi_now()
|
|
3398
|
+
if now_ist.tzinfo is not None:
|
|
3399
|
+
now_ist = now_ist.astimezone(dt.timezone(dt.timedelta(hours=5, minutes=30))).replace(tzinfo=None)
|
|
3400
|
+
market_open = dt.datetime.combine(now_ist.date(), dt.time(9, 15))
|
|
3401
|
+
return market_open.strftime("%Y-%m-%d %H:%M:%S")
|
|
3402
|
+
|
|
3395
3403
|
# Extract the timestamp using regex (allow negative for .NET min-date, treated as invalid)
|
|
3396
3404
|
match = re.search(r"/Date\((-?\d+)\)/", date_string)
|
|
3397
3405
|
if not match:
|
|
3398
3406
|
trading_logger.log_warning(
|
|
3399
3407
|
"Invalid date format", {"date_string": date_string, "expected_format": "/Date(milliseconds)/"}
|
|
3400
3408
|
)
|
|
3401
|
-
return
|
|
3409
|
+
return _stale_market_open_timestamp()
|
|
3402
3410
|
|
|
3403
3411
|
try:
|
|
3404
3412
|
# Convert the timestamp from milliseconds to seconds
|
|
3405
3413
|
timestamp_ms = int(match.group(1))
|
|
3406
|
-
# Treat negative timestamps (e.g. .NET min date) as invalid; substitute
|
|
3414
|
+
# Treat negative timestamps (e.g. .NET min date) as invalid; substitute market-open stale time
|
|
3407
3415
|
if timestamp_ms < 0:
|
|
3408
3416
|
trading_logger.log_info(
|
|
3409
3417
|
"Invalid date (negative timestamp)",
|
|
3410
3418
|
{"date_string": date_string, "expected_format": "/Date(milliseconds)/"},
|
|
3411
3419
|
)
|
|
3412
|
-
return
|
|
3420
|
+
return _stale_market_open_timestamp()
|
|
3413
3421
|
timestamp_s = timestamp_ms / 1000
|
|
3414
3422
|
|
|
3415
3423
|
# Convert to UTC datetime
|
|
@@ -3429,18 +3437,28 @@ class FivePaisa(BrokerBase):
|
|
|
3429
3437
|
return result
|
|
3430
3438
|
|
|
3431
3439
|
except (ValueError, OverflowError) as e:
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3440
|
+
trading_logger.log_warning(
|
|
3441
|
+
"Error converting timestamp; using market-open stale timestamp",
|
|
3442
|
+
{
|
|
3443
|
+
"date_string": date_string,
|
|
3444
|
+
"timestamp_ms": timestamp_ms if "timestamp_ms" in locals() else None,
|
|
3445
|
+
"error": str(e),
|
|
3446
|
+
},
|
|
3436
3447
|
)
|
|
3437
|
-
|
|
3448
|
+
return _stale_market_open_timestamp()
|
|
3438
3449
|
|
|
3439
3450
|
except (ValidationError, DataError):
|
|
3440
|
-
|
|
3451
|
+
trading_logger.log_warning(
|
|
3452
|
+
"Validation/data error converting timestamp; using market-open stale timestamp",
|
|
3453
|
+
{"date_string": date_string},
|
|
3454
|
+
)
|
|
3455
|
+
return _stale_market_open_timestamp()
|
|
3441
3456
|
except Exception as e:
|
|
3442
|
-
|
|
3443
|
-
|
|
3457
|
+
trading_logger.log_warning(
|
|
3458
|
+
"Unexpected error converting timestamp; using market-open stale timestamp",
|
|
3459
|
+
{"date_string": date_string, "error": str(e)},
|
|
3460
|
+
)
|
|
3461
|
+
return _stale_market_open_timestamp()
|
|
3444
3462
|
|
|
3445
3463
|
@log_execution_time
|
|
3446
3464
|
@validate_inputs(
|
|
@@ -3760,7 +3778,14 @@ class FivePaisa(BrokerBase):
|
|
|
3760
3778
|
if json_data.get("TickDt"):
|
|
3761
3779
|
price.timestamp = self.convert_to_ist(json_data["TickDt"])
|
|
3762
3780
|
else:
|
|
3763
|
-
|
|
3781
|
+
now_ist = get_tradingapi_now()
|
|
3782
|
+
if now_ist.tzinfo is not None:
|
|
3783
|
+
now_ist = now_ist.astimezone(dt.timezone(dt.timedelta(hours=5, minutes=30))).replace(
|
|
3784
|
+
tzinfo=None
|
|
3785
|
+
)
|
|
3786
|
+
price.timestamp = dt.datetime.combine(now_ist.date(), dt.time(9, 15)).strftime(
|
|
3787
|
+
"%Y-%m-%d %H:%M:%S"
|
|
3788
|
+
)
|
|
3764
3789
|
prices[resolved_symbol] = price
|
|
3765
3790
|
return price
|
|
3766
3791
|
except Exception as e:
|
|
@@ -3939,9 +3964,13 @@ class FivePaisa(BrokerBase):
|
|
|
3939
3964
|
and is_ws_connected(ws)
|
|
3940
3965
|
)
|
|
3941
3966
|
|
|
3942
|
-
def reconnect_stream():
|
|
3967
|
+
def reconnect_stream() -> bool:
|
|
3943
3968
|
"""Start a fresh websocket and restore all subscriptions."""
|
|
3944
3969
|
with self._stream_reconnect_serial_lock:
|
|
3970
|
+
# Another caller may have restored the stream while this caller
|
|
3971
|
+
# waited for the serial reconnect lock.
|
|
3972
|
+
if has_live_stream():
|
|
3973
|
+
return False
|
|
3945
3974
|
previous_tick_ts = self._last_stream_tick_ts
|
|
3946
3975
|
try:
|
|
3947
3976
|
self._suppress_stream_reconnect = True
|
|
@@ -3970,7 +3999,9 @@ class FivePaisa(BrokerBase):
|
|
|
3970
3999
|
# rejects streaming on a token-restored session — subscriptions are accepted
|
|
3971
4000
|
# but no ticks are ever delivered. fresh_fn gets a genuine new session.
|
|
3972
4001
|
fresh_fn(token_path)
|
|
3973
|
-
|
|
4002
|
+
with self._stream_subscriptions_lock:
|
|
4003
|
+
subscribed_symbols = list(self.subscribed_symbols)
|
|
4004
|
+
req_list_full = expand_symbols_to_request(subscribed_symbols)
|
|
3974
4005
|
if not req_list_full:
|
|
3975
4006
|
context = create_error_context(operation=operation, symbols=symbols, exchange=exchange)
|
|
3976
4007
|
raise MarketDataError("No symbols to reconnect after socket closure", context)
|
|
@@ -3982,31 +4013,34 @@ class FivePaisa(BrokerBase):
|
|
|
3982
4013
|
copy.deepcopy(self.api.Request_Feed("oi", "s", oi_req_list_full))
|
|
3983
4014
|
] if oi_req_list_full else []
|
|
3984
4015
|
reconnect_started_ts = time.time()
|
|
3985
|
-
|
|
4016
|
+
stream_thread = threading.Thread(
|
|
3986
4017
|
target=connect_and_receive,
|
|
3987
4018
|
args=(req_data_full, extra_req_data_full),
|
|
3988
4019
|
name="MarketDataStreamer",
|
|
3989
4020
|
)
|
|
3990
|
-
self.subscribe_thread
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4021
|
+
self.subscribe_thread = stream_thread
|
|
4022
|
+
stream_thread.start()
|
|
4023
|
+
should_verify_ticks = previous_tick_ts > 0 and bool(subscribed_symbols)
|
|
4024
|
+
deadline = time.time() + _stream_reconnect_verify_timeout_secs
|
|
4025
|
+
while time.time() < deadline:
|
|
4026
|
+
if should_verify_ticks and self._last_stream_tick_ts >= reconnect_started_ts:
|
|
4027
|
+
break
|
|
4028
|
+
if not should_verify_ticks and has_live_stream():
|
|
4029
|
+
break
|
|
4030
|
+
if not stream_thread.is_alive():
|
|
4031
|
+
raise BrokerConnectionError("WebSocket thread died during reconnect verification")
|
|
4032
|
+
time.sleep(0.1)
|
|
4033
|
+
else:
|
|
4034
|
+
if should_verify_ticks:
|
|
4001
4035
|
raise BrokerConnectionError(
|
|
4002
4036
|
"WebSocket reconnected but remained silent after reconnect verification"
|
|
4003
4037
|
)
|
|
4004
|
-
|
|
4005
|
-
time.sleep(2)
|
|
4038
|
+
raise BrokerConnectionError("WebSocket did not connect during reconnect verification")
|
|
4006
4039
|
trading_logger.log_info(
|
|
4007
4040
|
"Reconnected after socket closure",
|
|
4008
|
-
{"subscribed_count": len(
|
|
4041
|
+
{"subscribed_count": len(subscribed_symbols), "verified_ticks": should_verify_ticks},
|
|
4009
4042
|
)
|
|
4043
|
+
return True
|
|
4010
4044
|
|
|
4011
4045
|
def send_stream_request(req_data):
|
|
4012
4046
|
"""Send an incremental subscribe/unsubscribe over an existing websocket."""
|
|
@@ -4079,17 +4113,21 @@ class FivePaisa(BrokerBase):
|
|
|
4079
4113
|
def update_current_subscriptions(operation, symbols):
|
|
4080
4114
|
"""Update current subscriptions."""
|
|
4081
4115
|
try:
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
|
|
4085
|
-
"Symbols subscribed"
|
|
4086
|
-
|
|
4087
|
-
|
|
4088
|
-
|
|
4089
|
-
|
|
4090
|
-
|
|
4091
|
-
|
|
4092
|
-
)
|
|
4116
|
+
with self._stream_subscriptions_lock:
|
|
4117
|
+
if operation == "s":
|
|
4118
|
+
self.subscribed_symbols = list(dict.fromkeys([*self.subscribed_symbols, *symbols]))
|
|
4119
|
+
message = "Symbols subscribed"
|
|
4120
|
+
else:
|
|
4121
|
+
removed_symbols = set(symbols)
|
|
4122
|
+
self.subscribed_symbols = [
|
|
4123
|
+
symbol for symbol in self.subscribed_symbols if symbol not in removed_symbols
|
|
4124
|
+
]
|
|
4125
|
+
message = "Symbols unsubscribed"
|
|
4126
|
+
total_subscribed = len(self.subscribed_symbols)
|
|
4127
|
+
trading_logger.log_info(
|
|
4128
|
+
message,
|
|
4129
|
+
{"symbols": symbols, "total_subscribed": total_subscribed},
|
|
4130
|
+
)
|
|
4093
4131
|
except Exception as e:
|
|
4094
4132
|
trading_logger.log_error(
|
|
4095
4133
|
"Error updating subscriptions", e, {"operation": operation, "symbols": symbols}
|
|
@@ -4111,7 +4149,10 @@ class FivePaisa(BrokerBase):
|
|
|
4111
4149
|
if not has_live_stream():
|
|
4112
4150
|
# WS is dead — full reconnect restoring all subscribed_symbols (already updated above).
|
|
4113
4151
|
# Connecting with just the current op's req_data would lose all prior subscriptions.
|
|
4114
|
-
reconnect_stream()
|
|
4152
|
+
if not reconnect_stream():
|
|
4153
|
+
send_stream_request(req_data)
|
|
4154
|
+
if oi_req_data:
|
|
4155
|
+
send_stream_request(oi_req_data)
|
|
4115
4156
|
else:
|
|
4116
4157
|
trading_logger.log_info(
|
|
4117
4158
|
"Requesting streaming for existing connection", {"req_data": json.dumps(req_data)}
|
|
@@ -4125,8 +4166,10 @@ class FivePaisa(BrokerBase):
|
|
|
4125
4166
|
"WebSocket closed, reconnecting...",
|
|
4126
4167
|
{"operation": operation, "symbols_count": len(symbols), "exchange": exchange},
|
|
4127
4168
|
)
|
|
4128
|
-
reconnect_stream()
|
|
4129
|
-
|
|
4169
|
+
if not reconnect_stream():
|
|
4170
|
+
send_stream_request(req_data)
|
|
4171
|
+
if oi_req_data:
|
|
4172
|
+
send_stream_request(oi_req_data)
|
|
4130
4173
|
else:
|
|
4131
4174
|
trading_logger.log_warning(
|
|
4132
4175
|
"No valid request list generated", {"symbols": symbols, "req_list": req_list}
|