fyers-apiv3 3.1.4__py3-none-any.whl → 3.1.6__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 +99 -0
- {fyers_apiv3-3.1.4.dist-info → fyers_apiv3-3.1.6.dist-info}/METADATA +16 -7
- {fyers_apiv3-3.1.4.dist-info → fyers_apiv3-3.1.6.dist-info}/RECORD +8 -6
- {fyers_apiv3-3.1.4.dist-info → fyers_apiv3-3.1.6.dist-info}/WHEEL +1 -1
- {fyers_apiv3-3.1.4.dist-info → fyers_apiv3-3.1.6.dist-info}/LICENSE.txt +0 -0
- {fyers_apiv3-3.1.4.dist-info → fyers_apiv3-3.1.6.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
|
@@ -26,7 +26,9 @@ class Config:
|
|
|
26
26
|
convert_position = "/positions"
|
|
27
27
|
funds = "/funds"
|
|
28
28
|
orders_endpoint = "/orders/sync"
|
|
29
|
+
gtt_orders_sync = "/gtt/orders/sync"
|
|
29
30
|
orderbook = "/orders"
|
|
31
|
+
gtt_orders = "/gtt/orders"
|
|
30
32
|
market_status = "/marketStatus"
|
|
31
33
|
auth = "/generate-authcode"
|
|
32
34
|
generate_access_token = "/validate-authcode"
|
|
@@ -38,6 +40,7 @@ class Config:
|
|
|
38
40
|
market_depth = "/depth"
|
|
39
41
|
option_chain = "/options-chain-v3"
|
|
40
42
|
multileg_orders = "/multileg/orders/sync"
|
|
43
|
+
logout = "/logout"
|
|
41
44
|
|
|
42
45
|
|
|
43
46
|
|
|
@@ -655,6 +658,18 @@ class FyersModel:
|
|
|
655
658
|
response = self.service.get_call(Config.holdings, self.header)
|
|
656
659
|
return response
|
|
657
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
|
+
|
|
658
673
|
def get_orders(self, data) -> dict:
|
|
659
674
|
"""
|
|
660
675
|
Retrieves order details by ID.
|
|
@@ -688,7 +703,21 @@ class FyersModel:
|
|
|
688
703
|
else:
|
|
689
704
|
response = self.service.get_call(Config.orderbook, self.header, data)
|
|
690
705
|
return response
|
|
706
|
+
|
|
707
|
+
def gtt_orderbook(self, data = None) -> dict:
|
|
708
|
+
"""
|
|
709
|
+
Retrieves the gtt order information.
|
|
691
710
|
|
|
711
|
+
Returns:
|
|
712
|
+
The response JSON as a dictionary.
|
|
713
|
+
"""
|
|
714
|
+
if self.is_async:
|
|
715
|
+
response = self.service.get_async_call(Config.gtt_orders, self.header, data)
|
|
716
|
+
|
|
717
|
+
else:
|
|
718
|
+
response = self.service.get_call(Config.gtt_orders, self.header, data)
|
|
719
|
+
return response
|
|
720
|
+
|
|
692
721
|
def market_status(self) -> dict:
|
|
693
722
|
"""
|
|
694
723
|
Retrieves market status.
|
|
@@ -749,6 +778,23 @@ class FyersModel:
|
|
|
749
778
|
else:
|
|
750
779
|
response = self.service.delete_call(Config.orders_endpoint, self.header, data)
|
|
751
780
|
return response
|
|
781
|
+
|
|
782
|
+
def cancel_gtt_order(self,data) -> dict:
|
|
783
|
+
"""
|
|
784
|
+
Cancel order.
|
|
785
|
+
|
|
786
|
+
Args:
|
|
787
|
+
id (str): Unique identifier for the order to be cancelled, e.g., "25010700000001".
|
|
788
|
+
|
|
789
|
+
Returns:
|
|
790
|
+
The response JSON as a dictionary.
|
|
791
|
+
"""
|
|
792
|
+
|
|
793
|
+
if self.is_async:
|
|
794
|
+
response = self.service.delete_async_call(Config.gtt_orders_sync, self.header, data)
|
|
795
|
+
else:
|
|
796
|
+
response = self.service.delete_call(Config.gtt_orders_sync, self.header, data)
|
|
797
|
+
return response
|
|
752
798
|
|
|
753
799
|
def place_order(self, data) -> dict:
|
|
754
800
|
"""
|
|
@@ -777,6 +823,35 @@ class FyersModel:
|
|
|
777
823
|
else:
|
|
778
824
|
response = self.service.post_call(Config.orders_endpoint, self.header, data)
|
|
779
825
|
return response
|
|
826
|
+
|
|
827
|
+
def place_gtt_order(self,data) -> dict:
|
|
828
|
+
"""
|
|
829
|
+
Places an order based on the provided data.
|
|
830
|
+
|
|
831
|
+
Args:
|
|
832
|
+
data (dict): A dictionary containing the order details.
|
|
833
|
+
- 'id*' (str): Unique identifier for the order to be modified, e.g., "25010700000001".
|
|
834
|
+
- 'side' (int): Indicates the side of the order: 1 for buy, -1 for sell.
|
|
835
|
+
- 'symbol' (str): The instrument's unique identifier, e.g., "NSE:CHOLAFIN-EQ"
|
|
836
|
+
- 'productType*' (str): The product type for the order. Valid values: "CNC", "MARGIN", "MTF".
|
|
837
|
+
- 'orderInfo*' (object): Contains information about the GTT/OCO order legs.
|
|
838
|
+
- 'orderInfo.leg1*' (object): Details for GTT order leg. Mandatory for all orders.
|
|
839
|
+
- 'orderInfo.leg1.price*' (number): Price at which the order.
|
|
840
|
+
- 'orderInfo.leg1.triggerPrice' (number): Trigger price for the GTT order. NOTE: for OCO order this leg trigger price should be always above LTP
|
|
841
|
+
- 'orderInfo.leg1.qty*' (int): Quantity for the GTT order leg.
|
|
842
|
+
- 'orderInfo.leg2*' (object): Details for OCO order leg. Optional and included only for OCO orders.
|
|
843
|
+
- 'orderInfo.leg2.price*' (number): Price at which the second leg of the OCO order should be placed.
|
|
844
|
+
- 'orderInfo.leg2.triggerPrice*' (number): Trigger price for the second leg of the OCO order.NOTE: for OCO order this leg trigger price should be always below LTP
|
|
845
|
+
- 'orderInfo.leg2.qty*' (integer): Quantity for the second leg of the OCO order.
|
|
846
|
+
|
|
847
|
+
Returns:
|
|
848
|
+
The response JSON as a dictionary.
|
|
849
|
+
"""
|
|
850
|
+
if self.is_async:
|
|
851
|
+
response = self.service.post_async_call(Config.gtt_orders_sync, self.header, data)
|
|
852
|
+
else:
|
|
853
|
+
response = self.service.post_call(Config.gtt_orders_sync, self.header, data)
|
|
854
|
+
return response
|
|
780
855
|
|
|
781
856
|
def modify_order(self, data) -> dict:
|
|
782
857
|
"""
|
|
@@ -798,6 +873,30 @@ class FyersModel:
|
|
|
798
873
|
else:
|
|
799
874
|
response = self.service.patch_call(Config.orders_endpoint, self.header, data)
|
|
800
875
|
return response
|
|
876
|
+
|
|
877
|
+
def modify_gtt_order(self,data) -> dict:
|
|
878
|
+
"""
|
|
879
|
+
Modifies the parameters of a pending order based on the provided details.
|
|
880
|
+
|
|
881
|
+
Parameters:
|
|
882
|
+
id (str): Unique identifier for the order to be modified, e.g., "25010700000001"
|
|
883
|
+
orderInfo* (object): Contains updated information about the GTT/OCO order legs.
|
|
884
|
+
orderInfo.leg1* (object): Details for GTT order leg. Mandatory for all modifications.
|
|
885
|
+
orderInfo.leg1.price* (number): Updated price at which the order should be placed.
|
|
886
|
+
orderInfo.leg1.triggerPrice* (number): Updated trigger price for the GTT order. NOTE: for OCO order this leg trigger price should be always above LTP.
|
|
887
|
+
orderInfo.leg1.qty** (integer): Updated quantity for the GTT order leg.
|
|
888
|
+
orderInfo.leg2* (object): Details for OCO order leg. Required if the order is an OCO type.
|
|
889
|
+
orderInfo.leg2.triggerPrice* (number): Updated trigger price for the second leg of the OCO order.NOTE: for OCO order this leg trigger price should be always below LTP.
|
|
890
|
+
orderInfo.leg2.qty* (integer): Updated quantity for the second leg of the OCO order.
|
|
891
|
+
|
|
892
|
+
Returns:
|
|
893
|
+
The response JSON as a dictionary.
|
|
894
|
+
"""
|
|
895
|
+
if self.is_async:
|
|
896
|
+
response = self.service.patch_async_call(Config.gtt_orders_sync, self.header, data)
|
|
897
|
+
else:
|
|
898
|
+
response = self.service.patch_call(Config.gtt_orders_sync, self.header, data)
|
|
899
|
+
return response
|
|
801
900
|
|
|
802
901
|
def exit_positions(self, data={}) -> dict:
|
|
803
902
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: fyers_apiv3
|
|
3
|
-
Version: 3.1.
|
|
3
|
+
Version: 3.1.6
|
|
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
|
|
@@ -10,11 +10,20 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
12
|
License-File: LICENSE.txt
|
|
13
|
-
Requires-Dist: requests
|
|
14
|
-
Requires-Dist: asyncio
|
|
15
|
-
Requires-Dist: aiohttp
|
|
16
|
-
Requires-Dist:
|
|
17
|
-
Requires-Dist: websocket-client
|
|
13
|
+
Requires-Dist: requests==2.31.0
|
|
14
|
+
Requires-Dist: asyncio==3.4.3
|
|
15
|
+
Requires-Dist: aiohttp==3.9.3
|
|
16
|
+
Requires-Dist: aws_lambda_powertools==1.25.5
|
|
17
|
+
Requires-Dist: websocket-client==1.6.1
|
|
18
|
+
Requires-Dist: protobuf==5.29.3
|
|
19
|
+
Dynamic: author
|
|
20
|
+
Dynamic: author-email
|
|
21
|
+
Dynamic: classifier
|
|
22
|
+
Dynamic: description
|
|
23
|
+
Dynamic: description-content-type
|
|
24
|
+
Dynamic: home-page
|
|
25
|
+
Dynamic: requires-dist
|
|
26
|
+
Dynamic: summary
|
|
18
27
|
|
|
19
28
|
# The Fyers API Python client - v3
|
|
20
29
|
|
|
@@ -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=-UUO9LD3FVjre23m7SfVpJ44HcMBACPAnSUyWIS3yPA,21191
|
|
11
|
+
fyers_apiv3-3.1.6.dist-info/LICENSE.txt,sha256=_a5I4lWvSmoZQxwGSPGVVvUbuYby780N9YevsBqNY3k,1063
|
|
12
|
+
fyers_apiv3-3.1.6.dist-info/METADATA,sha256=vLA3LkYMTBqA-oMR26HEPEBFxmQdU04_zqg7F8nVKnE,16478
|
|
13
|
+
fyers_apiv3-3.1.6.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
|
14
|
+
fyers_apiv3-3.1.6.dist-info/top_level.txt,sha256=IaT774gXqIM6uJpgCQPvXruJBOINsupO9oTe2ao6pkc,12
|
|
15
|
+
fyers_apiv3-3.1.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|