quantplay 2.0.2__tar.gz → 2.0.3__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.
- {quantplay-2.0.2 → quantplay-2.0.3}/PKG-INFO +1 -1
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/aliceblue.py +5 -6
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/angelone.py +2 -6
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/five_paisa.py +1 -3
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/generics/broker.py +33 -18
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/motilal.py +6 -34
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/noren.py +7 -4
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/upstox.py +39 -46
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/xts.py +8 -8
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/zerodha.py +2 -2
- quantplay-2.0.3/quantplay/exception/__init__.py +1 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/utils/constant.py +7 -7
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/utils/exchange.py +0 -1
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/utils/pickle_utils.py +4 -3
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/utils/selenium_utils.py +1 -1
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay.egg-info/PKG-INFO +1 -1
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay.egg-info/requires.txt +1 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/setup.py +1 -1
- quantplay-2.0.2/tests/wrapper/aws/__init__.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/README.md +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/pyproject.toml +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/__init__.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/__init__.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/auto_login/__init__.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/auto_login/aliceblue.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/finvasia_utils/__init__.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/finvasia_utils/fa_noren.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/flattrade.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/ft_utils/__init__.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/ft_utils/flattrade_utils.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/ft_utils/ft_noren.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/generics/__init__.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/iifl_xts.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/kite_utils.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/shoonya.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/uplink/__init__.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/uplink/uplink_utils.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/xts_utils/Connect.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/xts_utils/Exception.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/xts_utils/InteractiveSocketClient.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/broker/xts_utils/__init__.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/exception/exceptions.py +0 -0
- {quantplay-2.0.2/quantplay/exception → quantplay-2.0.3/quantplay/model}/__init__.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/model/broker/__init__.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/model/broker/generics.py +0 -0
- {quantplay-2.0.2/quantplay/model → quantplay-2.0.3/quantplay/strategies}/__init__.py +0 -0
- {quantplay-2.0.2/quantplay/strategies → quantplay-2.0.3/quantplay/strategies/equities}/__init__.py +0 -0
- {quantplay-2.0.2/quantplay/strategies/equities → quantplay-2.0.3/quantplay/strategies/equities/intraday}/__init__.py +0 -0
- {quantplay-2.0.2/quantplay/strategies/equities/intraday → quantplay-2.0.3/quantplay/strategies/equities/overnight}/__init__.py +0 -0
- {quantplay-2.0.2/quantplay/strategies/equities/overnight → quantplay-2.0.3/quantplay/strategies/futures}/__init__.py +0 -0
- {quantplay-2.0.2/quantplay/strategies/futures → quantplay-2.0.3/quantplay/strategies/futures/overnight}/__init__.py +0 -0
- {quantplay-2.0.2/quantplay/strategies/futures/overnight → quantplay-2.0.3/quantplay/strategies/options}/__init__.py +0 -0
- {quantplay-2.0.2/quantplay/strategies/options → quantplay-2.0.3/quantplay/strategies/options/intraday}/__init__.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/strategies/options/intraday/ladder.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/strategies/options/intraday/musk.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/strategies/options/intraday/short_straddle.py +0 -0
- {quantplay-2.0.2/quantplay/strategies/options/intraday → quantplay-2.0.3/quantplay/utils}/__init__.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/utils/number_utils.py +0 -0
- {quantplay-2.0.2/quantplay/utils → quantplay-2.0.3/quantplay/wrapper}/__init__.py +0 -0
- {quantplay-2.0.2/quantplay/wrapper → quantplay-2.0.3/quantplay/wrapper/aws}/__init__.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay/wrapper/aws/s3.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay.egg-info/SOURCES.txt +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay.egg-info/dependency_links.txt +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/quantplay.egg-info/top_level.txt +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/setup.cfg +0 -0
- {quantplay-2.0.2/quantplay/wrapper/aws → quantplay-2.0.3/tests}/__init__.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/tests/conftest.py +0 -0
- {quantplay-2.0.2/tests → quantplay-2.0.3/tests/wrapper}/__init__.py +0 -0
- {quantplay-2.0.2/tests/wrapper → quantplay-2.0.3/tests/wrapper/aws}/__init__.py +0 -0
- {quantplay-2.0.2 → quantplay-2.0.3}/tests/wrapper/aws/s3_test.py +0 -0
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import traceback
|
|
2
2
|
from typing import Callable, Dict, Literal
|
|
3
3
|
|
|
4
|
-
import pandas as pd
|
|
5
4
|
import polars as pl
|
|
6
5
|
from pya3 import Aliceblue as Alice
|
|
7
6
|
from pya3 import OrderType as AliceOrderType
|
|
8
7
|
from pya3 import ProductType
|
|
9
8
|
from pya3 import TransactionType
|
|
10
9
|
from retrying import retry
|
|
11
|
-
import numpy as np
|
|
12
10
|
import pickle
|
|
13
11
|
import copy
|
|
14
12
|
import codecs
|
|
@@ -24,7 +22,7 @@ from quantplay.exception.exceptions import (
|
|
|
24
22
|
)
|
|
25
23
|
|
|
26
24
|
from quantplay.exception.exceptions import TokenException as QuantplayTokenException
|
|
27
|
-
from quantplay.utils.constant import Constants, OrderType,
|
|
25
|
+
from quantplay.utils.constant import Constants, OrderType, timeit
|
|
28
26
|
from quantplay.utils.pickle_utils import InstrumentData
|
|
29
27
|
|
|
30
28
|
logger = Constants.logger
|
|
@@ -55,6 +53,7 @@ class Aliceblue(Broker):
|
|
|
55
53
|
api_key=api_key,
|
|
56
54
|
)
|
|
57
55
|
response = self.alice.get_session_id()
|
|
56
|
+
|
|
58
57
|
if "sessionID" not in response or response["sessionID"] is None:
|
|
59
58
|
if "emsg" in response:
|
|
60
59
|
if response["emsg"].lower() == "invalid input":
|
|
@@ -248,14 +247,14 @@ class Aliceblue(Broker):
|
|
|
248
247
|
if "trigger_price" in data and data["trigger_price"] is not None:
|
|
249
248
|
trigger_price = float(data["trigger_price"])
|
|
250
249
|
|
|
251
|
-
|
|
250
|
+
self.alice.modify_order(
|
|
252
251
|
instrument=self.alice.get_instrument_by_token(exchange, token),
|
|
253
252
|
transaction_type=transaction_type,
|
|
254
253
|
order_id=order_id,
|
|
255
254
|
product_type=product_type,
|
|
256
255
|
order_type=order_type,
|
|
257
256
|
price=float(data["price"]),
|
|
258
|
-
trigger_price=trigger_price, # type:ignore
|
|
257
|
+
trigger_price=trigger_price, # type: ignore
|
|
259
258
|
quantity=quantity,
|
|
260
259
|
)
|
|
261
260
|
logger.info("[MODIFY_ORDER_RESPONSE] [{order_id}] response [{response}]")
|
|
@@ -277,7 +276,7 @@ class Aliceblue(Broker):
|
|
|
277
276
|
else:
|
|
278
277
|
data["trigger_price"] = None
|
|
279
278
|
|
|
280
|
-
self.modify_order(data) # type:ignore
|
|
279
|
+
self.modify_order(data) # type: ignore
|
|
281
280
|
|
|
282
281
|
def cancel_order(self, order_id: str) -> None:
|
|
283
282
|
self.invoke_aliceblue_api(self.alice.cancel_order, nestordernmbr=order_id)
|
|
@@ -109,7 +109,7 @@ class AngelOne(Broker):
|
|
|
109
109
|
raise TokenException(f"{self.user_id}: Invalid Token")
|
|
110
110
|
|
|
111
111
|
@timeit(MetricName="Angelone:load_instrument")
|
|
112
|
-
def load_instrument(self, file_name=None):
|
|
112
|
+
def load_instrument(self, file_name: str | None = None) -> None:
|
|
113
113
|
try:
|
|
114
114
|
instrument_data_instance = InstrumentData.get_instance()
|
|
115
115
|
if instrument_data_instance is not None:
|
|
@@ -170,7 +170,7 @@ class AngelOne(Broker):
|
|
|
170
170
|
wait_exponential_max=10000,
|
|
171
171
|
stop_max_attempt_number=3,
|
|
172
172
|
)
|
|
173
|
-
def get_ltp(self, exchange=None, tradingsymbol=None):
|
|
173
|
+
def get_ltp(self, exchange=None, tradingsymbol=None) -> float:
|
|
174
174
|
if tradingsymbol in MarketConstants.INDEX_SYMBOL_TO_DERIVATIVE_SYMBOL_MAP:
|
|
175
175
|
tradingsymbol = MarketConstants.INDEX_SYMBOL_TO_DERIVATIVE_SYMBOL_MAP[
|
|
176
176
|
tradingsymbol
|
|
@@ -584,10 +584,6 @@ class AngelOne(Broker):
|
|
|
584
584
|
api_margins = api_margins["data"]
|
|
585
585
|
|
|
586
586
|
try:
|
|
587
|
-
collateral = 0
|
|
588
|
-
if "collateral" in api_margins:
|
|
589
|
-
collateral = float(api_margins["collateral"])
|
|
590
|
-
|
|
591
587
|
margins = {
|
|
592
588
|
"margin_used": float(api_margins["net"]),
|
|
593
589
|
"margin_available": float(api_margins["net"]),
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import numpy as np
|
|
2
1
|
import polars as pl
|
|
3
2
|
import pyotp
|
|
4
3
|
import pickle
|
|
@@ -6,7 +5,6 @@ import codecs
|
|
|
6
5
|
from py5paisa import FivePaisaClient
|
|
7
6
|
from retrying import retry
|
|
8
7
|
import traceback
|
|
9
|
-
from datetime import datetime
|
|
10
8
|
|
|
11
9
|
from quantplay.broker.generics.broker import Broker
|
|
12
10
|
from quantplay.exception.exceptions import (
|
|
@@ -138,7 +136,7 @@ class FivePaisa(Broker):
|
|
|
138
136
|
retry_on_exception=retry_exception,
|
|
139
137
|
)
|
|
140
138
|
@timeit(MetricName="5Paisa:get_ltp")
|
|
141
|
-
def get_ltp(self, exchange, tradingsymbol):
|
|
139
|
+
def get_ltp(self, exchange, tradingsymbol) -> float:
|
|
142
140
|
tradingsymbol = self.get_symbol(tradingsymbol, exchange)
|
|
143
141
|
exchange_name = self.get_exchange(exchange)
|
|
144
142
|
exchange_type = self.get_exchange_type(exchange)
|
|
@@ -5,7 +5,7 @@ import time
|
|
|
5
5
|
from collections import defaultdict
|
|
6
6
|
import threading
|
|
7
7
|
from threading import Lock
|
|
8
|
-
from typing import Any, Dict
|
|
8
|
+
from typing import Any, Dict, List, Literal
|
|
9
9
|
|
|
10
10
|
from cachetools import TTLCache, cached
|
|
11
11
|
import numpy as np
|
|
@@ -52,6 +52,10 @@ class Broker:
|
|
|
52
52
|
self.instrument_id_to_exchange_map = dict()
|
|
53
53
|
self.instrument_id_to_security_type_map = dict()
|
|
54
54
|
self.exchange_symbol_to_instrument_id_map = defaultdict(dict)
|
|
55
|
+
|
|
56
|
+
self.quantplay_symbol_map: Dict[str, str] = {}
|
|
57
|
+
self.broker_symbol_map: Dict[str, str] = {}
|
|
58
|
+
|
|
55
59
|
self.order_type_sl = "SL"
|
|
56
60
|
self.nfo_exchange = "NFO"
|
|
57
61
|
|
|
@@ -118,6 +122,7 @@ class Broker:
|
|
|
118
122
|
|
|
119
123
|
self.trigger_pending_status = "TRIGGER PENDING"
|
|
120
124
|
self.lock = Lock()
|
|
125
|
+
self.square_off_lock = Lock()
|
|
121
126
|
|
|
122
127
|
def validate_exchange(self, exchange):
|
|
123
128
|
if exchange not in ["NSE", "NFO", "BFO", "BSE"]:
|
|
@@ -137,7 +142,7 @@ class Broker:
|
|
|
137
142
|
)
|
|
138
143
|
raise StaleDataFound(f"Couldn't find symbol data for [{exchange}:{symbol}]")
|
|
139
144
|
|
|
140
|
-
def initialize_symbol_data(self, save_as=None):
|
|
145
|
+
def initialize_symbol_data(self, save_as: str | None = None) -> None:
|
|
141
146
|
instruments = self.instrument_data
|
|
142
147
|
instruments = instruments.to_dict("records")
|
|
143
148
|
self.symbol_data = {}
|
|
@@ -148,7 +153,8 @@ class Broker:
|
|
|
148
153
|
instrument_data = copy.deepcopy(instrument)
|
|
149
154
|
self.symbol_data["{}:{}".format(exchange, tradingsymbol)] = instrument_data
|
|
150
155
|
|
|
151
|
-
|
|
156
|
+
if save_as:
|
|
157
|
+
PickleUtils.save_data(self.symbol_data, save_as)
|
|
152
158
|
|
|
153
159
|
@timeit(MetricName="Broker:initialize_broker_symbol_map")
|
|
154
160
|
def initialize_broker_symbol_map(self):
|
|
@@ -163,7 +169,7 @@ class Broker:
|
|
|
163
169
|
and platform_version[0] == "3"
|
|
164
170
|
and platform_version[1] == "8"
|
|
165
171
|
):
|
|
166
|
-
self.quantplay_symbol_map = {}
|
|
172
|
+
self.quantplay_symbol_map: Dict[str, str] = {}
|
|
167
173
|
for k in self.broker_symbol_map:
|
|
168
174
|
v = self.broker_symbol_map[k]
|
|
169
175
|
self.quantplay_symbol_map[v] = k
|
|
@@ -171,7 +177,7 @@ class Broker:
|
|
|
171
177
|
self.quantplay_symbol_map = {v: k for k, v in self.broker_symbol_map.items()}
|
|
172
178
|
|
|
173
179
|
@timeit(MetricName="Broker:load_instrument")
|
|
174
|
-
def load_instrument(self, file_name):
|
|
180
|
+
def load_instrument(self, file_name: str) -> None:
|
|
175
181
|
try:
|
|
176
182
|
instrument_data_instance = InstrumentData.get_instance()
|
|
177
183
|
|
|
@@ -391,7 +397,7 @@ class Broker:
|
|
|
391
397
|
tradingsymbol=tradingsymbol,
|
|
392
398
|
exchange=exchange,
|
|
393
399
|
quantity=quantity,
|
|
394
|
-
order_type=self.order_type_sl, # type:ignore
|
|
400
|
+
order_type=self.order_type_sl, # type: ignore
|
|
395
401
|
transaction_type=sl_transaction_type,
|
|
396
402
|
tag=tag,
|
|
397
403
|
product=product,
|
|
@@ -600,18 +606,25 @@ class Broker:
|
|
|
600
606
|
stoploss_order["price"] = self.round_to_tick(ltp)
|
|
601
607
|
stoploss_order["trigger_price"] = None
|
|
602
608
|
|
|
603
|
-
self.modify_order(stoploss_order) # type:ignore
|
|
609
|
+
self.modify_order(stoploss_order) # type: ignore
|
|
604
610
|
time.sleep(0.1)
|
|
605
611
|
|
|
606
612
|
self.modify_orders_till_complete(orders_to_close, sleep_time=modify_sleep_time)
|
|
607
613
|
Constants.logger.info("All order have been closed successfully")
|
|
608
614
|
|
|
609
|
-
def market_protection_price(
|
|
615
|
+
def market_protection_price(
|
|
616
|
+
self,
|
|
617
|
+
price: float,
|
|
618
|
+
transaction_type: Literal["BUY", "SELL"],
|
|
619
|
+
market_protection: float = 0.02,
|
|
620
|
+
) -> float:
|
|
610
621
|
if transaction_type == "BUY":
|
|
611
622
|
price = self.round_to_tick(price * (1 + market_protection))
|
|
612
623
|
return price + 1
|
|
624
|
+
|
|
613
625
|
elif transaction_type == "SELL":
|
|
614
626
|
return self.round_to_tick(price * (1 - market_protection))
|
|
627
|
+
|
|
615
628
|
return price
|
|
616
629
|
|
|
617
630
|
def split_order(self, exchange, tradingsymbol, quantity, max_qty=None):
|
|
@@ -883,7 +896,9 @@ class Broker:
|
|
|
883
896
|
logger.error(f"Couldn't compute freeze quantity for {exchange} {symbol}")
|
|
884
897
|
return 25
|
|
885
898
|
|
|
886
|
-
def square_off_by_tag(
|
|
899
|
+
def square_off_by_tag(
|
|
900
|
+
self, tag, dry_run=True, sleep_time=0.05
|
|
901
|
+
) -> List[Dict[str, Any]]:
|
|
887
902
|
self.exit_all_trigger_orders(tag=tag)
|
|
888
903
|
orders = self.orders(tag=tag)
|
|
889
904
|
|
|
@@ -906,7 +921,7 @@ class Broker:
|
|
|
906
921
|
]
|
|
907
922
|
)
|
|
908
923
|
|
|
909
|
-
orders_to_close = []
|
|
924
|
+
orders_to_close: List[Dict[str, Any]] = []
|
|
910
925
|
exit_orders = exit_orders.filter(pl.col("exit_quantity") != 0)
|
|
911
926
|
positions = exit_orders.to_dicts()
|
|
912
927
|
for position in positions:
|
|
@@ -978,13 +993,13 @@ class Broker:
|
|
|
978
993
|
orders.loc[:, "ltp"] = orders["exchange_symbol"].map(symbol_ltp)
|
|
979
994
|
|
|
980
995
|
def modify_price(self, order_id, price, trigger_price=None, order_type=None):
|
|
981
|
-
data = {
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
996
|
+
data = {
|
|
997
|
+
"order_id": order_id,
|
|
998
|
+
"price": price,
|
|
999
|
+
"order_type": order_type,
|
|
1000
|
+
"variety": "regular",
|
|
1001
|
+
"trigger_price": trigger_price,
|
|
1002
|
+
}
|
|
988
1003
|
|
|
989
1004
|
self.modify_order(data) # type: ignore
|
|
990
1005
|
|
|
@@ -1111,7 +1126,7 @@ class Broker:
|
|
|
1111
1126
|
)
|
|
1112
1127
|
|
|
1113
1128
|
order["order_type"] = "LIMIT"
|
|
1114
|
-
self.modify_order(order) # type:ignore
|
|
1129
|
+
self.modify_order(order) # type: ignore
|
|
1115
1130
|
|
|
1116
1131
|
time.sleep(0.1)
|
|
1117
1132
|
|
|
@@ -147,9 +147,9 @@ class Motilal(Broker):
|
|
|
147
147
|
self.instrument_data_by_exchange[exchange] = df
|
|
148
148
|
|
|
149
149
|
@timeit(MetricName="Motilal:load_instrument")
|
|
150
|
-
def load_instrument(self, file_name=None):
|
|
150
|
+
def load_instrument(self, file_name: str | None = None) -> None:
|
|
151
151
|
try:
|
|
152
|
-
self.symbol_data = InstrumentData.get_instance().load_data( # type:ignore
|
|
152
|
+
self.symbol_data = InstrumentData.get_instance().load_data( # type: ignore
|
|
153
153
|
"motilal_instruments"
|
|
154
154
|
)
|
|
155
155
|
Constants.logger.info("[LOADING_INSTRUMENTS] loading data from cache")
|
|
@@ -256,7 +256,7 @@ class Motilal(Broker):
|
|
|
256
256
|
wait_exponential_max=10000,
|
|
257
257
|
stop_max_attempt_number=3,
|
|
258
258
|
)
|
|
259
|
-
def get_ltp(self, exchange=None, tradingsymbol=None):
|
|
259
|
+
def get_ltp(self, exchange=None, tradingsymbol=None) -> float:
|
|
260
260
|
tradingsymbol = self.get_symbol(tradingsymbol)
|
|
261
261
|
token = self.symbol_data["{}:{}".format(exchange, tradingsymbol)]["token"]
|
|
262
262
|
exchange = self.get_exchange(exchange)
|
|
@@ -273,35 +273,6 @@ class Motilal(Broker):
|
|
|
273
273
|
Constants.logger.info("[GET_LTP_RESPONSE] response {}".format(response.json()))
|
|
274
274
|
return response.json()["data"]["ltp"] / 100.0
|
|
275
275
|
|
|
276
|
-
def get_orders(self, order_status=None, order_type=None):
|
|
277
|
-
response = (requests.post(self.order_book_url, headers=self.headers)).json()
|
|
278
|
-
if response["status"] == "ERROR":
|
|
279
|
-
Constants.logger.info(
|
|
280
|
-
"Error while fetching order book [{}]".format(response["message"])
|
|
281
|
-
)
|
|
282
|
-
raise Exception(response["message"])
|
|
283
|
-
orders = response["data"]
|
|
284
|
-
|
|
285
|
-
if order_status:
|
|
286
|
-
orders = [a for a in orders if a["orderstatus"] == order_status]
|
|
287
|
-
|
|
288
|
-
if order_type:
|
|
289
|
-
orders = [a for a in orders if a["ordertype"] == order_type]
|
|
290
|
-
|
|
291
|
-
return orders
|
|
292
|
-
|
|
293
|
-
def get_positions(self):
|
|
294
|
-
response = (requests.post(self.positions_url, headers=self.headers)).json()
|
|
295
|
-
if response["status"] == "ERROR":
|
|
296
|
-
Constants.logger.info(
|
|
297
|
-
"Error while fetching order book [{}]".format(response["message"])
|
|
298
|
-
)
|
|
299
|
-
raise Exception(response["message"])
|
|
300
|
-
|
|
301
|
-
positions = response["data"]
|
|
302
|
-
|
|
303
|
-
return positions
|
|
304
|
-
|
|
305
276
|
@timeit(MetricName="Motilal:modify_price")
|
|
306
277
|
def modify_price(self, order_id, price, trigger_price=None, order_type=None):
|
|
307
278
|
orders = self.orders()
|
|
@@ -566,8 +537,8 @@ class Motilal(Broker):
|
|
|
566
537
|
@timeit(MetricName="Motilal:account_summary")
|
|
567
538
|
def account_summary(self):
|
|
568
539
|
response = self.margins()
|
|
540
|
+
response["pnl"] = float(self.positions()["pnl"].sum()) # type: ignore
|
|
569
541
|
|
|
570
|
-
response["pnl"] = self.positions()["pnl"].sum() # type:ignore
|
|
571
542
|
return response
|
|
572
543
|
|
|
573
544
|
@timeit(MetricName="Motilal:profile")
|
|
@@ -720,7 +691,8 @@ class Motilal(Broker):
|
|
|
720
691
|
)
|
|
721
692
|
raise Exception(response["message"])
|
|
722
693
|
orders = response["data"]
|
|
723
|
-
|
|
694
|
+
|
|
695
|
+
if orders is None or len(orders) == 0:
|
|
724
696
|
return pl.DataFrame(schema=self.orders_schema)
|
|
725
697
|
|
|
726
698
|
orders_df = pl.DataFrame(orders)
|
|
@@ -39,7 +39,7 @@ class Noren(Broker):
|
|
|
39
39
|
):
|
|
40
40
|
super(Noren, self).__init__()
|
|
41
41
|
|
|
42
|
-
self.order_updates: Queue = order_updates
|
|
42
|
+
self.order_updates: Queue | None = order_updates
|
|
43
43
|
|
|
44
44
|
if load_instrument:
|
|
45
45
|
self.load_instrument()
|
|
@@ -53,9 +53,9 @@ class Noren(Broker):
|
|
|
53
53
|
self.full_name = response["uname"]
|
|
54
54
|
self.user_token = response["susertoken"]
|
|
55
55
|
|
|
56
|
-
def load_instrument(self, file_name=None):
|
|
56
|
+
def load_instrument(self, file_name: str | None = None) -> None:
|
|
57
57
|
try:
|
|
58
|
-
self.symbol_data = InstrumentData.get_instance().load_data( # type:ignore
|
|
58
|
+
self.symbol_data = InstrumentData.get_instance().load_data( # type: ignore
|
|
59
59
|
"shoonya_instruments"
|
|
60
60
|
)
|
|
61
61
|
Constants.logger.info("[LOADING_INSTRUMENTS] loading data from cache")
|
|
@@ -169,7 +169,10 @@ class Noren(Broker):
|
|
|
169
169
|
order["status"] = "CANCELLED"
|
|
170
170
|
|
|
171
171
|
print(f"order feed {order}")
|
|
172
|
-
|
|
172
|
+
|
|
173
|
+
if self.order_updates:
|
|
174
|
+
self.order_updates.put(order)
|
|
175
|
+
|
|
173
176
|
except Exception as e:
|
|
174
177
|
logger.error("[ORDER_UPDATE_PROCESSING_FAILED] {}".format(e))
|
|
175
178
|
|
|
@@ -62,17 +62,22 @@ class Upstox(Broker):
|
|
|
62
62
|
|
|
63
63
|
self.configuration = upstox_client.Configuration()
|
|
64
64
|
self.configuration.access_token = self.access_token
|
|
65
|
-
|
|
65
|
+
|
|
66
66
|
except Exception as e:
|
|
67
67
|
raise e
|
|
68
68
|
|
|
69
69
|
self.configuration = upstox_client.Configuration()
|
|
70
70
|
self.configuration.access_token = self.access_token
|
|
71
|
+
self.api_version = "2.0"
|
|
72
|
+
|
|
73
|
+
self.api_client = upstox_client.ApiClient(self.configuration)
|
|
74
|
+
|
|
71
75
|
if load_instrument:
|
|
72
76
|
self.load_instrument()
|
|
77
|
+
|
|
73
78
|
super(Upstox, self).__init__()
|
|
74
79
|
|
|
75
|
-
def load_instrument(self, file_name=None):
|
|
80
|
+
def load_instrument(self, file_name: str | None = None) -> None:
|
|
76
81
|
super().load_instrument("upstox_instruments")
|
|
77
82
|
|
|
78
83
|
def handle_exception(self, e):
|
|
@@ -92,6 +97,7 @@ class Upstox(Broker):
|
|
|
92
97
|
return "D"
|
|
93
98
|
elif product == "MIS":
|
|
94
99
|
return "I"
|
|
100
|
+
|
|
95
101
|
return product
|
|
96
102
|
|
|
97
103
|
def get_exchange(self, exchange):
|
|
@@ -125,17 +131,15 @@ class Upstox(Broker):
|
|
|
125
131
|
)
|
|
126
132
|
@timeit(MetricName="Upstox:get_ltp")
|
|
127
133
|
def get_ltp(self, exchange: ExchangeType, tradingsymbol: str) -> float:
|
|
128
|
-
|
|
129
|
-
api_instance = upstox_client.MarketQuoteApi(
|
|
130
|
-
upstox_client.ApiClient(self.configuration)
|
|
131
|
-
)
|
|
132
|
-
api_version = "2.0" # str | API Version Header
|
|
134
|
+
api_instance = upstox_client.MarketQuoteApi(self.api_client)
|
|
133
135
|
|
|
134
136
|
ltp = 0
|
|
135
137
|
try:
|
|
136
138
|
symbol_info = self.symbol_data[f"{exchange}:{tradingsymbol}"]
|
|
137
139
|
# Market quotes and instruments - LTP quotes.
|
|
138
|
-
api_response = api_instance.ltp(
|
|
140
|
+
api_response = api_instance.ltp(
|
|
141
|
+
symbol_info["instrument_key"], self.api_version
|
|
142
|
+
)
|
|
139
143
|
|
|
140
144
|
ltp: float = api_response.data[ # type:ignore
|
|
141
145
|
f"{self.get_exchange(symbol_info['exchange'])}:{tradingsymbol}"
|
|
@@ -158,8 +162,7 @@ class Upstox(Broker):
|
|
|
158
162
|
or order_to_modify["trigger_price"] is None
|
|
159
163
|
):
|
|
160
164
|
order_to_modify["trigger_price"] = 0
|
|
161
|
-
|
|
162
|
-
api_instance = upstox_client.OrderApi(upstox_client.ApiClient(self.configuration))
|
|
165
|
+
api_instance = upstox_client.OrderApi(self.api_client)
|
|
163
166
|
body = upstox_client.ModifyOrderRequest(
|
|
164
167
|
validity="DAY",
|
|
165
168
|
price=order_to_modify["price"],
|
|
@@ -167,11 +170,10 @@ class Upstox(Broker):
|
|
|
167
170
|
order_type=order_to_modify["order_type"],
|
|
168
171
|
trigger_price=order_to_modify["trigger_price"],
|
|
169
172
|
)
|
|
170
|
-
api_version = "2.0" # str | API Version Header
|
|
171
173
|
|
|
172
174
|
try:
|
|
173
175
|
# Modify order
|
|
174
|
-
api_response = api_instance.modify_order(body, api_version)
|
|
176
|
+
api_response = api_instance.modify_order(body, self.api_version)
|
|
175
177
|
return api_response.status # type:ignore
|
|
176
178
|
except ApiException as e:
|
|
177
179
|
Constants.logger.error(
|
|
@@ -188,13 +190,11 @@ class Upstox(Broker):
|
|
|
188
190
|
|
|
189
191
|
@timeit(MetricName="Upstox:cancel_order")
|
|
190
192
|
def cancel_order(self, order_id: str) -> None:
|
|
191
|
-
|
|
192
|
-
api_instance = upstox_client.OrderApi(upstox_client.ApiClient(self.configuration))
|
|
193
|
-
api_version = "2.0" # str | API Version Header
|
|
193
|
+
api_instance = upstox_client.OrderApi(self.api_client)
|
|
194
194
|
|
|
195
195
|
try:
|
|
196
196
|
# Cancel order
|
|
197
|
-
api_response = api_instance.cancel_order(order_id, api_version)
|
|
197
|
+
api_response = api_instance.cancel_order(order_id, self.api_version)
|
|
198
198
|
return api_response.status # type:ignore
|
|
199
199
|
except ApiException as e:
|
|
200
200
|
if self.handle_exception(e) == 400:
|
|
@@ -224,10 +224,7 @@ class Upstox(Broker):
|
|
|
224
224
|
if trigger_price is None:
|
|
225
225
|
trigger_price = 0
|
|
226
226
|
|
|
227
|
-
|
|
228
|
-
api_instance = upstox_client.OrderApi(
|
|
229
|
-
upstox_client.ApiClient(self.configuration)
|
|
230
|
-
)
|
|
227
|
+
api_instance = upstox_client.OrderApi(self.api_client)
|
|
231
228
|
body = upstox_client.PlaceOrderRequest(
|
|
232
229
|
quantity=quantity,
|
|
233
230
|
product=product,
|
|
@@ -240,8 +237,8 @@ class Upstox(Broker):
|
|
|
240
237
|
trigger_price=trigger_price,
|
|
241
238
|
is_amo=False,
|
|
242
239
|
)
|
|
243
|
-
|
|
244
|
-
api_response = api_instance.place_order(body, api_version)
|
|
240
|
+
|
|
241
|
+
api_response = api_instance.place_order(body, self.api_version)
|
|
245
242
|
return api_response.data.order_id # type:ignore
|
|
246
243
|
except Exception as e:
|
|
247
244
|
raise QuantplayOrderPlacementException(str(e))
|
|
@@ -283,14 +280,12 @@ class Upstox(Broker):
|
|
|
283
280
|
)
|
|
284
281
|
@timeit(MetricName="Upstox:profile")
|
|
285
282
|
def profile(self) -> UserBrokerProfileResponse:
|
|
286
|
-
|
|
287
|
-
api_instance = upstox_client.UserApi(upstox_client.ApiClient(self.configuration))
|
|
288
|
-
api_version = "2.0" # str | API Version Header
|
|
283
|
+
api_instance = upstox_client.UserApi(self.api_client)
|
|
289
284
|
|
|
290
285
|
response: UserBrokerProfileResponse = {"user_id": "", "full_name": ""}
|
|
291
286
|
try:
|
|
292
287
|
# Get profile
|
|
293
|
-
api_response = api_instance.get_profile(api_version)
|
|
288
|
+
api_response = api_instance.get_profile(self.api_version)
|
|
294
289
|
profile_data = api_response.data # type:ignore
|
|
295
290
|
response: UserBrokerProfileResponse = {
|
|
296
291
|
"user_id": profile_data.user_id,
|
|
@@ -308,15 +303,12 @@ class Upstox(Broker):
|
|
|
308
303
|
return response
|
|
309
304
|
|
|
310
305
|
def holdings(self):
|
|
311
|
-
api_instance = upstox_client.PortfolioApi(
|
|
312
|
-
upstox_client.ApiClient(self.configuration)
|
|
313
|
-
)
|
|
306
|
+
api_instance = upstox_client.PortfolioApi(self.api_client)
|
|
314
307
|
|
|
315
|
-
api_version = "2.0" # str | API Version Header
|
|
316
308
|
holdings_df = pl.DataFrame()
|
|
317
309
|
try:
|
|
318
310
|
# Get Holdings
|
|
319
|
-
api_response = api_instance.get_holdings(api_version)
|
|
311
|
+
api_response = api_instance.get_holdings(self.api_version)
|
|
320
312
|
holdings = [holding.to_dict() for holding in api_response.data] # type:ignore
|
|
321
313
|
holdings_df = pl.DataFrame(holdings)
|
|
322
314
|
except ApiException as e:
|
|
@@ -364,15 +356,12 @@ class Upstox(Broker):
|
|
|
364
356
|
)
|
|
365
357
|
@timeit(MetricName="Upstox:positions")
|
|
366
358
|
def positions(self, drop_cnc: bool = True) -> pl.DataFrame:
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
upstox_client.ApiClient(self.configuration)
|
|
370
|
-
)
|
|
371
|
-
api_version = "2.0" # str | API Version Header
|
|
359
|
+
api_instance = upstox_client.PortfolioApi(self.api_client)
|
|
360
|
+
|
|
372
361
|
positions_df = pl.DataFrame(schema=self.positions_schema)
|
|
373
362
|
try:
|
|
374
363
|
# Get Positions
|
|
375
|
-
api_response = api_instance.get_positions(api_version)
|
|
364
|
+
api_response = api_instance.get_positions(self.api_version)
|
|
376
365
|
positions = [
|
|
377
366
|
position.to_dict() for position in api_response.data # type:ignore
|
|
378
367
|
]
|
|
@@ -462,14 +451,12 @@ class Upstox(Broker):
|
|
|
462
451
|
)
|
|
463
452
|
@timeit(MetricName="Upstox:orders")
|
|
464
453
|
def orders(self, tag: str | None = None, add_ltp: bool = True) -> pl.DataFrame:
|
|
465
|
-
|
|
466
|
-
api_instance = upstox_client.OrderApi(upstox_client.ApiClient(self.configuration))
|
|
467
|
-
api_version = "2.0" # str | API Version Header
|
|
454
|
+
api_instance = upstox_client.OrderApi(self.api_client)
|
|
468
455
|
|
|
469
456
|
orders_df = pl.DataFrame()
|
|
470
457
|
try:
|
|
471
458
|
# Get order book
|
|
472
|
-
api_response = api_instance.get_order_book(api_version)
|
|
459
|
+
api_response = api_instance.get_order_book(self.api_version)
|
|
473
460
|
orders = [order.to_dict() for order in api_response.data] # type:ignore
|
|
474
461
|
orders_df = pl.DataFrame(orders)
|
|
475
462
|
except ApiException as e:
|
|
@@ -569,15 +556,17 @@ class Upstox(Broker):
|
|
|
569
556
|
)
|
|
570
557
|
@timeit(MetricName="Upstox:margins")
|
|
571
558
|
def margins(self):
|
|
572
|
-
api_instance = upstox_client.UserApi(
|
|
573
|
-
|
|
559
|
+
api_instance = upstox_client.UserApi(self.api_client)
|
|
560
|
+
|
|
574
561
|
segment = "SEC" # str | (optional)
|
|
575
562
|
|
|
576
563
|
margin_used = 0
|
|
577
564
|
margin_available = 0
|
|
578
565
|
try:
|
|
579
566
|
# Get User Fund And Margin
|
|
580
|
-
api_response = api_instance.get_user_fund_margin(
|
|
567
|
+
api_response = api_instance.get_user_fund_margin(
|
|
568
|
+
self.api_version, segment=segment
|
|
569
|
+
)
|
|
581
570
|
margin_used: float = float(
|
|
582
571
|
api_response.data["equity"].used_margin # type:ignore
|
|
583
572
|
)
|
|
@@ -630,11 +619,13 @@ class Upstox(Broker):
|
|
|
630
619
|
# Configure OAuth2 access token for authorization: OAUTH2
|
|
631
620
|
configuration = upstox_client.Configuration()
|
|
632
621
|
|
|
633
|
-
api_version = "2.0"
|
|
622
|
+
self.api_version = "2.0"
|
|
634
623
|
configuration.access_token = self.access_token
|
|
635
624
|
|
|
636
625
|
# Get portfolio stream feed authorize
|
|
637
|
-
response = self.get_portfolio_stream_feed_authorize(
|
|
626
|
+
response = self.get_portfolio_stream_feed_authorize(
|
|
627
|
+
self.api_version, configuration
|
|
628
|
+
)
|
|
638
629
|
|
|
639
630
|
async with websockets.connect(
|
|
640
631
|
response.data.authorized_redirect_uri, # type:ignore
|
|
@@ -650,6 +641,7 @@ class Upstox(Broker):
|
|
|
650
641
|
def get_portfolio_stream_feed_authorize(self, api_version, configuration):
|
|
651
642
|
api_instance = upstox_client.WebsocketApi(upstox_client.ApiClient(configuration))
|
|
652
643
|
api_response = api_instance.get_portfolio_stream_feed_authorize(api_version)
|
|
644
|
+
|
|
653
645
|
return api_response
|
|
654
646
|
|
|
655
647
|
def get_quantplay_product(self, exchange, product):
|
|
@@ -673,6 +665,7 @@ class Upstox(Broker):
|
|
|
673
665
|
)
|
|
674
666
|
Constants.logger.info("[UPDATE_RECEIVED] {}".format(order))
|
|
675
667
|
self.order_updates.put(order)
|
|
668
|
+
|
|
676
669
|
except Exception as e:
|
|
677
670
|
traceback.print_exc()
|
|
678
671
|
Constants.logger.error("[ORDER_UPDATE_PROCESSING_FAILED] {}".format(e))
|
|
@@ -33,7 +33,7 @@ from quantplay.model.broker.generics import OrderType as NewOrderType
|
|
|
33
33
|
from quantplay.model.broker.generics import ProductType as NewProductType
|
|
34
34
|
|
|
35
35
|
# -# Suppress only the single warning from urllib3 needed.
|
|
36
|
-
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) # type:ignore
|
|
36
|
+
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) # type: ignore
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
class XTS(Broker):
|
|
@@ -54,7 +54,7 @@ class XTS(Broker):
|
|
|
54
54
|
load_instrument=True,
|
|
55
55
|
):
|
|
56
56
|
super(XTS, self).__init__()
|
|
57
|
-
self.order_updates: Queue = order_updates
|
|
57
|
+
self.order_updates: Queue | None = order_updates
|
|
58
58
|
self.root_url = root_url
|
|
59
59
|
|
|
60
60
|
try:
|
|
@@ -78,12 +78,10 @@ class XTS(Broker):
|
|
|
78
78
|
codecs.decode(serialized_md_wrapper.encode(), "base64")
|
|
79
79
|
)
|
|
80
80
|
|
|
81
|
-
def load_instrument(self, file_name=None):
|
|
81
|
+
def load_instrument(self, file_name: str | None = None) -> None:
|
|
82
82
|
# TODO: Check for Futures
|
|
83
83
|
try:
|
|
84
|
-
self.symbol_data = InstrumentData.get_instance().load_data( # type:ignore
|
|
85
|
-
"xts_instruments"
|
|
86
|
-
)
|
|
84
|
+
self.symbol_data = InstrumentData.get_instance().load_data("xts_instruments") # type: ignore
|
|
87
85
|
Constants.logger.info("[LOADING_INSTRUMENTS] loading data from cache")
|
|
88
86
|
except Exception:
|
|
89
87
|
instruments = pd.read_csv(
|
|
@@ -522,7 +520,7 @@ class XTS(Broker):
|
|
|
522
520
|
|
|
523
521
|
return exchange_code_map[exchange]
|
|
524
522
|
|
|
525
|
-
def get_ltp(self, exchange=None, tradingsymbol=None):
|
|
523
|
+
def get_ltp(self, exchange=None, tradingsymbol=None) -> float:
|
|
526
524
|
exchange_code = self.get_exchange_code(exchange)
|
|
527
525
|
exchange_token = self.symbol_data[f"{exchange}:{tradingsymbol}"]["exchange_token"]
|
|
528
526
|
|
|
@@ -745,7 +743,9 @@ class XTS(Broker):
|
|
|
745
743
|
|
|
746
744
|
if new_ord["status"] == "TRIGGER PENDING" and new_ord["trigger_price"] == 0:
|
|
747
745
|
return
|
|
748
|
-
|
|
746
|
+
|
|
747
|
+
if self.order_updates:
|
|
748
|
+
self.order_updates.put(new_ord)
|
|
749
749
|
|
|
750
750
|
except Exception as e:
|
|
751
751
|
print(e)
|
|
@@ -64,9 +64,9 @@ class Zerodha(Broker):
|
|
|
64
64
|
def set_wrapper(self, serialized_wrapper):
|
|
65
65
|
self.wrapper = pickle.loads(codecs.decode(serialized_wrapper.encode(), "base64"))
|
|
66
66
|
|
|
67
|
-
def initialize_symbol_data(self, save_as=None):
|
|
67
|
+
def initialize_symbol_data(self, save_as: str | None = None) -> None:
|
|
68
68
|
try:
|
|
69
|
-
self.symbol_data = InstrumentData.get_instance().load_data( # type:ignore
|
|
69
|
+
self.symbol_data = InstrumentData.get_instance().load_data( # type: ignore
|
|
70
70
|
"zerodha_instruments"
|
|
71
71
|
)
|
|
72
72
|
Constants.logger.info("[LOADING_INSTRUMENTS] loading data from cache")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .exceptions import * # noqa: F403
|
|
@@ -2,6 +2,7 @@ import logging
|
|
|
2
2
|
import decimal
|
|
3
3
|
from functools import wraps
|
|
4
4
|
import time
|
|
5
|
+
from typing import Any
|
|
5
6
|
import numpy as np
|
|
6
7
|
import json
|
|
7
8
|
from datetime import datetime
|
|
@@ -14,13 +15,15 @@ formatter = logging.Formatter(
|
|
|
14
15
|
|
|
15
16
|
class LoggerUtils:
|
|
16
17
|
@staticmethod
|
|
17
|
-
def get_log_file_path(file_name):
|
|
18
|
+
def get_log_file_path(file_name: str) -> str:
|
|
18
19
|
today_date = datetime.now()
|
|
19
20
|
|
|
20
21
|
return "/tmp/" + file_name + "-" + today_date.strftime("%Y-%m-%d:01") + ".log"
|
|
21
22
|
|
|
22
23
|
@staticmethod
|
|
23
|
-
def setup_logger(
|
|
24
|
+
def setup_logger(
|
|
25
|
+
logger_name: str, log_file: str, level=logging.DEBUG
|
|
26
|
+
) -> logging.Logger:
|
|
24
27
|
log_file = LoggerUtils.get_log_file_path(log_file)
|
|
25
28
|
"""Function setup as many loggers as you want"""
|
|
26
29
|
|
|
@@ -48,19 +51,16 @@ class Constants:
|
|
|
48
51
|
tick_logger = LoggerUtils.setup_logger("tick_logger", "tick")
|
|
49
52
|
|
|
50
53
|
@staticmethod
|
|
51
|
-
def myconverter(o):
|
|
54
|
+
def myconverter(o: Any):
|
|
52
55
|
if isinstance(o, datetime):
|
|
53
56
|
return o.__str__()
|
|
54
57
|
if isinstance(o, decimal.Decimal):
|
|
55
|
-
# wanted a simple yield str(o) in the next line,
|
|
56
|
-
# but that would mean a yield on the line with super(...),
|
|
57
|
-
# which wouldn't work (see my comment below), so...
|
|
58
58
|
return float(o)
|
|
59
59
|
if isinstance(o, np.int64): # type:ignore
|
|
60
60
|
return int(o)
|
|
61
61
|
|
|
62
62
|
@staticmethod
|
|
63
|
-
def round_to_tick(number):
|
|
63
|
+
def round_to_tick(number: int | float) -> float:
|
|
64
64
|
return round(number * 20) / 20
|
|
65
65
|
|
|
66
66
|
|
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import pickle
|
|
2
|
+
from typing import Any
|
|
2
3
|
from quantplay.utils.constant import timeit
|
|
3
4
|
from threading import Lock
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
class PickleUtils:
|
|
7
8
|
@staticmethod
|
|
8
|
-
def save_data(data, file_name):
|
|
9
|
+
def save_data(data: Any, file_name: str):
|
|
9
10
|
with open("/tmp/{}.pickle".format(file_name), "wb") as handle:
|
|
10
11
|
pickle.dump(data, handle, protocol=pickle.HIGHEST_PROTOCOL)
|
|
11
12
|
|
|
12
13
|
@staticmethod
|
|
13
14
|
@timeit(MetricName="PickleUtils:load_data")
|
|
14
|
-
def load_data(file_name):
|
|
15
|
+
def load_data(file_name: str):
|
|
15
16
|
with open("/tmp/{}.pickle".format(file_name), "rb") as disk_data:
|
|
16
17
|
unserialized_data = pickle.load(disk_data)
|
|
17
18
|
|
|
@@ -39,7 +40,7 @@ class InstrumentData:
|
|
|
39
40
|
InstrumentData.__instance = self
|
|
40
41
|
|
|
41
42
|
@timeit(MetricName="InstrumentData:load_data")
|
|
42
|
-
def load_data(self, file_name):
|
|
43
|
+
def load_data(self, file_name: str):
|
|
43
44
|
if file_name in self.instrument_data:
|
|
44
45
|
return self.instrument_data[file_name]
|
|
45
46
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quantplay-2.0.2/quantplay/strategies → quantplay-2.0.3/quantplay/strategies/equities}/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|