bitunix-automated-crypto-trading 3.2.5__tar.gz → 3.2.7__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.2.5 → bitunix_automated_crypto_trading-3.2.7}/PKG-INFO +1 -1
  2. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/README.md +3 -0
  3. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/bitunix_automated_crypto_trading/BitunixApi.py +68 -14
  4. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/bitunix_automated_crypto_trading/BitunixSignal.py +97 -93
  5. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/bitunix_automated_crypto_trading/TickerManager.py +7 -6
  6. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/bitunix_automated_crypto_trading/bitunix.py +1 -1
  7. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/bitunix_automated_crypto_trading/config.py +30 -27
  8. bitunix_automated_crypto_trading-3.2.7/bitunix_automated_crypto_trading/version.py +1 -0
  9. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/bitunix_automated_crypto_trading.egg-info/PKG-INFO +1 -1
  10. bitunix_automated_crypto_trading-3.2.5/bitunix_automated_crypto_trading/version.py +0 -1
  11. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/bitunix_automated_crypto_trading/AsyncThreadRunner.py +0 -0
  12. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/bitunix_automated_crypto_trading/BitunixWebSocket.py +0 -0
  13. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/bitunix_automated_crypto_trading/DataFrameHtmlRenderer.py +0 -0
  14. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/bitunix_automated_crypto_trading/NotificationManager.py +0 -0
  15. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/bitunix_automated_crypto_trading/SupportResistance.py +0 -0
  16. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/bitunix_automated_crypto_trading/ThreadManager.py +0 -0
  17. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/bitunix_automated_crypto_trading/__init__.py +0 -0
  18. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/bitunix_automated_crypto_trading/logger.py +0 -0
  19. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/bitunix_automated_crypto_trading.egg-info/SOURCES.txt +0 -0
  20. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/bitunix_automated_crypto_trading.egg-info/dependency_links.txt +0 -0
  21. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/bitunix_automated_crypto_trading.egg-info/entry_points.txt +0 -0
  22. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/bitunix_automated_crypto_trading.egg-info/requires.txt +0 -0
  23. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/bitunix_automated_crypto_trading.egg-info/top_level.txt +0 -0
  24. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/setup.cfg +0 -0
  25. {bitunix_automated_crypto_trading-3.2.5 → bitunix_automated_crypto_trading-3.2.7}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bitunix_automated_crypto_trading
3
- Version: 3.2.5
3
+ Version: 3.2.7
4
4
  Summary: Bitunix Futures Auto Trading Platform
5
5
  Home-page: https://github.com/tcj2001/bitunix-automated-crypto-trading
6
6
  Author: tcj2001
@@ -59,9 +59,12 @@ The platform can be configured through the `config.py` file or `config.txt`. Key
59
59
  - `MAX_AUTO_TRADES`: Maximum number of automated trades
60
60
  - `PROFIT_PERCENTAGE`: Target profit ROI percentage
61
61
  - `LOSS_PERCENTAGE`: Maximum loss ROI percentage
62
+ - `PROFIT_LOSS_PRICE_TYPE`: "MARK_PRICE" or "LAST_PRICE"
63
+ - `PROFIT_LOSS_ORDER_TYPE`: "MARKET" or "LIMIT"
62
64
  - `PROFIT_AMOUNT`: Target profit amount
63
65
  - `LOSS_AMOUNT`: Maximum loss amount
64
66
  - `OPTION_MOVING_AVERAGE`: Moving average period (1h, 1d, 15m, 5m, 1m)
67
+ - `DELAY_IN_MINUTES_FOR_SAME_TICKER_TRADES`: Delay in minutes to prohibit trading the same ticker again
65
68
  - `BARS`: Number of bars to use for study and charting
66
69
 
67
70
  - `Technical Indicators Parameters`:
@@ -9,6 +9,7 @@ from urllib.parse import urlencode
9
9
  from typing import Dict, Any
10
10
  import traceback
11
11
  from logger import Logger
12
+ #import ccxt
12
13
 
13
14
 
14
15
  class BitunixApi:
@@ -17,6 +18,7 @@ class BitunixApi:
17
18
  self.api_key = api_key
18
19
  self.secret_key = secret_key
19
20
  self.logger = logger
21
+ self.settings = settings
20
22
  self.session = requests.Session()
21
23
  self.session.headers.update({
22
24
  'Content-Type': 'application/json',
@@ -118,18 +120,70 @@ class BitunixApi:
118
120
  response.raise_for_status()
119
121
  return response.json()
120
122
 
121
- async def PlaceOrder(self, ticker, qty, price, side, positionId=0, tradeSide="OPEN",reduceOnly=False):
122
- data = {
123
- "side": side,
124
- "orderType":"LIMIT",
125
- "qty": qty,
126
- "price": price,
127
- "symbol": ticker,
128
- "tradeSide":tradeSide,
129
- "reduceOnly":reduceOnly,
130
- "positionId":positionId
131
- }
132
- datajs = await self._post_authenticated(self.placeOrder_Url,data)
123
+ async def PlaceOrder(self, ticker, qty, price, side, positionId=0, tradeSide="OPEN",reduceOnly=False, takeProfitPrice=0, stopLossPrice=0):
124
+ datajs = None
125
+ if takeProfitPrice == 0 or stopLossPrice == 0:
126
+ data = {
127
+ "side": side,
128
+ "orderType":"LIMIT",
129
+ "qty": qty,
130
+ "price": price,
131
+ "symbol": ticker,
132
+ "tradeSide":tradeSide,
133
+ "reduceOnly":reduceOnly,
134
+ "positionId":positionId
135
+ }
136
+ datajs = await self._post_authenticated(self.placeOrder_Url,data)
137
+ elif takeProfitPrice == 0:
138
+ data = {
139
+ "side": side,
140
+ "orderType":"LIMIT",
141
+ "qty": qty,
142
+ "price": price,
143
+ "symbol": ticker,
144
+ "tradeSide":tradeSide,
145
+ "slPrice": stopLossPrice,
146
+ "slStopType":'MARK_PRICE',
147
+ "slStopType": self.settings.PROFIT_LOSS_PRICE_TYPE,
148
+ "slpOrderType": self.settings.PROFIT_LOSS_ORDER_TYPE,
149
+ "positionId":positionId
150
+ }
151
+ datajs = await self._post_authenticated(self.placeOrder_Url,data)
152
+ elif stopLossPrice == 0:
153
+ data = {
154
+ "side": side,
155
+ "orderType":"LIMIT",
156
+ "qty": qty,
157
+ "price": price,
158
+ "symbol": ticker,
159
+ "tradeSide":tradeSide,
160
+ "tpPrice": takeProfitPrice,
161
+ "tpStopType": self.settings.PROFIT_LOSS_PRICE_TYPE,
162
+ "tppOrderType": self.settings.PROFIT_LOSS_ORDER_TYPE,
163
+ "tpOrderPrice":takeProfitPrice,
164
+ "positionId":positionId
165
+ }
166
+ datajs = await self._post_authenticated(self.placeOrder_Url,data)
167
+ else:
168
+ data = {
169
+ "side": side,
170
+ "orderType":"LIMIT",
171
+ "qty": qty,
172
+ "price": price,
173
+ "symbol": ticker,
174
+ "tradeSide":tradeSide,
175
+ "tpPrice": takeProfitPrice,
176
+ "tpStopType": self.settings.PROFIT_LOSS_PRICE_TYPE,
177
+ "tppOrderType": self.settings.PROFIT_LOSS_ORDER_TYPE,
178
+ "tpOrderPrice":takeProfitPrice,
179
+ "slPrice": stopLossPrice,
180
+ "slStopType": self.settings.PROFIT_LOSS_PRICE_TYPE,
181
+ "slpOrderType": self.settings.PROFIT_LOSS_ORDER_TYPE,
182
+ "slOrderPrice": stopLossPrice,
183
+ "positionId":positionId
184
+ }
185
+ datajs = await self._post_authenticated(self.placeOrder_Url,data)
186
+
133
187
  return datajs
134
188
 
135
189
  async def FlashClose(self, positionId):
@@ -193,7 +247,7 @@ class BitunixApi:
193
247
  except Exception as e:
194
248
  stack = traceback.extract_stack()
195
249
  function_name = stack[-2].name
196
- logger.error(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
250
+ self.logger.error(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
197
251
 
198
252
  async def GetTickersPair(self, tickersStr):
199
253
  try:
@@ -204,7 +258,7 @@ class BitunixApi:
204
258
  except Exception as e:
205
259
  stack = traceback.extract_stack()
206
260
  function_name = stack[-2].name
207
- logger.error(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
261
+ self.logger.error(f"Function: {function_name}, {e}, {e.args}, {type(e).__name__}")
208
262
 
209
263
  async def GetTickerList(self, threshold, volume):
210
264
  symbols=[]
@@ -111,7 +111,10 @@ class BitunixSignal:
111
111
  olist = [entry['symbol'] for entry in self.pendingOrders['orderList']]
112
112
  newlist=olist+plist+list(set(symbols))
113
113
  self.tickerList=newlist[:300]
114
- self.tickerList.remove("STMXUSDT")
114
+ if "STMXUSDT" in self.tickerList:
115
+ self.tickerList.remove("STMXUSDT")
116
+ if "AMBUSDT" in self.tickerList:
117
+ self.tickerList.remove("AMBUSDT")
115
118
  #self.tickerList=['POLUSDT']
116
119
  else:
117
120
  self.tickerList = self.settings.TICKERS.split(",")
@@ -615,7 +618,6 @@ class BitunixSignal:
615
618
  self.signaldf['sell'] = self.signaldf.apply(self.add_sell_button, axis=1)
616
619
  else:
617
620
  self.signaldf = pd.DataFrame()
618
- self.signaldfStyle= self.signaldfrenderer.render_html(self.signaldf)
619
621
 
620
622
  #if not self.settings.USE_PUBLIC_WEBSOCKET:
621
623
  #get bid las ask using api for max_auto_trades rows
@@ -629,6 +631,9 @@ class BitunixSignal:
629
631
  for index, row in self.signaldf[:m].iterrows()
630
632
  ]
631
633
  )
634
+ else:
635
+ self.signaldf = pd.DataFrame()
636
+ self.signaldfStyle= self.signaldfrenderer.render_html(self.signaldf)
632
637
 
633
638
  except Exception as e:
634
639
  self.logger.info(f"Function: BuySellList, {e}, {e.args}, {type(e).__name__}")
@@ -636,6 +641,39 @@ class BitunixSignal:
636
641
  del inuse1, inuse2, inuseTickers
637
642
  gc.collect()
638
643
 
644
+ async def get_duration_minutes_unixtime(self, trade_time_unix):
645
+ """Calculates the duration in minutes from trade time to current time."""
646
+ current_time_unix = int(time.time()) # Get current Unix timestamp
647
+ duration_seconds = current_time_unix - trade_time_unix
648
+ duration_minutes = duration_seconds / 60 # Convert seconds to minutes
649
+ return round(duration_minutes)
650
+
651
+ async def get_duration(self,datetime_str):
652
+ """Calculates the duration in minutes from a given datetime string to the current time."""
653
+ # Convert input datetime string to a datetime object
654
+ trade_time = datetime.strptime(datetime_str, "%Y-%m-%d %H:%M:%S")
655
+
656
+ # Convert datetime object to Unix timestamp
657
+ trade_time_unix = int(trade_time.timestamp())
658
+
659
+ # Get current Unix timestamp
660
+ current_time_unix = int(time.time())
661
+
662
+ # Calculate duration in minutes
663
+ duration_minutes = (current_time_unix - trade_time_unix) / 60
664
+
665
+ return round(duration_minutes)
666
+
667
+
668
+
669
+ async def calculate_candles(self, timeframe, duration_minutes):
670
+
671
+ timeframe_dict = {"5m": 5, "15m": 15, "1h": 60, "1d": 1440} # 1 day = 1440 minutes
672
+ if timeframe in timeframe_dict:
673
+ return duration_minutes // timeframe_dict[timeframe]
674
+ else:
675
+ return np.nan
676
+
639
677
  async def AutoTradeProcess(self):
640
678
  if self.settings.VERBOSE_LOGGING:
641
679
  self.logger.info(f"AutoTradeProcess started")
@@ -665,26 +703,57 @@ class BitunixSignal:
665
703
  #open position upto a max of max_auto_trades from the signal list
666
704
  df=self.signaldf.copy(deep=False)
667
705
  for index, row in df.iterrows():
668
- side = "BUY" if row[f'{period}_barcolor'] == self.green and row[f'{period}_trend'] == "BUY" else "SELL" if row[f'{period}_barcolor'] == self.red and row[f'{period}_trend'] == "SELL" else ""
669
- if side != "":
670
- select = True
671
- self.pendingPositions = await self.bitunixApi.GetPendingPositionData({'symbol': row.symbol})
672
- if self.pendingPositions and len(self.pendingPositions) == 1:
673
- select = False
674
- self.pendingOrders = await self.bitunixApi.GetPendingOrderData({'symbol': row.symbol})
675
- if self.pendingOrders and len(self.pendingOrders['orderList']) == 1:
676
- select = False
677
- if select:
678
- last, bid, ask, mtv = await self.GetTickerBidLastAsk(row.symbol)
679
- price = (bid if side == "BUY" else ask if side == "SELL" else last) if bid<=last<=ask else last
680
- balance = float(self.portfoliodf["available"].iloc[0]) + float(self.portfoliodf["crossUnrealizedPNL"].iloc[0])
681
- qty= str(max(balance * (float(self.settings.ORDER_AMOUNT_PERCENTAGE) / 100) / price * int(self.settings.LEVERAGE),mtv))
682
-
683
- self.notifications.add_notification(
684
- f'{colors.YELLOW} Opening {"long" if side=="BUY" else "short"} position for {row.symbol} with {qty} qty @ {price})'
685
- )
686
- datajs = await self.bitunixApi.PlaceOrder(row.symbol, qty, price, side)
687
- count=count+1
706
+
707
+ # Calculate the duration in minutes since the position was opened
708
+ data = await self.bitunixApi.GetPositionHistoryData({'symbol': row['symbol']})
709
+ xtime = float(data['positionList'][0]['mtime'])
710
+ mtime = pd.to_datetime(xtime, unit='ms').tz_localize('UTC').tz_convert(cst).strftime('%Y-%m-%d %H:%M:%S')
711
+ duration_minutes = await self.get_duration(mtime)
712
+ if duration_minutes > self.settings.DELAY_IN_MINUTES_FOR_SAME_TICKER_TRADES:
713
+ side = "BUY" if row[f'{period}_barcolor'] == self.green and row[f'{period}_trend'] == "BUY" else "SELL" if row[f'{period}_barcolor'] == self.red and row[f'{period}_trend'] == "SELL" else ""
714
+ if side != "":
715
+ select = True
716
+ self.pendingPositions = await self.bitunixApi.GetPendingPositionData({'symbol': row.symbol})
717
+ if self.pendingPositions and len(self.pendingPositions) == 1:
718
+ select = False
719
+ self.pendingOrders = await self.bitunixApi.GetPendingOrderData({'symbol': row.symbol})
720
+ if self.pendingOrders and len(self.pendingOrders['orderList']) == 1:
721
+ select = False
722
+ if select:
723
+ last, bid, ask, mtv = await self.GetTickerBidLastAsk(row.symbol)
724
+
725
+ price = (bid if side == "BUY" else ask if side == "SELL" else last) if bid<=last<=ask else last
726
+
727
+ tp_price=0
728
+ if self.settings.PROFIT_AMOUNT > 0:
729
+ tp_price = price * (1 + float(self.settings.PROFIT_AMOUNT)) if side == "BUY" else price * (1 - float(self.settings.PROFIT_AMOUNT))
730
+ if self.settings.PROFIT_PERCENTAGE > 0 or self.settings.PROFIT_AMOUNT > 0:
731
+ tp_price = 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)
732
+
733
+ lp_price=0
734
+ if self.settings.LOSS_AMOUNT > 0:
735
+ lp_price = price * (1 - float(self.settings.LOSS_AMOUNT)) if side == "BUY" else price * (1 + float(self.settings.LOSS_AMOUNT))
736
+ if self.settings.LOSS_PERCENTAGE > 0 or self.settings.LOSS_AMOUNT > 0:
737
+ lp_price = 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)
738
+
739
+ balance = float(self.portfoliodf["available"].iloc[0]) + float(self.portfoliodf["crossUnrealizedPNL"].iloc[0])
740
+ qty= str(max(balance * (float(self.settings.ORDER_AMOUNT_PERCENTAGE) / 100 ) / price * int(self.settings.LEVERAGE),mtv))
741
+
742
+ self.notifications.add_notification(
743
+ f'{colors.YELLOW} Opening {"long" if side=="BUY" else "short"} position for {row.symbol} with {qty} qty @ {price})'
744
+ )
745
+ datajs = None
746
+ if tp_price == 0 and lp_price == 0:
747
+ datajs = await self.bitunixApi.PlaceOrder(row.symbol, qty, price, side)
748
+ elif tp_price == 0:
749
+ datajs = await self.bitunixApi.PlaceOrder(row.symbol, qty, price, side, stopLossPrice=lp_price)
750
+ elif lp_price == 0:
751
+ datajs = await self.bitunixApi.PlaceOrder(row.symbol, qty, price, side, takeProfitPrice=tp_price)
752
+ else:
753
+ datajs = await self.bitunixApi.PlaceOrder(row.symbol, qty, price, side, takeProfitPrice=tp_price, stopLossPrice=lp_price)
754
+ count=count+1
755
+ else:
756
+ self.logger.info(f"Skipping {row.symbol} as it has been opened for less than {self.settings.DELAY_IN_MINUTES_FOR_SAME_TICKER_TRADES} minutes")
688
757
  if count >= int(self.settings.MAX_AUTO_TRADES):
689
758
  break
690
759
  await asyncio.sleep(0)
@@ -699,7 +768,7 @@ class BitunixSignal:
699
768
  current_time = time.time() * 1000
700
769
  df=self.orderdf.copy(deep=False)
701
770
  for index, row in df.iterrows():
702
- if current_time - int(row.ctime) > 60000:
771
+ if current_time - int(row.ctime) > 6000000000:
703
772
  self.notifications.add_notification(
704
773
  f'{colors.LBLUE} Canceling order {row.orderId}, {row.symbol} {row.qty} created at {row.rtime} '
705
774
  )
@@ -714,6 +783,11 @@ class BitunixSignal:
714
783
  total_pnl = unrealized_pnl + realized_pnl
715
784
  side=row['side']
716
785
 
786
+ # Calculate the duration in minutes since the position was opened
787
+ ctime = row['ctime']
788
+ duration_minutes = await self.get_duration(ctime)
789
+ no_of_candles_since_current_open = await self.calculate_candles(period, duration_minutes)
790
+
717
791
  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']
718
792
  required_cols = set(requiredCols)
719
793
 
@@ -728,76 +802,6 @@ class BitunixSignal:
728
802
 
729
803
  if select and int(self.settings.MAX_AUTO_TRADES)!=0:
730
804
 
731
- # check take profit amount or accept loss amount
732
- if float(self.settings.LOSS_AMOUNT) > 0 and total_pnl < -float(self.settings.LOSS_AMOUNT):
733
- last, bid, ask, mtv = await self.GetTickerBidLastAsk(row.symbol)
734
- price = (ask if row['side'] == "BUY" else bid if row['side'] == "SELL" else last) if bid<=last<=ask else last
735
-
736
- self.notifications.add_notification(
737
- f'{colors.CYAN} Closing {"long" if side=="BUY" else "short"} position due to stop loss amount for {row.symbol} with {row.qty} qty @ {price})'
738
- )
739
- datajs = await self.bitunixApi.PlaceOrder(
740
- positionId=row.positionId,
741
- ticker=row.symbol,
742
- qty=row.qty,
743
- price=price,
744
- side=row.side,
745
- tradeSide="CLOSE"
746
- )
747
- continue
748
-
749
- if float(self.settings.PROFIT_AMOUNT) > 0 and total_pnl > float(self.settings.PROFIT_AMOUNT):
750
- last, bid, ask, mtv = await self.GetTickerBidLastAsk(row.symbol)
751
- price = (ask if row['side'] == "BUY" else bid if row['side'] == "SELL" else last) if bid<=last<=ask else last
752
-
753
- self.notifications.add_notification(
754
- f'{colors.CYAN} Closing {"long" if side=="BUY" else "short"} position due to take profit amount for {row.symbol} with {row.qty} qty @ {price})'
755
- )
756
- datajs = await self.bitunixApi.PlaceOrder(
757
- positionId=row.positionId,
758
- ticker=row.symbol,
759
- qty=row.qty,
760
- price=price,
761
- side=row.side,
762
- tradeSide="CLOSE"
763
- )
764
- continue
765
-
766
- # check take profit percentage or accept loss percentage
767
- if float(self.settings.LOSS_PERCENTAGE) > 0 and row['ROI'] < -float(self.settings.LOSS_PERCENTAGE):
768
- last, bid, ask, mtv = await self.GetTickerBidLastAsk(row.symbol)
769
- price = (ask if row['side'] == "BUY" else bid if row['side'] == "SELL" else last) if bid<=last<=ask else last
770
-
771
- self.notifications.add_notification(
772
- f'{colors.CYAN} Closing {"long" if side=="BUY" else "short"} position due to stop loss percentage for {row.symbol} with {row.qty} qty @ {price})'
773
- )
774
- datajs = await self.bitunixApi.PlaceOrder(
775
- positionId=row.positionId,
776
- ticker=row.symbol,
777
- qty=row.qty,
778
- price=price,
779
- side=row.side,
780
- tradeSide="CLOSE"
781
- )
782
- continue
783
-
784
- if float(self.settings.PROFIT_PERCENTAGE) > 0 and row['ROI'] > float(self.settings.PROFIT_PERCENTAGE):
785
- last, bid, ask, mtv = await self.GetTickerBidLastAsk(row.symbol)
786
- price = (ask if row['side'] == "BUY" else bid if row['side'] == "SELL" else last) if bid<=last<=ask else last
787
-
788
- self.notifications.add_notification(
789
- f'{colors.CYAN} Closing {"long" if side=="BUY" else "short"} position due to take profit percentage for {row.symbol} with {row.qty} qty @ {price})'
790
- )
791
- datajs = await self.bitunixApi.PlaceOrder(
792
- positionId=row.positionId,
793
- ticker=row.symbol,
794
- qty=row.qty,
795
- price=price,
796
- side=row.side,
797
- tradeSide="CLOSE"
798
- )
799
- continue
800
-
801
805
  # Moving average comparison between fast and medium or medium and slow
802
806
  if self.settings.EMA_STUDY and self.settings.EMA_CHECK_ON_CLOSE:
803
807
  if row.side == 'BUY' and self.signaldf_full.at[row.symbol, f'{period}_ema_close'] == "SELL":
@@ -268,16 +268,16 @@ class Interval:
268
268
 
269
269
  if df is not None and len(df) >= 2:
270
270
  if self.settings.TRENDLINE_BREAKOUT:
271
- if df['close'].iloc[-1] > df['trend_resistance_line'].iloc[-1]:
271
+ if df['close'].iloc[-1] > df['trend_resistance_line'].iloc[-1] and df['trend_resistance_line'].iloc[-1] > df['trend_support_line'].iloc[-1]:
272
272
  self.trendline_signal = "BUY"
273
- elif df['close'].iloc[-1] < df['trend_support_line'].iloc[-1]:
273
+ elif df['close'].iloc[-1] < df['trend_support_line'].iloc[-1] and df['trend_resistance_line'].iloc[-1] > df['trend_support_line'].iloc[-1]:
274
274
  self.trendline_signal = "SELL"
275
275
  else:
276
276
  self.trendline_signal = "HOLD"
277
277
  else:
278
- if df['close'].iloc[-1] < df['trend_resistance_line'].iloc[-1] and df['close'].iloc[-2] < df['open'].iloc[-2] and df['close'].iloc[-1] < df['open'].iloc[-1]:
278
+ if df['close'].iloc[-1] < df['trend_resistance_line'].iloc[-1] and df['close'].iloc[-2] < df['open'].iloc[-2] and df['close'].iloc[-1] < df['open'].iloc[-1] and df['trend_resistance_line'].iloc[-1] > df['trend_support_line'].iloc[-1]:
279
279
  self.trendline_signal = "SELL"
280
- elif df['close'].iloc[-1] > df['trend_support_line'].iloc[-1] and df['close'].iloc[-2] > df['open'].iloc[-2] and df['close'].iloc[-1] > df['open'].iloc[-1]:
280
+ elif df['close'].iloc[-1] > df['trend_support_line'].iloc[-1] and df['close'].iloc[-2] > df['open'].iloc[-2] and df['close'].iloc[-1] > df['open'].iloc[-1] and df['trend_resistance_line'].iloc[-1] > df['trend_support_line'].iloc[-1]:
281
281
  self.trendline_signal = "BUY"
282
282
  else:
283
283
  self.trendline_signal = "HOLD"
@@ -334,10 +334,11 @@ class Interval:
334
334
  (self.settings.TRENDLINE_STUDY and self.settings.TRENDLINE_CHECK_ON_OPEN and self.trendline_signal == "BUY")
335
335
  )
336
336
  additional_buy_conditions = (
337
- (not self.settings.ADX_STUDY or not self.settings.ADX_CHECK_ON_OPEN or self.adx_signal == "STRONG") or
337
+ (not self.settings.ADX_STUDY or not self.settings.ADX_CHECK_ON_OPEN or self.adx_signal == "STRONG") and
338
338
  (not self.settings.CANDLE_TREND_STUDY or not self.settings.CANDLE_TREND_CHECK_ON_OPEN or self.candle_trend == "BULLISH")
339
339
  )
340
340
 
341
+
341
342
  # Check for SELL signal
342
343
  sell_conditions = (
343
344
  (self.settings.BOS_STUDY and self.settings.BOS_CHECK_ON_OPEN and self.bos_signal == "SELL") or
@@ -348,7 +349,7 @@ class Interval:
348
349
  (self.settings.TRENDLINE_STUDY and self.settings.TRENDLINE_CHECK_ON_OPEN and self.trendline_signal == "SELL")
349
350
  )
350
351
  additional_sell_conditions = (
351
- (self.settings.ADX_STUDY and self.settings.ADX_CHECK_ON_OPEN and self.adx_signal == "STRONG") or
352
+ (self.settings.ADX_STUDY and self.settings.ADX_CHECK_ON_OPEN and self.adx_signal == "STRONG") and
352
353
  (self.settings.CANDLE_TREND_STUDY and self.settings.CANDLE_TREND_CHECK_ON_OPEN or self.candle_trend == "BEARISH")
353
354
  )
354
355
 
@@ -616,7 +616,7 @@ async def stream_log_file(websocket: WebSocket):
616
616
  lines = log_file.readlines()
617
617
  # Determine starting point: offset_from_end
618
618
  start_index = max(len(lines) - 100, 0)
619
- for line in lines[start_index:]: # Yield existing lines from offset
619
+ for line in reversed(lines[start_index:]): # Yield existing lines from offset
620
620
  await websocket.send_text(line)
621
621
  # Stream new lines added to the file
622
622
  log_file.seek(0, 2) # Go to the end of the file
@@ -10,18 +10,21 @@ class Settings(BaseSettings):
10
10
 
11
11
  #Ticker list
12
12
  TICKERS:str = Field(default="")
13
- THRESHOLD: float = Field(default=5.0, ge=0.0)
14
- MIN_VOLUME: int = Field(default=10_000_000, ge=0)
13
+ THRESHOLD: float = Field(default=100.0, ge=0.0)
14
+ MIN_VOLUME: int = Field(default=500000000, ge=0)
15
15
 
16
16
  # Trading Parameters
17
17
  LEVERAGE: int = Field(default=20, ge=1, le=100)
18
- ORDER_AMOUNT_PERCENTAGE: float = Field(default=0.01, ge=0.0, le=100)
18
+ ORDER_AMOUNT_PERCENTAGE: float = Field(default=10, ge=0.0, le=100)
19
19
  MAX_AUTO_TRADES: int = Field(default=10, ge=0)
20
- PROFIT_PERCENTAGE: float = Field(default=25, ge=0.0)
21
- LOSS_PERCENTAGE: float = Field(default=50, ge=0.0)
22
- PROFIT_AMOUNT: float = Field(default=0.25, ge=0.0)
20
+ PROFIT_PERCENTAGE: float = Field(default=10, ge=0.0)
21
+ LOSS_PERCENTAGE: float = Field(default=10, ge=0.0)
22
+ PROFIT_AMOUNT: float = Field(default=0, ge=0.0)
23
23
  LOSS_AMOUNT: float = Field(default=5.0, ge=0.0)
24
+ PROFIT_LOSS_PRICE_TYPE: str = Field(default="MARK_PRICE", pattern=r"^(MARK_PRICE|LAST_PRICE)$")
25
+ PROFIT_LOSS_ORDER_TYPE: str = Field(default="LIMIT", pattern=r"^(MARKET|LIMIT)$")
24
26
  OPTION_MOVING_AVERAGE: str = Field(default="1h")
27
+ DELAY_IN_MINUTES_FOR_SAME_TICKER_TRADES: int = Field(default=15, ge=0)
25
28
  BARS: int = Field(default=100, ge=1)
26
29
 
27
30
  # Technical Indicators Parameters
@@ -40,50 +43,50 @@ class Settings(BaseSettings):
40
43
  # Technical Indicators
41
44
  OPEN_ON_ANY_SIGNAL: bool = Field(default=True)
42
45
 
43
- BOS_PERIOD: int = Field(default=20, ge=1)
44
- BOS_STUDY: bool = Field(default=True)
45
- BOS_CHART: bool = Field(default=True)
46
- BOS_CHECK_ON_OPEN: bool = Field(default=False)
47
- BOS_CHECK_ON_CLOSE: bool = Field(default=False)
48
-
49
46
  EMA_CHART: bool = Field(default=True)
50
47
  EMA_STUDY: bool = Field(default=True)
51
48
  EMA_CROSSING: bool = Field(default=False)
52
49
  EMA_CHECK_ON_OPEN: bool = Field(default=True)
53
50
  EMA_CHECK_ON_CLOSE: bool = Field(default=True)
54
- EMA_CLOSE_ON_FAST_MEDIUM: bool = Field(default=True)
51
+ EMA_CLOSE_ON_FAST_MEDIUM: bool = Field(default=False)
55
52
 
56
- MACD_CHART: bool = Field(default=False)
53
+ MACD_CHART: bool = Field(default=True)
57
54
  MACD_STUDY: bool = Field(default=True)
58
- MACD_CROSSING: bool = Field(default=False)
59
- MACD_CHECK_ON_OPEN: bool = Field(default=False)
55
+ MACD_CROSSING: bool = Field(default=True)
56
+ MACD_CHECK_ON_OPEN: bool = Field(default=True)
60
57
  MACD_CHECK_ON_CLOSE: bool = Field(default=False)
61
58
 
62
- BBM_CHART: bool = Field(default=False)
59
+ BBM_CHART: bool = Field(default=True)
63
60
  BBM_STUDY: bool = Field(default=True)
64
- BBM_CROSSING: bool = Field(default=False)
65
- BBM_CHECK_ON_OPEN: bool = Field(default=False)
61
+ BBM_CROSSING: bool = Field(default=True)
62
+ BBM_CHECK_ON_OPEN: bool = Field(default=True)
66
63
  BBM_CHECK_ON_CLOSE: bool = Field(default=False)
67
64
 
68
- RSI_CHART: bool = Field(default=False)
65
+ RSI_CHART: bool = Field(default=True)
69
66
  RSI_STUDY: bool = Field(default=True)
70
- RSI_CROSSING: bool = Field(default=False)
71
- RSI_CHECK_ON_OPEN: bool = Field(default=False)
67
+ RSI_CROSSING: bool = Field(default=True)
68
+ RSI_CHECK_ON_OPEN: bool = Field(default=True)
72
69
  RSI_CHECK_ON_CLOSE: bool = Field(default=False)
73
70
 
74
- TRENDLINE_PEAK_DISTANCE: int = Field(default=1, ge=0, le=30)
71
+ BOS_PERIOD: int = Field(default=24, ge=1)
72
+ BOS_STUDY: bool = Field(default=True)
73
+ BOS_CHART: bool = Field(default=True)
74
+ BOS_CHECK_ON_OPEN: bool = Field(default=True)
75
+ BOS_CHECK_ON_CLOSE: bool = Field(default=False)
76
+
77
+ TRENDLINE_PEAK_DISTANCE: int = Field(default=3, ge=0, le=30)
75
78
  TRENDLINE_CHART: bool = Field(default=True)
76
79
  TRENDLINE_STUDY: bool = Field(default=True)
77
- TRENDLINE_BREAKOUT: bool = Field(default=False)
78
- TRENDLINE_CHECK_ON_OPEN: bool = Field(default=False)
80
+ TRENDLINE_BREAKOUT: bool = Field(default=True)
81
+ TRENDLINE_CHECK_ON_OPEN: bool = Field(default=True)
79
82
  TRENDLINE_CHECK_ON_CLOSE: bool = Field(default=False)
80
83
 
81
84
  ADX_STUDY: bool = Field(default=True)
82
- ADX_CHECK_ON_OPEN: bool = Field(default=False)
85
+ ADX_CHECK_ON_OPEN: bool = Field(default=True)
83
86
  ADX_CHECK_ON_CLOSE: bool = Field(default=False)
84
87
 
85
88
  CANDLE_TREND_STUDY: bool = Field(default=True)
86
- CANDLE_TREND_CHECK_ON_OPEN: bool = Field(default=False)
89
+ CANDLE_TREND_CHECK_ON_OPEN: bool = Field(default=True)
87
90
  CANDLE_TREND_REVERSAL_CHECK_ON_CLOSE: bool = Field(default=False)
88
91
 
89
92
  # Time Intervals
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bitunix_automated_crypto_trading
3
- Version: 3.2.5
3
+ Version: 3.2.7
4
4
  Summary: Bitunix Futures Auto Trading Platform
5
5
  Home-page: https://github.com/tcj2001/bitunix-automated-crypto-trading
6
6
  Author: tcj2001