fyers-apiv3 3.1.5__py3-none-any.whl → 3.1.7__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.
- fyers_apiv3/FyersWebsocket/msg_pb2.py +50 -0
- fyers_apiv3/FyersWebsocket/tbt_ws.py +594 -0
- fyers_apiv3/fyersModel.py +13 -0
- {fyers_apiv3-3.1.5.dist-info → fyers_apiv3-3.1.7.dist-info}/METADATA +89 -1
- {fyers_apiv3-3.1.5.dist-info → fyers_apiv3-3.1.7.dist-info}/RECORD +8 -6
- {fyers_apiv3-3.1.5.dist-info → fyers_apiv3-3.1.7.dist-info}/WHEEL +1 -1
- {fyers_apiv3-3.1.5.dist-info → fyers_apiv3-3.1.7.dist-info}/LICENSE.txt +0 -0
- {fyers_apiv3-3.1.5.dist-info → fyers_apiv3-3.1.7.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# source: msg.proto
|
|
4
|
+
# Protobuf Python Version: 5.26.1
|
|
5
|
+
"""Generated protocol buffer code."""
|
|
6
|
+
from google.protobuf import descriptor as _descriptor
|
|
7
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
8
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
9
|
+
from google.protobuf.internal import builder as _builder
|
|
10
|
+
# @@protoc_insertion_point(imports)
|
|
11
|
+
|
|
12
|
+
_sym_db = _symbol_database.Default()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
from google.protobuf import wrappers_pb2 as google_dot_protobuf_dot_wrappers__pb2
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tmsg.proto\x1a\x1egoogle/protobuf/wrappers.proto\"\xbb\x01\n\x0bMarketLevel\x12*\n\x05price\x18\x01 \x01(\x0b\x32\x1b.google.protobuf.Int64Value\x12)\n\x03qty\x18\x02 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12*\n\x04nord\x18\x03 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12)\n\x03num\x18\x04 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\"\x95\x01\n\x05\x44\x65pth\x12)\n\x03tbq\x18\x01 \x01(\x0b\x32\x1c.google.protobuf.UInt64Value\x12)\n\x03tsq\x18\x02 \x01(\x0b\x32\x1c.google.protobuf.UInt64Value\x12\x1a\n\x04\x61sks\x18\x03 \x03(\x0b\x32\x0c.MarketLevel\x12\x1a\n\x04\x62ids\x18\x04 \x03(\x0b\x32\x0c.MarketLevel\"\xb7\x02\n\x05Quote\x12(\n\x03ltp\x18\x01 \x01(\x0b\x32\x1b.google.protobuf.Int64Value\x12)\n\x03ltt\x18\x02 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12)\n\x03ltq\x18\x03 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12)\n\x03vtt\x18\x04 \x01(\x0b\x32\x1c.google.protobuf.UInt64Value\x12.\n\x08vtt_diff\x18\x05 \x01(\x0b\x32\x1c.google.protobuf.UInt64Value\x12(\n\x02oi\x18\x06 \x01(\x0b\x32\x1c.google.protobuf.UInt64Value\x12)\n\x04ltpc\x18\x07 \x01(\x0b\x32\x1b.google.protobuf.Int64Value\"\x88\x03\n\rExtendedQuote\x12(\n\x03\x61tp\x18\x01 \x01(\x0b\x32\x1b.google.protobuf.Int64Value\x12\'\n\x02\x63p\x18\x02 \x01(\x0b\x32\x1b.google.protobuf.Int64Value\x12(\n\x02lc\x18\x03 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12(\n\x02uc\x18\x04 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12\'\n\x02yh\x18\x05 \x01(\x0b\x32\x1b.google.protobuf.Int64Value\x12\'\n\x02yl\x18\x06 \x01(\x0b\x32\x1b.google.protobuf.Int64Value\x12)\n\x03poi\x18\x07 \x01(\x0b\x32\x1c.google.protobuf.UInt64Value\x12)\n\x04oich\x18\x08 \x01(\x0b\x32\x1b.google.protobuf.Int64Value\x12(\n\x02pc\x18\t \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\"\x88\x02\n\nDailyQuote\x12\'\n\x02\x64o\x18\x01 \x01(\x0b\x32\x1b.google.protobuf.Int64Value\x12\'\n\x02\x64h\x18\x02 \x01(\x0b\x32\x1b.google.protobuf.Int64Value\x12\'\n\x02\x64l\x18\x03 \x01(\x0b\x32\x1b.google.protobuf.Int64Value\x12\'\n\x02\x64\x63\x18\x04 \x01(\x0b\x32\x1b.google.protobuf.Int64Value\x12*\n\x04\x64hoi\x18\x05 \x01(\x0b\x32\x1c.google.protobuf.UInt64Value\x12*\n\x04\x64loi\x18\x06 \x01(\x0b\x32\x1c.google.protobuf.UInt64Value\"\x8e\x02\n\x05OHLCV\x12)\n\x04open\x18\x01 \x01(\x0b\x32\x1b.google.protobuf.Int64Value\x12)\n\x04high\x18\x02 \x01(\x0b\x32\x1b.google.protobuf.Int64Value\x12(\n\x03low\x18\x03 \x01(\x0b\x32\x1b.google.protobuf.Int64Value\x12*\n\x05\x63lose\x18\x04 \x01(\x0b\x32\x1b.google.protobuf.Int64Value\x12,\n\x06volume\x18\x05 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12+\n\x05\x65poch\x18\x06 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\"\x1d\n\tSymDetail\x12\x10\n\x08ticksize\x18\x01 \x01(\t\"\xcd\x02\n\nMarketFeed\x12\x15\n\x05quote\x18\x01 \x01(\x0b\x32\x06.Quote\x12\x1a\n\x02\x65q\x18\x02 \x01(\x0b\x32\x0e.ExtendedQuote\x12\x17\n\x02\x64q\x18\x03 \x01(\x0b\x32\x0b.DailyQuote\x12\x15\n\x05ohlcv\x18\x04 \x01(\x0b\x32\x06.OHLCV\x12\x15\n\x05\x64\x65pth\x18\x05 \x01(\x0b\x32\x06.Depth\x12/\n\tfeed_time\x18\x06 \x01(\x0b\x32\x1c.google.protobuf.UInt64Value\x12/\n\tsend_time\x18\x07 \x01(\x0b\x32\x1c.google.protobuf.UInt64Value\x12\r\n\x05token\x18\x08 \x01(\t\x12\x13\n\x0bsequence_no\x18\t \x01(\x04\x12\x10\n\x08snapshot\x18\n \x01(\x08\x12\x0e\n\x06ticker\x18\x0b \x01(\t\x12\x1d\n\tsymdetail\x18\x0c \x01(\x0b\x32\n.SymDetail\"\xbe\x01\n\rSocketMessage\x12\x1a\n\x04type\x18\x01 \x01(\x0e\x32\x0c.MessageType\x12(\n\x05\x66\x65\x65\x64s\x18\x02 \x03(\x0b\x32\x19.SocketMessage.FeedsEntry\x12\x10\n\x08snapshot\x18\x03 \x01(\x08\x12\x0b\n\x03msg\x18\x04 \x01(\t\x12\r\n\x05\x65rror\x18\x05 \x01(\x08\x1a\x39\n\nFeedsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1a\n\x05value\x18\x02 \x01(\x0b\x32\x0b.MarketFeed:\x02\x38\x01*\x86\x01\n\x0bMessageType\x12\x08\n\x04ping\x10\x00\x12\t\n\x05quote\x10\x01\x12\x12\n\x0e\x65xtended_quote\x10\x02\x12\x0f\n\x0b\x64\x61ily_quote\x10\x03\x12\x10\n\x0cmarket_level\x10\x04\x12\t\n\x05ohlcv\x10\x05\x12\t\n\x05\x64\x65pth\x10\x06\x12\x07\n\x03\x61ll\x10\x07\x12\x0c\n\x08response\x10\x08\x42\nZ\x08/gencodeb\x06proto3')
|
|
19
|
+
|
|
20
|
+
_globals = globals()
|
|
21
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
22
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'msg_pb2', _globals)
|
|
23
|
+
if not _descriptor._USE_C_DESCRIPTORS:
|
|
24
|
+
_globals['DESCRIPTOR']._loaded_options = None
|
|
25
|
+
_globals['DESCRIPTOR']._serialized_options = b'Z\010/gencode'
|
|
26
|
+
_globals['_SOCKETMESSAGE_FEEDSENTRY']._loaded_options = None
|
|
27
|
+
_globals['_SOCKETMESSAGE_FEEDSENTRY']._serialized_options = b'8\001'
|
|
28
|
+
_globals['_MESSAGETYPE']._serialized_start=2197
|
|
29
|
+
_globals['_MESSAGETYPE']._serialized_end=2331
|
|
30
|
+
_globals['_MARKETLEVEL']._serialized_start=46
|
|
31
|
+
_globals['_MARKETLEVEL']._serialized_end=233
|
|
32
|
+
_globals['_DEPTH']._serialized_start=236
|
|
33
|
+
_globals['_DEPTH']._serialized_end=385
|
|
34
|
+
_globals['_QUOTE']._serialized_start=388
|
|
35
|
+
_globals['_QUOTE']._serialized_end=699
|
|
36
|
+
_globals['_EXTENDEDQUOTE']._serialized_start=702
|
|
37
|
+
_globals['_EXTENDEDQUOTE']._serialized_end=1094
|
|
38
|
+
_globals['_DAILYQUOTE']._serialized_start=1097
|
|
39
|
+
_globals['_DAILYQUOTE']._serialized_end=1361
|
|
40
|
+
_globals['_OHLCV']._serialized_start=1364
|
|
41
|
+
_globals['_OHLCV']._serialized_end=1634
|
|
42
|
+
_globals['_SYMDETAIL']._serialized_start=1636
|
|
43
|
+
_globals['_SYMDETAIL']._serialized_end=1665
|
|
44
|
+
_globals['_MARKETFEED']._serialized_start=1668
|
|
45
|
+
_globals['_MARKETFEED']._serialized_end=2001
|
|
46
|
+
_globals['_SOCKETMESSAGE']._serialized_start=2004
|
|
47
|
+
_globals['_SOCKETMESSAGE']._serialized_end=2194
|
|
48
|
+
_globals['_SOCKETMESSAGE_FEEDSENTRY']._serialized_start=2137
|
|
49
|
+
_globals['_SOCKETMESSAGE_FEEDSENTRY']._serialized_end=2194
|
|
50
|
+
# @@protoc_insertion_point(module_scope)
|
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
from typing import Any, Callable, Dict, Optional
|
|
2
|
+
from pkg_resources import resource_filename
|
|
3
|
+
import websocket
|
|
4
|
+
from threading import Thread
|
|
5
|
+
import logging
|
|
6
|
+
import threading
|
|
7
|
+
import time
|
|
8
|
+
import json
|
|
9
|
+
from fyers_apiv3.FyersWebsocket import defines
|
|
10
|
+
from fyers_apiv3.fyers_logger import FyersLogger
|
|
11
|
+
from typing import Set, List
|
|
12
|
+
import fyers_apiv3.FyersWebsocket.msg_pb2 as protomsg
|
|
13
|
+
from enum import Enum
|
|
14
|
+
import requests
|
|
15
|
+
|
|
16
|
+
## Models and definitions
|
|
17
|
+
|
|
18
|
+
def getUrl(access_token: str):
|
|
19
|
+
"""
|
|
20
|
+
Get the URL for the WebSocket connection.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
access_token (str): The access token to authenticate with. Format: APPID:SECRET_KEY
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
str: The URL for the WebSocket connection.
|
|
27
|
+
"""
|
|
28
|
+
data = requests.get('https://api-t1.fyers.in/indus/home/tbtws', headers={'Authorization': f'{access_token}'})
|
|
29
|
+
if data.status_code == 200:
|
|
30
|
+
return data.json()['data']['socket_url']
|
|
31
|
+
return "wss://rtsocket-api.fyers.in/versova"
|
|
32
|
+
|
|
33
|
+
class SubscriptionModes(Enum):
|
|
34
|
+
DEPTH = "depth"
|
|
35
|
+
|
|
36
|
+
class Depth:
|
|
37
|
+
def __init__(self):
|
|
38
|
+
self.tbq: int = 0
|
|
39
|
+
self.tsq: int = 0
|
|
40
|
+
self.bidprice: List[float] = [0.0] * 50
|
|
41
|
+
self.askprice: List[float] = [0.0] * 50
|
|
42
|
+
self.bidqty: List[float] = [0] * 50
|
|
43
|
+
self.askqty: List[float] = [0] * 50
|
|
44
|
+
self.bidordn: List[float] = [0] * 50
|
|
45
|
+
self.askordn: List[float] = [0] * 50
|
|
46
|
+
self.snapshot: bool = False
|
|
47
|
+
self.timestamp: int = 0
|
|
48
|
+
self.sendtime: int = 0
|
|
49
|
+
self.seqNo: int = 0
|
|
50
|
+
|
|
51
|
+
def __str__(self):
|
|
52
|
+
return (f"Depth{{ts: {self.timestamp}, "
|
|
53
|
+
f"send_ts: {self.sendtime}, "
|
|
54
|
+
f"tbq: {self.tbq}, tsq: {self.tsq}, "
|
|
55
|
+
f"bidprice: {self.bidprice}, askprice: {self.askprice}, "
|
|
56
|
+
f"bidqty: {self.bidqty}, askqty: {self.askqty}, "
|
|
57
|
+
f"bidordn: {self.bidordn}, askordn: {self.askordn}, "
|
|
58
|
+
f"snapshot: {self.snapshot}, sNo: {self.seqNo} }}")
|
|
59
|
+
|
|
60
|
+
def _addDepth(self, currdata: protomsg.MarketFeed, isSnapshot: bool):
|
|
61
|
+
if currdata.HasField('depth'):
|
|
62
|
+
self.snapshot = isSnapshot
|
|
63
|
+
if currdata.depth.HasField('tbq'):
|
|
64
|
+
self.tbq = currdata.depth.tbq.value
|
|
65
|
+
|
|
66
|
+
if currdata.depth.HasField('tsq'):
|
|
67
|
+
self.tsq = currdata.depth.tsq.value
|
|
68
|
+
|
|
69
|
+
if currdata.depth.asks is not None:
|
|
70
|
+
for i in range(len(currdata.depth.asks)):
|
|
71
|
+
if currdata.depth.asks[i].HasField('price'):
|
|
72
|
+
self.askprice[i] = currdata.depth.asks[i].price.value / 100
|
|
73
|
+
|
|
74
|
+
if currdata.depth.asks[i].HasField('qty'):
|
|
75
|
+
self.askqty[i] = currdata.depth.asks[i].qty.value
|
|
76
|
+
|
|
77
|
+
if currdata.depth.asks[i].HasField('nord'):
|
|
78
|
+
self.askordn[i] = currdata.depth.asks[i].nord.value
|
|
79
|
+
|
|
80
|
+
if currdata.depth.bids is not None:
|
|
81
|
+
for i in range(len(currdata.depth.bids)):
|
|
82
|
+
if currdata.depth.bids[i].HasField('price'):
|
|
83
|
+
self.bidprice[i] = currdata.depth.bids[i].price.value / 100
|
|
84
|
+
|
|
85
|
+
if currdata.depth.bids[i].HasField('qty'):
|
|
86
|
+
self.bidqty[i] = currdata.depth.bids[i].qty.value
|
|
87
|
+
|
|
88
|
+
if currdata.depth.bids[i].HasField('nord'):
|
|
89
|
+
self.bidordn[i] = currdata.depth.bids[i].nord.value
|
|
90
|
+
|
|
91
|
+
self.timestamp = currdata.feed_time.value
|
|
92
|
+
self.sendtime = currdata.send_time.value
|
|
93
|
+
self.seqNo = currdata.sequence_no
|
|
94
|
+
|
|
95
|
+
class SubscriptionInfo:
|
|
96
|
+
def __init__(self) -> None:
|
|
97
|
+
self._symbols: Dict[str, Set[str]] = {}
|
|
98
|
+
self._modeInfo: Dict[str, SubscriptionModes] = {}
|
|
99
|
+
self._activeChannels: Set[str] = set()
|
|
100
|
+
|
|
101
|
+
def subscribe(self, symbols: Set[str], channelNo: str, mode: SubscriptionModes) -> None:
|
|
102
|
+
if channelNo in self._symbols:
|
|
103
|
+
self._symbols[channelNo].update(symbols)
|
|
104
|
+
else:
|
|
105
|
+
self._symbols[channelNo] = set(symbols)
|
|
106
|
+
self._modeInfo[channelNo] = mode
|
|
107
|
+
|
|
108
|
+
def unsubscribe(self, symbols: Set[str], channelNo: str) -> None:
|
|
109
|
+
if channelNo in self._symbols:
|
|
110
|
+
self._symbols[channelNo].difference_update(symbols)
|
|
111
|
+
if not self._symbols[channelNo]:
|
|
112
|
+
del self._symbols[channelNo]
|
|
113
|
+
|
|
114
|
+
def updateChannels(self, pauseChannels: Set[str], resumeChannels: Set[str]) -> None:
|
|
115
|
+
self._activeChannels.difference_update(pauseChannels)
|
|
116
|
+
self._activeChannels.update(resumeChannels)
|
|
117
|
+
|
|
118
|
+
def updateMode(self, modeConfig: Dict[str, SubscriptionModes]) -> None:
|
|
119
|
+
for channelNo, mode in modeConfig.items():
|
|
120
|
+
self._modeInfo[channelNo] = mode
|
|
121
|
+
|
|
122
|
+
def getSymbolsInfo(self, chanNo: str) -> Set[str]:
|
|
123
|
+
return self._symbols[chanNo]
|
|
124
|
+
|
|
125
|
+
def getModeInfo(self, chanNo: str) -> SubscriptionModes:
|
|
126
|
+
return self._modeInfo[chanNo]
|
|
127
|
+
|
|
128
|
+
def getChannelInfo(self) -> Set[str]:
|
|
129
|
+
return self._activeChannels
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class DataStore:
|
|
133
|
+
depth: Dict[str, Depth] = {}
|
|
134
|
+
|
|
135
|
+
def updateDepth(self, packet: protomsg.SocketMessage, cb: Optional[Callable], diffOnly: bool):
|
|
136
|
+
if packet.feeds is not None:
|
|
137
|
+
for _, value in packet.feeds.items():
|
|
138
|
+
symbol = value.ticker
|
|
139
|
+
if symbol not in self.depth:
|
|
140
|
+
self.depth[symbol] = Depth()
|
|
141
|
+
if not diffOnly:
|
|
142
|
+
self.depth[symbol]._addDepth(value, packet.snapshot)
|
|
143
|
+
cb(symbol, self.depth[symbol])
|
|
144
|
+
else:
|
|
145
|
+
depth = Depth()
|
|
146
|
+
depth._addDepth(value, packet.snapshot)
|
|
147
|
+
cb(symbol, depth)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class FyersTbtSocket:
|
|
151
|
+
|
|
152
|
+
_instance = None
|
|
153
|
+
|
|
154
|
+
def __new__(cls, *args, **kwargs):
|
|
155
|
+
if not cls._instance:
|
|
156
|
+
cls._instance = super().__new__(cls)
|
|
157
|
+
return cls._instance
|
|
158
|
+
|
|
159
|
+
def __init__(
|
|
160
|
+
self,
|
|
161
|
+
access_token: str,
|
|
162
|
+
write_to_file: Optional[bool] = False,
|
|
163
|
+
log_path: Optional[str] = None,
|
|
164
|
+
on_depth_update: Optional[Callable] = None,
|
|
165
|
+
on_error_message: Optional[Callable] = None,
|
|
166
|
+
on_error: Optional[Callable] = None,
|
|
167
|
+
on_connect: Optional[Callable] = None,
|
|
168
|
+
on_close: Optional[Callable] = None,
|
|
169
|
+
on_open: Optional[Callable] = None,
|
|
170
|
+
reconnect : Optional[Callable] = True,
|
|
171
|
+
diff_only: bool = False,
|
|
172
|
+
reconnect_retry: int = 5
|
|
173
|
+
) -> None:
|
|
174
|
+
"""
|
|
175
|
+
Initializes the class instance.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
access_token (str): The access token to authenticate with.
|
|
179
|
+
write_to_file (bool, optional): Flag indicating whether to save data to a file. Defaults to False.
|
|
180
|
+
log_path (str, optional): The path to the log file. Defaults to None.
|
|
181
|
+
on_depth_update (callable, optional): Callback function for 50 depth events. Defaults to None.
|
|
182
|
+
on_error_message (callable, optional): Callback function for error msg received from server. Defaults to None.
|
|
183
|
+
on_error (callable, optional): Callback function for error events. Defaults to None.
|
|
184
|
+
on_connect (callable, optional): Callback function for connect events. Defaults to None.
|
|
185
|
+
on_close (callable, optional): Callback function for close events. Defaults to None.
|
|
186
|
+
on_open (callable, optional): Callback function for open events. Defaults to None.
|
|
187
|
+
reconnect (bool, optional): Flag indicating whether to attempt reconnection on disconnection. Defaults to True.
|
|
188
|
+
"""
|
|
189
|
+
self._datastore = DataStore()
|
|
190
|
+
self._subsinfo = SubscriptionInfo()
|
|
191
|
+
self.__access_token = access_token
|
|
192
|
+
self.log_path = log_path
|
|
193
|
+
self.__ws_object = None
|
|
194
|
+
self.__ws_run = False
|
|
195
|
+
self.ping_thread = None
|
|
196
|
+
self.write_to_file = write_to_file
|
|
197
|
+
self.background_flag = False
|
|
198
|
+
self.reconnect_delay = 0
|
|
199
|
+
self.onDepthUpdate = on_depth_update
|
|
200
|
+
self.onErrorMsg = on_error_message
|
|
201
|
+
self.restart_flag = reconnect
|
|
202
|
+
self.onerror = on_error
|
|
203
|
+
self.onopen = on_connect
|
|
204
|
+
self.max_reconnect_attempts = 50
|
|
205
|
+
self.reconnect_attempts = 0
|
|
206
|
+
self.diff_only = diff_only
|
|
207
|
+
if reconnect_retry < self.max_reconnect_attempts:
|
|
208
|
+
self.max_reconnect_attempts = reconnect_retry
|
|
209
|
+
|
|
210
|
+
self.onclose = on_close
|
|
211
|
+
self.onopen = on_open
|
|
212
|
+
self.__ws_object = None
|
|
213
|
+
self.running_thread=None
|
|
214
|
+
self.__url = getUrl(access_token)
|
|
215
|
+
|
|
216
|
+
if log_path:
|
|
217
|
+
self.tbtlogger = FyersLogger(
|
|
218
|
+
"FyersTbtSocket",
|
|
219
|
+
"DEBUG",
|
|
220
|
+
stack_level=2,
|
|
221
|
+
logger_handler=logging.FileHandler(log_path + "/fyersTBTSocket.log"),
|
|
222
|
+
)
|
|
223
|
+
else:
|
|
224
|
+
self.tbtlogger = FyersLogger(
|
|
225
|
+
"FyersTbtSocket",
|
|
226
|
+
"DEBUG",
|
|
227
|
+
stack_level=2,
|
|
228
|
+
logger_handler=logging.FileHandler("fyersTBTSocket.log"),
|
|
229
|
+
)
|
|
230
|
+
self.websocket_task = None
|
|
231
|
+
|
|
232
|
+
self.write_to_file = write_to_file
|
|
233
|
+
self.background_flag = False
|
|
234
|
+
|
|
235
|
+
def subscribe(self, symbol_tickers: Set[str], channelNo: str, mode: SubscriptionModes) -> None:
|
|
236
|
+
"""
|
|
237
|
+
Subscribe to a specific channel with the given symbols and mode.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
symbol_tickers (Set[str]): The set of symbol tickers to subscribe to.
|
|
241
|
+
channelNo (str): The channel number to subscribe to. Should be between 1 and 50
|
|
242
|
+
mode (SubscriptionModes): The mode of subscription.
|
|
243
|
+
"""
|
|
244
|
+
if (
|
|
245
|
+
self.__ws_object is not None
|
|
246
|
+
and self.__ws_object.sock
|
|
247
|
+
and self.__ws_object.sock.connected
|
|
248
|
+
):
|
|
249
|
+
self._subsinfo.subscribe(symbol_tickers, channelNo, mode)
|
|
250
|
+
self.__ws_object.send(
|
|
251
|
+
json.dumps(
|
|
252
|
+
{
|
|
253
|
+
"type": 1,
|
|
254
|
+
"data": {
|
|
255
|
+
"subs": 1,
|
|
256
|
+
"symbols": list(symbol_tickers),
|
|
257
|
+
"mode": mode.value,
|
|
258
|
+
"channel": channelNo,
|
|
259
|
+
},
|
|
260
|
+
}
|
|
261
|
+
)
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
def unsubscribe(self, symbol_tickers: Set[str], channelNo: str, mode: SubscriptionModes) -> None:
|
|
265
|
+
"""
|
|
266
|
+
Unsubscribe from a specific channel with the given symbols and mode.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
symbol_tickers (Set[str]): The set of symbol tickers to unsubscribe from.
|
|
270
|
+
channelNo (str): The channel number to unsubscribe from. Should be between 1 and 50
|
|
271
|
+
mode (SubscriptionModes): The mode of subscription.
|
|
272
|
+
"""
|
|
273
|
+
if (
|
|
274
|
+
self.__ws_object is not None
|
|
275
|
+
and self.__ws_object.sock
|
|
276
|
+
and self.__ws_object.sock.connected
|
|
277
|
+
):
|
|
278
|
+
self._subsinfo.unsubscribe(symbol_tickers, channelNo)
|
|
279
|
+
self.__ws_object.send(
|
|
280
|
+
json.dumps(
|
|
281
|
+
{
|
|
282
|
+
"type": 1,
|
|
283
|
+
"data": {
|
|
284
|
+
"subs": -1,
|
|
285
|
+
"symbols": list(symbol_tickers),
|
|
286
|
+
"mode": mode.value,
|
|
287
|
+
"channel": channelNo,
|
|
288
|
+
},
|
|
289
|
+
}
|
|
290
|
+
)
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
def switchChannel(self, resume_channels: Set[str], pause_channels: Set[str]) -> None:
|
|
294
|
+
"""
|
|
295
|
+
Resume and pause channels to receive data from the server.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
resume_channels (Set[str]): The set of channels to resume. Data will be received for symbols on these channels.
|
|
299
|
+
pause_channels (Set[str]): The set of channels to pause. Data will be paused for symbols on these channels.
|
|
300
|
+
"""
|
|
301
|
+
if (
|
|
302
|
+
self.__ws_object is not None
|
|
303
|
+
and self.__ws_object.sock
|
|
304
|
+
and self.__ws_object.sock.connected
|
|
305
|
+
):
|
|
306
|
+
self._subsinfo.updateChannels(pause_channels, resume_channels)
|
|
307
|
+
self.__ws_object.send(
|
|
308
|
+
json.dumps(
|
|
309
|
+
{
|
|
310
|
+
"type": 2,
|
|
311
|
+
"data": {
|
|
312
|
+
"resumeChannels": list(resume_channels),
|
|
313
|
+
"pauseChannels": list(pause_channels)
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
)
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
def on_depth_update(self, ticker: str, message: Depth):
|
|
320
|
+
"""
|
|
321
|
+
Callback function for depth update events.
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
ticker (str): The ticker symbol.
|
|
325
|
+
message (Depth): The depth message.
|
|
326
|
+
"""
|
|
327
|
+
try:
|
|
328
|
+
if self.onDepthUpdate is not None:
|
|
329
|
+
self.onDepthUpdate(ticker, message)
|
|
330
|
+
else:
|
|
331
|
+
if self.write_to_file:
|
|
332
|
+
self.tbtlogger.debug(f"{ticker}: {message}")
|
|
333
|
+
else:
|
|
334
|
+
print(f"{ticker}: {message}")
|
|
335
|
+
|
|
336
|
+
except Exception as e:
|
|
337
|
+
self.tbtlogger.error(e)
|
|
338
|
+
self.On_error(e)
|
|
339
|
+
|
|
340
|
+
def on_error_message(self, message: str):
|
|
341
|
+
"""
|
|
342
|
+
Callback function for error message events from the server
|
|
343
|
+
|
|
344
|
+
Args:
|
|
345
|
+
message (str): The error message.
|
|
346
|
+
"""
|
|
347
|
+
try:
|
|
348
|
+
if self.onErrorMsg is not None:
|
|
349
|
+
self.onErrorMsg(message)
|
|
350
|
+
else:
|
|
351
|
+
print(f"error received from server: {message}")
|
|
352
|
+
except Exception as e:
|
|
353
|
+
self.tbtlogger.error(e)
|
|
354
|
+
self.On_error(e)
|
|
355
|
+
|
|
356
|
+
def __on_message(self, message: Dict[str, Any]):
|
|
357
|
+
"""
|
|
358
|
+
Parses the response data based on its content.
|
|
359
|
+
|
|
360
|
+
Args:
|
|
361
|
+
message (str): The response message to be parsed.
|
|
362
|
+
|
|
363
|
+
Returns:
|
|
364
|
+
Any: The parsed response data.
|
|
365
|
+
"""
|
|
366
|
+
try:
|
|
367
|
+
if message != "pong":
|
|
368
|
+
d = protomsg.SocketMessage()
|
|
369
|
+
d.ParseFromString(message)
|
|
370
|
+
if d.error:
|
|
371
|
+
self.on_error_message(d.msg)
|
|
372
|
+
else:
|
|
373
|
+
self._datastore.updateDepth(d, self.on_depth_update, self.diff_only)
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
except Exception as e:
|
|
377
|
+
self.tbtlogger.error(e)
|
|
378
|
+
self.On_error(e)
|
|
379
|
+
|
|
380
|
+
def On_error(self, message: str) -> None:
|
|
381
|
+
"""
|
|
382
|
+
Callback function for handling error events.
|
|
383
|
+
|
|
384
|
+
Args:
|
|
385
|
+
message (str): The error message.
|
|
386
|
+
|
|
387
|
+
"""
|
|
388
|
+
if self.onerror is not None:
|
|
389
|
+
self.onerror(message)
|
|
390
|
+
self.tbtlogger.error(message)
|
|
391
|
+
else:
|
|
392
|
+
if self.write_to_file:
|
|
393
|
+
self.tbtlogger.debug(f"Response:{message}")
|
|
394
|
+
else:
|
|
395
|
+
print(f"Error Response : {message}")
|
|
396
|
+
|
|
397
|
+
def __on_open(self, ws):
|
|
398
|
+
"""
|
|
399
|
+
Callback function for open events from the server
|
|
400
|
+
|
|
401
|
+
Args:
|
|
402
|
+
ws (WebSocket): The WebSocket object.
|
|
403
|
+
"""
|
|
404
|
+
try:
|
|
405
|
+
if self.__ws_object is None:
|
|
406
|
+
self.__ws_object = ws
|
|
407
|
+
self.ping_thread = threading.Thread(target=self.__ping)
|
|
408
|
+
self.ping_thread.start()
|
|
409
|
+
self.reconnect_attempts = 0
|
|
410
|
+
self.reconnect_delay = 0
|
|
411
|
+
self.on_open()
|
|
412
|
+
|
|
413
|
+
except Exception as e:
|
|
414
|
+
self.tbtlogger.error(e)
|
|
415
|
+
self.On_error(e)
|
|
416
|
+
|
|
417
|
+
def __on_close(self, ws, close_code=None, close_reason=None):
|
|
418
|
+
"""
|
|
419
|
+
Handle the WebSocket connection close event.
|
|
420
|
+
|
|
421
|
+
Args:
|
|
422
|
+
ws (WebSocket): The WebSocket object.
|
|
423
|
+
close_code (int): The code indicating the reason for closure.
|
|
424
|
+
close_reason (str): The reason for closure.
|
|
425
|
+
|
|
426
|
+
Returns:
|
|
427
|
+
dict: A dictionary containing the response code, message, and s.
|
|
428
|
+
"""
|
|
429
|
+
try:
|
|
430
|
+
if self.restart_flag:
|
|
431
|
+
if self.reconnect_attempts < self.max_reconnect_attempts:
|
|
432
|
+
if self.write_to_file:
|
|
433
|
+
self.tbtlogger.debug(
|
|
434
|
+
f"Response:{f'Attempting reconnect {self.reconnect_attempts} of {self.max_reconnect_attempts}...'}"
|
|
435
|
+
)
|
|
436
|
+
else:
|
|
437
|
+
print(
|
|
438
|
+
f"Attempting reconnect {self.reconnect_attempts+1} of {self.max_reconnect_attempts}..."
|
|
439
|
+
)
|
|
440
|
+
if (self.reconnect_attempts) % 5 == 0:
|
|
441
|
+
self.reconnect_delay += 5
|
|
442
|
+
time.sleep(self.reconnect_delay)
|
|
443
|
+
self.reconnect_attempts += 1
|
|
444
|
+
|
|
445
|
+
self.__ws_object = None
|
|
446
|
+
self.connect()
|
|
447
|
+
else:
|
|
448
|
+
if self.write_to_file:
|
|
449
|
+
self.tbtlogger.debug(
|
|
450
|
+
f"Response:{'Max reconnect attempts reached. Connection abandoned.'}"
|
|
451
|
+
)
|
|
452
|
+
else:
|
|
453
|
+
print("Max reconnect attempts reached. Connection abandoned.")
|
|
454
|
+
else:
|
|
455
|
+
|
|
456
|
+
self.on_close(
|
|
457
|
+
{
|
|
458
|
+
"code": defines.SUCCESS_CODE,
|
|
459
|
+
"message": defines.CONNECTION_CLOSED,
|
|
460
|
+
"s": defines.SUCCESS,
|
|
461
|
+
}
|
|
462
|
+
)
|
|
463
|
+
except Exception as e:
|
|
464
|
+
self.tbtlogger.error(e)
|
|
465
|
+
self.On_error(e)
|
|
466
|
+
|
|
467
|
+
def __ping(self) -> None:
|
|
468
|
+
"""
|
|
469
|
+
Sends periodic ping messages to the server to maintain the WebSocket connection.
|
|
470
|
+
|
|
471
|
+
The method continuously sends "__ping" messages to the server at a regular interval
|
|
472
|
+
as long as the WebSocket connection is active.
|
|
473
|
+
|
|
474
|
+
"""
|
|
475
|
+
|
|
476
|
+
while (
|
|
477
|
+
self.__ws_object is not None
|
|
478
|
+
and self.__ws_object.sock
|
|
479
|
+
and self.__ws_object.sock.connected
|
|
480
|
+
):
|
|
481
|
+
self.__ws_object.send("ping")
|
|
482
|
+
time.sleep(10)
|
|
483
|
+
|
|
484
|
+
def on_close(self, message: dict) -> None:
|
|
485
|
+
"""
|
|
486
|
+
Handles the close event.
|
|
487
|
+
|
|
488
|
+
Args:
|
|
489
|
+
message (dict): The close message .
|
|
490
|
+
"""
|
|
491
|
+
|
|
492
|
+
if self.onclose:
|
|
493
|
+
self.onclose(message)
|
|
494
|
+
else:
|
|
495
|
+
print(f"Response: {message}")
|
|
496
|
+
|
|
497
|
+
def on_open(self) -> None:
|
|
498
|
+
"""
|
|
499
|
+
Performs initialization and waits before executing further actions.
|
|
500
|
+
"""
|
|
501
|
+
try:
|
|
502
|
+
if self.onopen:
|
|
503
|
+
self.onopen()
|
|
504
|
+
open_chans = self._subsinfo.getChannelInfo()
|
|
505
|
+
self.switchChannel(self._subsinfo.getChannelInfo(), set())
|
|
506
|
+
for channel in open_chans:
|
|
507
|
+
self.subscribe(self._subsinfo.getSymbolsInfo(channel), channel, self._subsinfo.getModeInfo(channel))
|
|
508
|
+
except Exception as e:
|
|
509
|
+
self.On_error(e)
|
|
510
|
+
|
|
511
|
+
def is_connected(self):
|
|
512
|
+
"""
|
|
513
|
+
Check if the websocket is connected.
|
|
514
|
+
|
|
515
|
+
Returns:
|
|
516
|
+
bool: True if the websocket is connected, False otherwise.
|
|
517
|
+
"""
|
|
518
|
+
if self.__ws_object:
|
|
519
|
+
return True
|
|
520
|
+
else:
|
|
521
|
+
return False
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
def __init_connection(self):
|
|
525
|
+
"""
|
|
526
|
+
Initializes the WebSocket connection and starts the WebSocketApp.
|
|
527
|
+
|
|
528
|
+
The method creates a WebSocketApp object with the specified URL and sets the appropriate event handlers.
|
|
529
|
+
It then starts the WebSocketApp in a separate thread.
|
|
530
|
+
"""
|
|
531
|
+
try:
|
|
532
|
+
if self.__ws_object is None:
|
|
533
|
+
if self.write_to_file:
|
|
534
|
+
self.background_flag = False
|
|
535
|
+
header = {"authorization": self.__access_token}
|
|
536
|
+
ws = websocket.WebSocketApp(
|
|
537
|
+
self.__url,
|
|
538
|
+
header=header,
|
|
539
|
+
on_message=lambda ws, msg: self.__on_message(msg),
|
|
540
|
+
on_error=lambda ws, msg: self.On_error(msg),
|
|
541
|
+
on_close=lambda ws, close_code, close_reason: self.__on_close(
|
|
542
|
+
ws, close_code, close_reason
|
|
543
|
+
),
|
|
544
|
+
on_open=lambda ws: self.__on_open(ws),
|
|
545
|
+
)
|
|
546
|
+
self.t = Thread(target=ws.run_forever)
|
|
547
|
+
self.t.daemon = self.background_flag
|
|
548
|
+
self.t.start()
|
|
549
|
+
|
|
550
|
+
except Exception as e:
|
|
551
|
+
self.tbtlogger.error(e)
|
|
552
|
+
|
|
553
|
+
def keep_running(self):
|
|
554
|
+
"""
|
|
555
|
+
Starts an infinite loop to keep the program running.
|
|
556
|
+
|
|
557
|
+
"""
|
|
558
|
+
self.__ws_run = True
|
|
559
|
+
self.running_thread = Thread(target=self.infinite_loop)
|
|
560
|
+
self.running_thread.start()
|
|
561
|
+
|
|
562
|
+
def stop_running(self):
|
|
563
|
+
self.__ws_run = False
|
|
564
|
+
|
|
565
|
+
def infinite_loop(self):
|
|
566
|
+
while self.__ws_run:
|
|
567
|
+
time.sleep(0.5)
|
|
568
|
+
|
|
569
|
+
def connect(self) -> None:
|
|
570
|
+
"""
|
|
571
|
+
Establishes a connection to the WebSocket.
|
|
572
|
+
|
|
573
|
+
If the WebSocket object is not already initialized, this method will create the
|
|
574
|
+
WebSocket connection.
|
|
575
|
+
|
|
576
|
+
"""
|
|
577
|
+
if self.__ws_object is None:
|
|
578
|
+
self.__init_connection()
|
|
579
|
+
time.sleep(2)
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
def close_connection(self):
|
|
583
|
+
"""
|
|
584
|
+
Closes the WebSocket connection
|
|
585
|
+
|
|
586
|
+
"""
|
|
587
|
+
if self.__ws_object is not None:
|
|
588
|
+
self.restart_flag = False
|
|
589
|
+
self.__ws_object.close()
|
|
590
|
+
self.__ws_object = None
|
|
591
|
+
self.__ws_run = None
|
|
592
|
+
self.running_thread.join()
|
|
593
|
+
self.t.join()
|
|
594
|
+
self.ping_thread.join()
|
fyers_apiv3/fyersModel.py
CHANGED
|
@@ -40,6 +40,7 @@ class Config:
|
|
|
40
40
|
market_depth = "/depth"
|
|
41
41
|
option_chain = "/options-chain-v3"
|
|
42
42
|
multileg_orders = "/multileg/orders/sync"
|
|
43
|
+
logout = "/logout"
|
|
43
44
|
|
|
44
45
|
|
|
45
46
|
|
|
@@ -657,6 +658,18 @@ class FyersModel:
|
|
|
657
658
|
response = self.service.get_call(Config.holdings, self.header)
|
|
658
659
|
return response
|
|
659
660
|
|
|
661
|
+
def logout(self) -> dict:
|
|
662
|
+
"""
|
|
663
|
+
Invalidates the access token.
|
|
664
|
+
|
|
665
|
+
"""
|
|
666
|
+
if self.is_async:
|
|
667
|
+
response = self.service.post_async_call(Config.logout, self.header)
|
|
668
|
+
|
|
669
|
+
else:
|
|
670
|
+
response = self.service.post_call(Config.logout, self.header)
|
|
671
|
+
return response
|
|
672
|
+
|
|
660
673
|
def get_orders(self, data) -> dict:
|
|
661
674
|
"""
|
|
662
675
|
Retrieves order details by ID.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: fyers_apiv3
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.1.7
|
|
4
4
|
Summary: Fyers trading APIs.
|
|
5
5
|
Home-page: https://github.com/FyersDev/fyers-api-sample-code/tree/sample_v3/v3/python
|
|
6
6
|
Author: Fyers-Tech
|
|
@@ -15,6 +15,7 @@ Requires-Dist: asyncio==3.4.3
|
|
|
15
15
|
Requires-Dist: aiohttp==3.9.3
|
|
16
16
|
Requires-Dist: aws_lambda_powertools==1.25.5
|
|
17
17
|
Requires-Dist: websocket-client==1.6.1
|
|
18
|
+
Requires-Dist: protobuf==5.29.3
|
|
18
19
|
Dynamic: author
|
|
19
20
|
Dynamic: author-email
|
|
20
21
|
Dynamic: classifier
|
|
@@ -530,4 +531,91 @@ fyers = order_ws.FyersOrderSocket(
|
|
|
530
531
|
fyers.connect()
|
|
531
532
|
|
|
532
533
|
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
## Getting started with TBT Socket
|
|
537
|
+
|
|
538
|
+
```python
|
|
539
|
+
from fyers_apiv3.FyersWebsocket.tbt_ws import FyersTbtSocket, SubscriptionModes
|
|
540
|
+
|
|
541
|
+
def on_depth_update(ticker, message):
|
|
542
|
+
"""
|
|
543
|
+
Callback function to handle incoming messages from the FyersDataSocket WebSocket.
|
|
544
|
+
|
|
545
|
+
Parameters:
|
|
546
|
+
ticker (str): The symbol for which the message is received.
|
|
547
|
+
message (dict): The received message from the WebSocket.
|
|
548
|
+
|
|
549
|
+
"""
|
|
550
|
+
print("Depth Response:", ticker, message)
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
def onerror_message( message):
|
|
554
|
+
"""
|
|
555
|
+
Callback function to handle incoming messages from the FyersDataSocket WebSocket.
|
|
556
|
+
|
|
557
|
+
Parameters:
|
|
558
|
+
message (str): error message from the server
|
|
559
|
+
|
|
560
|
+
"""
|
|
561
|
+
print("server returned error:", message)
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
def onerror(message):
|
|
565
|
+
"""
|
|
566
|
+
Callback function to handle WebSocket errors.
|
|
567
|
+
|
|
568
|
+
Parameters:
|
|
569
|
+
message (dict): The error message received from the WebSocket.
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
"""
|
|
573
|
+
print("Error:", message)
|
|
574
|
+
|
|
575
|
+
|
|
576
|
+
def onclose(message):
|
|
577
|
+
"""
|
|
578
|
+
Callback function to handle WebSocket connection close events.
|
|
579
|
+
"""
|
|
580
|
+
print("Connection closed:", message)
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
def onopen():
|
|
584
|
+
"""
|
|
585
|
+
Callback function to subscribe to data type and symbols upon WebSocket connection.
|
|
586
|
+
|
|
587
|
+
"""
|
|
588
|
+
print("Connection opened")
|
|
589
|
+
# Specify the data type and symbols you want to subscribe to
|
|
590
|
+
mode = SubscriptionModes.DEPTH
|
|
591
|
+
Channel = '1'
|
|
592
|
+
# Subscribe to the specified symbols and data type
|
|
593
|
+
symbols = ['NSE:NIFTY25MARFUT']
|
|
594
|
+
|
|
595
|
+
fyers.subscribe(symbol_tickers=symbols, channelNo=Channel, mode=mode)
|
|
596
|
+
fyers.switchChannel(resume_channels=[Channel], pause_channels=[])
|
|
597
|
+
|
|
598
|
+
# Keep the socket running to receive real-time data
|
|
599
|
+
fyers.keep_running()
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
# Replace the sample access token with your actual access token obtained from Fyers
|
|
603
|
+
access_token = "XCXXXXXXM-100:eyJ0tHfZNSBoLo"
|
|
604
|
+
|
|
605
|
+
fyers = FyersTbtSocket(
|
|
606
|
+
access_token=access_token, # Your access token for authenticating with the Fyers API.
|
|
607
|
+
write_to_file=False, # A boolean flag indicating whether to write data to a log file or not.
|
|
608
|
+
log_path="", # The path to the log file if write_to_file is set to True (empty string means current directory).
|
|
609
|
+
on_open=onopen, # Callback function to be executed upon successful WebSocket connection.
|
|
610
|
+
on_close=onclose, # Callback function to be executed when the WebSocket connection is closed.
|
|
611
|
+
on_error=onerror, # Callback function to handle any WebSocket errors that may occur.
|
|
612
|
+
on_depth_update=on_depth_update, # Callback function to handle depth-related events from the WebSocket
|
|
613
|
+
on_error_message=onerror_message # Callback function to handle server-related erros from the WebSocket.
|
|
614
|
+
)
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
# Establish a connection to the Fyers WebSocket
|
|
618
|
+
fyers.connect()
|
|
619
|
+
|
|
620
|
+
|
|
533
621
|
```
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
fyers_apiv3/__init__.py,sha256=1vA_F8dAtLKcyOqrmdUOkw1Syl8gIFUtxDoLde8y94E,18
|
|
2
|
-
fyers_apiv3/fyersModel.py,sha256=
|
|
2
|
+
fyers_apiv3/fyersModel.py,sha256=MHo1NWgphuZBUx-t1VAnqsuLZZS_-9P5xwWOBMIwj_U,45096
|
|
3
3
|
fyers_apiv3/fyers_logger.py,sha256=S_WiIwBLytQ_tyHd9bUC8gZo7GHpANCJO0CS6rCJE90,3795
|
|
4
4
|
fyers_apiv3/FyersWebsocket/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
5
|
fyers_apiv3/FyersWebsocket/data_ws.py,sha256=fSNfsUB_I5-MkWX4hQZvklRQhuQ9n3w5hMDRFrMaBE8,68105
|
|
6
6
|
fyers_apiv3/FyersWebsocket/defines.py,sha256=mhKkkQ6FZHFa6e0_83cRYMOucmSn5yHtcod1Jn2ygCc,1143
|
|
7
7
|
fyers_apiv3/FyersWebsocket/map.json,sha256=GfAgk-zqzUki5Cu0ZlG08PiMhfKTyGIsPo62mIYotZ4,10075
|
|
8
|
+
fyers_apiv3/FyersWebsocket/msg_pb2.py,sha256=Po6emBFB6aCmt3rkuN6zjDA7JEzsixejfNSOQYtYgnE,6272
|
|
8
9
|
fyers_apiv3/FyersWebsocket/order_ws.py,sha256=npLBtCcpPxclc5YRaVtLvBkRDCF3oV9NabK_y5EwoAk,16998
|
|
9
|
-
fyers_apiv3
|
|
10
|
-
fyers_apiv3-3.1.
|
|
11
|
-
fyers_apiv3-3.1.
|
|
12
|
-
fyers_apiv3-3.1.
|
|
13
|
-
fyers_apiv3-3.1.
|
|
10
|
+
fyers_apiv3/FyersWebsocket/tbt_ws.py,sha256=rlg0taA9KCS8YbyKZ8UxqRw5rUcqRETz0sbM9KBUcF0,21191
|
|
11
|
+
fyers_apiv3-3.1.7.dist-info/LICENSE.txt,sha256=_a5I4lWvSmoZQxwGSPGVVvUbuYby780N9YevsBqNY3k,1063
|
|
12
|
+
fyers_apiv3-3.1.7.dist-info/METADATA,sha256=JOgjxfBjmTzfhPPt_A0rxaA_ThhD1Tf9_7TZBtKfTro,19232
|
|
13
|
+
fyers_apiv3-3.1.7.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
|
14
|
+
fyers_apiv3-3.1.7.dist-info/top_level.txt,sha256=IaT774gXqIM6uJpgCQPvXruJBOINsupO9oTe2ao6pkc,12
|
|
15
|
+
fyers_apiv3-3.1.7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|