bitunix-automated-crypto-trading 3.1.1__py3-none-any.whl → 3.1.3__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.
@@ -249,21 +249,21 @@ class BitunixApi:
249
249
  function_name = stack[-2].name
250
250
  logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
251
251
 
252
- async def GetKlineHistory(self, ticker, interval, limit):
252
+ async def GetKlineHistory(self, ticker, interval, limit, starttime):
253
253
  data = []
254
254
  lm=limit
255
- current_unix_timestamp = int(time.time())
255
+ st=starttime
256
256
  try:
257
257
  while True:
258
- url = f'{self.kline_Url}?symbol={ticker}&startTime={current_unix_timestamp}&interval={interval}&limit={lm}'
258
+ url = f'{self.kline_Url}?symbol={ticker}&startTime={st}&interval={interval}&limit={lm}'
259
259
  resp = self.session.get(url)
260
260
  datajs = resp.json()
261
- if len(datajs['data']) > 0:
262
- current_unix_timestamp = str(int(datajs['data'][-1]['time']) + 1) # Adjust 'time' to match the appropriate key in your data
263
- data.extend(datajs['data'])
261
+ if datajs['data'] == []:
262
+ break
263
+ data.extend(datajs['data'])
264
+ if len(datajs['data']) < lm:
265
+ st = int(datajs['data'][-1]['time']) + 1
264
266
  lm=limit-len(data)
265
- if len(data) >= limit:
266
- break
267
267
  else:
268
268
  break
269
269
  return data
@@ -110,7 +110,8 @@ class BitunixSignal:
110
110
  olist = [entry['symbol'] for entry in self.pendingOrders['orderList']]
111
111
  newlist=olist+plist+list(set(symbols))
112
112
  self.tickerList=newlist[:300]
113
- #self.tickerList=['IMXUSDT']
113
+ self.tickerList.remove("STMXUSDT")
114
+ #self.tickerList=['PIXELUSDT']
114
115
 
115
116
  [await self.add_ticker_to_tickerObjects(sym) for sym in self.tickerList]
116
117
  self.notifications.add_notification(f"{len(self.tickerList)} ticker list loaded")
@@ -218,7 +219,7 @@ class BitunixSignal:
218
219
  intervals = self.tickerObjects.get_intervalIds()
219
220
  for ticker in self.tickerList:
220
221
  for intervalId in intervals:
221
- data = await self.bitunixApi.GetKlineHistory(ticker, intervalId, self.settings.BARS)
222
+ data = await self.bitunixApi.GetKlineHistory(ticker, intervalId, self.settings.BARS, int(time.time()))
222
223
  if data is not None:
223
224
  self.tickerObjects.load_kline_history(ticker, intervalId, self.settings.BARS, data)
224
225
  if self.settings.VERBOSE_LOGGING:
@@ -397,7 +398,7 @@ class BitunixSignal:
397
398
  self.notifications.add_notification("AutoTradeProcess or GetTickerData is not running")
398
399
  os._exit(1)
399
400
  break
400
- await asyncio.sleep(300)
401
+ await asyncio.sleep(60*30)
401
402
 
402
403
  async def GetportfolioData(self):
403
404
  start=time.time()
@@ -36,11 +36,57 @@ class Interval:
36
36
  return self._data
37
37
 
38
38
  def set_data(self, new_value):
39
- self._data = self.calculate_study(new_value)
40
-
39
+ study = self.calculate_study(new_value)
40
+ self._data = study
41
+
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
+
41
87
  def calculate_study(self, new_value):
42
88
  df = pd.DataFrame(new_value)
43
- if not df.empty and df.shape[0] >= int(self.settings.BARS):
89
+ if not df.empty: #and df.shape[0] >= int(self.settings.BARS):
44
90
 
45
91
  try:
46
92
  #consecutive same color candle
@@ -53,7 +99,6 @@ class Interval:
53
99
  # Calculate the Moving Averages
54
100
  if self.settings.EMA_STUDY:
55
101
  df['ma_fast'] = talib.EMA(df['close'], timeperiod=self.settings.MA_FAST)
56
- df['ma_fast'] = df['ma_fast'].bfill()
57
102
  df.fillna({'ma_fast':0}, inplace=True)
58
103
  df['ma_fast_slope'] = df['ma_fast'].diff()
59
104
  df['ma_fast_angle'] = np.degrees(np.arctan(df['ma_fast_slope']))
@@ -61,7 +106,6 @@ class Interval:
61
106
  df.fillna({'ma_fast_angle':0}, inplace=True)
62
107
 
63
108
  df['ma_medium'] = talib.EMA(df['close'], timeperiod=self.settings.MA_MEDIUM)
64
- df['ma_medium'] = df['ma_medium'].bfill()
65
109
  df.fillna({'ma_medium':0}, inplace=True)
66
110
  df['ma_medium_slope'] = df['ma_medium'].diff()
67
111
  df['ma_medium_angle'] = np.degrees(np.arctan(df['ma_medium_slope']))
@@ -69,7 +113,6 @@ class Interval:
69
113
  df.fillna({'ma_medium_angle':0}, inplace=True)
70
114
 
71
115
  df['ma_slow'] = talib.EMA(df['close'], timeperiod=self.settings.MA_SLOW)
72
- df['ma_slow'] = df['ma_slow'].bfill()
73
116
  df.fillna({'ma_slow':0}, inplace=True)
74
117
  df['ma_slow_slope'] = df['ma_slow'].diff()
75
118
  df['ma_slow_angle'] = np.degrees(np.arctan(df['ma_slow_slope']))
@@ -77,33 +120,35 @@ class Interval:
77
120
  df.fillna({'ma_slow_angle':0}, inplace=True)
78
121
 
79
122
  if self.settings.EMA_CROSSING:
80
- if df['ma_medium'].iloc[-2] <= df['ma_slow'].iloc[-2] and df['ma_medium'].iloc[-1] > df['ma_slow'].iloc[-1]:
81
- self.ema_open_signal = "BUY"
82
- self.ema_close_signal = "BUY"
83
- elif df['ma_medium'].iloc[-2] >= df['ma_slow'].iloc[-2] and df['ma_medium'].iloc[-1] < df['ma_slow'].iloc[-1]:
84
- self.ema_open_signal = "SELL"
85
- self.ema_close_signal = "SELL"
86
- else:
87
- self.ema_open_signal = "HOLD"
88
- self.ema_close_signal = "HOLD"
89
-
90
- if self.settings.EMA_CLOSE_ON_FAST_MEDIUM:
91
- if df['ma_fast'].iloc[-2] <= df['ma_medium'].iloc[-2] and df['ma_fast'].iloc[-1] > df['ma_medium'].iloc[-1]:
123
+ if df is not None and len(df) >= 2:
124
+ if df['ma_medium'].iloc[-2] <= df['ma_slow'].iloc[-2] and df['ma_medium'].iloc[-1] > df['ma_slow'].iloc[-1]:
125
+ self.ema_open_signal = "BUY"
92
126
  self.ema_close_signal = "BUY"
93
- elif df['ma_fast'].iloc[-2] >= df['ma_medium'].iloc[-2] and df['ma_fast'].iloc[-1] < df['ma_medium'].iloc[-1]:
127
+ elif df['ma_medium'].iloc[-2] >= df['ma_slow'].iloc[-2] and df['ma_medium'].iloc[-1] < df['ma_slow'].iloc[-1]:
128
+ self.ema_open_signal = "SELL"
94
129
  self.ema_close_signal = "SELL"
95
130
  else:
131
+ self.ema_open_signal = "HOLD"
96
132
  self.ema_close_signal = "HOLD"
133
+
134
+ if self.settings.EMA_CLOSE_ON_FAST_MEDIUM:
135
+ if df['ma_fast'].iloc[-2] <= df['ma_medium'].iloc[-2] and df['ma_fast'].iloc[-1] > df['ma_medium'].iloc[-1]:
136
+ self.ema_close_signal = "BUY"
137
+ elif df['ma_fast'].iloc[-2] >= df['ma_medium'].iloc[-2] 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"
97
141
  else:
98
- if df['close'].iloc[-1] > df['ma_medium'].iloc[-1] and df['ma_medium'].iloc[-1] > df['ma_slow'].iloc[-1]:
99
- self.ema_open_signal = "BUY"
100
- self.ema_close_signal = "BUY"
101
- elif df['close'].iloc[-1] < df['ma_medium'].iloc[-1] and df['ma_medium'].iloc[-1] < df['ma_slow'].iloc[-1]:
102
- self.ema_open_signal = "SELL"
103
- self.ema_close_signal = "SELL"
104
- else:
105
- self.ema_open_signal = "HOLD"
106
- self.ema_close_signal = "HOLD"
142
+ if df is not None and len(df) >= 1:
143
+ if df['close'].iloc[-1] > df['ma_medium'].iloc[-1] and df['ma_medium'].iloc[-1] > df['ma_slow'].iloc[-1]:
144
+ self.ema_open_signal = "BUY"
145
+ self.ema_close_signal = "BUY"
146
+ elif df['close'].iloc[-1] < df['ma_medium'].iloc[-1] and df['ma_medium'].iloc[-1] < df['ma_slow'].iloc[-1]:
147
+ self.ema_open_signal = "SELL"
148
+ self.ema_close_signal = "SELL"
149
+ else:
150
+ self.ema_open_signal = "HOLD"
151
+ self.ema_close_signal = "HOLD"
107
152
  else:
108
153
  # Drop EMA columns if not used
109
154
  df.drop(['ma_fast', 'ma_medium', 'ma_slow', 'ma_slope', 'ma_angle'], axis=1, inplace=True, errors='ignore')
@@ -114,29 +159,29 @@ class Interval:
114
159
  df['MACD_Signal'] = 0.0
115
160
  df['MACD_Histogram'] = 0.0
116
161
  df['MACD_Line'], df['MACD_Signal'], df['MACD_Histogram'] = talib.MACD(df['close'], fastperiod=self.settings.MACD_SHORT, slowperiod=self.settings.MACD_LONG, signalperiod=self.settings.MACD_PERIOD)
117
- df.fillna({'MACD_Line':0}, inplace=True)
118
- df.fillna({'MACD_Signal':0}, inplace=True)
119
- df.fillna({'MACD_Histogram':0}, inplace=True)
120
-
162
+ df.fillna({'MACD_Line':0, 'MACD_Signal':0, 'MACD_Histogram':0}, inplace=True)
121
163
  df['MACD_Line_slope'] = df['MACD_Line'].diff()
122
164
  df['MACD_Line_angle'] = np.degrees(np.arctan(df['MACD_Line_slope']))
123
165
  df.fillna({'MACD_Line_slope':0}, inplace=True)
124
- df.fillna({'MACD_Line_angle':0}, inplace=True)
166
+ df.fillna({'MACD_Line_angle':0}, inplace=True)
167
+
125
168
 
126
169
  if self.settings.MACD_CROSSING:
127
- if df['MACD_Line'].iloc[-2] <= df['MACD_Signal'].iloc[-2] and df['MACD_Line'].iloc[-1] > df['MACD_Signal'].iloc[-1]:
128
- self.macd_signal = "BUY"
129
- elif df['MACD_Line'].iloc[-2] >= df['MACD_Signal'].iloc[-2] and df['MACD_Line'].iloc[-1] < df['MACD_Signal'].iloc[-1]:
130
- self.macd_signal = "SELL"
131
- else:
132
- self.macd_signal = "HOLD"
170
+ if df is not None and len(df) >= 2:
171
+ if df['MACD_Line'].iloc[-2] <= df['MACD_Signal'].iloc[-2] and df['MACD_Line'].iloc[-1] > df['MACD_Signal'].iloc[-1]:
172
+ self.macd_signal = "BUY"
173
+ elif df['MACD_Line'].iloc[-2] >= df['MACD_Signal'].iloc[-2] and df['MACD_Line'].iloc[-1] < df['MACD_Signal'].iloc[-1]:
174
+ self.macd_signal = "SELL"
175
+ else:
176
+ self.macd_signal = "HOLD"
133
177
  else:
134
- if df['MACD_Line'].iloc[-1] > df['MACD_Signal'].iloc[-1]:
135
- self.macd_signal = "BUY"
136
- elif df['MACD_Line'].iloc[-1] < df['MACD_Signal'].iloc[-1]:
137
- self.macd_signal = "SELL"
138
- else:
139
- self.macd_signal = "HOLD"
178
+ if df is not None and len(df) >= 1:
179
+ if df['MACD_Line'].iloc[-1] > df['MACD_Signal'].iloc[-1]:
180
+ self.macd_signal = "BUY"
181
+ elif df['MACD_Line'].iloc[-1] < df['MACD_Signal'].iloc[-1]:
182
+ self.macd_signal = "SELL"
183
+ else:
184
+ self.macd_signal = "HOLD"
140
185
  else:
141
186
  # Drop MACD columns if not used
142
187
  df.drop(['MACD_Line', 'MACD_Signal', 'MACD_Histogram', 'MACD_slope', 'MACD_angle'], axis=1, inplace=True, errors='ignore')
@@ -147,9 +192,7 @@ class Interval:
147
192
  df['BBM'] = 0.0
148
193
  df['BBU'] = 0.0
149
194
  df['BBU'], df['BBM'], df['BBL'] = talib.BBANDS(df['close'], timeperiod=self.settings.BBM_PERIOD, nbdevup=self.settings.BBM_STD, nbdevdn=self.settings.BBM_STD, )
150
- df.fillna({'BBL':0}, inplace=True)
151
- df.fillna({'BBM':0}, inplace=True)
152
- df.fillna({'BBU':0}, inplace=True)
195
+ df.fillna({'BBM':0, 'BBU':0, 'BBL':0}, inplace=True)
153
196
 
154
197
  df['BBM_slope'] = df['BBM'].diff()
155
198
  df['BBM_angle'] = np.degrees(np.arctan(df['BBM_slope']))
@@ -157,19 +200,21 @@ class Interval:
157
200
  df.fillna({'BBM_angle':0}, inplace=True)
158
201
 
159
202
  if self.settings.BBM_CROSSING:
160
- if df['close'].iloc[-2] <= df['BBM'].iloc[-2] and df['close'].iloc[-1] > df['BBM'].iloc[-1]:
161
- self.bbm_signal = "BUY"
162
- elif df['close'].iloc[-2] >= df['BBM'].iloc[-2] and df['close'].iloc[-1] < df['BBM'].iloc[-1]:
163
- self.bbm_signal = "SELL"
164
- else:
165
- self.bbm_signal = "HOLD"
203
+ if df is not None and len(df) >= 2:
204
+ if df['close'].iloc[-2] <= df['BBM'].iloc[-2] and df['close'].iloc[-1] > df['BBM'].iloc[-1]:
205
+ self.bbm_signal = "BUY"
206
+ elif df['close'].iloc[-2] >= df['BBM'].iloc[-2] and df['close'].iloc[-1] < df['BBM'].iloc[-1]:
207
+ self.bbm_signal = "SELL"
208
+ else:
209
+ self.bbm_signal = "HOLD"
166
210
  else:
167
- if df['close'].iloc[-1] > df['BBM'].iloc[-1] and df['close'].iloc[-2] > df['BBM'].iloc[-2]:
168
- self.bbm_signal = "BUY"
169
- elif df['close'].iloc[-1] < df['BBM'].iloc[-1] and df['close'].iloc[-2] < df['BBM'].iloc[-2]:
170
- self.bbm_signal = "SELL"
171
- else:
172
- self.bbm_signal = "HOLD"
211
+ if df is not None and len(df) >= 1:
212
+ if df['close'].iloc[-1] > df['BBM'].iloc[-1]:
213
+ self.bbm_signal = "BUY"
214
+ elif df['close'].iloc[-1] < df['BBM'].iloc[-1]:
215
+ self.bbm_signal = "SELL"
216
+ else:
217
+ self.bbm_signal = "HOLD"
173
218
  else:
174
219
  # Drop BBM columns if not used
175
220
  df.drop(['BBL', 'BBM', 'BBU', 'BBM_slope', 'BBM_angle'], axis=1, inplace=True, errors='ignore')
@@ -192,54 +237,69 @@ class Interval:
192
237
 
193
238
 
194
239
  if self.settings.RSI_CROSSING:
195
- if df['rsi_fast'].iloc[-2] <= df['rsi_slow'].iloc[-2] and df['rsi_fast'].iloc[-1] > df['rsi_slow'].iloc[-1]:
196
- self.rsi_signal = "BUY"
197
- elif df['rsi_fast'].iloc[-2] >= df['rsi_slow'].iloc[-2] and df['rsi_fast'].iloc[-1] < df['rsi_slow'].iloc[-1]:
198
- self.rsi_signal = "SELL"
199
- else:
200
- self.rsi_signal = "HOLD"
240
+ if df is not None and len(df) >= 2:
241
+ if df['rsi_fast'].iloc[-2] <= df['rsi_slow'].iloc[-2] and df['rsi_fast'].iloc[-1] > df['rsi_slow'].iloc[-1]:
242
+ self.rsi_signal = "BUY"
243
+ elif df['rsi_fast'].iloc[-2] >= df['rsi_slow'].iloc[-2] and df['rsi_fast'].iloc[-1] < df['rsi_slow'].iloc[-1]:
244
+ self.rsi_signal = "SELL"
245
+ else:
246
+ self.rsi_signal = "HOLD"
201
247
  else:
202
- if df['rsi_fast'].iloc[-1] > df['rsi_slow'].iloc[-1]:
203
- self.rsi_signal = "BUY"
204
- elif df['rsi_fast'].iloc[-1] < df['rsi_slow'].iloc[-1]:
205
- self.rsi_signal = "SELL"
206
- else:
207
- self.rsi_signal = "HOLD"
248
+ if df is not None and len(df) >= 1:
249
+ if df['rsi_fast'].iloc[-1] > df['rsi_slow'].iloc[-1]:
250
+ self.rsi_signal = "BUY"
251
+ elif df['rsi_fast'].iloc[-1] < df['rsi_slow'].iloc[-1]:
252
+ self.rsi_signal = "SELL"
253
+ else:
254
+ self.rsi_signal = "HOLD"
208
255
  else:
209
256
  # Drop RSI columns if not used
210
257
  df.drop(['rsi_fast', 'rsi_slow', 'rsi_slope', 'rsi_angle'], axis=1, inplace=True, errors='ignore')
211
258
 
259
+ #Trendline
260
+ 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)
269
+
212
270
  # Calculate the ADX
213
271
  if self.settings.ADX_STUDY:
214
- df['ADX'] = talib.ADX(df['high'], df['low'], df['close'], timeperiod=self.settings.ADX_PERIOD)
215
- df.fillna({'ADX':0}, inplace=True)
216
- if df['ADX'].iloc[-1] > 25:
217
- self.adx_signal = "STRONG"
218
- else:
219
- self.adx_signal = "WEAK"
272
+ if df is not None and len(df) >= 1:
273
+ df['ADX'] = talib.ADX(df['high'], df['low'], df['close'], timeperiod=self.settings.ADX_PERIOD)
274
+ df.fillna({'ADX':0}, inplace=True)
275
+ if df['ADX'].iloc[-1] > 25:
276
+ self.adx_signal = "STRONG"
277
+ else:
278
+ self.adx_signal = "WEAK"
220
279
  else:
221
280
  # Drop ADX columns if not used
222
281
  df.drop(['ADX'], axis=1, inplace=True, errors='ignore')
223
282
 
224
283
  # Calculate the close proximity
225
284
  if self.settings.CANDLE_TREND_STUDY:
226
- df['range'] = df['high'] - df['low']
227
- df['candle_trend'] = ((df['close'] - df['low'])/df['range'])*100
228
- df.fillna({'candle_trend':0}, inplace=True)
229
- df.fillna({'range':0}, inplace=True)
230
-
231
- #open and close criteria
232
- if df['candle_trend'].iloc[-1] > 70:
233
- self.candle_trend = 'BULLISH'
234
- elif df['candle_trend'].iloc[-1] < 30:
235
- self.candle_trend = 'BEARISH'
236
- else:
237
- self.candle_trend = 'HOLD'
285
+ if df is not None and len(df) >= 1:
286
+ df['range'] = df['high'] - df['low']
287
+ df['candle_trend'] = ((df['close'] - df['low'])/df['range'])*100
288
+ df.fillna({'candle_trend':0}, inplace=True)
289
+ df.fillna({'range':0}, inplace=True)
290
+
291
+ #open and close criteria
292
+ if df['candle_trend'].iloc[-1] > 70:
293
+ self.candle_trend = 'BULLISH'
294
+ elif df['candle_trend'].iloc[-1] < 30:
295
+ self.candle_trend = 'BEARISH'
296
+ else:
297
+ self.candle_trend = 'HOLD'
238
298
  else:
239
299
  # Drop candle trend columns if not used
240
300
  df.drop(['candle_trend', 'range'], axis=1, inplace=True, errors='ignore')
241
301
 
242
-
302
+
243
303
  #replace infinity
244
304
  df.replace([np.inf, -np.inf], 0, inplace=True)
245
305
 
@@ -411,7 +471,8 @@ class Ticker:
411
471
  self._intervals = intervals
412
472
 
413
473
  def get_interval_ticks(self, intervalId):
414
- return self._intervals.get(intervalId, None)
474
+ interval = self._intervals.get(intervalId, None)
475
+ return interval
415
476
 
416
477
  def set_interval_ticks(self, intervalId, new_value):
417
478
  self._intervals[intervalId] = new_value
@@ -450,6 +511,7 @@ class Ticker:
450
511
  ticks_interval=intervalObj.get_data()
451
512
  if ticks_interval is None:
452
513
  return
514
+ #print('create_bar_with_last_and_ts', self.symbol, intervalId, len(ticks_interval))
453
515
  if len(ticks_interval)==0 or self._ts - ticks_interval[-1]['time'] >= intervalObj.delta:
454
516
  ticks_interval.append(new_item)
455
517
  current_bar = ticks_interval[-1]
@@ -464,6 +526,7 @@ class Ticker:
464
526
  current_bar['barcolor'] = self.red if current_bar['close'] <= current_bar['open'] else self.green
465
527
  #print(f'{self.symbol} {self._last}, {self._ts} {intervalId} {ticks_interval[-1]['time']} c' )
466
528
 
529
+ #print ('create_bar_with_last_and_ts', ticks_interval[-1])
467
530
  intervalObj.set_data(ticks_interval)
468
531
 
469
532
  except Exception as e:
@@ -567,24 +630,29 @@ class Tickers:
567
630
  # since the calulation is done at a different thread and uses a different memory space,
568
631
  # the caluated value has to be reassigned to the original ticker class instance from the caluated ticker instance
569
632
  def form_candle(self, tuples_list):
570
- with ProcessPoolExecutor() as executor:
571
- results = executor.map(self.process_ticker_candle, tuples_list, chunksize=10)
572
- for args, result in zip(tuples_list, results):
573
- for intervalId, interval in result[3].items():
574
- if not interval._data is None:
575
- args[0].get_interval_ticks(intervalId)._data = interval._data
576
- args[0].get_interval_ticks(intervalId).current_signal = interval.current_signal
577
- args[0].get_interval_ticks(intervalId).ema_open_signal = interval.ema_open_signal
578
- args[0].get_interval_ticks(intervalId).ema_close_signal = interval.ema_close_signal
579
- args[0].get_interval_ticks(intervalId).macd_signal = interval.macd_signal
580
- args[0].get_interval_ticks(intervalId).bbm_signal = interval.bbm_signal
581
- args[0].get_interval_ticks(intervalId).rsi_signal = interval.rsi_signal
582
- args[0].get_interval_ticks(intervalId).candle_trend = interval.candle_trend
583
- args[0].get_interval_ticks(intervalId).adx_signal = interval.adx_signal
584
- args[0].get_interval_ticks(intervalId).signal_strength = interval.signal_strength
585
- args[0]._last = result[0]
586
- args[0].lastcolor = result[1]
587
- args[0]._ts = result[2]
633
+ if not self.settings.CPU_PROCCESING:
634
+ for args in tuples_list:
635
+ ticker_obj, last, ts = args
636
+ result = self.process_ticker_candle(args)
637
+ else:
638
+ with ProcessPoolExecutor() as executor:
639
+ results = executor.map(self.process_ticker_candle, tuples_list, chunksize=10)
640
+ for args, result in zip(tuples_list, results):
641
+ for intervalId, interval in result[3].items():
642
+ if not interval._data is None:
643
+ args[0].get_interval_ticks(intervalId)._data = interval._data
644
+ args[0].get_interval_ticks(intervalId).current_signal = interval.current_signal
645
+ args[0].get_interval_ticks(intervalId).ema_open_signal = interval.ema_open_signal
646
+ args[0].get_interval_ticks(intervalId).ema_close_signal = interval.ema_close_signal
647
+ args[0].get_interval_ticks(intervalId).macd_signal = interval.macd_signal
648
+ args[0].get_interval_ticks(intervalId).bbm_signal = interval.bbm_signal
649
+ args[0].get_interval_ticks(intervalId).rsi_signal = interval.rsi_signal
650
+ args[0].get_interval_ticks(intervalId).candle_trend = interval.candle_trend
651
+ args[0].get_interval_ticks(intervalId).adx_signal = interval.adx_signal
652
+ args[0].get_interval_ticks(intervalId).signal_strength = interval.signal_strength
653
+ args[0]._last = result[0]
654
+ args[0].lastcolor = result[1]
655
+ args[0]._ts = result[2]
588
656
 
589
657
  def getCurrentData(self, period):
590
658
  current_data = []
@@ -603,33 +671,32 @@ class Tickers:
603
671
  ticks = intervalObj.get_data()
604
672
  if not ticks:
605
673
  continue
606
- if len(ticks)>=int(self.settings.BARS):
607
- lastcandle = intervalObj.get_data()[-1]
608
- if len(lastcandle) >= 20:
609
- new_row = {
610
- 'symbol' : symbol,
611
- f"{period}_trend": intervalObj.current_signal,
612
- f"{period}_cb": intervalObj.signal_strength,
613
- f"{period}_barcolor": lastcandle['barcolor'],
614
- f"{period}_ema_open": intervalObj.ema_open_signal,
615
- f"{period}_ema_close": intervalObj.ema_close_signal,
616
- f"{period}_macd":intervalObj.macd_signal,
617
- f"{period}_bbm":intervalObj.bbm_signal,
618
- f"{period}_rsi":intervalObj.rsi_signal,
619
- f"{period}_adx":intervalObj.adx_signal,
620
- f"{period}_candle_trend":intervalObj.candle_trend,
621
- 'bid' : bid,
622
- 'bidcolor' : bidcolor,
623
- 'last' : last,
624
- 'lastcolor' : lastcolor,
625
- 'ask' : ask,
626
- 'askcolor' : askcolor,
627
- f"{period}_open": lastcandle['open'],
628
- f"{period}_close": lastcandle['close'],
629
- f"{period}_high": lastcandle['high'],
630
- f"{period}_low": lastcandle['low'],
631
- }
632
- current_data.append(new_row)
674
+ lastcandle = intervalObj.get_data()[-1]
675
+ if len(lastcandle) >= 20:
676
+ new_row = {
677
+ 'symbol' : symbol,
678
+ f"{period}_trend": intervalObj.current_signal,
679
+ f"{period}_cb": intervalObj.signal_strength,
680
+ f"{period}_barcolor": lastcandle['barcolor'],
681
+ f"{period}_ema_open": intervalObj.ema_open_signal,
682
+ f"{period}_ema_close": intervalObj.ema_close_signal,
683
+ f"{period}_macd":intervalObj.macd_signal,
684
+ f"{period}_bbm":intervalObj.bbm_signal,
685
+ f"{period}_rsi":intervalObj.rsi_signal,
686
+ f"{period}_adx":intervalObj.adx_signal,
687
+ f"{period}_candle_trend":intervalObj.candle_trend,
688
+ 'bid' : bid,
689
+ 'bidcolor' : bidcolor,
690
+ 'last' : last,
691
+ 'lastcolor' : lastcolor,
692
+ 'ask' : ask,
693
+ 'askcolor' : askcolor,
694
+ f"{period}_open": lastcandle['open'],
695
+ f"{period}_close": lastcandle['close'],
696
+ f"{period}_high": lastcandle['high'],
697
+ f"{period}_low": lastcandle['low'],
698
+ }
699
+ current_data.append(new_row)
633
700
 
634
701
  df = pd.DataFrame(current_data)
635
702
  if not df.empty:
@@ -70,76 +70,6 @@ class bitunix():
70
70
  #create benchmark table
71
71
  self.cursor.execute("CREATE TABLE IF NOT EXISTS benchmark (id INTEGER PRIMARY KEY, process_name TEXT, time INTEGER)")
72
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
-
142
-
143
73
  async def update_settings(self, settings):
144
74
  self.settings = settings
145
75
  await self.bitunixSignal.update_settings(settings)
@@ -470,6 +400,8 @@ async def wscharts(websocket):
470
400
  "buysell": buysell,
471
401
  "ema_study": settings.EMA_STUDY,
472
402
  "ema_chart": settings.EMA_CHART,
403
+ "trendline_study": settings.TRENDLINE_STUDY,
404
+ "trendline_chart": settings.TRENDLINE_CHART,
473
405
  "macd_study": settings.MACD_STUDY,
474
406
  "macd_chart": settings.MACD_CHART,
475
407
  "bbm_study": settings.BBM_STUDY,
@@ -557,6 +489,8 @@ async def wschart(websocket):
557
489
  "period": period,
558
490
  "ema_study": settings.EMA_STUDY,
559
491
  "ema_chart": settings.EMA_CHART,
492
+ "trendline_study": settings.TRENDLINE_STUDY,
493
+ "trendline_chart": settings.TRENDLINE_CHART,
560
494
  "macd_study": settings.MACD_STUDY,
561
495
  "macd_chart": settings.MACD_CHART,
562
496
  "bbm_study": settings.BBM_STUDY,
@@ -59,6 +59,12 @@ class Settings(BaseSettings):
59
59
  RSI_CHECK_ON_OPEN: bool = Field(default=False)
60
60
  RSI_CHECK_ON_CLOSE: bool = Field(default=False)
61
61
 
62
+ TRENDLINE_CHART: bool = Field(default=True)
63
+ TRENDLINE_STUDY: bool = Field(default=True)
64
+ TRENDLINE_LOOKBACK: int = Field(default=20, ge=10, le=100)
65
+ TRENDLINE_CHECK_ON_OPEN: bool = Field(default=False)
66
+ TRENDLINE_CHECK_ON_CLOSE: bool = Field(default=False)
67
+
62
68
  ADX_STUDY: bool = Field(default=True)
63
69
  ADX_CHECK_ON_OPEN: bool = Field(default=False)
64
70
  ADX_CHECK_ON_CLOSE: bool = Field(default=False)
@@ -84,6 +90,9 @@ class Settings(BaseSettings):
84
90
  # Logger
85
91
  VERBOSE_LOGGING: bool = Field(default=False)
86
92
 
93
+ #use CPU
94
+ CPU_PROCCESING: bool = Field(default=True)
95
+
87
96
  # Benchmark
88
97
  BENCHMARK: bool = Field(default=False)
89
98
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bitunix_automated_crypto_trading
3
- Version: 3.1.1
3
+ Version: 3.1.3
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,17 @@
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=17_VPwagjax-vFJ-A20cMApcyBtPCGdzis8EhAVoNrk,64734
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/ThreadManager.py,sha256=WmYRQu8aNrgp5HwSqpBqX1WESNSEpbD2CznPFpd9HuI,2330
8
+ bitunix_automated_crypto_trading/TickerManager.py,sha256=W4UuW1RyU_4w_6IV0eDOlJ0PUFNorQOY9Ust4bI-69s,36785
9
+ bitunix_automated_crypto_trading/__init__.py,sha256=1hzk6nX8NnUCr1tsq8oFq1qGCNhNwnwldWE75641Eew,78
10
+ 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/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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (79.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,17 +0,0 @@
1
- bitunix_automated_crypto_trading/AsyncThreadRunner.py,sha256=LJ6ny1KSCVoIbfeNypsY4aHYcbkGME9M3epQ9e8B1O8,3224
2
- bitunix_automated_crypto_trading/BitunixApi.py,sha256=wJhknpmVFrckoL89h-nlWUAMsPxhIacY0nOGJT9M6Vw,11360
3
- bitunix_automated_crypto_trading/BitunixSignal.py,sha256=LKxWy_-alKkawB_l9_AdzwtCt0rx6lTqfBDuiGBMoe4,64669
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/ThreadManager.py,sha256=WmYRQu8aNrgp5HwSqpBqX1WESNSEpbD2CznPFpd9HuI,2330
8
- bitunix_automated_crypto_trading/TickerManager.py,sha256=eeOK5paiae10XHjKMbucJnVAZkAsEC86N37bRPuCcFc,33427
9
- bitunix_automated_crypto_trading/__init__.py,sha256=1hzk6nX8NnUCr1tsq8oFq1qGCNhNwnwldWE75641Eew,78
10
- bitunix_automated_crypto_trading/bitunix.py,sha256=c5vKRuN1-UaNz_ZB5Txu4-2UM6PsnRhs-ztvhRtpEzc,32779
11
- bitunix_automated_crypto_trading/config.py,sha256=QWAe5Ruq8A5anaFS-CamVm-3t1wMJjGG1DWaPIIWfiM,4521
12
- bitunix_automated_crypto_trading/logger.py,sha256=tAaTQcv5r--zupk_Fhfe1USfBAzSiXzVa4QRnaOFIFY,2933
13
- bitunix_automated_crypto_trading-3.1.1.dist-info/METADATA,sha256=5cwQuEB4OsaVHSAxpiYo0gIDSK3Rs39TgID_E7dCihU,996
14
- bitunix_automated_crypto_trading-3.1.1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
15
- bitunix_automated_crypto_trading-3.1.1.dist-info/entry_points.txt,sha256=UXREYHuSl2XYd_tOtLIq0zg3d1kX3lixX5SpN8yGBw4,82
16
- bitunix_automated_crypto_trading-3.1.1.dist-info/top_level.txt,sha256=uyFzHUCOsp8elnG2Ovor6xXcf7dxRxY-C-Txiwix64Q,33
17
- bitunix_automated_crypto_trading-3.1.1.dist-info/RECORD,,