bitunix-automated-crypto-trading 3.0.4__tar.gz → 3.0.7__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.
Files changed (22) hide show
  1. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/PKG-INFO +1 -1
  2. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/BitunixSignal.py +153 -156
  3. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/bitunix.py +79 -0
  4. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/config.py +25 -1
  5. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/logger.py +1 -1
  6. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading.egg-info/PKG-INFO +1 -1
  7. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/setup.py +1 -1
  8. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/README.md +0 -0
  9. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/AsyncThreadRunner.py +0 -0
  10. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/BitunixApi.py +0 -0
  11. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/BitunixWebSocket.py +0 -0
  12. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/DataFrameHtmlRenderer.py +0 -0
  13. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/NotificationManager.py +0 -0
  14. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/ThreadManager.py +0 -0
  15. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/TickerManager.py +0 -0
  16. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/__init__.py +0 -0
  17. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading.egg-info/SOURCES.txt +0 -0
  18. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading.egg-info/dependency_links.txt +0 -0
  19. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading.egg-info/entry_points.txt +0 -0
  20. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading.egg-info/requires.txt +0 -0
  21. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading.egg-info/top_level.txt +0 -0
  22. {bitunix_automated_crypto_trading-3.0.4 → bitunix_automated_crypto_trading-3.0.7}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bitunix_automated_crypto_trading
3
- Version: 3.0.4
3
+ Version: 3.0.7
4
4
  Summary: Bitunix Futures Auto Trading Platform
5
5
  Home-page: https://github.com/tcj2001/bitunix-automated-crypto-trading
6
6
  Author: tcj2001
@@ -16,7 +16,13 @@ from logger import Logger, Colors
16
16
  logger = Logger(__name__).get_logger()
17
17
  colors = Colors()
18
18
  import gc
19
+ import os
19
20
  from concurrent.futures import ProcessPoolExecutor
21
+ import sqlite3
22
+ import queue
23
+ from collections import deque
24
+ import threading
25
+
20
26
 
21
27
  cst = pytz.timezone('US/Central')
22
28
 
@@ -70,6 +76,7 @@ class BitunixSignal:
70
76
 
71
77
  #websockets
72
78
  self.bitunixPrivateWebSocketClient = BitunixPrivateWebSocketClient(self.api_key, self.secret_key)
79
+
73
80
  if self.settings.USE_PUBLIC_WEBSOCKET:
74
81
  self.bitunixPublicDepthWebSocketClient = BitunixPublicWebSocketClient(self.api_key, self.secret_key, "depth")
75
82
  self.bitunixPublicTickerWebSocketClient = BitunixPublicWebSocketClient(self.api_key, self.secret_key, "ticker")
@@ -84,6 +91,9 @@ class BitunixSignal:
84
91
  self.lastAutoTradeTime = time.time()
85
92
  self.lastTickerDataTime = time.time()
86
93
 
94
+ #sqllite database connection
95
+ self.connection = sqlite3.connect("bitunix.db")
96
+
87
97
  async def update_settings(self, settings):
88
98
  self.settings = settings
89
99
  self.tickerObjects.update_settings(settings)
@@ -129,94 +139,39 @@ class BitunixSignal:
129
139
  self.GetPositionHistoryDataTask = AsyncThreadRunner(self.GetPositionHistoryData, interval=int(self.settings.POSITION_HISTORY_API_INTERVAL))
130
140
  self.GetPositionHistoryDataTask.start_thread(thread_name="GetPositionHistoryData")
131
141
 
132
- #run restartable asynch thread
133
- await self.restartable_jobs()
134
-
135
- async def restart_jobs(self):
136
-
137
- #stop websocket async thread jobs
138
- await self.bitunixPrivateWebSocketClient.stop_websocket()
139
- await self.ProcessPrivateDataTask.stop_thread()
140
-
141
- if self.settings.USE_PUBLIC_WEBSOCKET:
142
- await self.bitunixPublicDepthWebSocketClient.stop_websocket()
143
- await self.UpdateDepthDataTask.stop_thread()
144
-
145
- await self.bitunixPublicTickerWebSocketClient.stop_websocket()
146
- await self.UpdateTickerDataTask.stop_thread()
147
-
148
- #kill the loop to restart public websocket
149
- #not using for now
150
- #await self.restartPublicWebsocketTask.stop_thread()
151
-
152
- #stop onetime / periodic async thread jobs
153
- await self.LoadKlineHistoryTask.stop_thread()
154
- await self.GetTickerDataTask.stop_thread()
155
- await self.AutoTradeProcessTask.stop_thread()
156
-
157
- #start jobs
158
- await self.load_tickers()
159
- await self.restartable_jobs()
160
-
161
- async def restartable_jobs(self):
162
- #start cancelable async jobs
163
- #websocket jobs
164
142
  self.ProcessPrivateDataTask = AsyncThreadRunner(self.bitunixPrivateWebSocketClient.run_websocket, 0, self.ProcessPrivateData)
165
143
  self.ProcessPrivateDataTask.start_thread(thread_name="ProcessPrivateData")
166
144
 
167
145
  if self.settings.USE_PUBLIC_WEBSOCKET:
168
-
169
146
  self.bitunixPublicDepthWebSocketClient.tickerList = self.tickerList
170
- self.UpdateDepthDataTask = AsyncThreadRunner(self.bitunixPublicDepthWebSocketClient.run_websocket, 0, self.UpdateDepthData)
171
- self.UpdateDepthDataTask.start_thread(thread_name="UpdateDepthData")
147
+ self.StoreDepthDataTask = AsyncThreadRunner(self.bitunixPublicDepthWebSocketClient.run_websocket, 0, self.StoreDepthData)
148
+ self.StoreDepthDataTask.start_thread(thread_name="StoreDepthData")
149
+ self.depth_que = asyncio.Queue()
150
+ self.ProcessDepthDataTask = AsyncThreadRunner(self.ProcessDepthData, interval=0) # run only once
151
+ self.ProcessDepthDataTask.start_thread(thread_name="ProcessDepthData")
172
152
 
173
153
  self.bitunixPublicTickerWebSocketClient.tickerList = self.tickerList
174
- self.UpdateTickerDataTask = AsyncThreadRunner(self.bitunixPublicTickerWebSocketClient.run_websocket, 0, self.UpdateTickerData)
175
- self.UpdateTickerDataTask.start_thread(thread_name="UpdateTickerData")
154
+ self.StoreTickerDataTask = AsyncThreadRunner(self.bitunixPublicTickerWebSocketClient.run_websocket, 0, self.StoreTickerData)
155
+ self.StoreTickerDataTask.start_thread(thread_name="StoreTickerData")
156
+ self.ticker_que = asyncio.Queue()
157
+ self.ProcessTickerDataTask = AsyncThreadRunner(self.ProcessTickerData, interval=0) # run only once
158
+ self.ProcessTickerDataTask.start_thread(thread_name="ProcessTickerData")
159
+
176
160
 
177
161
  #normal processes
178
162
  self.LoadKlineHistoryTask = AsyncThreadRunner(self.LoadKlineHistory, interval=0) # run only once
179
163
  self.LoadKlineHistoryTask.start_thread(thread_name="LoadKlineHistory")
180
164
 
181
- self.GetTickerDataTask = AsyncThreadRunner(self.GetTickerData, interval=int(self.settings.TICKER_DATA_API_INTERVAL))
182
- self.GetTickerDataTask.start_thread(thread_name="GetTickerData")
183
-
184
165
  self.AutoTradeProcessTask = AsyncThreadRunner(self.AutoTradeProcess, interval=int(self.settings.SIGNAL_CHECK_INTERVAL))
185
166
  self.AutoTradeProcessTask.start_thread(thread_name="AutoTradeProcess")
186
167
 
187
- #start the loop to restart public websocket
188
- #if self.settings.USE_PUBLIC_WEBSOCKET:
189
- # self.restartPublicWebsocketTask = AsyncThreadRunner(self.restartPublicWebsocket, interval=0)
190
- # self.restartPublicWebsocketTask.start_thread(thread_name="restartPublicWebsocket")
191
-
192
- #this is a normal task runing in a async thread, that can be cancelled
193
- # this runs in a async thread to stop and start the public websocket, as we found some lagging when it runs continously
194
- #not used now
195
- async def restartPublicWebsocket(self):
196
- while True:
197
- await asyncio.sleep(int(self.settings.PUBLIC_WEBSOCKET_RESTART_INTERVAL))
198
-
199
- if self.settings.VERBOSE_LOGGING:
200
- self.notifications.add_notification('Restarting public websocket')
201
- logger.info(f"Restarting public websocket")
202
-
203
- if self.settings.USE_PUBLIC_WEBSOCKET:
204
- await self.UpdateDepthDataTask.stop_thread()
205
- await self.UpdateTickerDataTask.stop_thread()
206
-
207
- await asyncio.sleep(30)
208
-
209
- if self.settings.USE_PUBLIC_WEBSOCKET:
210
- self.bitunixPublicDepthWebSocketClient.tickerList = self.tickerList
211
- self.UpdateDepthDataTask = AsyncThreadRunner(self.bitunixPublicDepthWebSocketClient.run_websocket, 0, self.UpdateDepthData)
212
- self.UpdateDepthDataTask.start_thread(thread_name="UpdateDepthData")
168
+ if not self.settings.USE_PUBLIC_WEBSOCKET:
169
+ self.GetTickerDataTask = AsyncThreadRunner(self.GetTickerData, interval=int(self.settings.TICKER_DATA_API_INTERVAL))
170
+ self.GetTickerDataTask.start_thread(thread_name="GetTickerData")
213
171
 
214
- self.bitunixPublicTickerWebSocketClient.tickerList = self.tickerList
215
- self.UpdateTickerDataTask = AsyncThreadRunner(self.bitunixPublicTickerWebSocketClient.run_websocket, 0, self.UpdateTickerData)
216
- self.UpdateTickerDataTask.start_thread(thread_name="UpdateTickerData")
172
+ self.checkPeriodicProcessTask = AsyncThreadRunner(self.checkPeriodicProcess, interval=0)
173
+ self.checkPeriodicProcessTask.start_thread(thread_name="checkPeriodicProcess")
217
174
 
218
- if self.settings.VERBOSE_LOGGING:
219
- self.notifications.add_notification('Restared public websocket')
220
175
 
221
176
  ###########################################################################################################
222
177
  async def DefinehtmlRenderers(self):
@@ -271,86 +226,115 @@ class BitunixSignal:
271
226
 
272
227
  #api data
273
228
  async def GetTickerData(self):
274
- start=time.time()
275
-
276
- # Get the current time and set the seconds and microseconds to zero
277
- current_time = datetime.now()
278
- current_minute = current_time.replace(second=0, microsecond=0)
279
- ts = int(current_minute.timestamp())*1000
229
+ if not self.settings.USE_PUBLIC_WEBSOCKET:
230
+ start=time.time()
231
+ # Get the current time and set the seconds and microseconds to zero
232
+ current_time = datetime.now()
233
+ current_minute = current_time.replace(second=0, microsecond=0)
234
+ ts = int(current_minute.timestamp())*1000
280
235
 
281
- #api used insted of websocket
282
- data = await self.bitunixApi.GetTickerData()
283
- self.tickerdf = pd.DataFrame()
284
- if data:
285
-
286
- # Create a DataFrame from the data
287
- self.tickerdf = pd.DataFrame(data, columns=["symbol", "last"])
288
-
289
- #remove not required symbols
290
- self.tickerdf.loc[~self.tickerdf['symbol'].isin(self.tickerObjects.symbols()), :] = None
291
- self.tickerdf.dropna(inplace=True)
292
-
293
- self.tickerdf['ts']=ts
294
- self.tickerdf["tickerObj"] = self.tickerdf["symbol"].map(self.tickerObjects.get_tickerDict())
295
- self.tuples_list = list(zip(self.tickerdf["tickerObj"], self.tickerdf["last"].astype(float), self.tickerdf["ts"]))
296
- self.tickerObjects.form_candle(self.tuples_list)
236
+ #api used insted of websocket
237
+ data = await self.bitunixApi.GetTickerData()
238
+ self.tickerdf = pd.DataFrame()
239
+ if data:
240
+
241
+ # Create a DataFrame from the data
242
+ self.tickerdf = pd.DataFrame(data, columns=["symbol", "last"])
243
+
244
+ #remove not required symbols
245
+ self.tickerdf.loc[~self.tickerdf['symbol'].isin(self.tickerObjects.symbols()), :] = None
246
+ self.tickerdf.dropna(inplace=True)
247
+
248
+ self.tickerdf['ts']=ts
249
+ self.tickerdf["tickerObj"] = self.tickerdf["symbol"].map(self.tickerObjects.get_tickerDict())
250
+ self.tuples_list = list(zip(self.tickerdf["tickerObj"], self.tickerdf["last"].astype(float), self.tickerdf["ts"]))
251
+ self.tickerObjects.form_candle(self.tuples_list)
297
252
 
298
- self.lastTickerDataTime = time.time()
299
- if self.settings.VERBOSE_LOGGING:
300
- logger.info(f"GetTickerData: elapsed time {time.time()-start}")
253
+ self.lastTickerDataTime = time.time()
254
+ if self.settings.VERBOSE_LOGGING:
255
+ logger.info(f"GetTickerData: elapsed time {time.time()-start}")
256
+ if self.settings.BENCHMARK:
257
+ self.connection = sqlite3.connect("bitunix.db")
258
+ self.cursor = self.connection.cursor()
259
+ self.cursor.execute("INSERT INTO benchmark (process_name, time) VALUES (?, ?)", ("GetTickerData", time.time()-start))
260
+ self.connection.commit()
301
261
 
302
262
 
303
- #websocket data
304
- async def UpdateTickerData(self, message):
305
- if message=="":
306
- return
307
- try:
263
+ #websocket data to update ticker lastprice and other relavent data
264
+ # Function to add data to the last price deque
265
+ async def StoreTickerData(self, message):
266
+ if self.settings.USE_PUBLIC_WEBSOCKET:
267
+ if message=="":
268
+ return
308
269
  data = json.loads(message)
309
270
  if 'symbol' in data and data['ch'] in ['ticker']:
310
- symbol = data['symbol']
311
- ts = data['ts']
312
- last= float(data['data']['la'])
313
- highest= float(data['data']['h'])
314
- lowest= float(data['data']['l'])
315
- volume= float(data['data']['b'])
316
- volumeInCurrency= float(data['data']['q'])
317
- tickerObj = self.tickerObjects.get(symbol)
318
- if tickerObj:
319
- tickerObj.set_24hrData(highest,lowest,volume,volumeInCurrency)
320
- tickerObj.form_candle(last, ts)
321
- del tickerObj
322
- gc.collect()
323
- del data
324
- gc.collect()
325
- except Exception as e:
326
- logger.info(f"Function: UpdateTickerData, {e}, {e.args}, {type(e).__name__}")
327
- if self.settings.VERBOSE_LOGGING:
328
- logger.info(f"Function: UpdateTickerData, time:{ts}, symbol:{symbol}, highest:{highest}, lowest:{lowest}, volume:{volume}, volumeInCurrency:{volumeInCurrency}")
271
+ await self.ticker_que.put(data)
329
272
 
330
- #websocket data
331
- async def UpdateDepthData(self, message):
332
- if message=="":
333
- return
334
- try:
273
+ # Function to process the last price deque
274
+ async def ProcessTickerData(self):
275
+ while True:
276
+ try:
277
+ batch_size = self.settings.BATCH_PROCESS_SIZE
278
+ latest_data = {}
279
+ reversed_items = list(self.ticker_que._queue)[::-1]
280
+ self.ticker_que._queue.clear()
281
+ for i in range(min(batch_size, len(reversed_items))):
282
+ data = reversed_items[i]
283
+ symbol = data["symbol"]
284
+ ts = data["ts"]
285
+ if symbol not in latest_data or ts > latest_data[symbol]['ts']:
286
+ latest_data[symbol] = {'ts': ts, 'last': float(data['data']['la'])}
287
+ # Convert to DataFrame
288
+ self.tickerdf = pd.DataFrame.from_dict(latest_data, orient="index")
289
+ if not self.tickerdf.empty:
290
+ self.tickerdf["tickerObj"] = self.tickerdf.index.map(self.tickerObjects.get_tickerDict())
291
+ self.tuples_list = list(zip(self.tickerdf["tickerObj"], self.tickerdf["last"].astype(float), self.tickerdf["ts"]))
292
+ self.tickerObjects.form_candle(self.tuples_list)
293
+ self.lastTickerDataTime = time.time()
294
+ except Exception as e:
295
+ logger.info(f"Function: ProcessTickerData, {e}, {e.args}, {type(e).__name__}")
296
+ logger.info(traceback.print_exc())
297
+ await asyncio.sleep(0.5)
298
+
299
+ #websocket data to update bid and ask
300
+ async def StoreDepthData(self, message):
301
+ if self.settings.USE_PUBLIC_WEBSOCKET:
302
+ if message=="":
303
+ return
335
304
  data = json.loads(message)
336
305
  if 'symbol' in data and data['ch'] in ['depth_book1']:
337
- symbol = data['symbol']
338
- ts = data['ts']
339
- bid = float(data['data']['b'][0][0])
340
- ask = float(data['data']['a'][0][0])
341
- tickerObj = self.tickerObjects.get(symbol)
342
- if tickerObj:
343
- tickerObj.set_bid(bid)
344
- tickerObj.set_ask(ask)
345
- del tickerObj
346
- gc.collect()
347
- del data
348
- gc.collect()
349
- except Exception as e:
350
- logger.info(f"Function: UpdateDepthData, {e}, {e.args}, {type(e).__name__}")
351
- if self.settings.VERBOSE_LOGGING:
352
- logger.info(f"Function: UpdateDepthData, time:{ts}, symbol:{symbol}, bid:{bid}, ask:{ask}")
306
+ await self.depth_que.put(data)
353
307
 
308
+ # Function to process the bid, ask
309
+ async def ProcessDepthData(self):
310
+ while True:
311
+ try:
312
+ batch_size = self.settings.BATCH_PROCESS_SIZE
313
+ latest_data = {}
314
+ reversed_items = list(self.depth_que._queue)[::-1]
315
+ self.depth_que._queue.clear()
316
+ for i in range(min(batch_size, len(reversed_items))):
317
+ data = reversed_items[i]
318
+ symbol = data["symbol"]
319
+ ts = data["ts"]
320
+ if symbol not in latest_data or ts > latest_data[symbol]['ts']:
321
+ latest_data[symbol] = {'ts': ts, 'bid': float(data['data']['b'][0][0]), 'ask': float(data['data']['a'][0][0])}
322
+ # Convert to DataFrame
323
+ self.depthdf = pd.DataFrame.from_dict(latest_data, orient="index")
324
+ if not self.depthdf.empty:
325
+ self.depthdf["tickerObj"] = self.depthdf.index.map(self.tickerObjects.get_tickerDict())
326
+ self.depthdf.apply(self.apply_depth_data2, axis=1)
327
+ except Exception as e:
328
+ logger.info(f"Function: ProcessTickerData, {e}, {e.args}, {type(e).__name__}")
329
+ logger.info(traceback.print_exc())
330
+ await asyncio.sleep(0.5)
331
+
332
+ def apply_depth_data2(self, row):
333
+ row["tickerObj"].set_ask(row["ask"])
334
+ row["tickerObj"].set_bid(row["bid"])
335
+ return row
336
+
337
+ # non websocket method
354
338
  # this is called to update last price, as the websocket is lagging
355
339
  # this is only called for the tickers in the pendingpositions
356
340
  # and for first few records in the signaldf
@@ -377,6 +361,7 @@ class BitunixSignal:
377
361
  if self.settings.VERBOSE_LOGGING:
378
362
  logger.info(f"apply_last_data: elapsed time {time.time()-start}")
379
363
 
364
+ # non websocket method
380
365
  # this is called to update bid and ask,
381
366
  # as it is time consuming to call the api for each ticker,
382
367
  # this is only called for the tickers in the pendingpositions
@@ -397,6 +382,13 @@ class BitunixSignal:
397
382
  gc.collect()
398
383
 
399
384
  ###########################################################################################################
385
+ async def checkPeriodicProcess(self):
386
+ while True:
387
+ if self.lastAutoTradeTime + 300 < time.time() or self.lastTickerDataTime + 300 < time.time():
388
+ self.notifications.add_notification("AutoTradeProcess or GetTickerData is not running")
389
+ os._exit(1)
390
+ break
391
+ await asyncio.sleep(300)
400
392
 
401
393
  async def GetportfolioData(self):
402
394
  start=time.time()
@@ -457,14 +449,14 @@ class BitunixSignal:
457
449
  #if not self.settings.USE_PUBLIC_WEBSOCKET:
458
450
  #get bid las ask using api for the symbols in pending psotion
459
451
  if not self.positiondf.empty:
460
- if self.settings.USE_PUBLIC_WEBSOCKET:
452
+ if not self.settings.USE_PUBLIC_WEBSOCKET:
461
453
  await asyncio.create_task(self.apply_last_data(','.join(self.positiondf['symbol'].astype(str).tolist())))
462
- await asyncio.gather(
463
- *[
464
- asyncio.create_task(self.apply_depth_data(row['symbol']))
465
- for index, row in self.positiondf.iterrows()
466
- ]
467
- )
454
+ await asyncio.gather(
455
+ *[
456
+ asyncio.create_task(self.apply_depth_data(row['symbol']))
457
+ for index, row in self.positiondf.iterrows()
458
+ ]
459
+ )
468
460
 
469
461
 
470
462
  except Exception as e:
@@ -581,7 +573,7 @@ class BitunixSignal:
581
573
  self.signaldf = self.signaldf_filtered[[
582
574
  "symbol", f"{period}_trend",f"{period}_cb", f"{period}_barcolor",
583
575
  f"{period}_ema_open", f"{period}_ema_close", f"{period}_macd", f"{period}_bbm", f"{period}_rsi",f"{period}_adx",f"{period}_candle_trend",
584
- 'lastcolor', 'bidcolor', 'askcolor', 'bid', 'ask', 'last',
576
+ 'lastcolor', 'bidcolor', 'askcolor', 'bid', 'last', 'ask',
585
577
  f"{period}_open", f"{period}_close", f"{period}_high", f"{period}_low",
586
578
  ]].sort_values(by=[f'{period}_cb'], ascending=[False])
587
579
 
@@ -598,14 +590,14 @@ class BitunixSignal:
598
590
  #get bid las ask using api for max_auto_trades rows
599
591
  if not self.signaldf.empty:
600
592
  m = min(self.signaldf.shape[0], int(self.settings.MAX_AUTO_TRADES))
601
- if self.settings.USE_PUBLIC_WEBSOCKET:
593
+ if not self.settings.USE_PUBLIC_WEBSOCKET:
602
594
  await asyncio.create_task(self.apply_last_data(','.join(self.signaldf['symbol'][:m].astype(str).tolist())))
603
- await asyncio.gather(
604
- *[
605
- asyncio.create_task(self.apply_depth_data(row['symbol']))
606
- for index, row in self.signaldf[:m].iterrows()
607
- ]
608
- )
595
+ await asyncio.gather(
596
+ *[
597
+ asyncio.create_task(self.apply_depth_data(row['symbol']))
598
+ for index, row in self.signaldf[:m].iterrows()
599
+ ]
600
+ )
609
601
 
610
602
  except Exception as e:
611
603
  logger.info(f"Function: BuySellList, {e}, {e.args}, {type(e).__name__}")
@@ -958,6 +950,11 @@ class BitunixSignal:
958
950
 
959
951
  if self.settings.VERBOSE_LOGGING:
960
952
  logger.info(f"AutoTradeProcess: elapsed time {time.time()-start}")
953
+ if self.settings.BENCHMARK:
954
+ self.connection = sqlite3.connect("bitunix.db")
955
+ self.cursor = self.connection.cursor()
956
+ self.cursor.execute("INSERT INTO benchmark (process_name, time) VALUES (?, ?)", ("AutoTradeProcess", time.time()-start))
957
+ self.connection.commit()
961
958
 
962
959
  del df
963
960
  gc.collect()
@@ -32,6 +32,8 @@ from fastapi.staticfiles import StaticFiles
32
32
  from dotenv import load_dotenv, dotenv_values, set_key
33
33
  from pydantic import ValidationError
34
34
 
35
+ import sqlite3
36
+
35
37
  ENV_FILE = ".env"
36
38
  CONFIG_FILE = os.path.dirname(os.path.abspath(__file__))+"/config.txt"
37
39
  LOG_FILE = "app.log"
@@ -60,6 +62,83 @@ class bitunix():
60
62
 
61
63
  self.websocket_connections = set()
62
64
  self.DB = {"admin": {"password": password}}
65
+
66
+ #sqllite database connection
67
+ self.connection = sqlite3.connect("bitunix.db")
68
+ #create table if not exist
69
+ self.cursor = self.connection.cursor()
70
+ #create benchmark table
71
+ self.cursor.execute("CREATE TABLE IF NOT EXISTS benchmark (id INTEGER PRIMARY KEY, process_name TEXT, time INTEGER)")
72
+
73
+ #create settings table
74
+ self.cursor.execute("CREATE TABLE IF NOT EXISTS settings (param TEXT PRIMARY KEY, value TEXT)")
75
+ self.cursor.execute(f"SELECT param, value FROM settings")
76
+ rows = self.cursor.fetchall()
77
+ if len(rows) == 0:
78
+ # fille value from confix.txt file to db
79
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("AUTOTRADE","True"))
80
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("OPTION_MOVING_AVERAGE","1h"))
81
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("MAX_AUTO_TRADES","10"))
82
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("PROFIT_AMOUNT","3"))
83
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("LOSS_AMOUNT","0"))
84
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("LEVERAGE","20"))
85
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("THRESHOLD","5"))
86
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("MIN_VOLUME","10000000"))
87
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("ORDER_AMOUNT_PERCENTAGE","5"))
88
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("BARS","100"))
89
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("MA_FAST","10"))
90
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("MA_MEDIUM","20"))
91
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("MA_SLOW","50"))
92
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("RSI_FAST","6"))
93
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("RSI_SLOW","24"))
94
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("BBM_PERIOD","20"))
95
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("BBM_STD","2"))
96
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("MACD_PERIOD","9"))
97
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("MACD_SHORT","12"))
98
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("MACD_LONG","26"))
99
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("ADX_PERIOD","14"))
100
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("OPEN_ON_ANY_SIGNAL","True"))
101
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("EMA_STUDY","True"))
102
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("EMA_CHART","True"))
103
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("EMA_CROSSING","True"))
104
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("EMA_CHECK_ON_OPEN","True"))
105
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("EMA_CHECK_ON_CLOSE","True"))
106
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("EMA_CLOSE_ON_FAST_MEDIUM","True"))
107
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("MACD_STUDY","True"))
108
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("MACD_CHART","True"))
109
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("MACD_CROSSING","True"))
110
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("MACD_CHECK_ON_OPEN","True"))
111
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("MACD_CHECK_ON_CLOSE","True"))
112
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("BBM_STUDY","True"))
113
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("BBM_CHART","False"))
114
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("BBM_CROSSING","False"))
115
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("BBM_CHECK_ON_OPEN","False"))
116
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("BBM_CHECK_ON_CLOSE","False"))
117
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("RSI_STUDY","True"))
118
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("RSI_CHART","True"))
119
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("RSI_CROSSING","False"))
120
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("RSI_CHECK_ON_OPEN","False"))
121
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("RSI_CHECK_ON_CLOSE","False"))
122
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("ADX_STUDY","True"))
123
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("ADX_CHECK_ON_OPEN","True"))
124
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("ADX_CHECK_ON_CLOSE","False"))
125
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("CANDLE_TREND_STUDY","False"))
126
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("CANDLE_TREND_CHECK_ON_OPEN","False"))
127
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("CANDLE_TREND_CHECK_ON_CLOSE","False"))
128
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("SCREEN_REFRESH_INTERVAL","1"))
129
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("SIGNAL_CHECK_INTERVAL","15"))
130
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("PORTFOLIO_API_INTERVAL","3"))
131
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("PENDING_POSITIONS_API_INTERVAL","3"))
132
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("PENDING_ORDERS_API_INTERVAL","3"))
133
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("TRADE_HISTORY_API_INTERVAL","3"))
134
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("POSITION_HISTORY_API_INTERVAL","3"))
135
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("TICKER_DATA_API_INTERVAL","120"))
136
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("BATCH_PROCESS_SIZE","1000"))
137
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("USE_PUBLIC_WEBSOCKET","True"))
138
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("VERBOSE_LOGGING","False"))
139
+ self.cursor.execute("INSERT INTO settings (param, value) VALUES (?, ?)", ("BENCHMARK","False"))
140
+ self.connection.commit()
141
+
63
142
 
64
143
  async def update_settings(self, settings):
65
144
  self.settings = settings
@@ -1,6 +1,7 @@
1
1
  from pydantic import Field, SecretStr, validator
2
2
  from pydantic_settings import BaseSettings
3
3
  import os
4
+ import sqlite3
4
5
 
5
6
  class Settings(BaseSettings):
6
7
  # Start autotrading on start
@@ -75,7 +76,7 @@ class Settings(BaseSettings):
75
76
  TRADE_HISTORY_API_INTERVAL: int = Field(default=3, ge=1)
76
77
  POSITION_HISTORY_API_INTERVAL: int = Field(default=3, ge=1)
77
78
  TICKER_DATA_API_INTERVAL: int = Field(default=30, ge=1)
78
- PUBLIC_WEBSOCKET_RESTART_INTERVAL: int = Field(default=10_800, ge=1)
79
+ BATCH_PROCESS_SIZE: int = Field(default=1000, ge=1)
79
80
 
80
81
  # Use websocket or API
81
82
  USE_PUBLIC_WEBSOCKET: bool = Field(default=False) # If there is lagging issue then use API
@@ -89,3 +90,26 @@ class Settings(BaseSettings):
89
90
  class Config:
90
91
  # Specify the file name for loading environment variables
91
92
  env_file = os.path.dirname(os.path.abspath(__file__))+"/config.txt"
93
+
94
+ @classmethod
95
+ def load_from_db(cls, db_path: str, table_name: str = "settings"):
96
+ # Connect to SQLite database
97
+ connection = sqlite3.connect(db_path)
98
+ cursor = connection.cursor()
99
+
100
+ # Fetch all parameters and values from the database
101
+ cursor.execute(f"SELECT param, value FROM {table_name}")
102
+ rows = cursor.fetchall()
103
+ connection.close()
104
+
105
+ # Map fetched data to the class attributes dynamically
106
+ settings_dict = {}
107
+ for param, value in rows:
108
+ # Convert string 'value' to proper type based on attribute type
109
+ if hasattr(cls, param):
110
+ attr_type = type(getattr(cls(), param))
111
+ settings_dict[param] = attr_type(value) if attr_type != bool else value.lower() in ("true", "1")
112
+
113
+ # Create the Settings instance with mapped data
114
+ return cls(**settings_dict)
115
+
@@ -45,7 +45,7 @@ class CSTFormatter(logging.Formatter):
45
45
  return cst_time.isoformat()
46
46
 
47
47
  class Logger:
48
- def __init__(self, logger_name, log_file='app.log', level=logging.DEBUG, max_bytes=5 * 1024 * 1024, backup_count=3):
48
+ def __init__(self, logger_name, log_file='app.log', level=logging.DEBUG, max_bytes=50 * 1024 * 1024, backup_count=100):
49
49
  """
50
50
  Initialize the logger.
51
51
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bitunix_automated_crypto_trading
3
- Version: 3.0.4
3
+ Version: 3.0.7
4
4
  Summary: Bitunix Futures Auto Trading Platform
5
5
  Home-page: https://github.com/tcj2001/bitunix-automated-crypto-trading
6
6
  Author: tcj2001
@@ -66,7 +66,7 @@ class CustomInstall(install):
66
66
 
67
67
  setup(
68
68
  name="bitunix_automated_crypto_trading",
69
- version="3.0.4",
69
+ version="3.0.7",
70
70
  license="MIT",
71
71
  author="tcj2001",
72
72
  author_email="thomsonmathews@hotmail.com",