bitunix-automated-crypto-trading 3.0.4__py3-none-any.whl → 3.0.7__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 +153 -156
- 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.4.dist-info → bitunix_automated_crypto_trading-3.0.7.dist-info}/METADATA +1 -1
- {bitunix_automated_crypto_trading-3.0.4.dist-info → bitunix_automated_crypto_trading-3.0.7.dist-info}/RECORD +9 -9
- {bitunix_automated_crypto_trading-3.0.4.dist-info → bitunix_automated_crypto_trading-3.0.7.dist-info}/WHEEL +0 -0
- {bitunix_automated_crypto_trading-3.0.4.dist-info → bitunix_automated_crypto_trading-3.0.7.dist-info}/entry_points.txt +0 -0
- {bitunix_automated_crypto_trading-3.0.4.dist-info → bitunix_automated_crypto_trading-3.0.7.dist-info}/top_level.txt +0 -0
@@ -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.
|
171
|
-
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")
|
172
152
|
|
173
153
|
self.bitunixPublicTickerWebSocketClient.tickerList = self.tickerList
|
174
|
-
self.
|
175
|
-
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
|
+
|
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
|
-
|
188
|
-
|
189
|
-
|
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
|
-
|
215
|
-
|
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
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
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
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
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
|
-
|
299
|
-
|
300
|
-
|
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
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
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
|
-
|
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
|
-
#
|
331
|
-
async def
|
332
|
-
|
333
|
-
|
334
|
-
|
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
|
-
|
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
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
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', '
|
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
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
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
|
-
|
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=fqmarMKcJi8qmXaF-O0EjN-ROTR5BBAbeGCmlkF-zgA,64164
|
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.7.dist-info/METADATA,sha256=6XzEoQONyYR3KuG5Q3r-8-td9F3AtWdQU0M0vWrUXTw,996
|
14
|
+
bitunix_automated_crypto_trading-3.0.7.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
15
|
+
bitunix_automated_crypto_trading-3.0.7.dist-info/entry_points.txt,sha256=UXREYHuSl2XYd_tOtLIq0zg3d1kX3lixX5SpN8yGBw4,82
|
16
|
+
bitunix_automated_crypto_trading-3.0.7.dist-info/top_level.txt,sha256=uyFzHUCOsp8elnG2Ovor6xXcf7dxRxY-C-Txiwix64Q,33
|
17
|
+
bitunix_automated_crypto_trading-3.0.7.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|