You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

168 lines
6.0 KiB

import time
import logging
import httpx
from httpx import BasicAuth
# 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
def login():
"""
登录 WorldQuant Brain API,返回 httpx.Client 对象。
注意:登录成功后 client 已包含认证信息,无需额外处理。
"""
# 从 nacos 获取账号密码
nacos_resp = httpx.get('http://192.168.31.41:30848/nacos/v1/cs/configs?dataId=wq_account&group=quantify')
if nacos_resp.status_code != 200:
logger.error('获取账号密码失败')
return None
config = nacos_resp.json()
username = config['user_name']
password = config['password']
logger.info(f"正在登录账户: {username}")
# 创建客户端并认证
client = httpx.Client(auth=BasicAuth(username, password))
# 发送登录请求
response = client.post('https://api.worldquantbrain.com/authentication')
logger.info(f"登录状态: {response.status_code}")
if response.status_code == 201:
logger.info("登录成功!")
logger.debug(response.json())
return client
else:
logger.error(f"登录失败: {response.json()}")
client.close()
return None
def submit_alpha(alpha_id):
"""
提交 alpha,自动重试直到成功或达到最大重试次数。
返回 True 表示提交成功,False 表示最终失败。
"""
TOTAL_RETRY_COUNT = 100000
retry_count = 0
client = None
while retry_count < TOTAL_RETRY_COUNT:
# 如果没有有效 client,则重新登录
if client is None:
client = login()
if client is None:
logger.error("登录失败,等待 10 秒后重试")
time.sleep(10)
retry_count += 1
continue
try:
url = f"https://api.worldquantbrain.com/alphas/{alpha_id}/submit"
logger.debug(f'url: {url}')
# 1. 发送提交请求
res = client.post(url)
# 处理特殊情况:已经提交过(需要轮询结果)
if res.status_code == 400 and "The plain HTTP request was sent to HTTPS port" in res.text:
logger.info("Alpha 已提交,正在轮询状态...")
# 轮询获取最终结果
poll_interval = 1.0 # 初始轮询间隔
while True:
time.sleep(poll_interval)
print(".", end="", flush=True)
# 使用 GET 请求查询当前状态
poll_res = client.get(url)
# 如果服务器返回了 Retry-After,则使用它
if "retry-after" in poll_res.headers:
poll_interval = max(float(poll_res.headers["retry-after"]), 3)
else:
poll_interval = 3 # 默认间隔
# 当状态不再是 400(处理中)时,退出轮询
if poll_res.status_code != 400 or "The plain HTTP request was sent to HTTPS port" not in poll_res.text:
res = poll_res
break
logger.info(f"轮询结束,最终状态码: {res.status_code}")
# 2. 处理各种状态码
if res.status_code == 429:
logger.info("触发限流 (429),休眠 60 秒后重试")
time.sleep(60)
retry_count += 1
continue
if res.status_code == 401:
logger.warning("认证失效,重新登录")
if client:
client.close()
client = None
retry_count += 1
continue
if res.status_code == 404:
logger.warning(f"Alpha {alpha_id} 不存在或超时,重试 ({retry_count+1}/{TOTAL_RETRY_COUNT})")
retry_count += 1
continue
if res.status_code // 100 == 5:
logger.warning(f"服务器错误 {res.status_code},5 秒后重试")
time.sleep(5)
retry_count += 1
continue
if res.status_code == 403:
logger.info(f"{alpha_id} 提交失败 (403)")
fail_checks = []
try:
checks = res.json()["is"]["checks"]
fail_checks = [x for x in checks if x.get("result") == "FAIL"]
except Exception as e:
logger.error(f"解析失败原因时出错: {e}")
logger.info(f"失败的检查项: {fail_checks}")
# 如果是提交次数超限,则直接退出,不再重试
if any(x.get("name") in ["REGULAR_SUBMISSION", "SUPER_SUBMISSION"] for x in fail_checks):
logger.error("提交次数超过限制,放弃重试")
return False
# 其他 403 错误也视为永久失败
return False
if res.status_code == 200:
logger.info(f"{alpha_id} 提交成功")
return True
# 其他非 2xx 状态码,视为未知错误,直接退出
logger.error(f"未处理的响应状态码 {res.status_code},放弃重试。响应内容: {res.text[:200]}")
return False
except httpx.RequestError as e:
logger.error(f"网络请求异常 (alpha_id={alpha_id}, retry={retry_count}): {e}")
retry_count += 1
time.sleep(10)
continue
except Exception as e:
logger.error(f"未预期的异常 (alpha_id={alpha_id}): {e}")
return False
# 超过最大重试次数
logger.error(f"达到最大重试次数 {TOTAL_RETRY_COUNT},提交失败")
return False
# 使用示例
if __name__ == "__main__":
# 测试提交一个 alpha
alpha_id = "your_alpha_id_here"
success = submit_alpha(alpha_id)
if success:
print("提交完成")
else:
print("提交失败")