bitunix-automated-crypto-trading 3.3.2__tar.gz → 3.3.4__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.
Files changed (25) hide show
  1. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/PKG-INFO +1 -1
  2. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/README.md +8 -3
  3. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/bitunix_automated_crypto_trading/BitunixApi.py +92 -43
  4. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/bitunix_automated_crypto_trading/BitunixSignal.py +77 -2
  5. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/bitunix_automated_crypto_trading/TickerManager.py +40 -72
  6. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/bitunix_automated_crypto_trading/config.py +5 -4
  7. bitunix_automated_crypto_trading-3.3.4/bitunix_automated_crypto_trading/version.py +1 -0
  8. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/bitunix_automated_crypto_trading.egg-info/PKG-INFO +1 -1
  9. bitunix_automated_crypto_trading-3.3.2/bitunix_automated_crypto_trading/version.py +0 -1
  10. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/bitunix_automated_crypto_trading/AsyncThreadRunner.py +0 -0
  11. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/bitunix_automated_crypto_trading/BitunixWebSocket.py +0 -0
  12. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/bitunix_automated_crypto_trading/DataFrameHtmlRenderer.py +0 -0
  13. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/bitunix_automated_crypto_trading/NotificationManager.py +0 -0
  14. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/bitunix_automated_crypto_trading/SupportResistance.py +0 -0
  15. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/bitunix_automated_crypto_trading/ThreadManager.py +0 -0
  16. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/bitunix_automated_crypto_trading/__init__.py +0 -0
  17. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/bitunix_automated_crypto_trading/bitunix.py +0 -0
  18. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/bitunix_automated_crypto_trading/logger.py +0 -0
  19. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/bitunix_automated_crypto_trading.egg-info/SOURCES.txt +0 -0
  20. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/bitunix_automated_crypto_trading.egg-info/dependency_links.txt +0 -0
  21. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/bitunix_automated_crypto_trading.egg-info/entry_points.txt +0 -0
  22. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/bitunix_automated_crypto_trading.egg-info/requires.txt +0 -0
  23. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/bitunix_automated_crypto_trading.egg-info/top_level.txt +0 -0
  24. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/setup.cfg +0 -0
  25. {bitunix_automated_crypto_trading-3.3.2 → bitunix_automated_crypto_trading-3.3.4}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bitunix_automated_crypto_trading
3
- Version: 3.3.2
3
+ Version: 3.3.4
4
4
  Summary: Bitunix Futures Auto Trading Platform
5
5
  Home-page: https://github.com/tcj2001/bitunix-automated-crypto-trading
6
6
  Author: tcj2001
@@ -57,12 +57,16 @@ The platform can be configured through the `config.py` file or `config.txt`. Key
57
57
  - `MIN_VOLUME`: Ticker selection based on Minimum trading volume
58
58
  - `ORDER_AMOUNT_PERCENTAGE`: Order size as percentage of portfolio
59
59
  - `MAX_AUTO_TRADES`: Maximum number of automated trades
60
+ - `PROFIT_AMOUNT`: Target profit amount
61
+ - `LOSS_AMOUNT`: Maximum loss amount
60
62
  - `PROFIT_PERCENTAGE`: Target profit ROI percentage
61
63
  - `LOSS_PERCENTAGE`: Maximum loss ROI percentage
64
+ - `BOT_TAKE_PROFIT_STOP_LOSS`: True or False
65
+ if false then take profit and stop loss will be placed by the bot when TP or SL is reached, if true then take profit and stop loss will be placed when the trade is opened
66
+ - `BOT_TRAIL_STOP_LOSS`: True or False
67
+ if set to True then bot will move the stop loss to breakeven when the trade is reaches 50% of the target profit and then move the stop loss to the 50% of the target profit when the trade reaches 90% of the target profit.
62
68
  - `PROFIT_LOSS_PRICE_TYPE`: "MARK_PRICE" or "LAST_PRICE"
63
69
  - `PROFIT_LOSS_ORDER_TYPE`: "MARKET" or "LIMIT"
64
- - `PROFIT_AMOUNT`: Target profit amount
65
- - `LOSS_AMOUNT`: Maximum loss amount
66
70
  - `OPTION_MOVING_AVERAGE`: Moving average period (1h, 1d, 15m, 5m, 1m)
67
71
  - `DELAY_IN_MINUTES_FOR_SAME_TICKER_TRADES`: Delay in minutes to prohibit trading the same ticker again
68
72
  - `BARS`: Number of bars to use for study and charting
@@ -177,6 +181,7 @@ The platform can be configured through the `config.py` file or `config.txt`. Key
177
181
  - 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
178
182
  - You can control the trading strategy using the CalculateStudy function in TickerManager.py and AutoTradeProcess function in BitunixSignal.py
179
183
  - Changes are activated by unchecking and checking the AutoTrade checkbox
184
+ - Trailing TP and SL in the direction of profit, if BOT_TRAIL_STOP_LOSS to True and BOT_TAKE_PROFIT_STOP_LOSS to False in the config file
180
185
 
181
186
  ## Auto Trading
182
187
 
@@ -228,7 +233,7 @@ The platform can be configured through the `config.py` file or `config.txt`. Key
228
233
  - check python version should be 3.13 or above
229
234
  - python3 --version
230
235
  - python -m pip install --upgrade pip
231
- - pip install https://github.com/cgohlke/talib-build/releases/download/v0.6.3/ta_lib-0.6.3-cp313-cp313-win_amd64.whl
236
+ - ta-lib, install it from https://ta-lib.org/install/#executable-installer-recommended
232
237
  - change the tags/3.1.8 to the latest version in the script below and run it
233
238
  - wget https://github.com/tcj2001/bitunix-automated-crypto-trading/archive/refs/tags/3.1.8.zip
234
239
  - unzip zip file and copy the content inside C:\bitunix\Ver1.0.zip\bitunix-automated-crypto-trading-Ver1.0 to c:\bitunix
@@ -4,6 +4,7 @@ import time
4
4
  import json
5
5
  import hashlib
6
6
  import asyncio
7
+ import re
7
8
  import requests
8
9
  from urllib.parse import urlencode
9
10
  from typing import Dict, Any
@@ -33,17 +34,19 @@ class BitunixApi:
33
34
  self.depth_Url='https://fapi.bitunix.com/api/v1/futures/market/depth'
34
35
  self.placeOrder_Url="https://fapi.bitunix.com/api/v1/futures/trade/place_order"
35
36
  self.flashClose_Url="https://fapi.bitunix.com/api/v1/futures/trade/flash_close_position"
37
+ self.get_order_url="https://fapi.bitunix.com/api/v1/futures/trade/get_order_detail"
36
38
  self.pending_order_url="https://fapi.bitunix.com/api/v1/futures/trade/get_pending_orders"
37
39
  self.cancelOrder_Url="https://fapi.bitunix.com/api/v1/futures/trade/cancel_orders"
40
+ self.pending_tpsl_order_url="https://fapi.bitunix.com/api/v1/futures/tpsl/get_pending_orders"
41
+ self.place_tpsl_order_url="https://fapi.bitunix.com/api/v1/futures/tpsl/place_order"
42
+ self.modify_tpsl_order_url="https://fapi.bitunix.com/api/v1/futures/tpsl/modify_order"
43
+ self.cancel_tpsl_Order_Url="https://fapi.bitunix.com/api/v1/futures/tpsl/cancel_order"
38
44
  self.Trade_history_Url="https://fapi.bitunix.com/api/v1/futures/trade/get_history_trades"
39
45
  self.position_history_Url="https://fapi.bitunix.com/api/v1/futures/position/get_history_positions"
40
46
 
41
47
  async def update_settings(self, settings):
42
48
  self.settings = settings
43
49
 
44
- async def create_timestamp(self):
45
- return str(int(time.time()*1000))
46
-
47
50
  async def is_near_high_of_day(self, current_value, high_of_day, threshold_percentage=5):
48
51
  # Calculate the difference as a percentage of the high of the day
49
52
  difference_percentage = ((high_of_day - current_value) / high_of_day) * 100
@@ -56,70 +59,80 @@ class BitunixApi:
56
59
  # Check if the difference is within the threshold
57
60
  return difference_percentage <= threshold_percentage
58
61
 
62
+ #signature generation related methods
63
+ async def _create_timestamp(self):
64
+ return str(int(time.time()*1000))
59
65
 
60
66
  async def _generate_nonce(self) -> str:
61
67
  random_bytes = secrets.token_bytes(32)
62
68
  return base64.b64encode(random_bytes).decode('utf-8')
63
69
 
70
+ async def _generate_sign_api(self, nonce, timestamp, api_key, secret_key, method, data):
71
+ query_params = ""
72
+ body = ""
73
+ if data:
74
+ if method.lower() == "get":
75
+ data = {k: v for k, v in data.items() if v is not None}
76
+ query_params = '&'.join([f"{k}={v}" for k, v in sorted(data.items())])
77
+ query_params = re.sub(r'[^a-zA-Z0-9]', '', query_params)
78
+ if method.lower() == "post":
79
+ # body = str(data).replace(" ", "")
80
+ body = str(data)
64
81
 
65
- async def sign_request(self, nonce: str, timestamp: str,
66
- query_params: str = None,
67
- body: str = None) -> str:
68
- query_string = query_params if query_params else ""
69
- body_string = body if body else ""
70
- message = f"{nonce}{timestamp}{self.api_key}{query_string}{body_string}"
71
-
72
- # First SHA256 encryption
73
- digest = hashlib.sha256(message.encode()).hexdigest()
74
-
75
- # Second SHA256 encryption
76
- sign = hashlib.sha256((digest + self.secret_key).encode()).hexdigest()
77
-
78
- return sign
79
-
80
- async def _get(self, endpoint: str, params: Dict[str, Any] = None) -> Dict[str, Any]:
81
- response = self.session.get(endpoint, params=params)
82
- response.raise_for_status()
83
- return response.json()
82
+ digest_input = nonce + timestamp + api_key + query_params + body
83
+ # print(f"digest_input={digest_input}")
84
+ digest = hashlib.sha256((digest_input).encode()).hexdigest()
85
+ # print(f"digest={digest}")
86
+ sign_input = digest + secret_key
87
+ # print(f"sign_input={sign_input}")
88
+ sign = hashlib.sha256((sign_input).encode()).hexdigest()
84
89
 
90
+ return sign
91
+
85
92
  async def _get_authenticated(self, endpoint: str, params: Dict[str, Any] = None) -> Dict[str, Any]:
86
- timestamp = await self.create_timestamp()
93
+ timestamp = await self._create_timestamp()
87
94
  nonce = await self._generate_nonce()
88
95
 
96
+ signature = await self._generate_sign_api(nonce, timestamp, self.api_key, self.secret_key, "get", params)
97
+
89
98
  headers = {
90
99
  "api-key": self.api_key,
100
+ "nonce": nonce,
91
101
  "timestamp": timestamp,
92
- "nonce": nonce,
102
+ "sign": signature, # Placeholder for signature
103
+ "language": "en-US",
104
+ "Content-Type": "application/json"
93
105
  }
94
-
95
- query_string = urlencode(sorted(params.items())).replace('=','').replace('&','') if params else ""
96
- signature = await self.sign_request(nonce, timestamp, query_params=query_string)
97
- headers["sign"] = signature
98
106
 
99
107
  response = self.session.get(endpoint, params=params, headers=headers)
100
108
  response.raise_for_status()
101
109
  return response.json()
102
110
 
103
111
  async def _post_authenticated(self, endpoint: str, data: Dict[str, Any]) -> Dict[str, Any]:
104
- timestamp = await self.create_timestamp()
112
+ timestamp = await self._create_timestamp()
105
113
  nonce = await self._generate_nonce()
106
114
 
115
+ body_string = json.dumps(data, separators=(',',':'))
116
+ signature = await self._generate_sign_api(nonce, timestamp, self.api_key, self.secret_key, "post", body_string)
117
+
107
118
  headers = {
108
119
  "api-key": self.api_key,
109
120
  "timestamp": timestamp,
110
121
  "nonce": nonce,
122
+ "sign": signature,
111
123
  "Content-Type": "application/json"
112
124
  }
113
-
114
- body_string = json.dumps(data, separators=(',', ':'))
115
- signature = await self.sign_request(nonce, timestamp, body=body_string)
116
- headers["sign"] = signature
117
125
 
118
126
  response = self.session.post(endpoint, data=body_string, headers=headers)
119
127
  self.logger.info(f"Response: {body_string} {response.json()}")
120
128
  response.raise_for_status()
121
129
  return response.json()
122
130
 
131
+ async def _get(self, endpoint: str, params: Dict[str, Any] = None) -> Dict[str, Any]:
132
+ response = self.session.get(endpoint, params=params)
133
+ response.raise_for_status()
134
+ return response.json()
135
+
123
136
  async def PlaceOrder(self, ticker, qty, price, side, positionId=0, tradeSide="OPEN",reduceOnly=False, tpPrice=0, tpOrderPrice=0, slPrice=0, slOrderPrice=0):
124
137
  datajs = None
125
138
  if tpPrice == 0 and slPrice == 0:
@@ -143,8 +156,8 @@ class BitunixApi:
143
156
  "symbol": ticker,
144
157
  "tradeSide":tradeSide,
145
158
  "slPrice": slPrice,
146
- "slStopType": self.settings.STOP_LOSS_PRICE_TYPE,
147
- "slOrderType": self.settings.STOP_LOSS_ORDER_TYPE,
159
+ "slStopType": self.settings.SL_STOP_TYPE,
160
+ "slOrderType": self.settings.SL_ORDER_TYPE,
148
161
  "slOrderPrice": slOrderPrice,
149
162
  "positionId":positionId
150
163
  }
@@ -158,8 +171,8 @@ class BitunixApi:
158
171
  "symbol": ticker,
159
172
  "tradeSide":tradeSide,
160
173
  "tpPrice": tpPrice,
161
- "tpStopType": self.settings.TAKE_PROFIT_PRICE_TYPE,
162
- "tpOrderType": self.settings.TAKE_PROFIT_ORDER_TYPE,
174
+ "tpStopType": self.settings.TP_STOP_TYPE,
175
+ "tpOrderType": self.settings.TP_ORDER_TYPE,
163
176
  "tpOrderPrice":tpOrderPrice,
164
177
  "positionId":positionId
165
178
  }
@@ -173,12 +186,12 @@ class BitunixApi:
173
186
  "symbol": ticker,
174
187
  "tradeSide":tradeSide,
175
188
  "tpPrice": tpPrice,
176
- "tpStopType": self.settings.TAKE_PROFIT_PRICE_TYPE,
177
- "tpOrderType": self.settings.TAKE_PROFIT_ORDER_TYPE,
189
+ "tpStopType": self.settings.TP_STOP_TYPE,
190
+ "tpOrderType": self.settings.TP_ORDER_TYPE,
178
191
  "tpOrderPrice":tpOrderPrice,
179
192
  "slPrice": slPrice,
180
- "slStopType": self.settings.STOP_LOSS_PRICE_TYPE,
181
- "slOrderType": self.settings.STOP_LOSS_ORDER_TYPE,
193
+ "slStopType": self.settings.SL_STOP_TYPE,
194
+ "slOrderType": self.settings.SL_ORDER_TYPE,
182
195
  "slOrderPrice": slOrderPrice,
183
196
  "positionId":positionId
184
197
  }
@@ -201,13 +214,20 @@ class BitunixApi:
201
214
  datajs = await self._post_authenticated(self.cancelOrder_Url,data)
202
215
  return datajs
203
216
 
204
- async def GetTradeHistoryData(self):
205
- tradeHistory=await self._get_authenticated(self.Trade_history_Url)
217
+ async def GetTradeHistoryData(self, dictparm={}):
218
+ tradeHistory=await self._get_authenticated(self.Trade_history_Url, dictparm)
206
219
  if tradeHistory['code']==0:
207
220
  return tradeHistory['data']
208
221
  else:
209
222
  self.logger.info(tradeHistory['msg'])
210
223
 
224
+ async def GetOrderData(self, dictparm={}):
225
+ orders=await self._get_authenticated(self.get_order_url, dictparm)
226
+ if orders['code']==0:
227
+ return orders['data']
228
+ else:
229
+ self.logger.info(orders['msg'])
230
+
211
231
  async def GetPendingOrderData(self,dictparm={}):
212
232
  orders=await self._get_authenticated(self.pending_order_url, dictparm)
213
233
  if orders['code']==0:
@@ -215,6 +235,35 @@ class BitunixApi:
215
235
  else:
216
236
  self.logger.info(orders['msg'])
217
237
 
238
+ async def GetPendingTpSlOrderData(self,dictparm={}):
239
+ orders=await self._get_authenticated(self.pending_tpsl_order_url, dictparm)
240
+ if orders['code']==0:
241
+ return orders['data']
242
+ else:
243
+ self.logger.info(orders['msg'])
244
+
245
+ async def PlaceTpSlOrder(self,dictparm={}):
246
+ orders=await self._post_authenticated(self.place_tpsl_order_url, dictparm)
247
+ if orders['code']==0:
248
+ return orders['data']
249
+ else:
250
+ self.logger.info(orders['msg'])
251
+
252
+ async def ModifyTpSlOrder(self,dictparm={}):
253
+ orders=await self._post_authenticated(self.modify_tpsl_order_url, dictparm)
254
+ if orders['code']==0:
255
+ return orders['data']
256
+ else:
257
+ self.logger.info(orders['msg'])
258
+
259
+ async def CancelTpSlOrder(self, symbol, orderId):
260
+ data = {
261
+ "symbol": symbol,
262
+ "orderId":orderId
263
+ }
264
+ datajs = await self._post_authenticated(self.cancel_tpsl_Order_Url,data)
265
+ return datajs
266
+
218
267
  async def GetPendingPositionData(self, dictparm={}):
219
268
  positions=await self._get_authenticated(self.pending_positions_URL, dictparm)
220
269
  if positions['code']==0:
@@ -646,6 +646,7 @@ class BitunixSignal:
646
646
  async def get_duration_minutes_unixtime(self, trade_time_unix):
647
647
  """Calculates the duration in minutes from trade time to current time."""
648
648
  current_time_unix = int(time.time()) # Get current Unix timestamp
649
+ print(f"Current time (Unix): {current_time_unix}, Trade time (Unix): {trade_time_unix}")
649
650
  duration_seconds = current_time_unix - trade_time_unix
650
651
  duration_minutes = duration_seconds / 60 # Convert seconds to minutes
651
652
  return round(duration_minutes)
@@ -678,6 +679,12 @@ class BitunixSignal:
678
679
  increment = Decimal(f'1e-{decimal_places}')
679
680
  return float(value + increment)
680
681
 
682
+ async def decrement_by_last_decimal(self, value_str):
683
+ value = Decimal(value_str)
684
+ # Count number of decimal places
685
+ decimal_places = abs(value.as_tuple().exponent)
686
+ decrement = Decimal(f'1e-{decimal_places}')
687
+ return float(value - decrement)
681
688
 
682
689
  async def calculate_candles(self, timeframe, duration_minutes):
683
690
 
@@ -752,12 +759,12 @@ class BitunixSignal:
752
759
  tpPrice = price + float(self.settings.PROFIT_AMOUNT)/qty if side == "BUY" else price - float(self.settings.PROFIT_AMOUNT)/qty
753
760
  tpPrice = Decimal(await self.str_precision(tpPrice))
754
761
  tpPrice =float(str(tpPrice.quantize(Decimal(f'1e-{decimal_places}'))))
755
- tpOrderPrice = await self.increment_by_last_decimal(str(tpPrice))
762
+ tpOrderPrice = await self.decrement_by_last_decimal(str(tpPrice))
756
763
  if self.settings.PROFIT_PERCENTAGE > 0 or self.settings.PROFIT_AMOUNT > 0:
757
764
  tpPrice = price * (1 + float(self.settings.PROFIT_PERCENTAGE) / 100 / self.settings.LEVERAGE) if side == "BUY" else price * (1 - float(self.settings.PROFIT_PERCENTAGE) / 100 / self.settings.LEVERAGE)
758
765
  tpPrice = Decimal(await self.str_precision(tpPrice))
759
766
  tpPrice = float(str(tpPrice.quantize(Decimal(f'1e-{decimal_places}'))))
760
- tpOrderPrice = await self.increment_by_last_decimal(await self.str_precision(tpPrice))
767
+ tpOrderPrice = await self.decrement_by_last_decimal(await self.str_precision(tpPrice))
761
768
 
762
769
  slPrice=0
763
770
  slOrderPrice=0
@@ -838,6 +845,74 @@ class BitunixSignal:
838
845
  select = False
839
846
 
840
847
  if select and int(self.settings.MAX_AUTO_TRADES)!=0:
848
+
849
+ if self.settings.BOT_TRAIL_STOP_LOSS and self.settings.PROFIT_PERCENTAGE > 0 and self.settings.LOSS_PERCENTAGE > 0:
850
+ last, bid, ask, mtv = await self.GetTickerBidLastAsk(row.symbol)
851
+ price = (bid if side == "BUY" else ask if side == "SELL" else last) if bid<=last<=ask else last
852
+ decimal_places = abs(Decimal(str(price)).as_tuple().exponent)
853
+
854
+ symbol = row['symbol']
855
+ positionId = row['positionId']
856
+ avgOpenPrice = float(row['avgOpenPrice'])
857
+ qty = float(row['qty'])
858
+
859
+ tpPrice = None
860
+ tporderId = None
861
+ old_tpPrice = None
862
+ tpStopType = self.settings.SL_STOP_TYPE
863
+ tpOrderType = self.settings.SL_ORDER_TYPE
864
+
865
+ slPrice = None
866
+ slorderId = None
867
+ old_slPrice = None
868
+ slStopType = self.settings.SL_STOP_TYPE
869
+ slOrderType = self.settings.SL_ORDER_TYPE
870
+
871
+ datajs = await self.bitunixApi.GetPendingTpSlOrderData({'symbol': symbol})
872
+ if datajs:
873
+ for order in datajs:
874
+ if order['tpPrice'] is not None:
875
+ tporderId = order['id']
876
+ tpStopType = order['tpStopType']
877
+ tpOrderType = order['tpOrderType']
878
+ tpPrice = float(order['tpPrice'])
879
+ old_tpPrice = tpPrice
880
+ if order['slPrice'] is not None:
881
+ slorderId = order['id']
882
+ slStopType = order['slStopType']
883
+ slOrderType = order['slOrderType']
884
+ slPrice = float(order['slPrice'])
885
+ old_slPrice = slPrice
886
+
887
+ # move TP and SL in the direction of profit
888
+ midpoint = tpPrice / (1 + self.settings.PROFIT_PERCENTAGE/100/self.settings.LEVERAGE) if side == "BUY" else tpPrice / (1 - self.settings.PROFIT_PERCENTAGE/100/self.settings.LEVERAGE) if tpPrice is not None else None
889
+ if price > midpoint and side == "BUY" or price < midpoint and side == "SELL":
890
+
891
+ tpPrice = price * (1 + float(self.settings.PROFIT_PERCENTAGE) / 100 / self.settings.LEVERAGE) if side == "BUY" else price * (1 - float(self.settings.PROFIT_PERCENTAGE) / 100 / self.settings.LEVERAGE)
892
+ tpPrice = Decimal(await self.str_precision(tpPrice))
893
+ tpPrice =float(str(tpPrice.quantize(Decimal(f'1e-{decimal_places}'))))
894
+ tpOrderPrice = await self.decrement_by_last_decimal(str(tpPrice))
895
+
896
+ slPrice = price * (1 - float(self.settings.LOSS_PERCENTAGE) / 100 / self.settings.LEVERAGE) if side == "BUY" else price * (1 + float(self.settings.LOSS_PERCENTAGE) / 100 / self.settings.LEVERAGE)
897
+ slPrice = Decimal(await self.str_precision(slPrice))
898
+ slPrice = float(str(slPrice.quantize(Decimal(f'1e-{decimal_places}'))))
899
+ slOrderPrice = await self.increment_by_last_decimal(await self.str_precision(slPrice))
900
+
901
+ self.notifications.add_notification(
902
+ f'{colors.CYAN} {row.symbol} TP/SL midpoint is at {midpoint}'
903
+ )
904
+ datajs3 = await self.bitunixApi.ModifyTpSlOrder({'orderId':slorderId,'slPrice':str(slPrice),'slQty':str(qty),'slStopType':slStopType,'slOrderType':slOrderType})
905
+ if datajs3 is not None:
906
+ self.notifications.add_notification(
907
+ f'{colors.CYAN} Stop Loss order for {row.symbol} moved from {old_slPrice} to {slPrice}'
908
+ )
909
+ datajs2 = await self.bitunixApi.ModifyTpSlOrder({'orderId':tporderId,'tpPrice':str(tpPrice), 'tpOrderPrice':str(tpOrderPrice), 'tpQty':str(qty),'tpStopType':tpStopType,'tpOrderType':tpOrderType})
910
+ if datajs2 is not None:
911
+ self.notifications.add_notification(
912
+ f'{colors.CYAN} Take Profit order for {row.symbol} moved from {old_tpPrice} to {tpPrice}'
913
+ )
914
+
915
+
841
916
  if self.settings.BOT_TAKE_PROFIT_STOP_LOSS:
842
917
  # check take profit amount or accept loss amount
843
918
  if float(self.settings.LOSS_AMOUNT) > 0 and total_pnl < -float(self.settings.LOSS_AMOUNT):
@@ -319,20 +319,16 @@ class Interval:
319
319
  df.replace([np.inf, -np.inf], 0, inplace=True)
320
320
 
321
321
  if self.settings.OPEN_ON_ANY_SIGNAL:
322
- # If EMA is enabled and crossing or MACD is enabled and crossing or BBM is enabled and crossing or RSI is enbabled and crossing
323
- # and
324
- # ADX is enabled and strong and candle trend is enabled and bullish
325
- # then BUY or SELL
326
322
 
327
323
  # Check for BUY signal
328
- buy_conditions = (
329
- (self.settings.BOS_STUDY and self.settings.BOS_CHECK_ON_OPEN and self.bos_signal == "BUY") or
330
- (self.settings.EMA_STUDY and self.settings.EMA_CHECK_ON_OPEN and self.ema_open_signal == "BUY") or
331
- (self.settings.MACD_STUDY and self.settings.MACD_CHECK_ON_OPEN and self.macd_signal == "BUY") or
332
- (self.settings.BBM_STUDY and self.settings.BBM_CHECK_ON_OPEN and self.bbm_signal == "BUY") or
333
- (self.settings.RSI_STUDY and self.settings.RSI_CHECK_ON_OPEN and self.rsi_signal == "BUY") or
334
- (self.settings.TRENDLINE_STUDY and self.settings.TRENDLINE_CHECK_ON_OPEN and self.trendline_signal == "BUY")
335
- )
324
+ ema_open = (self.settings.EMA_STUDY and self.settings.EMA_CHECK_ON_OPEN and self.ema_open_signal == "BUY")
325
+ macd_open = (self.settings.MACD_STUDY and self.settings.MACD_CHECK_ON_OPEN and self.macd_signal == "BUY")
326
+ bbm_open = (self.settings.BBM_STUDY and self.settings.BBM_CHECK_ON_OPEN and self.bbm_signal == "BUY")
327
+ rsi_open = (self.settings.RSI_STUDY and self.settings.RSI_CHECK_ON_OPEN and self.rsi_signal == "BUY")
328
+ bos_open = (self.settings.BOS_STUDY and self.settings.BOS_CHECK_ON_OPEN and self.bos_signal == "BUY")
329
+ trendline_open = (self.settings.TRENDLINE_STUDY and self.settings.TRENDLINE_CHECK_ON_OPEN and self.trendline_signal == "BUY")
330
+
331
+ buy_conditions = ema_open or macd_open or bbm_open or rsi_open or bos_open or trendline_open
336
332
  additional_buy_conditions = (
337
333
  (not self.settings.ADX_STUDY or not self.settings.ADX_CHECK_ON_OPEN or self.adx_signal == "STRONG") and
338
334
  (not self.settings.CANDLE_TREND_STUDY or not self.settings.CANDLE_TREND_CHECK_ON_OPEN or self.candle_trend == "BULLISH")
@@ -340,14 +336,14 @@ class Interval:
340
336
 
341
337
 
342
338
  # Check for SELL signal
343
- sell_conditions = (
344
- (self.settings.BOS_STUDY and self.settings.BOS_CHECK_ON_OPEN and self.bos_signal == "SELL") or
345
- (self.settings.EMA_STUDY and self.settings.EMA_CHECK_ON_OPEN and self.ema_open_signal == "SELL") or
346
- (self.settings.MACD_STUDY and self.settings.MACD_CHECK_ON_OPEN and self.macd_signal == "SELL") or
347
- (self.settings.BBM_STUDY and self.settings.BBM_CHECK_ON_OPEN and self.bbm_signal == "SELL") or
348
- (self.settings.RSI_STUDY and self.settings.RSI_CHECK_ON_OPEN and self.rsi_signal == "SELL") or
349
- (self.settings.TRENDLINE_STUDY and self.settings.TRENDLINE_CHECK_ON_OPEN and self.trendline_signal == "SELL")
350
- )
339
+ ema_close = (self.settings.EMA_STUDY and self.settings.EMA_CHECK_ON_CLOSE and self.ema_close_signal == "SELL")
340
+ macd_close = (self.settings.MACD_STUDY and self.settings.MACD_CHECK_ON_CLOSE and self.macd_signal == "SELL")
341
+ bbm_close = (self.settings.BBM_STUDY and self.settings.BBM_CHECK_ON_CLOSE and self.bbm_signal == "SELL")
342
+ rsi_close = (self.settings.RSI_STUDY and self.settings.RSI_CHECK_ON_CLOSE and self.rsi_signal == "SELL")
343
+ bos_close = (self.settings.BOS_STUDY and self.settings.BOS_CHECK_ON_CLOSE and self.bos_signal == "SELL")
344
+ trendline_close = (self.settings.TRENDLINE_STUDY and self.settings.TRENDLINE_CHECK_ON_CLOSE and self.trendline_signal == "SELL")
345
+
346
+ sell_conditions = ema_close or macd_close or bbm_close or rsi_close or bos_close or trendline_close
351
347
  additional_sell_conditions = (
352
348
  (self.settings.ADX_STUDY and self.settings.ADX_CHECK_ON_OPEN and self.adx_signal == "STRONG") and
353
349
  (self.settings.CANDLE_TREND_STUDY and self.settings.CANDLE_TREND_CHECK_ON_OPEN or self.candle_trend == "BEARISH")
@@ -361,68 +357,40 @@ class Interval:
361
357
  else:
362
358
  self.current_signal = "HOLD"
363
359
  else:
364
- buy_conditions = (
365
- (self.settings.BOS_STUDY and self.settings.BOS_CHECK_ON_OPEN and self.bos_signal == "BUY")
366
- or not self.settings.BOS_STUDY
367
- ) and (
368
- (self.settings.EMA_STUDY and self.settings.EMA_CHECK_ON_OPEN and self.ema_open_signal == "BUY")
369
- or not self.settings.EMA_STUDY or not self.settings.EMA_CHECK_ON_OPEN
370
- ) and (
371
- (self.settings.MACD_STUDY and self.settings.MACD_CHECK_ON_OPEN and self.macd_signal == "BUY")
372
- or not self.settings.MACD_STUDY or not self.settings.MACD_CHECK_ON_OPEN
373
- ) and (
374
- (self.settings.BBM_STUDY and self.settings.BBM_CHECK_ON_OPEN and self.bbm_signal == "BUY")
360
+ ema_open =(self.settings.EMA_STUDY and self.settings.EMA_CHECK_ON_OPEN and self.ema_open_signal == "BUY") \
361
+ or not self.settings.EMA_STUDY or not self.settings.EMA_CHECK_ON_OPEN
362
+ macd_open = (self.settings.MACD_STUDY and self.settings.MACD_CHECK_ON_OPEN and self.macd_signal == "BUY") \
363
+ or not self.settings.MACD_STUDY or not self.settings.MACD_CHECK_ON_OPEN
364
+ bbm_open = (self.settings.BBM_STUDY and self.settings.BBM_CHECK_ON_OPEN and self.bbm_signal == "BUY") \
375
365
  or not self.settings.BBM_STUDY or not self.settings.BBM_CHECK_ON_OPEN
376
- ) and (
377
- (self.settings.RSI_STUDY and self.settings.RSI_CHECK_ON_OPEN and self.rsi_signal == "BUY")
366
+ rsi_open = (self.settings.RSI_STUDY and self.settings.RSI_CHECK_ON_OPEN and self.rsi_signal == "BUY") \
378
367
  or not self.settings.RSI_STUDY or not self.settings.RSI_CHECK_ON_OPEN
379
- ) and (
380
- (self.settings.TRENDLINE_STUDY and self.settings.TRENDLINE_CHECK_ON_OPEN and self.trendline_signal == "BUY")
368
+ bos_open = (self.settings.BOS_STUDY and self.settings.BOS_CHECK_ON_OPEN and self.bos_signal == "BUY") \
369
+ or not self.settings.BOS_STUDY or not self.settings.BOS_CHECK_ON_OPEN
370
+ trendline_open = (self.settings.TRENDLINE_STUDY and self.settings.TRENDLINE_CHECK_ON_OPEN and self.trendline_signal == "BUY") \
381
371
  or not self.settings.TRENDLINE_STUDY or not self.settings.TRENDLINE_CHECK_ON_OPEN
382
- )
383
- if not any([
384
- self.settings.EMA_STUDY, self.settings.MACD_STUDY, self.settings.BBM_STUDY,
385
- self.settings.RSI_STUDY, self.settings.TRENDLINE_STUDY
386
- ]) and not any([
387
- self.settings.EMA_CHECK_ON_OPEN, self.settings.MACD_CHECK_ON_OPEN, self.settings.BBM_CHECK_ON_OPEN,
388
- self.settings.RSI_CHECK_ON_OPEN, self.settings.TRENDLINE_CHECK_ON_OPEN
389
- ]):
390
- buy_conditions = False
391
372
 
373
+ buy_conditions = ema_open and macd_open and bbm_open and rsi_open and bos_open and trendline_open
392
374
  additional_buy_conditions = (
393
375
  (not self.settings.ADX_STUDY or not self.settings.ADX_CHECK_ON_OPEN or self.adx_signal == "STRONG") and
394
376
  (not self.settings.CANDLE_TREND_STUDY or not self.settings.CANDLE_TREND_CHECK_ON_OPEN or self.candle_trend == "BULLISH")
395
377
  )
396
378
 
397
379
  # Check for SELL signal
398
- sell_conditions = (
399
- (self.settings.BOS_STUDY and self.settings.BOS_CHECK_ON_OPEN and self.bos_signal == "SELL")
400
- or not self.settings.BOS_STUDY
401
- ) and (
402
- (self.settings.EMA_STUDY and self.settings.EMA_CHECK_ON_OPEN and self.ema_open_signal == "SELL")
403
- or not self.settings.EMA_STUDY or not self.settings.EMA_CHECK_ON_OPEN
404
- ) and (
405
- (self.settings.MACD_STUDY and self.settings.MACD_CHECK_ON_OPEN and self.macd_signal == "SELL")
406
- or not self.settings.MACD_STUDY or not self.settings.MACD_CHECK_ON_OPEN
407
- ) and (
408
- (self.settings.BBM_STUDY and self.settings.BBM_CHECK_ON_OPEN and self.bbm_signal == "SELL")
409
- or not self.settings.BBM_STUDY or not self.settings.BBM_CHECK_ON_OPEN
410
- ) and (
411
- (self.settings.RSI_STUDY and self.settings.RSI_CHECK_ON_OPEN and self.rsi_signal == "SELL")
412
- or not self.settings.RSI_STUDY or not self.settings.RSI_CHECK_ON_OPEN
413
- ) and (
414
- (self.settings.TRENDLINE_STUDY and self.settings.TRENDLINE_CHECK_ON_OPEN and self.trendline_signal == "SELL")
415
- or not self.settings.TRENDLINE_STUDY or not self.settings.TRENDLINE_CHECK_ON_OPEN
416
- )
417
- if not any([
418
- self.settings.EMA_STUDY, self.settings.MACD_STUDY, self.settings.BBM_STUDY,
419
- self.settings.RSI_STUDY, self.settings.TRENDLINE_STUDY
420
- ]) and not any([
421
- self.settings.EMA_CHECK_ON_OPEN, self.settings.MACD_CHECK_ON_OPEN, self.settings.BBM_CHECK_ON_OPEN,
422
- self.settings.RSI_CHECK_ON_OPEN, self.settings.TRENDLINE_CHECK_ON_OPEN
423
- ]):
424
- sell_conditions = False
425
-
380
+ ema_close= (self.settings.EMA_STUDY and self.settings.EMA_CHECK_ON_CLOSE and self.ema_close_signal == "SELL") \
381
+ or not self.settings.EMA_STUDY or not self.settings.EMA_CHECK_ON_CLOSE
382
+ macd_close = (self.settings.MACD_STUDY and self.settings.MACD_CHECK_ON_CLOSE and self.macd_signal == "SELL") \
383
+ or not self.settings.MACD_STUDY or not self.settings.MACD_CHECK_ON_CLOSE
384
+ bbm_close = (self.settings.BBM_STUDY and self.settings.BBM_CHECK_ON_CLOSE and self.bbm_signal == "SELL") \
385
+ or not self.settings.BBM_STUDY or not self.settings.BBM_CHECK_ON_CLOSE
386
+ rsi_close = (self.settings.RSI_STUDY and self.settings.RSI_CHECK_ON_CLOSE and self.rsi_signal == "SELL") \
387
+ or not self.settings.RSI_STUDY or not self.settings.RSI_CHECK_ON_CLOSE
388
+ bos_close = (self.settings.BOS_STUDY and self.settings.BOS_CHECK_ON_CLOSE and self.bos_signal == "SELL") \
389
+ or not self.settings.BOS_STUDY or not self.settings.BOS_CHECK_ON_CLOSE
390
+ trendline_close = (self.settings.TRENDLINE_STUDY and self.settings.TRENDLINE_CHECK_ON_CLOSE and self.trendline_signal == "SELL") \
391
+ or not self.settings.TRENDLINE_STUDY or not self.settings.TRENDLINE_CHECK_ON_CLOSE
392
+
393
+ sell_conditions = ema_close and macd_close and bbm_close and rsi_close and bos_close and trendline_close
426
394
  additional_sell_conditions = (
427
395
  (self.settings.ADX_STUDY and self.settings.ADX_CHECK_ON_OPEN and self.adx_signal == "STRONG") or
428
396
  (self.settings.CANDLE_TREND_STUDY and self.settings.CANDLE_TREND_CHECK_ON_OPEN or self.candle_trend == "BEARISH")
@@ -22,10 +22,11 @@ class Settings(BaseSettings):
22
22
  PROFIT_AMOUNT: float = Field(default=0, ge=0.0)
23
23
  LOSS_AMOUNT: float = Field(default=5.0, ge=0.0)
24
24
  BOT_TAKE_PROFIT_STOP_LOSS: bool = Field(default=False)
25
- TAKE_PROFIT_PRICE_TYPE: str = Field(default="MARK_PRICE")
26
- TAKE_PROFIT_ORDER_TYPE: str = Field(default="LIMIT")
27
- STOP_LOSS_PRICE_TYPE: str = Field(default="MARK_PRICE")
28
- STOP_LOSS_ORDER_TYPE: str = Field(default="MARKET")
25
+ BOT_TRAIL_STOP_LOSS: bool = Field(default=False)
26
+ TP_STOP_TYPE: str = Field(default="MARK_PRICE")
27
+ TP_ORDER_TYPE: str = Field(default="LIMIT")
28
+ SL_STOP_TYPE: str = Field(default="MARK_PRICE")
29
+ SL_ORDER_TYPE: str = Field(default="MARKET")
29
30
  OPTION_MOVING_AVERAGE: str = Field(default="1h")
30
31
  DELAY_IN_MINUTES_FOR_SAME_TICKER_TRADES: int = Field(default=15, ge=0)
31
32
  BARS: int = Field(default=100, ge=1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bitunix_automated_crypto_trading
3
- Version: 3.3.2
3
+ Version: 3.3.4
4
4
  Summary: Bitunix Futures Auto Trading Platform
5
5
  Home-page: https://github.com/tcj2001/bitunix-automated-crypto-trading
6
6
  Author: tcj2001