kkunal 1.0.0__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.
kkunal-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kkunal
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT/TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
kkunal-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,410 @@
1
+ Metadata-Version: 2.4
2
+ Name: kkunal
3
+ Version: 1.0.0
4
+ Summary: Kkunal - Python library for Choice FINX Trading API
5
+ Author: Kkunal
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Operating System :: OS Independent
9
+ Requires-Python: >=3.7
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Requires-Dist: requests>=2.28.0
13
+ Requires-Dist: pycryptodome>=3.17.0
14
+ Requires-Dist: websockets>=11.0.3
15
+ Requires-Dist: pandas>=1.3.0
16
+ Dynamic: author
17
+ Dynamic: classifier
18
+ Dynamic: description
19
+ Dynamic: description-content-type
20
+ Dynamic: license-file
21
+ Dynamic: requires-dist
22
+ Dynamic: requires-python
23
+ Dynamic: summary
24
+
25
+ # Kkunal
26
+
27
+ A Python library for the Choice FINX Trading API. Supports REST API, Interactive WebSockets (order/trade updates), and Live Price Feed WebSockets (FIX3.0 compressed data).
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pip install kkunal
33
+ ```
34
+
35
+ All dependencies (`requests`, `pycryptodome`, `websockets`, `pandas`) are installed automatically.
36
+
37
+ ---
38
+
39
+ ## Quick Start
40
+
41
+ ```python
42
+ from choice_api import ChoiceClient
43
+
44
+ client = ChoiceClient(
45
+ vendor_id="YOUR_VENDOR_ID",
46
+ vendor_key="YOUR_VENDOR_KEY",
47
+ api_key="YOUR_JWT_BEARER_TOKEN",
48
+ aes_key="YOUR_AES_KEY",
49
+ aes_iv="YOUR_AES_IV"
50
+ )
51
+
52
+ # Login (TOTP flow is handled automatically)
53
+ session_id = client.login(mobile_no="1234567890")
54
+ print(f"Session ID: {session_id}")
55
+ ```
56
+
57
+ ### Session Persistence
58
+
59
+ You can save and reload sessions to avoid logging in repeatedly during the same trading day:
60
+
61
+ ```python
62
+ session_file = "my_session.json"
63
+
64
+ if client.load_session(session_file):
65
+ print("Restored today's session.")
66
+ else:
67
+ client.login(mobile_no="1234567890")
68
+ client.save_session(session_file)
69
+ ```
70
+
71
+ > **Note:** Sessions expire daily. `load_session` will return `False` if the saved session is from a previous day.
72
+
73
+ ---
74
+
75
+ ## Scrip Master
76
+
77
+ The Scrip Master CSV is automatically downloaded when you log in. It maps instrument symbols to their tokens, lot sizes, and other metadata.
78
+
79
+ ### `get_token(symbol, exchange=None)`
80
+
81
+ Returns the token for a given symbol.
82
+
83
+ - For **NSE** instruments, no `exchange` parameter is needed (returns NSE by default).
84
+ - For **BSE** instruments, pass `exchange="BSE"` explicitly.
85
+
86
+ ```python
87
+ # NSE (default)
88
+ reliance_token = client.scrip_master.get_token("RELIANCE")
89
+
90
+ # BSE (must specify exchange)
91
+ reliance_bse_token = client.scrip_master.get_token("RELIANCE", exchange="BSE")
92
+ ```
93
+
94
+ ### `get_details(token)`
95
+
96
+ Returns all CSV row details for a given token as a dictionary.
97
+
98
+ ```python
99
+ details = client.scrip_master.get_details("2885")
100
+ print(details)
101
+ # {'Exchange': 'NSE', 'Segment': '1', 'Token': '2885', 'Symbol': 'RELIANCE', ...}
102
+ ```
103
+
104
+ ### `get_lot_size(token)`
105
+
106
+ Returns the market lot size for a token.
107
+
108
+ ```python
109
+ lot = client.scrip_master.get_lot_size("2885")
110
+ print(lot) # 1 for equity, 250 for NIFTY futures, etc.
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Orders
116
+
117
+ > **Important:** Prices must be in **paisa** (multiply INR by 100). For F&O orders, `qty` must be in **total shares** (multiples of the lot size), not the number of lots.
118
+
119
+ ### `client.orders.place_order(...)`
120
+
121
+ | Parameter | Type | Description |
122
+ |---|---|---|
123
+ | `segment_id` | `int` | `1` = NSE Cash, `2` = NSE F&O, `3` = BSE Cash |
124
+ | `token` | `int` | Instrument token from Scrip Master |
125
+ | `order_type` | `str` | `"RL_MKT"` = Market, `"RL_LIMIT"` = Limit |
126
+ | `bs` | `int` | `1` = Buy, `2` = Sell |
127
+ | `qty` | `int` | Total quantity in shares |
128
+ | `price` | `float` | Price in paisa (e.g., 1300 INR → `130000`) |
129
+ | `trigger_price` | `float` | Trigger price in paisa (0 for non-SL orders) |
130
+ | `validity` | `int` | `1` = Day |
131
+ | `product_type` | `str` | `"M"` = Intraday (Margin), `"D"` = Delivery/CarryForward |
132
+ | `disclosed_qty` | `int` | Optional. Disclosed quantity (default `0`) |
133
+
134
+ ```python
135
+ response = client.orders.place_order(
136
+ segment_id=1,
137
+ token=2885,
138
+ order_type="RL_MKT",
139
+ bs=1,
140
+ qty=1,
141
+ price=0,
142
+ trigger_price=0,
143
+ validity=1,
144
+ product_type="D"
145
+ )
146
+ ```
147
+
148
+ ### `client.orders.modify_order(...)`
149
+
150
+ Modifies an existing order. Requires `client_order_no`, `exchange_order_no`, and `gateway_order_no` from the order book.
151
+
152
+ ```python
153
+ response = client.orders.modify_order(
154
+ client_order_no=123456,
155
+ exchange_order_no="1234567890",
156
+ gateway_order_no="1234567890",
157
+ segment_id=1,
158
+ token=2885,
159
+ order_type="RL_LIMIT",
160
+ bs=1,
161
+ qty=1,
162
+ price=130000,
163
+ trigger_price=0,
164
+ validity=1,
165
+ product_type="D"
166
+ )
167
+ ```
168
+
169
+ ### `client.orders.cancel_order(...)`
170
+
171
+ Cancels an existing order. Same parameters as `modify_order` plus optional `exchange_order_time`.
172
+
173
+ ### `client.orders.get_order_book()`
174
+
175
+ Returns all orders placed during the current session.
176
+
177
+ ```python
178
+ order_book = client.orders.get_order_book()
179
+ ```
180
+
181
+ ### `client.orders.get_order_book_v2()`
182
+
183
+ Returns the order book (version 2 format).
184
+
185
+ ### `client.orders.get_order_by_no(order_no)`
186
+
187
+ Returns details for a specific order number.
188
+
189
+ ```python
190
+ order = client.orders.get_order_by_no(123456)
191
+ ```
192
+
193
+ ### `client.orders.get_trade_book()`
194
+
195
+ Returns all executed trades.
196
+
197
+ ```python
198
+ trades = client.orders.get_trade_book()
199
+ ```
200
+
201
+ ### `client.orders.get_order_messages(req_id)`
202
+
203
+ Returns order-related messages for a given request ID.
204
+
205
+ ---
206
+
207
+ ## Portfolio
208
+
209
+ ### `client.portfolio.get_holdings()`
210
+
211
+ Returns current holdings.
212
+
213
+ ```python
214
+ holdings = client.portfolio.get_holdings()
215
+ ```
216
+
217
+ ### `client.portfolio.get_net_position()`
218
+
219
+ Returns net positions.
220
+
221
+ ```python
222
+ positions = client.portfolio.get_net_position()
223
+ ```
224
+
225
+ ### `client.portfolio.position_conversion(...)`
226
+
227
+ Converts an open position from one product type to another (e.g., Intraday to Delivery).
228
+
229
+ | Parameter | Type | Description |
230
+ |---|---|---|
231
+ | `segment_id` | `int` | Exchange segment |
232
+ | `token` | `int` | Instrument token |
233
+ | `client_order_no` | `int` | Client order number |
234
+ | `buy_sell` | `int` | `1` = Buy, `2` = Sell |
235
+ | `quantity` | `int` | Quantity to convert |
236
+ | `product_type` | `str` | Target product type |
237
+ | `source_product_type` | `str` | Current product type |
238
+
239
+ ### `client.portfolio.verify_dis(...)`
240
+
241
+ Verifies eDIS (Electronic Delivery Instruction Slip) for delivery sell orders.
242
+
243
+ ### `client.portfolio.get_dis_status()`
244
+
245
+ Returns the current DIS verification status.
246
+
247
+ ---
248
+
249
+ ## Funds
250
+
251
+ ### `client.funds.get_funds_view()`
252
+
253
+ Returns funds summary.
254
+
255
+ ```python
256
+ funds = client.funds.get_funds_view()
257
+ ```
258
+
259
+ ### `client.funds.get_funds_view_new()`
260
+
261
+ Returns funds summary in the new format.
262
+
263
+ ### `client.funds.process_payout(amount, bank_acc_no, product_type=0)`
264
+
265
+ Initiates a fund withdrawal.
266
+
267
+ ### `client.funds.payment_via_netbanking(amount, bank_acc_no, bank_ifsc_code, return_url, segment_id, product_type=0)`
268
+
269
+ Initiates a net banking payment.
270
+
271
+ ### `client.funds.payment_via_hdfc_upi(amount, bank_acc_no, user_vpa, segment_id, product_type=0)`
272
+
273
+ Initiates a HDFC UPI payment.
274
+
275
+ ### `client.funds.check_vpa(user_vpa)`
276
+
277
+ Validates a UPI VPA address.
278
+
279
+ ### `client.funds.payment_via_razorpay(amount, bank_acc_no, bank_ifsc_code, upi_id, segment_id, payment_type=0, product_type=0)`
280
+
281
+ Initiates a RazorPay payment.
282
+
283
+ ### `client.funds.payment_ack_response(transaction_id)`
284
+
285
+ Acknowledges a payment transaction.
286
+
287
+ ---
288
+
289
+ ## Market
290
+
291
+ ### `client.market.get_market_status()`
292
+
293
+ Returns current market status across all segments.
294
+
295
+ ```python
296
+ status = client.market.get_market_status()
297
+ ```
298
+
299
+ ### `client.market.get_user_profile()`
300
+
301
+ Returns the authenticated user's profile.
302
+
303
+ ```python
304
+ profile = client.market.get_user_profile()
305
+ ```
306
+
307
+ ### `client.market.get_multiple_touchline(multiple_seg_token)`
308
+
309
+ Returns touchline data for multiple instruments.
310
+
311
+ ```python
312
+ # Format: "SegmentId1,Token1|SegmentId2,Token2"
313
+ touchline = client.market.get_multiple_touchline("1,2885|1,11536")
314
+ ```
315
+
316
+ ---
317
+
318
+ ## Historical Data
319
+
320
+ ### `client.historical.get_historical_data(segment_id, token, from_date, to_date, resolution)`
321
+
322
+ Returns historical OHLCV data as a **Pandas DataFrame**.
323
+
324
+ | Parameter | Type | Description |
325
+ |---|---|---|
326
+ | `segment_id` | `int` | Exchange segment |
327
+ | `token` | `int` | Instrument token |
328
+ | `from_date` | `str` or `int` | Start date (`"YYYY-MM-DD"` or seconds from 1980) |
329
+ | `to_date` | `str` or `int` | End date (`"YYYY-MM-DD"` or seconds from 1980) |
330
+ | `resolution` | `str` | `"1"` = 1 min, `"5"` = 5 min, `"D"` = Daily |
331
+
332
+ ```python
333
+ df = client.historical.get_historical_data(
334
+ segment_id=1,
335
+ token=2885,
336
+ from_date="2024-01-01",
337
+ to_date="2024-12-31",
338
+ resolution="D"
339
+ )
340
+ print(df.head())
341
+ # Time Open High Low Close Volume OI
342
+ # 0 2024-01-01 00:00:00 2501.00 2520.50 2490.00 2515.30 1234567 0
343
+ ```
344
+
345
+ The returned DataFrame has columns: `Time`, `Open`, `High`, `Low`, `Close`, `Volume`, `OI`. Prices are automatically adjusted using the `PriceDivisor` from the API response.
346
+
347
+ ---
348
+
349
+ ## Interactive WebSockets
350
+
351
+ Receives live order updates, trade confirmations, and market status events.
352
+
353
+ ```python
354
+ import asyncio
355
+ from choice_api import InteractiveSocketClient
356
+
357
+ async def main():
358
+ ws = InteractiveSocketClient(token=client.session_id)
359
+
360
+ ws.on("ORD_NRML", lambda data: print(f"Order Update: {data}"))
361
+ ws.on("TRD_MSG", lambda data: print(f"Trade: {data}"))
362
+ ws.on("MKT_STAT", lambda data: print(f"Market Status: {data}"))
363
+
364
+ await ws.connect()
365
+
366
+ asyncio.run(main())
367
+ ```
368
+
369
+ **Event types:** `ORD_NRML` (order updates), `TRD_MSG` (trade confirmations), `MKT_STAT` (market open/close).
370
+
371
+ ---
372
+
373
+ ## Price Feed WebSockets (FIX3.0)
374
+
375
+ Receives live Level 1 (Touchline) and Level 2 (Best Five / Depth) market data via TCP socket with Zlib compression.
376
+
377
+ ```python
378
+ import asyncio
379
+ from choice_api import PriceFeedSocketClient
380
+
381
+ async def main():
382
+ feed = PriceFeedSocketClient(
383
+ host=client.bcast_ip,
384
+ port=client.bcast_port,
385
+ user_id="YOUR_USER_ID"
386
+ )
387
+
388
+ feed.on_message(lambda raw: print(f"Feed: {raw}"))
389
+
390
+ # Start connection (sends login automatically)
391
+ asyncio.create_task(feed.connect())
392
+
393
+ # Wait for connection, then subscribe
394
+ await asyncio.sleep(2)
395
+ feed.subscribe_touchline(client.session_id, segment_id=1, token=2885)
396
+ feed.subscribe_best_five(client.session_id, segment_id=1, token=2885)
397
+
398
+ # Keep running
399
+ await asyncio.sleep(3600)
400
+
401
+ asyncio.run(main())
402
+ ```
403
+
404
+ ---
405
+
406
+ ## Logoff
407
+
408
+ ```python
409
+ client.logoff()
410
+ ```