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.
- {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/PKG-INFO +1 -1
- {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/BitunixSignal.py +137 -81
- {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/bitunix.py +79 -0
- {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/config.py +25 -1
- {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/logger.py +1 -1
- {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading.egg-info/PKG-INFO +1 -1
- {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/setup.py +1 -1
- {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/README.md +0 -0
- {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/AsyncThreadRunner.py +0 -0
- {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/BitunixApi.py +0 -0
- {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/BitunixWebSocket.py +0 -0
- {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/DataFrameHtmlRenderer.py +0 -0
- {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/NotificationManager.py +0 -0
- {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/ThreadManager.py +0 -0
- {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/TickerManager.py +0 -0
- {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading/__init__.py +0 -0
- {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading.egg-info/SOURCES.txt +0 -0
- {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
- {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
- {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/bitunix_automated_crypto_trading.egg-info/requires.txt +0 -0
- {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
- {bitunix_automated_crypto_trading-3.0.5 → bitunix_automated_crypto_trading-3.0.7}/setup.cfg +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,115 @@ 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
|
-
|
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
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
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
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
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
|
273
|
-
if
|
274
|
-
|
275
|
-
|
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
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
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', '
|
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
|
-
|
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
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|