wiz-trader 0.34.0__tar.gz → 0.36.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.
- {wiz_trader-0.34.0/src/wiz_trader.egg-info → wiz_trader-0.36.0}/PKG-INFO +584 -1
- {wiz_trader-0.34.0 → wiz_trader-0.36.0}/README.md +583 -0
- {wiz_trader-0.34.0 → wiz_trader-0.36.0}/pyproject.toml +1 -1
- {wiz_trader-0.34.0 → wiz_trader-0.36.0}/src/wiz_trader/__init__.py +1 -1
- {wiz_trader-0.34.0 → wiz_trader-0.36.0}/src/wiz_trader/apis/client.py +441 -1
- {wiz_trader-0.34.0 → wiz_trader-0.36.0}/src/wiz_trader/quotes/client.py +112 -6
- {wiz_trader-0.34.0 → wiz_trader-0.36.0/src/wiz_trader.egg-info}/PKG-INFO +584 -1
- {wiz_trader-0.34.0 → wiz_trader-0.36.0}/MANIFEST.in +0 -0
- {wiz_trader-0.34.0 → wiz_trader-0.36.0}/setup.cfg +0 -0
- {wiz_trader-0.34.0 → wiz_trader-0.36.0}/setup.py +0 -0
- {wiz_trader-0.34.0 → wiz_trader-0.36.0}/src/wiz_trader/apis/__init__.py +0 -0
- {wiz_trader-0.34.0 → wiz_trader-0.36.0}/src/wiz_trader/quotes/__init__.py +0 -0
- {wiz_trader-0.34.0 → wiz_trader-0.36.0}/src/wiz_trader.egg-info/SOURCES.txt +0 -0
- {wiz_trader-0.34.0 → wiz_trader-0.36.0}/src/wiz_trader.egg-info/dependency_links.txt +0 -0
- {wiz_trader-0.34.0 → wiz_trader-0.36.0}/src/wiz_trader.egg-info/requires.txt +0 -0
- {wiz_trader-0.34.0 → wiz_trader-0.36.0}/src/wiz_trader.egg-info/top_level.txt +0 -0
- {wiz_trader-0.34.0 → wiz_trader-0.36.0}/tests/test_apis.py +0 -0
- {wiz_trader-0.34.0 → wiz_trader-0.36.0}/tests/test_quotes.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: wiz_trader
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.36.0
|
4
4
|
Summary: A Python SDK for connecting to the Wizzer.
|
5
5
|
Home-page: https://bitbucket.org/wizzer-tech/quotes_sdk.git
|
6
6
|
Author: Pawan Wagh
|
@@ -38,6 +38,7 @@ Dynamic: requires-python
|
|
38
38
|
- [Callbacks](#callbacks)
|
39
39
|
- [Subscribing to Instruments](#subscribing-to-instruments)
|
40
40
|
- [Unsubscribing from Instruments](#unsubscribing-from-instruments)
|
41
|
+
- [Account Events](#account-events)
|
41
42
|
- [Handling WebSocket Connection](#handling-websocket-connection)
|
42
43
|
- [Complete Examples](#quotes-client-examples)
|
43
44
|
5. [Wizzer Client](#wizzer-client)
|
@@ -253,6 +254,347 @@ ws.unsubscribe(["NSE:RELIANCE:2885"])
|
|
253
254
|
- Can be called multiple times for different instrument sets
|
254
255
|
- No callback fired for unsubscribed instruments
|
255
256
|
|
257
|
+
### Account Events
|
258
|
+
|
259
|
+
The QuotesClient now supports automatic reception of account-related events (orders, trades, positions, holdings) from the quotes server. These events are automatically pushed to SDK users via dedicated callback handlers based on the event type.
|
260
|
+
|
261
|
+
#### Event Handlers
|
262
|
+
|
263
|
+
Register event handlers to receive real-time updates for your account activity:
|
264
|
+
|
265
|
+
```python
|
266
|
+
def on_order(ws, order):
|
267
|
+
print(f"Order event: {order}")
|
268
|
+
|
269
|
+
def on_trade(ws, trade):
|
270
|
+
print(f"Trade event: {trade}")
|
271
|
+
|
272
|
+
def on_position(ws, position):
|
273
|
+
print(f"Position event: {position}")
|
274
|
+
|
275
|
+
def on_holding(ws, holding):
|
276
|
+
print(f"Holding event: {holding}")
|
277
|
+
|
278
|
+
# Register event handlers
|
279
|
+
client.on_order = on_order
|
280
|
+
client.on_trade = on_trade
|
281
|
+
client.on_position = on_position
|
282
|
+
client.on_holding = on_holding
|
283
|
+
```
|
284
|
+
|
285
|
+
#### Key Features
|
286
|
+
|
287
|
+
- **Automatic Event Reception**: No explicit subscription needed - events are automatically sent based on your JWT token's account ID and strategy ID
|
288
|
+
- **Optional Handlers**: Events are silently dropped if no handler is registered - you only receive what you need
|
289
|
+
- **Message Normalization**: The SDK automatically handles different message formats and delivers clean, flattened event data to your handlers
|
290
|
+
- **Error Resilience**: Handler exceptions are caught and logged without breaking the event flow
|
291
|
+
- **Backward Compatible**: Existing tick and Greeks functionality remains unchanged
|
292
|
+
|
293
|
+
#### Event Data Structure
|
294
|
+
|
295
|
+
All event handlers receive flattened event data with the event type preserved:
|
296
|
+
|
297
|
+
```python
|
298
|
+
# Example order event data
|
299
|
+
{
|
300
|
+
"_id": "order_01k10np7weemkv812nx55bppw5",
|
301
|
+
"accountEntityType": "client",
|
302
|
+
"accountId": "user_01j9brznsffphvzqn9gzyj1frw",
|
303
|
+
"avgPrice": 72.65,
|
304
|
+
"broker": "wizzer",
|
305
|
+
"brokerOrderId": "dssorder_175344314711184",
|
306
|
+
"brokerTimestamp": "2025-07-25T11:32:28.147Z",
|
307
|
+
"cancelledQty": 0,
|
308
|
+
"completedAt": "2025-07-25T11:32:28.147Z",
|
309
|
+
"createdAt": "2025-07-25T11:32:27.722Z",
|
310
|
+
"disclosedQty": 0,
|
311
|
+
"exchange": "NSE",
|
312
|
+
"exchangeOrderId": "dssx_1753443147",
|
313
|
+
"exchangeToken": 11184,
|
314
|
+
"exchangeUpdateTimestamp": "2025-07-25T11:32:28.147Z",
|
315
|
+
"executionType": "regular",
|
316
|
+
"filledQty": 1,
|
317
|
+
"guid": "t0lVueivgBgcEh2k_BP",
|
318
|
+
"instrumentType": "EQLC",
|
319
|
+
"isDealer": false,
|
320
|
+
"isOrderModified": false,
|
321
|
+
"legs": [],
|
322
|
+
"marginUsed": 0,
|
323
|
+
"meta": {
|
324
|
+
"dss": {"orderId": "order_01k10np7weemkv812nx55bppw5"},
|
325
|
+
"source": "wizzer.dss",
|
326
|
+
"wizzer": {"mode": "paper_trading"}
|
327
|
+
},
|
328
|
+
"orderType": "MARKET",
|
329
|
+
"parentOrderId": "",
|
330
|
+
"pendingQty": 1,
|
331
|
+
"placedBy": "user_01j9brznsffphvzqn9gzyj1frw",
|
332
|
+
"price": 0,
|
333
|
+
"product": "CNC",
|
334
|
+
"qty": 1,
|
335
|
+
"remarks": "",
|
336
|
+
"segment": "NSECM",
|
337
|
+
"slPrice": 0,
|
338
|
+
"slTriggerPrice": 0,
|
339
|
+
"sqOffHoldingId": "",
|
340
|
+
"sqOffPositionId": "",
|
341
|
+
"status": "COMPLETED",
|
342
|
+
"stoploss": 0,
|
343
|
+
"stoplossOrderId": "",
|
344
|
+
"strategy": {
|
345
|
+
"id": "stgy_01jtn7t3erf8ss78spqy40k8en",
|
346
|
+
"identifier": "S-608377",
|
347
|
+
"name": "Test 1122"
|
348
|
+
},
|
349
|
+
"target": 0,
|
350
|
+
"targetOrderId": "",
|
351
|
+
"tradingSymbol": "IDFCFIRSTB",
|
352
|
+
"transactionType": "BUY",
|
353
|
+
"triggerId": "",
|
354
|
+
"triggerPrice": 0,
|
355
|
+
"updatedAt": "2025-07-25T11:32:28.254Z",
|
356
|
+
"userId": "user_01j9brznsffphvzqn9gzyj1frw",
|
357
|
+
"validity": "DAY",
|
358
|
+
"variety": "REGULAR"
|
359
|
+
}
|
360
|
+
|
361
|
+
# Example trade event data
|
362
|
+
{
|
363
|
+
"_id": "trade_01k10np8rqef793zr93ctqj0wt",
|
364
|
+
"accountId": "user_01j9brznsffphvzqn9gzyj1frw",
|
365
|
+
"avgPrice": 72.65,
|
366
|
+
"broker": "wizzer",
|
367
|
+
"brokerOrderId": "dssorder_175344314711184",
|
368
|
+
"createdAt": "2025-07-25T11:32:28.567Z",
|
369
|
+
"exchange": "NSE",
|
370
|
+
"exchangeOrderId": "dssx_1753443147",
|
371
|
+
"exchangeTimestamp": "2025-07-25T11:32:28.147Z",
|
372
|
+
"exchangeToken": 11184,
|
373
|
+
"isDividend": false,
|
374
|
+
"meta": {
|
375
|
+
"dss": {"orderId": "order_01k10np7weemkv812nx55bppw5"},
|
376
|
+
"source": "wizzer.dss",
|
377
|
+
"wizzer": {"mode": "paper_trading"}
|
378
|
+
},
|
379
|
+
"orderId": "order_01k10np7weemkv812nx55bppw5",
|
380
|
+
"orderType": "MARKET",
|
381
|
+
"parentOrderId": "",
|
382
|
+
"parentTradeId": "",
|
383
|
+
"product": "CNC",
|
384
|
+
"qty": 1,
|
385
|
+
"segment": "NSECM",
|
386
|
+
"strategy": {
|
387
|
+
"id": "stgy_01jtn7t3erf8ss78spqy40k8en",
|
388
|
+
"identifier": "S-608377",
|
389
|
+
"name": "Test 1122"
|
390
|
+
},
|
391
|
+
"tradeId": "dsstrade_50PccSfWvsCg-Cr5dQE",
|
392
|
+
"tradingSymbol": "IDFCFIRSTB",
|
393
|
+
"transactionType": "BUY",
|
394
|
+
"updatedAt": "2025-07-25T11:32:28.567Z",
|
395
|
+
"userId": "user_01j9brznsffphvzqn9gzyj1frw"
|
396
|
+
}
|
397
|
+
|
398
|
+
# Example position event data
|
399
|
+
{
|
400
|
+
"_id": "pos_01k10np8t7e0fs63q52hacbx8m",
|
401
|
+
"accountEntityType": "client",
|
402
|
+
"accountId": "user_01j9brznsffphvzqn9gzyj1frw",
|
403
|
+
"bep": 0,
|
404
|
+
"broker": "wizzer",
|
405
|
+
"buyAvgPrice": 72.65,
|
406
|
+
"buyOrders": ["order_01k10np7weemkv812nx55bppw5"],
|
407
|
+
"buyQty": 1,
|
408
|
+
"buyTrades": ["trade_01k10np8rqef793zr93ctqj0wt"],
|
409
|
+
"buyValue": 72.65,
|
410
|
+
"closePrice": 0,
|
411
|
+
"createdAt": "2025-07-25T11:32:28.614Z",
|
412
|
+
"exchange": "NSE",
|
413
|
+
"exchangeToken": 11184,
|
414
|
+
"instrumentType": "EQLC",
|
415
|
+
"lotSize": 1,
|
416
|
+
"meta": {
|
417
|
+
"source": "wizzer.dss",
|
418
|
+
"wizzer": {"mode": "paper_trading"}
|
419
|
+
},
|
420
|
+
"netQty": 1,
|
421
|
+
"pnlPercentage": 0,
|
422
|
+
"product": "CNC",
|
423
|
+
"realisedPnlToday": 0,
|
424
|
+
"segment": "NSECM",
|
425
|
+
"sellAvgPrice": 0,
|
426
|
+
"sellOrders": [],
|
427
|
+
"sellQty": 0,
|
428
|
+
"sellTrades": [],
|
429
|
+
"sellValue": 0,
|
430
|
+
"slPrice": null,
|
431
|
+
"slTriggerPrice": null,
|
432
|
+
"status": "open",
|
433
|
+
"stoploss": 0,
|
434
|
+
"strategy": {
|
435
|
+
"id": "stgy_01jtn7t3erf8ss78spqy40k8en",
|
436
|
+
"identifier": "S-608377",
|
437
|
+
"name": "Test 1122"
|
438
|
+
},
|
439
|
+
"target": 0,
|
440
|
+
"tradingSymbol": "IDFCFIRSTB",
|
441
|
+
"unrealisedPnlToday": 0,
|
442
|
+
"updatedAt": "2025-07-25T11:32:28.614Z",
|
443
|
+
"userId": "user_01j9brznsffphvzqn9gzyj1frw"
|
444
|
+
}
|
445
|
+
|
446
|
+
# Example holding event data
|
447
|
+
{
|
448
|
+
"_id": "holding_01k10np8t7e0fs63q52hacbx8m",
|
449
|
+
"accountEntityType": "client",
|
450
|
+
"accountId": "user_01j9brznsffphvzqn9gzyj1frw",
|
451
|
+
"bep": 0,
|
452
|
+
"broker": "wizzer",
|
453
|
+
"buyAvgPrice": 72.65,
|
454
|
+
"buyOrders": ["order_01k10np7weemkv812nx55bppw5"],
|
455
|
+
"buyQty": 1,
|
456
|
+
"buyTrades": ["trade_01k10np8rqef793zr93ctqj0wt"],
|
457
|
+
"buyValue": 72.65,
|
458
|
+
"closePrice": 0,
|
459
|
+
"createdAt": "2025-07-25T11:32:28.614Z",
|
460
|
+
"exchange": "NSE",
|
461
|
+
"exchangeToken": 11184,
|
462
|
+
"instrumentType": "EQLC",
|
463
|
+
"lotSize": 1,
|
464
|
+
"meta": {
|
465
|
+
"source": "wizzer.dss",
|
466
|
+
"wizzer": {"mode": "paper_trading"}
|
467
|
+
},
|
468
|
+
"netQty": 1,
|
469
|
+
"pnlPercentage": 0,
|
470
|
+
"product": "CNC",
|
471
|
+
"realisedPnlToday": 0,
|
472
|
+
"segment": "NSECM",
|
473
|
+
"sellAvgPrice": 0,
|
474
|
+
"sellOrders": [],
|
475
|
+
"sellQty": 0,
|
476
|
+
"sellTrades": [],
|
477
|
+
"sellValue": 0,
|
478
|
+
"slPrice": null,
|
479
|
+
"slTriggerPrice": null,
|
480
|
+
"status": "open",
|
481
|
+
"stoploss": 0,
|
482
|
+
"strategy": {
|
483
|
+
"id": "stgy_01jtn7t3erf8ss78spqy40k8en",
|
484
|
+
"identifier": "S-608377",
|
485
|
+
"name": "Test 1122"
|
486
|
+
},
|
487
|
+
"target": 0,
|
488
|
+
"tradingSymbol": "IDFCFIRSTB",
|
489
|
+
"unrealisedPnlToday": 0,
|
490
|
+
"updatedAt": "2025-07-25T11:32:28.614Z",
|
491
|
+
"userId": "user_01j9brznsffphvzqn9gzyj1frw"
|
492
|
+
}
|
493
|
+
```
|
494
|
+
|
495
|
+
#### Complete Event Handling Example
|
496
|
+
|
497
|
+
```python
|
498
|
+
from wiz_trader import QuotesClient
|
499
|
+
|
500
|
+
# Initialize client
|
501
|
+
client = QuotesClient(
|
502
|
+
base_url="wss://websocket-url/quotes",
|
503
|
+
token="your-jwt-token",
|
504
|
+
log_level="info"
|
505
|
+
)
|
506
|
+
|
507
|
+
def on_order(ws, order):
|
508
|
+
# React to order status changes
|
509
|
+
if order['status'] == ORDER_STATUS_COMPLETED:
|
510
|
+
print(f"Order {order['_id']} completed!")
|
511
|
+
elif order['status'] == ORDER_STATUS_REJECTED:
|
512
|
+
print(f"Order {order['_id']} rejected: {order.get('remarks', 'Unknown')}")
|
513
|
+
|
514
|
+
def on_trade(ws, trade):
|
515
|
+
print(f"Trade executed: {trade['qty']} x {trade['tradingSymbol']} @ {trade['avgPrice']}")
|
516
|
+
|
517
|
+
def on_position(ws, position):
|
518
|
+
print(f"Position update: {position['tradingSymbol']} = {position['netQty']} (PnL: {position.get('unrealisedPnlToday', 0)})")
|
519
|
+
|
520
|
+
def on_holding(ws, holding):
|
521
|
+
print(f"Holding update: {holding['tradingSymbol']} = {holding['netQty']}")
|
522
|
+
|
523
|
+
def on_connect(ws):
|
524
|
+
print("Connected! Ready to receive events.")
|
525
|
+
# Also subscribe to some ticks if needed
|
526
|
+
ws.subscribe(["NSE:SBIN:3045"], mode="ticks")
|
527
|
+
|
528
|
+
# Register all handlers
|
529
|
+
client.on_order = on_order
|
530
|
+
client.on_trade = on_trade
|
531
|
+
client.on_position = on_position
|
532
|
+
client.on_holding = on_holding
|
533
|
+
client.on_connect = on_connect
|
534
|
+
|
535
|
+
# Connect and receive events
|
536
|
+
client.connect()
|
537
|
+
```
|
538
|
+
|
539
|
+
#### Event-Driven Trading Strategy Example
|
540
|
+
|
541
|
+
```python
|
542
|
+
class EventDrivenStrategy:
|
543
|
+
def __init__(self):
|
544
|
+
self.orders = {}
|
545
|
+
self.positions = {}
|
546
|
+
self.pending_orders = set()
|
547
|
+
|
548
|
+
def on_order_event(self, ws, order):
|
549
|
+
order_id = order['_id']
|
550
|
+
self.orders[order_id] = order
|
551
|
+
|
552
|
+
# Track pending orders
|
553
|
+
if order['status'] == ORDER_STATUS_OPEN:
|
554
|
+
self.pending_orders.add(order_id)
|
555
|
+
elif order_id in self.pending_orders:
|
556
|
+
self.pending_orders.remove(order_id)
|
557
|
+
|
558
|
+
# Log order lifecycle
|
559
|
+
print(f"Order {order_id}: {order['status']}")
|
560
|
+
|
561
|
+
# React to rejections
|
562
|
+
if order['status'] == ORDER_STATUS_REJECTED:
|
563
|
+
print(f"Order rejected! Reason: {order.get('remarks', 'Unknown')}")
|
564
|
+
# Implement retry logic or alternative strategy
|
565
|
+
|
566
|
+
def on_position_event(self, ws, position):
|
567
|
+
symbol = position['tradingSymbol']
|
568
|
+
self.positions[symbol] = position
|
569
|
+
|
570
|
+
# Monitor position changes
|
571
|
+
print(f"Position {symbol}: Qty={position['netQty']}, PnL={position.get('unrealisedPnlToday', 0)}")
|
572
|
+
|
573
|
+
# Implement stop-loss or take-profit logic
|
574
|
+
pnl = position.get('unrealisedPnlToday', 0)
|
575
|
+
if pnl < -100: # Stop loss at -100
|
576
|
+
print(f"Stop loss triggered for {symbol}")
|
577
|
+
# Place exit order
|
578
|
+
elif pnl > 200: # Take profit at 200
|
579
|
+
print(f"Take profit triggered for {symbol}")
|
580
|
+
# Place exit order
|
581
|
+
|
582
|
+
def on_trade_event(self, ws, trade):
|
583
|
+
print(f"Trade executed: {trade['qty']} @ {trade['avgPrice']}")
|
584
|
+
# Update internal trade log, calculate VWAP, etc.
|
585
|
+
|
586
|
+
# Initialize strategy
|
587
|
+
strategy = EventDrivenStrategy()
|
588
|
+
|
589
|
+
# Set up client with strategy handlers
|
590
|
+
client = QuotesClient(base_url="wss://...", token="...")
|
591
|
+
client.on_order = strategy.on_order_event
|
592
|
+
client.on_position = strategy.on_position_event
|
593
|
+
client.on_trade = strategy.on_trade_event
|
594
|
+
|
595
|
+
client.connect()
|
596
|
+
```
|
597
|
+
|
256
598
|
### Complete Examples
|
257
599
|
|
258
600
|
#### Blocking Example
|
@@ -425,6 +767,7 @@ Available constants:
|
|
425
767
|
- Transaction types: `TRANSACTION_TYPE_BUY`, `TRANSACTION_TYPE_SELL`
|
426
768
|
- Product types: `PRODUCT_CNC`, `PRODUCT_MIS`, `PRODUCT_NRML`
|
427
769
|
- Order types: `ORDER_TYPE_MARKET`, `ORDER_TYPE_LIMIT`, `ORDER_TYPE_SL`, `ORDER_TYPE_SLM`
|
770
|
+
- Order status: `ORDER_STATUS_OPEN`, `ORDER_STATUS_CANCELLED`, `ORDER_STATUS_REJECTED`, `ORDER_STATUS_PENDING`, `ORDER_STATUS_COMPLETED`
|
428
771
|
- Validity types: `VALIDITY_DAY`, `VALIDITY_IOC`, `VALIDITY_GTT`
|
429
772
|
- Variety types: `VARIETY_REGULAR`, `VARIETY_AMO`, `VARIETY_BO`, `VARIETY_CO`
|
430
773
|
- Exchanges: `EXCHANGE_NSE`, `EXCHANGE_BSE`, `EXCHANGE_WZR`
|
@@ -3663,3 +4006,243 @@ results = client.run_screener(
|
|
3663
4006
|
limit=10
|
3664
4007
|
)
|
3665
4008
|
```
|
4009
|
+
|
4010
|
+
## KV Store Integration
|
4011
|
+
|
4012
|
+
The WizzerClient includes comprehensive Key-Value (KV) store functionality for persistent data management. This feature allows you to store configuration data, state information, user preferences, and other persistent data associated with your trading strategies.
|
4013
|
+
|
4014
|
+
### Supported Data Types
|
4015
|
+
|
4016
|
+
The KV store supports all JSON-serializable data types:
|
4017
|
+
|
4018
|
+
- **STRING**: Text values (`"production"`, `"user123"`)
|
4019
|
+
- **NUMBER**: Integer and float values (`42`, `150.25`)
|
4020
|
+
- **BOOLEAN**: True/false values (`True`, `False`)
|
4021
|
+
- **ARRAY**: Lists of values (`["AAPL", "GOOGL", "MSFT"]`)
|
4022
|
+
- **OBJECT**: Dictionaries/objects (`{"positions": 30, "cash": 50000}`)
|
4023
|
+
|
4024
|
+
### Available Constants
|
4025
|
+
|
4026
|
+
```python
|
4027
|
+
client.KV_TYPE_STRING # "string"
|
4028
|
+
client.KV_TYPE_NUMBER # "number"
|
4029
|
+
client.KV_TYPE_BOOLEAN # "boolean"
|
4030
|
+
client.KV_TYPE_ARRAY # "array"
|
4031
|
+
client.KV_TYPE_OBJECT # "object"
|
4032
|
+
```
|
4033
|
+
|
4034
|
+
### Core CRUD Operations
|
4035
|
+
|
4036
|
+
#### Create Key-Value Pair
|
4037
|
+
|
4038
|
+
```python
|
4039
|
+
# Store different data types
|
4040
|
+
client.create_kv("environment", "production")
|
4041
|
+
client.create_kv("trade_count", 42)
|
4042
|
+
client.create_kv("is_active", True)
|
4043
|
+
client.create_kv("symbols", ["AAPL", "GOOGL", "MSFT"])
|
4044
|
+
|
4045
|
+
# Store complex configuration with TTL (expires in 1 hour)
|
4046
|
+
config = {
|
4047
|
+
"risk_level": "medium",
|
4048
|
+
"max_positions": 10,
|
4049
|
+
"stop_loss_pct": 0.05,
|
4050
|
+
"take_profit_pct": 0.15
|
4051
|
+
}
|
4052
|
+
client.create_kv("strategy_config", config, ttl=3600)
|
4053
|
+
```
|
4054
|
+
|
4055
|
+
#### Retrieve Key-Value Pair
|
4056
|
+
|
4057
|
+
```python
|
4058
|
+
# Get a single KV pair
|
4059
|
+
data = client.get_kv("strategy_config")
|
4060
|
+
print(data["value"]) # The stored object
|
4061
|
+
print(data["type"]) # "object"
|
4062
|
+
print(data["ttl"]) # Remaining time to live (if set)
|
4063
|
+
|
4064
|
+
# Access nested values
|
4065
|
+
risk_level = data["value"]["risk_level"] # "medium"
|
4066
|
+
```
|
4067
|
+
|
4068
|
+
#### Update Key-Value Pair (Complete Replacement)
|
4069
|
+
|
4070
|
+
```python
|
4071
|
+
# Complete replacement of existing key
|
4072
|
+
new_config = {"risk_level": "high", "max_positions": 5}
|
4073
|
+
client.update_kv("strategy_config", new_config, ttl=1800)
|
4074
|
+
```
|
4075
|
+
|
4076
|
+
#### Patch Key-Value Pair (Partial Update)
|
4077
|
+
|
4078
|
+
```python
|
4079
|
+
# For objects - merges with existing data
|
4080
|
+
client.patch_kv("strategy_config", {"last_updated": "2024-01-15"})
|
4081
|
+
|
4082
|
+
# For non-objects - replaces entirely
|
4083
|
+
client.patch_kv("trade_count", 50)
|
4084
|
+
|
4085
|
+
# Update only TTL
|
4086
|
+
client.patch_kv("strategy_config", ttl=7200)
|
4087
|
+
```
|
4088
|
+
|
4089
|
+
#### Delete Key-Value Pair
|
4090
|
+
|
4091
|
+
```python
|
4092
|
+
response = client.delete_kv("old_config")
|
4093
|
+
if response["success"]:
|
4094
|
+
print("Key deleted successfully")
|
4095
|
+
```
|
4096
|
+
|
4097
|
+
### List Operations
|
4098
|
+
|
4099
|
+
#### Get All KV Pairs (with values)
|
4100
|
+
|
4101
|
+
```python
|
4102
|
+
# Get first page (20 items)
|
4103
|
+
kvs = client.get_all_kvs(page_no=1)
|
4104
|
+
|
4105
|
+
# Get all pages automatically
|
4106
|
+
all_kvs = client.get_all_kvs(paginate=True)
|
4107
|
+
|
4108
|
+
for kv in all_kvs:
|
4109
|
+
print(f"Key: {kv['key']}")
|
4110
|
+
print(f"Type: {kv['type']}")
|
4111
|
+
print(f"Value: {kv['value']}")
|
4112
|
+
```
|
4113
|
+
|
4114
|
+
#### Get Keys Only (memory efficient)
|
4115
|
+
|
4116
|
+
```python
|
4117
|
+
# Get all keys without values (faster for large datasets)
|
4118
|
+
keys = client.get_kv_keys(paginate=True)
|
4119
|
+
|
4120
|
+
for key_info in keys:
|
4121
|
+
print(f"Key: {key_info['key']}, Type: {key_info['type']}")
|
4122
|
+
if 'ttl' in key_info:
|
4123
|
+
print(f" TTL: {key_info['ttl']} seconds remaining")
|
4124
|
+
```
|
4125
|
+
|
4126
|
+
### Advanced Usage Examples
|
4127
|
+
|
4128
|
+
#### Strategy Configuration Management
|
4129
|
+
|
4130
|
+
```python
|
4131
|
+
# Store strategy parameters
|
4132
|
+
params = {
|
4133
|
+
"moving_average_period": 20,
|
4134
|
+
"rsi_oversold": 30,
|
4135
|
+
"rsi_overbought": 70,
|
4136
|
+
"position_size": 0.02
|
4137
|
+
}
|
4138
|
+
client.create_kv("strategy_params", params)
|
4139
|
+
|
4140
|
+
# Update specific parameters using patch (merges with existing)
|
4141
|
+
client.patch_kv("strategy_params", {"moving_average_period": 50})
|
4142
|
+
```
|
4143
|
+
|
4144
|
+
#### State Persistence
|
4145
|
+
|
4146
|
+
```python
|
4147
|
+
# Save current portfolio state
|
4148
|
+
portfolio_state = {
|
4149
|
+
"cash_balance": 50000,
|
4150
|
+
"open_positions": 5,
|
4151
|
+
"daily_pnl": 1250.75,
|
4152
|
+
"last_updated": "2024-01-15T15:30:00Z"
|
4153
|
+
}
|
4154
|
+
client.create_kv("portfolio_state", portfolio_state, ttl=3600)
|
4155
|
+
|
4156
|
+
# Update state periodically
|
4157
|
+
client.patch_kv("portfolio_state", {
|
4158
|
+
"daily_pnl": 1500.25,
|
4159
|
+
"last_updated": "2024-01-15T16:00:00Z"
|
4160
|
+
})
|
4161
|
+
```
|
4162
|
+
|
4163
|
+
#### Feature Flags
|
4164
|
+
|
4165
|
+
```python
|
4166
|
+
# Enable/disable features dynamically
|
4167
|
+
features = {
|
4168
|
+
"advanced_charts": True,
|
4169
|
+
"paper_trading": True,
|
4170
|
+
"options_trading": False,
|
4171
|
+
"algo_trading": True
|
4172
|
+
}
|
4173
|
+
client.create_kv("feature_flags", features)
|
4174
|
+
|
4175
|
+
# Toggle a feature
|
4176
|
+
client.patch_kv("feature_flags", {"options_trading": True})
|
4177
|
+
```
|
4178
|
+
|
4179
|
+
#### Data Caching with TTL
|
4180
|
+
|
4181
|
+
```python
|
4182
|
+
# Cache market data with short TTL (5 minutes)
|
4183
|
+
market_status = {
|
4184
|
+
"nse_open": True,
|
4185
|
+
"bse_open": True,
|
4186
|
+
"last_checked": "2024-01-15T09:15:00Z"
|
4187
|
+
}
|
4188
|
+
client.create_kv("market_status", market_status, ttl=300)
|
4189
|
+
```
|
4190
|
+
|
4191
|
+
### Object Merge Behavior
|
4192
|
+
|
4193
|
+
For OBJECT type data, the `patch_kv` method performs intelligent merging:
|
4194
|
+
|
4195
|
+
```python
|
4196
|
+
# Initial object
|
4197
|
+
client.create_kv("user_prefs", {
|
4198
|
+
"theme": "dark",
|
4199
|
+
"notifications": True,
|
4200
|
+
"language": "en"
|
4201
|
+
})
|
4202
|
+
|
4203
|
+
# Patch merges new fields with existing ones
|
4204
|
+
client.patch_kv("user_prefs", {
|
4205
|
+
"notifications": False, # Updates existing field
|
4206
|
+
"timezone": "UTC" # Adds new field
|
4207
|
+
})
|
4208
|
+
|
4209
|
+
# Result: {"theme": "dark", "notifications": False, "language": "en", "timezone": "UTC"}
|
4210
|
+
```
|
4211
|
+
|
4212
|
+
### Delete All KV Pairs
|
4213
|
+
|
4214
|
+
The `delete_all_kv` method allows you to remove all key-value pairs for a strategy at once:
|
4215
|
+
|
4216
|
+
```python
|
4217
|
+
# Delete all KV pairs for the current strategy
|
4218
|
+
response = client.delete_all_kv()
|
4219
|
+
print(f"Deleted {response['deleted']} key-value pairs")
|
4220
|
+
|
4221
|
+
# Example response:
|
4222
|
+
# {
|
4223
|
+
# "success": True,
|
4224
|
+
# "deleted": 15,
|
4225
|
+
# "message": "Successfully deleted 15 key-value pairs"
|
4226
|
+
# }
|
4227
|
+
|
4228
|
+
# Common usage - reset strategy state before reinitialization
|
4229
|
+
def reset_strategy_state():
|
4230
|
+
"""Reset all persistent state for the strategy."""
|
4231
|
+
# Clear all existing KV pairs
|
4232
|
+
result = client.delete_all_kv()
|
4233
|
+
print(f"Cleared {result['deleted']} KV pairs")
|
4234
|
+
|
4235
|
+
# Reinitialize with default configuration
|
4236
|
+
client.create_kv("config", {
|
4237
|
+
"mode": "production",
|
4238
|
+
"risk_percentage": 0.02,
|
4239
|
+
"max_positions": 10
|
4240
|
+
})
|
4241
|
+
client.create_kv("state", {
|
4242
|
+
"initialized": True,
|
4243
|
+
"start_time": datetime.now().isoformat()
|
4244
|
+
})
|
4245
|
+
print("Strategy state reset successfully")
|
4246
|
+
|
4247
|
+
# Use with caution - this operation cannot be undone!
|
4248
|
+
```
|