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.
- bitunix_automated_crypto_trading/BitunixSignal.py +145 -86
- bitunix_automated_crypto_trading/bitunix.py +79 -0
- bitunix_automated_crypto_trading/config.py +25 -1
- bitunix_automated_crypto_trading/logger.py +1 -1
- {bitunix_automated_crypto_trading-3.0.5.dist-info → bitunix_automated_crypto_trading-3.0.9.dist-info}/METADATA +1 -1
- {bitunix_automated_crypto_trading-3.0.5.dist-info → bitunix_automated_crypto_trading-3.0.9.dist-info}/RECORD +9 -9
- {bitunix_automated_crypto_trading-3.0.5.dist-info → bitunix_automated_crypto_trading-3.0.9.dist-info}/WHEEL +0 -0
- {bitunix_automated_crypto_trading-3.0.5.dist-info → bitunix_automated_crypto_trading-3.0.9.dist-info}/entry_points.txt +0 -0
- {bitunix_automated_crypto_trading-3.0.5.dist-info → bitunix_automated_crypto_trading-3.0.9.dist-info}/top_level.txt +0 -0
@@ -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.
|
145
|
-
self.
|
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.
|
149
|
-
self.
|
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
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
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
|
-
|
240
|
-
|
241
|
-
|
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
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
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
|
273
|
-
if message
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
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', '
|
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
|
-
|
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=
|
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,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=
|
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=
|
11
|
-
bitunix_automated_crypto_trading/config.py,sha256=
|
12
|
-
bitunix_automated_crypto_trading/logger.py,sha256=
|
13
|
-
bitunix_automated_crypto_trading-3.0.
|
14
|
-
bitunix_automated_crypto_trading-3.0.
|
15
|
-
bitunix_automated_crypto_trading-3.0.
|
16
|
-
bitunix_automated_crypto_trading-3.0.
|
17
|
-
bitunix_automated_crypto_trading-3.0.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|