bitunix-automated-crypto-trading 3.0.5__py3-none-any.whl → 3.0.9__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.
@@ -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,118 @@ 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}")
242
-
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()
243
261
 
262
+ async def drain_queue(self, queue):
263
+ items = []
264
+ while not queue.empty():
265
+ items.append(await queue.get())
266
+ return deque(items[::-1][:self.settings.BATCH_PROCESS_SIZE]) # Reverse items
267
+
244
268
  #websocket data to update ticker lastprice and other relavent data
245
- async def UpdateTickerData(self, message):
246
- if message=="":
247
- return
248
- try:
249
- data = json.loads(message)
250
- 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}")
269
+ # Function to add data to the last price deque
270
+ async def StoreTickerData(self, message):
271
+ if self.settings.USE_PUBLIC_WEBSOCKET and message:
272
+ try:
273
+ data = json.loads(message)
274
+ if data.get('symbol') and data.get('ch') == 'ticker':
275
+ await self.ticker_que.put(data)
276
+ except json.JSONDecodeError as e:
277
+ logger.warning(f"Failed to decode message: {message}. Error: {e}")
278
+
279
+ # Function to process the last price deque
280
+ async def ProcessTickerData(self):
281
+ while True:
282
+ try:
283
+ latest_data = {}
284
+ reversed_items = await self.drain_queue(self.ticker_que)
285
+ while reversed_items:
286
+ data = reversed_items.popleft()
287
+ symbol = data["symbol"]
288
+ ts = data["ts"]
289
+ if symbol not in latest_data or ts > latest_data[symbol]['ts']:
290
+ latest_data[symbol] = {'ts': ts, 'last': float(data['data']['la'])}
291
+ # Convert to DataFrame
292
+ self.tickerdf = pd.DataFrame.from_dict(latest_data, orient="index")
293
+ if not self.tickerdf.empty:
294
+ self.tickerdf["tickerObj"] = self.tickerdf.index.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)
297
+ self.lastTickerDataTime = time.time()
298
+ except Exception as e:
299
+ logger.info(f"Function: ProcessTickerData, {e}, {e.args}, {type(e).__name__}")
300
+ logger.info(traceback.print_exc())
301
+ await asyncio.sleep(0.5)
270
302
 
271
303
  #websocket data to update bid and ask
272
- async def UpdateDepthData(self, message):
273
- if message=="":
274
- return
275
- try:
276
- data = json.loads(message)
277
- 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
-
304
+ async def StoreDepthData(self, message):
305
+ if self.settings.USE_PUBLIC_WEBSOCKET and message:
306
+ try:
307
+ data = json.loads(message)
308
+ if data.get('symbol') and data.get('ch') == 'depth_book1':
309
+ await self.depth_que.put(data)
310
+ except json.JSONDecodeError as e:
311
+ logger.warning(f"Failed to decode message: {message}. Error: {e}")
312
+
313
+ # Function to process the bid, ask
314
+ async def ProcessDepthData(self):
315
+ while True:
316
+ try:
317
+ latest_data = {}
318
+ reversed_items = await self.drain_queue(self.depth_que)
319
+ while reversed_items:
320
+ data = reversed_items.popleft()
321
+ symbol = data["symbol"]
322
+ ts = data["ts"]
323
+ if symbol not in latest_data or ts > latest_data[symbol]['ts']:
324
+ latest_data[symbol] = {'ts': ts, 'bid': float(data['data']['b'][0][0]), 'ask': float(data['data']['a'][0][0])}
325
+ # Convert to DataFrame
326
+ self.depthdf = pd.DataFrame.from_dict(latest_data, orient="index")
327
+ if not self.depthdf.empty:
328
+ self.depthdf["tickerObj"] = self.depthdf.index.map(self.tickerObjects.get_tickerDict())
329
+ self.depthdf.apply(self.apply_depth_data2, axis=1)
330
+ except Exception as e:
331
+ logger.info(f"Function: ProcessTickerData, {e}, {e.args}, {type(e).__name__}")
332
+ logger.info(traceback.print_exc())
333
+ await asyncio.sleep(0.5)
334
+
335
+ def apply_depth_data2(self, row):
336
+ row["tickerObj"].set_ask(row["ask"])
337
+ row["tickerObj"].set_bid(row["bid"])
338
+ return row
339
+
340
+ # non websocket method
295
341
  # this is called to update last price, as the websocket is lagging
296
342
  # this is only called for the tickers in the pendingpositions
297
343
  # and for first few records in the signaldf
@@ -318,6 +364,7 @@ class BitunixSignal:
318
364
  if self.settings.VERBOSE_LOGGING:
319
365
  logger.info(f"apply_last_data: elapsed time {time.time()-start}")
320
366
 
367
+ # non websocket method
321
368
  # this is called to update bid and ask,
322
369
  # as it is time consuming to call the api for each ticker,
323
370
  # this is only called for the tickers in the pendingpositions
@@ -338,6 +385,13 @@ class BitunixSignal:
338
385
  gc.collect()
339
386
 
340
387
  ###########################################################################################################
388
+ async def checkPeriodicProcess(self):
389
+ while True:
390
+ if self.lastAutoTradeTime + 300 < time.time() or self.lastTickerDataTime + 300 < time.time():
391
+ self.notifications.add_notification("AutoTradeProcess or GetTickerData is not running")
392
+ os._exit(1)
393
+ break
394
+ await asyncio.sleep(300)
341
395
 
342
396
  async def GetportfolioData(self):
343
397
  start=time.time()
@@ -522,7 +576,7 @@ class BitunixSignal:
522
576
  self.signaldf = self.signaldf_filtered[[
523
577
  "symbol", f"{period}_trend",f"{period}_cb", f"{period}_barcolor",
524
578
  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',
579
+ 'lastcolor', 'bidcolor', 'askcolor', 'bid', 'last', 'ask',
526
580
  f"{period}_open", f"{period}_close", f"{period}_high", f"{period}_low",
527
581
  ]].sort_values(by=[f'{period}_cb'], ascending=[False])
528
582
 
@@ -899,6 +953,11 @@ class BitunixSignal:
899
953
 
900
954
  if self.settings.VERBOSE_LOGGING:
901
955
  logger.info(f"AutoTradeProcess: elapsed time {time.time()-start}")
956
+ if self.settings.BENCHMARK:
957
+ self.connection = sqlite3.connect("bitunix.db")
958
+ self.cursor = self.connection.cursor()
959
+ self.cursor.execute("INSERT INTO benchmark (process_name, time) VALUES (?, ?)", ("AutoTradeProcess", time.time()-start))
960
+ self.connection.commit()
902
961
 
903
962
  del df
904
963
  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.9
4
4
  Summary: Bitunix Futures Auto Trading Platform
5
5
  Home-page: https://github.com/tcj2001/bitunix-automated-crypto-trading
6
6
  Author: tcj2001
@@ -1,17 +1,17 @@
1
1
  bitunix_automated_crypto_trading/AsyncThreadRunner.py,sha256=JDpAUiTZLB9KV4tGPonAvAUJyBqZz2-ehblH6vsunz8,3142
2
2
  bitunix_automated_crypto_trading/BitunixApi.py,sha256=wJhknpmVFrckoL89h-nlWUAMsPxhIacY0nOGJT9M6Vw,11360
3
- bitunix_automated_crypto_trading/BitunixSignal.py,sha256=EP2PW8rXlLObj4ycgUxLH900BlleJzqosAecj6KA45A,60878
3
+ bitunix_automated_crypto_trading/BitunixSignal.py,sha256=sArNBVXFZu4unBeTBhv3S1yNuZd-8PZtu10yGBGVkW8,64377
4
4
  bitunix_automated_crypto_trading/BitunixWebSocket.py,sha256=mbuvk8UFWKgv4KLV07TgLgxLVTRJnOKuf02mLB-VoCY,11143
5
5
  bitunix_automated_crypto_trading/DataFrameHtmlRenderer.py,sha256=Pqdzhh_nfIxFEZH9L_R5QXB8moDPbgeTGT_hmBkHWMg,2899
6
6
  bitunix_automated_crypto_trading/NotificationManager.py,sha256=pqDquEe-oujD2v8B543524vo62aRMjfB4YW-3DMhVGQ,795
7
7
  bitunix_automated_crypto_trading/ThreadManager.py,sha256=WmYRQu8aNrgp5HwSqpBqX1WESNSEpbD2CznPFpd9HuI,2330
8
8
  bitunix_automated_crypto_trading/TickerManager.py,sha256=eeOK5paiae10XHjKMbucJnVAZkAsEC86N37bRPuCcFc,33427
9
9
  bitunix_automated_crypto_trading/__init__.py,sha256=1hzk6nX8NnUCr1tsq8oFq1qGCNhNwnwldWE75641Eew,78
10
- bitunix_automated_crypto_trading/bitunix.py,sha256=ooahU8kEhibVKYbUBdd2yxvTDiXGSvn1ENmepDoqA1g,24671
11
- bitunix_automated_crypto_trading/config.py,sha256=sZ4pXnpFxdN3rqXSyhYnXsb6dUQzvDYW6A4q7fFNO_k,3628
12
- bitunix_automated_crypto_trading/logger.py,sha256=FB5g2ZqTUuaIcqrzaUFtp1ZtQi-4STYL_VHLDuv9Ysg,2930
13
- bitunix_automated_crypto_trading-3.0.5.dist-info/METADATA,sha256=KBLm8zce1nx5w_7m7uY2OGEdsinMvE075LuEywA5HKU,996
14
- bitunix_automated_crypto_trading-3.0.5.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
15
- bitunix_automated_crypto_trading-3.0.5.dist-info/entry_points.txt,sha256=UXREYHuSl2XYd_tOtLIq0zg3d1kX3lixX5SpN8yGBw4,82
16
- bitunix_automated_crypto_trading-3.0.5.dist-info/top_level.txt,sha256=uyFzHUCOsp8elnG2Ovor6xXcf7dxRxY-C-Txiwix64Q,33
17
- bitunix_automated_crypto_trading-3.0.5.dist-info/RECORD,,
10
+ bitunix_automated_crypto_trading/bitunix.py,sha256=XLYaSSX0g2MMQ6TgR762qL1ppthKm2Ki6vMR77xSpdM,32248
11
+ bitunix_automated_crypto_trading/config.py,sha256=QWAe5Ruq8A5anaFS-CamVm-3t1wMJjGG1DWaPIIWfiM,4521
12
+ bitunix_automated_crypto_trading/logger.py,sha256=tAaTQcv5r--zupk_Fhfe1USfBAzSiXzVa4QRnaOFIFY,2933
13
+ bitunix_automated_crypto_trading-3.0.9.dist-info/METADATA,sha256=h3fxV9vRsIa5kna4waL0fpixwZ_PnpqTgOSTcyjB2U4,996
14
+ bitunix_automated_crypto_trading-3.0.9.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
15
+ bitunix_automated_crypto_trading-3.0.9.dist-info/entry_points.txt,sha256=UXREYHuSl2XYd_tOtLIq0zg3d1kX3lixX5SpN8yGBw4,82
16
+ bitunix_automated_crypto_trading-3.0.9.dist-info/top_level.txt,sha256=uyFzHUCOsp8elnG2Ovor6xXcf7dxRxY-C-Txiwix64Q,33
17
+ bitunix_automated_crypto_trading-3.0.9.dist-info/RECORD,,