HedgeTech 0.1.0__py3-none-any.whl → 0.2.2b0__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.
@@ -0,0 +1,757 @@
1
+ # ========================================|======================================== #
2
+ # Imports #
3
+ # ========================================|======================================== #
4
+
5
+ from typing import (
6
+ Literal,
7
+ )
8
+ from .__io_types import (
9
+ HexUUID,
10
+ OrderStatus,
11
+ )
12
+ from HedgeTech.Auth import AuthAsyncClient
13
+ from PIL.Image import open as image_open
14
+ from PIL.ImageFile import ImageFile
15
+ from io import BytesIO
16
+ from asyncio import sleep
17
+
18
+ # ========================================|======================================== #
19
+ # Class Definitions #
20
+ # ========================================|======================================== #
21
+ class Order:
22
+
23
+ """
24
+ Represents a single trading order in EMS Engine.
25
+
26
+ This class provides methods to send, edit, check status,
27
+ and delete an order after creation.
28
+
29
+ An Order instance is usually created via
30
+ `EmsEngine_TseIfb_AsyncClient` methods.
31
+ """
32
+
33
+ def __init__(
34
+ self,
35
+ *,
36
+ SendOrder_RequestInfo : dict,
37
+ AuthASyncClient : AuthAsyncClient,
38
+ SymbolNameOrIsin : str,
39
+ Price : int,
40
+ Volume :int,
41
+ Order_ValidityType : Literal[
42
+ 'DAY',
43
+ 'GTC', # Good Till Cancelled
44
+ 'GTD', # Good Till Date
45
+ 'FAK', # Fill And Kill
46
+ 'FOK', # Fill Or Kill
47
+ ] = 'DAY',
48
+ ValidityDate : int = 0,
49
+ ):
50
+
51
+ """
52
+ Represents a stock order in the EMS Engine for TSE IFB.
53
+
54
+ This class allows sending, editing, deleting, and checking the status
55
+ of a stock order using an asynchronous client.
56
+
57
+ Attributes
58
+ ----------
59
+ ValidityType : str
60
+ Type of order validity ('DAY', 'GTC', 'GTD', 'FAK', 'FOK').
61
+ ValidityDate : int
62
+ Expiration date of the order (for 'GTD' type).
63
+ SymbolNameOrIsin : str
64
+ Symbol name or ISIN code of the security.
65
+ Price : int
66
+ Order price.
67
+ Volume : int
68
+ Order volume.
69
+ is_deleted : bool
70
+ True if the order has been deleted.
71
+ OrderId : HexUUID | None
72
+ Unique identifier of the order after submission.
73
+
74
+ Methods
75
+ -------
76
+ send()
77
+ Sends the order asynchronously to the EMS engine.
78
+ Edit(Order_ValidityType, ValidityDate, Price, Volume)
79
+ Edits the existing order asynchronously.
80
+ Status()
81
+ Retrieves the current status of the order asynchronously.
82
+ Delete()
83
+ Deletes the order asynchronously.
84
+ """
85
+
86
+
87
+ self.__AuthASyncClient = AuthASyncClient
88
+ self.__SendOrder_RequestInfo = SendOrder_RequestInfo
89
+
90
+ self.ValidityType : str = Order_ValidityType
91
+ self.ValidityDate : int = ValidityDate
92
+ self.SymbolNameOrIsin : str = SymbolNameOrIsin
93
+ self.Price : int = Price
94
+ self.Volume : int = Volume
95
+ self.is_deleted : bool = False
96
+
97
+ self.OrderId : HexUUID | None = None
98
+
99
+ # +--------------------------------------------------------------------------------------+ #
100
+
101
+ async def send(self)-> None:
102
+
103
+ """
104
+ Sends the order to the EMS engine asynchronously.
105
+
106
+ If the order has not been sent before, it posts the request
107
+ using the provided AuthAsyncClient.
108
+
109
+ Returns
110
+ -------
111
+ None
112
+
113
+ Example
114
+ -------
115
+ >>> order = await client.Buy_by_Name(symbolName="اطلس", Price=100000, Volume=10)
116
+ >>> await order.send()
117
+ >>> print(order.OrderId)
118
+ '953097ac6ced45ca8ef205b76ca6faf2'
119
+ """
120
+
121
+ if self.OrderId is None :
122
+
123
+ SendOrder_Response = await self.__AuthASyncClient.httpx_Client.post(**self.__SendOrder_RequestInfo)
124
+
125
+ match SendOrder_Response.status_code :
126
+
127
+ case 200 :
128
+
129
+ self.OrderId = SendOrder_Response.json()['Data']['order_uuid']
130
+ return None
131
+
132
+ case _ : return None
133
+
134
+ else : return None
135
+
136
+ # +--------------------------------------------------------------------------------------+ #
137
+ async def Edit(
138
+ self,
139
+ *,
140
+ Price : int,
141
+ Volume :int,
142
+ Order_ValidityType : Literal[
143
+ 'DAY',
144
+ 'GTC', # Good Till Cancelled
145
+ 'GTD', # Good Till Date
146
+ 'FAK', # Fill And Kill
147
+ 'FOK', # Fill Or Kill
148
+ ] = 'DAY',
149
+ ValidityDate : int = 0,
150
+ )-> None:
151
+
152
+ """
153
+ Edits an existing order asynchronously.
154
+
155
+ Parameters
156
+ ----------
157
+ Order_ValidityType : str
158
+ The new order validity type.
159
+ ValidityDate : int
160
+ New expiration date if validity type is 'GTD'.
161
+ Price : int
162
+ New order price.
163
+ Volume : int
164
+ New order volume.
165
+
166
+ Returns
167
+ -------
168
+ None
169
+
170
+ Example
171
+ -------
172
+ >>> await order.Edit(Price=105, Volume=15)
173
+ """
174
+
175
+ if self.OrderId is not None:
176
+
177
+ Edit_response = await self.__AuthASyncClient.httpx_Client.patch(
178
+ url='https://core.hedgetech.ir/ems-engine/tse-ifb/order/edit',
179
+ data={
180
+ 'order_uuid' : self.OrderId,
181
+ 'Order_ValidityType' : Order_ValidityType,
182
+ 'ValidityDate' : ValidityDate,
183
+ 'Price' : Price,
184
+ 'Volume' : Volume
185
+ }
186
+ )
187
+
188
+
189
+ match Edit_response.status_code:
190
+
191
+ case 200:
192
+
193
+ Edit_response = Edit_response.json()
194
+
195
+ self.OrderId = Edit_response['Data']['order_uuid']
196
+ self.ValidityType = Edit_response['Data']['order_validity_type']
197
+ self.ValidityDate = ValidityDate
198
+ self.Price = Edit_response['Data']['order_price']
199
+ self.Volume = Edit_response['Data']['order_volume']
200
+
201
+ return None
202
+
203
+ case _ : return None
204
+
205
+ else : return None
206
+
207
+ # +--------------------------------------------------------------------------------------+ #
208
+
209
+ async def Status(self)-> OrderStatus | None:
210
+
211
+ """
212
+ Retrieves the current status of the order asynchronously.
213
+
214
+ Returns
215
+ -------
216
+ OrderStatus | None
217
+ A dictionary containing the current order status with the following keys:
218
+ - order_uuid: HexUUID, unique identifier of the order.
219
+ - order_status: 'InQueue' | 'Cancelled' | 'Broken' | 'Settled'
220
+ - Price: int, the order price.
221
+ - Volume: int, the original order volume.
222
+ - RemainedVolume: int, volume remaining unexecuted.
223
+ - ExecutedVolume: int, executed volume.
224
+ - OrderSide: 'Buy' | 'Sell', the order side.
225
+ - ValidityType: 'DAY' | 'GTC' | 'GTD', the type of order validity.
226
+ - ValidityDate: int, expiration date if applicable.
227
+
228
+ Example
229
+ -------
230
+ >>> status = await order.Status()
231
+ >>> print(status)
232
+ {
233
+ 'order_uuid': '953097ac6ced45ca8ef205b76ca6faf2',
234
+ 'order_status': 'InQueue',
235
+ 'Price': 100,
236
+ 'Volume': 10,
237
+ 'RemainedVolume': 10,
238
+ 'ExecutedVolume': 0,
239
+ 'OrderSide': 'Buy',
240
+ 'ValidityType': 'DAY',
241
+ 'ValidityDate': 0
242
+ }
243
+ """
244
+
245
+ if self.OrderId is not None:
246
+
247
+ status_respnse = await self.__AuthASyncClient.httpx_Client.get(
248
+ url='https://core.hedgetech.ir/ems-engine/tse-ifb/order/status',
249
+ params={'order_uuid' : self.OrderId}
250
+ )
251
+
252
+ match status_respnse.status_code :
253
+
254
+ case 200:
255
+
256
+ return status_respnse.json()['Data']
257
+
258
+ case _ : return None
259
+
260
+ else : return None
261
+
262
+ # +--------------------------------------------------------------------------------------+ #
263
+
264
+ async def Delete(self)-> None:
265
+
266
+ """
267
+ Deletes the order asynchronously.
268
+
269
+ Sets `is_deleted` to True if successful.
270
+
271
+ Returns
272
+ -------
273
+ None
274
+
275
+ Example
276
+ -------
277
+ >>> await order.Delete()
278
+ >>> print(order.is_deleted)
279
+ True
280
+ """
281
+
282
+ if self.OrderId is not None:
283
+
284
+ Delete_respnse = await self.__AuthASyncClient.httpx_Client.delete(
285
+ url= 'https://core.hedgetech.ir/ems-engine/tse-ifb/order/delete',
286
+ params={'order_uuid' : self.OrderId}
287
+ )
288
+
289
+
290
+ match Delete_respnse.status_code :
291
+
292
+ case 200:
293
+
294
+ self.is_deleted = True
295
+ return None
296
+
297
+ case _ : return None
298
+
299
+ else : return None
300
+
301
+ # ================================================================================= #
302
+
303
+ class EmsEngine_TseIfb_AsyncClient:
304
+
305
+ """
306
+ Asynchronous EMS Engine client for TSE/IFB markets.
307
+
308
+ This client manages authentication, OMS login,
309
+ and order creation.
310
+ """
311
+
312
+ def __init__(
313
+ self,
314
+ AuthASyncClient : AuthAsyncClient,
315
+ ):
316
+
317
+ """
318
+ Asynchronous client for interacting with the EMS Engine (TSE IFB).
319
+
320
+ Provides methods for logging in, retrieving CAPTCHA, and creating buy/sell orders
321
+ by symbol name or ISIN.
322
+
323
+ Attributes
324
+ ----------
325
+ Customer_FullName : str | None
326
+ Full name of the logged-in customer.
327
+ Customer_TSEBourseCode : str | None
328
+ TSE bourse code of the customer.
329
+ oms_session : HexUUID | None
330
+ Session token obtained after login.
331
+
332
+ Methods
333
+ -------
334
+ Get_Captcha(OMS)
335
+ Retrieves the login CAPTCHA image asynchronously.
336
+
337
+ oms_login(username, password, captcha_value)
338
+ Logs into the OMS asynchronously.
339
+
340
+ Buy_by_Name(Order_ValidityType, ValidityDate, symbolName, Price, Volume)
341
+ Creates a new buy order by symbol name asynchronously.
342
+
343
+ Sell_by_Name(Order_ValidityType, ValidityDate, symbolName, Price, Volume)
344
+ Creates a new sell order by symbol name asynchronously.
345
+
346
+ Buy_by_isin(Order_ValidityType, ValidityDate, symbolIsin, Price, Volume)
347
+ Creates a new buy order by ISIN asynchronously.
348
+
349
+ Sell_by_isin(Order_ValidityType, ValidityDate, symbolIsin, Price, Volume)
350
+ Creates a new sell order by ISIN asynchronously.
351
+ """
352
+
353
+ self.__AuthASyncClient = AuthASyncClient
354
+
355
+ self.Customer_FullName : str | None = None
356
+ self.Customer_TSEBourseCode : str | None = None
357
+ self.oms_session : HexUUID | None = None
358
+
359
+ # +--------------------------------------------------------------------------------------+ #
360
+
361
+
362
+ async def Get_Captcha(
363
+ self,
364
+ OMS : Literal[
365
+ 'Omex | Parsian',
366
+ 'Sahra | Karamad',
367
+ ]
368
+ )-> ImageFile:
369
+
370
+ """
371
+ Retrieves the CAPTCHA image for the specified OMS asynchronously.
372
+
373
+ Parameters
374
+ ----------
375
+ OMS : str
376
+ OMS identifier ('Omex | Parsian' or 'Sahra | Karamad').
377
+
378
+ Returns
379
+ -------
380
+ ImageFile
381
+ The CAPTCHA image object.
382
+
383
+ Raises
384
+ ------
385
+ ValueError
386
+ If the request fails or returns an error.
387
+
388
+ Example
389
+ -------
390
+ >>> captcha = await client.Get_Captcha('Omex | Parsian')
391
+ >>> captcha.show()
392
+ """
393
+
394
+ Captcha = await self.__AuthASyncClient.httpx_Client.get(
395
+ url='https://core.hedgetech.ir/ems-engine/tse-ifb/oms/login',
396
+ params={'oms' : OMS }
397
+ )
398
+
399
+ if Captcha.status_code == 200: return image_open(BytesIO(Captcha.content))
400
+
401
+ else : raise ValueError(Captcha.json()['detail']['Status']['Description']['en'])
402
+
403
+
404
+ # +--------------------------------------------------------------------------------------+ #
405
+
406
+ async def oms_login(
407
+ self,
408
+ username: str,
409
+ password: str,
410
+ captcha_value: str,
411
+ ) -> None :
412
+
413
+ """
414
+ Logs into the OMS asynchronously with provided credentials and CAPTCHA.
415
+
416
+ Parameters
417
+ ----------
418
+ username : str
419
+ OMS username.
420
+ password : str
421
+ OMS password.
422
+ captcha_value : str
423
+ Solved CAPTCHA value.
424
+
425
+ Returns
426
+ -------
427
+ None
428
+
429
+ Raises
430
+ ------
431
+ ValueError
432
+ If login fails or CAPTCHA is invalid.
433
+
434
+ Example
435
+ -------
436
+ >>> await client.oms_login("user123", "pass123", "abcd")
437
+ >>> print(client.oms_session)
438
+ 'session_token_here'
439
+ """
440
+
441
+ response = await self.__AuthASyncClient.httpx_Client.post(
442
+ url='https://core.hedgetech.ir/ems-engine/tse-ifb/oms/login',
443
+ data={
444
+ 'username' : username,
445
+ 'Password' : password,
446
+ 'Captcha_Value' : captcha_value
447
+ },
448
+ )
449
+
450
+ match response.status_code :
451
+
452
+ case 200 :
453
+
454
+ data = response.json()
455
+
456
+ self.Customer_FullName = data['Data']['Customer_FullName']
457
+ self.Customer_TSEBourseCode = data['Data']['Customer_TSEBourseCode']
458
+ self.oms_session = data['Data']['oms_session']
459
+
460
+ return None
461
+
462
+ case 400 :
463
+
464
+ raise ValueError(response.json()['detail']['Status']['Description']['en'])
465
+
466
+ case _ :
467
+
468
+ raise ValueError(response.text)
469
+
470
+ # +--------------------------------------------------------------------------------------+ #
471
+
472
+
473
+ async def Buy_by_Name(
474
+ self,
475
+ *,
476
+ symbolName : str,
477
+ Price : int,
478
+ Volume :int,
479
+ Order_ValidityType : Literal[
480
+ 'DAY',
481
+ 'GTC', # Good Till Cancelled
482
+ 'GTD', # Good Till Date
483
+ 'FAK', # Fill And Kill
484
+ 'FOK', # Fill Or Kill
485
+ ] = 'DAY',
486
+ ValidityDate : int = 0,
487
+ )-> Order | None:
488
+
489
+ """
490
+ Creates a new buy order by symbol name asynchronously.
491
+
492
+ Parameters
493
+ ----------
494
+ Order_ValidityType : str
495
+ Order validity type.
496
+ ValidityDate : int
497
+ Expiration date if validity type is 'GTD'.
498
+ symbolName : str
499
+ Symbol name of the security.
500
+ Price : int
501
+ Order price.
502
+ Volume : int
503
+ Order volume.
504
+
505
+ Returns
506
+ -------
507
+ Order | None
508
+ A new Order instance if OMS session exists, else None.
509
+
510
+ Example
511
+ -------
512
+ >>> order = await client.Buy_by_Name(symbolName="اطلس", Price=100000, Volume=10)
513
+ >>> await order.send()
514
+ """
515
+
516
+ await sleep(0)
517
+
518
+ if self.oms_session is not None:
519
+
520
+ return Order(
521
+ AuthASyncClient=self.__AuthASyncClient,
522
+ Order_ValidityType=Order_ValidityType,
523
+ ValidityDate=ValidityDate,
524
+ SymbolNameOrIsin = symbolName,
525
+ Price=Price,
526
+ Volume=Volume,
527
+ SendOrder_RequestInfo = {
528
+ 'url' : 'https://core.hedgetech.ir/ems-engine/tse-ifb/order/new/buy/name',
529
+ 'data' : {
530
+ 'oms_session' : self.oms_session,
531
+ 'Order_ValidityType' : Order_ValidityType,
532
+ 'ValidityDate' : ValidityDate,
533
+ 'symbolName' : symbolName,
534
+ 'Price' : Price,
535
+ 'Volume' : Volume
536
+ }
537
+ }
538
+ )
539
+
540
+ else : return None
541
+
542
+ # +--------------------------------------------------------------------------------------+ #
543
+
544
+
545
+ async def Sell_by_Name(
546
+ self,
547
+ *,
548
+ symbolName : str,
549
+ Price : int,
550
+ Volume :int,
551
+ Order_ValidityType : Literal[
552
+ 'DAY',
553
+ 'GTC', # Good Till Cancelled
554
+ 'GTD', # Good Till Date
555
+ 'FAK', # Fill And Kill
556
+ 'FOK', # Fill Or Kill
557
+ ] = 'DAY',
558
+ ValidityDate : int = 0,
559
+ )-> Order | None:
560
+
561
+ """
562
+ Creates a new sell order by symbol name asynchronously.
563
+
564
+ Parameters
565
+ ----------
566
+ Order_ValidityType : str
567
+ Order validity type.
568
+ ValidityDate : int
569
+ Expiration date if validity type is 'GTD'.
570
+ symbolName : str
571
+ Symbol name of the security.
572
+ Price : int
573
+ Order price.
574
+ Volume : int
575
+ Order volume.
576
+
577
+ Returns
578
+ -------
579
+ Order | None
580
+ A new Order instance if OMS session exists, else None.
581
+
582
+ Example
583
+ -------
584
+ >>> order = await client.Sell_by_Name(symbolName="اطلس", Price=100000, Volume=10)
585
+ >>> await order.send()
586
+ """
587
+
588
+ await sleep(0)
589
+
590
+ if self.oms_session is not None:
591
+
592
+ return Order(
593
+ AuthASyncClient=self.__AuthASyncClient,
594
+ Order_ValidityType=Order_ValidityType,
595
+ ValidityDate=ValidityDate,
596
+ SymbolNameOrIsin = symbolName,
597
+ Price=Price,
598
+ Volume=Volume,
599
+ SendOrder_RequestInfo = {
600
+ 'url' : 'https://core.hedgetech.ir/ems-engine/tse-ifb/order/new/sell/name',
601
+ 'data' : {
602
+ 'oms_session' : self.oms_session,
603
+ 'Order_ValidityType' : Order_ValidityType,
604
+ 'ValidityDate' : ValidityDate,
605
+ 'symbolName' : symbolName,
606
+ 'Price' : Price,
607
+ 'Volume' : Volume
608
+ }
609
+ }
610
+ )
611
+
612
+ else : return None
613
+
614
+ # +--------------------------------------------------------------------------------------+ #
615
+
616
+ async def Buy_by_isin(
617
+ self,
618
+ *,
619
+ symbolIsin : str,
620
+ Price : int,
621
+ Volume :int,
622
+ Order_ValidityType : Literal[
623
+ 'DAY',
624
+ 'GTC', # Good Till Cancelled
625
+ 'GTD', # Good Till Date
626
+ 'FAK', # Fill And Kill
627
+ 'FOK', # Fill Or Kill
628
+ ] = 'DAY',
629
+ ValidityDate : int = 0,
630
+ )-> Order | None:
631
+
632
+ """
633
+ Creates a new buy order by symbol isin asynchronously.
634
+
635
+ Parameters
636
+ ----------
637
+ Order_ValidityType : str
638
+ Order validity type.
639
+ ValidityDate : int
640
+ Expiration date if validity type is 'GTD'.
641
+ symbolIsin : str
642
+ Symbol name of the security.
643
+ Price : int
644
+ Order price.
645
+ Volume : int
646
+ Order volume.
647
+
648
+ Returns
649
+ -------
650
+ Order | None
651
+ A new Order instance if OMS session exists, else None.
652
+
653
+ Example
654
+ -------
655
+ >>> order = await client.Buy_by_isin(symbolIsin="اطلس", Price=100000, Volume=10)
656
+ >>> await order.send()
657
+ """
658
+
659
+ await sleep(0)
660
+
661
+ if self.oms_session is not None:
662
+
663
+ return Order(
664
+ AuthASyncClient=self.__AuthASyncClient,
665
+ Order_ValidityType=Order_ValidityType,
666
+ ValidityDate=ValidityDate,
667
+ SymbolNameOrIsin = symbolIsin,
668
+ Price=Price,
669
+ Volume=Volume,
670
+ SendOrder_RequestInfo = {
671
+ 'url' : 'https://core.hedgetech.ir/ems-engine/tse-ifb/order/new/buy/isin',
672
+ 'data' : {
673
+ 'oms_session' : self.oms_session,
674
+ 'Order_ValidityType' : Order_ValidityType,
675
+ 'ValidityDate' : ValidityDate,
676
+ 'symbolIsin' : symbolIsin,
677
+ 'Price' : Price,
678
+ 'Volume' : Volume
679
+ }
680
+ }
681
+ )
682
+
683
+ else : return None
684
+
685
+ # +--------------------------------------------------------------------------------------+ #
686
+
687
+ async def Sell_by_isin(
688
+ self,
689
+ *,
690
+ symbolIsin : str,
691
+ Price : int,
692
+ Volume :int,
693
+ Order_ValidityType : Literal[
694
+ 'DAY',
695
+ 'GTC', # Good Till Cancelled
696
+ 'GTD', # Good Till Date
697
+ 'FAK', # Fill And Kill
698
+ 'FOK', # Fill Or Kill
699
+ ] = 'DAY',
700
+ ValidityDate : int = 0,
701
+ )-> Order | None:
702
+
703
+ """
704
+ Creates a new sell order by symbol isin asynchronously.
705
+
706
+ Parameters
707
+ ----------
708
+ Order_ValidityType : str
709
+ Order validity type.
710
+ ValidityDate : int
711
+ Expiration date if validity type is 'GTD'.
712
+ symbolIsin : str
713
+ Symbol name of the security.
714
+ Price : int
715
+ Order price.
716
+ Volume : int
717
+ Order volume.
718
+
719
+ Returns
720
+ -------
721
+ Order | None
722
+ A new Order instance if OMS session exists, else None.
723
+
724
+ Example
725
+ -------
726
+ >>> order = await client.Sell_by_isin(symbolIsin="اطلس", Price=100000, Volume=10)
727
+ >>> await order.send()
728
+ """
729
+
730
+ await sleep(0)
731
+
732
+ if self.oms_session is not None:
733
+
734
+ return Order(
735
+ AuthASyncClient=self.__AuthASyncClient,
736
+ Order_ValidityType=Order_ValidityType,
737
+ ValidityDate=ValidityDate,
738
+ SymbolNameOrIsin = symbolIsin,
739
+ Price=Price,
740
+ Volume=Volume,
741
+ SendOrder_RequestInfo = {
742
+ 'url' : 'https://core.hedgetech.ir/ems-engine/tse-ifb/order/new/sell/isin',
743
+ 'data' : {
744
+ 'oms_session' : self.oms_session,
745
+ 'Order_ValidityType' : Order_ValidityType,
746
+ 'ValidityDate' : ValidityDate,
747
+ 'symbolIsin' : symbolIsin,
748
+ 'Price' : Price,
749
+ 'Volume' : Volume
750
+ }
751
+ }
752
+ )
753
+
754
+ else : return None
755
+
756
+ # +--------------------------------------------------------------------------------------+ #
757
+