bitunix-automated-crypto-trading 3.0.5__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.5 → bitunix_automated_crypto_trading-3.0.7}/PKG-INFO +1 -1
  2. {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/BitunixSignal.py +137 -81
  3. {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/bitunix.py +79 -0
  4. {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/config.py +25 -1
  5. {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/logger.py +1 -1
  6. {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading.egg-info/PKG-INFO +1 -1
  7. {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/setup.py +1 -1
  8. {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/README.md +0 -0
  9. {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/AsyncThreadRunner.py +0 -0
  10. {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/BitunixApi.py +0 -0
  11. {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/BitunixWebSocket.py +0 -0
  12. {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/DataFrameHtmlRenderer.py +0 -0
  13. {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/NotificationManager.py +0 -0
  14. {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/ThreadManager.py +0 -0
  15. {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/TickerManager.py +0 -0
  16. {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/__init__.py +0 -0
  17. {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading.egg-info/SOURCES.txt +0 -0
  18. {bitunix_automated_crypto_trading-3.0.5 → 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.5 → 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.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading.egg-info/requires.txt +0 -0
  21. {bitunix_automated_crypto_trading-3.0.5 → 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.5 → 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.5
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,8 +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
20
21
  import sqlite3
22
+ import queue
23
+ from collections import deque
24
+ import threading
25
+
21
26
 
22
27
  cst = pytz.timezone('US/Central')
23
28
 
@@ -71,6 +76,7 @@ class BitunixSignal:
71
76
 
72
77
  #websockets
73
78
  self.bitunixPrivateWebSocketClient = BitunixPrivateWebSocketClient(self.api_key, self.secret_key)
79
+
74
80
  if self.settings.USE_PUBLIC_WEBSOCKET:
75
81
  self.bitunixPublicDepthWebSocketClient = BitunixPublicWebSocketClient(self.api_key, self.secret_key, "depth")
76
82
  self.bitunixPublicTickerWebSocketClient = BitunixPublicWebSocketClient(self.api_key, self.secret_key, "ticker")
@@ -87,8 +93,6 @@ class BitunixSignal:
87
93
 
88
94
  #sqllite database connection
89
95
  self.connection = sqlite3.connect("bitunix.db")
90
- self.cursor = self.connection.cursor()
91
- self.cursor.execute("CREATE TABLE IF NOT EXISTS benchmark (id INTEGER PRIMARY KEY, process_name TEXT, time INTEGER)")
92
96
 
93
97
  async def update_settings(self, settings):
94
98
  self.settings = settings
@@ -139,25 +143,35 @@ class BitunixSignal:
139
143
  self.ProcessPrivateDataTask.start_thread(thread_name="ProcessPrivateData")
140
144
 
141
145
  if self.settings.USE_PUBLIC_WEBSOCKET:
142
-
143
146
  self.bitunixPublicDepthWebSocketClient.tickerList = self.tickerList
144
- self.UpdateDepthDataTask = AsyncThreadRunner(self.bitunixPublicDepthWebSocketClient.run_websocket, 0, self.UpdateDepthData)
145
- 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")
146
152
 
147
153
  self.bitunixPublicTickerWebSocketClient.tickerList = self.tickerList
148
- self.UpdateTickerDataTask = AsyncThreadRunner(self.bitunixPublicTickerWebSocketClient.run_websocket, 0, self.UpdateTickerData)
149
- 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
+
150
160
 
151
161
  #normal processes
152
162
  self.LoadKlineHistoryTask = AsyncThreadRunner(self.LoadKlineHistory, interval=0) # run only once
153
163
  self.LoadKlineHistoryTask.start_thread(thread_name="LoadKlineHistory")
154
164
 
155
- self.GetTickerDataTask = AsyncThreadRunner(self.GetTickerData, interval=int(self.settings.TICKER_DATA_API_INTERVAL))
156
- self.GetTickerDataTask.start_thread(thread_name="GetTickerData")
157
-
158
165
  self.AutoTradeProcessTask = AsyncThreadRunner(self.AutoTradeProcess, interval=int(self.settings.SIGNAL_CHECK_INTERVAL))
159
166
  self.AutoTradeProcessTask.start_thread(thread_name="AutoTradeProcess")
160
167
 
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")
171
+
172
+ self.checkPeriodicProcessTask = AsyncThreadRunner(self.checkPeriodicProcess, interval=0)
173
+ self.checkPeriodicProcessTask.start_thread(thread_name="checkPeriodicProcess")
174
+
161
175
 
162
176
  ###########################################################################################################
163
177
  async def DefinehtmlRenderers(self):
@@ -212,86 +226,115 @@ class BitunixSignal:
212
226
 
213
227
  #api data
214
228
  async def GetTickerData(self):
215
- start=time.time()
216
-
217
- # Get the current time and set the seconds and microseconds to zero
218
- current_time = datetime.now()
219
- current_minute = current_time.replace(second=0, microsecond=0)
220
- ts = int(current_minute.timestamp())*1000
221
-
222
- #api used insted of websocket
223
- data = await self.bitunixApi.GetTickerData()
224
- self.tickerdf = pd.DataFrame()
225
- if data:
226
-
227
- # Create a DataFrame from the data
228
- self.tickerdf = pd.DataFrame(data, columns=["symbol", "last"])
229
-
230
- #remove not required symbols
231
- self.tickerdf.loc[~self.tickerdf['symbol'].isin(self.tickerObjects.symbols()), :] = None
232
- self.tickerdf.dropna(inplace=True)
233
-
234
- self.tickerdf['ts']=ts
235
- self.tickerdf["tickerObj"] = self.tickerdf["symbol"].map(self.tickerObjects.get_tickerDict())
236
- self.tuples_list = list(zip(self.tickerdf["tickerObj"], self.tickerdf["last"].astype(float), self.tickerdf["ts"]))
237
- self.tickerObjects.form_candle(self.tuples_list)
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
238
235
 
239
- self.lastTickerDataTime = time.time()
240
- if self.settings.VERBOSE_LOGGING:
241
- logger.info(f"GetTickerData: elapsed time {time.time()-start}")
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)
252
+
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()
242
261
 
243
262
 
244
263
  #websocket data to update ticker lastprice and other relavent data
245
- async def UpdateTickerData(self, message):
246
- if message=="":
247
- return
248
- try:
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
249
269
  data = json.loads(message)
250
270
  if 'symbol' in data and data['ch'] in ['ticker']:
251
- symbol = data['symbol']
252
- ts = data['ts']
253
- last= float(data['data']['la'])
254
- highest= float(data['data']['h'])
255
- lowest= float(data['data']['l'])
256
- volume= float(data['data']['b'])
257
- volumeInCurrency= float(data['data']['q'])
258
- tickerObj = self.tickerObjects.get(symbol)
259
- if tickerObj:
260
- tickerObj.set_24hrData(highest,lowest,volume,volumeInCurrency)
261
- tickerObj.form_candle(last, ts)
262
- del tickerObj
263
- gc.collect()
264
- del data
265
- gc.collect()
266
- except Exception as e:
267
- logger.info(f"Function: UpdateTickerData, {e}, {e.args}, {type(e).__name__}")
268
- if self.settings.VERBOSE_LOGGING:
269
- logger.info(f"Function: UpdateTickerData, time:{ts}, symbol:{symbol}, highest:{highest}, lowest:{lowest}, volume:{volume}, volumeInCurrency:{volumeInCurrency}")
271
+ await self.ticker_que.put(data)
272
+
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)
270
298
 
271
299
  #websocket data to update bid and ask
272
- async def UpdateDepthData(self, message):
273
- if message=="":
274
- return
275
- try:
300
+ async def StoreDepthData(self, message):
301
+ if self.settings.USE_PUBLIC_WEBSOCKET:
302
+ if message=="":
303
+ return
276
304
  data = json.loads(message)
277
305
  if 'symbol' in data and data['ch'] in ['depth_book1']:
278
- symbol = data['symbol']
279
- ts = data['ts']
280
- bid = float(data['data']['b'][0][0])
281
- ask = float(data['data']['a'][0][0])
282
- tickerObj = self.tickerObjects.get(symbol)
283
- if tickerObj:
284
- tickerObj.set_bid(bid)
285
- tickerObj.set_ask(ask)
286
- del tickerObj
287
- gc.collect()
288
- del data
289
- gc.collect()
290
- except Exception as e:
291
- logger.info(f"Function: UpdateDepthData, {e}, {e.args}, {type(e).__name__}")
292
- if self.settings.VERBOSE_LOGGING:
293
- logger.info(f"Function: UpdateDepthData, time:{ts}, symbol:{symbol}, bid:{bid}, ask:{ask}")
294
-
306
+ await self.depth_que.put(data)
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
295
338
  # this is called to update last price, as the websocket is lagging
296
339
  # this is only called for the tickers in the pendingpositions
297
340
  # and for first few records in the signaldf
@@ -318,6 +361,7 @@ class BitunixSignal:
318
361
  if self.settings.VERBOSE_LOGGING:
319
362
  logger.info(f"apply_last_data: elapsed time {time.time()-start}")
320
363
 
364
+ # non websocket method
321
365
  # this is called to update bid and ask,
322
366
  # as it is time consuming to call the api for each ticker,
323
367
  # this is only called for the tickers in the pendingpositions
@@ -338,6 +382,13 @@ class BitunixSignal:
338
382
  gc.collect()
339
383
 
340
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)
341
392
 
342
393
  async def GetportfolioData(self):
343
394
  start=time.time()
@@ -522,7 +573,7 @@ class BitunixSignal:
522
573
  self.signaldf = self.signaldf_filtered[[
523
574
  "symbol", f"{period}_trend",f"{period}_cb", f"{period}_barcolor",
524
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",
525
- 'lastcolor', 'bidcolor', 'askcolor', 'bid', 'ask', 'last',
576
+ 'lastcolor', 'bidcolor', 'askcolor', 'bid', 'last', 'ask',
526
577
  f"{period}_open", f"{period}_close", f"{period}_high", f"{period}_low",
527
578
  ]].sort_values(by=[f'{period}_cb'], ascending=[False])
528
579
 
@@ -899,6 +950,11 @@ class BitunixSignal:
899
950
 
900
951
  if self.settings.VERBOSE_LOGGING:
901
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()
902
958
 
903
959
  del df
904
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.5
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.5",
69
+ version="3.0.7",
70
70
  license="MIT",
71
71
  author="tcj2001",
72
72
  author_email="thomsonmathews@hotmail.com",