bitunix-automated-crypto-trading 3.1.3__py3-none-any.whl → 3.1.6__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.
@@ -99,19 +99,23 @@ class BitunixSignal:
99
99
  self.tickerObjects.update_settings(settings)
100
100
 
101
101
  async def load_tickers(self):
102
- symbols = await self.bitunixApi.GetTickerList(float(self.settings.THRESHOLD), float(self.settings.MIN_VOLUME))
103
- self.pendingPositions= await self.bitunixApi.GetPendingPositionData()
104
- self.pendingOrders= await self.bitunixApi.GetPendingOrderData()
105
- olist=[]
106
- plist=[]
107
- if self.pendingPositions:
108
- plist = [entry['symbol'] for entry in self.pendingPositions]
109
- if self.pendingOrders['orderList']:
110
- olist = [entry['symbol'] for entry in self.pendingOrders['orderList']]
111
- newlist=olist+plist+list(set(symbols))
112
- self.tickerList=newlist[:300]
113
- self.tickerList.remove("STMXUSDT")
114
- #self.tickerList=['PIXELUSDT']
102
+ if self.settings.TICKERS=="":
103
+ symbols = await self.bitunixApi.GetTickerList(float(self.settings.THRESHOLD), float(self.settings.MIN_VOLUME))
104
+ self.pendingPositions= await self.bitunixApi.GetPendingPositionData()
105
+ self.pendingOrders= await self.bitunixApi.GetPendingOrderData()
106
+ olist=[]
107
+ plist=[]
108
+ if self.pendingPositions:
109
+ plist = [entry['symbol'] for entry in self.pendingPositions]
110
+ if self.pendingOrders['orderList']:
111
+ olist = [entry['symbol'] for entry in self.pendingOrders['orderList']]
112
+ newlist=olist+plist+list(set(symbols))
113
+ self.tickerList=newlist[:300]
114
+ self.tickerList.remove("STMXUSDT")
115
+ #self.tickerList=['POLUSDT']
116
+ else:
117
+ self.tickerList = self.settings.TICKERS.split(",")
118
+ self.tickerList = [sym.strip().upper() for sym in self.tickerList if sym.strip()]
115
119
 
116
120
  [await self.add_ticker_to_tickerObjects(sym) for sym in self.tickerList]
117
121
  self.notifications.add_notification(f"{len(self.tickerList)} ticker list loaded")
@@ -271,12 +275,7 @@ class BitunixSignal:
271
275
  # Function to add data to the last price deque
272
276
  async def StoreTickerData(self, message):
273
277
  if self.settings.USE_PUBLIC_WEBSOCKET and message:
274
- try:
275
- data = json.loads(message)
276
- if data.get('symbol') and data.get('ch') == 'ticker':
277
- await self.ticker_que.put(data)
278
- except json.JSONDecodeError as e:
279
- logger.warning(f"Failed to decode message: {message}. Error: {e}")
278
+ await self.ticker_que.put(message)
280
279
 
281
280
  # Function to process the last price deque
282
281
  async def ProcessTickerData(self):
@@ -285,11 +284,14 @@ class BitunixSignal:
285
284
  latest_data = {}
286
285
  reversed_items = await self.drain_queue(self.ticker_que)
287
286
  while reversed_items:
288
- data = reversed_items.popleft()
289
- symbol = data["symbol"]
290
- ts = data["ts"]
291
- if symbol not in latest_data or ts > latest_data[symbol]['ts']:
292
- latest_data[symbol] = {'ts': ts, 'last': float(data['data']['la'])}
287
+ message = reversed_items.popleft()
288
+ data = json.loads(message)
289
+ if data.get('symbol') and data.get('ch') == 'ticker':
290
+ symbol = data["symbol"]
291
+ ts = data["ts"]
292
+ if symbol not in latest_data or ts > latest_data[symbol]['ts']:
293
+ latest_data[symbol] = {'ts': ts, 'last': float(data['data']['la'])}
294
+ await asyncio.sleep(0.01)
293
295
  # Convert to DataFrame
294
296
  self.tickerdf = pd.DataFrame.from_dict(latest_data, orient="index")
295
297
  if not self.tickerdf.empty:
@@ -300,7 +302,7 @@ class BitunixSignal:
300
302
  except Exception as e:
301
303
  logger.info(f"Function: ProcessTickerData, {e}, {e.args}, {type(e).__name__}")
302
304
  logger.info(traceback.print_exc())
303
- await asyncio.sleep(0.5)
305
+ await asyncio.sleep(0.01)
304
306
  logger.info(f"ProcessTickerData: exitied out of the loop, exiting app")
305
307
  os._exit(1) # Exit the program
306
308
 
@@ -308,13 +310,8 @@ class BitunixSignal:
308
310
  #websocket data to update bid and ask
309
311
  async def StoreDepthData(self, message):
310
312
  if self.settings.USE_PUBLIC_WEBSOCKET and message:
311
- try:
312
- data = json.loads(message)
313
- if data.get('symbol') and data.get('ch') == 'depth_book1':
314
- await self.depth_que.put(data)
315
- except json.JSONDecodeError as e:
316
- logger.warning(f"Failed to decode message: {message}. Error: {e}")
317
-
313
+ await self.depth_que.put(message)
314
+
318
315
  # Function to process the bid, ask
319
316
  async def ProcessDepthData(self):
320
317
  while True:
@@ -322,11 +319,14 @@ class BitunixSignal:
322
319
  latest_data = {}
323
320
  reversed_items = await self.drain_queue(self.depth_que)
324
321
  while reversed_items:
325
- data = reversed_items.popleft()
326
- symbol = data["symbol"]
327
- ts = data["ts"]
328
- if symbol not in latest_data or ts > latest_data[symbol]['ts']:
329
- latest_data[symbol] = {'ts': ts, 'bid': float(data['data']['b'][0][0]), 'ask': float(data['data']['a'][0][0])}
322
+ message = reversed_items.popleft()
323
+ data = json.loads(message)
324
+ if data.get('symbol') and data.get('ch') == 'depth_book1':
325
+ symbol = data["symbol"]
326
+ ts = data["ts"]
327
+ if symbol not in latest_data or ts > latest_data[symbol]['ts']:
328
+ latest_data[symbol] = {'ts': ts, 'bid': float(data['data']['b'][0][0]), 'ask': float(data['data']['a'][0][0])}
329
+ await asyncio.sleep(0.01)
330
330
  # Convert to DataFrame
331
331
  self.depthdf = pd.DataFrame.from_dict(latest_data, orient="index")
332
332
  if not self.depthdf.empty:
@@ -335,7 +335,7 @@ class BitunixSignal:
335
335
  except Exception as e:
336
336
  logger.info(f"Function: ProcessTickerData, {e}, {e.args}, {type(e).__name__}")
337
337
  logger.info(traceback.print_exc())
338
- await asyncio.sleep(0.5)
338
+ await asyncio.sleep(0.01)
339
339
  logger.info(f"ProcessDepthData: exitied out of the loop, exiting app")
340
340
  os._exit(1) # Exit the program
341
341
 
@@ -563,12 +563,17 @@ class BitunixSignal:
563
563
  self.signaldf_filtered = self.tickerObjects.signaldf_filtered
564
564
 
565
565
  if not self.positiondf.empty and not self.signaldf_full.empty:
566
- columns=['symbol', f"{period}_trend", f"{period}_cb", f"{period}_barcolor", f"{period}_ema_open", f"{period}_ema_close", f"{period}_macd", f"{period}_bbm", f"{period}_rsi", f"{period}_candle_trend", f"{period}_open", f"{period}_close", f"{period}_high", f"{period}_low"]
566
+ columns=['symbol', f"{period}_trend", f"{period}_cb", f"{period}_barcolor",
567
+ f"{period}_bos",
568
+ f"{period}_ema_open", f"{period}_ema_close", f"{period}_macd", f"{period}_bbm", f"{period}_rsi", f"{period}_trendline", f"{period}_candle_trend", f"{period}_open", f"{period}_close", f"{period}_high", f"{period}_low"]
567
569
  columns2=["qty", "side", "unrealizedPNL", "realizedPNL", "ctime", "avgOpenPrice", "bid", "bidcolor", "last", "lastcolor", "ask", "askcolor", "charts", "bitunix", "action", "add", "reduce"]
568
570
  if set(columns).issubset(self.signaldf_full.columns) and set(columns2).issubset(self.positiondf.columns):
569
- columnOrder= ['symbol', "side", "unrealizedPNL", "realizedPNL", f"{period}_trend", f"{period}_cb", f"{period}_barcolor", f"{period}_ema_open", f"{period}_ema_close", f"{period}_macd", f"{period}_bbm", f"{period}_rsi", f"{period}_adx", f"{period}_candle_trend", f"{period}_open", f"{period}_close", f"{period}_high", f"{period}_low", "qty", "ctime", "avgOpenPrice", "bid", "bidcolor", "last", "lastcolor", "ask", "askcolor", "charts", "bitunix", "action", "add", "reduce"]
571
+ columnOrder= ['symbol', "side", "unrealizedPNL", "realizedPNL", f"{period}_trend", f"{period}_cb", f"{period}_barcolor",
572
+ f"{period}_bos",
573
+ f"{period}_ema_open", f"{period}_ema_close", f"{period}_macd", f"{period}_bbm", f"{period}_rsi", f"{period}_trendline", f"{period}_adx", f"{period}_candle_trend", f"{period}_open", f"{period}_close", f"{period}_high", f"{period}_low", "qty", "ctime", "avgOpenPrice", "bid", "bidcolor", "last", "lastcolor", "ask", "askcolor", "charts", "bitunix", "action", "add", "reduce"]
570
574
  self.positiondf2 = pd.merge(self.positiondf, self.signaldf_full[["symbol", f"{period}_open", f"{period}_close", f"{period}_high", f"{period}_low",
571
- f"{period}_ema_open", f"{period}_ema_close", f"{period}_macd", f"{period}_bbm", f"{period}_rsi", f"{period}_adx", f"{period}_candle_trend",
575
+ f"{period}_bos",
576
+ f"{period}_ema_open", f"{period}_ema_close", f"{period}_macd", f"{period}_bbm", f"{period}_rsi", f"{period}_trendline", f"{period}_adx", f"{period}_candle_trend",
572
577
  f"{period}_trend",f"{period}_cb", f"{period}_barcolor"]], left_on="symbol", right_index=True, how="left")[columnOrder]
573
578
  self.positiondfStyle= self.positiondfrenderer.render_html(self.positiondf2)
574
579
  else:
@@ -582,7 +587,8 @@ class BitunixSignal:
582
587
  # Assign to self.signaldf for HTML rendering
583
588
  self.signaldf = self.signaldf_filtered[[
584
589
  "symbol", f"{period}_trend",f"{period}_cb", f"{period}_barcolor",
585
- f"{period}_ema_open", f"{period}_ema_close", f"{period}_macd", f"{period}_bbm", f"{period}_rsi",f"{period}_adx",f"{period}_candle_trend",
590
+ f"{period}_bos",
591
+ f"{period}_ema_open", f"{period}_ema_close", f"{period}_macd", f"{period}_bbm", f"{period}_rsi", f"{period}_trendline",f"{period}_adx",f"{period}_candle_trend",
586
592
  'lastcolor', 'bidcolor', 'askcolor', 'bid', 'last', 'ask',
587
593
  f"{period}_open", f"{period}_close", f"{period}_high", f"{period}_low",
588
594
  ]].sort_values(by=[f'{period}_cb'], ascending=[False])
@@ -644,7 +650,7 @@ class BitunixSignal:
644
650
  #open position upto a max of max_auto_trades from the signal list
645
651
  df=self.signaldf.copy(deep=False)
646
652
  for index, row in df.iterrows():
647
- side = "BUY" if row[f'{period}_barcolor'] == self.green else "SELL" if row[f'{period}_barcolor'] == self.red else ""
653
+ side = "BUY" if row[f'{period}_barcolor'] == self.green and row[f'{period}_trend'] == "BUY" else "SELL" if row[f'{period}_barcolor'] == self.red and row[f'{period}_trend'] == "SELL" else ""
648
654
  if side != "":
649
655
  select = True
650
656
  self.pendingPositions = await self.bitunixApi.GetPendingPositionData({'symbol': row.symbol})
@@ -693,7 +699,7 @@ class BitunixSignal:
693
699
  total_pnl = unrealized_pnl + realized_pnl
694
700
  side=row['side']
695
701
 
696
- requiredCols=[f'{period}_open', f'{period}_close', f'{period}_high', f'{period}_low', f'{period}_ema_open', f"{period}_ema_close", f'{period}_macd', f'{period}_bbm', f'{period}_rsi', f'{period}_candle_trend', f'{period}_trend', f'{period}_cb', f'{period}_barcolor']
702
+ requiredCols=[f'{period}_open', f'{period}_close', f'{period}_high', f'{period}_low', f'{period}_ema_open', f"{period}_ema_close", f'{period}_macd', f'{period}_bbm', f'{period}_rsi', f'{period}_trendline', f'{period}_candle_trend', f'{period}_trend', f'{period}_cb', f'{period}_barcolor']
697
703
  required_cols = set(requiredCols)
698
704
 
699
705
  # Close position that fall the below criteria
@@ -879,6 +885,41 @@ class BitunixSignal:
879
885
  )
880
886
  continue
881
887
 
888
+ # TrendLine
889
+ if self.settings.TRENDLINE_STUDY and self.settings.TRENDLINE_CHECK_ON_CLOSE:
890
+ if row.side == 'BUY' and self.signaldf_full.at[row.symbol, f'{period}_trendline'] == "SELL" and total_pnl>0:
891
+ last, bid, ask, mtv = await self.GetTickerBidLastAsk(row.symbol)
892
+ price = (ask if row['side'] == "BUY" else bid if row['side'] == "SELL" else last) if bid<=last<=ask else last
893
+
894
+ self.notifications.add_notification(
895
+ f'{colors.CYAN} Closing {"long" if side=="BUY" else "short"} position due to {period} trendline for {row.symbol} with {row.qty} qty @ {price})'
896
+ )
897
+ datajs = await self.bitunixApi.PlaceOrder(
898
+ positionId=row.positionId,
899
+ ticker=row.symbol,
900
+ qty=row.qty,
901
+ price=price,
902
+ side=row.side,
903
+ tradeSide="CLOSE"
904
+ )
905
+ continue
906
+
907
+ if row.side == 'SELL' and self.signaldf_full.at[row.symbol, f'{period}_trendline'] == "BUY" and total_pnl>0:
908
+ last, bid, ask, mtv = await self.GetTickerBidLastAsk(row.symbol)
909
+ price = (ask if row['side'] == "BUY" else bid if row['side'] == "SELL" else last) if bid<=last<=ask else last
910
+ self.notifications.add_notification(
911
+ f'{colors.CYAN} Closing {"long" if side=="BUY" else "short"} position due to {period} trendline for {row.symbol} with {row.qty} qty @ {price})'
912
+ )
913
+ datajs = await self.bitunixApi.PlaceOrder(
914
+ positionId=row.positionId,
915
+ ticker=row.symbol,
916
+ qty=row.qty,
917
+ price=price,
918
+ side=row.side,
919
+ tradeSide="CLOSE"
920
+ )
921
+ continue
922
+
882
923
  # Close on weak trend after open
883
924
  if self.settings.ADX_STUDY and self.settings.ADX_CHECK_ON_CLOSE:
884
925
  if row.side == 'BUY' and self.signaldf_full.at[row.symbol, f'{period}_adx'] == "WEAK":
@@ -914,8 +955,8 @@ class BitunixSignal:
914
955
  continue
915
956
 
916
957
  # candle reversed
917
- if self.settings.CANDLE_TREND_STUDY and self.settings.CANDLE_TREND_CHECK_ON_CLOSE:
918
- if row.side == 'BUY' and self.signaldf_full.at[row.symbol, f'{period}_barcolor'] == self.red and self.signaldf_full.at[row.symbol, f'{period}_candle_trend'] == "BEARISH":
958
+ if self.settings.CANDLE_TREND_STUDY and self.settings.CANDLE_TREND_REVERSAL_CHECK_ON_CLOSE:
959
+ if row.side == 'BUY' and self.signaldf_full.at[row.symbol, f'{period}_barcolor'] == self.red and self.signaldf_full.at[row.symbol, f'{period}_cb'] > 1 and self.signaldf_full.at[row.symbol, f'{period}_candle_trend'] == "BULLISH":
919
960
  last, bid, ask, mtv = await self.GetTickerBidLastAsk(row.symbol)
920
961
  price = (ask if row['side'] == "BUY" else bid if row['side'] == "SELL" else last) if bid<=last<=ask else last
921
962
 
@@ -932,7 +973,7 @@ class BitunixSignal:
932
973
  )
933
974
  continue
934
975
 
935
- if row.side == 'SELL' and self.signaldf_full.at[row.symbol, f'{period}_barcolor'] == self.green and self.signaldf_full.at[row.symbol, f'{period}_candle_trend'] == "BULLISH":
976
+ if row.side == 'SELL' and self.signaldf_full.at[row.symbol, f'{period}_barcolor'] == self.green and self.signaldf_full.at[row.symbol, f'{period}_cb'] > 1 and self.signaldf_full.at[row.symbol, f'{period}_candle_trend'] == "BULLISH":
936
977
  last, bid, ask, mtv = await self.GetTickerBidLastAsk(row.symbol)
937
978
  price = (ask if row['side'] == "BUY" else bid if row['side'] == "SELL" else last) if bid<=last<=ask else last
938
979
 
@@ -951,7 +992,7 @@ class BitunixSignal:
951
992
 
952
993
  await asyncio.sleep(0)
953
994
 
954
- self.lastAutoTradeTime = time.time()
995
+ self.lastAutoTradeTime = time.time()
955
996
  except Exception as e:
956
997
  stack = traceback.extract_stack()
957
998
  function_name = stack[-1].name
@@ -0,0 +1,89 @@
1
+ import pandas as pd
2
+ import numpy as np
3
+ from scipy.signal import find_peaks
4
+
5
+ class SupportResistance:
6
+ import pandas as pd
7
+ import numpy as np
8
+
9
+ class SupportResistance:
10
+ def _find_peaks(self, prices, prominence=None, distance=None):
11
+ """Finds local peaks using scipy.signal.find_peaks."""
12
+ peaks, _ = find_peaks(prices, prominence=prominence, distance=distance)
13
+ return peaks
14
+
15
+ def _find_troughs(self, prices, prominence=None, distance=None):
16
+ """Finds local troughs by finding peaks in the inverted prices."""
17
+ troughs, _ = find_peaks(-prices, prominence=prominence, distance=distance)
18
+ return troughs
19
+
20
+ def _get_n_most_recent_prominent(self, indices, prices, n=2):
21
+ """Gets the indices of the n most recent prominent peaks or troughs."""
22
+ if not indices.size:
23
+ return np.array([])
24
+ # Sort indices by time (which is the index itself in our case) in descending order
25
+ sorted_indices_by_time = np.sort(indices)[::-1]
26
+ return sorted_indices_by_time[:n]
27
+
28
+ def _get_line(self, index1, price1, index2, price2, length):
29
+ """Calculates the line equation (y = mx + c)."""
30
+ if index1 == index2:
31
+ return np.full(length, price1) # Horizontal line
32
+ slope = (price2 - price1) / (index2 - index1)
33
+ intercept = price1 - slope * index1
34
+ return slope * np.arange(length) + intercept
35
+
36
+ def support_resistance_trend_lines(self, data, lookback=20, prominence=None, distance=None):
37
+ if data is None or len(data) < lookback:
38
+ return pd.DataFrame()
39
+ try:
40
+ recent_data = data.iloc[-lookback:].copy()
41
+ high_prices = recent_data['high'].values
42
+ low_prices = recent_data['low'].values
43
+ ts = recent_data['time'].values
44
+
45
+ # Find all local peaks and troughs
46
+ 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
+ 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
+
58
+ support_line = np.full(lookback, np.nan)
59
+ resistance_line = np.full(lookback, np.nan)
60
+
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
80
+
81
+ results_df = pd.DataFrame({
82
+ 'time': ts,
83
+ 'support_line': support_line,
84
+ 'resistance_line': resistance_line
85
+ })
86
+ return results_df
87
+ except Exception as e:
88
+ print(f"An error occurred: {e}")
89
+ return pd.DataFrame()
@@ -1,5 +1,7 @@
1
1
  import pandas as pd
2
- #pd.set_option('future.no_silent_downcasting', True)
2
+ pd.set_option('future.no_silent_downcasting', True)
3
+ import warnings
4
+ warnings.filterwarnings("ignore", category=RuntimeWarning)
3
5
  import numpy as np
4
6
  import asyncio
5
7
  import talib
@@ -8,6 +10,8 @@ from logger import Logger
8
10
  logger = Logger(__name__).get_logger()
9
11
  import gc
10
12
  from concurrent.futures import ProcessPoolExecutor
13
+ from SupportResistance import SupportResistance
14
+ sr = SupportResistance()
11
15
 
12
16
  class Interval:
13
17
  def __init__(self, symbol, intervalId, delta, data, settings):
@@ -21,6 +25,7 @@ class Interval:
21
25
  self.current_signal="HOLD"
22
26
  self.ema_open_signal="HOLD"
23
27
  self.ema_close_signal="HOLD"
28
+ self.trendline_signal = "HOLD"
24
29
  self.macd_signal="HOLD"
25
30
  self.bbm_signal="HOLD"
26
31
  self.rsi_signal="HOLD"
@@ -39,51 +44,13 @@ class Interval:
39
44
  study = self.calculate_study(new_value)
40
45
  self._data = study
41
46
 
42
- def support_resistance_trend_lines(self, data, lookback=20):
43
- recent_data = data[-lookback:]
44
- high_prices = recent_data['high'].values
45
- low_prices = recent_data['low'].values
46
- candle_indices = np.arange(lookback)
47
- ts = recent_data['time']
48
-
49
- # 1. Identify highest high and lowest low candle
50
- highest_high_index = np.argmax(high_prices)
51
- lowest_low_index = np.argmin(low_prices)
52
- highest_high_price = high_prices[highest_high_index]
53
- lowest_low_price = low_prices[lowest_low_index]
54
-
55
- # 2. Top line: Tilt down from highest high to touch a subsequent high (if any)
56
- slope_top = -float('inf') # Initialize with a very steep negative slope
57
- intercept_top = highest_high_price - slope_top * highest_high_index
58
-
59
- for i in range(highest_high_index + 1, lookback):
60
- current_slope = (high_prices[i] - highest_high_price) / (i - highest_high_index) if (i - highest_high_index) != 0 else 0
61
- if current_slope > slope_top:
62
- slope_top = current_slope
63
- intercept_top = highest_high_price - slope_top * highest_high_index
64
-
65
- top_line = slope_top * candle_indices + intercept_top
66
-
67
- # 3. Bottom line: Tilt up from lowest low to touch a subsequent low (if any)
68
- slope_bottom = float('inf') # Initialize with a very steep positive slope
69
- intercept_bottom = lowest_low_price - slope_bottom * lowest_low_index
70
-
71
- for i in range(lowest_low_index + 1, lookback):
72
- current_slope = (low_prices[i] - lowest_low_price) / (i - lowest_low_index) if (i - lowest_low_index) != 0 else 0
73
- if current_slope < slope_bottom:
74
- slope_bottom = current_slope
75
- intercept_bottom = lowest_low_price - slope_bottom * lowest_low_index
76
-
77
- bottom_line = slope_bottom * candle_indices + intercept_bottom
78
-
79
- results_df = pd.DataFrame({
80
- 'time': ts,
81
- 'top_line': top_line,
82
- 'bottom_line': bottom_line
83
- })
84
-
85
- return results_df
86
-
47
+ def get_line(self, lookback, candle_indices, index1, price1, index2, price2):
48
+ if index1 == index2:
49
+ return np.full(lookback, price1) # Horizontal line
50
+ slope = (price2 - price1) / (index2 - index1)
51
+ intercept = price1 - slope * index1
52
+ return slope * candle_indices + intercept
53
+
87
54
  def calculate_study(self, new_value):
88
55
  df = pd.DataFrame(new_value)
89
56
  if not df.empty: #and df.shape[0] >= int(self.settings.BARS):
@@ -96,6 +63,20 @@ class Interval:
96
63
  # Get the last color and its consecutive count
97
64
  self.signal_strength = df['Consecutive'].iloc[-1]
98
65
 
66
+ # Break of Strcuture
67
+ if self.settings.BOS_STUDY:
68
+ high = df.iloc[:-1]['high'].max()
69
+ low = df.iloc[:-1]['low'].min()
70
+ close = df['close'].iloc[-1]
71
+ if close > high:
72
+ self.bos_signal = "BUY"
73
+ elif close < low:
74
+ self.bos_signal = "SELL"
75
+ else:
76
+ self.bos_signal = "HOLD"
77
+ else:
78
+ self.bos_signal = "HOLD"
79
+
99
80
  # Calculate the Moving Averages
100
81
  if self.settings.EMA_STUDY:
101
82
  df['ma_fast'] = talib.EMA(df['close'], timeperiod=self.settings.MA_FAST)
@@ -149,6 +130,14 @@ class Interval:
149
130
  else:
150
131
  self.ema_open_signal = "HOLD"
151
132
  self.ema_close_signal = "HOLD"
133
+
134
+ if self.settings.EMA_CLOSE_ON_FAST_MEDIUM:
135
+ if df['close'].iloc[-1] > df['ma_fast'].iloc[-1] and df['ma_fast'].iloc[-1] > df['ma_medium'].iloc[-1]:
136
+ self.ema_close_signal = "BUY"
137
+ elif df['close'].iloc[-1] < df['ma_fast'].iloc[-1] and df['ma_fast'].iloc[-1] < df['ma_medium'].iloc[-1]:
138
+ self.ema_close_signal = "SELL"
139
+ else:
140
+ self.ema_close_signal = "HOLD"
152
141
  else:
153
142
  # Drop EMA columns if not used
154
143
  df.drop(['ma_fast', 'ma_medium', 'ma_slow', 'ma_slope', 'ma_angle'], axis=1, inplace=True, errors='ignore')
@@ -258,14 +247,28 @@ class Interval:
258
247
 
259
248
  #Trendline
260
249
  if self.settings.TRENDLINE_STUDY:
261
- lookback = self.settings.TRENDLINE_LOOKBACK
262
- trend_df = self.support_resistance_trend_lines(df, lookback)
263
-
264
- df['support_line'] = trend_df['top_line']
265
- df.fillna({'support_line':0}, inplace=True)
266
-
267
- df['resistance_line'] = trend_df['bottom_line']
268
- df.fillna({'resistance_line':0}, inplace=True)
250
+ loopback = len(df)
251
+ 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)
252
+ if df_sr is not None and len(df_sr) >= 1:
253
+ df['support_line'] = df_sr['support_line']
254
+ df['resistance_line'] = df_sr['resistance_line']
255
+ df.fillna({'support_line':0}, inplace=True)
256
+ df.fillna({'resistance_line':0}, inplace=True)
257
+ if df is not None and len(df) >= 2:
258
+ if self.settings.TRENDLINE_BREAKOUT:
259
+ 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]:
260
+ self.trendline_signal = "BUY"
261
+ 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]:
262
+ self.trendline_signal = "SELL"
263
+ else:
264
+ self.trendline_signal = "HOLD"
265
+ else:
266
+ 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]:
267
+ self.trendline_signal = "SELL"
268
+ 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]:
269
+ self.trendline_signal = "BUY"
270
+ else:
271
+ self.trendline_signal = "HOLD"
269
272
 
270
273
  # Calculate the ADX
271
274
  if self.settings.ADX_STUDY:
@@ -311,10 +314,12 @@ class Interval:
311
314
 
312
315
  # Check for BUY signal
313
316
  buy_conditions = (
317
+ (self.settings.BOS_STUDY and self.bos_signal == "BUY") or
314
318
  (self.settings.EMA_STUDY and self.settings.EMA_CHECK_ON_OPEN and self.ema_open_signal == "BUY") or
315
319
  (self.settings.MACD_STUDY and self.settings.MACD_CHECK_ON_OPEN and self.macd_signal == "BUY") or
316
320
  (self.settings.BBM_STUDY and self.settings.BBM_CHECK_ON_OPEN and self.bbm_signal == "BUY") or
317
- (self.settings.RSI_STUDY and self.settings.RSI_CHECK_ON_OPEN and self.rsi_signal == "BUY")
321
+ (self.settings.RSI_STUDY and self.settings.RSI_CHECK_ON_OPEN and self.rsi_signal == "BUY") or
322
+ (self.settings.TRENDLINE_STUDY and self.settings.TRENDLINE_CHECK_ON_OPEN and self.trendline_signal == "BUY")
318
323
  )
319
324
  additional_buy_conditions = (
320
325
  (not self.settings.ADX_STUDY or not self.settings.ADX_CHECK_ON_OPEN or self.adx_signal == "STRONG") or
@@ -323,10 +328,12 @@ class Interval:
323
328
 
324
329
  # Check for SELL signal
325
330
  sell_conditions = (
331
+ (self.settings.BOS_STUDY and self.bos_signal == "SELL") or
326
332
  (self.settings.EMA_STUDY and self.settings.EMA_CHECK_ON_OPEN and self.ema_open_signal == "SELL") or
327
333
  (self.settings.MACD_STUDY and self.settings.MACD_CHECK_ON_OPEN and self.macd_signal == "SELL") or
328
334
  (self.settings.BBM_STUDY and self.settings.BBM_CHECK_ON_OPEN and self.bbm_signal == "SELL") or
329
- (self.settings.RSI_STUDY and self.settings.RSI_CHECK_ON_OPEN and self.rsi_signal == "SELL")
335
+ (self.settings.RSI_STUDY and self.settings.RSI_CHECK_ON_OPEN and self.rsi_signal == "SELL") or
336
+ (self.settings.TRENDLINE_STUDY and self.settings.TRENDLINE_CHECK_ON_OPEN and self.trendline_signal == "SELL")
330
337
  )
331
338
  additional_sell_conditions = (
332
339
  (self.settings.ADX_STUDY and self.settings.ADX_CHECK_ON_OPEN and self.adx_signal == "STRONG") or
@@ -341,16 +348,34 @@ class Interval:
341
348
  else:
342
349
  self.current_signal = "HOLD"
343
350
  else:
344
- # If EMA is enabled and crossing and MACD is enabled and crossing and BBM is enabled and crossing and RSI is enbabled and crossing
345
- # and
346
- # ADX is enabled and strong and candle trend is enabled and bullish
347
- # then BUY or SELL
348
351
  buy_conditions = (
349
- (not self.settings.EMA_STUDY or not self.settings.EMA_CHECK_ON_OPEN or self.ema_open_signal == "BUY") and
350
- (not self.settings.MACD_STUDY or not self.settings.MACD_CHECK_ON_OPEN or self.macd_signal == "BUY") and
351
- (not self.settings.BBM_STUDY or not self.settings.BBM_CHECK_ON_OPEN or self.bbm_signal == "BUY") and
352
- (not self.settings.RSI_STUDY or not self.settings.RSI_CHECK_ON_OPEN or self.rsi_signal == "BUY")
352
+ (self.settings.BOS_STUDY and self.bos_signal == "BUY")
353
+ or not self.settings.BOS_STUDY
354
+ ) and (
355
+ (self.settings.EMA_STUDY and self.settings.EMA_CHECK_ON_OPEN and self.ema_open_signal == "BUY")
356
+ or not self.settings.EMA_STUDY or not self.settings.EMA_CHECK_ON_OPEN
357
+ ) and (
358
+ (self.settings.MACD_STUDY and self.settings.MACD_CHECK_ON_OPEN and self.macd_signal == "BUY")
359
+ or not self.settings.MACD_STUDY or not self.settings.MACD_CHECK_ON_OPEN
360
+ ) and (
361
+ (self.settings.BBM_STUDY and self.settings.BBM_CHECK_ON_OPEN and self.bbm_signal == "BUY")
362
+ or not self.settings.BBM_STUDY or not self.settings.BBM_CHECK_ON_OPEN
363
+ ) and (
364
+ (self.settings.RSI_STUDY and self.settings.RSI_CHECK_ON_OPEN and self.rsi_signal == "BUY")
365
+ or not self.settings.RSI_STUDY or not self.settings.RSI_CHECK_ON_OPEN
366
+ ) and (
367
+ (self.settings.TRENDLINE_STUDY and self.settings.TRENDLINE_CHECK_ON_OPEN and self.trendline_signal == "BUY")
368
+ or not self.settings.TRENDLINE_STUDY or not self.settings.TRENDLINE_CHECK_ON_OPEN
353
369
  )
370
+ if not any([
371
+ self.settings.EMA_STUDY, self.settings.MACD_STUDY, self.settings.BBM_STUDY,
372
+ self.settings.RSI_STUDY, self.settings.TRENDLINE_STUDY
373
+ ]) and not any([
374
+ self.settings.EMA_CHECK_ON_OPEN, self.settings.MACD_CHECK_ON_OPEN, self.settings.BBM_CHECK_ON_OPEN,
375
+ self.settings.RSI_CHECK_ON_OPEN, self.settings.TRENDLINE_CHECK_ON_OPEN
376
+ ]):
377
+ buy_conditions = False
378
+
354
379
  additional_buy_conditions = (
355
380
  (not self.settings.ADX_STUDY or not self.settings.ADX_CHECK_ON_OPEN or self.adx_signal == "STRONG") and
356
381
  (not self.settings.CANDLE_TREND_STUDY or not self.settings.CANDLE_TREND_CHECK_ON_OPEN or self.candle_trend == "BULLISH")
@@ -358,14 +383,36 @@ class Interval:
358
383
 
359
384
  # Check for SELL signal
360
385
  sell_conditions = (
361
- (not self.settings.EMA_STUDY or not self.settings.EMA_CHECK_ON_OPEN or self.ema_open_signal == "SELL") and
362
- (not self.settings.MACD_STUDY or not self.settings.MACD_CHECK_ON_OPEN or self.macd_signal == "SELL") and
363
- (not self.settings.BBM_STUDY or not self.settings.BBM_CHECK_ON_OPEN or self.bbm_signal == "SELL") and
364
- (not self.settings.RSI_STUDY or not self.settings.RSI_CHECK_ON_OPEN or self.rsi_signal == "SELL")
386
+ (self.settings.BOS_STUDY and self.bos_signal == "SELL")
387
+ or not self.settings.BOS_STUDY
388
+ ) and (
389
+ (self.settings.EMA_STUDY and self.settings.EMA_CHECK_ON_OPEN and self.ema_open_signal == "SELL")
390
+ or not self.settings.EMA_STUDY or not self.settings.EMA_CHECK_ON_OPEN
391
+ ) and (
392
+ (self.settings.MACD_STUDY and self.settings.MACD_CHECK_ON_OPEN and self.macd_signal == "SELL")
393
+ or not self.settings.MACD_STUDY or not self.settings.MACD_CHECK_ON_OPEN
394
+ ) and (
395
+ (self.settings.BBM_STUDY and self.settings.BBM_CHECK_ON_OPEN and self.bbm_signal == "SELL")
396
+ or not self.settings.BBM_STUDY or not self.settings.BBM_CHECK_ON_OPEN
397
+ ) and (
398
+ (self.settings.RSI_STUDY and self.settings.RSI_CHECK_ON_OPEN and self.rsi_signal == "SELL")
399
+ or not self.settings.RSI_STUDY or not self.settings.RSI_CHECK_ON_OPEN
400
+ ) and (
401
+ (self.settings.TRENDLINE_STUDY and self.settings.TRENDLINE_CHECK_ON_OPEN and self.trendline_signal == "SELL")
402
+ or not self.settings.TRENDLINE_STUDY or not self.settings.TRENDLINE_CHECK_ON_OPEN
365
403
  )
404
+ if not any([
405
+ self.settings.EMA_STUDY, self.settings.MACD_STUDY, self.settings.BBM_STUDY,
406
+ self.settings.RSI_STUDY, self.settings.TRENDLINE_STUDY
407
+ ]) and not any([
408
+ self.settings.EMA_CHECK_ON_OPEN, self.settings.MACD_CHECK_ON_OPEN, self.settings.BBM_CHECK_ON_OPEN,
409
+ self.settings.RSI_CHECK_ON_OPEN, self.settings.TRENDLINE_CHECK_ON_OPEN
410
+ ]):
411
+ sell_conditions = False
412
+
366
413
  additional_sell_conditions = (
367
- (not self.settings.ADX_STUDY or not self.settings.ADX_CHECK_ON_OPEN or self.adx_signal == "STRONG") and
368
- (not self.settings.CANDLE_TREND_STUDY or not self.settings.CANDLE_TREND_CHECK_ON_OPEN or self.candle_trend == "BEARISH")
414
+ (self.settings.ADX_STUDY and self.settings.ADX_CHECK_ON_OPEN and self.adx_signal == "STRONG") or
415
+ (self.settings.CANDLE_TREND_STUDY and self.settings.CANDLE_TREND_CHECK_ON_OPEN or self.candle_trend == "BEARISH")
369
416
  )
370
417
 
371
418
  # Determine current signal
@@ -379,7 +426,7 @@ class Interval:
379
426
 
380
427
  except Exception as e:
381
428
  logger.info(f"Function: calculate_study, {e}, {e.args}, {type(e).__name__}")
382
- logger.info(traceback.logger.info_exc())
429
+ logger.info(traceback.print_exc())
383
430
  retval = df.to_dict('records')
384
431
  del df
385
432
  gc.collect()
@@ -531,7 +578,7 @@ class Ticker:
531
578
 
532
579
  except Exception as e:
533
580
  logger.info(f"Function: create_bar_with_last_and_ts, {e}, {e.args}, {type(e).__name__}")
534
- logger.info(traceback.logger.info_exc())
581
+ logger.info(traceback.print_exc())
535
582
 
536
583
 
537
584
  class Tickers:
@@ -639,14 +686,16 @@ class Tickers:
639
686
  results = executor.map(self.process_ticker_candle, tuples_list, chunksize=10)
640
687
  for args, result in zip(tuples_list, results):
641
688
  for intervalId, interval in result[3].items():
642
- if not interval._data is None:
689
+ if not interval._data is None and len(interval._data) >= self.settings.BARS:
643
690
  args[0].get_interval_ticks(intervalId)._data = interval._data
644
691
  args[0].get_interval_ticks(intervalId).current_signal = interval.current_signal
692
+ args[0].get_interval_ticks(intervalId).bos_signal = interval.bos_signal
645
693
  args[0].get_interval_ticks(intervalId).ema_open_signal = interval.ema_open_signal
646
694
  args[0].get_interval_ticks(intervalId).ema_close_signal = interval.ema_close_signal
647
695
  args[0].get_interval_ticks(intervalId).macd_signal = interval.macd_signal
648
696
  args[0].get_interval_ticks(intervalId).bbm_signal = interval.bbm_signal
649
697
  args[0].get_interval_ticks(intervalId).rsi_signal = interval.rsi_signal
698
+ args[0].get_interval_ticks(intervalId).trendline_signal, = interval.trendline_signal,
650
699
  args[0].get_interval_ticks(intervalId).candle_trend = interval.candle_trend
651
700
  args[0].get_interval_ticks(intervalId).adx_signal = interval.adx_signal
652
701
  args[0].get_interval_ticks(intervalId).signal_strength = interval.signal_strength
@@ -678,11 +727,14 @@ class Tickers:
678
727
  f"{period}_trend": intervalObj.current_signal,
679
728
  f"{period}_cb": intervalObj.signal_strength,
680
729
  f"{period}_barcolor": lastcandle['barcolor'],
730
+ f"{period}_bos": intervalObj.bos_signal,
731
+ f"{period}_ema_open": intervalObj.ema_open_signal,
681
732
  f"{period}_ema_open": intervalObj.ema_open_signal,
682
733
  f"{period}_ema_close": intervalObj.ema_close_signal,
683
734
  f"{period}_macd":intervalObj.macd_signal,
684
735
  f"{period}_bbm":intervalObj.bbm_signal,
685
736
  f"{period}_rsi":intervalObj.rsi_signal,
737
+ f"{period}_trendline": intervalObj.trendline_signal,
686
738
  f"{period}_adx":intervalObj.adx_signal,
687
739
  f"{period}_candle_trend":intervalObj.candle_trend,
688
740
  'bid' : bid,
@@ -704,7 +756,8 @@ class Tickers:
704
756
  'lastcolor': "", 'bidcolor': "", 'askcolor': "", 'bid': 0.0, 'ask': 0.0, 'last': 0.0,
705
757
  f"{period}_cb":0, f"{period}_barcolor": "", f"{period}_trend": "",
706
758
  f"{period}_open": 0.0, f"{period}_close": 0.0, f"{period}_high": 0.0, f"{period}_low": 0.0,
707
- f"{period}_ema": "", f"{period}_macd": "", f"{period}_bbm": "", f"{period}_rsi": "", f"{period}_candle_trend": "", f"{period}_adx": ""
759
+ f"{period}_bos": "",
760
+ f"{period}_ema": "", f"{period}_macd": "", f"{period}_bbm": "", f"{period}_rsi": "", f"{period}_trendline": "", f"{period}_candle_trend": "", f"{period}_adx": ""
708
761
  }
709
762
  df.fillna(fill_values, inplace=True)
710
763
  df.set_index("symbol", inplace=True, drop=False)
@@ -713,19 +766,21 @@ class Tickers:
713
766
  trending_conditions = [
714
767
  (
715
768
  (df[f'{period}_trend']=='BUY') &
716
- (df[f'{period}_cb']>1) &
717
- (df[f'{period}_barcolor']==self.green)
769
+ (df[f'{period}_cb']>1)
770
+ #&
771
+ #(df[f'{period}_barcolor']==self.green)
718
772
  ),
719
773
  (
720
774
  (df[f'{period}_trend']=='SELL') &
721
- (df[f'{period}_cb']>1) &
722
- (df[f'{period}_barcolor']==self.red)
775
+ (df[f'{period}_cb']>1)
776
+ #&
777
+ #(df[f'{period}_barcolor']==self.red)
723
778
  )
724
779
  ]
725
780
  self.signaldf_filtered = df[np.any(trending_conditions, axis=0)].copy()
726
781
  except Exception as e:
727
782
  logger.info(f"Function: getCurrentData, {e}, {e.args}, {type(e).__name__}")
728
- logger.info(traceback.logger.info_exc())
783
+ logger.info(traceback.print_exc())
729
784
  finally:
730
785
  del df, current_data
731
786
  gc.collect()
@@ -6,11 +6,14 @@ import sqlite3
6
6
  class Settings(BaseSettings):
7
7
  # Start autotrading on start
8
8
  AUTOTRADE: bool = Field(default=False)
9
+
10
+ #Ticker list
11
+ TICKERS:str = Field(default="")
12
+ THRESHOLD: float = Field(default=5.0, ge=0.0)
13
+ MIN_VOLUME: int = Field(default=10_000_000, ge=0)
9
14
 
10
15
  # Trading Parameters
11
16
  LEVERAGE: int = Field(default=20, ge=1, le=100)
12
- THRESHOLD: float = Field(default=5.0, ge=0.0)
13
- MIN_VOLUME: int = Field(default=10_000_000, ge=0)
14
17
  ORDER_AMOUNT_PERCENTAGE: float = Field(default=0.01, ge=0.0, le=100)
15
18
  MAX_AUTO_TRADES: int = Field(default=10, ge=0)
16
19
  PROFIT_AMOUNT: float = Field(default=0.25, ge=0.0)
@@ -34,6 +37,7 @@ class Settings(BaseSettings):
34
37
  # Technical Indicators
35
38
  OPEN_ON_ANY_SIGNAL: bool = Field(default=True)
36
39
 
40
+ BOS_STUDY: bool = Field(default=True)
37
41
  EMA_CHART: bool = Field(default=True)
38
42
  EMA_STUDY: bool = Field(default=True)
39
43
  EMA_CROSSING: bool = Field(default=False)
@@ -59,9 +63,10 @@ class Settings(BaseSettings):
59
63
  RSI_CHECK_ON_OPEN: bool = Field(default=False)
60
64
  RSI_CHECK_ON_CLOSE: bool = Field(default=False)
61
65
 
66
+ TRENDLINE_PEAK_DISTANCE: int = Field(default=1, ge=0, le=30)
62
67
  TRENDLINE_CHART: bool = Field(default=True)
63
68
  TRENDLINE_STUDY: bool = Field(default=True)
64
- TRENDLINE_LOOKBACK: int = Field(default=20, ge=10, le=100)
69
+ TRENDLINE_BREAKOUT: bool = Field(default=False)
65
70
  TRENDLINE_CHECK_ON_OPEN: bool = Field(default=False)
66
71
  TRENDLINE_CHECK_ON_CLOSE: bool = Field(default=False)
67
72
 
@@ -71,7 +76,7 @@ class Settings(BaseSettings):
71
76
 
72
77
  CANDLE_TREND_STUDY: bool = Field(default=True)
73
78
  CANDLE_TREND_CHECK_ON_OPEN: bool = Field(default=False)
74
- CANDLE_TREND_CHECK_ON_CLOSE: bool = Field(default=False)
79
+ CANDLE_TREND_REVERSAL_CHECK_ON_CLOSE: bool = Field(default=False)
75
80
 
76
81
  # Time Intervals
77
82
  SCREEN_REFRESH_INTERVAL: int = Field(default=1, ge=1)
@@ -100,6 +105,7 @@ class Settings(BaseSettings):
100
105
  # Specify the file name for loading environment variables
101
106
  env_file = os.path.dirname(os.path.abspath(__file__))+"/config.txt"
102
107
 
108
+
103
109
  @classmethod
104
110
  def load_from_db(cls, db_path: str, table_name: str = "settings"):
105
111
  # Connect to SQLite database
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bitunix_automated_crypto_trading
3
- Version: 3.1.3
3
+ Version: 3.1.6
4
4
  Summary: Bitunix Futures Auto Trading Platform
5
5
  Home-page: https://github.com/tcj2001/bitunix-automated-crypto-trading
6
6
  Author: tcj2001
@@ -1,17 +1,18 @@
1
1
  bitunix_automated_crypto_trading/AsyncThreadRunner.py,sha256=LJ6ny1KSCVoIbfeNypsY4aHYcbkGME9M3epQ9e8B1O8,3224
2
2
  bitunix_automated_crypto_trading/BitunixApi.py,sha256=W0uem1wIs1uM-jaGtXXzL_JQfnCDb7imyTZ2Tqjk8e8,11230
3
- bitunix_automated_crypto_trading/BitunixSignal.py,sha256=17_VPwagjax-vFJ-A20cMApcyBtPCGdzis8EhAVoNrk,64734
3
+ bitunix_automated_crypto_trading/BitunixSignal.py,sha256=JIJumU3nYcO_NRS5xMqrPeisMIiy_TM0EOEA5nr44cQ,67993
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
+ bitunix_automated_crypto_trading/SupportResistance.py,sha256=Dx4uBC4On-GO1t2EFk02jzSmZzaMD48mUJI9rG2rgMo,4946
7
8
  bitunix_automated_crypto_trading/ThreadManager.py,sha256=WmYRQu8aNrgp5HwSqpBqX1WESNSEpbD2CznPFpd9HuI,2330
8
- bitunix_automated_crypto_trading/TickerManager.py,sha256=W4UuW1RyU_4w_6IV0eDOlJ0PUFNorQOY9Ust4bI-69s,36785
9
+ bitunix_automated_crypto_trading/TickerManager.py,sha256=n2GyvfH2YMuZ31Q_IcLd4U3ZOEIOLhLwMIR8iI5Gp5A,41460
9
10
  bitunix_automated_crypto_trading/__init__.py,sha256=1hzk6nX8NnUCr1tsq8oFq1qGCNhNwnwldWE75641Eew,78
10
11
  bitunix_automated_crypto_trading/bitunix.py,sha256=Zyrf8P-xi7DJogX_bcc6uDW66J7FAxdS-0jbuHRPFfM,25845
11
- bitunix_automated_crypto_trading/config.py,sha256=Jj1AXFU9c3RHKRnxnAS7lJ6UIyfl6qRByoVbOVrEz1k,4857
12
+ bitunix_automated_crypto_trading/config.py,sha256=aBBHdkMnw4ODzcwQl1CCx_r1O08hShDRGlSA6JXt5lg,5024
12
13
  bitunix_automated_crypto_trading/logger.py,sha256=tAaTQcv5r--zupk_Fhfe1USfBAzSiXzVa4QRnaOFIFY,2933
13
- bitunix_automated_crypto_trading-3.1.3.dist-info/METADATA,sha256=UoUpv4RK83MLzcKnh8A6R3p0wltVYqLTQEpR6_ZzzCY,996
14
- bitunix_automated_crypto_trading-3.1.3.dist-info/WHEEL,sha256=pxyMxgL8-pra_rKaQ4drOZAegBVuX-G_4nRHjjgWbmo,91
15
- bitunix_automated_crypto_trading-3.1.3.dist-info/entry_points.txt,sha256=UXREYHuSl2XYd_tOtLIq0zg3d1kX3lixX5SpN8yGBw4,82
16
- bitunix_automated_crypto_trading-3.1.3.dist-info/top_level.txt,sha256=uyFzHUCOsp8elnG2Ovor6xXcf7dxRxY-C-Txiwix64Q,33
17
- bitunix_automated_crypto_trading-3.1.3.dist-info/RECORD,,
14
+ bitunix_automated_crypto_trading-3.1.6.dist-info/METADATA,sha256=CzoCXqOg2hhZOrZZ9UMEnC3ohtqNrWEi6xT17G7pjq0,996
15
+ bitunix_automated_crypto_trading-3.1.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
+ bitunix_automated_crypto_trading-3.1.6.dist-info/entry_points.txt,sha256=UXREYHuSl2XYd_tOtLIq0zg3d1kX3lixX5SpN8yGBw4,82
17
+ bitunix_automated_crypto_trading-3.1.6.dist-info/top_level.txt,sha256=uyFzHUCOsp8elnG2Ovor6xXcf7dxRxY-C-Txiwix64Q,33
18
+ bitunix_automated_crypto_trading-3.1.6.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (79.0.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5