dnse-sdk-openapi 0.0.1__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.
Files changed (50) hide show
  1. broker-api/get_list_care_by.py +22 -0
  2. dnse/__init__.py +4 -0
  3. dnse/client.py +408 -0
  4. dnse/common.py +103 -0
  5. dnse_sdk_openapi-0.0.1.dist-info/METADATA +120 -0
  6. dnse_sdk_openapi-0.0.1.dist-info/RECORD +50 -0
  7. dnse_sdk_openapi-0.0.1.dist-info/WHEEL +5 -0
  8. dnse_sdk_openapi-0.0.1.dist-info/top_level.txt +5 -0
  9. marketdata-api/get_instruments.py +22 -0
  10. marketdata-api/get_latest_trade.py +22 -0
  11. marketdata-api/get_ohlc.py +31 -0
  12. marketdata-api/get_security_definition.py +22 -0
  13. marketdata-api/get_trades.py +22 -0
  14. marketdata-api/get_working_dates.py +22 -0
  15. trading-api/cancel_order.py +29 -0
  16. trading-api/close_position.py +27 -0
  17. trading-api/create_trading_token.py +26 -0
  18. trading-api/get_accounts.py +22 -0
  19. trading-api/get_balances.py +22 -0
  20. trading-api/get_close_price.py +22 -0
  21. trading-api/get_execution_detail.py +28 -0
  22. trading-api/get_loan_packages.py +27 -0
  23. trading-api/get_order_detail.py +28 -0
  24. trading-api/get_order_history.py +30 -0
  25. trading-api/get_orders.py +27 -0
  26. trading-api/get_position_by_id.py +26 -0
  27. trading-api/get_positions.py +26 -0
  28. trading-api/get_ppse.py +29 -0
  29. trading-api/post_order.py +38 -0
  30. trading-api/put_order.py +35 -0
  31. trading-api/send_email_otp.py +22 -0
  32. websocket-marketdata/expected_price.py +53 -0
  33. websocket-marketdata/foreign_investor.py +51 -0
  34. websocket-marketdata/market_index.py +52 -0
  35. websocket-marketdata/ohlc.py +55 -0
  36. websocket-marketdata/ohlc_closed.py +55 -0
  37. websocket-marketdata/order.py +51 -0
  38. websocket-marketdata/quote.py +50 -0
  39. websocket-marketdata/sec_def.py +52 -0
  40. websocket-marketdata/trade.py +52 -0
  41. websocket-marketdata/trade_extra.py +51 -0
  42. websocket-marketdata/trading_websocket/__init__.py +33 -0
  43. websocket-marketdata/trading_websocket/_version.py +3 -0
  44. websocket-marketdata/trading_websocket/auth.py +59 -0
  45. websocket-marketdata/trading_websocket/client.py +790 -0
  46. websocket-marketdata/trading_websocket/connection.py +151 -0
  47. websocket-marketdata/trading_websocket/encoding.py +78 -0
  48. websocket-marketdata/trading_websocket/exceptions.py +38 -0
  49. websocket-marketdata/trading_websocket/models.py +525 -0
  50. websocket-marketdata/trading_websocket/py.typed +0 -0
@@ -0,0 +1,525 @@
1
+ """Data models for market data and private channel updates.
2
+
3
+ This module provides typed data models for all message types:
4
+ - Market data: Trade, Quote, OHLC, ExpectedPrice, TradeExtra, SecurityDefinition
5
+ - Private channels: Order, Position, AccountUpdate
6
+
7
+ All models support parsing from both abbreviated (MessagePack) and full (JSON) field names.
8
+ """
9
+
10
+ from dataclasses import dataclass, field
11
+ from datetime import datetime
12
+ from decimal import Decimal
13
+ from typing import Optional, List, Dict, Any, Tuple
14
+
15
+
16
+ def parse_timestamp(v: Any, date_only: bool = False) -> Optional[str]:
17
+ """Parse various timestamp formats into string.
18
+
19
+ Supports:
20
+ - protobuf: {'Seconds': 1501718400, 'Nanos': 0}
21
+ - ISO string: '2017-08-03T00:00:00Z'
22
+ - Unix int/float: 1501718400
23
+ """
24
+ try:
25
+ if v is None:
26
+ return None
27
+ fmt = "%Y-%m-%d" if date_only else "%Y-%m-%d %H:%M:%S"
28
+ if isinstance(v, str):
29
+ return datetime.fromisoformat(v.replace("Z", "+00:00")).strftime(fmt)
30
+ if isinstance(v, dict):
31
+ seconds = v.get("Seconds", v.get("seconds", 0))
32
+ nanos = v.get("Nanos", v.get("nanos", 0))
33
+ return datetime.fromtimestamp(seconds + nanos / 1e9).strftime(fmt)
34
+ if isinstance(v, (int, float)):
35
+ return datetime.fromtimestamp(v).strftime(fmt)
36
+ except Exception:
37
+ return None
38
+
39
+
40
+ @dataclass
41
+ class PriceLevel:
42
+ price: float
43
+ quantity: int
44
+
45
+ @classmethod
46
+ def from_dict(cls, data: Dict[str, Any]) -> "PriceLevel":
47
+ return cls(
48
+ price=data.get("price"),
49
+ quantity=data.get("qtty")
50
+ )
51
+
52
+
53
+ @dataclass
54
+ class Trade:
55
+ marketId: str
56
+ boardId: str
57
+ isin: str
58
+ symbol: str
59
+ price: float
60
+ quantity: int
61
+ totalVolumeTraded: int
62
+ grossTradeAmount: float
63
+ highestPrice: float
64
+ lowestPrice: float
65
+ openPrice: float
66
+ tradingSessionId: int
67
+ receivedAt: Optional[float] = field(default=None, repr=False)
68
+
69
+ @classmethod
70
+ def from_dict(cls, data: Dict[str, Any]) -> "Trade":
71
+ return cls(
72
+ marketId=data.get("marketId"),
73
+ boardId=data.get("boardId"),
74
+ isin=data.get("isin"),
75
+ symbol=data.get("symbol"),
76
+ price=data.get("matchPrice"),
77
+ quantity=data.get("matchQtty"),
78
+ totalVolumeTraded=data.get("totalVolumeTraded"),
79
+ grossTradeAmount=data.get("grossTradeAmount"),
80
+ highestPrice=data.get("highestPrice"),
81
+ lowestPrice=data.get("lowestPrice"),
82
+ openPrice=data.get("openPrice"),
83
+ tradingSessionId=data.get("tradingSessionId"),
84
+ receivedAt=data.get("_receivedAt"),
85
+ )
86
+
87
+
88
+ @dataclass
89
+ class TradeExtra:
90
+ marketId: str
91
+ boardId: str
92
+ isin: str
93
+ symbol: str
94
+ price: float
95
+ quantity: int
96
+ side: int
97
+ avgPrice: float
98
+ totalVolumeTraded: int
99
+ grossTradeAmount: float
100
+ highestPrice: float
101
+ lowestPrice: float
102
+ openPrice: float
103
+ tradingSessionId: int
104
+ receivedAt: Optional[float] = field(default=None, repr=False)
105
+
106
+ @classmethod
107
+ def from_dict(cls, data: Dict[str, Any]) -> "TradeExtra":
108
+ return cls(
109
+ marketId=data.get("marketId"),
110
+ boardId=data.get("boardId"),
111
+ isin=data.get("isin"),
112
+ symbol=data.get("symbol"),
113
+ price=data.get("matchPrice"),
114
+ quantity=data.get("matchQtty"),
115
+ side=data.get("side"),
116
+ avgPrice=data.get("avgPrice"),
117
+ totalVolumeTraded=data.get("totalVolumeTraded"),
118
+ grossTradeAmount=data.get("grossTradeAmount"),
119
+ highestPrice=data.get("highestPrice"),
120
+ lowestPrice=data.get("lowestPrice"),
121
+ openPrice=data.get("openPrice"),
122
+ tradingSessionId=data.get("tradingSessionId"),
123
+ receivedAt=data.get("_receivedAt"),
124
+ )
125
+
126
+ @dataclass
127
+ class ForeignInvestor:
128
+ marketId: str
129
+ boardId: str
130
+ tradingSessionId: str
131
+ symbol: str
132
+ transactTime: str
133
+ foreignInvestorTypeCode: str
134
+
135
+ sellVolume: int
136
+ sellTradedAmount: int
137
+ buyVolume: int
138
+ buyTradedAmount: int
139
+
140
+ totalSellVolume: int
141
+ totalSellTradedAmount: int
142
+ totalBuyVolume: int
143
+ totalBuyTradedAmount: int
144
+
145
+ foreignerOrderLimitQuantity: int
146
+ foreignerBuyPossibleQuantity: int
147
+ receivedAt: Optional[float] = field(default=None, repr=False)
148
+
149
+ @classmethod
150
+ def from_dict(cls, data: Dict[str, Any]) -> "ForeignInvestor":
151
+ return cls(
152
+ marketId=data.get("marketId"),
153
+ boardId=data.get("boardId"),
154
+ tradingSessionId=data.get("tradingSessionId"),
155
+ symbol=data.get("symbol"),
156
+ transactTime=data.get("transactTime"),
157
+ foreignInvestorTypeCode=data.get("foreignInvestorTypeCode"),
158
+ sellVolume=data.get("sellVolume"),
159
+ sellTradedAmount=data.get("sellTradedAmount"),
160
+ buyVolume=data.get("buyVolume"),
161
+ buyTradedAmount=data.get("buyTradedAmount"),
162
+ totalSellVolume=data.get("totalSellVolume"),
163
+ totalSellTradedAmount=data.get("totalSellTradedAmount"),
164
+ totalBuyVolume=data.get("totalBuyVolume"),
165
+ totalBuyTradedAmount=data.get("totalBuyTradedAmount"),
166
+ foreignerOrderLimitQuantity=data.get("foreignerOrderLimitQuantity"),
167
+ foreignerBuyPossibleQuantity=data.get("foreignerBuyPossibleQuantity"),
168
+ receivedAt=data.get("_receivedAt"),
169
+ )
170
+
171
+
172
+ @dataclass
173
+ class MarketIndex:
174
+ indexName: str
175
+
176
+ changedRatio: float
177
+ changedValue: float
178
+
179
+ fluctuationSteadinessIssueCount: int
180
+ fluctuationDownIssueCount: int
181
+ fluctuationUpIssueCount: int
182
+ fluctuationLowerLimitIssueCount: int
183
+ fluctuationUpperLimitIssueCount: int
184
+
185
+ fluctuationDownIssueVolume: int
186
+ fluctuationUpIssueVolume: int
187
+ fluctuationSteadinessIssueVolume: int
188
+
189
+ currencyCode: str
190
+ indexTypeCode: str
191
+
192
+ lowestValueIndexes: float
193
+ highestValueIndexes: float
194
+ priorValueIndexes: float
195
+ valueIndexes: float
196
+
197
+ contauctAccTrdVal: float
198
+ contauctAccTrdVol: int
199
+ blkTrdAccTrdVal: float
200
+ blkTrdAccTrdVol: int
201
+
202
+ grossTradeAmount: float
203
+ totalVolumeTraded: int
204
+ marketIndexClass: int
205
+ marketId: int
206
+ tradingSessionId: int
207
+ transactTime: str
208
+
209
+ receivedAt: Optional[float] = field(default=None, repr=False)
210
+
211
+ @classmethod
212
+ def from_dict(cls, data: Dict[str, Any]) -> "MarketIndex":
213
+ return cls(
214
+ indexName=data.get("indexName"),
215
+ changedRatio=data.get("changedRatio"),
216
+ changedValue=data.get("changedValue"),
217
+ fluctuationSteadinessIssueCount=data.get("fluctuationSteadinessIssueCount"),
218
+ fluctuationDownIssueCount=data.get("fluctuationDownIssueCount"),
219
+ fluctuationUpIssueCount=data.get("fluctuationUpIssueCount"),
220
+ fluctuationLowerLimitIssueCount=data.get("fluctuationLowerLimitIssueCount"),
221
+ fluctuationUpperLimitIssueCount=data.get("fluctuationUpperLimitIssueCount"),
222
+ fluctuationDownIssueVolume=data.get("fluctuationDownIssueVolume"),
223
+ fluctuationUpIssueVolume=data.get("fluctuationUpIssueVolume"),
224
+ fluctuationSteadinessIssueVolume=data.get("fluctuationSteadinessIssueVolume"),
225
+ currencyCode=data.get("currencyCode"),
226
+ indexTypeCode=data.get("indexTypeCode"),
227
+ lowestValueIndexes=data.get("lowestValueIndexes"),
228
+ highestValueIndexes=data.get("highestValueIndexes"),
229
+ priorValueIndexes=data.get("priorValueIndexes"),
230
+ valueIndexes=data.get("valueIndexes"),
231
+ contauctAccTrdVal=data.get("contauctAccTrdVal"),
232
+ contauctAccTrdVol=data.get("contauctAccTrdVol"),
233
+ blkTrdAccTrdVal=data.get("blkTrdAccTrdVal"),
234
+ blkTrdAccTrdVol=data.get("blkTrdAccTrdVol"),
235
+ grossTradeAmount=data.get("grossTradeAmount"),
236
+ totalVolumeTraded=data.get("totalVolumeTraded"),
237
+ marketIndexClass=data.get("marketIndexClass"),
238
+ marketId=data.get("marketId"),
239
+ tradingSessionId=data.get("tradingSessionId"),
240
+ transactTime=parse_timestamp(data.get("transactTime")),
241
+ receivedAt=data.get("_receivedAt"),
242
+ )
243
+
244
+
245
+ @dataclass
246
+ class ExpectedPrice:
247
+ marketId: str
248
+ boardId: str
249
+ isin: str
250
+ symbol: str
251
+ closePrice: float
252
+ expectedTradePrice: float
253
+ expectedTradeQuantity: int
254
+ receivedAt: Optional[float] = field(default=None, repr=False)
255
+
256
+ @classmethod
257
+ def from_dict(cls, data: Dict[str, Any]) -> "ExpectedPrice":
258
+ return cls(
259
+ marketId=data.get("marketId"),
260
+ boardId=data.get("boardId"),
261
+ isin=data.get("isin"),
262
+ symbol=data.get("symbol"),
263
+ closePrice=data.get("closePrice"),
264
+ expectedTradePrice=data.get("expectedTradePrice"),
265
+ expectedTradeQuantity=data.get("expectedTradeQuantity"),
266
+ receivedAt=data.get("_receivedAt"),
267
+ )
268
+
269
+
270
+ @dataclass
271
+ class SecurityDefinition:
272
+ marketId: str
273
+ boardId: str
274
+ symbol: str
275
+ isin: str
276
+ productGrpId: str
277
+ securityGroupId: str
278
+ basicPrice: float
279
+ ceilingPrice: float
280
+ floorPrice: float
281
+ openInterestQuantity: int
282
+ securityStatus: str
283
+ symbolAdminStatusCode: str
284
+ symbolTradingMethodStatusCode: str
285
+ symbolTradingSanctionStatusCode: str
286
+ finalTradeDate: Optional[str]
287
+ listingDate: Optional[str]
288
+ receivedAt: Optional[float] = field(default=None, repr=False)
289
+
290
+ @classmethod
291
+ def from_dict(cls, data: Dict[str, Any]) -> "SecurityDefinition":
292
+ return cls(
293
+ symbol=data.get("symbol"),
294
+ marketId=data.get("marketId"),
295
+ boardId=data.get("boardId"),
296
+ isin=data.get("isin"),
297
+ productGrpId=data.get("productGrpId"),
298
+ securityGroupId=data.get("securityGroupId"),
299
+ basicPrice=data.get("basicPrice"),
300
+ ceilingPrice=data.get("ceilingPrice"),
301
+ floorPrice=data.get("floorPrice"),
302
+ openInterestQuantity=data.get("openInterestQuantity"),
303
+ securityStatus=data.get("securityStatus"),
304
+ symbolAdminStatusCode=data.get("symbolAdminStatusCode"),
305
+ symbolTradingMethodStatusCode=data.get("symbolTradingMethodStatusCode"),
306
+ symbolTradingSanctionStatusCode=data.get("symbolTradingSanctionStatusCode"),
307
+ finalTradeDate=parse_timestamp(data.get("finalTradeDate"), date_only=True),
308
+ listingDate=parse_timestamp(data.get("listingDate"), date_only=True),
309
+ receivedAt=data.get("_receivedAt"),
310
+ )
311
+
312
+
313
+ @dataclass
314
+ class Order:
315
+ id: str
316
+ side: str
317
+ accountNo: str
318
+ symbol: str
319
+
320
+ price: float
321
+ priceSecure: float
322
+ averagePrice: float
323
+
324
+ quantity: int
325
+ fillQuantity: int
326
+ canceledQuantity: int
327
+ leaveQuantity: int
328
+
329
+ orderType: str
330
+ orderStatus: str
331
+
332
+ loanPackageId: int
333
+ marketType: str
334
+
335
+ transDate: str
336
+ createdDate: str
337
+ modifiedDate: str
338
+ receivedAt: Optional[float] = field(default=None, repr=False)
339
+
340
+ @classmethod
341
+ def from_dict(cls, data: Dict[str, Any]) -> "Order":
342
+ return cls(
343
+ id=data.get("id"),
344
+ side=data.get("side"),
345
+ accountNo=data.get("accountNo"),
346
+ symbol=data.get("symbol"),
347
+
348
+ price=float(data.get("price")),
349
+ priceSecure=float(data.get("priceSecure")),
350
+ averagePrice=float(data.get("averagePrice")),
351
+
352
+ quantity=int(data.get("quantity")),
353
+ fillQuantity=int(data.get("fillQuantity")),
354
+ canceledQuantity=int(data.get("canceledQuantity")),
355
+ leaveQuantity=int(data.get("leaveQuantity")),
356
+
357
+ orderType=data.get("orderType"),
358
+ orderStatus=data.get("orderStatus"),
359
+
360
+ loanPackageId=int(data.get("loanPackageId")),
361
+ marketType=data.get("marketType"),
362
+
363
+ transDate=data.get("transDate"),
364
+ createdDate=data.get("createdDate"),
365
+ modifiedDate=data.get("modifiedDate"),
366
+ receivedAt=data.get("_receivedAt"),
367
+ )
368
+
369
+
370
+ @dataclass
371
+ class Quote:
372
+ marketId: str
373
+ boardId: str
374
+ symbol: str
375
+ isin: str
376
+ bid: List[PriceLevel]
377
+ offer: List[PriceLevel]
378
+ totalOfferQtty: float
379
+ totalBidQtty: float
380
+ receivedAt: Optional[float] = field(default=None, repr=False)
381
+
382
+ @classmethod
383
+ def from_dict(cls, data: Dict[str, Any]) -> "Quote":
384
+ # Parse bids array
385
+ bids_data = data.get("bid") or []
386
+ bids = [PriceLevel.from_dict(level) for level in bids_data]
387
+
388
+ # Parse asks array
389
+ offer_data = data.get("offer") or []
390
+ offers = [PriceLevel.from_dict(level) for level in offer_data]
391
+
392
+ return cls(
393
+ symbol=data.get("symbol"),
394
+ marketId=data.get("marketId"),
395
+ boardId=data.get("boardId"),
396
+ isin=data.get("isin", ""),
397
+ bid=bids,
398
+ offer=offers,
399
+ totalOfferQtty=data.get("totalOfferQtty"),
400
+ totalBidQtty=data.get("totalBidQtty"),
401
+ receivedAt=data.get("_receivedAt"),
402
+ )
403
+
404
+ @property
405
+ def best_bid(self) -> Optional[Tuple[float, int]]:
406
+ if not self.bid:
407
+ return None
408
+ return self.bid[0].price, self.bid[0].quantity
409
+
410
+ @property
411
+ def best_ask(self) -> Optional[Tuple[float, int]]:
412
+ if not self.offer:
413
+ return None
414
+ return self.offer[0].price, self.offer[0].quantity
415
+
416
+ @property
417
+ def spread(self) -> Optional[float]:
418
+ bid = self.best_bid
419
+ offer = self.best_ask
420
+ if bid and offer:
421
+ return offer[0] - bid[0]
422
+ return None
423
+
424
+
425
+ @dataclass
426
+ class Ohlc:
427
+ symbol: str
428
+ resolution: str
429
+ open: float
430
+ high: float
431
+ low: float
432
+ close: float
433
+ volume: int
434
+ time: int
435
+ lastUpdated: int
436
+ type: str
437
+ receivedAt: Optional[float] = field(default=None, repr=False)
438
+
439
+ @classmethod
440
+ def from_dict(cls, data: Dict[str, Any]) -> "Ohlc":
441
+ # Helper function to round to 2 decimal places (standard rounding)
442
+ def round_value(value) -> float:
443
+ if value is None:
444
+ return 0.0
445
+ return round(float(value), 2)
446
+
447
+ return cls(
448
+ symbol=data.get("symbol"),
449
+ resolution=data.get("resolution"),
450
+ open=round_value(data.get("open")),
451
+ high=round_value(data.get("high")),
452
+ low=round_value(data.get("low")),
453
+ close=round_value(data.get("close")),
454
+ volume=data.get("volume"),
455
+ time=data.get("time"),
456
+ type=data.get("type"),
457
+ lastUpdated=data.get("lastUpdated"),
458
+ receivedAt=data.get("_receivedAt"),
459
+ )
460
+
461
+
462
+ @dataclass
463
+ class Position:
464
+ symbol: str
465
+ quantity: int
466
+ averagePrice: Decimal
467
+ marketValue: Decimal
468
+ costBasis: Decimal
469
+ unrealizedPl: Decimal
470
+ unrealizedPlPercent: Decimal
471
+ timestamp: datetime
472
+
473
+ @classmethod
474
+ def from_dict(cls, data: Dict[str, Any]) -> "Position":
475
+ """Parse position from message data.
476
+
477
+ Args:
478
+ data: Raw message dict with either abbreviated or full field names
479
+
480
+ Returns:
481
+ Position instance
482
+
483
+ Example:
484
+ >>> Position.from_dict({"S": "AAPL", "q": 100, "ap": "150.00", ...})
485
+ """
486
+ return cls(
487
+ symbol=data.get("symbol"),
488
+ quantity=data.get("quantity"),
489
+ averagePrice=Decimal(str(data.get("averagePrice"))),
490
+ marketValue=Decimal(str(data.get("marketValue"))),
491
+ costBasis=Decimal(str(data.get("costBasis"))),
492
+ unrealizedPl=Decimal(str(data.get("unrealizedPl"))),
493
+ unrealizedPlPercent=Decimal(str(data.get("unrealizedPlPercent"))),
494
+ timestamp=datetime.fromtimestamp((data.get("timestamp")) / 1000),
495
+ )
496
+
497
+
498
+ @dataclass
499
+ class AccountUpdate:
500
+ cash: Decimal
501
+ buyingPower: Decimal
502
+ portfolioValue: Decimal
503
+ equity: Decimal
504
+ timestamp: datetime
505
+
506
+ @classmethod
507
+ def from_dict(cls, data: Dict[str, Any]) -> "AccountUpdate":
508
+ """Parse account update from message data.
509
+
510
+ Args:
511
+ data: Raw message dict with either abbreviated or full field names
512
+
513
+ Returns:
514
+ AccountUpdate instance
515
+
516
+ Example:
517
+ >>> AccountUpdate.from_dict({"c": "10000.00", "bp": "20000.00", ...})
518
+ """
519
+ return cls(
520
+ cash=Decimal(str(data.get("cash"))),
521
+ buyingPower=Decimal(str(data.get("buyingPower"))),
522
+ portfolioValue=Decimal(str(data.get("portfolioValue"))),
523
+ equity=Decimal(str(data.get("equity"))),
524
+ timestamp=datetime.fromtimestamp((data.get("timestamp")) / 1000)
525
+ )
File without changes