bitunix-automated-crypto-trading 3.1.2__py3-none-any.whl → 3.1.5__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bitunix_automated_crypto_trading/BitunixApi.py +2 -0
- bitunix_automated_crypto_trading/BitunixSignal.py +64 -32
- bitunix_automated_crypto_trading/SupportResistance.py +89 -0
- bitunix_automated_crypto_trading/TickerManager.py +205 -146
- bitunix_automated_crypto_trading/bitunix.py +4 -70
- bitunix_automated_crypto_trading/config.py +10 -0
- {bitunix_automated_crypto_trading-3.1.2.dist-info → bitunix_automated_crypto_trading-3.1.5.dist-info}/METADATA +1 -1
- bitunix_automated_crypto_trading-3.1.5.dist-info/RECORD +18 -0
- {bitunix_automated_crypto_trading-3.1.2.dist-info → bitunix_automated_crypto_trading-3.1.5.dist-info}/WHEEL +1 -1
- bitunix_automated_crypto_trading-3.1.2.dist-info/RECORD +0 -17
- {bitunix_automated_crypto_trading-3.1.2.dist-info → bitunix_automated_crypto_trading-3.1.5.dist-info}/entry_points.txt +0 -0
- {bitunix_automated_crypto_trading-3.1.2.dist-info → bitunix_automated_crypto_trading-3.1.5.dist-info}/top_level.txt +0 -0
@@ -258,6 +258,8 @@ class BitunixApi:
|
|
258
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 datajs['data'] == []:
|
262
|
+
break
|
261
263
|
data.extend(datajs['data'])
|
262
264
|
if len(datajs['data']) < lm:
|
263
265
|
st = int(datajs['data'][-1]['time']) + 1
|
@@ -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
|
-
|
113
|
+
self.tickerList.remove("STMXUSDT")
|
114
|
+
#self.tickerList=['POLUSDT']
|
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")
|
@@ -270,12 +271,7 @@ class BitunixSignal:
|
|
270
271
|
# Function to add data to the last price deque
|
271
272
|
async def StoreTickerData(self, message):
|
272
273
|
if self.settings.USE_PUBLIC_WEBSOCKET and message:
|
273
|
-
|
274
|
-
data = json.loads(message)
|
275
|
-
if data.get('symbol') and data.get('ch') == 'ticker':
|
276
|
-
await self.ticker_que.put(data)
|
277
|
-
except json.JSONDecodeError as e:
|
278
|
-
logger.warning(f"Failed to decode message: {message}. Error: {e}")
|
274
|
+
await self.ticker_que.put(message)
|
279
275
|
|
280
276
|
# Function to process the last price deque
|
281
277
|
async def ProcessTickerData(self):
|
@@ -284,11 +280,14 @@ class BitunixSignal:
|
|
284
280
|
latest_data = {}
|
285
281
|
reversed_items = await self.drain_queue(self.ticker_que)
|
286
282
|
while reversed_items:
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
283
|
+
message = reversed_items.popleft()
|
284
|
+
data = json.loads(message)
|
285
|
+
if data.get('symbol') and data.get('ch') == 'ticker':
|
286
|
+
symbol = data["symbol"]
|
287
|
+
ts = data["ts"]
|
288
|
+
if symbol not in latest_data or ts > latest_data[symbol]['ts']:
|
289
|
+
latest_data[symbol] = {'ts': ts, 'last': float(data['data']['la'])}
|
290
|
+
await asyncio.sleep(0.01)
|
292
291
|
# Convert to DataFrame
|
293
292
|
self.tickerdf = pd.DataFrame.from_dict(latest_data, orient="index")
|
294
293
|
if not self.tickerdf.empty:
|
@@ -299,7 +298,7 @@ class BitunixSignal:
|
|
299
298
|
except Exception as e:
|
300
299
|
logger.info(f"Function: ProcessTickerData, {e}, {e.args}, {type(e).__name__}")
|
301
300
|
logger.info(traceback.print_exc())
|
302
|
-
await asyncio.sleep(0.
|
301
|
+
await asyncio.sleep(0.01)
|
303
302
|
logger.info(f"ProcessTickerData: exitied out of the loop, exiting app")
|
304
303
|
os._exit(1) # Exit the program
|
305
304
|
|
@@ -307,13 +306,8 @@ class BitunixSignal:
|
|
307
306
|
#websocket data to update bid and ask
|
308
307
|
async def StoreDepthData(self, message):
|
309
308
|
if self.settings.USE_PUBLIC_WEBSOCKET and message:
|
310
|
-
|
311
|
-
|
312
|
-
if data.get('symbol') and data.get('ch') == 'depth_book1':
|
313
|
-
await self.depth_que.put(data)
|
314
|
-
except json.JSONDecodeError as e:
|
315
|
-
logger.warning(f"Failed to decode message: {message}. Error: {e}")
|
316
|
-
|
309
|
+
await self.depth_que.put(message)
|
310
|
+
|
317
311
|
# Function to process the bid, ask
|
318
312
|
async def ProcessDepthData(self):
|
319
313
|
while True:
|
@@ -321,11 +315,14 @@ class BitunixSignal:
|
|
321
315
|
latest_data = {}
|
322
316
|
reversed_items = await self.drain_queue(self.depth_que)
|
323
317
|
while reversed_items:
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
318
|
+
message = reversed_items.popleft()
|
319
|
+
data = json.loads(message)
|
320
|
+
if data.get('symbol') and data.get('ch') == 'depth_book1':
|
321
|
+
symbol = data["symbol"]
|
322
|
+
ts = data["ts"]
|
323
|
+
if symbol not in latest_data or ts > latest_data[symbol]['ts']:
|
324
|
+
latest_data[symbol] = {'ts': ts, 'bid': float(data['data']['b'][0][0]), 'ask': float(data['data']['a'][0][0])}
|
325
|
+
await asyncio.sleep(0.01)
|
329
326
|
# Convert to DataFrame
|
330
327
|
self.depthdf = pd.DataFrame.from_dict(latest_data, orient="index")
|
331
328
|
if not self.depthdf.empty:
|
@@ -334,7 +331,7 @@ class BitunixSignal:
|
|
334
331
|
except Exception as e:
|
335
332
|
logger.info(f"Function: ProcessTickerData, {e}, {e.args}, {type(e).__name__}")
|
336
333
|
logger.info(traceback.print_exc())
|
337
|
-
await asyncio.sleep(0.
|
334
|
+
await asyncio.sleep(0.01)
|
338
335
|
logger.info(f"ProcessDepthData: exitied out of the loop, exiting app")
|
339
336
|
os._exit(1) # Exit the program
|
340
337
|
|
@@ -397,7 +394,7 @@ class BitunixSignal:
|
|
397
394
|
self.notifications.add_notification("AutoTradeProcess or GetTickerData is not running")
|
398
395
|
os._exit(1)
|
399
396
|
break
|
400
|
-
await asyncio.sleep(
|
397
|
+
await asyncio.sleep(60*30)
|
401
398
|
|
402
399
|
async def GetportfolioData(self):
|
403
400
|
start=time.time()
|
@@ -562,12 +559,12 @@ class BitunixSignal:
|
|
562
559
|
self.signaldf_filtered = self.tickerObjects.signaldf_filtered
|
563
560
|
|
564
561
|
if not self.positiondf.empty and not self.signaldf_full.empty:
|
565
|
-
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"]
|
562
|
+
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}_trendline", f"{period}_candle_trend", f"{period}_open", f"{period}_close", f"{period}_high", f"{period}_low"]
|
566
563
|
columns2=["qty", "side", "unrealizedPNL", "realizedPNL", "ctime", "avgOpenPrice", "bid", "bidcolor", "last", "lastcolor", "ask", "askcolor", "charts", "bitunix", "action", "add", "reduce"]
|
567
564
|
if set(columns).issubset(self.signaldf_full.columns) and set(columns2).issubset(self.positiondf.columns):
|
568
|
-
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"]
|
565
|
+
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}_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"]
|
569
566
|
self.positiondf2 = pd.merge(self.positiondf, self.signaldf_full[["symbol", f"{period}_open", f"{period}_close", f"{period}_high", f"{period}_low",
|
570
|
-
f"{period}_ema_open", f"{period}_ema_close", f"{period}_macd", f"{period}_bbm", f"{period}_rsi", f"{period}_adx", f"{period}_candle_trend",
|
567
|
+
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",
|
571
568
|
f"{period}_trend",f"{period}_cb", f"{period}_barcolor"]], left_on="symbol", right_index=True, how="left")[columnOrder]
|
572
569
|
self.positiondfStyle= self.positiondfrenderer.render_html(self.positiondf2)
|
573
570
|
else:
|
@@ -581,7 +578,7 @@ class BitunixSignal:
|
|
581
578
|
# Assign to self.signaldf for HTML rendering
|
582
579
|
self.signaldf = self.signaldf_filtered[[
|
583
580
|
"symbol", f"{period}_trend",f"{period}_cb", f"{period}_barcolor",
|
584
|
-
f"{period}_ema_open", f"{period}_ema_close", f"{period}_macd", f"{period}_bbm", f"{period}_rsi",f"{period}_adx",f"{period}_candle_trend",
|
581
|
+
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",
|
585
582
|
'lastcolor', 'bidcolor', 'askcolor', 'bid', 'last', 'ask',
|
586
583
|
f"{period}_open", f"{period}_close", f"{period}_high", f"{period}_low",
|
587
584
|
]].sort_values(by=[f'{period}_cb'], ascending=[False])
|
@@ -692,7 +689,7 @@ class BitunixSignal:
|
|
692
689
|
total_pnl = unrealized_pnl + realized_pnl
|
693
690
|
side=row['side']
|
694
691
|
|
695
|
-
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']
|
692
|
+
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']
|
696
693
|
required_cols = set(requiredCols)
|
697
694
|
|
698
695
|
# Close position that fall the below criteria
|
@@ -878,6 +875,41 @@ class BitunixSignal:
|
|
878
875
|
)
|
879
876
|
continue
|
880
877
|
|
878
|
+
# TrendLine
|
879
|
+
if self.settings.TRENDLINE_STUDY and self.settings.TRENDLINE_CHECK_ON_CLOSE:
|
880
|
+
if row.side == 'BUY' and self.signaldf_full.at[row.symbol, f'{period}_trendline'] == "SELL" and total_pnl>0:
|
881
|
+
last, bid, ask, mtv = await self.GetTickerBidLastAsk(row.symbol)
|
882
|
+
price = (ask if row['side'] == "BUY" else bid if row['side'] == "SELL" else last) if bid<=last<=ask else last
|
883
|
+
|
884
|
+
self.notifications.add_notification(
|
885
|
+
f'{colors.CYAN} Closing {"long" if side=="BUY" else "short"} position due to {period} trendline for {row.symbol} with {row.qty} qty @ {price})'
|
886
|
+
)
|
887
|
+
datajs = await self.bitunixApi.PlaceOrder(
|
888
|
+
positionId=row.positionId,
|
889
|
+
ticker=row.symbol,
|
890
|
+
qty=row.qty,
|
891
|
+
price=price,
|
892
|
+
side=row.side,
|
893
|
+
tradeSide="CLOSE"
|
894
|
+
)
|
895
|
+
continue
|
896
|
+
|
897
|
+
if row.side == 'SELL' and self.signaldf_full.at[row.symbol, f'{period}_trendline'] == "BUY" and total_pnl>0:
|
898
|
+
last, bid, ask, mtv = await self.GetTickerBidLastAsk(row.symbol)
|
899
|
+
price = (ask if row['side'] == "BUY" else bid if row['side'] == "SELL" else last) if bid<=last<=ask else last
|
900
|
+
self.notifications.add_notification(
|
901
|
+
f'{colors.CYAN} Closing {"long" if side=="BUY" else "short"} position due to {period} trendline for {row.symbol} with {row.qty} qty @ {price})'
|
902
|
+
)
|
903
|
+
datajs = await self.bitunixApi.PlaceOrder(
|
904
|
+
positionId=row.positionId,
|
905
|
+
ticker=row.symbol,
|
906
|
+
qty=row.qty,
|
907
|
+
price=price,
|
908
|
+
side=row.side,
|
909
|
+
tradeSide="CLOSE"
|
910
|
+
)
|
911
|
+
continue
|
912
|
+
|
881
913
|
# Close on weak trend after open
|
882
914
|
if self.settings.ADX_STUDY and self.settings.ADX_CHECK_ON_CLOSE:
|
883
915
|
if row.side == 'BUY' and self.signaldf_full.at[row.symbol, f'{period}_adx'] == "WEAK":
|
@@ -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
|
-
|
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"
|
@@ -36,11 +41,19 @@ class Interval:
|
|
36
41
|
return self._data
|
37
42
|
|
38
43
|
def set_data(self, new_value):
|
39
|
-
|
40
|
-
|
44
|
+
study = self.calculate_study(new_value)
|
45
|
+
self._data = study
|
46
|
+
|
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
|
+
|
41
54
|
def calculate_study(self, new_value):
|
42
55
|
df = pd.DataFrame(new_value)
|
43
|
-
if not df.empty and df.shape[0] >= int(self.settings.BARS):
|
56
|
+
if not df.empty: #and df.shape[0] >= int(self.settings.BARS):
|
44
57
|
|
45
58
|
try:
|
46
59
|
#consecutive same color candle
|
@@ -53,54 +66,56 @@ class Interval:
|
|
53
66
|
# Calculate the Moving Averages
|
54
67
|
if self.settings.EMA_STUDY:
|
55
68
|
df['ma_fast'] = talib.EMA(df['close'], timeperiod=self.settings.MA_FAST)
|
56
|
-
df.
|
69
|
+
df.fillna({'ma_fast':0}, inplace=True)
|
57
70
|
df['ma_fast_slope'] = df['ma_fast'].diff()
|
58
71
|
df['ma_fast_angle'] = np.degrees(np.arctan(df['ma_fast_slope']))
|
59
72
|
df.fillna({'ma_fast_slope':0}, inplace=True)
|
60
73
|
df.fillna({'ma_fast_angle':0}, inplace=True)
|
61
74
|
|
62
75
|
df['ma_medium'] = talib.EMA(df['close'], timeperiod=self.settings.MA_MEDIUM)
|
63
|
-
df.
|
76
|
+
df.fillna({'ma_medium':0}, inplace=True)
|
64
77
|
df['ma_medium_slope'] = df['ma_medium'].diff()
|
65
78
|
df['ma_medium_angle'] = np.degrees(np.arctan(df['ma_medium_slope']))
|
66
79
|
df.fillna({'ma_medium_slope':0}, inplace=True)
|
67
80
|
df.fillna({'ma_medium_angle':0}, inplace=True)
|
68
81
|
|
69
82
|
df['ma_slow'] = talib.EMA(df['close'], timeperiod=self.settings.MA_SLOW)
|
70
|
-
df.
|
83
|
+
df.fillna({'ma_slow':0}, inplace=True)
|
71
84
|
df['ma_slow_slope'] = df['ma_slow'].diff()
|
72
85
|
df['ma_slow_angle'] = np.degrees(np.arctan(df['ma_slow_slope']))
|
73
86
|
df.fillna({'ma_slow_slope':0}, inplace=True)
|
74
87
|
df.fillna({'ma_slow_angle':0}, inplace=True)
|
75
88
|
|
76
89
|
if self.settings.EMA_CROSSING:
|
77
|
-
if df
|
78
|
-
|
79
|
-
|
80
|
-
elif 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 = "SELL"
|
82
|
-
self.ema_close_signal = "SELL"
|
83
|
-
else:
|
84
|
-
self.ema_open_signal = "HOLD"
|
85
|
-
self.ema_close_signal = "HOLD"
|
86
|
-
|
87
|
-
if self.settings.EMA_CLOSE_ON_FAST_MEDIUM:
|
88
|
-
if df['ma_fast'].iloc[-2] <= df['ma_medium'].iloc[-2] and df['ma_fast'].iloc[-1] > df['ma_medium'].iloc[-1]:
|
90
|
+
if df is not None and len(df) >= 2:
|
91
|
+
if df['ma_medium'].iloc[-2] <= df['ma_slow'].iloc[-2] and df['ma_medium'].iloc[-1] > df['ma_slow'].iloc[-1]:
|
92
|
+
self.ema_open_signal = "BUY"
|
89
93
|
self.ema_close_signal = "BUY"
|
90
|
-
elif df['
|
94
|
+
elif df['ma_medium'].iloc[-2] >= df['ma_slow'].iloc[-2] and df['ma_medium'].iloc[-1] < df['ma_slow'].iloc[-1]:
|
95
|
+
self.ema_open_signal = "SELL"
|
91
96
|
self.ema_close_signal = "SELL"
|
92
97
|
else:
|
98
|
+
self.ema_open_signal = "HOLD"
|
93
99
|
self.ema_close_signal = "HOLD"
|
100
|
+
|
101
|
+
if self.settings.EMA_CLOSE_ON_FAST_MEDIUM:
|
102
|
+
if df['ma_fast'].iloc[-2] <= df['ma_medium'].iloc[-2] and df['ma_fast'].iloc[-1] > df['ma_medium'].iloc[-1]:
|
103
|
+
self.ema_close_signal = "BUY"
|
104
|
+
elif df['ma_fast'].iloc[-2] >= df['ma_medium'].iloc[-2] and df['ma_fast'].iloc[-1] < df['ma_medium'].iloc[-1]:
|
105
|
+
self.ema_close_signal = "SELL"
|
106
|
+
else:
|
107
|
+
self.ema_close_signal = "HOLD"
|
94
108
|
else:
|
95
|
-
if df
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
109
|
+
if df is not None and len(df) >= 1:
|
110
|
+
if df['close'].iloc[-1] > df['ma_medium'].iloc[-1] and df['ma_medium'].iloc[-1] > df['ma_slow'].iloc[-1]:
|
111
|
+
self.ema_open_signal = "BUY"
|
112
|
+
self.ema_close_signal = "BUY"
|
113
|
+
elif df['close'].iloc[-1] < df['ma_medium'].iloc[-1] and df['ma_medium'].iloc[-1] < df['ma_slow'].iloc[-1]:
|
114
|
+
self.ema_open_signal = "SELL"
|
115
|
+
self.ema_close_signal = "SELL"
|
116
|
+
else:
|
117
|
+
self.ema_open_signal = "HOLD"
|
118
|
+
self.ema_close_signal = "HOLD"
|
104
119
|
else:
|
105
120
|
# Drop EMA columns if not used
|
106
121
|
df.drop(['ma_fast', 'ma_medium', 'ma_slow', 'ma_slope', 'ma_angle'], axis=1, inplace=True, errors='ignore')
|
@@ -111,7 +126,7 @@ class Interval:
|
|
111
126
|
df['MACD_Signal'] = 0.0
|
112
127
|
df['MACD_Histogram'] = 0.0
|
113
128
|
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)
|
114
|
-
df.
|
129
|
+
df.fillna({'MACD_Line':0, 'MACD_Signal':0, 'MACD_Histogram':0}, inplace=True)
|
115
130
|
df['MACD_Line_slope'] = df['MACD_Line'].diff()
|
116
131
|
df['MACD_Line_angle'] = np.degrees(np.arctan(df['MACD_Line_slope']))
|
117
132
|
df.fillna({'MACD_Line_slope':0}, inplace=True)
|
@@ -119,19 +134,21 @@ class Interval:
|
|
119
134
|
|
120
135
|
|
121
136
|
if self.settings.MACD_CROSSING:
|
122
|
-
if df
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
137
|
+
if df is not None and len(df) >= 2:
|
138
|
+
if df['MACD_Line'].iloc[-2] <= df['MACD_Signal'].iloc[-2] and df['MACD_Line'].iloc[-1] > df['MACD_Signal'].iloc[-1]:
|
139
|
+
self.macd_signal = "BUY"
|
140
|
+
elif df['MACD_Line'].iloc[-2] >= df['MACD_Signal'].iloc[-2] and df['MACD_Line'].iloc[-1] < df['MACD_Signal'].iloc[-1]:
|
141
|
+
self.macd_signal = "SELL"
|
142
|
+
else:
|
143
|
+
self.macd_signal = "HOLD"
|
128
144
|
else:
|
129
|
-
if df
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
145
|
+
if df is not None and len(df) >= 1:
|
146
|
+
if df['MACD_Line'].iloc[-1] > df['MACD_Signal'].iloc[-1]:
|
147
|
+
self.macd_signal = "BUY"
|
148
|
+
elif df['MACD_Line'].iloc[-1] < df['MACD_Signal'].iloc[-1]:
|
149
|
+
self.macd_signal = "SELL"
|
150
|
+
else:
|
151
|
+
self.macd_signal = "HOLD"
|
135
152
|
else:
|
136
153
|
# Drop MACD columns if not used
|
137
154
|
df.drop(['MACD_Line', 'MACD_Signal', 'MACD_Histogram', 'MACD_slope', 'MACD_angle'], axis=1, inplace=True, errors='ignore')
|
@@ -142,7 +159,7 @@ class Interval:
|
|
142
159
|
df['BBM'] = 0.0
|
143
160
|
df['BBU'] = 0.0
|
144
161
|
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, )
|
145
|
-
df.
|
162
|
+
df.fillna({'BBM':0, 'BBU':0, 'BBL':0}, inplace=True)
|
146
163
|
|
147
164
|
df['BBM_slope'] = df['BBM'].diff()
|
148
165
|
df['BBM_angle'] = np.degrees(np.arctan(df['BBM_slope']))
|
@@ -150,19 +167,21 @@ class Interval:
|
|
150
167
|
df.fillna({'BBM_angle':0}, inplace=True)
|
151
168
|
|
152
169
|
if self.settings.BBM_CROSSING:
|
153
|
-
if df
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
170
|
+
if df is not None and len(df) >= 2:
|
171
|
+
if df['close'].iloc[-2] <= df['BBM'].iloc[-2] and df['close'].iloc[-1] > df['BBM'].iloc[-1]:
|
172
|
+
self.bbm_signal = "BUY"
|
173
|
+
elif df['close'].iloc[-2] >= df['BBM'].iloc[-2] and df['close'].iloc[-1] < df['BBM'].iloc[-1]:
|
174
|
+
self.bbm_signal = "SELL"
|
175
|
+
else:
|
176
|
+
self.bbm_signal = "HOLD"
|
159
177
|
else:
|
160
|
-
if df
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
178
|
+
if df is not None and len(df) >= 1:
|
179
|
+
if df['close'].iloc[-1] > df['BBM'].iloc[-1]:
|
180
|
+
self.bbm_signal = "BUY"
|
181
|
+
elif df['close'].iloc[-1] < df['BBM'].iloc[-1]:
|
182
|
+
self.bbm_signal = "SELL"
|
183
|
+
else:
|
184
|
+
self.bbm_signal = "HOLD"
|
166
185
|
else:
|
167
186
|
# Drop BBM columns if not used
|
168
187
|
df.drop(['BBL', 'BBM', 'BBU', 'BBM_slope', 'BBM_angle'], axis=1, inplace=True, errors='ignore')
|
@@ -170,14 +189,14 @@ class Interval:
|
|
170
189
|
# Calculate the RSI
|
171
190
|
if self.settings.RSI_STUDY:
|
172
191
|
df['rsi_fast'] = talib.RSI(df['close'],timeperiod=self.settings.RSI_FAST)
|
173
|
-
df.
|
192
|
+
df.fillna({'rsi_fast':0}, inplace=True)
|
174
193
|
df['rsi_fast_slope'] = df['rsi_fast'].diff()
|
175
194
|
df['rsi_fast_angle'] = np.degrees(np.arctan(df['rsi_fast_slope']))
|
176
195
|
df.fillna({'rsi_fast_slope':0}, inplace=True)
|
177
196
|
df.fillna({'rsi_fast_angle':0}, inplace=True)
|
178
197
|
|
179
198
|
df['rsi_slow'] = talib.RSI(df['close'],timeperiod=self.settings.RSI_SLOW)
|
180
|
-
df.
|
199
|
+
df.fillna({'rsi_slow':0}, inplace=True)
|
181
200
|
df['rsi_slow_slope'] = df['rsi_slow'].diff()
|
182
201
|
df['rsi_slow_angle'] = np.degrees(np.arctan(df['rsi_slow_slope']))
|
183
202
|
df.fillna({'rsi_slow_slope':0}, inplace=True)
|
@@ -185,54 +204,79 @@ class Interval:
|
|
185
204
|
|
186
205
|
|
187
206
|
if self.settings.RSI_CROSSING:
|
188
|
-
if df
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
207
|
+
if df is not None and len(df) >= 2:
|
208
|
+
if df['rsi_fast'].iloc[-2] <= df['rsi_slow'].iloc[-2] and df['rsi_fast'].iloc[-1] > df['rsi_slow'].iloc[-1]:
|
209
|
+
self.rsi_signal = "BUY"
|
210
|
+
elif df['rsi_fast'].iloc[-2] >= df['rsi_slow'].iloc[-2] and df['rsi_fast'].iloc[-1] < df['rsi_slow'].iloc[-1]:
|
211
|
+
self.rsi_signal = "SELL"
|
212
|
+
else:
|
213
|
+
self.rsi_signal = "HOLD"
|
194
214
|
else:
|
195
|
-
if df
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
215
|
+
if df is not None and len(df) >= 1:
|
216
|
+
if df['rsi_fast'].iloc[-1] > df['rsi_slow'].iloc[-1]:
|
217
|
+
self.rsi_signal = "BUY"
|
218
|
+
elif df['rsi_fast'].iloc[-1] < df['rsi_slow'].iloc[-1]:
|
219
|
+
self.rsi_signal = "SELL"
|
220
|
+
else:
|
221
|
+
self.rsi_signal = "HOLD"
|
201
222
|
else:
|
202
223
|
# Drop RSI columns if not used
|
203
224
|
df.drop(['rsi_fast', 'rsi_slow', 'rsi_slope', 'rsi_angle'], axis=1, inplace=True, errors='ignore')
|
204
225
|
|
226
|
+
#Trendline
|
227
|
+
if self.settings.TRENDLINE_STUDY:
|
228
|
+
loopback = len(df)
|
229
|
+
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)
|
230
|
+
if df_sr is not None and len(df_sr) >= 1:
|
231
|
+
df['support_line'] = df_sr['support_line']
|
232
|
+
df['resistance_line'] = df_sr['resistance_line']
|
233
|
+
df.fillna({'support_line':0}, inplace=True)
|
234
|
+
df.fillna({'resistance_line':0}, inplace=True)
|
235
|
+
if df is not None and len(df) >= 2:
|
236
|
+
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]:
|
237
|
+
self.trendline_signal = "SELL"
|
238
|
+
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]:
|
239
|
+
self.trendline_signal = "BUY"
|
240
|
+
elif df['close'].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]:
|
241
|
+
self.trendline_signal = "BUY"
|
242
|
+
elif df['close'].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]:
|
243
|
+
self.trendline_signal = "SELL"
|
244
|
+
else:
|
245
|
+
self.trendline_signal = "HOLD"
|
246
|
+
|
205
247
|
# Calculate the ADX
|
206
248
|
if self.settings.ADX_STUDY:
|
207
|
-
df
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
249
|
+
if df is not None and len(df) >= 1:
|
250
|
+
df['ADX'] = talib.ADX(df['high'], df['low'], df['close'], timeperiod=self.settings.ADX_PERIOD)
|
251
|
+
df.fillna({'ADX':0}, inplace=True)
|
252
|
+
if df['ADX'].iloc[-1] > 25:
|
253
|
+
self.adx_signal = "STRONG"
|
254
|
+
else:
|
255
|
+
self.adx_signal = "WEAK"
|
213
256
|
else:
|
214
257
|
# Drop ADX columns if not used
|
215
258
|
df.drop(['ADX'], axis=1, inplace=True, errors='ignore')
|
216
259
|
|
217
260
|
# Calculate the close proximity
|
218
261
|
if self.settings.CANDLE_TREND_STUDY:
|
219
|
-
df
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
262
|
+
if df is not None and len(df) >= 1:
|
263
|
+
df['range'] = df['high'] - df['low']
|
264
|
+
df['candle_trend'] = ((df['close'] - df['low'])/df['range'])*100
|
265
|
+
df.fillna({'candle_trend':0}, inplace=True)
|
266
|
+
df.fillna({'range':0}, inplace=True)
|
267
|
+
|
268
|
+
#open and close criteria
|
269
|
+
if df['candle_trend'].iloc[-1] > 70:
|
270
|
+
self.candle_trend = 'BULLISH'
|
271
|
+
elif df['candle_trend'].iloc[-1] < 30:
|
272
|
+
self.candle_trend = 'BEARISH'
|
273
|
+
else:
|
274
|
+
self.candle_trend = 'HOLD'
|
231
275
|
else:
|
232
276
|
# Drop candle trend columns if not used
|
233
277
|
df.drop(['candle_trend', 'range'], axis=1, inplace=True, errors='ignore')
|
234
278
|
|
235
|
-
|
279
|
+
|
236
280
|
#replace infinity
|
237
281
|
df.replace([np.inf, -np.inf], 0, inplace=True)
|
238
282
|
|
@@ -247,7 +291,8 @@ class Interval:
|
|
247
291
|
(self.settings.EMA_STUDY and self.settings.EMA_CHECK_ON_OPEN and self.ema_open_signal == "BUY") or
|
248
292
|
(self.settings.MACD_STUDY and self.settings.MACD_CHECK_ON_OPEN and self.macd_signal == "BUY") or
|
249
293
|
(self.settings.BBM_STUDY and self.settings.BBM_CHECK_ON_OPEN and self.bbm_signal == "BUY") or
|
250
|
-
(self.settings.RSI_STUDY and self.settings.RSI_CHECK_ON_OPEN and self.rsi_signal == "BUY")
|
294
|
+
(self.settings.RSI_STUDY and self.settings.RSI_CHECK_ON_OPEN and self.rsi_signal == "BUY") or
|
295
|
+
(self.settings.TRENDLINE_STUDY and self.settings.TRENDLINE_CHECK_ON_OPEN and self.trendline_signal == "BUY")
|
251
296
|
)
|
252
297
|
additional_buy_conditions = (
|
253
298
|
(not self.settings.ADX_STUDY or not self.settings.ADX_CHECK_ON_OPEN or self.adx_signal == "STRONG") or
|
@@ -259,7 +304,8 @@ class Interval:
|
|
259
304
|
(self.settings.EMA_STUDY and self.settings.EMA_CHECK_ON_OPEN and self.ema_open_signal == "SELL") or
|
260
305
|
(self.settings.MACD_STUDY and self.settings.MACD_CHECK_ON_OPEN and self.macd_signal == "SELL") or
|
261
306
|
(self.settings.BBM_STUDY and self.settings.BBM_CHECK_ON_OPEN and self.bbm_signal == "SELL") or
|
262
|
-
(self.settings.RSI_STUDY and self.settings.RSI_CHECK_ON_OPEN and self.rsi_signal == "SELL")
|
307
|
+
(self.settings.RSI_STUDY and self.settings.RSI_CHECK_ON_OPEN and self.rsi_signal == "SELL") or
|
308
|
+
(self.settings.TRENDLINE_STUDY and self.settings.TRENDLINE_CHECK_ON_OPEN and self.trendline_signal == "SELL")
|
263
309
|
)
|
264
310
|
additional_sell_conditions = (
|
265
311
|
(self.settings.ADX_STUDY and self.settings.ADX_CHECK_ON_OPEN and self.adx_signal == "STRONG") or
|
@@ -282,7 +328,8 @@ class Interval:
|
|
282
328
|
(not self.settings.EMA_STUDY or not self.settings.EMA_CHECK_ON_OPEN or self.ema_open_signal == "BUY") and
|
283
329
|
(not self.settings.MACD_STUDY or not self.settings.MACD_CHECK_ON_OPEN or self.macd_signal == "BUY") and
|
284
330
|
(not self.settings.BBM_STUDY or not self.settings.BBM_CHECK_ON_OPEN or self.bbm_signal == "BUY") and
|
285
|
-
(not self.settings.RSI_STUDY or not self.settings.RSI_CHECK_ON_OPEN or self.rsi_signal == "BUY")
|
331
|
+
(not self.settings.RSI_STUDY or not self.settings.RSI_CHECK_ON_OPEN or self.rsi_signal == "BUY") and
|
332
|
+
(not self.settings.TRENDLINE_STUDY or not self.settings.TRENDLINE_CHECK_ON_OPEN or self.trendline_signal == "BUY")
|
286
333
|
)
|
287
334
|
additional_buy_conditions = (
|
288
335
|
(not self.settings.ADX_STUDY or not self.settings.ADX_CHECK_ON_OPEN or self.adx_signal == "STRONG") and
|
@@ -294,7 +341,8 @@ class Interval:
|
|
294
341
|
(not self.settings.EMA_STUDY or not self.settings.EMA_CHECK_ON_OPEN or self.ema_open_signal == "SELL") and
|
295
342
|
(not self.settings.MACD_STUDY or not self.settings.MACD_CHECK_ON_OPEN or self.macd_signal == "SELL") and
|
296
343
|
(not self.settings.BBM_STUDY or not self.settings.BBM_CHECK_ON_OPEN or self.bbm_signal == "SELL") and
|
297
|
-
(not self.settings.RSI_STUDY or not self.settings.RSI_CHECK_ON_OPEN or self.rsi_signal == "SELL")
|
344
|
+
(not self.settings.RSI_STUDY or not self.settings.RSI_CHECK_ON_OPEN or self.rsi_signal == "SELL") and
|
345
|
+
(not self.settings.TRENDLINE_STUDY or not self.settings.TRENDLINE_CHECK_ON_OPEN or self.trendline_signal == "SELL")
|
298
346
|
)
|
299
347
|
additional_sell_conditions = (
|
300
348
|
(not self.settings.ADX_STUDY or not self.settings.ADX_CHECK_ON_OPEN or self.adx_signal == "STRONG") and
|
@@ -312,7 +360,7 @@ class Interval:
|
|
312
360
|
|
313
361
|
except Exception as e:
|
314
362
|
logger.info(f"Function: calculate_study, {e}, {e.args}, {type(e).__name__}")
|
315
|
-
logger.info(traceback.
|
363
|
+
logger.info(traceback.print_exc())
|
316
364
|
retval = df.to_dict('records')
|
317
365
|
del df
|
318
366
|
gc.collect()
|
@@ -404,7 +452,8 @@ class Ticker:
|
|
404
452
|
self._intervals = intervals
|
405
453
|
|
406
454
|
def get_interval_ticks(self, intervalId):
|
407
|
-
|
455
|
+
interval = self._intervals.get(intervalId, None)
|
456
|
+
return interval
|
408
457
|
|
409
458
|
def set_interval_ticks(self, intervalId, new_value):
|
410
459
|
self._intervals[intervalId] = new_value
|
@@ -443,6 +492,7 @@ class Ticker:
|
|
443
492
|
ticks_interval=intervalObj.get_data()
|
444
493
|
if ticks_interval is None:
|
445
494
|
return
|
495
|
+
#print('create_bar_with_last_and_ts', self.symbol, intervalId, len(ticks_interval))
|
446
496
|
if len(ticks_interval)==0 or self._ts - ticks_interval[-1]['time'] >= intervalObj.delta:
|
447
497
|
ticks_interval.append(new_item)
|
448
498
|
current_bar = ticks_interval[-1]
|
@@ -457,11 +507,12 @@ class Ticker:
|
|
457
507
|
current_bar['barcolor'] = self.red if current_bar['close'] <= current_bar['open'] else self.green
|
458
508
|
#print(f'{self.symbol} {self._last}, {self._ts} {intervalId} {ticks_interval[-1]['time']} c' )
|
459
509
|
|
510
|
+
#print ('create_bar_with_last_and_ts', ticks_interval[-1])
|
460
511
|
intervalObj.set_data(ticks_interval)
|
461
512
|
|
462
513
|
except Exception as e:
|
463
514
|
logger.info(f"Function: create_bar_with_last_and_ts, {e}, {e.args}, {type(e).__name__}")
|
464
|
-
logger.info(traceback.
|
515
|
+
logger.info(traceback.print_exc())
|
465
516
|
|
466
517
|
|
467
518
|
class Tickers:
|
@@ -560,24 +611,30 @@ class Tickers:
|
|
560
611
|
# since the calulation is done at a different thread and uses a different memory space,
|
561
612
|
# the caluated value has to be reassigned to the original ticker class instance from the caluated ticker instance
|
562
613
|
def form_candle(self, tuples_list):
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
614
|
+
if not self.settings.CPU_PROCCESING:
|
615
|
+
for args in tuples_list:
|
616
|
+
ticker_obj, last, ts = args
|
617
|
+
result = self.process_ticker_candle(args)
|
618
|
+
else:
|
619
|
+
with ProcessPoolExecutor() as executor:
|
620
|
+
results = executor.map(self.process_ticker_candle, tuples_list, chunksize=10)
|
621
|
+
for args, result in zip(tuples_list, results):
|
622
|
+
for intervalId, interval in result[3].items():
|
623
|
+
if not interval._data is None and len(interval._data) >= self.settings.BARS:
|
624
|
+
args[0].get_interval_ticks(intervalId)._data = interval._data
|
625
|
+
args[0].get_interval_ticks(intervalId).current_signal = interval.current_signal
|
626
|
+
args[0].get_interval_ticks(intervalId).ema_open_signal = interval.ema_open_signal
|
627
|
+
args[0].get_interval_ticks(intervalId).ema_close_signal = interval.ema_close_signal
|
628
|
+
args[0].get_interval_ticks(intervalId).macd_signal = interval.macd_signal
|
629
|
+
args[0].get_interval_ticks(intervalId).bbm_signal = interval.bbm_signal
|
630
|
+
args[0].get_interval_ticks(intervalId).rsi_signal = interval.rsi_signal
|
631
|
+
args[0].get_interval_ticks(intervalId).trendline_signal, = interval.trendline_signal,
|
632
|
+
args[0].get_interval_ticks(intervalId).candle_trend = interval.candle_trend
|
633
|
+
args[0].get_interval_ticks(intervalId).adx_signal = interval.adx_signal
|
634
|
+
args[0].get_interval_ticks(intervalId).signal_strength = interval.signal_strength
|
635
|
+
args[0]._last = result[0]
|
636
|
+
args[0].lastcolor = result[1]
|
637
|
+
args[0]._ts = result[2]
|
581
638
|
|
582
639
|
def getCurrentData(self, period):
|
583
640
|
current_data = []
|
@@ -596,33 +653,33 @@ class Tickers:
|
|
596
653
|
ticks = intervalObj.get_data()
|
597
654
|
if not ticks:
|
598
655
|
continue
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
656
|
+
lastcandle = intervalObj.get_data()[-1]
|
657
|
+
if len(lastcandle) >= 20:
|
658
|
+
new_row = {
|
659
|
+
'symbol' : symbol,
|
660
|
+
f"{period}_trend": intervalObj.current_signal,
|
661
|
+
f"{period}_cb": intervalObj.signal_strength,
|
662
|
+
f"{period}_barcolor": lastcandle['barcolor'],
|
663
|
+
f"{period}_ema_open": intervalObj.ema_open_signal,
|
664
|
+
f"{period}_ema_close": intervalObj.ema_close_signal,
|
665
|
+
f"{period}_macd":intervalObj.macd_signal,
|
666
|
+
f"{period}_bbm":intervalObj.bbm_signal,
|
667
|
+
f"{period}_rsi":intervalObj.rsi_signal,
|
668
|
+
f"{period}_trendline": intervalObj.trendline_signal,
|
669
|
+
f"{period}_adx":intervalObj.adx_signal,
|
670
|
+
f"{period}_candle_trend":intervalObj.candle_trend,
|
671
|
+
'bid' : bid,
|
672
|
+
'bidcolor' : bidcolor,
|
673
|
+
'last' : last,
|
674
|
+
'lastcolor' : lastcolor,
|
675
|
+
'ask' : ask,
|
676
|
+
'askcolor' : askcolor,
|
677
|
+
f"{period}_open": lastcandle['open'],
|
678
|
+
f"{period}_close": lastcandle['close'],
|
679
|
+
f"{period}_high": lastcandle['high'],
|
680
|
+
f"{period}_low": lastcandle['low'],
|
681
|
+
}
|
682
|
+
current_data.append(new_row)
|
626
683
|
|
627
684
|
df = pd.DataFrame(current_data)
|
628
685
|
if not df.empty:
|
@@ -630,7 +687,7 @@ class Tickers:
|
|
630
687
|
'lastcolor': "", 'bidcolor': "", 'askcolor': "", 'bid': 0.0, 'ask': 0.0, 'last': 0.0,
|
631
688
|
f"{period}_cb":0, f"{period}_barcolor": "", f"{period}_trend": "",
|
632
689
|
f"{period}_open": 0.0, f"{period}_close": 0.0, f"{period}_high": 0.0, f"{period}_low": 0.0,
|
633
|
-
f"{period}_ema": "", f"{period}_macd": "", f"{period}_bbm": "", f"{period}_rsi": "", f"{period}_candle_trend": "", f"{period}_adx": ""
|
690
|
+
f"{period}_ema": "", f"{period}_macd": "", f"{period}_bbm": "", f"{period}_rsi": "", f"{period}_trendline": "", f"{period}_candle_trend": "", f"{period}_adx": ""
|
634
691
|
}
|
635
692
|
df.fillna(fill_values, inplace=True)
|
636
693
|
df.set_index("symbol", inplace=True, drop=False)
|
@@ -639,19 +696,21 @@ class Tickers:
|
|
639
696
|
trending_conditions = [
|
640
697
|
(
|
641
698
|
(df[f'{period}_trend']=='BUY') &
|
642
|
-
(df[f'{period}_cb']>1)
|
643
|
-
|
699
|
+
(df[f'{period}_cb']>1)
|
700
|
+
#&
|
701
|
+
#(df[f'{period}_barcolor']==self.green)
|
644
702
|
),
|
645
703
|
(
|
646
704
|
(df[f'{period}_trend']=='SELL') &
|
647
|
-
(df[f'{period}_cb']>1)
|
648
|
-
|
705
|
+
(df[f'{period}_cb']>1)
|
706
|
+
#&
|
707
|
+
#(df[f'{period}_barcolor']==self.red)
|
649
708
|
)
|
650
709
|
]
|
651
710
|
self.signaldf_filtered = df[np.any(trending_conditions, axis=0)].copy()
|
652
711
|
except Exception as e:
|
653
712
|
logger.info(f"Function: getCurrentData, {e}, {e.args}, {type(e).__name__}")
|
654
|
-
logger.info(traceback.
|
713
|
+
logger.info(traceback.print_exc())
|
655
714
|
finally:
|
656
715
|
del df, current_data
|
657
716
|
gc.collect()
|
@@ -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,13 @@ 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_PEAK_DISTANCE: int = Field(default=1, ge=0, le=30)
|
65
|
+
TRENDLINE_LOOKBACK: int = Field(default=20, ge=10, le=200)
|
66
|
+
TRENDLINE_CHECK_ON_OPEN: bool = Field(default=False)
|
67
|
+
TRENDLINE_CHECK_ON_CLOSE: bool = Field(default=False)
|
68
|
+
|
62
69
|
ADX_STUDY: bool = Field(default=True)
|
63
70
|
ADX_CHECK_ON_OPEN: bool = Field(default=False)
|
64
71
|
ADX_CHECK_ON_CLOSE: bool = Field(default=False)
|
@@ -84,6 +91,9 @@ class Settings(BaseSettings):
|
|
84
91
|
# Logger
|
85
92
|
VERBOSE_LOGGING: bool = Field(default=False)
|
86
93
|
|
94
|
+
#use CPU
|
95
|
+
CPU_PROCCESING: bool = Field(default=True)
|
96
|
+
|
87
97
|
# Benchmark
|
88
98
|
BENCHMARK: bool = Field(default=False)
|
89
99
|
|
@@ -0,0 +1,18 @@
|
|
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=TOseCeTNpCwVcReXRvHoq1sQybhoiIkFvATbu05wQ8c,67215
|
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=JTlXUL9GadRjloK6Gw2HhacjvIe31CiiIFl1KGkWztA,37444
|
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=zeNmV1KK8Yv5cHzBxaly0lmcjwSZEBAMUm_OvFSBCK4,4922
|
13
|
+
bitunix_automated_crypto_trading/logger.py,sha256=tAaTQcv5r--zupk_Fhfe1USfBAzSiXzVa4QRnaOFIFY,2933
|
14
|
+
bitunix_automated_crypto_trading-3.1.5.dist-info/METADATA,sha256=YJdYd5cjlCI7XA-Ef9WMVDCnblI22sSxyX--HmMcK-Q,996
|
15
|
+
bitunix_automated_crypto_trading-3.1.5.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
|
16
|
+
bitunix_automated_crypto_trading-3.1.5.dist-info/entry_points.txt,sha256=UXREYHuSl2XYd_tOtLIq0zg3d1kX3lixX5SpN8yGBw4,82
|
17
|
+
bitunix_automated_crypto_trading-3.1.5.dist-info/top_level.txt,sha256=uyFzHUCOsp8elnG2Ovor6xXcf7dxRxY-C-Txiwix64Q,33
|
18
|
+
bitunix_automated_crypto_trading-3.1.5.dist-info/RECORD,,
|
@@ -1,17 +0,0 @@
|
|
1
|
-
bitunix_automated_crypto_trading/AsyncThreadRunner.py,sha256=LJ6ny1KSCVoIbfeNypsY4aHYcbkGME9M3epQ9e8B1O8,3224
|
2
|
-
bitunix_automated_crypto_trading/BitunixApi.py,sha256=gmhWi83k4EHOMIY7ZPa3x2NuIgVyzH13Sjk9AlVKjtQ,11163
|
3
|
-
bitunix_automated_crypto_trading/BitunixSignal.py,sha256=z8-mvwZOR1VAAmrgy9HjgFyv-IRx1GxogcXjwCPJ4oY,64687
|
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=g9Imr3uJijWeLqJbmCDrcDBwCHaBXV6P6sse73akmTA,33082
|
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.2.dist-info/METADATA,sha256=5hhgVdOaJ-kqG0zdmen-OXVGpvBtXJxXjoU-tg_8-WI,996
|
14
|
-
bitunix_automated_crypto_trading-3.1.2.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
15
|
-
bitunix_automated_crypto_trading-3.1.2.dist-info/entry_points.txt,sha256=UXREYHuSl2XYd_tOtLIq0zg3d1kX3lixX5SpN8yGBw4,82
|
16
|
-
bitunix_automated_crypto_trading-3.1.2.dist-info/top_level.txt,sha256=uyFzHUCOsp8elnG2Ovor6xXcf7dxRxY-C-Txiwix64Q,33
|
17
|
-
bitunix_automated_crypto_trading-3.1.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|