bitunix-automated-crypto-trading 3.1.7__tar.gz → 3.1.8__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/PKG-INFO +1 -1
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/README.md +16 -4
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/bitunix_automated_crypto_trading/AsyncThreadRunner.py +8 -8
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/bitunix_automated_crypto_trading/BitunixApi.py +12 -13
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/bitunix_automated_crypto_trading/BitunixSignal.py +83 -47
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/bitunix_automated_crypto_trading/BitunixWebSocket.py +21 -21
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/bitunix_automated_crypto_trading/NotificationManager.py +3 -3
- bitunix_automated_crypto_trading-3.1.8/bitunix_automated_crypto_trading/SupportResistance.py +117 -0
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/bitunix_automated_crypto_trading/ThreadManager.py +4 -3
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/bitunix_automated_crypto_trading/TickerManager.py +56 -47
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/bitunix_automated_crypto_trading/bitunix.py +42 -16
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/bitunix_automated_crypto_trading/config.py +8 -2
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/bitunix_automated_crypto_trading/logger.py +1 -1
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/bitunix_automated_crypto_trading.egg-info/PKG-INFO +1 -1
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/setup.py +1 -1
- bitunix_automated_crypto_trading-3.1.7/bitunix_automated_crypto_trading/SupportResistance.py +0 -89
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/bitunix_automated_crypto_trading/DataFrameHtmlRenderer.py +0 -0
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/bitunix_automated_crypto_trading/__init__.py +0 -0
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/bitunix_automated_crypto_trading.egg-info/SOURCES.txt +0 -0
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/bitunix_automated_crypto_trading.egg-info/dependency_links.txt +0 -0
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/bitunix_automated_crypto_trading.egg-info/entry_points.txt +0 -0
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/bitunix_automated_crypto_trading.egg-info/requires.txt +0 -0
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/bitunix_automated_crypto_trading.egg-info/top_level.txt +0 -0
- {bitunix_automated_crypto_trading-3.1.7 → bitunix_automated_crypto_trading-3.1.8}/setup.cfg +0 -0
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
A real-time cryptocurrency trading platform built with FastAPI and WebSocket technology using Bitunix API and websockets for Futures. The platform provides automated trading capabilities, real-time market data visualization, and portfolio management features.
|
4
4
|
|
5
|
+
Supports running the app in multiple instances (bots) with each instance having its own configuration file and env file containing the API keys and other parameters.
|
6
|
+
|
5
7
|
## Features
|
6
8
|
- Real-time private account/new postion/new order data streaming via WebSocket
|
7
9
|
- Real-time public depth/kline for bid, ask and last streaming via WebSocket or thru api calls, configurable in the config file
|
@@ -61,7 +63,6 @@ The platform can be configured through the `config.py` file or `config.txt`. Key
|
|
61
63
|
- `BARS`: Number of bars to use for study and charting
|
62
64
|
|
63
65
|
- `Technical Indicators Parameters`:
|
64
|
-
- `BOS_PERIOD`: Break of structure period, number of bars to look back to determine the previous high or low
|
65
66
|
- `MA_FAST`: Fast moving average period
|
66
67
|
- `MA_MEDIUM`: Medium moving average period
|
67
68
|
- `MA_SLOW`: Slow moving average period
|
@@ -75,7 +76,6 @@ The platform can be configured through the `config.py` file or `config.txt`. Key
|
|
75
76
|
- `ADX_PERIOD`: ADX period
|
76
77
|
|
77
78
|
- `Study Parameters`:
|
78
|
-
- `BOS_STUDY`: Enable Break Of Structure study
|
79
79
|
- `EMA_STUDY`: Enable EMA study
|
80
80
|
- `EMA_CHART`: Display EMA chart
|
81
81
|
- `EMA_STUDY`: Enable EMA study
|
@@ -102,6 +102,12 @@ The platform can be configured through the `config.py` file or `config.txt`. Key
|
|
102
102
|
- `RSI_CHECK_ON_OPEN`: Check RSI on open
|
103
103
|
- `RSI_CHECK_ON_CLOSE`: Check RSI on close
|
104
104
|
|
105
|
+
- `BOS_PERIOD`: Break of structure period, number of bars to look back to determine the previous high or low
|
106
|
+
- `BOS_STUDY`: Enable Break Of Structure study
|
107
|
+
- `BOS_CHART`: Display Break Of Structure chart
|
108
|
+
- `BOS_CHECK_ON_OPEN`: Check bos support and resistance on open
|
109
|
+
- `BOS_CHECK_ON_CLOSE`: Check bos support and resistance on close
|
110
|
+
|
105
111
|
- `TRENDLINE_PEAK_DISTANCE` : distance between peaks or troughs
|
106
112
|
- `TRENDLINE_STUDY`: Enable Trendline support and resistance study
|
107
113
|
- `TRENDLINE_CHART`: Display Trendline chart
|
@@ -162,6 +168,7 @@ The platform can be configured through the `config.py` file or `config.txt`. Key
|
|
162
168
|
- Take Profit amount
|
163
169
|
- Accept Loss amount
|
164
170
|
- BOS study is basically when the price breakout from the previous high or low
|
171
|
+
- Trendline study is basically when the price breakout from the trendline support or resistance
|
165
172
|
- You can control the study like Moving Average, MACD, Bollinger Band, RSI or close proximity to high or low of the candle using the env file
|
166
173
|
- You can control the trading strategy using the CalculateStudy function in TickerManager.py and AutoTradeProcess function in BitunixSignal.py
|
167
174
|
- Changes are activated by unchecking and checking the AutoTrade checkbox
|
@@ -193,7 +200,7 @@ The platform can be configured through the `config.py` file or `config.txt`. Key
|
|
193
200
|
apt-get install -y python3-pip wget unzip dos2unix && \
|
194
201
|
python3 -m pip install --upgrade pip && \
|
195
202
|
mkdir bitunix && cd bitunix && \
|
196
|
-
wget https://github.com/tcj2001/bitunix-automated-crypto-trading/archive/refs/tags/
|
203
|
+
wget https://github.com/tcj2001/bitunix-automated-crypto-trading/archive/refs/tags/v3.1.8.tar.gz -O bitunix.tar.gz && \
|
197
204
|
mkdir code && \
|
198
205
|
tar --strip-components=1 -xvzf bitunix.tar.gz -C code && \
|
199
206
|
cd code && \
|
@@ -201,7 +208,12 @@ The platform can be configured through the `config.py` file or `config.txt`. Key
|
|
201
208
|
cp sampleenv.txt .env"
|
202
209
|
- The package will be installed in the bitunix/code directory
|
203
210
|
- cd bitunix/code
|
204
|
-
- python3 bitunix.py
|
211
|
+
- python3 bitunix.py .env config.txt 8000 (This is the default even if you dont pass these)
|
212
|
+
|
213
|
+
For multiple instance or bots
|
214
|
+
- python3 bitunix.py .env1 bot1.txt 8001
|
215
|
+
- python3 bitunix.py .env2 bot2.txt 8002
|
216
|
+
|
205
217
|
|
206
218
|
- Windows
|
207
219
|
- mkdir c:\bitunix
|
@@ -2,10 +2,9 @@ import asyncio
|
|
2
2
|
import threading
|
3
3
|
from logger import Logger
|
4
4
|
import os
|
5
|
-
logger = Logger(__name__).get_logger()
|
6
5
|
|
7
6
|
class AsyncThreadRunner:
|
8
|
-
def __init__(self, async_func, interval, *args, **kwargs):
|
7
|
+
def __init__(self, async_func, logger, interval, *args, **kwargs):
|
9
8
|
self.async_func = async_func
|
10
9
|
self.interval = interval
|
11
10
|
self.args = args
|
@@ -14,6 +13,7 @@ class AsyncThreadRunner:
|
|
14
13
|
self._stop_event = threading.Event()
|
15
14
|
self.thread = threading.Thread(target=self.thread_function)
|
16
15
|
self.task = None
|
16
|
+
self.logger = logger
|
17
17
|
|
18
18
|
def thread_function(self):
|
19
19
|
asyncio.set_event_loop(self.loop)
|
@@ -28,7 +28,7 @@ class AsyncThreadRunner:
|
|
28
28
|
)
|
29
29
|
self.loop.run_forever()
|
30
30
|
except Exception as e:
|
31
|
-
logger.info(f"Async Thread function error: {e}, exiting app")
|
31
|
+
self.logger.info(f"Async Thread function error: {e}, exiting app")
|
32
32
|
os._exit(1) # Exit the program if the thread is stopped
|
33
33
|
finally:
|
34
34
|
pending = asyncio.all_tasks(self.loop)
|
@@ -47,10 +47,10 @@ class AsyncThreadRunner:
|
|
47
47
|
try:
|
48
48
|
await self.async_func(*self.args, **self.kwargs)
|
49
49
|
except Exception as e:
|
50
|
-
logger.info(f"error in periodic_run async thread {self.thread.name} {e}")
|
50
|
+
self.logger.info(f"error in periodic_run async thread {self.thread.name} {e}")
|
51
51
|
os._exit(1) # Exit the program if the thread is stopped
|
52
52
|
await asyncio.sleep(self.interval)
|
53
|
-
logger.info(f"periodic {self.thread.name} Thread stopped, exiting app.")
|
53
|
+
self.logger.info(f"periodic {self.thread.name} Thread stopped, exiting app.")
|
54
54
|
os._exit(1) # Exit the program if the thread is stopped
|
55
55
|
except asyncio.CancelledError:
|
56
56
|
pass
|
@@ -70,13 +70,13 @@ class AsyncThreadRunner:
|
|
70
70
|
try:
|
71
71
|
await asyncio.wrap_future(self.task) # Wait for the cancellation to propagate
|
72
72
|
except asyncio.CancelledError:
|
73
|
-
logger.info(f"{self.thread.name} Task cancelled successfully.")
|
73
|
+
self.logger.info(f"{self.thread.name} Task cancelled successfully.")
|
74
74
|
except Exception as e:
|
75
|
-
logger.error(f"Unexpected error while cancelling the task {self.thread.name}: {e}")
|
75
|
+
self.logger.error(f"Unexpected error while cancelling the task {self.thread.name}: {e}")
|
76
76
|
|
77
77
|
# Stop the event loop safely
|
78
78
|
self.loop.call_soon_threadsafe(self.loop.stop)
|
79
79
|
|
80
80
|
# Wait for the thread to join
|
81
81
|
self.thread.join()
|
82
|
-
logger.info(f"{self.thread.name} Thread stopped and event loop cleaned up.")
|
82
|
+
self.logger.info(f"{self.thread.name} Thread stopped and event loop cleaned up.")
|
@@ -9,15 +9,14 @@ from urllib.parse import urlencode
|
|
9
9
|
from typing import Dict, Any
|
10
10
|
import traceback
|
11
11
|
from logger import Logger
|
12
|
-
logger = Logger(__name__).get_logger()
|
13
12
|
|
14
13
|
|
15
14
|
class BitunixApi:
|
16
15
|
|
17
|
-
def __init__(self, api_key, secret_key, settings):
|
16
|
+
def __init__(self, api_key, secret_key, settings, logger):
|
18
17
|
self.api_key = api_key
|
19
18
|
self.secret_key = secret_key
|
20
|
-
|
19
|
+
self.logger = logger
|
21
20
|
self.session = requests.Session()
|
22
21
|
self.session.headers.update({
|
23
22
|
'Content-Type': 'application/json',
|
@@ -115,7 +114,7 @@ class BitunixApi:
|
|
115
114
|
headers["sign"] = signature
|
116
115
|
|
117
116
|
response = self.session.post(endpoint, data=body_string, headers=headers)
|
118
|
-
logger.info(f"Response: {body_string} {response.json()}")
|
117
|
+
self.logger.info(f"Response: {body_string} {response.json()}")
|
119
118
|
response.raise_for_status()
|
120
119
|
return response.json()
|
121
120
|
|
@@ -153,28 +152,28 @@ class BitunixApi:
|
|
153
152
|
if tradeHistory['code']==0:
|
154
153
|
return tradeHistory['data']
|
155
154
|
else:
|
156
|
-
logger.info(tradeHistory['msg'])
|
155
|
+
self.logger.info(tradeHistory['msg'])
|
157
156
|
|
158
157
|
async def GetPendingOrderData(self,dictparm={}):
|
159
158
|
orders=await self._get_authenticated(self.pending_order_url, dictparm)
|
160
159
|
if orders['code']==0:
|
161
160
|
return orders['data']
|
162
161
|
else:
|
163
|
-
logger.info(orders['msg'])
|
162
|
+
self.logger.info(orders['msg'])
|
164
163
|
|
165
164
|
async def GetPendingPositionData(self, dictparm={}):
|
166
165
|
positions=await self._get_authenticated(self.pending_positions_URL, dictparm)
|
167
166
|
if positions['code']==0:
|
168
167
|
return positions['data']
|
169
168
|
else:
|
170
|
-
logger.info(positions['msg'])
|
169
|
+
self.logger.info(positions['msg'])
|
171
170
|
|
172
171
|
async def GetPositionHistoryData(self, dictparm={}):
|
173
172
|
tradeHistory=await self._get_authenticated(self.position_history_Url, dictparm)
|
174
173
|
if tradeHistory['code']==0:
|
175
174
|
return tradeHistory['data']
|
176
175
|
else:
|
177
|
-
logger.info(tradeHistory['msg'])
|
176
|
+
self.logger.info(tradeHistory['msg'])
|
178
177
|
|
179
178
|
|
180
179
|
async def GetportfolioData(self):
|
@@ -182,7 +181,7 @@ class BitunixApi:
|
|
182
181
|
if portfolio['code']==0:
|
183
182
|
return portfolio['data']
|
184
183
|
else:
|
185
|
-
logger.info(portfolio['msg'])
|
184
|
+
self.logger.info(portfolio['msg'])
|
186
185
|
|
187
186
|
|
188
187
|
async def GetTickerslastPrice(self, tickersStr):
|
@@ -223,7 +222,7 @@ class BitunixApi:
|
|
223
222
|
except Exception as e:
|
224
223
|
stack = traceback.extract_stack()
|
225
224
|
function_name = stack[-2].name
|
226
|
-
logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
|
225
|
+
self.logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
|
227
226
|
|
228
227
|
async def GetTickerData(self):
|
229
228
|
try:
|
@@ -235,7 +234,7 @@ class BitunixApi:
|
|
235
234
|
except Exception as e:
|
236
235
|
stack = traceback.extract_stack()
|
237
236
|
function_name = stack[-2].name
|
238
|
-
logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
|
237
|
+
self.logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
|
239
238
|
|
240
239
|
async def GetDepthData(self,symbol,limit):
|
241
240
|
try:
|
@@ -247,7 +246,7 @@ class BitunixApi:
|
|
247
246
|
except Exception as e:
|
248
247
|
stack = traceback.extract_stack()
|
249
248
|
function_name = stack[-2].name
|
250
|
-
logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
|
249
|
+
self.logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
|
251
250
|
|
252
251
|
async def GetKlineHistory(self, ticker, interval, limit, starttime):
|
253
252
|
data = []
|
@@ -270,7 +269,7 @@ class BitunixApi:
|
|
270
269
|
except Exception as e:
|
271
270
|
stack = traceback.extract_stack()
|
272
271
|
function_name = stack[-2].name
|
273
|
-
logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
|
272
|
+
self.logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
|
274
273
|
|
275
274
|
def __del__(self):
|
276
275
|
"""Cleanup method to close the session"""
|
@@ -13,7 +13,6 @@ from AsyncThreadRunner import AsyncThreadRunner
|
|
13
13
|
from TickerManager import Tickers, Ticker, Interval
|
14
14
|
from DataFrameHtmlRenderer import DataFrameHtmlRenderer
|
15
15
|
from logger import Logger, Colors
|
16
|
-
logger = Logger(__name__).get_logger()
|
17
16
|
colors = Colors()
|
18
17
|
import gc
|
19
18
|
import os
|
@@ -27,16 +26,17 @@ import threading
|
|
27
26
|
cst = pytz.timezone('US/Central')
|
28
27
|
|
29
28
|
class BitunixSignal:
|
30
|
-
def __init__(self, api_key, secret_key, settings, threadManager, notifications, bitunixApi):
|
29
|
+
def __init__(self, api_key, secret_key, settings, threadManager, notifications, bitunixApi, logger):
|
31
30
|
self.api_key = api_key
|
32
31
|
self.secret_key = secret_key
|
33
32
|
self.settings=settings
|
34
33
|
self.threadManager = threadManager
|
35
34
|
self.notifications = notifications
|
36
35
|
self.bitunixApi = bitunixApi
|
36
|
+
self.logger = logger
|
37
37
|
|
38
38
|
#Ticker object
|
39
|
-
self.tickerObjects = Tickers(self.settings)
|
39
|
+
self.tickerObjects = Tickers(self.settings, self.logger)
|
40
40
|
|
41
41
|
#these are used for html rendering as well as storing
|
42
42
|
self.signaldf= pd.DataFrame()
|
@@ -78,8 +78,8 @@ class BitunixSignal:
|
|
78
78
|
self.bitunixPrivateWebSocketClient = BitunixPrivateWebSocketClient(self.api_key, self.secret_key)
|
79
79
|
|
80
80
|
if self.settings.USE_PUBLIC_WEBSOCKET:
|
81
|
-
self.bitunixPublicDepthWebSocketClient = BitunixPublicWebSocketClient(self.api_key, self.secret_key, "depth")
|
82
|
-
self.bitunixPublicTickerWebSocketClient = BitunixPublicWebSocketClient(self.api_key, self.secret_key, "ticker")
|
81
|
+
self.bitunixPublicDepthWebSocketClient = BitunixPublicWebSocketClient(self.api_key, self.secret_key, "depth", logger)
|
82
|
+
self.bitunixPublicTickerWebSocketClient = BitunixPublicWebSocketClient(self.api_key, self.secret_key, "ticker", logger)
|
83
83
|
|
84
84
|
self.tickerList=[]
|
85
85
|
|
@@ -129,52 +129,52 @@ class BitunixSignal:
|
|
129
129
|
await asyncio.create_task(self.DefinehtmlRenderers())
|
130
130
|
|
131
131
|
#async thread that runs forever jobs
|
132
|
-
self.GetportfolioDataTask = AsyncThreadRunner(self.GetportfolioData, interval=int(self.settings.PORTFOLIO_API_INTERVAL))
|
132
|
+
self.GetportfolioDataTask = AsyncThreadRunner(self.GetportfolioData, self.logger, interval=int(self.settings.PORTFOLIO_API_INTERVAL))
|
133
133
|
self.GetportfolioDataTask.start_thread(thread_name="GetportfolioData")
|
134
134
|
|
135
|
-
self.GetPendingPositionDataTask = AsyncThreadRunner(self.GetPendingPositionData, interval=int(self.settings.PENDING_POSITIONS_API_INTERVAL))
|
135
|
+
self.GetPendingPositionDataTask = AsyncThreadRunner(self.GetPendingPositionData, self.logger, interval=int(self.settings.PENDING_POSITIONS_API_INTERVAL))
|
136
136
|
self.GetPendingPositionDataTask.start_thread(thread_name="GetPendingPositionData")
|
137
137
|
|
138
|
-
self.GetPendingOrderDataTask = AsyncThreadRunner(self.GetPendingOrderData, interval=int(self.settings.PENDING_ORDERS_API_INTERVAL))
|
138
|
+
self.GetPendingOrderDataTask = AsyncThreadRunner(self.GetPendingOrderData, self.logger, interval=int(self.settings.PENDING_ORDERS_API_INTERVAL))
|
139
139
|
self.GetPendingOrderDataTask.start_thread(thread_name="GetPendingOrderData")
|
140
140
|
|
141
|
-
self.GetTradeHistoryDataTask = AsyncThreadRunner(self.GetTradeHistoryData, interval=int(self.settings.TRADE_HISTORY_API_INTERVAL))
|
141
|
+
self.GetTradeHistoryDataTask = AsyncThreadRunner(self.GetTradeHistoryData, self.logger, interval=int(self.settings.TRADE_HISTORY_API_INTERVAL))
|
142
142
|
self.GetTradeHistoryDataTask.start_thread(thread_name="GetTradeHistoryData")
|
143
143
|
|
144
|
-
self.GetPositionHistoryDataTask = AsyncThreadRunner(self.GetPositionHistoryData, interval=int(self.settings.POSITION_HISTORY_API_INTERVAL))
|
144
|
+
self.GetPositionHistoryDataTask = AsyncThreadRunner(self.GetPositionHistoryData, self.logger, interval=int(self.settings.POSITION_HISTORY_API_INTERVAL))
|
145
145
|
self.GetPositionHistoryDataTask.start_thread(thread_name="GetPositionHistoryData")
|
146
146
|
|
147
|
-
self.ProcessPrivateDataTask = AsyncThreadRunner(self.bitunixPrivateWebSocketClient.run_websocket, 0, self.ProcessPrivateData)
|
147
|
+
self.ProcessPrivateDataTask = AsyncThreadRunner(self.bitunixPrivateWebSocketClient.run_websocket, self.logger, 0, self.ProcessPrivateData)
|
148
148
|
self.ProcessPrivateDataTask.start_thread(thread_name="ProcessPrivateData")
|
149
149
|
|
150
150
|
if self.settings.USE_PUBLIC_WEBSOCKET:
|
151
151
|
self.bitunixPublicDepthWebSocketClient.tickerList = self.tickerList
|
152
|
-
self.StoreDepthDataTask = AsyncThreadRunner(self.bitunixPublicDepthWebSocketClient.run_websocket, 0, self.StoreDepthData)
|
152
|
+
self.StoreDepthDataTask = AsyncThreadRunner(self.bitunixPublicDepthWebSocketClient.run_websocket, self.logger, 0, self.StoreDepthData)
|
153
153
|
self.StoreDepthDataTask.start_thread(thread_name="StoreDepthData")
|
154
154
|
self.depth_que = asyncio.Queue()
|
155
|
-
self.ProcessDepthDataTask = AsyncThreadRunner(self.ProcessDepthData, interval=0) # run only once
|
155
|
+
self.ProcessDepthDataTask = AsyncThreadRunner(self.ProcessDepthData, self.logger, interval=0) # run only once
|
156
156
|
self.ProcessDepthDataTask.start_thread(thread_name="ProcessDepthData")
|
157
157
|
|
158
158
|
self.bitunixPublicTickerWebSocketClient.tickerList = self.tickerList
|
159
|
-
self.StoreTickerDataTask = AsyncThreadRunner(self.bitunixPublicTickerWebSocketClient.run_websocket, 0, self.StoreTickerData)
|
159
|
+
self.StoreTickerDataTask = AsyncThreadRunner(self.bitunixPublicTickerWebSocketClient.run_websocket, self.logger, 0, self.StoreTickerData)
|
160
160
|
self.StoreTickerDataTask.start_thread(thread_name="StoreTickerData")
|
161
161
|
self.ticker_que = asyncio.Queue()
|
162
|
-
self.ProcessTickerDataTask = AsyncThreadRunner(self.ProcessTickerData, interval=0) # run only once
|
162
|
+
self.ProcessTickerDataTask = AsyncThreadRunner(self.ProcessTickerData, self.logger, interval=0) # run only once
|
163
163
|
self.ProcessTickerDataTask.start_thread(thread_name="ProcessTickerData")
|
164
164
|
|
165
165
|
|
166
166
|
#normal processes
|
167
|
-
self.LoadKlineHistoryTask = AsyncThreadRunner(self.LoadKlineHistory, interval=0) # run only once
|
167
|
+
self.LoadKlineHistoryTask = AsyncThreadRunner(self.LoadKlineHistory, self.logger, interval=0) # run only once
|
168
168
|
self.LoadKlineHistoryTask.start_thread(thread_name="LoadKlineHistory")
|
169
169
|
|
170
|
-
self.AutoTradeProcessTask = AsyncThreadRunner(self.AutoTradeProcess, interval=int(self.settings.SIGNAL_CHECK_INTERVAL))
|
170
|
+
self.AutoTradeProcessTask = AsyncThreadRunner(self.AutoTradeProcess, self.logger, interval=int(self.settings.SIGNAL_CHECK_INTERVAL))
|
171
171
|
self.AutoTradeProcessTask.start_thread(thread_name="AutoTradeProcess")
|
172
172
|
|
173
|
-
self.checkTickerAndAutotradeStatusTask = AsyncThreadRunner(self.checkTickerAndAutotradeStatus, interval=0)
|
173
|
+
self.checkTickerAndAutotradeStatusTask = AsyncThreadRunner(self.checkTickerAndAutotradeStatus, self.logger, interval=0)
|
174
174
|
self.checkTickerAndAutotradeStatusTask.start_thread(thread_name="checkTickerAndAutotradeStatus")
|
175
175
|
|
176
176
|
if not self.settings.USE_PUBLIC_WEBSOCKET:
|
177
|
-
self.GetTickerDataTask = AsyncThreadRunner(self.GetTickerData, interval=int(self.settings.TICKER_DATA_API_INTERVAL))
|
177
|
+
self.GetTickerDataTask = AsyncThreadRunner(self.GetTickerData, self.logger, interval=int(self.settings.TICKER_DATA_API_INTERVAL))
|
178
178
|
self.GetTickerDataTask.start_thread(thread_name="GetTickerData")
|
179
179
|
|
180
180
|
|
@@ -227,7 +227,7 @@ class BitunixSignal:
|
|
227
227
|
if data is not None:
|
228
228
|
self.tickerObjects.load_kline_history(ticker, intervalId, self.settings.BARS, data)
|
229
229
|
if self.settings.VERBOSE_LOGGING:
|
230
|
-
logger.info(f"kline_history: elapsed time {time.time()-start}")
|
230
|
+
self.logger.info(f"kline_history: elapsed time {time.time()-start}")
|
231
231
|
self.notifications.add_notification("Kline history loaded")
|
232
232
|
|
233
233
|
#api data
|
@@ -258,7 +258,7 @@ class BitunixSignal:
|
|
258
258
|
|
259
259
|
self.lastTickerDataTime = time.time()
|
260
260
|
if self.settings.VERBOSE_LOGGING:
|
261
|
-
logger.info(f"GetTickerData: elapsed time {time.time()-start}")
|
261
|
+
self.logger.info(f"GetTickerData: elapsed time {time.time()-start}")
|
262
262
|
if self.settings.BENCHMARK:
|
263
263
|
self.connection = sqlite3.connect("bitunix.db")
|
264
264
|
self.cursor = self.connection.cursor()
|
@@ -300,10 +300,10 @@ class BitunixSignal:
|
|
300
300
|
self.tickerObjects.form_candle(self.tuples_list)
|
301
301
|
self.lastTickerDataTime = time.time()
|
302
302
|
except Exception as e:
|
303
|
-
logger.info(f"Function: ProcessTickerData, {e}, {e.args}, {type(e).__name__}")
|
304
|
-
logger.info(traceback.print_exc())
|
303
|
+
self.logger.info(f"Function: ProcessTickerData, {e}, {e.args}, {type(e).__name__}")
|
304
|
+
self.logger.info(traceback.print_exc())
|
305
305
|
await asyncio.sleep(0.01)
|
306
|
-
logger.info(f"ProcessTickerData: exitied out of the loop, exiting app")
|
306
|
+
self.logger.info(f"ProcessTickerData: exitied out of the loop, exiting app")
|
307
307
|
os._exit(1) # Exit the program
|
308
308
|
|
309
309
|
|
@@ -333,10 +333,10 @@ class BitunixSignal:
|
|
333
333
|
self.depthdf["tickerObj"] = self.depthdf.index.map(self.tickerObjects.get_tickerDict())
|
334
334
|
self.depthdf.apply(self.apply_depth_data2, axis=1)
|
335
335
|
except Exception as e:
|
336
|
-
logger.info(f"Function: ProcessTickerData, {e}, {e.args}, {type(e).__name__}")
|
337
|
-
logger.info(traceback.print_exc())
|
336
|
+
self.logger.info(f"Function: ProcessTickerData, {e}, {e.args}, {type(e).__name__}")
|
337
|
+
self.logger.info(traceback.print_exc())
|
338
338
|
await asyncio.sleep(0.01)
|
339
|
-
logger.info(f"ProcessDepthData: exitied out of the loop, exiting app")
|
339
|
+
self.logger.info(f"ProcessDepthData: exitied out of the loop, exiting app")
|
340
340
|
os._exit(1) # Exit the program
|
341
341
|
|
342
342
|
def apply_depth_data2(self, row):
|
@@ -367,9 +367,9 @@ class BitunixSignal:
|
|
367
367
|
del data, tickerdf, tuples_list
|
368
368
|
gc.collect()
|
369
369
|
except Exception as e:
|
370
|
-
logger.info(e)
|
370
|
+
self.logger.info(e)
|
371
371
|
if self.settings.VERBOSE_LOGGING:
|
372
|
-
logger.info(f"apply_last_data: elapsed time {time.time()-start}")
|
372
|
+
self.logger.info(f"apply_last_data: elapsed time {time.time()-start}")
|
373
373
|
|
374
374
|
# non websocket method
|
375
375
|
# this is called to update bid and ask,
|
@@ -411,9 +411,9 @@ class BitunixSignal:
|
|
411
411
|
self.portfoliodfStyle= self.portfoliodfrenderer.render_html(self.portfoliodf)
|
412
412
|
|
413
413
|
except Exception as e:
|
414
|
-
logger.info(f"Function: GetportfolioData, {e}, {e.args}, {type(e).__name__}")
|
414
|
+
self.logger.info(f"Function: GetportfolioData, {e}, {e.args}, {type(e).__name__}")
|
415
415
|
if self.settings.VERBOSE_LOGGING:
|
416
|
-
logger.info(f"GetportfolioData: elapsed time {time.time()-start}")
|
416
|
+
self.logger.info(f"GetportfolioData: elapsed time {time.time()-start}")
|
417
417
|
|
418
418
|
async def GetPendingPositionData(self):
|
419
419
|
start=time.time()
|
@@ -470,9 +470,9 @@ class BitunixSignal:
|
|
470
470
|
|
471
471
|
|
472
472
|
except Exception as e:
|
473
|
-
logger.info(f"Function: GetPendingPositionData, {e}, {e.args}, {type(e).__name__}")
|
473
|
+
self.logger.info(f"Function: GetPendingPositionData, {e}, {e.args}, {type(e).__name__}")
|
474
474
|
if self.settings.VERBOSE_LOGGING:
|
475
|
-
logger.info(f"GetPendingPositionData: elapsed time {time.time()-start}")
|
475
|
+
self.logger.info(f"GetPendingPositionData: elapsed time {time.time()-start}")
|
476
476
|
|
477
477
|
async def GetPendingOrderData(self):
|
478
478
|
start=time.time()
|
@@ -489,9 +489,9 @@ class BitunixSignal:
|
|
489
489
|
self.orderdfStyle= self.orderdfrenderer.render_html(self.orderdf)
|
490
490
|
|
491
491
|
except Exception as e:
|
492
|
-
logger.info(f"Function: GetPendingOrderData, {e}, {e.args}, {type(e).__name__}")
|
492
|
+
self.logger.info(f"Function: GetPendingOrderData, {e}, {e.args}, {type(e).__name__}")
|
493
493
|
if self.settings.VERBOSE_LOGGING:
|
494
|
-
logger.info(f"GetPendingOrderData: elapsed time {time.time()-start}")
|
494
|
+
self.logger.info(f"GetPendingOrderData: elapsed time {time.time()-start}")
|
495
495
|
|
496
496
|
async def GetPositionHistoryData(self):
|
497
497
|
start=time.time()
|
@@ -509,9 +509,9 @@ class BitunixSignal:
|
|
509
509
|
self.positionHistorydfStyle= self.positionHistorydfrenderer.render_html(self.positionHistorydf)
|
510
510
|
|
511
511
|
except Exception as e:
|
512
|
-
logger.info(f"Function: GetPositionHistoryData, {e}, {e.args}, {type(e).__name__}")
|
512
|
+
self.logger.info(f"Function: GetPositionHistoryData, {e}, {e.args}, {type(e).__name__}")
|
513
513
|
if self.settings.VERBOSE_LOGGING:
|
514
|
-
logger.info(f"GetPositionHistoryData: elapsed time {time.time()-start}")
|
514
|
+
self.logger.info(f"GetPositionHistoryData: elapsed time {time.time()-start}")
|
515
515
|
|
516
516
|
async def GetTradeHistoryData(self):
|
517
517
|
start=time.time()
|
@@ -526,9 +526,9 @@ class BitunixSignal:
|
|
526
526
|
# Filter trades for the current symbol and convert them to a list of dicts
|
527
527
|
tickerObj.trades = grouped_trades.get_group(symbol).to_dict("records")
|
528
528
|
except Exception as e:
|
529
|
-
logger.info(f"Function: GetTradeHistoryData, {e}, {e.args}, {type(e).__name__}")
|
529
|
+
self.logger.info(f"Function: GetTradeHistoryData, {e}, {e.args}, {type(e).__name__}")
|
530
530
|
if self.settings.VERBOSE_LOGGING:
|
531
|
-
logger.info(f"GetTradeHistoryData: elapsed time {time.time()-start}")
|
531
|
+
self.logger.info(f"GetTradeHistoryData: elapsed time {time.time()-start}")
|
532
532
|
|
533
533
|
|
534
534
|
###########################################################################################################
|
@@ -545,6 +545,7 @@ class BitunixSignal:
|
|
545
545
|
inuseTickers = set(inuse1 + inuse2)
|
546
546
|
|
547
547
|
# Extract buy/sell ticker data
|
548
|
+
self.signaldf_filtered = pd.DataFrame();
|
548
549
|
self.tickerObjects.getCurrentData(period)
|
549
550
|
|
550
551
|
self.signaldf_full = self.tickerObjects.signaldf_full
|
@@ -616,14 +617,14 @@ class BitunixSignal:
|
|
616
617
|
)
|
617
618
|
|
618
619
|
except Exception as e:
|
619
|
-
logger.info(f"Function: BuySellList, {e}, {e.args}, {type(e).__name__}")
|
620
|
-
logger.info(traceback.print_exc())
|
620
|
+
self.logger.info(f"Function: BuySellList, {e}, {e.args}, {type(e).__name__}")
|
621
|
+
self.logger.info(traceback.print_exc())
|
621
622
|
del inuse1, inuse2, inuseTickers
|
622
623
|
gc.collect()
|
623
624
|
|
624
625
|
async def AutoTradeProcess(self):
|
625
626
|
if self.settings.VERBOSE_LOGGING:
|
626
|
-
logger.info(f"AutoTradeProcess started")
|
627
|
+
self.logger.info(f"AutoTradeProcess started")
|
627
628
|
start=time.time()
|
628
629
|
|
629
630
|
period = self.settings.OPTION_MOVING_AVERAGE
|
@@ -885,6 +886,41 @@ class BitunixSignal:
|
|
885
886
|
)
|
886
887
|
continue
|
887
888
|
|
889
|
+
# BOS
|
890
|
+
if self.settings.BOS_STUDY and self.settings.BOS_CHECK_ON_CLOSE:
|
891
|
+
if row.side == 'BUY' and self.signaldf_full.at[row.symbol, f'{period}_bos'] == "SELL" and total_pnl>0:
|
892
|
+
last, bid, ask, mtv = await self.GetTickerBidLastAsk(row.symbol)
|
893
|
+
price = (ask if row['side'] == "BUY" else bid if row['side'] == "SELL" else last) if bid<=last<=ask else last
|
894
|
+
|
895
|
+
self.notifications.add_notification(
|
896
|
+
f'{colors.CYAN} Closing {"long" if side=="BUY" else "short"} position due to {period} bos for {row.symbol} with {row.qty} qty @ {price})'
|
897
|
+
)
|
898
|
+
datajs = await self.bitunixApi.PlaceOrder(
|
899
|
+
positionId=row.positionId,
|
900
|
+
ticker=row.symbol,
|
901
|
+
qty=row.qty,
|
902
|
+
price=price,
|
903
|
+
side=row.side,
|
904
|
+
tradeSide="CLOSE"
|
905
|
+
)
|
906
|
+
continue
|
907
|
+
|
908
|
+
if row.side == 'SELL' and self.signaldf_full.at[row.symbol, f'{period}_bos'] == "BUY" and total_pnl>0:
|
909
|
+
last, bid, ask, mtv = await self.GetTickerBidLastAsk(row.symbol)
|
910
|
+
price = (ask if row['side'] == "BUY" else bid if row['side'] == "SELL" else last) if bid<=last<=ask else last
|
911
|
+
self.notifications.add_notification(
|
912
|
+
f'{colors.CYAN} Closing {"long" if side=="BUY" else "short"} position due to {period} bos for {row.symbol} with {row.qty} qty @ {price})'
|
913
|
+
)
|
914
|
+
datajs = await self.bitunixApi.PlaceOrder(
|
915
|
+
positionId=row.positionId,
|
916
|
+
ticker=row.symbol,
|
917
|
+
qty=row.qty,
|
918
|
+
price=price,
|
919
|
+
side=row.side,
|
920
|
+
tradeSide="CLOSE"
|
921
|
+
)
|
922
|
+
continue
|
923
|
+
|
888
924
|
# TrendLine
|
889
925
|
if self.settings.TRENDLINE_STUDY and self.settings.TRENDLINE_CHECK_ON_CLOSE:
|
890
926
|
if row.side == 'BUY' and self.signaldf_full.at[row.symbol, f'{period}_trendline'] == "SELL" and total_pnl>0:
|
@@ -996,11 +1032,11 @@ class BitunixSignal:
|
|
996
1032
|
except Exception as e:
|
997
1033
|
stack = traceback.extract_stack()
|
998
1034
|
function_name = stack[-1].name
|
999
|
-
logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
|
1000
|
-
logger.info(traceback.print_exc())
|
1035
|
+
self.logger.info(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
|
1036
|
+
self.logger.info(traceback.print_exc())
|
1001
1037
|
|
1002
1038
|
if self.settings.VERBOSE_LOGGING:
|
1003
|
-
logger.info(f"AutoTradeProcess: elapsed time {time.time()-start}")
|
1039
|
+
self.logger.info(f"AutoTradeProcess: elapsed time {time.time()-start}")
|
1004
1040
|
if self.settings.BENCHMARK:
|
1005
1041
|
self.connection = sqlite3.connect("bitunix.db")
|
1006
1042
|
self.cursor = self.connection.cursor()
|
@@ -1066,7 +1102,7 @@ class BitunixSignal:
|
|
1066
1102
|
|
1067
1103
|
self.available = data['available']
|
1068
1104
|
self.margin = data['margin']
|
1069
|
-
# logger.info(feed)
|
1105
|
+
# self.logger.info(feed)
|
1070
1106
|
|
1071
1107
|
elif channel == 'position':
|
1072
1108
|
|
@@ -1104,7 +1140,7 @@ class BitunixSignal:
|
|
1104
1140
|
del feed
|
1105
1141
|
gc.collect()
|
1106
1142
|
except Exception as e:
|
1107
|
-
logger.info(f"Function: ProcessPrivateData, {e}, {e.args}, {type(e).__name__}")
|
1143
|
+
self.logger.info(f"Function: ProcessPrivateData, {e}, {e.args}, {type(e).__name__}")
|
1108
1144
|
|
1109
1145
|
def color_cells(val, color):
|
1110
1146
|
return f'background-color: {color}' if val else ''
|