From 81ee93878be0b615185631f36f2fa00558a6c6de Mon Sep 17 00:00:00 2001 From: Administrator Date: Fri, 26 Dec 2025 23:14:39 +0800 Subject: [PATCH] =?UTF-8?q?bitmart=E4=BC=98=E5=8C=96=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bitmart/均线自动化开单.py | 438 +++++++++++++++++++++++---- telegram/8619211027341.session | Bin 45056 -> 45056 bytes telegram/bot_session.session | Bin 49152 -> 49152 bytes telegram/bot_session.session-journal | Bin 8720 -> 0 bytes telegram/sign.db | Bin 40960 -> 40960 bytes 5 files changed, 374 insertions(+), 64 deletions(-) delete mode 100644 telegram/bot_session.session-journal diff --git a/bitmart/均线自动化开单.py b/bitmart/均线自动化开单.py index 60a57d9..66700f4 100644 --- a/bitmart/均线自动化开单.py +++ b/bitmart/均线自动化开单.py @@ -2,6 +2,7 @@ import time import uuid import datetime import requests +from typing import Tuple from tqdm import tqdm from loguru import logger @@ -107,6 +108,19 @@ class StrategyConfig: post_sl_mult_max: float = 1.16 post_sl_vol_alpha: float = 0.20 + # ========================================================= + # ✅ 正常平仓条件:价格回归到EMA附近时平仓 + # ========================================================= + normal_exit_threshold: float = 0.0003 # 0.03%,价格回归到EMA附近时平仓 + normal_exit_min_profit: float = 0.0002 # 0.02%,正常平仓最小盈利要求 + + # ========================================================= + # ✅ 手续费配置(固定10u,30倍杠杆) + # ========================================================= + fixed_margin: float = 10.0 # 固定每单10u保证金 + platform_fee_rate: float = 0.0005 # 平台手续费:开仓价值的万分之五 + rebate_rate: float = 0.90 # 返佣比例:90% + class BitmartFuturesMeanReversionBot: def __init__(self, cfg: StrategyConfig, bit_id=None): @@ -132,6 +146,10 @@ class BitmartFuturesMeanReversionBot: self.entry_ts = None self.last_exit_ts = 0 + # 开仓信息(用于手续费计算) + self.entry_margin = None # 开仓保证金(固定10u) + self.entry_position_value = None # 开仓时的仓位价值(保证金 * 杠杆) + # 日内权益基准 self.day_start_equity = None self.trading_enabled = True @@ -532,20 +550,30 @@ class BitmartFuturesMeanReversionBot: # ----------------- 下单 ----------------- def calculate_size(self, price: float) -> int: + """ + 计算仓位大小 + 固定每单10u保证金,30倍杠杆 + """ bal = self.get_assets_available() - if bal < 10: + if bal < self.cfg.fixed_margin: + logger.warning(f"余额不足:{bal:.2f} USDT < {self.cfg.fixed_margin} USDT") return 0 - margin = bal * self.cfg.risk_percent + # 固定保证金10u + margin = self.cfg.fixed_margin lev = int(self.cfg.leverage) # ⚠️ 沿用你的原假设:1张≈0.001ETH + # 仓位价值 = 保证金 * 杠杆 = 10 * 30 = 300u + # size = 仓位价值 / (价格 * 0.001) size = int((margin * lev) / (price * 0.001)) size = max(self.cfg.min_size, size) size = min(self.cfg.max_size, size) + + logger.info(f"计算仓位:保证金={margin}u, 杠杆={lev}x, 仓位价值={margin * lev}u, size={size}") return size - def place_market_order(self, side: int, size: int, ) -> bool: + def place_market_order(self, side: int, size: int) -> bool: """ 【下单函数】实际执行下单操作 side: 1=开多, 4=开空, 2=平多, 3=平空 @@ -554,43 +582,221 @@ class BitmartFuturesMeanReversionBot: return False try: - + # 开多单 if side == 1: self.click_safe('x://button[normalize-space(text()) ="市价"]') - self.page.ele('x://*[@id="size_0"]').input(size) - self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]') - elif side == 4: self.click_safe('x://button[normalize-space(text()) ="市价"]') - self.page.ele('x://*[@id="size_0"]').input(size) + self.click_safe('x://button[normalize-space(text()) ="市价"]') + self.page.ele('x://*[@id="size_0"]').input(size, clear=True) self.click_safe('x://span[normalize-space(text()) ="买入/做多"]') - else: - self.click_safe('x://span[normalize-space(text()) ="市价"]') - - # if resp.get("code") == 1000: + logger.info(f"✅ 开多单: size={size}") return True - # self.ding(f"下单失败: {resp}", error=True) - return False + # 开空单 + elif side == 4: + self.click_safe('x://button[normalize-space(text()) ="市价"]') + self.click_safe('x://button[normalize-space(text()) ="市价"]') + self.click_safe('x://button[normalize-space(text()) ="市价"]') + self.page.ele('x://*[@id="size_0"]').input(size, clear=True) + self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]') + logger.info(f"✅ 开空单: size={size}") + return True - except APIException as e: - logger.error(f"API下单异常: {e}") - self.ding(f"API下单异常: {e}", error=True) - return False + # 平多单(平多 = 卖出) + elif side == 2: + self.click_safe('x://button[normalize-space(text()) ="市价"]') + time.sleep(0.3) # 等待界面响应 + # 平仓时size可以设置大一些确保全部平仓 + self.page.ele('x://*[@id="size_0"]').input(size, clear=True) + time.sleep(0.3) + self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]') + logger.info(f"✅ 平多单操作执行: size={size}") + return True + + # 平空单(平空 = 买入) + elif side == 3: + self.click_safe('x://button[normalize-space(text()) ="市价"]') + time.sleep(0.3) # 等待界面响应 + self.page.ele('x://*[@id="size_0"]').input(size, clear=True) + time.sleep(0.3) + self.click_safe('x://span[normalize-space(text()) ="买入/做多"]') + logger.info(f"✅ 平空单操作执行: size={size}") + return True + + else: + logger.error(f"未知的订单方向: side={side}") + return False except Exception as e: - logger.error(f"下单未知异常: {e}") - self.ding(f"下单未知异常: {e}", error=True) + logger.error(f"下单异常: {e}") + self.ding(f"下单异常: {e}", error=True) return False - def close_position_all(self): + def calculate_fee(self, position_value: float) -> float: + """ + 计算手续费 + 平台手续费 = 仓位价值 * 0.0005(万分之五) + 实际手续费 = 平台手续费 * (1 - 返佣比例) = 平台手续费 * 0.1 + """ + platform_fee = position_value * self.cfg.platform_fee_rate + actual_fee = platform_fee * (1 - self.cfg.rebate_rate) + return actual_fee + + def calculate_net_pnl(self, price: float) -> Tuple[float, float, float]: + """ + 计算扣除手续费后的净盈亏 + 返回: (净盈亏比例, 总手续费, 毛盈亏比例) + + 注意:在杠杆交易中,盈亏比例 = 杠杆倍数 * 价格变动比例 + 例如:30倍杠杆,价格涨1%,实际盈亏是30% + """ + if self.pos == 0 or self.entry_price is None or self.entry_margin is None or self.entry_position_value is None: + return 0.0, 0.0, 0.0 + + leverage = int(self.cfg.leverage) + + # 计算价格变动比例 if self.pos == 1: - ok = self.place_market_order(3, 999999) - if ok: - self.pos = 0 - elif self.pos == -1: - ok = self.place_market_order(2, 999999) - if ok: + price_change_ratio = (price - self.entry_price) / self.entry_price + else: + price_change_ratio = (self.entry_price - price) / self.entry_price + + # 计算毛盈亏比例(考虑杠杆) + gross_pnl_ratio = leverage * price_change_ratio + + # 计算开仓手续费 + entry_fee = self.calculate_fee(self.entry_position_value) + + # 计算平仓手续费(使用当前价格计算平仓时的仓位价值) + # 平仓时的仓位价值 ≈ 开仓时的仓位价值(假设size不变) + exit_position_value = self.entry_position_value + exit_fee = self.calculate_fee(exit_position_value) + + # 总手续费 + total_fee = entry_fee + exit_fee + + # 手续费相对于保证金的比率 + fee_ratio = total_fee / self.entry_margin + + # 净盈亏 = 毛盈亏 - 手续费比率 + net_pnl_ratio = gross_pnl_ratio - fee_ratio + + return net_pnl_ratio, total_fee, gross_pnl_ratio + + def close_position_all(self) -> bool: + """ + 平掉所有持仓 + 重试机制:最多尝试3次,每次平仓后通过SDK验证是否成功 + 返回: True=平仓成功, False=平仓失败 + """ + if self.pos == 0: + logger.info("当前无持仓,无需平仓") + return True + + max_retries = 3 + retry_delay = 1.0 # 每次重试间隔1秒 + + for attempt in range(1, max_retries + 1): + logger.info(f"平仓尝试 {attempt}/{max_retries}...") + + # 记录平仓前的持仓状态 + old_pos = self.pos + + # 执行平仓操作 + if self.pos == 1: + # 平多单,使用side=2 + ok = self.place_market_order(2, 999999) + if not ok: + logger.warning(f"平多单操作失败 (尝试 {attempt}/{max_retries})") + if attempt < max_retries: + time.sleep(retry_delay) + continue + else: + self.ding(f"平多单失败:已重试{max_retries}次仍失败", error=True) + return False + elif self.pos == -1: + # 平空单,使用side=3 + ok = self.place_market_order(3, 999999) + if not ok: + logger.warning(f"平空单操作失败 (尝试 {attempt}/{max_retries})") + if attempt < max_retries: + time.sleep(retry_delay) + continue + else: + self.ding(f"平空单失败:已重试{max_retries}次仍失败", error=True) + return False + else: + logger.info("持仓状态异常,无需平仓") + return True + + # 等待订单执行 + time.sleep(1.5) # 等待订单执行 + + # 通过SDK验证是否平仓成功 + verify_success = self._verify_position_closed(old_pos) + + if verify_success: + # 平仓成功,清空状态 self.pos = 0 + self.entry_margin = None + self.entry_position_value = None + logger.success(f"✅ 平仓成功 (尝试 {attempt}/{max_retries})") + return True + else: + # 平仓失败,准备重试 + logger.warning(f"平仓验证失败,持仓仍存在 (尝试 {attempt}/{max_retries})") + if attempt < max_retries: + time.sleep(retry_delay) + # 重新获取持仓状态 + self.get_position_status() + else: + self.ding(f"平仓失败:已重试{max_retries}次,持仓仍未平掉", error=True) + return False + + return False + + def _verify_position_closed(self, expected_old_pos: int) -> bool: + """ + 验证持仓是否已平仓 + 通过SDK查询持仓状态,确认是否真的平仓成功 + 返回: True=平仓成功, False=仍有持仓 + """ + try: + # 调用SDK查询持仓状态 + resp = self.contractAPI.get_position(contract_symbol=self.cfg.contract_symbol)[0] + + if resp.get("code") != 1000: + logger.warning(f"查询持仓状态失败: {resp}") + return False + + positions = resp.get("data", []) + + # 如果没有持仓,说明平仓成功 + if not positions or len(positions) == 0: + logger.info("✅ SDK验证:持仓已平仓") + return True + + # 检查持仓是否还存在 + # position_type: 1=多, 2=空 (根据get_position_status的逻辑) + for p in positions: + position_type = p.get("position_type", 0) + # 根据get_position_status的逻辑:1=多(pos=1), 其他=空(pos=-1) + current_pos = 1 if position_type == 1 else -1 + + if current_pos == expected_old_pos: + # 持仓仍然存在(与平仓前的方向一致) + logger.warning( + f"⚠️ SDK验证:持仓仍存在 (position_type={position_type}, 方向={'多' if current_pos == 1 else '空'})") + return False + + # 持仓已改变或不存在(平仓成功) + logger.info("✅ SDK验证:持仓已平仓或已改变") + return True + + except Exception as e: + logger.error(f"验证持仓状态异常: {e}") + # 验证失败时,保守处理:认为可能未平仓 + return False # ----------------- 止损后机制 ----------------- def _reentry_penalty_active(self, dev: float, entry_dev: float) -> bool: @@ -674,7 +880,11 @@ class BitmartFuturesMeanReversionBot: self.pos = 1 self.entry_price = price self.entry_ts = time.time() - self.ding(f"✅开多:dev={dev * 100:.3f}% size={size} entry={price:.2f}") + # 记录开仓信息(用于手续费计算) + self.entry_margin = self.cfg.fixed_margin + self.entry_position_value = self.entry_margin * int(self.cfg.leverage) + self.ding( + f"✅开多:dev={dev * 100:.3f}% size={size} entry={price:.2f} margin={self.entry_margin}u value={self.entry_position_value}u") # ========== 【开单位置2】开空单 ========== # 方向:空(做空,卖出) @@ -684,56 +894,130 @@ class BitmartFuturesMeanReversionBot: self.pos = -1 self.entry_price = price self.entry_ts = time.time() - self.ding(f"✅开空:dev={dev * 100:.3f}% size={size} entry={price:.2f}") + # 记录开仓信息(用于手续费计算) + self.entry_margin = self.cfg.fixed_margin + self.entry_position_value = self.entry_margin * int(self.cfg.leverage) + self.ding( + f"✅开空:dev={dev * 100:.3f}% size={size} entry={price:.2f} margin={self.entry_margin}u value={self.entry_position_value}u") - def maybe_exit(self, price: float, tp: float, sl: float, vol_scale: float): - """检查并执行出场""" + def maybe_exit(self, price: float, ema_value: float, tp: float, sl: float, vol_scale: float): + """ + 检查并执行出场 + 【平仓函数】包含四种平仓条件,所有平仓都会考虑手续费: + 1. 正常平仓:价格回归到EMA附近时平仓(扣除手续费后仍有盈利) + 2. 止盈:达到止盈阈值(扣除手续费后仍有盈利) + 3. 止损:达到止损阈值 + 4. 超时平仓:持仓时间超过限制(扣除手续费后仍有盈利) + """ if self.pos == 0 or self.entry_price is None or self.entry_ts is None: return hold = time.time() - self.entry_ts - if self.pos == 1: - pnl = (price - self.entry_price) / self.entry_price - else: - pnl = (self.entry_price - price) / self.entry_price + # 计算毛盈亏和净盈亏(扣除手续费后) + # calculate_net_pnl已经考虑了杠杆倍数 + net_pnl, total_fee, gross_pnl = self.calculate_net_pnl(price) + # 计算价格偏离EMA的程度 + dev = (price - ema_value) / ema_value if ema_value else 0.0 + + # 计算止损倍数(考虑止损后放宽) sl_mult = 1.0 if self.post_sl_dir == self.pos and self.post_sl_dir != 0: sl_mult = self._post_sl_dynamic_mult() effective_sl = sl * sl_mult - if pnl >= tp: - self.close_position_all() - self.ding(f"🎯止盈:pnl={pnl * 100:.3f}% price={price:.2f} tp={tp * 100:.3f}%") - self.entry_price, self.entry_ts = None, None - self.last_exit_ts = time.time() + # ========== 【平仓条件1】正常平仓:价格回归到EMA附近 ========== + # 这是均值回归策略的核心:当价格回归到EMA附近时,说明均值回归已经完成,应该平仓 + # 但必须扣除手续费后仍有盈利才平仓 + if abs(dev) <= self.cfg.normal_exit_threshold: + if net_pnl > 0: # 扣除手续费后仍有盈利 + if self.close_position_all(): + self.ding( + f"📊正常平仓:价格回归EMA附近 dev={dev * 100:.3f}% " + f"毛盈亏={gross_pnl * 100:.3f}% 净盈亏={net_pnl * 100:.3f}% " + f"手续费={total_fee:.4f}u price={price:.2f}" + ) + self.entry_price, self.entry_ts = None, None + self.entry_margin, self.entry_position_value = None, None + self.last_exit_ts = time.time() + return + else: + logger.error("正常平仓失败,持仓可能仍存在") + # 平仓失败时不更新状态,下次循环会继续尝试 + return - elif pnl <= -effective_sl: + # ========== 【平仓条件2】止盈 ========== + # 达到止盈阈值,且扣除手续费后仍有盈利 + if gross_pnl >= tp: + if net_pnl > 0: # 扣除手续费后仍有盈利 + if self.close_position_all(): + self.ding( + f"🎯止盈:毛盈亏={gross_pnl * 100:.3f}% 净盈亏={net_pnl * 100:.3f}% " + f"手续费={total_fee:.4f}u price={price:.2f} tp={tp * 100:.3f}%" + ) + self.entry_price, self.entry_ts = None, None + self.entry_margin, self.entry_position_value = None, None + self.last_exit_ts = time.time() + return + else: + logger.error("止盈平仓失败,持仓可能仍存在") + return + + # ========== 【平仓条件3】止损 ========== + # 止损使用毛盈亏判断(因为止损是风险控制,即使扣除手续费后亏损也要止损) + if gross_pnl <= -effective_sl: sl_dir = self.pos - self.close_position_all() - self.ding( - f"🛑止损:pnl={pnl * 100:.3f}% price={price:.2f} " - f"sl={sl * 100:.3f}% effective_sl={effective_sl * 100:.3f}%(×{sl_mult:.2f})", - error=True - ) + if self.close_position_all(): + self.ding( + f"🛑止损:毛盈亏={gross_pnl * 100:.3f}% 净盈亏={net_pnl * 100:.3f}% " + f"手续费={total_fee:.4f}u price={price:.2f} " + f"sl={sl * 100:.3f}% effective_sl={effective_sl * 100:.3f}%(×{sl_mult:.2f})", + error=True + ) - self.last_sl_dir = sl_dir - self.last_sl_ts = time.time() + self.last_sl_dir = sl_dir + self.last_sl_ts = time.time() - self.post_sl_dir = sl_dir - self.post_sl_ts = time.time() - self.post_sl_vol_scale = float(vol_scale) + self.post_sl_dir = sl_dir + self.post_sl_ts = time.time() + self.post_sl_vol_scale = float(vol_scale) - self.entry_price, self.entry_ts = None, None - self.last_exit_ts = time.time() + self.entry_price, self.entry_ts = None, None + self.entry_margin, self.entry_position_value = None, None + self.last_exit_ts = time.time() + return + else: + logger.error("止损平仓失败,持仓可能仍存在,风险较高!") + self.ding("⚠️ 止损平仓失败,请手动检查持仓!", error=True) + # 即使平仓失败,也更新止损状态,避免重复触发 + self.last_sl_dir = sl_dir + self.last_sl_ts = time.time() + return - elif hold >= self.cfg.max_hold_sec: - self.close_position_all() - self.ding(f"⏱超时:hold={int(hold)}s pnl={pnl * 100:.3f}% price={price:.2f}") - self.entry_price, self.entry_ts = None, None - self.last_exit_ts = time.time() + # ========== 【平仓条件4】超时平仓 ========== + # 超时平仓也要考虑手续费,只有扣除手续费后仍有盈利才平仓 + if hold >= self.cfg.max_hold_sec: + if net_pnl > 0: # 扣除手续费后仍有盈利 + if self.close_position_all(): + self.ding( + f"⏱超时:hold={int(hold)}s 毛盈亏={gross_pnl * 100:.3f}% " + f"净盈亏={net_pnl * 100:.3f}% 手续费={total_fee:.4f}u price={price:.2f}" + ) + self.entry_price, self.entry_ts = None, None + self.entry_margin, self.entry_position_value = None, None + self.last_exit_ts = time.time() + return + else: + logger.error("超时平仓失败,持仓可能仍存在") + return + else: + # 超时但扣除手续费后亏损,继续持有等待盈利 + logger.debug( + f"⏱超时但净盈亏为负,继续持有:hold={int(hold)}s " + f"毛盈亏={gross_pnl * 100:.3f}% 净盈亏={net_pnl * 100:.3f}%" + ) def notify_status_throttled(self, price: float, ema_value: float, dev: float, bal: float, atr_ratio: float, base_ratio: float, vol_scale: float, @@ -754,6 +1038,13 @@ class BitmartFuturesMeanReversionBot: base_age = int(now - self._base_ratio_ts) if self._base_ratio_ts else -1 + # 计算当前盈亏(如果有持仓) + current_gross_pnl = 0.0 + current_net_pnl = 0.0 + current_fee = 0.0 + if self.pos != 0 and self.entry_price: + current_net_pnl, current_fee, current_gross_pnl = self.calculate_net_pnl(price) + msg = ( f"【BitMart {self.cfg.contract_symbol}|1m均值回归(动态阈值)】\n" f"📊 状态:{direction_str}\n" @@ -762,11 +1053,23 @@ class BitmartFuturesMeanReversionBot: f"🌊 波动率:ATR比={atr_ratio * 100:.3f}% | 基准={base_ratio * 100:.3f}% | 缩放={vol_scale:.2f}\n" f"🎯 动态Floor:入场={entry_floor * 100:.3f}% | 止盈={tp_floor * 100:.3f}% | 止损={sl_floor * 100:.3f}%\n" f"💰 止盈/止损:{tp * 100:.3f}% / {sl * 100:.3f}% (盈亏比:{tp / sl:.2f})\n" + f"📊 正常平仓阈值:±{self.cfg.normal_exit_threshold * 100:.3f}%\n" f"🔄 基准刷新:{self.cfg.base_ratio_refresh_sec}s (已过={base_age}s)\n" f"⚠️ 止损同向加门槛:{'开启' if penalty_active else '关闭'} (方向={self.last_sl_dir})\n" - f"💳 可用余额:{bal:.2f} USDT | 杠杆:{self.cfg.leverage}x\n" - f"⏱️ 持仓限制:{self.cfg.max_hold_sec}s | 冷却:{self.cfg.cooldown_sec_after_exit}s" + f"💳 可用余额:{bal:.2f} USDT | 杠杆:{self.cfg.leverage}x | 固定保证金:{self.cfg.fixed_margin}u\n" ) + + if self.pos != 0 and self.entry_price is not None and self.entry_position_value is not None: + msg += ( + f"📈 当前盈亏:毛盈亏={current_gross_pnl * 100:.3f}% | " + f"净盈亏={current_net_pnl * 100:.3f}% | " + f"手续费={current_fee:.4f}u\n" + f"📍 入场价:{self.entry_price:.2f} | " + f"仓位价值:{self.entry_position_value:.2f}u\n" + ) + + msg += f"⏱️ 持仓限制:{self.cfg.max_hold_sec}s | 冷却:{self.cfg.cooldown_sec_after_exit}s" + self.ding(msg) def openBrowser(self): @@ -914,7 +1217,11 @@ class BitmartFuturesMeanReversionBot: # 7. 检查交易是否启用 if not self.trading_enabled: if self.pos != 0: - self.close_position_all() + if self.close_position_all(): + logger.info("交易被禁用,已平仓") + else: + logger.error("交易被禁用,但平仓失败,请手动检查!") + self.ding("⚠️ 交易被禁用但平仓失败,请手动检查持仓!", error=True) logger.warning("交易被禁用(风控触发),等待...") time.sleep(5) continue @@ -922,12 +1229,14 @@ class BitmartFuturesMeanReversionBot: # 8. 检查危险市场 if self.is_danger_market(klines, price): logger.warning("危险模式:高波动/大实体K,暂停开仓") - self.maybe_exit(price, tp, sl, vol_scale) + self.maybe_exit(price, ema_value, tp, sl, vol_scale) time.sleep(self.cfg.tick_refresh_sec) continue # 9. 执行交易逻辑 - self.maybe_exit(price, tp, sl, vol_scale) + # 先检查平仓(包括正常平仓、止盈、止损) + self.maybe_exit(price, ema_value, tp, sl, vol_scale) + # 再检查开仓 self.maybe_enter(price, ema_value, entry_dev) # 10. 状态通知 @@ -971,3 +1280,4 @@ if __name__ == "__main__": logger.error(f"程序异常退出: {e}") bot.ding(f"❌ 策略异常退出: {e}", error=True) raise + diff --git a/telegram/8619211027341.session b/telegram/8619211027341.session index 3a4596ca5f3e4c9fe540d3996dcba325c0d1e78c..66ec9d3ea7890e7d1549de6999d08a6e19f06feb 100644 GIT binary patch delta 89 zcmZp8z|`=7X@V4!@wJIEPC#;FLSL=|tBB*R;DuYdCwU9>bZp)dz{bGD!Oo%d7AU&( syUu(5$$WXrOw(^}HqJZ2!ZiK%=D$Ug7@4Nu**vjC$r{L?ymh@d06ASHP5=M^ delta 89 zcmZp8z|`=7X@V4!e*Z)nCm^{op)Xf~h0pO;pwR-Yu3blyBKPB{n4AQt3Ej2Kj-_szH_Wr!fGWvbMUI@#=ume zU)csLxF@c|c^HN<$6H6bHC-u(qUZRjpXGG8M^^Vu$7l?ZB(8{ReA!^swl9ZP^SZ#0 zkN@ul7$=;+#o@29ETBr;k%qrenvA0&*dMO|x;zD{ZNYqTc`r?9&r*C?^+ggtmjp4Y zSRgFdac>v&TT(1%sVS-gY#fu1$-Cq(d9hqA_m@q|dSuPA-Ledsg8Rum=6X5c%v?IB zkp7aslwOlINDHOOQa{P0N@3ZGv2dih(*f8;L@iQ?mc8ks8EO9XN zof%+`F{Mm46UVTk5z$qVOH?9C6Q$3hKhf9e7J4V0N{3Njs7F*MRZHbliz$*EBYQ{> zxrfXkLx~?m9D(o`co$xS=i|xPI39rBp+V$9B`6IAp+h+P_ZYK?{UuBvFQYDPqJonu%n z=mcX5GW1~1@CW0nEv8D-zLJtSG*B4ChbR zYY?>9;vsA4e3<6$BeWZ6{-HY#@vrUQ5FB)v2yKE3CLLNSha}iGY!nmTcp)&J8~k0* zd?8-W9sWz>F&f|U9O{}w&>`q-wt&i+stobII(1p^laC@RH=h1on5f^p&S=u>gq3gC imF?1*^iw|gChwpeKl6z%=qQv=ebh*=l7HVbQ}hRw7b;W$ delta 680 zcmXw0Ye-XZ9R2_AYumlMTlpc{keQk~XAieXZR(nf%yeo#B0;G%N-RusSY+BZow~Gg z^XD^_9w^Zh+BKOP^+7Z%j3g=yqd+hSs}HRY%YrUM2hRC&IOoGT4hQLQkRCO0#^epP zi2C3#?Btgw-&hbr(w@%MFXw#brmbp}_P(I?T4lw$wTMBOqKL3K?}z4!98De!Rarwf z%>DPp7%%c4cw!0d1G;(`7YetlQwZ#W`h+lGZ02yiAu%btwB|^yts<-8PE5}Y|74~F zF)9>Gk!IcC9=}j-n_$S~(K%sb-Cc^%I3Ye&52SsZ(B#8oyVMeM|7*ofJPfv%P3$HV~;ilZ(!!0{G6g=>)pOoM9BIHTcrQw&WlB6=0lZQ&4X zO90%Y2iC2HNS78`8{;6*91k96f{<}3h$(UZ9N2Ls6Hd9pVA{PM(mGC(*5Fa_b>zV2 zeoLoQ1G}7Bm~ozl;>JzjYs`h7q-T)jiWWRB6DF-IPhh?|5;nG^!$8YfGHav|#!|F! zqV*~m+yKAa2jHVIN)RrjW5`R4g`)OJG4u)68={4ji*hXGF^3MKw+Q5(N0dw_{UgkJ zV#VTMsSxmXGla?r?R^pWE8Oq1!;C+Qw3NMuZ+?r=+drceUE$EJGNpL7Fy&8``~?p1 B<`e(` diff --git a/telegram/bot_session.session-journal b/telegram/bot_session.session-journal deleted file mode 100644 index ae05151e8e30b2f8070951bba296eea31b28ab7e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8720 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 F`#;ZzvA+NS diff --git a/telegram/sign.db b/telegram/sign.db index 72bb787f0f21c60d2828edbbafb9349e5dbc0cc2..f5dc4f4a1d275480e71dcc017aef37dbbcc8bc87 100644 GIT binary patch delta 411 zcmZoTz|?SnX@WH4w23m#jMFwINQ!gI85tOv>KYp98ktRQ5l`ecLhv_#5TDJ6A}~!d zhmG|IgD{`VWOfA==F@yGn+*-b_{2n%SXdP~85o<+`OHJ;pB$xEC?=u^kxH2|;R8Zy z^FuvlMlE4JUk3iEe7pIQ`6~He^Q-gQ@~`C212<-5qgpYJc_58v7SAo{Z z^XZ5JjbcQ%MF}d3YJuYBO1m~k#>!0%ECS3%EKer0Pl#t@^D#Jn28{lV3=rRWo%$&XrX6fYGPq-3e{3JdE*?(%`>KEZ~*`{+iDE} delta 240 zcmZoTz|?SnX@WFk-$WT_#=eaSlH%MVMg~Tvx`sx&My8Wn#1pwS;C!RaAH-)fA_NRA zHcykxVPiebAjlUrnO#AJF?O?|ffS#Js00hEA|nH1%9IHoV7ezq=@p9c{a_GNin`7`T