async def get_insurance(offer: EscrowOffer) -> Decimal: """Get insurance of escrow asset in ``offer`` taking limits into account.""" offer_sum = offer[f"sum_{offer.type}"].to_decimal() asset = offer[offer.type] limits = await get_escrow_instance(asset).get_limits(asset) if not limits: return offer_sum insured = min(offer_sum, limits.single) cursor = database.escrow.aggregate([{ "$group": { "_id": 0, "insured_total": { "$sum": "$insured" } } }]) if await cursor.fetch_next: insured_total = cursor.next_object()["insured_total"] if insured_total != 0: insured_total = insured_total.to_decimal() total_difference = limits.total - insured_total - insured if total_difference < 0: insured += total_difference return normalize(insured)
async def edit_field(message: types.Message, state: FSMContext): """Ask new value of chosen order's field during editing.""" user = database_user.get() edit = user["edit"] field = edit["field"] invert = user.get("invert_order", False) set_dict = {} error = None if field == "sum_buy": try: transaction_sum = money.money(message.text) except money.MoneyValueError as exception: error = str(exception) else: order = await database.orders.find_one({"_id": edit["order_id"]}) set_dict["sum_buy"] = Decimal128(transaction_sum) if "price_sell" in order: set_dict["sum_sell"] = Decimal128( money.normalize(transaction_sum * order["price_sell"].to_decimal()) ) elif field == "sum_sell": try: transaction_sum = money.money(message.text) except money.MoneyValueError as exception: error = str(exception) else: order = await database.orders.find_one({"_id": edit["order_id"]}) set_dict["sum_sell"] = Decimal128(transaction_sum) if "price_buy" in order: set_dict["sum_buy"] = Decimal128( money.normalize(transaction_sum * order["price_buy"].to_decimal()) ) elif field == "price": try: price = money.money(message.text) except money.MoneyValueError as exception: error = str(exception) else: order = await database.orders.find_one({"_id": edit["order_id"]}) if invert: price_sell = money.normalize(Decimal(1) / price) set_dict["price_buy"] = Decimal128(price) set_dict["price_sell"] = Decimal128(price_sell) if order.get("sum_currency") == "buy": set_dict["sum_sell"] = Decimal128( money.normalize(order["sum_buy"].to_decimal() * price_sell) ) elif "sum_sell" in order: set_dict["sum_buy"] = Decimal128( money.normalize(order["sum_sell"].to_decimal() * price) ) else: price_buy = money.normalize(Decimal(1) / price) set_dict["price_buy"] = Decimal128(price_buy) set_dict["price_sell"] = Decimal128(price) if order.get("sum_currency") == "sell": set_dict["sum_buy"] = Decimal128( money.normalize(order["sum_sell"].to_decimal() * price_buy) ) elif "sum_buy" in order: set_dict["sum_sell"] = Decimal128( money.normalize(order["sum_buy"].to_decimal() * price) ) elif field == "payment_system": payment_system = message.text.replace("\n", " ") if len(payment_system) >= 150: await tg.send_message( message.chat.id, i18n("exceeded_character_limit {limit} {sent}").format( limit=150, sent=len(payment_system) ), ) return set_dict["payment_system"] = payment_system elif field == "duration": try: duration = int(message.text) if duration <= 0: raise ValueError except ValueError: error = i18n("send_natural_number") else: if duration > config.ORDER_DURATION_LIMIT: error = i18n("exceeded_duration_limit {limit}").format( limit=config.ORDER_DURATION_LIMIT ) else: order = await database.orders.find_one({"_id": edit["order_id"]}) set_dict["duration"] = duration set_dict["expiration_time"] = time() + duration * 24 * 60 * 60 set_dict["notify"] = True elif field == "comments": comments = message.text if len(comments) >= 150: await tg.send_message( message.chat.id, i18n("exceeded_character_limit {limit} {sent}").format( limit=150, sent=len(comments) ), ) return set_dict["comments"] = comments if set_dict: await finish_edit(user, {"$set": set_dict}) await message.delete() try: await tg.delete_message(user["chat"], edit["message_id"]) except MessageCantBeDeleted: return elif error: await message.delete() await tg.edit_message_text(error, message.chat.id, edit["message_id"])
async def choose_sum(message: types.Message, state: FSMContext): """Set sum. If price and sum in another currency were not specified, ask for sum in another currency. Otherwise calculate it if price was specified, and, finally, ask for cashless payment system. """ order = await database.creation.find_one({"user_id": message.from_user.id}) if "sum_currency" not in order: currency = message.text.upper() if currency == order["buy"]: sum_currency = "buy" elif currency == order["sell"]: sum_currency = "sell" else: await tg.send_message(message.chat.id, i18n("choose_sum_currency_with_buttons")) return await database.creation.update_one( {"_id": order["_id"]}, {"$set": { "sum_currency": sum_currency }}) await tg.send_message( message.chat.id, i18n("ask_order_sum {currency}").format(currency=currency)) return try: transaction_sum = money(message.text) except MoneyValueError as exception: await tg.send_message(message.chat.id, str(exception)) return update_dict = {"sum_" + order["sum_currency"]: Decimal128(transaction_sum)} new_sum_currency = "sell" if order["sum_currency"] == "buy" else "buy" sum_field = f"sum_{new_sum_currency}" price_field = f"price_{new_sum_currency}" if price_field in order: update_dict[sum_field] = Decimal128( normalize(transaction_sum * order[price_field].to_decimal())) elif sum_field not in order: update_dict["sum_currency"] = new_sum_currency await database.creation.update_one({"_id": order["_id"]}, {"$set": update_dict}) await tg.send_message( message.chat.id, i18n("ask_order_sum {currency}").format( currency=order[update_dict["sum_currency"]]), reply_markup=InlineKeyboardMarkup( inline_keyboard=await inline_control_buttons(back=False)), ) return await database.creation.update_one({"_id": order["_id"]}, {"$set": update_dict}) if order["buy"] not in whitelist.FIAT and order[ "sell"] not in whitelist.FIAT: await OrderCreation.location.set() await tg.send_message( message.chat.id, i18n("ask_location"), reply_markup=InlineKeyboardMarkup( inline_keyboard=await inline_control_buttons()), ) return await OrderCreation.payment_system.set() await tg.send_message( message.chat.id, i18n("cashless_payment_system"), reply_markup=InlineKeyboardMarkup( inline_keyboard=await inline_control_buttons()), )
async def orders_list( cursor: Cursor, chat_id: int, start: int, quantity: int, buttons_data: str, user_id: typing.Optional[int] = None, message_id: typing.Optional[int] = None, invert: typing.Optional[bool] = None, ) -> None: """Send list of orders. :param cursor: Cursor of MongoDB query to orders. :param chat_id: Telegram ID of current chat. :param start: Start index. :param quantity: Quantity of orders in cursor. :param buttons_data: Beginning of callback data of left/right buttons. :param user_id: Telegram ID of current user if cursor is not user-specific. :param message_id: Telegram ID of message to edit. :param invert: Invert all prices. """ user = await database.users.find_one({"id": types.User.get_current().id}) if invert is None: invert = user.get("invert_book", False) else: await database.users.update_one({"_id": user["_id"]}, {"$set": { "invert_book": invert }}) keyboard = types.InlineKeyboardMarkup( row_width=min(config.ORDERS_COUNT // 2, 8)) inline_orders_buttons = ( types.InlineKeyboardButton( emojize(":arrow_left:"), callback_data="{} {} {}".format(buttons_data, start - config.ORDERS_COUNT, int(invert)), ), types.InlineKeyboardButton( emojize(":arrow_right:"), callback_data="{} {} {}".format(buttons_data, start + config.ORDERS_COUNT, int(invert)), ), ) if quantity == 0: keyboard.row(*inline_orders_buttons) text = i18n("no_orders") if message_id is None: await tg.send_message(chat_id, text, reply_markup=keyboard) else: await tg.edit_message_text(text, chat_id, message_id, reply_markup=keyboard) return all_orders = await cursor.to_list(length=start + config.ORDERS_COUNT) orders = all_orders[start:] lines = [] buttons = [] current_time = time() for i, order in enumerate(orders): line = "" if user_id is None: if not order.get( "archived") and order["expiration_time"] > current_time: line += emojize(":arrow_forward: ") else: line += emojize(":pause_button: ") exp = Decimal("1e-5") if "sum_sell" in order: line += "{:,} ".format( normalize(order["sum_sell"].to_decimal(), exp)) line += "{} → ".format(order["sell"]) if "sum_buy" in order: line += "{:,} ".format( normalize(order["sum_buy"].to_decimal(), exp)) line += order["buy"] if "price_sell" in order: if invert: line += " ({:,} {}/{})".format( normalize(order["price_buy"].to_decimal(), exp), order["buy"], order["sell"], ) else: line += " ({:,} {}/{})".format( normalize(order["price_sell"].to_decimal(), exp), order["sell"], order["buy"], ) if user_id is not None and order["user_id"] == user_id: line = f"*{line}*" lines.append(f"{i + 1}. {line}") buttons.append( types.InlineKeyboardButton("{}".format(i + 1), callback_data="get_order {}".format( order["_id"]))) keyboard.row( types.InlineKeyboardButton( i18n("invert"), callback_data="{} {} {}".format(buttons_data, start, int(not invert)), )) keyboard.add(*buttons) keyboard.row(*inline_orders_buttons) text = ("\\[" + i18n("page {number} {total}").format( number=math.ceil(start / config.ORDERS_COUNT) + 1, total=math.ceil(quantity / config.ORDERS_COUNT), ) + "]\n" + "\n".join(lines)) if message_id is None: await tg.send_message( chat_id, text, reply_markup=keyboard, parse_mode=types.ParseMode.MARKDOWN, disable_web_page_preview=True, ) else: await tg.edit_message_text( text, chat_id, message_id, reply_markup=keyboard, parse_mode=types.ParseMode.MARKDOWN, disable_web_page_preview=True, )