From 2dded6bb33a5f950410cc5806bc940e2cce4bde0 Mon Sep 17 00:00:00 2001 From: Carlos Wu Fei Date: Sat, 9 Sep 2023 05:58:33 +0100 Subject: [PATCH] Improve errors on failure --- api/account/assets.py | 1 - api/deals/margin.py | 80 +++-- web/public/index.html | 2 +- web/src/assets/scss/paper-dashboard.scss | 1 - .../scss/paper-dashboard/_fixed-plugin.scss | 339 ------------------ .../scss/paper-dashboard/_typography.scss | 26 +- .../scss/paper-dashboard/_utilities.scss | 20 +- web/src/components/BotCard.jsx | 12 +- web/src/pages/dashboard/Dashboard.jsx | 236 ++++++------ .../dashboard/PortfolioBenchmarkChart.jsx | 39 +- 10 files changed, 236 insertions(+), 520 deletions(-) delete mode 100644 web/src/assets/scss/paper-dashboard/_fixed-plugin.scss diff --git a/api/account/assets.py b/api/account/assets.py index 3ac850ac3..5eeddd657 100644 --- a/api/account/assets.py +++ b/api/account/assets.py @@ -314,7 +314,6 @@ async def get_balance_series(self, end_date, start_date): balances_series_diff = [] balances_series_dates = [] balance_btc_diff = [] - balance_series_diff_qty = [] balance_series.sort(key=lambda item: item["_id"], reverse=False) for index, item in enumerate(balance_series): diff --git a/api/deals/margin.py b/api/deals/margin.py index 2deb57b0a..207f6fcbd 100644 --- a/api/deals/margin.py +++ b/api/deals/margin.py @@ -14,11 +14,8 @@ from deals.schema import MarginOrderSchema from pydantic import ValidationError from tools.handle_error import QuantityTooLow, IsolateBalanceError, BinanceErrors -from tools.round_numbers import ( - round_numbers, - supress_notation, - round_numbers_ceiling -) +from tools.round_numbers import round_numbers, supress_notation, round_numbers_ceiling + class MarginShortError(Exception): pass @@ -97,17 +94,23 @@ def get_remaining_assets(self) -> tuple[float, float]: """ if float(self.isolated_balance[0]["quoteAsset"]["borrowed"]) > 0: - self._append_errors(f'Borrowed {self.isolated_balance[0]["quoteAsset"]["asset"]} still remaining, please clear out manually') + self._append_errors( + f'Borrowed {self.isolated_balance[0]["quoteAsset"]["asset"]} still remaining, please clear out manually' + ) self.active_bot.status = Status.error - + if float(self.isolated_balance[0]["baseAsset"]["borrowed"]) > 0: - self._append_errors(f'Borrowed {self.isolated_balance[0]["baseAsset"]["asset"]} still remaining, please clear out manually') + self._append_errors( + f'Borrowed {self.isolated_balance[0]["baseAsset"]["asset"]} still remaining, please clear out manually' + ) self.active_bot.status = Status.error - + quote_asset = float(self.isolated_balance[0]["quoteAsset"]["free"]) base_asset = float(self.isolated_balance[0]["baseAsset"]["free"]) - return round_numbers(quote_asset, self.qty_precision), round_numbers(base_asset, self.qty_precision) + return round_numbers(quote_asset, self.qty_precision), round_numbers( + base_asset, self.qty_precision + ) def cancel_open_orders(self, deal_type): """ @@ -150,9 +153,16 @@ def terminate_failed_transactions(self): symbol=self.active_bot.pair, amount=qty, ) - self.disable_isolated_margin_account(symbol=self.active_bot.pair) - - + try: + self.disable_isolated_margin_account(symbol=self.active_bot.pair) + except BinanceErrors as error: + if error.code == -1003: + self._append_errors("Isolated margin account can't be disabled within 24hrs, please disable manually") + all_errors = ". ".join(self.active_bot.errors) + # save error so it's available in the bot logs + self.save_bot_streaming() + # raise error so that it returns to the JSON response + raise MarginShortError(all_errors) def init_margin_short(self, initial_price): """ @@ -188,7 +198,9 @@ def init_margin_short(self, initial_price): raise QuantityTooLow("Margin short quantity is too low") # transfer quantity is base order size (what we want to invest) + stop loss to cover losses - stop_loss_price_inc = (float(initial_price) * (1 + (self.active_bot.stop_loss / 100))) + stop_loss_price_inc = float(initial_price) * ( + 1 + (self.active_bot.stop_loss / 100) + ) final_qty = float(stop_loss_price_inc * qty) transfer_qty = round_numbers_ceiling( final_qty, @@ -262,8 +274,14 @@ def retry_repayment(self, query_loan, buy_back_fiat): balance = float(self.isolated_balance[0]["quoteAsset"]["free"]) required_qty_quote = float(query_loan["rows"][0]["principal"]) - balance current_price = float(self.matching_engine(self.active_bot.pair, False)) - total_base_qty = round_numbers_ceiling(current_price * required_qty_quote, self.qty_precision) - qty = round_numbers_ceiling(float(query_loan["rows"][0]["principal"]) + float(self.isolated_balance[0]["baseAsset"]["interest"]), self.qty_precision) + total_base_qty = round_numbers_ceiling( + current_price * required_qty_quote, self.qty_precision + ) + qty = round_numbers_ceiling( + float(query_loan["rows"][0]["principal"]) + + float(self.isolated_balance[0]["baseAsset"]["interest"]), + self.qty_precision, + ) try: res = self.buy_margin_order( symbol=self.active_bot.pair, qty=qty, price=current_price @@ -302,10 +320,11 @@ def retry_repayment(self, query_loan, buy_back_fiat): self.retry_repayment(query_loan, buy_back_fiat) except Exception as error: print(error) - self._append_errors("Not enough SPOT balance to repay loan, need to liquidate manually") + self._append_errors( + "Not enough SPOT balance to repay loan, need to liquidate manually" + ) return - def terminate_margin_short(self, buy_back_fiat: bool = True): """ @@ -319,7 +338,9 @@ def terminate_margin_short(self, buy_back_fiat: bool = True): 2. Exchange asset to quote asset (USDT) 3. Transfer back to spot """ - logging.info(f"Terminating margin_short {self.active_bot.pair} for real bots trading") + logging.info( + f"Terminating margin_short {self.active_bot.pair} for real bots trading" + ) # Check margin account balance first balance = float(self.isolated_balance[0]["quoteAsset"]["free"]) @@ -378,7 +399,7 @@ def terminate_margin_short(self, buy_back_fiat: bool = True): self.active_bot.deal.margin_short_loan_timestamp = repay_details[ "timestamp" ] - + self.isolated_balance = self.get_isolated_balance(self.active_bot.pair) sell_back_qty = supress_notation( self.isolated_balance[0]["baseAsset"]["free"], @@ -389,7 +410,7 @@ def terminate_margin_short(self, buy_back_fiat: bool = True): # Sell quote and get base asset (USDT) # In theory, we should sell self.active_bot.base_order # but this can be out of sync - + res = self.sell_margin_order( symbol=self.active_bot.pair, qty=sell_back_qty ) @@ -470,7 +491,7 @@ def terminate_margin_short(self, buy_back_fiat: bool = True): bot = self.save_bot_streaming() self.active_bot = BotSchema.parse_obj(bot) - + return self.active_bot def margin_short_base_order(self): @@ -486,7 +507,10 @@ def margin_short_base_order(self): if self.db_collection.name == "bots": self.init_margin_short(initial_price) - order_res = self.sell_margin_order(symbol=self.active_bot.pair, qty=self.active_bot.deal.margin_short_base_order) + order_res = self.sell_margin_order( + symbol=self.active_bot.pair, + qty=self.active_bot.deal.margin_short_base_order, + ) else: # Simulate Margin sell # qty doesn't matter in paper bots @@ -796,9 +820,7 @@ def execute_take_profit(self, price=None): # we want base asset anyway now, because of long bot quote, base = self.get_remaining_assets() price = self.matching_engine(self.active_bot.pair, True, qty) - qty = round_numbers( - float(quote) / float(price), self.qty_precision - ) + qty = round_numbers(float(quote) / float(price), self.qty_precision) # If still qty = 0, it means everything is clear if qty == 0: @@ -874,9 +896,9 @@ def switch_to_long_bot(self, current_price): ) # start from current stop_loss_price which is where the bot switched to long strategy new_base_order_price = current_price - tp_price = new_base_order_price * (1 + ( - float(self.active_bot.take_profit) / 100 - )) + tp_price = new_base_order_price * ( + 1 + (float(self.active_bot.take_profit) / 100) + ) if float(self.active_bot.stop_loss) > 0: stop_loss_price = new_base_order_price - ( new_base_order_price * (float(self.active_bot.stop_loss) / 100) diff --git a/web/public/index.html b/web/public/index.html index 518b2c998..b713d4b3b 100644 --- a/web/public/index.html +++ b/web/public/index.html @@ -18,7 +18,7 @@ href="%PUBLIC_URL%/apple-icon.png" /> - + %REACT_APP_TITLE% diff --git a/web/src/assets/scss/paper-dashboard.scss b/web/src/assets/scss/paper-dashboard.scss index 411b0a4d0..fd58d7ba9 100644 --- a/web/src/assets/scss/paper-dashboard.scss +++ b/web/src/assets/scss/paper-dashboard.scss @@ -21,7 +21,6 @@ @import "paper-dashboard/tables"; @import "paper-dashboard/sidebar-and-main-panel"; @import "paper-dashboard/footers"; -@import "paper-dashboard/fixed-plugin"; // cards @import "paper-dashboard/cards"; diff --git a/web/src/assets/scss/paper-dashboard/_fixed-plugin.scss b/web/src/assets/scss/paper-dashboard/_fixed-plugin.scss deleted file mode 100644 index 561e7aacf..000000000 --- a/web/src/assets/scss/paper-dashboard/_fixed-plugin.scss +++ /dev/null @@ -1,339 +0,0 @@ -.fixed-plugin { - position: fixed; - right: 0; - width: 64px; - background: rgba(0, 0, 0, 0.3); - z-index: 1031; - border-radius: 8px 0 0 8px; - text-align: center; - top: 120px; - - li > a, - .badge { - transition: all 0.34s; - -webkit-transition: all 0.34s; - -moz-transition: all 0.34s; - } - - .fa-cog { - color: #ffffff; - padding: 10px; - border-radius: 0 0 6px 6px; - width: auto; - } - - .dropdown-menu { - right: 80px; - left: auto !important; - top: -52px !important; - width: 290px; - border-radius: 10px; - padding: 0 10px; - } - - .dropdown .dropdown-menu .nc-icon { - top: 2px; - right: 10px; - font-size: 14px; - } - - .dropdown-menu:after, - .dropdown-menu:before { - right: 10px; - margin-left: auto; - left: auto; - } - - .fa-circle-thin { - color: #ffffff; - } - - .active .fa-circle-thin { - color: #00bbff; - } - - .dropdown-menu > .active > a, - .dropdown-menu > .active > a:hover, - .dropdown-menu > .active > a:focus { - color: #777777; - text-align: center; - } - - img { - border-radius: 0; - width: 100%; - height: 100px; - margin: 0 auto; - } - - .dropdown-menu li > a:hover, - .dropdown-menu li > a:focus { - box-shadow: none; - } - - .badge { - border: 3px solid #ffffff; - border-radius: 50%; - cursor: pointer; - display: inline-block; - height: 23px; - margin-right: 5px; - position: relative; - width: 23px; - - &.badge-light { - border: 1px solid $light-gray; - - &.active, - &:hover { - border: 3px solid #0bf; - } - } - } - - .badge.active, - .badge:hover { - border-color: #00bbff; - } - - .badge-blue { - background-color: $brand-info; - } - .badge-green { - background-color: $brand-success; - } - .badge-orange { - background-color: $brand-primary; - } - .badge-yellow { - background-color: $brand-warning; - } - .badge-red { - background-color: $brand-danger; - } - - h5 { - font-size: 14px; - margin: 10px; - } - - .dropdown-menu li { - display: block; - padding: 15px 2px; - width: 25%; - float: left; - } - - li.adjustments-line, - li.header-title, - li.button-container { - width: 100%; - height: 35px; - min-height: inherit; - } - - li.button-container { - height: auto; - - div { - margin-bottom: 5px; - } - } - - #sharrreTitle { - text-align: center; - padding: 10px 0; - height: 50px; - } - - li.header-title { - height: 30px; - line-height: 25px; - font-size: 12px; - font-weight: 600; - text-align: center; - text-transform: uppercase; - } - - .adjustments-line { - p { - float: left; - display: inline-block; - margin-bottom: 0; - font-size: 1em; - color: #3c4858; - } - - a { - color: transparent; - - .badge-colors { - position: relative; - top: -2px; - } - - a:hover, - a:focus { - color: transparent; - } - } - - .togglebutton { - text-align: center; - - .label-switch { - position: relative; - left: -10px; - font-size: $font-size-mini; - color: $default-color; - - &.label-right { - left: 10px; - } - } - - .toggle { - margin-right: 0; - } - } - - .dropdown-menu > li.adjustments-line > a { - padding-right: 0; - padding-left: 0; - border-bottom: 1px solid #ddd; - border-radius: 0; - margin: 0; - } - } - - .dropdown-menu { - > li { - & > a.img-holder { - font-size: 16px; - text-align: center; - border-radius: 10px; - background-color: #fff; - border: 3px solid #fff; - padding-left: 0; - padding-right: 0; - opacity: 1; - cursor: pointer; - display: block; - max-height: 100px; - overflow: hidden; - padding: 0; - - img { - margin-top: auto; - } - } - - a.switch-trigger:hover, - & > a.switch-trigger:focus { - background-color: transparent; - } - - &:hover, - &:focus { - > a.img-holder { - border-color: rgba(0, 187, 255, 0.53); - } - } - } - - > .active > a.img-holder, - > .active > a.img-holder { - border-color: #00bbff; - background-color: #ffffff; - } - } - - .btn-social { - width: 50%; - display: block; - width: 48%; - float: left; - font-weight: 600; - } - - .btn-social { - i { - margin-right: 5px; - } - - &:first-child { - margin-right: 2%; - } - } - - .dropdown { - .dropdown-menu { - transform-origin: 0 0; - - &:before { - border-bottom: 16px solid rgba(0, 0, 0, 0); - border-left: 16px solid rgba(0, 0, 0, 0.2); - border-top: 16px solid rgba(0, 0, 0, 0); - right: -27px; - bottom: 425px; - } - - &:after { - border-bottom: 16px solid rgba(0, 0, 0, 0); - border-left: 16px solid #ffffff; - border-top: 16px solid rgba(0, 0, 0, 0); - right: -26px; - bottom: 425px; - } - - &:before, - &:after { - content: ""; - display: inline-block; - position: absolute; - width: 16px; - transform: translateY(-50px); - -webkit-transform: translateY(-50px); - -moz-transform: translateY(-50px); - } - } - - &.show-dropdown .show { - .dropdown-menu .show { - transform: translate3d(0, -60px, 0) !important; - bottom: auto !important; - top: 0 !important; - } - } - } - - .bootstrap-switch { - margin: 0; - } -} - -.fixed-plugin { - .show-dropdown { - .dropdown-menu[x-placement="bottom-start"] { - @include transform-translate-y-fixed-plugin(-100px); - - &:before, - &:after { - top: 100px; - } - } - .dropdown-menu[x-placement="top-start"] { - @include transform-translate-y-fixed-plugin(100px); - } - - &.show { - .dropdown-menu.show[x-placement="bottom-start"] { - @include transform-translate-y-fixed-plugin(-60px); - } - - .dropdown-menu.show[x-placement="top-start"] { - @include transform-translate-y-fixed-plugin(470px); - } - } - } -} diff --git a/web/src/assets/scss/paper-dashboard/_typography.scss b/web/src/assets/scss/paper-dashboard/_typography.scss index 9b936f826..25aa95d9c 100644 --- a/web/src/assets/scss/paper-dashboard/_typography.scss +++ b/web/src/assets/scss/paper-dashboard/_typography.scss @@ -1,3 +1,13 @@ +// Typography utilities +.capitalize { + text-transform: capitalize; +} + +.uppercase { + @extend .uppercase; +} + + button, input, optgroup, @@ -29,7 +39,7 @@ h1, small { font-weight: $font-weight-bold; - text-transform: uppercase; + @extend .uppercase; opacity: 0.8; } } @@ -66,7 +76,7 @@ h6, .h6 { font-size: $font-size-h6; font-weight: $font-weight-bold; - text-transform: uppercase; + @extend .uppercase; } p { margin-bottom: $margin-base-vertical; @@ -75,17 +85,11 @@ p { } } -// i.fa{ -// font-size: 18px; -// position: relative; -// top: 1px; -// } - .title { font-weight: $font-weight-bold; &.title-up { - text-transform: uppercase; + @extend .uppercase; a { color: $black-color; @@ -106,10 +110,8 @@ p { } .category, .card-category { - text-transform: capitalize; font-weight: $font-weight-normal; color: $dark-gray; - font-size: $font-size-mini; } .card-category { @@ -158,7 +160,7 @@ a.text-gray:hover { small { color: $default-color; font-size: $font-size-small; - text-transform: uppercase; + @extend .uppercase; } &.blockquote-primary { diff --git a/web/src/assets/scss/paper-dashboard/_utilities.scss b/web/src/assets/scss/paper-dashboard/_utilities.scss index 878aa13a3..5b232027f 100644 --- a/web/src/assets/scss/paper-dashboard/_utilities.scss +++ b/web/src/assets/scss/paper-dashboard/_utilities.scss @@ -6,14 +6,6 @@ z-index: $zindex-modal; } -.u-uppercase { - text-transform: uppercase; -} - -.u-capitalize { - text-transform: capitalize; -} - .u-center { text-align: center; } @@ -106,3 +98,15 @@ .u-text-right { text-align: right; } + +// Align large Fontawesome icons +.u-fa-lg { + position: relative; + display: inline-block; + i { + position: absolute; + top: 50%; + left: 50%; + font-size: 2rem; + } +} \ No newline at end of file diff --git a/web/src/components/BotCard.jsx b/web/src/components/BotCard.jsx index a83dbb516..3fbd747e7 100644 --- a/web/src/components/BotCard.jsx +++ b/web/src/components/BotCard.jsx @@ -62,7 +62,7 @@ export default function BotCard({ - + {!checkValue(x.deal) && ( 0 ? "success" : "danger"}> {getNetProfit(x) + "%"} @@ -74,7 +74,7 @@ export default function BotCard({
-

{x.name}

+

{x.name}

@@ -104,7 +104,7 @@ export default function BotCard({

Mode

-

+

{!checkValue(x.mode) ? x.mode : "Unknown"}

@@ -114,7 +114,7 @@ export default function BotCard({

Strategy

-

{x.strategy}

+

{x.strategy}

@@ -232,7 +232,7 @@ export default function BotCard({ history.push(`${history.location.pathname}/edit/${x.id}`) } > - +