From cb10966b190ca3fe0728d21affd48c21e7f1d90d Mon Sep 17 00:00:00 2001 From: Administrator Date: Mon, 15 Dec 2025 16:56:35 +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 --- telegram/8619211027341.session | Bin 45056 -> 45056 bytes telegram/bot_session.session | Bin 36864 -> 40960 bytes telegram/bot_session.session-journal | Bin 12824 -> 8720 bytes 交易/bitmart_优化版.py | 154 ++++++------ 交易/weex_交易.py | 17 +- 交易/weex_优化版.py | 357 --------------------------- 6 files changed, 85 insertions(+), 443 deletions(-) delete mode 100644 交易/weex_优化版.py diff --git a/telegram/8619211027341.session b/telegram/8619211027341.session index c008fb34997296d6dce686023033b17ff3f25699..4d4e5366cc7bcc8d94e360ec2990b671d6268189 100644 GIT binary patch delta 64 zcmZp8z|`=7X@WGP`9v9KM)QpcOLI9@9B=u54ybl*nar1`%yia%vvJ-D7N)ZfoBtL~ UVq`k&xOrlUk~NS&dFy&_03UQ2s{jB1 delta 64 zcmV-G0Kfl$-~xc)0+1U44v`!~0S>WXrELiiLE2C8ckm)llMZep0>$04Gj7NP0>$35 W{c@540>$66k#r(E0>$2wwXaJ(XB*!D diff --git a/telegram/bot_session.session b/telegram/bot_session.session index 33d0125ed0dee04512f74a4443c866210117d1e6..9fe0992398fc166349df072067e7d915d0c7af92 100644 GIT binary patch delta 3550 zcmcImc~nzp7Jo@z_T@>XBq1U}Awfc9OITDyp{}4s6sYc6+~M$^XBXSoQ0fD@vRm03*^AkeSWj79tXdY%ie}N6-!R*mPG%-k!Wdz6F%B@Sj7&xl z{VDxA{ezkGGWuKeB)T8^0KJSJLTzXa>Q8$@>!ux~*=WmX2{b?Ied+~jJ=H>8L{(7P zl-rc!l}-S`YUDNR!u5+>I??sx1R@DP8q;bf@!fQmxJ^Q zIrw#ZF{pz&s!Ay+s!f-N?0J0a7a0>_NDv7s)IQpCF=gIDT_J9ajY;x7V4X~0Ol2sz zRUr=&?&%+WE{FOE@+!)@n6l?i@}c7S^QD>k*fH;di?u<0J>TR!s4UB zfp|HPTh-w0!g!FI7!Ca6CV<3Z1xQL%?wS$65o9U~*DidDu(J5N2Y zjEAOSNE8|qGj+7=0eyLnDJREfC|pZeV_^n4gxPA6rkM{Msg45i7Aa7u!@%^|P_TO11aK!d4&0B61UY6ISUM#E46lm-)v9Q4 zYnlSIPn`r*(;`6+%q$z0vuv6QG$cd<|LF=~pPB$VEOMaT95L1pVIp+c^C&`j-sxYa zPwDdq(VJDEYfCz)-ZB-`+hc*IIKiIMQ^=?AkwwT8gxSyhh~mfG!89;uF~>1pGHx{epd!G8vs`r#%t$u-{|Xu-6FQ zLY`n3*mKz-7@PHs^%d(V8iD4aJl00mDlD8OqxCX>rH#^V(3)w*w0X1u>I3R|>R##^ z>P%{opn>v;(oV^M2_s^6*x}5=3t-pjJqpkZ`nwPi)n9Qo=}8Ylbk;AZ-wNZ0c^SCXOqvTeQ%k_|s@ynb zEPaAWhnSXER4*(K+zVrVHnMsP$O zx~oD4W;>#RusRbgERO;uWpTj2bTat5R0*z^hubBtuEv8N3?UGMF`X-JHy;WQSV0&H zaIM+oaVD<++woErsUSvW$h8~lq%U?Y(_5_;t4Azyo!)jg$d|>nl^cr(9=;v8#706OpMt(-((@y z>LATnOvad`SP$1o7ib8B9ybtXtvC8}m4|7*=*w5`>~~O39!|$?-VOox%ap*7qL?tH zBtz^v^xi`&sVPQ7KOEoG*IMwEhc~?426k0Mf|HeUyVzCJ-ap%?H+{p83m31@Vsi<@ z8a&7I!QlF5UzUeLu^2XO$6r4`AXKQv=++eF=+mu9&Jk4g z)=!CPX$HJdt2Yw*sclM0{m{2xuJS1mslh8AjhnI|O=mOW%sgV9#|6kPQ%D2rFZ6tu zOsYxHq6p-2o!WFE4TpDwdpTQQIvzYJk&hSczyFtJF{vm-ZSUJQ36kf*p!HfC>1*uP z2+-^dYfIibodRSPVW1-}9Q=8k5_H3fbbZ_G@uGb#XL`I)6rv*{H}B`64|6hbSb5Uk zt_}y&YnFhH3h8*!-WSg2-rR)f2>yO+c!jqW`aI?(|Q<3Xj z%~gfZcwP0Yxyh1>QID9ck!YXe``wsNI_K!6t6f>~$O7DM){r=YbT|Fyl|C_(qg5|D z^72%M%|eh64|F|jD>ac)3F;Pw?gyEZdoxUhka;woGKuzov$cGg&$^Gl{%OGAj_M4P zS?|TT%X#|NZxGiVR@!=ZZw&3dx6JZTz~ZCpop{=qsslwm)cgpXUV{eA;WpZ zd>EaIIe=hySrY6M>>yT(X|P$Cj5&a@1P=tC3XU@WC8!eU1#<;*0Shi+pYa>{7XA$W zINk{F2JZ;Z&dcH{xG%WZSV`OlZXR62#GFT*vz%Jat1CH?9F*P9ZecswIcyahVfC>z ztuR@;$n~P*xEBLLblG()Z}`kj(HnYGf%mH1jvspRd?~-8Ua{m_dgr9YxJE~oQ{?)* zX^*Eb5JZT!be~cs+=!3N!p81YDwp|H_g*^b8xfK z+s6|3wyJX*NO2I_y>_#^wUnhvB?_%N0^%U0$8M6it6JLL@M*XO1yA;Vb?2j52E9dx z=aM)3M@xab{QVPN7MGxo+QnU6)@fKOVYS-46d-b!yzB5%fDp}J^o@3P-00jili5tJ z6d@AVXzwt~_xWS=;BK&TX>}TI)Di~V?12L4EeV00`O9#%7f?drbDEA z!Q>ig7-0KquwmTGjZJ4gt$b^Tl^OqGYS^t^=;_2iT1`Qv@x zr|k>2?Et%H25%;WNZlC$#(aF`K)%9gPjo;e?TP9>G08oyL2j2Mof!6ZpL&18C-d6# z)7gjfZ&co&7hCpn3K0lHDLc#2J?5zMpZu9!f&LAvgY-F4#O&k6H?+7ZZ2C%dYaVqC zHzWs5Q^cgwF4XWNN`ZVz8K+_9fpICjn!C+TC@a}=-oX{|Rm_OUam(02gC@1JAw@P0 zN|$IY7vi59iJ?TC!%Z=)<=>-rVZUJIdHN|ko!Lh{%&+Y8nr2!jE=HUC2xUe{J=~rI z9yD7RHzKXMsA{yJ+-rra)r#ZW@-XP#nLT~i4--RKA`#MvMyt;su_{z#@y)axli(rE4SLT}5T_Dh)2qtvFe%CK~n}=7hiXtuU~?yrXq~^KBB?}VnbQOEF_9@sW?o^Fc2siQzQ?E`({I)dzh+{ZuxBPYgFwb zcBp05ocAz;9*;dxQQPZCzKV01U84q6v+#@ZmohGnDW{bl zgGe!kp?w~lx_eejnx4Fk; zly2u5*%Q(^rjy?!y~`Yx*0C<`OP11^++=0}Jw&fE*O@778?BTA(ll-%PYjO?pBrlQ z4gMe8*`JBxh#g&V7hXu%krpxIx4s+%^&6j!J8>nE3rC+Fm%|Rc9xuSWWC2=2d3ZBX zfFr$bY)(264QJx_L_tr;hGfDEXFMPM$=OKLqj%$O%x||ridvD{VaK(;3_K1w5sqbG zPp2Iz4LP`||LO8>8y+?|aVTQN+ff_NZOlY{rv*1UGVo!{jFg?}NZD*dX4sCe*#Eg0*q!kw~QPX8Uo{dt&~w4grV;7Z$GMQbGY#7Lx^ zfh$^rT7Nc1quB`ha^Q^_Udo3c>W(S$!bD&X0Yj%%F`2*p{r PxMdMa8eYNCh6VouQ!a%e diff --git a/telegram/bot_session.session-journal b/telegram/bot_session.session-journal index 2c8066e79931c298e42c26618777e747fd01cc6f..f1aaf4da51366a56573168d288c9370b0b3e6ef1 100644 GIT binary patch delta 3628 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 delta 2915 zcmai#dvFuS9mn^i)5CiB4A{uBEO})tOY#Hcw>3_P1$HUm1{(ttcigIH>tr3)%es>v zgpR0SVyDFkWP@=?9B2xmI^*z&6DCb3P3zX_5IUsO)yaxQ zF|aeg+sA!(_qU(@-R}<64ZCz8sni~xIfA<>0ElpvHufXYXGBD4XBigGbtCzR`G!3D+Q3-W!2(zxm$9GG)rHUnj{kPEm0OROqLM;5@k|U zYDb-{Nl7Y7h#uMB$>qef;=1}N)mcd-#i?vnzb&3nrzi$RZSo;yDdkN$uDquHOX?$3 zo#Z6-J~+R4GSs2)NXs@6)4+=+HF`9^vH6uM9{5(2}j+ zD=&Tgw)rA5Yb(&@tYS1}u%SJf1*9Yba#2Ty0lku$j{9&TNjVPw_OqWmhb;OlSmiaT=;DitE5~bs1j82sC7x=lwmFhc~?0?xFo1Ogk>?J+!xv&EHvu zDs%-V*63i#cw?q;6pX8f#a;4+$6QP@~gF`f6^;n#-R z1^@Kq7mr&{&D1sy1%turs%nm=cd+ezAjHyaapOb!;R}Cy!!9hFK~BCf{>i0(e|e#| z!|my@*L(dmYijx~+6=3ZF2_Qu&VpieI@OAtItyBlBk7zz7ft1qThuAQGQU~aPD}Qe zwqB{a>2wA{qgBO55Kd= zWw+bwm=M#+=H=?pr?4EIf(0$9kANrM3k!N9`JcI-|EhLfbJKn=>vsD+ZVuz>qv1~0 z&)O6pnTLOW{d`JHE76cY+Su6m@O$^fp(-8h)tF# zg0PUJD6k2sRC%#RF4CqO5M!)CS;oyIRfh-YbX`^nd}A4q0SW&8NkH7ZkRD}g&WLbE z*nw^+^H$^G)o-oEd+ULj?5{eO{dyo&Mac1V)^5loZoU0mN!tE_vGK;ZZuzT;Z2;H;)Y!{-S;*@| zb5jG$GteCf`Dh-Nfiyr6pcX;^90sh!dH^yJ&odoN2nItqd4&34C(~E-|JsN!HOO-b^<4@<(Dfie%8#CuJI=k=XaJfPrKDywlBk8E&y9xdXxC_&oslToq0i+re-=Z1V=%mOhs|g7{(qTRz^qq^SoF$8G`~wDZ5W&yrDzOaz8i{>cfjvgg@+=cm3N?8L z_r&Nus4rr;Z`cWb$B;x4OrSr&Lx0%oO@b3Yo$w5EOvq3B7|1id{L+%3C*Xg;CEdM3 zGJgn@qxf#hQH4n0w6|j+MmdfR_;G~BYw;bN5~KIvcs3H&kM1}L?<99P5C+%{D z@OJm35D4)Jrb~0ef#GrDTp)ZFk)<^}-N^*PeB4LD#k+Ad5K@g!M|24=uyZjO3zg^& W6bpmUl4v9)CUqh+S!)kp$@?F)hf}iv diff --git a/交易/bitmart_优化版.py b/交易/bitmart_优化版.py index d26dd9c..ceb1071 100644 --- a/交易/bitmart_优化版.py +++ b/交易/bitmart_优化版.py @@ -120,10 +120,12 @@ class WeexTransaction: for _ in range(3): try: tab.get("https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT") - res = tab.listen.wait(timeout=5) + res = tab.listen.wait(timeout=15) # 请求头 + Cookies - self.session.headers.update(res.request.headers) + for i in res.request.headers: + if ":" not in i: + self.session.headers[i] = res.request.headers[i] for c in res.request.cookies: self.cookies[c["name"]] = c["value"] @@ -309,93 +311,89 @@ class WeexTransaction: self.pbar = tqdm(total=30, desc="等待半小时周期", ncols=80) - now = time.localtime() - minute = now.tm_min + while True: + now = time.localtime() + minute = now.tm_min - # 更新进度条 - self.pbar.n = minute if minute < 30 else minute - 30 - self.pbar.refresh() + # 更新进度条 + self.pbar.n = minute if minute < 30 else minute - 30 + self.pbar.refresh() - # 必须是整点或半点及前 5 分钟 - if minute not in [0, 1, 2, 3, 4, 5, 30, 31, 32, 33, 34, 35]: - time.sleep(8) - return + # # 必须是整点或半点及前 5 分钟 + # if minute not in [0, 1, 2, 3, 4, 5, 30, 31, 32, 33, 34, 35]: + # time.sleep(8) + # return - # 时间重复跳过 - if self.time_start == self.get_half_hour_timestamp(): - time.sleep(5) - return - - # ---- 获取 Token ---- - if not self.get_token(): - self.ding("获取 token 失败!", error=True) - return - logger.info("Token 获取成功") - - # ---- 获取价格 ---- - kdatas = self.get_price() - if not kdatas: - self.ding("获取价格失败!", error=True) - return - - self.kline_1, self.kline_2, self.kline_3 = kdatas[-3:] - if int(self.kline_3["id"]) != self.get_half_hour_timestamp(): - return - - logger.success("K线获取成功") - - self.time_start = self.get_half_hour_timestamp() - - # ---- 刷新页面 ---- - self.page.get("https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT") - - for i in range(3): - # ---- 获取仓位 ---- - if not self.get_position_status(): - self.ding("获取仓位失败!", error=True) + # 时间重复跳过 + if self.time_start == self.get_half_hour_timestamp(): + time.sleep(5) continue - if self.start: - break + # ---- 获取 Token ---- + if not self.get_token(): + self.ding("获取 token 失败!", error=True) + return + logger.info("Token 获取成功") - # ---- 止损平仓 ---- - try: - if self.start == 1 and is_bearish(self.kline_1) and is_bearish(self.kline_2): - self.ding("两根大阴线,平多") - self.click_safe('x://span[normalize-space(text()) ="市价"]') - self.start = 0 + # ---- 获取价格 ---- + kdatas = self.get_price() + if not kdatas: + self.ding("获取价格失败!", error=True) + return - elif self.start == -1 and is_bullish(self.kline_1) and is_bullish(self.kline_2): - self.ding("两根大阳线,平空") - self.click_safe('x://span[normalize-space(text()) ="市价"]') - self.start = 0 - except: - self.ding("止损平仓错误!", error=True) - # continue - return + self.kline_1, self.kline_2, self.kline_3 = kdatas[-3:] + if int(self.kline_3["id"]) != self.get_half_hour_timestamp(): + continue - # ---- 生成新信号 ---- - self.direction = self.check_signal(prev=self.kline_1, curr=self.kline_2) + logger.success("K线获取成功") - # ---- 执行交易 ---- - if self.direction: + self.time_start = self.get_half_hour_timestamp() + + # ---- 刷新页面 ---- + self.page.get("https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT") + + for i in range(3): + # ---- 获取仓位 ---- + if not self.get_position_status(): + self.ding("获取仓位失败!", error=True) + continue + + if self.start: + break + + # ---- 止损平仓 ---- try: - self.do_order() - except: - self.ding("下单失败!", error=True) + if self.start == 1 and is_bearish(self.kline_1) and is_bearish(self.kline_2): + self.ding("两根大阴线,平多") + self.click_safe('x://span[normalize-space(text()) ="市价"]') + self.start = 0 - # ---- 周期结束消息 ---- - self.pbar.reset() - self.ding( - f"持仓:{'无' if self.start == 0 else ('多' if self.start == 1 else '空')}," - f"信号:{'无' if not self.direction else ('多' if self.direction == 'long' else '空')}" - ) + elif self.start == -1 and is_bullish(self.kline_1) and is_bullish(self.kline_2): + self.ding("两根大阳线,平空") + self.click_safe('x://span[normalize-space(text()) ="市价"]') + self.start = 0 + except: + self.ding("止损平仓错误!", error=True) + # continue + return + + # ---- 生成新信号 ---- + self.direction = self.check_signal(prev=self.kline_1, curr=self.kline_2) + + # ---- 执行交易 ---- + if self.direction: + try: + self.do_order() + except: + self.ding("下单失败!", error=True) + + # ---- 周期结束消息 ---- + self.pbar.reset() + self.ding( + f"持仓:{'无' if self.start == 0 else ('多' if self.start == 1 else '空')}," + f"信号:{'无' if not self.direction else ('多' if self.direction == 'long' else '空')}" + ) if __name__ == '__main__': - while True: - try: - WeexTransaction(tge_id=196495).action() - except: - pass - time.sleep(30) + WeexTransaction(tge_id=196495).action() diff --git a/交易/weex_交易.py b/交易/weex_交易.py index f47a602..672a385 100644 --- a/交易/weex_交易.py +++ b/交易/weex_交易.py @@ -353,17 +353,18 @@ class WeexTransaction: 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 range(60): # 判断是否是 新的30分钟了 time.sleep(10) continue - if not self.kline_3 or self.get_now_time() != self.kline_3["id"]: - if self.get_token(): # 获取token - logger.info("获取token成功!!!") - else: - logger.info("获取token失败!!!") - self.send_dingtalk_message(message_content=f"获取token失败!!!", type=0) - continue + if self.kline_3 and self.get_now_time() == self.kline_3["id"]: + continue + + if self.get_token(): # 获取token + logger.info("获取token成功!!!") + else: + logger.info("获取token失败!!!") + self.send_dingtalk_message(message_content=f"获取token失败!!!", type=0) new_price_datas = self.get_price() if not new_price_datas: diff --git a/交易/weex_优化版.py b/交易/weex_优化版.py deleted file mode 100644 index ba4a105..0000000 --- a/交易/weex_优化版.py +++ /dev/null @@ -1,357 +0,0 @@ -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" - } - - # 浏览器 - 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 and 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()