From ce70991051e16a15088ff86215f71140c7b00171 Mon Sep 17 00:00:00 2001 From: 27942 Date: Fri, 12 Dec 2025 17:14:29 +0800 Subject: [PATCH] rgfewfger --- test2.py | 146 +++++++++++++++++- 交易/weex_交易.py | 4 +- 交易/weex_优化版.py | 361 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 505 insertions(+), 6 deletions(-) create mode 100644 交易/weex_优化版.py diff --git a/test2.py b/test2.py index 92de29f..ce1218c 100644 --- a/test2.py +++ b/test2.py @@ -1,6 +1,144 @@ import time +import random -timestamp = time.time() -local_time = time.localtime(timestamp) -formatted_time = time.strftime("%Y-%m-%d %H:%M:%S", local_time) -print(formatted_time) +import datetime +from loguru import * +from curl_cffi import requests + +from models.combined_table import CombinedTable +from models.concrete_wallet import ConcreteWallet +from models.ips import Ips +from tools import get_evm_wallet_singed_message + + +class Credentials: + def __init__(self, sql_info): + + self.sql_info = sql_info + + self.headers = { + 'accept': '*/*', + 'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6', + 'cache-control': 'no-cache', + 'content-type': 'application/json', + 'pragma': 'no-cache', + 'priority': 'u=1, i', + 'referer': 'https://points.concrete.xyz/home', + 'sec-ch-ua': '"Microsoft Edge";v="143", "Chromium";v="143", "Not A(Brand";v="24"', + 'sec-ch-ua-mobile': '?0', + 'sec-ch-ua-platform': '"Windows"', + 'sec-fetch-dest': 'empty', + 'sec-fetch-mode': 'cors', + 'sec-fetch-site': 'same-origin', + 'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0', + # 'cookie': 'client-season=z2zi-tzc2; domain=https%3A%2F%2Fpoints.concrete.xyz; __Host-authjs.csrf-token=afc782765e3380ca973101d02f54c9516d317120fe83faea733390bfc2e46cb4%7Cdca5d6028cadbe52f05820005ed29862e6bd8fff72ae5514142c25bf0d0ad97f; __Secure-authjs.callback-url=https%3A%2F%2Fboost.absinthe.network; redirect-pathname=%2Fhome', + } + + self.session = requests.Session() + self.session.headers.update(self.headers) + + def csrf(self): + for i in range(3): + try: + response = self.session.get('https://points.concrete.xyz/api/auth/csrf', ) + + return response.json()["csrfToken"] + except: + time.sleep(random.randint(1, 5)) + + return False + + def credentials(self, message, signature, csrfToken): + params = '' + + data = { + 'message': message, + 'redirect': 'false', + 'signature': "0x" + signature, + 'csrfToken': csrfToken, + 'callbackUrl': 'https://points.concrete.xyz/home', + } + + for i in range(3): + try: + response = self.session.post( + 'https://points.concrete.xyz/api/auth/callback/credentials', + params=params, + data=data, + ) + print(response.json()) + + return True + except: + time.sleep(random.randint(1, 5)) + + return False + + def add_ips_to_combined_table(self, ): + # 获取 ips 表中的所有记录 + all_ips_records = Ips.select() + for ips_record in all_ips_records: + # 检查该记录是否已经存在于 CombinedTable 中 + existing_record = CombinedTable.get_or_none( + CombinedTable.host == ips_record.host, + CombinedTable.port == ips_record.port, + CombinedTable.username == ips_record.username, + CombinedTable.pwd == ips_record.pwd + ) + if not existing_record: + # 如果记录不存在,则将其添加到 CombinedTable 中 + # 这里假设 mnemonic 字段有默认值或者你可以根据需求传入具体值 + + self.com_info.host = ips_record.host + self.com_info.port = ips_record.port + self.com_info.username = ips_record.username + self.com_info.pwd = ips_record.pwd + self.com_info.save() + + print("成功添加记录到 CombinedTable。") + return True + + print("ips 表中所有数据在 CombinedTable 中都已存在,结束操作。") + return False + + def action(self): + + self.com_info, type1 = CombinedTable.get_or_create( + mnemonic=self.sql_info.mnemonic, + ) + + if not self.com_info.host: + if self.add_ips_to_combined_table(): + logger.info("添加ip成功!!!") + else: + logger.error("没有ip了") + return + + proxies = { + 'http': f'socks5://{self.com_info.username}:{self.com_info.pwd}@{self.com_info.host}:{self.com_info.port}', + 'https': f'socks5://{self.com_info.username}:{self.com_info.pwd}@{self.com_info.host}:{self.com_info.port}', + } + self.session.proxies.update(proxies) + + csrf_token = self.csrf() + if csrf_token: + logger.info("获取签名信息成功!!!") + else: + logger.error("获取签名信息失败!!!") + + # 获取当前 UTC 时间 + current_utc_time = datetime.datetime.now(datetime.timezone.utc) + original_msg = f"points.concrete.xyz wants you to sign in with your Ethereum account:\n{self.sql_info.address}\n\nPlease sign with your account\n\nURI: https://points.concrete.xyz\nVersion: 1\nChain ID: 1\nNonce: {csrf_token}\nIssued At: {current_utc_time.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + 'Z'}\nResources:\n- connector://metaMask" + signature = get_evm_wallet_singed_message( + original_msg=original_msg, + private_key=self.sql_info.private_key, + + ) + + self.credentials(message=original_msg, signature=signature, csrfToken=csrf_token) + + +if __name__ == '__main__': + for concrete_info in ConcreteWallet.select(): + credentials = Credentials(sql_info=concrete_info) + credentials.action() diff --git a/交易/weex_交易.py b/交易/weex_交易.py index 1b0558b..776f6e1 100644 --- a/交易/weex_交易.py +++ b/交易/weex_交易.py @@ -349,8 +349,8 @@ class WeexTransaction: self.pbar.n = current_minute - 30 self.pbar.refresh() - # if current_minute not in [0, 1, 2, 3, 4, 5, 30, 31, 32, 33, 34, 35]: # 判断是否是 新的30分钟了 - if current_minute not in range(60): # 判断是否是 新的30分钟了 + if current_minute not in [0, 1, 2, 3, 4, 5, 30, 31, 32, 33, 34, 35]: # 判断是否是 新的30分钟了 + # if current_minute not in range(60): # 判断是否是 新的30分钟了 time.sleep(10) continue diff --git a/交易/weex_优化版.py b/交易/weex_优化版.py new file mode 100644 index 0000000..79fdeb2 --- /dev/null +++ b/交易/weex_优化版.py @@ -0,0 +1,361 @@ +import time +import datetime +import asyncio +from tqdm import tqdm +from loguru import logger +from DrissionPage import ChromiumOptions, ChromiumPage +from curl_cffi import requests + +from 交易.tools import send_dingtalk_message + + +# ---------------------------------------------- +# 工具函数 +# ---------------------------------------------- +def is_bullish(candle): # 阳线 + return float(candle["close"]) > float(candle["open"]) + + +def is_bearish(candle): # 阴线 + return float(candle["close"]) < float(candle["open"]) + + +def now_str(): + return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") + + +# ---------------------------------------------- +# 主类 +# ---------------------------------------------- +class WeexTransaction: + def __init__(self, tge_id): + # ===================== 基础配置 ===================== + self.tge_id = tge_id + self.tge_port = None + self.tge_url = "http://127.0.0.1:50326" + + self.tge_headers = { + "Authorization": "Bearer asp_174003986c9b0799677c5b2c1adb76e402735d753bc91a91", + "Content-Type": "application/json" + } + + # DingTalk + self.webhook_url = "..." + self.secret = "..." + + # 浏览器 + self.page: ChromiumPage = None + + # 当前仓位状态 -1空仓,0无仓位,1多仓 + self.start = 0 + + # 保存 k 线 + self.kline_1 = None + self.kline_2 = None + self.kline_3 = None + + # 当前信号方向 + self.direction = None + + # 进度条 + self.pbar = None + + # HTTP session + self.session = requests.Session() + self.session.headers = {} + + # 避免同一分钟重复触发 + self.last_action_time = None + + # ------------------------------------------------------- + # DingTalk 通知 + # ------------------------------------------------------- + def send_msg(self, msg, err=False): + prefix = "❌" if err else "🔔" + send_dingtalk_message(f"{prefix}weex:{now_str()},{msg}") + + # ------------------------------------------------------- + # BitBrowser 控制 + # ------------------------------------------------------- + def open_browser(self): + try: + resp = requests.post( + f"{self.tge_url}/api/browser/start", + json={"envId": self.tge_id}, + headers=self.tge_headers + ) + + self.tge_port = resp.json()["data"]["port"] + return True + except Exception as e: + logger.error(f"打开 TGE 浏览器失败:{e}") + return False + + def takeover_browser(self): + try: + co = ChromiumOptions() + co.set_local_port(self.tge_port) + + self.page = ChromiumPage(addr_or_opts=co) + self.page.set.window.max() + return True + except Exception as e: + logger.error(f"接管浏览器失败:{e}") + return False + + # ------------------------------------------------------- + # 数据接口 + # ------------------------------------------------------- + def get_price(self): + params = { + 'unit': '30', + 'resolution': 'M', + 'contractID': '2', + 'offset': '340', + 'endTime': str(int(time.time())), + } + + for _ in range(3): + try: + resp = self.session.get( + 'https://contract-v2.bitmart.com/v1/ifcontract/quote/kline', + params=params, + timeout=5 + ) + result = [] + for i in resp.json()["data"]: + result.append({ + 'id': int(i["timestamp"]) - 1, + 'open': float(i["open"]), + 'high': float(i["high"]), + 'low': float(i["low"]), + 'close': float(i["close"]), + }) + return sorted(result, key=lambda x: x["id"]) + except: + time.sleep(1) + + return None + + # ------------------------------------------------------- + # Token 获取 + # ------------------------------------------------------- + def get_token(self): + tab = self.page.new_tab() + tab.listen.start("/user/security/getLanguageType") + + for _ in range(3): + try: + tab.get("https://www.weeaxs.site/zh-CN/futures/ETH-USDT") + res = tab.listen.wait(timeout=5) + + token = res.request.headers.get("U-TOKEN") + if token: + self.session.headers["U-TOKEN"] = token + tab.close() + return True + except: + time.sleep(1) + + tab.close() + return False + + # ------------------------------------------------------- + # 获取账户余额 + # ------------------------------------------------------- + def get_balance(self): + for _ in range(3): + try: + resp = self.session.post( + "https://gateway2.ngsvsfx.cn/v1/gw/assetsWithBalance/new" + ) + return resp.json()["data"]["newContract"]["balanceList"][0]["accountRights"] + except: + time.sleep(1) + return None + + # ------------------------------------------------------- + # 查询仓位状态 + # ------------------------------------------------------- + def get_position(self): + payload = { + 'filterContractIdList': [10000002], + 'limit': 100, + 'languageType': 0, + 'sign': 'SIGN', + 'timeZone': 'string', + } + + for _ in range(3): + try: + resp = self.session.post( + "https://http-gateway2.ngsvsfx.cn/api/v1/private/order/v2/getHistoryOrderFillTransactionPage", + json=payload, + ) + lst = resp.json()["data"]["dataList"] + last = lst[0]["legacyOrderDirection"] + + if last == "OPEN_LONG": + self.start = 1 + elif last == "OPEN_SHORT": + self.start = -1 + else: + self.start = 0 + + return True + except: + time.sleep(1) + + return False + + # ------------------------------------------------------- + # 信号判断:包住形态 + # ------------------------------------------------------- + def check_signal(self, prev, curr): + p_open, p_close = prev["open"], prev["close"] + c_open, c_close = curr["open"], curr["close"] + + # 前跌后涨包住 → 多 + if is_bearish(prev) and is_bullish(curr) and c_open <= p_close and c_close >= p_open: + return "long" + + # 前涨后跌包住 → 空 + if is_bullish(prev) and is_bearish(curr) and c_open >= p_close and c_close <= p_open: + return "short" + + return None + + # ------------------------------------------------------- + # 下单逻辑 + # ------------------------------------------------------- + def place_order(self): + # 刷新余额 + balance = self.get_balance() + if not balance: + self.send_msg("获取余额失败", err=True) + return + + amount = float(balance) / 100 + self.page.ele('x://input[@placeholder="请输入数量"]').input(amount) + time.sleep(1) + + # 开仓和平仓统一逻辑 + def click(btn_text): + self.page.ele(f'x://*[contains(text(), "{btn_text}")]').click() + + if self.direction == "long": + if self.start == 0: + click("买入开多") + self.send_msg(f"开多:{amount}") + self.start = 1 + elif self.start == -1: + click("闪电平仓") + time.sleep(2) + click("买入开多") + self.send_msg(f"反手做多:{amount}") + self.start = 1 + + elif self.direction == "short": + if self.start == 0: + click("卖出开空") + self.send_msg(f"开空:{amount}") + self.start = -1 + elif self.start == 1: + click("闪电平仓") + time.sleep(2) + click("卖出开空") + self.send_msg(f"反手做空:{amount}") + self.start = -1 + + # ------------------------------------------------------- + # 主执行循环 + # ------------------------------------------------------- + def action(self): + # 1. 打开浏览器 + if not self.open_browser(): + logger.error("无法打开比特浏览器") + return + + # 2. 接管浏览器 + if not self.takeover_browser(): + logger.error("接管浏览器失败") + return + + # 3. 打开交易页 + self.page.get("https://www.weeaxs.site/zh-CN/futures/ETH-USDT") + + self.pbar = tqdm(total=30, desc="等待中", ncols=80) + + while True: + minute = datetime.datetime.now().minute + + # 更新进度条 + self.pbar.n = minute if minute < 30 else minute - 30 + self.pbar.refresh() + + # 非关键时间跳过 + if minute not in {0, 1, 2, 3, 4, 5, 30, 31, 32, 33, 34, 35}: + time.sleep(10) + continue + + # 避免同一分钟重复操作 + if self.kline_3 or self.last_action_time == self.kline_3["id"]: + continue + + # 获取新 token + if not self.get_token(): + self.send_msg("获取 token 失败", err=True) + continue + + # 获取 K 线 + prices = self.get_price() + if not prices: + self.send_msg("获取价格失败", err=True) + continue + + self.kline_1, self.kline_2, self.kline_3 = prices[-3:] + + self.last_action_time = self.kline_3["id"] + + # 获取仓位状态 + if not self.get_position(): + self.send_msg("获取仓位状态失败", err=True) + continue + + # 止损逻辑 + try: + if self.start == 1 and is_bearish(self.kline_1) and is_bearish(self.kline_2): + self.send_msg("平多") + self.page.ele('x://*[contains(text(), "闪电平仓")]').click() + self.start = 0 + + if self.start == -1 and is_bullish(self.kline_1) and is_bullish(self.kline_2): + self.send_msg("平空") + self.page.ele('x://*[contains(text(), "闪电平仓")]').click() + self.start = 0 + except: + self.send_msg("止损出错", err=True) + continue + + # 判断信号 + self.direction = self.check_signal(self.kline_1, self.kline_2) + + # 执行下单 + if self.direction: + try: + self.place_order() + except Exception as e: + self.send_msg(f"下单失败:{e}", err=True) + + # 推送当前状态 + self.send_msg(f"持仓:{self.start},信号:{self.direction or '无'}") + + # 重置进度条 + self.pbar.reset() + + +# ---------------------------------------------- +# 程序主入口 +# ---------------------------------------------- +if __name__ == '__main__': + WeexTransaction(tge_id=146473).action()