bitunix-automated-crypto-trading 3.1.7__py3-none-any.whl → 3.1.8__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.
- bitunix_automated_crypto_trading/AsyncThreadRunner.py +8 -8
- bitunix_automated_crypto_trading/BitunixApi.py +12 -13
- bitunix_automated_crypto_trading/BitunixSignal.py +83 -47
- bitunix_automated_crypto_trading/BitunixWebSocket.py +21 -21
- bitunix_automated_crypto_trading/NotificationManager.py +3 -3
- bitunix_automated_crypto_trading/SupportResistance.py +63 -35
- bitunix_automated_crypto_trading/ThreadManager.py +4 -3
- bitunix_automated_crypto_trading/TickerManager.py +56 -47
- bitunix_automated_crypto_trading/bitunix.py +42 -16
- bitunix_automated_crypto_trading/config.py +8 -2
- bitunix_automated_crypto_trading/logger.py +1 -1
- {bitunix_automated_crypto_trading-3.1.7.dist-info → bitunix_automated_crypto_trading-3.1.8.dist-info}/METADATA +1 -1
- bitunix_automated_crypto_trading-3.1.8.dist-info/RECORD +18 -0
- bitunix_automated_crypto_trading-3.1.7.dist-info/RECORD +0 -18
- {bitunix_automated_crypto_trading-3.1.7.dist-info → bitunix_automated_crypto_trading-3.1.8.dist-info}/WHEEL +0 -0
- {bitunix_automated_crypto_trading-3.1.7.dist-info → bitunix_automated_crypto_trading-3.1.8.dist-info}/entry_points.txt +0 -0
- {bitunix_automated_crypto_trading-3.1.7.dist-info → bitunix_automated_crypto_trading-3.1.8.dist-info}/top_level.txt +0 -0
@@ -2,10 +2,9 @@ import asyncio
|
|
2
2
|
import threading
|
3
3
|
from logger import Logger
|
4
4
|
import os
|
5
|
-
logger = Logger(__name__).get_logger()
|
6
5
|
|
7
6
|
class AsyncThreadRunner:
|
8
|
-
def __init__(self, async_func, interval, *args, **kwargs):
|
7
|
+
def __init__(self, async_func, logger, interval, *args, **kwargs):
|
9
8
|
self.async_func = async_func
|
10
9
|
self.interval = interval
|
11
10
|
self.args = args
|
@@ -14,6 +13,7 @@ class AsyncThreadRunner:
|
|
14
13
|
self._stop_event = threading.Event()
|
15
14
|
self.thread = threading.Thread(target=self.thread_function)
|
16
15
|
self.task = None
|
16
|
+
self.logger = logger
|
17
17
|
|
18
18
|
def thread_function(self):
|
19
19
|
asyncio.set_event_loop(self.loop)
|
@@ -28,7 +28,7 @@ class AsyncThreadRunner:
|
|
28
28
|
)
|
29
29
|
self.loop.run_forever()
|
30
30
|
except Exception as e:
|
31
|
-
logger.info(f"Async Thread function error: {e}, exiting app")
|
31
|
+
self.logger.info(f"Async Thread function error: {e}, exiting app")
|
32
32
|
os._exit(1) # Exit the program if the thread is stopped
|
33
33
|
finally:
|
34
34
|
pending = asyncio.all_tasks(self.loop)
|
@@ -47,10 +47,10 @@ class AsyncThreadRunner:
|
|
47
47
|
try:
|
48
48
|
await self.async_func(*self.args, **self.kwargs)
|
49
49
|
except Exception as e:
|
50
|
-
logger.info(f"error in periodic_run async thread {self.thread.name} {e}")
|
50
|
+
self.logger.info(f"error in periodic_run async thread {self.thread.name} {e}")
|
51
51
|
os._exit(1) # Exit the program if the thread is stopped
|
52
52
|
await asyncio.sleep(self.interval)
|
53
|
-
logger.info(f"periodic {self.thread.name} Thread stopped, exiting app.")
|
53
|
+
self.logger.info(f"periodic {self.thread.name} Thread stopped, exiting app.")
|
54
54
|
os._exit(1) # Exit the program if the thread is stopped
|
55
55
|
except asyncio.CancelledError:
|
56
56
|
pass
|
@@ -70,13 +70,13 @@ class AsyncThreadRunner:
|
|
70
70
|
try:
|
71
71
|
await asyncio.wrap_future(self.task) # Wait for the cancellation to propagate
|
72
72
|
except asyncio.CancelledError:
|
73
|
-
logger.info(f"{self.thread.name} Task cancelled successfully.")
|
73
|
+
self.logger.info(f"{self.thread.name} Task cancelled successfully.")
|
74
74
|
except Exception as e:
|
75
|
-
logger.error(f"Unexpected error while cancelling the task {self.thread.name}: {e}")
|
75
|
+
self.logger.error(f"Unexpected error while cancelling the task {self.thread.name}: {e}")
|
76
76
|
|
77
77
|
# Stop the event loop safely
|
78
78
|
self.loop.call_soon_threadsafe(self.loop.stop)
|
79
79
|
|
80
80
|
# Wait for the thread to join
|
81
81
|
self.thread.join()
|
82
|
-
logger.info(f"{self.thread.name} Thread stopped and event loop cleaned up.")
|
82
|
+
self.logger.info(f"{self.thread.name} Thread stopped and event loop cleaned up.")
|
@@ -9,15 +9,14 @@ from urllib.parse import urlencode
|
|
9
9
|
from typing import Dict, Any
|
10
10
|
import traceback
|
11
11
|
from logger import Logger
|
12
|
-
logger = Logger(__name__).get_logger()
|
13
12
|
|
14
13
|
|
15
14
|
class BitunixApi:
|
16
15
|
|
17
|
-
def __init__(self, api_key, secret_key, settings):
|
16
|
+
def __init__(self, api_key, secret_key, settings, logger):
|
18
17
|
self.api_key = api_key
|
19
18
|
self.secret_key = secret_key
|
20
|
-
|
19
|
+
self.logger = logger
|
21
20
|
self.session = requests.Session()
|
22
21
|
self.session.headers.update({
|
23
22
|
'Content-Type': 'application/json',
|
@@ -115,7 +114,7 @@ class BitunixApi:
|
|
115
114
|
headers["sign"] = signature
|
116
115
|
|
117
116
|
response = self.session.post(endpoint, data=body_string, headers=headers)
|
118
|
-
logger.info(f"Response: {body_string} {response.json()}")
|
117
|
+
self.logger.info(f"Response: {body_string} {response.json()}")
|
119
118
|
response.raise_for_status()
|
120
119
|
return response.json()
|
121
120
|
|
@@ -153,28 +152,28 @@ class BitunixApi:
|
|
153
152
|
if tradeHistory['code']==0:
|
154
153
|
return tradeHistory['data']
|
155
154
|
else:
|
156
|
-
logger.info(tradeHistory['msg'])
|
155
|
+
self.logger.info(tradeHistory['msg'])
|
157
156
|
|
158
157
|
async def GetPendingOrderData(self,dictparm={}):
|
159
158
|
orders=await self._get_authenticated(self.pending_order_url, dictparm)
|
160
159
|
if orders['code']==0:
|
161
160
|
return orders['data']
|
162
161
|
else:
|
163
|
-
logger.info(orders['msg'])
|
162
|
+
self.logger.info(orders['msg'])
|
164
163
|
|
165
164
|
async def GetPendingPositionData(self, dictparm={}):
|
166
165
|
positions=await self._get_authenticated(self.pending_positions_URL, dictparm)
|
167
166
|
if positions['code']==0:
|
168
167
|
return positions['data']
|
169
168
|
else:
|
170
|
-
logger.info(positions['msg'])
|
169
|
+
self.logger.info(positions['msg'])
|
171
170
|
|
172
171
|
async def GetPositionHistoryData(self, dictparm={}):
|
173
172
|
tradeHistory=await self._get_authenticated(self.position_history_Url, dictparm)
|
174
173
|
if tradeHistory['code']==0:
|
175
174
|
return tradeHistory['data']
|
176
175
|
else:
|
177
|
-
logger.info(tradeHistory['msg'])
|
176
|
+
self.logger.info(tradeHistory['msg'])
|
178
177
|
|
179
178
|
|
180
179
|
async def GetportfolioData(self):
|
@@ -182,7 +181,7 @@ class BitunixApi:
|
|
182
181
|
if portfolio['code']==0:
|
183
182
|
return portfolio['data']
|
184
183
|
else:
|
185
|
-
logger.info(portfolio['msg'])
|
184
|
+
self.logger.info(portfolio['msg'])
|
186
185
|
|
187
186
|
|
188
187
|
async def GetTickerslastPrice(self, tickersStr):
|
@@ -223,7 +222,7 @@ class BitunixApi:
|
|
223
222
|
except Exception as e:
|
224
223
|
stack = traceback.extract_stack()
|
225
224
|
function_name = stack[-2].name
|
226
|
-
logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
|
225
|
+
self.logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
|
227
226
|
|
228
227
|
async def GetTickerData(self):
|
229
228
|
try:
|
@@ -235,7 +234,7 @@ class BitunixApi:
|
|
235
234
|
except Exception as e:
|
236
235
|
stack = traceback.extract_stack()
|
237
236
|
function_name = stack[-2].name
|
238
|
-
logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
|
237
|
+
self.logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
|
239
238
|
|
240
239
|
async def GetDepthData(self,symbol,limit):
|
241
240
|
try:
|
@@ -247,7 +246,7 @@ class BitunixApi:
|
|
247
246
|
except Exception as e:
|
248
247
|
stack = traceback.extract_stack()
|
249
248
|
function_name = stack[-2].name
|
250
|
-
logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
|
249
|
+
self.logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
|
251
250
|
|
252
251
|
async def GetKlineHistory(self, ticker, interval, limit, starttime):
|
253
252
|
data = []
|
@@ -270,7 +269,7 @@ class BitunixApi:
|
|
270
269
|
except Exception as e:
|
271
270
|
stack = traceback.extract_stack()
|
272
271
|
function_name = stack[-2].name
|
273
|
-
logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
|
272
|
+
self.logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
|
274
273
|
|
275
274
|
def __del__(self):
|
276
275
|
"""Cleanup method to close the session"""
|
@@ -13,7 +13,6 @@ from AsyncThreadRunner import AsyncThreadRunner
|
|
13
13
|
from TickerManager import Tickers, Ticker, Interval
|
14
14
|
from DataFrameHtmlRenderer import DataFrameHtmlRenderer
|
15
15
|
from logger import Logger, Colors
|
16
|
-
logger = Logger(__name__).get_logger()
|
17
16
|
colors = Colors()
|
18
17
|
import gc
|
19
18
|
import os
|
@@ -27,16 +26,17 @@ import threading
|
|
27
26
|
cst = pytz.timezone('US/Central')
|
28
27
|
|
29
28
|
class BitunixSignal:
|
30
|
-
def __init__(self, api_key, secret_key, settings, threadManager, notifications, bitunixApi):
|
29
|
+
def __init__(self, api_key, secret_key, settings, threadManager, notifications, bitunixApi, logger):
|
31
30
|
self.api_key = api_key
|
32
31
|
self.secret_key = secret_key
|
33
32
|
self.settings=settings
|
34
33
|
self.threadManager = threadManager
|
35
34
|
self.notifications = notifications
|
36
35
|
self.bitunixApi = bitunixApi
|
36
|
+
self.logger = logger
|
37
37
|
|
38
38
|
#Ticker object
|
39
|
-
self.tickerObjects = Tickers(self.settings)
|
39
|
+
self.tickerObjects = Tickers(self.settings, self.logger)
|
40
40
|
|
41
41
|
#these are used for html rendering as well as storing
|
42
42
|
self.signaldf= pd.DataFrame()
|
@@ -78,8 +78,8 @@ class BitunixSignal:
|
|
78
78
|
self.bitunixPrivateWebSocketClient = BitunixPrivateWebSocketClient(self.api_key, self.secret_key)
|
79
79
|
|
80
80
|
if self.settings.USE_PUBLIC_WEBSOCKET:
|
81
|
-
self.bitunixPublicDepthWebSocketClient = BitunixPublicWebSocketClient(self.api_key, self.secret_key, "depth")
|
82
|
-
self.bitunixPublicTickerWebSocketClient = BitunixPublicWebSocketClient(self.api_key, self.secret_key, "ticker")
|
81
|
+
self.bitunixPublicDepthWebSocketClient = BitunixPublicWebSocketClient(self.api_key, self.secret_key, "depth", logger)
|
82
|
+
self.bitunixPublicTickerWebSocketClient = BitunixPublicWebSocketClient(self.api_key, self.secret_key, "ticker", logger)
|
83
83
|
|
84
84
|
self.tickerList=[]
|
85
85
|
|
@@ -129,52 +129,52 @@ class BitunixSignal:
|
|
129
129
|
await asyncio.create_task(self.DefinehtmlRenderers())
|
130
130
|
|
131
131
|
#async thread that runs forever jobs
|
132
|
-
self.GetportfolioDataTask = AsyncThreadRunner(self.GetportfolioData, interval=int(self.settings.PORTFOLIO_API_INTERVAL))
|
132
|
+
self.GetportfolioDataTask = AsyncThreadRunner(self.GetportfolioData, self.logger, interval=int(self.settings.PORTFOLIO_API_INTERVAL))
|
133
133
|
self.GetportfolioDataTask.start_thread(thread_name="GetportfolioData")
|
134
134
|
|
135
|
-
self.GetPendingPositionDataTask = AsyncThreadRunner(self.GetPendingPositionData, interval=int(self.settings.PENDING_POSITIONS_API_INTERVAL))
|
135
|
+
self.GetPendingPositionDataTask = AsyncThreadRunner(self.GetPendingPositionData, self.logger, interval=int(self.settings.PENDING_POSITIONS_API_INTERVAL))
|
136
136
|
self.GetPendingPositionDataTask.start_thread(thread_name="GetPendingPositionData")
|
137
137
|
|
138
|
-
self.GetPendingOrderDataTask = AsyncThreadRunner(self.GetPendingOrderData, interval=int(self.settings.PENDING_ORDERS_API_INTERVAL))
|
138
|
+
self.GetPendingOrderDataTask = AsyncThreadRunner(self.GetPendingOrderData, self.logger, interval=int(self.settings.PENDING_ORDERS_API_INTERVAL))
|
139
139
|
self.GetPendingOrderDataTask.start_thread(thread_name="GetPendingOrderData")
|
140
140
|
|
141
|
-
self.GetTradeHistoryDataTask = AsyncThreadRunner(self.GetTradeHistoryData, interval=int(self.settings.TRADE_HISTORY_API_INTERVAL))
|
141
|
+
self.GetTradeHistoryDataTask = AsyncThreadRunner(self.GetTradeHistoryData, self.logger, interval=int(self.settings.TRADE_HISTORY_API_INTERVAL))
|
142
142
|
self.GetTradeHistoryDataTask.start_thread(thread_name="GetTradeHistoryData")
|
143
143
|
|
144
|
-
self.GetPositionHistoryDataTask = AsyncThreadRunner(self.GetPositionHistoryData, interval=int(self.settings.POSITION_HISTORY_API_INTERVAL))
|
144
|
+
self.GetPositionHistoryDataTask = AsyncThreadRunner(self.GetPositionHistoryData, self.logger, interval=int(self.settings.POSITION_HISTORY_API_INTERVAL))
|
145
145
|
self.GetPositionHistoryDataTask.start_thread(thread_name="GetPositionHistoryData")
|
146
146
|
|
147
|
-
self.ProcessPrivateDataTask = AsyncThreadRunner(self.bitunixPrivateWebSocketClient.run_websocket, 0, self.ProcessPrivateData)
|
147
|
+
self.ProcessPrivateDataTask = AsyncThreadRunner(self.bitunixPrivateWebSocketClient.run_websocket, self.logger, 0, self.ProcessPrivateData)
|
148
148
|
self.ProcessPrivateDataTask.start_thread(thread_name="ProcessPrivateData")
|
149
149
|
|
150
150
|
if self.settings.USE_PUBLIC_WEBSOCKET:
|
151
151
|
self.bitunixPublicDepthWebSocketClient.tickerList = self.tickerList
|
152
|
-
self.StoreDepthDataTask = AsyncThreadRunner(self.bitunixPublicDepthWebSocketClient.run_websocket, 0, self.StoreDepthData)
|
152
|
+
self.StoreDepthDataTask = AsyncThreadRunner(self.bitunixPublicDepthWebSocketClient.run_websocket, self.logger, 0, self.StoreDepthData)
|
153
153
|
self.StoreDepthDataTask.start_thread(thread_name="StoreDepthData")
|
154
154
|
self.depth_que = asyncio.Queue()
|
155
|
-
self.ProcessDepthDataTask = AsyncThreadRunner(self.ProcessDepthData, interval=0) # run only once
|
155
|
+
self.ProcessDepthDataTask = AsyncThreadRunner(self.ProcessDepthData, self.logger, interval=0) # run only once
|
156
156
|
self.ProcessDepthDataTask.start_thread(thread_name="ProcessDepthData")
|
157
157
|
|
158
158
|
self.bitunixPublicTickerWebSocketClient.tickerList = self.tickerList
|
159
|
-
self.StoreTickerDataTask = AsyncThreadRunner(self.bitunixPublicTickerWebSocketClient.run_websocket, 0, self.StoreTickerData)
|
159
|
+
self.StoreTickerDataTask = AsyncThreadRunner(self.bitunixPublicTickerWebSocketClient.run_websocket, self.logger, 0, self.StoreTickerData)
|
160
160
|
self.StoreTickerDataTask.start_thread(thread_name="StoreTickerData")
|
161
161
|
self.ticker_que = asyncio.Queue()
|
162
|
-
self.ProcessTickerDataTask = AsyncThreadRunner(self.ProcessTickerData, interval=0) # run only once
|
162
|
+
self.ProcessTickerDataTask = AsyncThreadRunner(self.ProcessTickerData, self.logger, interval=0) # run only once
|
163
163
|
self.ProcessTickerDataTask.start_thread(thread_name="ProcessTickerData")
|
164
164
|
|
165
165
|
|
166
166
|
#normal processes
|
167
|
-
self.LoadKlineHistoryTask = AsyncThreadRunner(self.LoadKlineHistory, interval=0) # run only once
|
167
|
+
self.LoadKlineHistoryTask = AsyncThreadRunner(self.LoadKlineHistory, self.logger, interval=0) # run only once
|
168
168
|
self.LoadKlineHistoryTask.start_thread(thread_name="LoadKlineHistory")
|
169
169
|
|
170
|
-
self.AutoTradeProcessTask = AsyncThreadRunner(self.AutoTradeProcess, interval=int(self.settings.SIGNAL_CHECK_INTERVAL))
|
170
|
+
self.AutoTradeProcessTask = AsyncThreadRunner(self.AutoTradeProcess, self.logger, interval=int(self.settings.SIGNAL_CHECK_INTERVAL))
|
171
171
|
self.AutoTradeProcessTask.start_thread(thread_name="AutoTradeProcess")
|
172
172
|
|
173
|
-
self.checkTickerAndAutotradeStatusTask = AsyncThreadRunner(self.checkTickerAndAutotradeStatus, interval=0)
|
173
|
+
self.checkTickerAndAutotradeStatusTask = AsyncThreadRunner(self.checkTickerAndAutotradeStatus, self.logger, interval=0)
|
174
174
|
self.checkTickerAndAutotradeStatusTask.start_thread(thread_name="checkTickerAndAutotradeStatus")
|
175
175
|
|
176
176
|
if not self.settings.USE_PUBLIC_WEBSOCKET:
|
177
|
-
self.GetTickerDataTask = AsyncThreadRunner(self.GetTickerData, interval=int(self.settings.TICKER_DATA_API_INTERVAL))
|
177
|
+
self.GetTickerDataTask = AsyncThreadRunner(self.GetTickerData, self.logger, interval=int(self.settings.TICKER_DATA_API_INTERVAL))
|
178
178
|
self.GetTickerDataTask.start_thread(thread_name="GetTickerData")
|
179
179
|
|
180
180
|
|
@@ -227,7 +227,7 @@ class BitunixSignal:
|
|
227
227
|
if data is not None:
|
228
228
|
self.tickerObjects.load_kline_history(ticker, intervalId, self.settings.BARS, data)
|
229
229
|
if self.settings.VERBOSE_LOGGING:
|
230
|
-
logger.info(f"kline_history: elapsed time {time.time()-start}")
|
230
|
+
self.logger.info(f"kline_history: elapsed time {time.time()-start}")
|
231
231
|
self.notifications.add_notification("Kline history loaded")
|
232
232
|
|
233
233
|
#api data
|
@@ -258,7 +258,7 @@ class BitunixSignal:
|
|
258
258
|
|
259
259
|
self.lastTickerDataTime = time.time()
|
260
260
|
if self.settings.VERBOSE_LOGGING:
|
261
|
-
logger.info(f"GetTickerData: elapsed time {time.time()-start}")
|
261
|
+
self.logger.info(f"GetTickerData: elapsed time {time.time()-start}")
|
262
262
|
if self.settings.BENCHMARK:
|
263
263
|
self.connection = sqlite3.connect("bitunix.db")
|
264
264
|
self.cursor = self.connection.cursor()
|
@@ -300,10 +300,10 @@ class BitunixSignal:
|
|
300
300
|
self.tickerObjects.form_candle(self.tuples_list)
|
301
301
|
self.lastTickerDataTime = time.time()
|
302
302
|
except Exception as e:
|
303
|
-
logger.info(f"Function: ProcessTickerData, {e}, {e.args}, {type(e).__name__}")
|
304
|
-
logger.info(traceback.print_exc())
|
303
|
+
self.logger.info(f"Function: ProcessTickerData, {e}, {e.args}, {type(e).__name__}")
|
304
|
+
self.logger.info(traceback.print_exc())
|
305
305
|
await asyncio.sleep(0.01)
|
306
|
-
logger.info(f"ProcessTickerData: exitied out of the loop, exiting app")
|
306
|
+
self.logger.info(f"ProcessTickerData: exitied out of the loop, exiting app")
|
307
307
|
os._exit(1) # Exit the program
|
308
308
|
|
309
309
|
|
@@ -333,10 +333,10 @@ class BitunixSignal:
|
|
333
333
|
self.depthdf["tickerObj"] = self.depthdf.index.map(self.tickerObjects.get_tickerDict())
|
334
334
|
self.depthdf.apply(self.apply_depth_data2, axis=1)
|
335
335
|
except Exception as e:
|
336
|
-
logger.info(f"Function: ProcessTickerData, {e}, {e.args}, {type(e).__name__}")
|
337
|
-
logger.info(traceback.print_exc())
|
336
|
+
self.logger.info(f"Function: ProcessTickerData, {e}, {e.args}, {type(e).__name__}")
|
337
|
+
self.logger.info(traceback.print_exc())
|
338
338
|
await asyncio.sleep(0.01)
|
339
|
-
logger.info(f"ProcessDepthData: exitied out of the loop, exiting app")
|
339
|
+
self.logger.info(f"ProcessDepthData: exitied out of the loop, exiting app")
|
340
340
|
os._exit(1) # Exit the program
|
341
341
|
|
342
342
|
def apply_depth_data2(self, row):
|
@@ -367,9 +367,9 @@ class BitunixSignal:
|
|
367
367
|
del data, tickerdf, tuples_list
|
368
368
|
gc.collect()
|
369
369
|
except Exception as e:
|
370
|
-
logger.info(e)
|
370
|
+
self.logger.info(e)
|
371
371
|
if self.settings.VERBOSE_LOGGING:
|
372
|
-
logger.info(f"apply_last_data: elapsed time {time.time()-start}")
|
372
|
+
self.logger.info(f"apply_last_data: elapsed time {time.time()-start}")
|
373
373
|
|
374
374
|
# non websocket method
|
375
375
|
# this is called to update bid and ask,
|
@@ -411,9 +411,9 @@ class BitunixSignal:
|
|
411
411
|
self.portfoliodfStyle= self.portfoliodfrenderer.render_html(self.portfoliodf)
|
412
412
|
|
413
413
|
except Exception as e:
|
414
|
-
logger.info(f"Function: GetportfolioData, {e}, {e.args}, {type(e).__name__}")
|
414
|
+
self.logger.info(f"Function: GetportfolioData, {e}, {e.args}, {type(e).__name__}")
|
415
415
|
if self.settings.VERBOSE_LOGGING:
|
416
|
-
logger.info(f"GetportfolioData: elapsed time {time.time()-start}")
|
416
|
+
self.logger.info(f"GetportfolioData: elapsed time {time.time()-start}")
|
417
417
|
|
418
418
|
async def GetPendingPositionData(self):
|
419
419
|
start=time.time()
|
@@ -470,9 +470,9 @@ class BitunixSignal:
|
|
470
470
|
|
471
471
|
|
472
472
|
except Exception as e:
|
473
|
-
logger.info(f"Function: GetPendingPositionData, {e}, {e.args}, {type(e).__name__}")
|
473
|
+
self.logger.info(f"Function: GetPendingPositionData, {e}, {e.args}, {type(e).__name__}")
|
474
474
|
if self.settings.VERBOSE_LOGGING:
|
475
|
-
logger.info(f"GetPendingPositionData: elapsed time {time.time()-start}")
|
475
|
+
self.logger.info(f"GetPendingPositionData: elapsed time {time.time()-start}")
|
476
476
|
|
477
477
|
async def GetPendingOrderData(self):
|
478
478
|
start=time.time()
|
@@ -489,9 +489,9 @@ class BitunixSignal:
|
|
489
489
|
self.orderdfStyle= self.orderdfrenderer.render_html(self.orderdf)
|
490
490
|
|
491
491
|
except Exception as e:
|
492
|
-
logger.info(f"Function: GetPendingOrderData, {e}, {e.args}, {type(e).__name__}")
|
492
|
+
self.logger.info(f"Function: GetPendingOrderData, {e}, {e.args}, {type(e).__name__}")
|
493
493
|
if self.settings.VERBOSE_LOGGING:
|
494
|
-
logger.info(f"GetPendingOrderData: elapsed time {time.time()-start}")
|
494
|
+
self.logger.info(f"GetPendingOrderData: elapsed time {time.time()-start}")
|
495
495
|
|
496
496
|
async def GetPositionHistoryData(self):
|
497
497
|
start=time.time()
|
@@ -509,9 +509,9 @@ class BitunixSignal:
|
|
509
509
|
self.positionHistorydfStyle= self.positionHistorydfrenderer.render_html(self.positionHistorydf)
|
510
510
|
|
511
511
|
except Exception as e:
|
512
|
-
logger.info(f"Function: GetPositionHistoryData, {e}, {e.args}, {type(e).__name__}")
|
512
|
+
self.logger.info(f"Function: GetPositionHistoryData, {e}, {e.args}, {type(e).__name__}")
|
513
513
|
if self.settings.VERBOSE_LOGGING:
|
514
|
-
logger.info(f"GetPositionHistoryData: elapsed time {time.time()-start}")
|
514
|
+
self.logger.info(f"GetPositionHistoryData: elapsed time {time.time()-start}")
|
515
515
|
|
516
516
|
async def GetTradeHistoryData(self):
|
517
517
|
start=time.time()
|
@@ -526,9 +526,9 @@ class BitunixSignal:
|
|
526
526
|
# Filter trades for the current symbol and convert them to a list of dicts
|
527
527
|
tickerObj.trades = grouped_trades.get_group(symbol).to_dict("records")
|
528
528
|
except Exception as e:
|
529
|
-
logger.info(f"Function: GetTradeHistoryData, {e}, {e.args}, {type(e).__name__}")
|
529
|
+
self.logger.info(f"Function: GetTradeHistoryData, {e}, {e.args}, {type(e).__name__}")
|
530
530
|
if self.settings.VERBOSE_LOGGING:
|
531
|
-
logger.info(f"GetTradeHistoryData: elapsed time {time.time()-start}")
|
531
|
+
self.logger.info(f"GetTradeHistoryData: elapsed time {time.time()-start}")
|
532
532
|
|
533
533
|
|
534
534
|
###########################################################################################################
|
@@ -545,6 +545,7 @@ class BitunixSignal:
|
|
545
545
|
inuseTickers = set(inuse1 + inuse2)
|
546
546
|
|
547
547
|
# Extract buy/sell ticker data
|
548
|
+
self.signaldf_filtered = pd.DataFrame();
|
548
549
|
self.tickerObjects.getCurrentData(period)
|
549
550
|
|
550
551
|
self.signaldf_full = self.tickerObjects.signaldf_full
|
@@ -616,14 +617,14 @@ class BitunixSignal:
|
|
616
617
|
)
|
617
618
|
|
618
619
|
except Exception as e:
|
619
|
-
logger.info(f"Function: BuySellList, {e}, {e.args}, {type(e).__name__}")
|
620
|
-
logger.info(traceback.print_exc())
|
620
|
+
self.logger.info(f"Function: BuySellList, {e}, {e.args}, {type(e).__name__}")
|
621
|
+
self.logger.info(traceback.print_exc())
|
621
622
|
del inuse1, inuse2, inuseTickers
|
622
623
|
gc.collect()
|
623
624
|
|
624
625
|
async def AutoTradeProcess(self):
|
625
626
|
if self.settings.VERBOSE_LOGGING:
|
626
|
-
logger.info(f"AutoTradeProcess started")
|
627
|
+
self.logger.info(f"AutoTradeProcess started")
|
627
628
|
start=time.time()
|
628
629
|
|
629
630
|
period = self.settings.OPTION_MOVING_AVERAGE
|
@@ -885,6 +886,41 @@ class BitunixSignal:
|
|
885
886
|
)
|
886
887
|
continue
|
887
888
|
|
889
|
+
# BOS
|
890
|
+
if self.settings.BOS_STUDY and self.settings.BOS_CHECK_ON_CLOSE:
|
891
|
+
if row.side == 'BUY' and self.signaldf_full.at[row.symbol, f'{period}_bos'] == "SELL" and total_pnl>0:
|
892
|
+
last, bid, ask, mtv = await self.GetTickerBidLastAsk(row.symbol)
|
893
|
+
price = (ask if row['side'] == "BUY" else bid if row['side'] == "SELL" else last) if bid<=last<=ask else last
|
894
|
+
|
895
|
+
self.notifications.add_notification(
|
896
|
+
f'{colors.CYAN} Closing {"long" if side=="BUY" else "short"} position due to {period} bos for {row.symbol} with {row.qty} qty @ {price})'
|
897
|
+
)
|
898
|
+
datajs = await self.bitunixApi.PlaceOrder(
|
899
|
+
positionId=row.positionId,
|
900
|
+
ticker=row.symbol,
|
901
|
+
qty=row.qty,
|
902
|
+
price=price,
|
903
|
+
side=row.side,
|
904
|
+
tradeSide="CLOSE"
|
905
|
+
)
|
906
|
+
continue
|
907
|
+
|
908
|
+
if row.side == 'SELL' and self.signaldf_full.at[row.symbol, f'{period}_bos'] == "BUY" and total_pnl>0:
|
909
|
+
last, bid, ask, mtv = await self.GetTickerBidLastAsk(row.symbol)
|
910
|
+
price = (ask if row['side'] == "BUY" else bid if row['side'] == "SELL" else last) if bid<=last<=ask else last
|
911
|
+
self.notifications.add_notification(
|
912
|
+
f'{colors.CYAN} Closing {"long" if side=="BUY" else "short"} position due to {period} bos for {row.symbol} with {row.qty} qty @ {price})'
|
913
|
+
)
|
914
|
+
datajs = await self.bitunixApi.PlaceOrder(
|
915
|
+
positionId=row.positionId,
|
916
|
+
ticker=row.symbol,
|
917
|
+
qty=row.qty,
|
918
|
+
price=price,
|
919
|
+
side=row.side,
|
920
|
+
tradeSide="CLOSE"
|
921
|
+
)
|
922
|
+
continue
|
923
|
+
|
888
924
|
# TrendLine
|
889
925
|
if self.settings.TRENDLINE_STUDY and self.settings.TRENDLINE_CHECK_ON_CLOSE:
|
890
926
|
if row.side == 'BUY' and self.signaldf_full.at[row.symbol, f'{period}_trendline'] == "SELL" and total_pnl>0:
|
@@ -996,11 +1032,11 @@ class BitunixSignal:
|
|
996
1032
|
except Exception as e:
|
997
1033
|
stack = traceback.extract_stack()
|
998
1034
|
function_name = stack[-1].name
|
999
|
-
logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
|
1000
|
-
logger.info(traceback.print_exc())
|
1035
|
+
self.logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
|
1036
|
+
self.logger.info(traceback.print_exc())
|
1001
1037
|
|
1002
1038
|
if self.settings.VERBOSE_LOGGING:
|
1003
|
-
logger.info(f"AutoTradeProcess: elapsed time {time.time()-start}")
|
1039
|
+
self.logger.info(f"AutoTradeProcess: elapsed time {time.time()-start}")
|
1004
1040
|
if self.settings.BENCHMARK:
|
1005
1041
|
self.connection = sqlite3.connect("bitunix.db")
|
1006
1042
|
self.cursor = self.connection.cursor()
|
@@ -1066,7 +1102,7 @@ class BitunixSignal:
|
|
1066
1102
|
|
1067
1103
|
self.available = data['available']
|
1068
1104
|
self.margin = data['margin']
|
1069
|
-
# logger.info(feed)
|
1105
|
+
# self.logger.info(feed)
|
1070
1106
|
|
1071
1107
|
elif channel == 'position':
|
1072
1108
|
|
@@ -1104,7 +1140,7 @@ class BitunixSignal:
|
|
1104
1140
|
del feed
|
1105
1141
|
gc.collect()
|
1106
1142
|
except Exception as e:
|
1107
|
-
logger.info(f"Function: ProcessPrivateData, {e}, {e.args}, {type(e).__name__}")
|
1143
|
+
self.logger.info(f"Function: ProcessPrivateData, {e}, {e.args}, {type(e).__name__}")
|
1108
1144
|
|
1109
1145
|
def color_cells(val, color):
|
1110
1146
|
return f'background-color: {color}' if val else ''
|
@@ -8,11 +8,10 @@ import string
|
|
8
8
|
from typing import Callable
|
9
9
|
import threading
|
10
10
|
from logger import Logger
|
11
|
-
logger = Logger(__name__).get_logger()
|
12
11
|
import gc
|
13
12
|
|
14
13
|
class BitunixPublicWebSocketClient:
|
15
|
-
def __init__(self, api_key, secret_key, type):
|
14
|
+
def __init__(self, api_key, secret_key, type, logger):
|
16
15
|
self.api_key = api_key
|
17
16
|
self.secret_key = secret_key
|
18
17
|
self.type = type
|
@@ -22,6 +21,7 @@ class BitunixPublicWebSocketClient:
|
|
22
21
|
self.loop = asyncio.new_event_loop()
|
23
22
|
self.loop_thread = threading.Thread(target=self.start_loop)
|
24
23
|
self.loop_thread.daemon = True # Ensure the thread closes with the program
|
24
|
+
self.logger=logger
|
25
25
|
self.loop_thread.start()
|
26
26
|
|
27
27
|
|
@@ -35,7 +35,7 @@ class BitunixPublicWebSocketClient:
|
|
35
35
|
|
36
36
|
# Wait for the thread to finish
|
37
37
|
self.loop_thread.join()
|
38
|
-
logger.info("Event loop stopped cleanly")
|
38
|
+
self.logger.info("Event loop stopped cleanly")
|
39
39
|
|
40
40
|
|
41
41
|
async def run_websocket(self, process_func: Callable):
|
@@ -44,7 +44,7 @@ class BitunixPublicWebSocketClient:
|
|
44
44
|
try:
|
45
45
|
async with websockets.connect(self.url, ping_interval=None, open_timeout=30) as self.websocket:
|
46
46
|
connect_response = await self.websocket.recv()
|
47
|
-
logger.info(f"{self.url} {self.type} websocket connect Response: {connect_response}")
|
47
|
+
self.logger.info(f"{self.url} {self.type} websocket connect Response: {connect_response}")
|
48
48
|
|
49
49
|
self.heartbeat_task = asyncio.create_task(self.send_heartbeat(self.websocket))
|
50
50
|
|
@@ -81,12 +81,12 @@ class BitunixPublicWebSocketClient:
|
|
81
81
|
await process_func(message)
|
82
82
|
logger.warning(f"{self.url} {self.type} WebSocket connection closed")
|
83
83
|
except asyncio.CancelledError:
|
84
|
-
logger.info(f"{self.url} {self.type} WebSocket receive task cancelled")
|
84
|
+
self.logger.info(f"{self.url} {self.type} WebSocket receive task cancelled")
|
85
85
|
self.running = False
|
86
86
|
except websockets.exceptions.ConnectionClosedError as e:
|
87
|
-
logger.info(f"{self.url} {self.type} Websocket: Connection closed: {e}")
|
87
|
+
self.logger.info(f"{self.url} {self.type} Websocket: Connection closed: {e}")
|
88
88
|
except Exception as e:
|
89
|
-
logger.info(f"{self.url} {self.type} Websocket: Unexpected error: {e}")
|
89
|
+
self.logger.info(f"{self.url} {self.type} Websocket: Unexpected error: {e}")
|
90
90
|
pass
|
91
91
|
del message, process_func
|
92
92
|
gc.collect()
|
@@ -97,11 +97,11 @@ class BitunixPublicWebSocketClient:
|
|
97
97
|
await websocket.send(json.dumps({"op": "ping", "ping": int(time.time())}))
|
98
98
|
await asyncio.sleep(30)
|
99
99
|
except asyncio.CancelledError:
|
100
|
-
logger.info(f"{self.url} {self.type} WebSocket hearbeat task cancelled")
|
100
|
+
self.logger.info(f"{self.url} {self.type} WebSocket hearbeat task cancelled")
|
101
101
|
except websockets.exceptions.ConnectionClosed:
|
102
|
-
logger.info(f"{self.url} {self.type} WebSocket connection for heartbeat is closed")
|
102
|
+
self.logger.info(f"{self.url} {self.type} WebSocket connection for heartbeat is closed")
|
103
103
|
except Exception as e:
|
104
|
-
logger.info(f"{self.url} {self.type} Websocket for heartbeat: Unexpected error: {e}")
|
104
|
+
self.logger.info(f"{self.url} {self.type} Websocket for heartbeat: Unexpected error: {e}")
|
105
105
|
pass
|
106
106
|
|
107
107
|
async def stop_websocket(self):
|
@@ -124,7 +124,7 @@ class BitunixPublicWebSocketClient:
|
|
124
124
|
finally:
|
125
125
|
# Stop the event loop
|
126
126
|
self.loop.call_soon_threadsafe(self.loop.stop)
|
127
|
-
logger.info(f"{self.url} {self.type} WebSocket thread stopped and resources cleaned up.")
|
127
|
+
self.logger.info(f"{self.url} {self.type} WebSocket thread stopped and resources cleaned up.")
|
128
128
|
|
129
129
|
class BitunixPrivateWebSocketClient:
|
130
130
|
def __init__(self, api_key, secret_key):
|
@@ -149,7 +149,7 @@ class BitunixPrivateWebSocketClient:
|
|
149
149
|
|
150
150
|
# Wait for the thread to finish
|
151
151
|
self.loop_thread.join()
|
152
|
-
logger.info("Event loop stopped cleanly")
|
152
|
+
self.logger.info("Event loop stopped cleanly")
|
153
153
|
|
154
154
|
async def run_websocket(self, process_func: Callable):
|
155
155
|
self.running = True
|
@@ -157,7 +157,7 @@ class BitunixPrivateWebSocketClient:
|
|
157
157
|
try:
|
158
158
|
async with websockets.connect(self.url, ping_interval=None, open_timeout=30) as self.websocket:
|
159
159
|
connect_response = await self.websocket.recv()
|
160
|
-
logger.info(f"{self.url} websocket connect Response: {connect_response}")
|
160
|
+
self.logger.info(f"{self.url} websocket connect Response: {connect_response}")
|
161
161
|
|
162
162
|
self.heartbeat_task = asyncio.create_task(self.send_heartbeat(self.websocket))
|
163
163
|
|
@@ -176,7 +176,7 @@ class BitunixPrivateWebSocketClient:
|
|
176
176
|
}
|
177
177
|
await self.websocket.send(json.dumps(login_request))
|
178
178
|
login_response = await self.websocket.recv()
|
179
|
-
logger.info(f"{self.url} Login Response: {login_response}")
|
179
|
+
self.logger.info(f"{self.url} Login Response: {login_response}")
|
180
180
|
|
181
181
|
self.recv_task = asyncio.create_task(self.receive_messages(process_func))
|
182
182
|
await self.recv_task
|
@@ -195,12 +195,12 @@ class BitunixPrivateWebSocketClient:
|
|
195
195
|
await process_func(message)
|
196
196
|
logger.warning(f"{self.type} WebSocket connection closed")
|
197
197
|
except asyncio.CancelledError:
|
198
|
-
logger.info(f"{self.url} WebSocket receive task cancelled")
|
198
|
+
self.logger.info(f"{self.url} WebSocket receive task cancelled")
|
199
199
|
self.running = False
|
200
200
|
except websockets.exceptions.ConnectionClosedError as e:
|
201
|
-
logger.info(f"{self.url} Websocket: Connection closed: {e}")
|
201
|
+
self.logger.info(f"{self.url} Websocket: Connection closed: {e}")
|
202
202
|
except Exception as e:
|
203
|
-
logger.info(f"{self.url} Websocket: Unexpected error: {e}")
|
203
|
+
self.logger.info(f"{self.url} Websocket: Unexpected error: {e}")
|
204
204
|
pass
|
205
205
|
del message, process_func
|
206
206
|
gc.collect()
|
@@ -211,11 +211,11 @@ class BitunixPrivateWebSocketClient:
|
|
211
211
|
await websocket.send(json.dumps({"op": "ping", "ping": int(time.time())}))
|
212
212
|
await asyncio.sleep(30)
|
213
213
|
except asyncio.CancelledError:
|
214
|
-
logger.info(f"{self.url} WebSocket hearbeat task cancelled")
|
214
|
+
self.logger.info(f"{self.url} WebSocket hearbeat task cancelled")
|
215
215
|
except websockets.exceptions.ConnectionClosed:
|
216
|
-
logger.info(f"{self.url} WebSocket connection for heartbeat is closed")
|
216
|
+
self.logger.info(f"{self.url} WebSocket connection for heartbeat is closed")
|
217
217
|
except Exception as e:
|
218
|
-
logger.info(f"{self.url} Websocket for heartbeat: Unexpected error: {e}")
|
218
|
+
self.logger.info(f"{self.url} Websocket for heartbeat: Unexpected error: {e}")
|
219
219
|
pass
|
220
220
|
|
221
221
|
async def stop_websocket(self):
|
@@ -238,7 +238,7 @@ class BitunixPrivateWebSocketClient:
|
|
238
238
|
finally:
|
239
239
|
# Stop the event loop
|
240
240
|
self.loop.call_soon_threadsafe(self.loop.stop)
|
241
|
-
logger.info(f"{self.url} WebSocket thread stopped and resources cleaned up.")
|
241
|
+
self.logger.info(f"{self.url} WebSocket thread stopped and resources cleaned up.")
|
242
242
|
|
243
243
|
|
244
244
|
async def generate_signature(self, api_key, secret_key, nonce):
|
@@ -1,19 +1,19 @@
|
|
1
1
|
from datetime import datetime
|
2
2
|
import pytz
|
3
3
|
from logger import Logger
|
4
|
-
logger = Logger(__name__).get_logger()
|
5
4
|
|
6
5
|
class NotificationManager:
|
7
|
-
def __init__(self, max_size=100):
|
6
|
+
def __init__(self, logger, max_size=100):
|
8
7
|
self.max_size = max_size
|
9
8
|
self.notifications = []
|
9
|
+
self.logger = logger
|
10
10
|
|
11
11
|
def add_notification(self, notification):
|
12
12
|
# Add the new notification at the top
|
13
13
|
timezone = pytz.timezone('America/Chicago')
|
14
14
|
timestamp = datetime.now(timezone).strftime('%Y-%m-%d %H:%M:%S')
|
15
15
|
self.notifications.insert(0, f'({timestamp}) {notification}')
|
16
|
-
logger.info(f'({timestamp}) {notification}')
|
16
|
+
self.logger.info(f'({timestamp}) {notification}')
|
17
17
|
# Ensure the list doesn't exceed the maximum size
|
18
18
|
if len(self.notifications) > self.max_size:
|
19
19
|
self.notifications.pop()
|