bitunix-automated-crypto-trading 3.1.7__py3-none-any.whl → 3.1.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.
@@ -1,10 +1,7 @@
1
1
  import pandas as pd
2
2
  import numpy as np
3
3
  from scipy.signal import find_peaks
4
-
5
- class SupportResistance:
6
- import pandas as pd
7
- import numpy as np
4
+ import talib
8
5
 
9
6
  class SupportResistance:
10
7
  def _find_peaks(self, prices, prominence=None, distance=None):
@@ -33,7 +30,7 @@ class SupportResistance:
33
30
  intercept = price1 - slope * index1
34
31
  return slope * np.arange(length) + intercept
35
32
 
36
- def support_resistance_trend_lines(self, data, lookback=20, prominence=None, distance=None):
33
+ def support_resistance_trend_lines(self, data, lookback=24, prominence=None, distance=None):
37
34
  if data is None or len(data) < lookback:
38
35
  return pd.DataFrame()
39
36
  try:
@@ -44,39 +41,69 @@ class SupportResistance:
44
41
 
45
42
  # Find all local peaks and troughs
46
43
  peak_indices_all = self._find_peaks(high_prices, prominence=prominence, distance=distance)
47
- if len(peak_indices_all) > 0 and peak_indices_all[-1] < lookback - 2 and high_prices[-2] > high_prices[lookback - peak_indices_all[-1]]:
48
- peak_indices_all = np.append(peak_indices_all,lookback - 2)
49
44
  trough_indices_all = self._find_troughs(low_prices, prominence=prominence, distance=distance)
50
- if len(trough_indices_all) > 0 and trough_indices_all[-1] < lookback - 2 and low_prices[-2] < low_prices[lookback - trough_indices_all[-1]]:
51
- trough_indices_all = np.append(trough_indices_all, lookback - 2)
52
- # Get the two most recent prominent peaks
53
- most_recent_peaks_indices = self._get_n_most_recent_prominent(peak_indices_all, high_prices, n=2)
54
-
55
- # Get the two most recent prominent troughs
56
- most_recent_troughs_indices = self._get_n_most_recent_prominent(trough_indices_all, low_prices, n=2)
57
45
 
58
46
  support_line = np.full(lookback, np.nan)
59
47
  resistance_line = np.full(lookback, np.nan)
60
48
 
61
- if len(most_recent_peaks_indices) >= 2:
62
- # Sort by index (time) to ensure correct order
63
- sorted_peak_indices = most_recent_peaks_indices #np.sort(most_recent_peaks_indices)
64
- resistance_line_full = self._get_line(sorted_peak_indices[0], high_prices[sorted_peak_indices[0]],
65
- sorted_peak_indices[1], high_prices[sorted_peak_indices[1]], lookback)
66
- # Set values before the second peak to NaN
67
- resistance_line[sorted_peak_indices[1]:] = resistance_line_full[sorted_peak_indices[1]:]
68
- elif len(most_recent_peaks_indices) == 1:
69
- resistance_line[most_recent_peaks_indices[0]:] = high_prices[most_recent_peaks_indices[0]] # Horizontal line from the peak onwards
70
-
71
- if len(most_recent_troughs_indices) >= 2:
72
- # Sort by index (time) to ensure correct order
73
- sorted_trough_indices = most_recent_troughs_indices #np.sort(most_recent_troughs_indices)
74
- support_line_full = self._get_line(sorted_trough_indices[0], low_prices[sorted_trough_indices[0]],
75
- sorted_trough_indices[1], low_prices[sorted_trough_indices[1]], lookback)
76
- # Set values before the second trough to NaN
77
- support_line[sorted_trough_indices[1]:] = support_line_full[sorted_trough_indices[1]:]
78
- elif len(most_recent_troughs_indices) == 1:
79
- support_line[most_recent_troughs_indices[0]:] = low_prices[most_recent_troughs_indices[0]] # Horizontal line from the trough onwards
49
+ if len(peak_indices_all) > 0:
50
+ if len(peak_indices_all) >= 2:
51
+ resistance_line_full = self._get_line(peak_indices_all[-1], high_prices[peak_indices_all[-1]],
52
+ peak_indices_all[-2], high_prices[peak_indices_all[-2]], lookback)
53
+ # Set values before the second peak to NaN
54
+ resistance_line[peak_indices_all[-2]:] = resistance_line_full[peak_indices_all[-2]:]
55
+ elif len(peak_indices_all) == 1:
56
+ resistance_line[peak_indices_all[-1]:] = high_prices[peak_indices_all[-1]] # Horizontal line from the peak onwards
57
+
58
+ if len(trough_indices_all) > 0:
59
+ if len(trough_indices_all) >= 2:
60
+ support_line_full = self._get_line(trough_indices_all[-1], low_prices[trough_indices_all[-1]],
61
+ trough_indices_all[-2], low_prices[trough_indices_all[-2]], lookback)
62
+ # Set values before the second trough to NaN
63
+ support_line[trough_indices_all[-2]:] = support_line_full[trough_indices_all[-2]:]
64
+ elif len(trough_indices_all) == 1:
65
+ support_line[trough_indices_all[-1]:] = low_prices[trough_indices_all[-1]] # Horizontal line from the trough onwards
66
+
67
+ results_df = pd.DataFrame({
68
+ 'time': ts,
69
+ 'support_line': support_line,
70
+ 'resistance_line': resistance_line
71
+ })
72
+ return results_df
73
+ except Exception as e:
74
+ print(f"trendline error occurred: {e}")
75
+ return pd.DataFrame()
76
+
77
+ def support_resistance_bos_lines(self, data, length, lookback=24):
78
+ if data is None or len(data) < lookback:
79
+ return pd.DataFrame()
80
+ try:
81
+ recent_data = data.iloc[-length:].copy()
82
+ highs = recent_data['high'].values
83
+ high_prices = np.full_like(highs, np.nan, dtype=np.float64)
84
+ high_prices[-lookback:] = highs[-lookback:]
85
+
86
+ lows = recent_data['low'].values
87
+ low_prices = np.full_like(lows, np.nan, dtype=np.float64)
88
+ low_prices[-lookback:] = lows[-lookback:]
89
+
90
+ ts = recent_data['time'].values
91
+
92
+ support_line = np.full(length, np.nan)
93
+ resistance_line = np.full(length, np.nan)
94
+
95
+ # Find all local peaks and troughs and its highest and lowest values
96
+ peak_indices_all = self._find_peaks(high_prices)
97
+ if len(peak_indices_all) > 0:
98
+ highest_peak_index = peak_indices_all[np.argmax(high_prices[peak_indices_all])]
99
+ highest_peak_value = high_prices[highest_peak_index]
100
+ resistance_line[highest_peak_index:] = high_prices[highest_peak_index] # Horizontal line from the highest point onwards
101
+
102
+ trough_indices_all = self._find_troughs(low_prices)
103
+ if len(trough_indices_all) > 0:
104
+ lowest_peak_index = trough_indices_all[np.argmin(low_prices[trough_indices_all])]
105
+ lowest_peak_value = low_prices[lowest_peak_index]
106
+ support_line[lowest_peak_index:] = low_prices[lowest_peak_index] # Horizontal line from the lowset point onwards
80
107
 
81
108
  results_df = pd.DataFrame({
82
109
  'time': ts,
@@ -85,5 +112,6 @@ class SupportResistance:
85
112
  })
86
113
  return results_df
87
114
  except Exception as e:
88
- print(f"An error occurred: {e}")
89
- return pd.DataFrame()
115
+ print(f"bos error occurred: {e}")
116
+ return pd.DataFrame()
117
+
@@ -10,7 +10,7 @@
10
10
  import time
11
11
  import threading
12
12
  from logger import Logger
13
- logger = Logger(__name__).get_logger()
13
+ global LOG_FILE
14
14
 
15
15
  def job_func(*args, **kwargs):
16
16
  print(f"Job running with arguments: {args}, {kwargs}")
@@ -20,13 +20,14 @@ def run_threaded(job_func, args):
20
20
  job_thread.start()
21
21
 
22
22
  class ManagedThread(threading.Thread):
23
- def __init__(self, interval, target, *args, **kwargs):
23
+ def __init__(self, interval, target, logger, *args, **kwargs):
24
24
  super().__init__()
25
25
  self.target = target
26
26
  self.interval = interval
27
27
  self.args = args
28
28
  self.kwargs = kwargs
29
29
  self._stop_event = threading.Event()
30
+ self.logger = logger
30
31
 
31
32
  def run(self):
32
33
  while not self._stop_event.is_set():
@@ -34,7 +35,7 @@ class ManagedThread(threading.Thread):
34
35
  try:
35
36
  self.target(self, *self.args, **self.kwargs)
36
37
  except Exception as e:
37
- logger.info(f"error in thread {self.name}, {e}, {e.args}, {type(e).__name__}")
38
+ self.logger.info(f"error in thread {self.name}, {e}, {e.args}, {type(e).__name__}")
38
39
  elapsed_time = time.time() - stime
39
40
  if self.interval==0:
40
41
  break
@@ -7,25 +7,27 @@ import asyncio
7
7
  import talib
8
8
  import traceback
9
9
  from logger import Logger
10
- logger = Logger(__name__).get_logger()
11
10
  import gc
12
11
  from concurrent.futures import ProcessPoolExecutor
13
12
  from SupportResistance import SupportResistance
14
13
  sr = SupportResistance()
15
14
 
16
15
  class Interval:
17
- def __init__(self, symbol, intervalId, delta, data, settings):
16
+ def __init__(self, symbol, intervalId, delta, data, settings, logger):
18
17
  self.symbol = symbol
19
18
  self.intervalId = intervalId
20
19
  self.delta = delta
21
20
  self.settings=settings
22
21
  self._data = data
22
+ self.logger = logger
23
+
23
24
 
24
25
  #these signals are used to list stocks in the signals sections on the main page
25
26
  self.current_signal="HOLD"
26
27
  self.ema_open_signal="HOLD"
27
28
  self.ema_close_signal="HOLD"
28
29
  self.trendline_signal = "HOLD"
30
+ self.bos_signal = "HOLD"
29
31
  self.macd_signal="HOLD"
30
32
  self.bbm_signal="HOLD"
31
33
  self.rsi_signal="HOLD"
@@ -63,23 +65,6 @@ class Interval:
63
65
  # Get the last color and its consecutive count
64
66
  self.signal_strength = df['Consecutive'].iloc[-1]
65
67
 
66
- # Break of Strcuture
67
- if self.settings.BOS_STUDY:
68
- if df['high'].size > 1 and df['low'].size > 1 and df['close'].size > 1:
69
- high = talib.MAX(df.iloc[:-1]['high'].values, timeperiod=self.settings.BOS_PERIOD)[-1]
70
- low = talib.MIN(df.iloc[:-1]['low'].values, timeperiod=self.settings.BOS_PERIOD)[-1]
71
- close = df['close'].iloc[-1]
72
- if close > high:
73
- self.bos_signal = "BUY"
74
- elif close < low:
75
- self.bos_signal = "SELL"
76
- else:
77
- self.bos_signal = "HOLD"
78
- else:
79
- self.bos_signal = "HOLD"
80
- else:
81
- self.bos_signal = "HOLD"
82
-
83
68
  # Calculate the Moving Averages
84
69
  if self.settings.EMA_STUDY:
85
70
  df['ma_fast'] = talib.EMA(df['close'], timeperiod=self.settings.MA_FAST)
@@ -248,27 +233,47 @@ class Interval:
248
233
  # Drop RSI columns if not used
249
234
  df.drop(['rsi_fast', 'rsi_slow', 'rsi_slope', 'rsi_angle'], axis=1, inplace=True, errors='ignore')
250
235
 
236
+ # Break of Strcuture
237
+ if self.settings.BOS_STUDY:
238
+ df_sr= sr.support_resistance_bos_lines(df[['time','open', 'high', 'low', 'close']], len(df), self.settings.BOS_PERIOD)
239
+ if df_sr is not None and len(df_sr) >= 1:
240
+ df['bos_support_line'] = df_sr['support_line']
241
+ df['bos_resistance_line'] = df_sr['resistance_line']
242
+ df.fillna({'bos_support_line':0}, inplace=True)
243
+ df.fillna({'bos_resistance_line':0}, inplace=True)
244
+
245
+ if df['high'].size > 1 and df['low'].size > 1 and df['close'].size > 1:
246
+ if df['close'].iloc[-1] > df['bos_resistance_line'].iloc[-1]:
247
+ self.bos_signal = "BUY"
248
+ elif df['close'].iloc[-1] < df['bos_support_line'].iloc[-1]:
249
+ self.bos_signal = "SELL"
250
+ else:
251
+ self.bos_signal = "HOLD"
252
+ else:
253
+ self.bos_signal = "HOLD"
254
+
251
255
  #Trendline
252
256
  if self.settings.TRENDLINE_STUDY:
253
257
  loopback = len(df)
254
258
  df_sr= sr.support_resistance_trend_lines(df[['time','open', 'high', 'low', 'close']], loopback, prominence=None, distance=None if self.settings.TRENDLINE_PEAK_DISTANCE==0 else self.settings.TRENDLINE_PEAK_DISTANCE)
255
259
  if df_sr is not None and len(df_sr) >= 1:
256
- df['support_line'] = df_sr['support_line']
257
- df['resistance_line'] = df_sr['resistance_line']
258
- df.fillna({'support_line':0}, inplace=True)
259
- df.fillna({'resistance_line':0}, inplace=True)
260
+ df['trend_support_line'] = df_sr['support_line']
261
+ df['trend_resistance_line'] = df_sr['resistance_line']
262
+ df.fillna({'trend_support_line':0}, inplace=True)
263
+ df.fillna({'trend_resistance_line':0}, inplace=True)
264
+
260
265
  if df is not None and len(df) >= 2:
261
266
  if self.settings.TRENDLINE_BREAKOUT:
262
- if df['close'].iloc[-2] > df['support_line'].iloc[-2] and df['open'].iloc[-1] > df['support_line'].iloc[-1] and df['close'].iloc[-1] > df['open'].iloc[-1]:
267
+ if df['close'].iloc[-1] > df['trend_resistance_line'].iloc[-1]:
263
268
  self.trendline_signal = "BUY"
264
- elif df['close'].iloc[-2] < df['resistance_line'].iloc[-2] and df['open'].iloc[-1] < df['resistance_line'].iloc[-1] and df['close'].iloc[-1] < df['open'].iloc[-1]:
269
+ elif df['close'].iloc[-1] < df['trend_support_line'].iloc[-1]:
265
270
  self.trendline_signal = "SELL"
266
271
  else:
267
272
  self.trendline_signal = "HOLD"
268
273
  else:
269
- if df['high'].iloc[-3] >= df['support_line'].iloc[-3] and df['close'].iloc[-2] < df['support_line'].iloc[-2] and df['close'].iloc[-1] < df['open'].iloc[-1]:
274
+ if df['close'].iloc[-1] < df['trend_resistance_line'].iloc[-1] and df['close'].iloc[-2] < df['open'].iloc[-2] and df['close'].iloc[-1] < df['open'].iloc[-1]:
270
275
  self.trendline_signal = "SELL"
271
- elif df['low'].iloc[-3] <= df['resistance_line'].iloc[-3] and df['close'].iloc[-2] > df['resistance_line'].iloc[-2] and df['close'].iloc[-1] > df['open'].iloc[-1]:
276
+ elif df['close'].iloc[-1] > df['trend_support_line'].iloc[-1] and df['close'].iloc[-2] > df['open'].iloc[-2] and df['close'].iloc[-1] > df['open'].iloc[-1]:
272
277
  self.trendline_signal = "BUY"
273
278
  else:
274
279
  self.trendline_signal = "HOLD"
@@ -317,7 +322,7 @@ class Interval:
317
322
 
318
323
  # Check for BUY signal
319
324
  buy_conditions = (
320
- (self.settings.BOS_STUDY and self.bos_signal == "BUY") or
325
+ (self.settings.BOS_STUDY and self.settings.BOS_CHECK_ON_OPEN and self.bos_signal == "BUY") or
321
326
  (self.settings.EMA_STUDY and self.settings.EMA_CHECK_ON_OPEN and self.ema_open_signal == "BUY") or
322
327
  (self.settings.MACD_STUDY and self.settings.MACD_CHECK_ON_OPEN and self.macd_signal == "BUY") or
323
328
  (self.settings.BBM_STUDY and self.settings.BBM_CHECK_ON_OPEN and self.bbm_signal == "BUY") or
@@ -331,7 +336,7 @@ class Interval:
331
336
 
332
337
  # Check for SELL signal
333
338
  sell_conditions = (
334
- (self.settings.BOS_STUDY and self.bos_signal == "SELL") or
339
+ (self.settings.BOS_STUDY and self.settings.BOS_CHECK_ON_OPEN and self.bos_signal == "SELL") or
335
340
  (self.settings.EMA_STUDY and self.settings.EMA_CHECK_ON_OPEN and self.ema_open_signal == "SELL") or
336
341
  (self.settings.MACD_STUDY and self.settings.MACD_CHECK_ON_OPEN and self.macd_signal == "SELL") or
337
342
  (self.settings.BBM_STUDY and self.settings.BBM_CHECK_ON_OPEN and self.bbm_signal == "SELL") or
@@ -352,7 +357,7 @@ class Interval:
352
357
  self.current_signal = "HOLD"
353
358
  else:
354
359
  buy_conditions = (
355
- (self.settings.BOS_STUDY and self.bos_signal == "BUY")
360
+ (self.settings.BOS_STUDY and self.settings.BOS_CHECK_ON_OPEN and self.bos_signal == "BUY")
356
361
  or not self.settings.BOS_STUDY
357
362
  ) and (
358
363
  (self.settings.EMA_STUDY and self.settings.EMA_CHECK_ON_OPEN and self.ema_open_signal == "BUY")
@@ -386,7 +391,7 @@ class Interval:
386
391
 
387
392
  # Check for SELL signal
388
393
  sell_conditions = (
389
- (self.settings.BOS_STUDY and self.bos_signal == "SELL")
394
+ (self.settings.BOS_STUDY and self.settings.BOS_CHECK_ON_OPEN and self.bos_signal == "SELL")
390
395
  or not self.settings.BOS_STUDY
391
396
  ) and (
392
397
  (self.settings.EMA_STUDY and self.settings.EMA_CHECK_ON_OPEN and self.ema_open_signal == "SELL")
@@ -428,8 +433,8 @@ class Interval:
428
433
 
429
434
 
430
435
  except Exception as e:
431
- logger.info(f"Function: calculate_study, {e}, {e.args}, {type(e).__name__}")
432
- logger.info(traceback.print_exc())
436
+ self.logger.info(f"Function: calculate_study, {e}, {e.args}, {type(e).__name__}")
437
+ self.logger.info(traceback.print_exc())
433
438
  retval = df.to_dict('records')
434
439
  del df
435
440
  gc.collect()
@@ -437,10 +442,10 @@ class Interval:
437
442
 
438
443
  class Ticker:
439
444
 
440
- def __init__(self, symbol, intervalIds, settings):
445
+ def __init__(self, symbol, intervalIds, settings, logger):
441
446
  self.symbol=symbol
442
447
  self.settings=settings
443
-
448
+ self.logger=logger
444
449
  self._ts=0
445
450
  self._bid = 0.0
446
451
  self.bidcolor = ""
@@ -451,11 +456,11 @@ class Ticker:
451
456
  self._mtv=0.0
452
457
  self.intervalIds=intervalIds
453
458
  self._intervals = {
454
- '1m' : Interval(self.symbol, '1m', 60000, [], self.settings),
455
- '5m' : Interval(self.symbol, '5m', 300000, [], self.settings),
456
- '15m': Interval(self.symbol, '15m', 900000, [], self.settings),
457
- '1h' : Interval(self.symbol, '1h', 3600000, [], self.settings),
458
- '1d' : Interval(self.symbol, '1d', 86400000, [], self.settings),
459
+ '1m' : Interval(self.symbol, '1m', 60000, [], self.settings, self.logger),
460
+ '5m' : Interval(self.symbol, '5m', 300000, [], self.settings, self.logger),
461
+ '15m': Interval(self.symbol, '15m', 900000, [], self.settings, self.logger),
462
+ '1h' : Interval(self.symbol, '1h', 3600000, [], self.settings, self.logger),
463
+ '1d' : Interval(self.symbol, '1d', 86400000, [], self.settings, self.logger),
459
464
  }
460
465
  self.current_data={}
461
466
  self.trades = []
@@ -463,6 +468,8 @@ class Ticker:
463
468
  self.green="#A5DFDF"
464
469
  self.red="#FFB1C1"
465
470
 
471
+ self.logger = logger
472
+
466
473
  def update_settings(self, settings):
467
474
  self.settings=settings
468
475
  for intervalId in self.intervalIds:
@@ -580,12 +587,12 @@ class Ticker:
580
587
  intervalObj.set_data(ticks_interval)
581
588
 
582
589
  except Exception as e:
583
- logger.info(f"Function: create_bar_with_last_and_ts, {e}, {e.args}, {type(e).__name__}")
584
- logger.info(traceback.print_exc())
590
+ self.logger.info(f"Function: create_bar_with_last_and_ts, {e}, {e.args}, {type(e).__name__}")
591
+ self.logger.info(traceback.print_exc())
585
592
 
586
593
 
587
594
  class Tickers:
588
- def __init__(self, settings):
595
+ def __init__(self, settings, logger):
589
596
  self.settings=settings
590
597
 
591
598
  self._tickerObjects={}
@@ -598,13 +605,15 @@ class Tickers:
598
605
 
599
606
  self.intervalIds=['1m','5m','15m','1h','1d']
600
607
 
608
+ self.logger = logger
609
+
601
610
  def update_settings(self, settings):
602
611
  self.settings=settings
603
612
  for symbol in self._tickerObjects:
604
613
  self._tickerObjects[symbol].update_settings(settings)
605
614
 
606
615
  def add(self, symbol):
607
- ticker = Ticker(symbol, self.intervalIds, self.settings)
616
+ ticker = Ticker(symbol, self.intervalIds, self.settings, self.logger)
608
617
  self._tickerObjects[symbol]=ticker
609
618
 
610
619
  def get_tickerDict(self):
@@ -730,13 +739,13 @@ class Tickers:
730
739
  f"{period}_trend": intervalObj.current_signal,
731
740
  f"{period}_cb": intervalObj.signal_strength,
732
741
  f"{period}_barcolor": lastcandle['barcolor'],
733
- f"{period}_bos": intervalObj.bos_signal,
734
742
  f"{period}_ema_open": intervalObj.ema_open_signal,
735
743
  f"{period}_ema_open": intervalObj.ema_open_signal,
736
744
  f"{period}_ema_close": intervalObj.ema_close_signal,
737
745
  f"{period}_macd":intervalObj.macd_signal,
738
746
  f"{period}_bbm":intervalObj.bbm_signal,
739
747
  f"{period}_rsi":intervalObj.rsi_signal,
748
+ f"{period}_bos": intervalObj.bos_signal,
740
749
  f"{period}_trendline": intervalObj.trendline_signal,
741
750
  f"{period}_adx":intervalObj.adx_signal,
742
751
  f"{period}_candle_trend":intervalObj.candle_trend,
@@ -782,8 +791,8 @@ class Tickers:
782
791
  ]
783
792
  self.signaldf_filtered = df[np.any(trending_conditions, axis=0)].copy()
784
793
  except Exception as e:
785
- logger.info(f"Function: getCurrentData, {e}, {e.args}, {type(e).__name__}")
786
- logger.info(traceback.print_exc())
794
+ self.logger.info(f"Function: getCurrentData, {e}, {e.args}, {type(e).__name__}")
795
+ self.logger.info(traceback.print_exc())
787
796
  finally:
788
797
  del df, current_data
789
798
  gc.collect()
@@ -1,5 +1,6 @@
1
1
  import asyncio
2
2
  import os
3
+ import sys
3
4
  import uvicorn
4
5
  import numpy as np
5
6
  import pandas as pd
@@ -17,7 +18,6 @@ from NotificationManager import NotificationManager
17
18
  from DataFrameHtmlRenderer import DataFrameHtmlRenderer
18
19
  from config import Settings
19
20
  from logger import Logger
20
- logger = Logger(__name__).get_logger()
21
21
 
22
22
  from fastapi import FastAPI, Request, Form, WebSocket, WebSocketDisconnect, Depends, Query
23
23
  from fastapi.responses import HTMLResponse , JSONResponse, RedirectResponse, StreamingResponse
@@ -34,9 +34,24 @@ from pydantic import ValidationError
34
34
 
35
35
  import sqlite3
36
36
 
37
- ENV_FILE = ".env"
38
- CONFIG_FILE = os.path.dirname(os.path.abspath(__file__))+"/config.txt"
39
- LOG_FILE = "app.log"
37
+
38
+ global LOG_FILE
39
+ global ENV_FILE
40
+ global CONFIG_FILE
41
+ global API_KEY
42
+ global SECRET_KEY
43
+ global SECRET
44
+ global PASSWORD
45
+ global HOST
46
+ global port
47
+
48
+ env_file = sys.argv[1] if len(sys.argv) > 1 else ".env"
49
+ config_file=sys.argv[2] if len(sys.argv) > 2 else "config.txt"
50
+ port = sys.argv[3] if len(sys.argv) > 3 else "8000"
51
+
52
+ ENV_FILE = os.path.dirname(os.path.abspath(__file__))+"/"+env_file
53
+ CONFIG_FILE = os.path.dirname(os.path.abspath(__file__))+"/"+config_file
54
+ LOG_FILE = os.path.dirname(os.path.abspath(__file__))+"/"+config_file.replace(".txt", ".log")
40
55
 
41
56
  #load environment variables
42
57
  load_dotenv(ENV_FILE)
@@ -46,23 +61,24 @@ SECRET = os.getenv('SECRET')
46
61
  PASSWORD = os.getenv('PASSWORD')
47
62
  HOST = os.getenv('HOST')
48
63
 
49
- #load config variables using setting class in config.py validating using pydantic
50
- settings = Settings()
64
+ logger = Logger(__name__, LOG_FILE).get_logger()
65
+
66
+ #logger.info(f"secret: {SECRET}, api_key: {API_KEY}, secret_key: {SECRET_KEY}, password: {PASSWORD}, host: {HOST}, port: {port}, config_file: {config_file}")
51
67
 
52
68
  class bitunix():
53
- def __init__(self, password, api_key, secret_key, settings):
69
+ def __init__(self, password, api_key, secret_key, settings, logger):
54
70
  self.screen_refresh_interval =settings.SCREEN_REFRESH_INTERVAL
55
-
71
+ self.logger=logger
56
72
  self.autoTrade=settings.AUTOTRADE
57
73
 
58
74
  self.threadManager = ThreadManager()
59
- self.notifications = NotificationManager()
60
- self.bitunixApi = BitunixApi(api_key, secret_key, settings)
61
- self.bitunixSignal = BitunixSignal(api_key, secret_key, settings, self.threadManager, self.notifications, self.bitunixApi)
75
+ self.notifications = NotificationManager(logger)
76
+ self.bitunixApi = BitunixApi(api_key, secret_key, settings, self.logger)
77
+ self.bitunixSignal = BitunixSignal(api_key, secret_key, settings, self.threadManager, self.notifications, self.bitunixApi, self.logger)
62
78
 
63
79
  self.websocket_connections = set()
64
80
  self.DB = {"admin": {"password": password}}
65
-
81
+
66
82
  #sqllite database connection
67
83
  self.connection = sqlite3.connect("bitunix.db")
68
84
  #create table if not exist
@@ -104,7 +120,7 @@ app.add_middleware(
104
120
  allow_headers=["*"],
105
121
  )
106
122
  templates = Jinja2Templates(directory=os.path.dirname(os.path.abspath(__file__))+"/templates")
107
- SECRET=os.getenv('SECRET')
123
+ #SECRET=os.getenv('SECRET')
108
124
  login_manager = LoginManager(SECRET, token_url="/auth/login", use_cookie=True)
109
125
  login_manager.cookie_name = "auth_token"
110
126
 
@@ -242,6 +258,7 @@ async def wsmain(websocket):
242
258
  "profit" : bitunix.bitunixSignal.profit,
243
259
  "atctime": atctime,
244
260
  "tdctime": tdctime,
261
+ "port" : port,
245
262
  "status_messages": [] if len(notifications)==0 else notifications
246
263
  }
247
264
 
@@ -402,6 +419,8 @@ async def wscharts(websocket):
402
419
  "ema_chart": settings.EMA_CHART,
403
420
  "trendline_study": settings.TRENDLINE_STUDY,
404
421
  "trendline_chart": settings.TRENDLINE_CHART,
422
+ "bos_study": settings.BOS_STUDY,
423
+ "bos_chart": settings.BOS_CHART,
405
424
  "macd_study": settings.MACD_STUDY,
406
425
  "macd_chart": settings.MACD_CHART,
407
426
  "bbm_study": settings.BBM_STUDY,
@@ -491,6 +510,8 @@ async def wschart(websocket):
491
510
  "ema_chart": settings.EMA_CHART,
492
511
  "trendline_study": settings.TRENDLINE_STUDY,
493
512
  "trendline_chart": settings.TRENDLINE_CHART,
513
+ "bos_study": settings.BOS_STUDY,
514
+ "bos_chart": settings.BOS_CHART,
494
515
  "macd_study": settings.MACD_STUDY,
495
516
  "macd_chart": settings.MACD_CHART,
496
517
  "bbm_study": settings.BBM_STUDY,
@@ -604,14 +625,19 @@ async def stream_log_file(websocket: WebSocket):
604
625
 
605
626
  def main():
606
627
  global bitunix
607
- bitunix = bitunix(PASSWORD, API_KEY, SECRET_KEY, settings)
608
- bitunix.bitunixSignal.notifications.add_notification(f"Starting....................")
628
+ global settings
629
+
630
+ #load config variables using setting class in config.py validating using pydantic
631
+ settings = Settings()
632
+
633
+ bitunix = bitunix(PASSWORD, API_KEY, SECRET_KEY, settings, logger)
634
+ bitunix.bitunixSignal.notifications.add_notification(f"Starting auto trade on port {port} using {env_file} and {config_file} ....................")
609
635
  import uvicorn
610
636
  if settings.VERBOSE_LOGGING:
611
637
  llevel = "debug"
612
638
  else:
613
639
  llevel = "error"
614
- config1 = uvicorn.Config(app, host=HOST, port=8000, log_level=llevel, reload=False)
640
+ config1 = uvicorn.Config(app, host=HOST, port=port, log_level=llevel, reload=False)
615
641
  server = uvicorn.Server(config1)
616
642
  server.run()
617
643
 
@@ -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 sys
4
5
  import sqlite3
5
6
 
6
7
  class Settings(BaseSettings):
@@ -22,7 +23,6 @@ class Settings(BaseSettings):
22
23
  BARS: int = Field(default=100, ge=1)
23
24
 
24
25
  # Technical Indicators Parameters
25
- BOS_PERIOD: int = Field(default=20, ge=1)
26
26
  MA_FAST: int = Field(default=10, ge=1)
27
27
  MA_MEDIUM: int = Field(default=20, ge=1)
28
28
  MA_SLOW: int = Field(default=50, ge=1)
@@ -38,7 +38,12 @@ class Settings(BaseSettings):
38
38
  # Technical Indicators
39
39
  OPEN_ON_ANY_SIGNAL: bool = Field(default=True)
40
40
 
41
+ BOS_PERIOD: int = Field(default=20, ge=1)
41
42
  BOS_STUDY: bool = Field(default=True)
43
+ BOS_CHART: bool = Field(default=True)
44
+ BOS_CHECK_ON_OPEN: bool = Field(default=False)
45
+ BOS_CHECK_ON_CLOSE: bool = Field(default=False)
46
+
42
47
  EMA_CHART: bool = Field(default=True)
43
48
  EMA_STUDY: bool = Field(default=True)
44
49
  EMA_CROSSING: bool = Field(default=False)
@@ -104,7 +109,8 @@ class Settings(BaseSettings):
104
109
 
105
110
  class Config:
106
111
  # Specify the file name for loading environment variables
107
- env_file = os.path.dirname(os.path.abspath(__file__))+"/config.txt"
112
+ config_file=sys.argv[2] if len(sys.argv) > 1 else "config.txt"
113
+ env_file = os.path.dirname(os.path.abspath(__file__))+"/"+config_file
108
114
 
109
115
 
110
116
  @classmethod
@@ -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=50 * 1024 * 1024, backup_count=100):
48
+ def __init__(self, logger_name, log_file, 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.1.7
3
+ Version: 3.1.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
@@ -0,0 +1,18 @@
1
+ bitunix_automated_crypto_trading/AsyncThreadRunner.py,sha256=bNIM_1xRYQOFEsIn74EX6qVpC59-GMhhr2CeiPr_GWg,3253
2
+ bitunix_automated_crypto_trading/BitunixApi.py,sha256=udgBwbWowf7v46jVVyfs-VjeNh9ajnghTiJf70lvn5w,11269
3
+ bitunix_automated_crypto_trading/BitunixSignal.py,sha256=hZvu1FKhdHqESIUmDMMlVdnxsROnJHTMCfGSitrXNiM,70882
4
+ bitunix_automated_crypto_trading/BitunixWebSocket.py,sha256=-_7wRAZxJXG63joBq2k3TUC4qd06v7Miki-LGVtNIDk,11234
5
+ bitunix_automated_crypto_trading/DataFrameHtmlRenderer.py,sha256=Pqdzhh_nfIxFEZH9L_R5QXB8moDPbgeTGT_hmBkHWMg,2899
6
+ bitunix_automated_crypto_trading/NotificationManager.py,sha256=exs6REABBA1omTeTGuUuECzxs5dGqdyL7oI8WyxS6Xc,798
7
+ bitunix_automated_crypto_trading/SupportResistance.py,sha256=x_to4M4OHg0h8o40DXDBa4E_5io-y2Lb5qo2VzFnu_8,5765
8
+ bitunix_automated_crypto_trading/ThreadManager.py,sha256=Lw5_1EIT0m3AFSv5CIMpnjtA0DnNw2qQ6JtSpT34LyM,2349
9
+ bitunix_automated_crypto_trading/TickerManager.py,sha256=B9HlgvBLIZ2skBCEojY0M0BS5ZvZR2rw4l25LhuTt1o,42307
10
+ bitunix_automated_crypto_trading/__init__.py,sha256=1hzk6nX8NnUCr1tsq8oFq1qGCNhNwnwldWE75641Eew,78
11
+ bitunix_automated_crypto_trading/bitunix.py,sha256=wEzJpa1Gv84tYJK1iV-kQVezbn1TxJuAk-CY7kwKShM,26887
12
+ bitunix_automated_crypto_trading/config.py,sha256=cdbPFRNbDPw2mxEKL4asffVtfUcq_zM0zTVqY0o3Hg4,5300
13
+ bitunix_automated_crypto_trading/logger.py,sha256=NHnA5JZdUFkTAhB7i-1iCAwrdf1fxhDuRvJUkbKPi9Y,2923
14
+ bitunix_automated_crypto_trading-3.1.9.dist-info/METADATA,sha256=WKb5fZi1xjoXmcX8IErXwTlpVcfTSMHF7GDqAQVyovw,996
15
+ bitunix_automated_crypto_trading-3.1.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
+ bitunix_automated_crypto_trading-3.1.9.dist-info/entry_points.txt,sha256=UXREYHuSl2XYd_tOtLIq0zg3d1kX3lixX5SpN8yGBw4,82
17
+ bitunix_automated_crypto_trading-3.1.9.dist-info/top_level.txt,sha256=uyFzHUCOsp8elnG2Ovor6xXcf7dxRxY-C-Txiwix64Q,33
18
+ bitunix_automated_crypto_trading-3.1.9.dist-info/RECORD,,
@@ -1,18 +0,0 @@
1
- bitunix_automated_crypto_trading/AsyncThreadRunner.py,sha256=LJ6ny1KSCVoIbfeNypsY4aHYcbkGME9M3epQ9e8B1O8,3224
2
- bitunix_automated_crypto_trading/BitunixApi.py,sha256=W0uem1wIs1uM-jaGtXXzL_JQfnCDb7imyTZ2Tqjk8e8,11230
3
- bitunix_automated_crypto_trading/BitunixSignal.py,sha256=JIJumU3nYcO_NRS5xMqrPeisMIiy_TM0EOEA5nr44cQ,67993
4
- bitunix_automated_crypto_trading/BitunixWebSocket.py,sha256=mbuvk8UFWKgv4KLV07TgLgxLVTRJnOKuf02mLB-VoCY,11143
5
- bitunix_automated_crypto_trading/DataFrameHtmlRenderer.py,sha256=Pqdzhh_nfIxFEZH9L_R5QXB8moDPbgeTGT_hmBkHWMg,2899
6
- bitunix_automated_crypto_trading/NotificationManager.py,sha256=pqDquEe-oujD2v8B543524vo62aRMjfB4YW-3DMhVGQ,795
7
- bitunix_automated_crypto_trading/SupportResistance.py,sha256=Dx4uBC4On-GO1t2EFk02jzSmZzaMD48mUJI9rG2rgMo,4946
8
- bitunix_automated_crypto_trading/ThreadManager.py,sha256=WmYRQu8aNrgp5HwSqpBqX1WESNSEpbD2CznPFpd9HuI,2330
9
- bitunix_automated_crypto_trading/TickerManager.py,sha256=5V86dZEEFIFf2W1-qWKYuK0E9L7lxcK_TyaVAzS1OEg,41769
10
- bitunix_automated_crypto_trading/__init__.py,sha256=1hzk6nX8NnUCr1tsq8oFq1qGCNhNwnwldWE75641Eew,78
11
- bitunix_automated_crypto_trading/bitunix.py,sha256=Zyrf8P-xi7DJogX_bcc6uDW66J7FAxdS-0jbuHRPFfM,25845
12
- bitunix_automated_crypto_trading/config.py,sha256=zio989qxqU52dCdyUvudtBqVZpDCGLgypy09cECcAc0,5070
13
- bitunix_automated_crypto_trading/logger.py,sha256=tAaTQcv5r--zupk_Fhfe1USfBAzSiXzVa4QRnaOFIFY,2933
14
- bitunix_automated_crypto_trading-3.1.7.dist-info/METADATA,sha256=4lxIzSR06V9fh5oVzCHDgISGb21zE8JeV7vPbroiJnY,996
15
- bitunix_automated_crypto_trading-3.1.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
- bitunix_automated_crypto_trading-3.1.7.dist-info/entry_points.txt,sha256=UXREYHuSl2XYd_tOtLIq0zg3d1kX3lixX5SpN8yGBw4,82
17
- bitunix_automated_crypto_trading-3.1.7.dist-info/top_level.txt,sha256=uyFzHUCOsp8elnG2Ovor6xXcf7dxRxY-C-Txiwix64Q,33
18
- bitunix_automated_crypto_trading-3.1.7.dist-info/RECORD,,