Esempio n. 1
0
def remove_order(order_record: Order):
    db.connect()
    try:
        order_record.delete_instance()
    except Exception as e:
        logger.error(f'Database error: {e}')
    finally:
        db.close()
Esempio n. 2
0
def remove_token(token_record: Token):
    db.connect()
    try:
        token_record.delete_instance(recursive=True)
    except Exception as e:
        logger.error(f'Database error: {e}')
    finally:
        db.close()
Esempio n. 3
0
    def command_edittoken_emoji(self, update: Update,
                                context: CallbackContext):
        assert context.user_data is not None
        edit = context.user_data['edittoken']
        token: TokenWatcher = self.parent.watchers[edit['token_address']]
        if update.message is not None:
            assert update.message.text
            edit['icon'] = update.message.text.strip()
        else:
            assert update.callback_query
            query = update.callback_query
            assert query.data
            if query.data == 'cancel':
                return self.command_canceltoken(update, context)
            elif query.data == 'None':
                edit['icon'] = None
            else:
                edit['icon'] = query.data

        token_record = token.token_record
        try:
            db.connect()
            with db.atomic():
                token_record.icon = edit['icon']
                token_record.save()
        except Exception as e:
            self.command_error(update,
                               context,
                               text=f'Failed to update database record: {e}')
            return ConversationHandler.END
        finally:
            del context.user_data['edittoken']
            db.close()
        token.emoji = token_record.icon + ' ' if token_record.icon else ''
        token.name = token.emoji + token.symbol
        chat_message(
            update,
            context,
            text=f'✅ Alright, the token will show as <b>"{token.name}"</b>. ',
            edit=self.config.update_messages,
        )
        return ConversationHandler.END
Esempio n. 4
0
 def command_addorder_summary(self, update: Update,
                              context: CallbackContext):
     assert update.effective_chat and update.callback_query and context.user_data is not None
     query = update.callback_query
     if query.data != 'ok':
         self.cancel_command(update, context)
         return ConversationHandler.END
     add = context.user_data['addorder']
     token: TokenWatcher = self.parent.watchers[add['token_address']]
     del add['token_address']  # not needed in order record creation
     try:
         db.connect()
         with db.atomic():
             order_record = Order.create(token=token.token_record,
                                         created=datetime.now(),
                                         **add)
     except Exception as e:
         self.command_error(update,
                            context,
                            text=f'Failed to create database record: {e}')
         return ConversationHandler.END
     finally:
         del context.user_data['addorder']
         db.close()
     order = OrderWatcher(order_record=order_record,
                          net=self.net,
                          dispatcher=context.dispatcher,
                          chat_id=update.effective_chat.id)
     token.orders.append(order)
     chat_message(
         update,
         context,
         text=f'✅ Order #{order_record.id} was added successfully!',
         edit=self.config.update_messages,
     )
     for job in token.scheduler.get_jobs():  # check prices now
         job.modify(next_run_time=datetime.now())
     return ConversationHandler.END
Esempio n. 5
0
    def command_edittoken_buyprice(self, update: Update,
                                   context: CallbackContext):
        assert context.user_data is not None
        edit = context.user_data['edittoken']
        token: TokenWatcher = self.parent.watchers[edit['token_address']]
        effective_buy_price: Optional[Decimal]
        if update.message is not None:
            assert update.message.text
            user_input = update.message.text.strip().lower()
            if 'bnb' in user_input:
                balance = self.net.get_token_balance(
                    token_address=token.address)
                if balance == 0:  # would lead to division by zero
                    chat_message(
                        update,
                        context,
                        text=
                        '⚠️ The token balance is zero, can\'t use calculation from BNB amount. '
                        + 'Try again with a price instead:',
                        edit=False,
                    )
                    return self.next.BUYPRICE
                try:
                    buy_amount = Decimal(user_input[:-3])
                except Exception:
                    chat_message(
                        update,
                        context,
                        text=
                        '⚠️ The BNB amount you inserted is not valid. Try again:',
                        edit=False)
                    return self.next.BUYPRICE
                effective_buy_price = buy_amount / balance
            else:
                try:
                    effective_buy_price = Decimal(user_input)
                except ValueError:
                    chat_message(
                        update,
                        context,
                        text='⚠️ This is not a valid price value. Try again:',
                        edit=False,
                    )
                    return self.next.BUYPRICE
        else:
            assert update.callback_query
            query = update.callback_query
            assert query.data
            if query.data == 'cancel':
                return self.command_canceltoken(update, context)
            elif query.data == 'None':
                effective_buy_price = None
            else:
                self.command_error(update, context, text='Invalid callback.')
                return ConversationHandler.END

        edit['effective_buy_price'] = effective_buy_price

        token_record = token.token_record
        try:
            db.connect()
            with db.atomic():
                token_record.effective_buy_price = (str(
                    edit['effective_buy_price']) if edit['effective_buy_price']
                                                    else None)
                token_record.save()
        except Exception as e:
            self.command_error(update,
                               context,
                               text=f'Failed to update database record: {e}')
            return ConversationHandler.END
        finally:
            del context.user_data['edittoken']
            db.close()
        token.effective_buy_price = edit['effective_buy_price']
        if effective_buy_price is None:
            chat_message(
                update,
                context,
                text=
                '✅ Alright, effective buy price for profit calculation is disabled.',
                edit=self.config.update_messages,
            )
        else:
            chat_message(
                update,
                context,
                text=f'✅ Alright, the token {token.name} ' +
                f'was bought at {token.effective_buy_price:.4g} BNB per token.',
                edit=self.config.update_messages,
            )
        return ConversationHandler.END
Esempio n. 6
0
    def command_edittoken_slippage(self, update: Update,
                                   context: CallbackContext):
        assert context.user_data is not None
        edit = context.user_data['edittoken']
        token: TokenWatcher = self.parent.watchers[edit['token_address']]
        if update.message is not None:
            assert update.message.text
            try:
                slippage = int(update.message.text.strip())
            except ValueError:
                chat_message(
                    update,
                    context,
                    text=
                    '⚠️ This is not a valid slippage value. Please enter an integer number for percentage '
                    + '(without percent sign). Try again:',
                    edit=False,
                )
                return self.next.SLIPPAGE
        else:
            assert update.callback_query
            query = update.callback_query
            assert query.data
            if query.data == 'cancel':
                return self.command_canceltoken(update, context)
            try:
                slippage = int(query.data)
            except ValueError:
                self.command_error(update,
                                   context,
                                   text='Invalid default slippage.')
                return ConversationHandler.END
        if slippage < 1:
            chat_message(
                update,
                context,
                text=
                '⚠️ This is not a valid slippage value. Please enter a positive integer number for percentage. '
                + 'Try again:',
                edit=False,
            )
            return self.next.SLIPPAGE
        edit['default_slippage'] = slippage

        token_record = token.token_record
        try:
            db.connect()
            with db.atomic():
                token_record.default_slippage = edit['default_slippage']
                token_record.save()
        except Exception as e:
            self.command_error(update,
                               context,
                               text=f'Failed to update database record: {e}')
            return ConversationHandler.END
        finally:
            del context.user_data['edittoken']
            db.close()
        token.default_slippage = token_record.default_slippage
        chat_message(
            update,
            context,
            text=f'✅ Alright, the token {token.name} ' +
            f'will use <b>{edit["default_slippage"]}%</b> slippage by default.',
            edit=self.config.update_messages,
        )
        return ConversationHandler.END
Esempio n. 7
0
 def buy(self, v2: bool, sell_v2: bool):
     balance_before = self.net.get_token_balance(
         token_address=self.token_record.address)
     buy_price_before = self.token_record.effective_buy_price
     res, tokens_out, txhash_or_error = self.net.buy_tokens(
         self.token_record.address,
         amount_bnb=self.amount,
         slippage_percent=self.slippage,
         gas_price=self.gas_price,
         v2=v2,
     )
     if not res:
         if len(txhash_or_error) == 66:
             reason_or_link = f'<a href="https://bscscan.com/tx/{txhash_or_error}">{txhash_or_error[:8]}...</a>'
         else:
             reason_or_link = txhash_or_error
         logger.error(f'Transaction failed: {reason_or_link}')
         self.dispatcher.bot.send_message(
             chat_id=self.chat_id,
             text=f'⛔️ <u>Transaction failed:</u> {txhash_or_error}\n' +
             'Order below deleted:\n' + self.long_repr(),
         )
         self.remove_order()
         self.finished = True  # will trigger deletion of the object
         return
     effective_price = self.get_human_amount() / tokens_out
     db.connect()
     try:
         with db.atomic():
             if buy_price_before is not None:
                 self.token_record.effective_buy_price = str(
                     (balance_before * Decimal(buy_price_before) +
                      tokens_out * effective_price) /
                     (balance_before + tokens_out))
             else:
                 self.token_record.effective_buy_price = str(
                     effective_price)
             self.token_record.save()
     except Exception as e:
         logger.error(f'Effective buy price update failed: {e}')
         self.dispatcher.bot.send_message(
             chat_id=self.chat_id,
             text=f'⛔️ Effective buy price update failed: {e}',
         )
     finally:
         db.close()
     logger.success(
         f'Buy transaction succeeded. Received {format_token_amount(tokens_out)} {self.token_record.symbol}. '
         + f'Effective price (after tax) {effective_price:.4g} BNB/token')
     self.dispatcher.bot.send_message(
         chat_id=self.chat_id,
         text='<u>Closing the following order:</u>\n' + self.long_repr())
     self.dispatcher.bot.send_message(
         chat_id=self.chat_id,
         text=
         f'✅ Got {format_token_amount(tokens_out)} {self.token_record.symbol} at '
         +
         f'tx <a href="https://bscscan.com/tx/{txhash_or_error}">{txhash_or_error[:8]}...</a>\n'
         + f'Effective price (after tax) {effective_price:.4g} BNB/token',
     )
     if not self.net.is_approved(token_address=self.token_record.address,
                                 v2=sell_v2):
         # pre-approve for later sell
         version = 'v2' if sell_v2 else 'v1'
         logger.info(
             f'Approving {self.token_record.symbol} for trading on PancakeSwap {version}.'
         )
         self.dispatcher.bot.send_message(
             chat_id=self.chat_id,
             text=
             f'Approving {self.token_record.symbol} for trading on PancakeSwap {version}...',
         )
         res = self.net.approve(token_address=self.token_record.address,
                                v2=sell_v2)
         if res:
             self.dispatcher.bot.send_message(
                 chat_id=self.chat_id,
                 text='✅ Approval successful!',
             )
         else:
             self.dispatcher.bot.send_message(
                 chat_id=self.chat_id,
                 text='⛔ Approval failed',
             )
     self.remove_order()
     self.finished = True  # will trigger deletion of the object
Esempio n. 8
0
    def command_addtoken_slippage(self, update: Update,
                                  context: CallbackContext):
        assert update.message and update.message.text and context.user_data is not None
        try:
            slippage = int(update.message.text.strip())
        except ValueError:
            chat_message(
                update,
                context,
                text=
                '⚠️ This is not a valid slippage value. Please enter an integer number for percentage. Try again:',
                edit=False,
            )
            return self.next.SLIPPAGE
        if slippage < 1:
            chat_message(
                update,
                context,
                text=
                '⚠️ This is not a valid slippage value. Please enter a positive integer number for percentage. '
                + 'Try again:',
                edit=False,
            )
            return self.next.SLIPPAGE
        add = context.user_data['addtoken']
        add['default_slippage'] = slippage
        emoji = add['icon'] + ' ' if add['icon'] else ''

        chat_message(
            update,
            context,
            text=f'Alright, the token <b>{emoji}{add["symbol"]}</b> ' +
            f'will use <b>{add["default_slippage"]}%</b> slippage by default.',
            edit=False,
        )
        try:
            db.connect()
            with db.atomic():
                token_record = Token.create(**add)
        except Exception as e:
            chat_message(update,
                         context,
                         text=f'⛔ Failed to create database record: {e}',
                         edit=False)
            del context.user_data['addtoken']
            return ConversationHandler.END
        finally:
            del context.user_data['addtoken']
            db.close()
        token = TokenWatcher(token_record=token_record,
                             net=self.net,
                             dispatcher=context.dispatcher,
                             config=self.config)
        self.parent.watchers[token.address] = token
        balance = self.net.get_token_balance(token_address=token.address)
        balance_usd = self.net.get_token_balance_usd(
            token_address=token.address, balance=balance)
        reply_markup = InlineKeyboardMarkup([[
            InlineKeyboardButton('➕ Create order',
                                 callback_data=f'addorder:{token.address}'),
            InlineKeyboardButton('💰 Buy/Sell now',
                                 callback_data=f'buysell:{token.address}'),
        ]])
        chat_message(
            update,
            context,
            text='✅ Token was added successfully. ' +
            f'Balance is {format_token_amount(balance)} {token.symbol} (${balance_usd:.2f}).',
            reply_markup=reply_markup,
            edit=False,
        )
        return ConversationHandler.END