Files
alpha_tools/backup_code/wqb_app/simulator/alpha_submitter.py
T
2026-02-05 10:18:53 +08:00

404 lines
15 KiB
Python
Executable File

import requests
import json
import time
from datetime import datetime
import os
from pathlib import Path
import getpass
import sys
# Platform specific imports
if sys.platform == 'win32':
import msvcrt
else:
import tty
import termios
def input_with_asterisks(prompt):
"""Cross-platform password input showing asterisks"""
print(prompt, end='', flush=True)
password = []
try:
if sys.platform == 'win32':
# Windows: Use msvcrt.getch()
while True:
char = msvcrt.getch()
# Handle Enter key
if char in [b'\r', b'\n']:
print() # New line
break
# Handle Backspace
elif char == b'\x08': # Backspace
if password:
password.pop()
# Move cursor back, print space, move cursor back again
print('\b \b', end='', flush=True)
# Handle Ctrl+C
elif char == b'\x03': # Ctrl+C
print()
raise KeyboardInterrupt
# Handle printable characters (ASCII)
elif 32 <= ord(char) <= 126: # Printable ASCII range
password.append(char.decode('ascii'))
print('*', end='', flush=True)
# Handle extended characters
else:
try:
decoded_char = char.decode('utf-8')
if decoded_char.isprintable():
password.append(decoded_char)
print('*', end='', flush=True)
except UnicodeDecodeError:
continue
else:
# Unix/macOS: Use tty and termios
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(fd)
while True:
char = sys.stdin.read(1)
# Handle Enter key
if char in ['\r', '\n']:
print('\r\n', end='', flush=True)
break
# Handle Backspace
elif char in ['\x7f', '\x08']:
if password:
password.pop()
print('\b \b', end='', flush=True)
# Handle Ctrl+C
elif char == '\x03':
print('\r\n', end='', flush=True)
raise KeyboardInterrupt
# Handle printable characters
elif char.isprintable():
password.append(char)
print('*', end='', flush=True)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
except Exception as e:
# Fallback to getpass
print(f"\nError reading password: {e}")
print("Falling back to getpass...")
return getpass.getpass()
return ''.join(password)
def login(account_choice=None):
"""Login to WorldQuant Brain API"""
s = requests.Session()
# Prompt user for credentials
print("\n=== WorldQuant Brain Login ===")
email = input("Enter your email: ").strip()
# Use custom password input with asterisk masking
try:
password = input_with_asterisks("Enter your password: ")
if not password:
print("❌ Password is required.")
return None
except Exception as e:
print(f"❌ Error with custom password input: {e}")
print("Trying standard getpass...")
try:
password = getpass.getpass("Enter your password: ")
if not password:
print("❌ Password is required.")
return None
except Exception as e2:
print(f"❌ Error reading password: {e2}")
return None
if not email:
print("❌ Email is required.")
return None
print(f"Logging in with: {email}")
# Set basic auth
s.auth = (email, password)
try:
# Send authentication request
response = s.post('https://api.worldquantbrain.com/authentication')
print(f"Login response status: {response.status_code}")
print(f"Login response headers: {dict(response.headers)}")
if response.text:
try:
response_json = response.json()
print(f"Login response body: {json.dumps(response_json, indent=2)}")
except json.JSONDecodeError:
print(f"Login response body (not JSON): {response.text}")
response.raise_for_status()
print("Login successful!")
return s
except requests.exceptions.RequestException as e:
print(f"Login failed: {e}")
if hasattr(e, 'response') and e.response is not None:
print(f"Error response status: {e.response.status_code}")
print(f"Error response body: {e.response.text}")
return None
def check_alpha_exists(s, alpha_id):
"""Check if an alpha exists by making a GET request to /alphas/<alpha_id>"""
try:
response = s.get(f"https://api.worldquantbrain.com/alphas/{alpha_id}")
print(f"Alpha check response status: {response.status_code}")
print(f"Alpha check response headers: {dict(response.headers)}")
if response.status_code == 200:
alpha_data = response.json()
print(f"✅ Alpha {alpha_id} exists - Type: {alpha_data.get('type', 'Unknown')}")
print(f"Alpha data: {json.dumps(alpha_data, indent=2)}")
return True, alpha_data
elif response.status_code == 404:
print(f"❌ Alpha {alpha_id} does not exist (404 Not Found)")
if response.text:
print(f"404 response body: {response.text}")
return False, None
else:
print(f"⚠️ Unexpected response for alpha {alpha_id}: {response.status_code}")
if response.text:
print(f"Unexpected response body: {response.text}")
return False, None
except requests.exceptions.RequestException as e:
print(f"❌ Error checking alpha {alpha_id}: {e}")
if hasattr(e, 'response') and e.response is not None:
print(f"Error response status: {e.response.status_code}")
print(f"Error response body: {e.response.text}")
return False, None
def get_alpha_recordsets(s, alpha_id):
"""Get available record sets for an alpha"""
try:
response = s.get(f"https://api.worldquantbrain.com/alphas/{alpha_id}/recordsets")
print(f"Recordsets response status: {response.status_code}")
print(f"Recordsets response headers: {dict(response.headers)}")
if response.status_code == 200:
recordsets_data = response.json()
print(f"📊 Alpha {alpha_id} has {recordsets_data.get('count', 0)} record sets available")
print(f"Recordsets data: {json.dumps(recordsets_data, indent=2)}")
return recordsets_data
else:
print(f"⚠️ Could not fetch record sets for alpha {alpha_id}: {response.status_code}")
if response.text:
print(f"Recordsets error response body: {response.text}")
return None
except requests.exceptions.RequestException as e:
print(f"❌ Error fetching record sets for alpha {alpha_id}: {e}")
if hasattr(e, 'response') and e.response is not None:
print(f"Error response status: {e.response.status_code}")
print(f"Error response body: {e.response.text}")
return None
def submit(s, alpha_id):
"""Submit a single alpha with retry logic - keeps trying until success"""
def submit_inner(s, alpha_id):
"""Inner submit function with rate limiting handling"""
try:
result = s.post(f"https://api.worldquantbrain.com/alphas/{alpha_id}/submit")
print(f"Alpha submit, alpha_id={alpha_id}, status_code={result.status_code}")
print(f"Response headers: {dict(result.headers)}")
# Handle rate limiting
while True:
if "retry-after" in result.headers:
wait_time = float(result.headers["Retry-After"])
print(f"Rate limited, waiting {wait_time} seconds...")
time.sleep(wait_time)
result = s.get(f"https://api.worldquantbrain.com/alphas/{alpha_id}/submit")
print(f"Retry GET response, status_code={result.status_code}")
print(f"Retry headers: {dict(result.headers)}")
else:
break
return result
except Exception as e:
print(f'Connection error: {e}, attempting to re-login...')
new_session = login()
if new_session is None:
return None
return submit_inner(new_session, alpha_id)
attempt_count = 1
result = None
while True:
print(f"Submit attempt {attempt_count} for alpha {alpha_id}")
result = submit_inner(s, alpha_id)
if result is None:
print(f"Failed to submit {alpha_id} - connection error")
return None
if result.status_code == 200:
print(f"✅ Alpha {alpha_id} submit successful, status_code={result.status_code}")
return result
elif result.status_code == 403:
print(f"❌ Alpha {alpha_id} submit forbidden, status_code={result.status_code}")
return result
else:
print(f"⚠️ Alpha submit fail, status_code={result.status_code}, alpha_id={alpha_id}, attempt {attempt_count}")
print(f"Waiting 2 minutes before retry...")
time.sleep(120) # 2 minutes = 120 seconds
attempt_count += 1
continue
def submit_alpha(alpha_id, session=None, account_choice=None):
"""Submit a single alpha with comprehensive error handling"""
if session is None:
s = login(account_choice)
if s is None:
return False
else:
s = session
# First check if the alpha exists
print(f"Checking if alpha {alpha_id} exists...")
exists, alpha_data = check_alpha_exists(s, alpha_id)
if not exists:
print(f"❌ Cannot submit alpha {alpha_id} - it does not exist")
return False
# Submit the alpha
res = submit(s, alpha_id)
if res is None:
print(f"Failed to submit {alpha_id} - connection error")
return False
# Parse response
if res.text:
try:
res_json = res.json()
print(f"Submit response parsed successfully")
except json.JSONDecodeError:
print(f"Submit response is not JSON: {res.text[:200]}...")
return False
else:
print(f"Submit response has no text content")
return False
# Check for various error conditions
if 'detail' in res_json and res_json['detail'] == 'Not found.':
print(f"{alpha_id} - Alpha ID not found")
return False
# Check submission status
submitted = True
if 'is' in res_json and 'checks' in res_json['is']:
for item in res_json['is']['checks']:
if item['name'] == 'ALREADY_SUBMITTED':
submitted = False
print(f"{alpha_id} - Already submitted")
break
if item['result'] == 'FAIL':
submitted = False
print(f"{alpha_id} - {item['name']} check failed, limit = {item['limit']}, value = {item['value']}")
break
if submitted:
print(f'{alpha_id} - Submission successful!')
return True
else:
return False
def main():
"""Main function to run the alpha submission script"""
print("=== WorldQuant Brain Alpha Submitter ===")
print("This script will help you submit alphas with automatic retry logic.")
print("You will be prompted to enter your WorldQuant Brain credentials.\n")
# Login with user credentials
session = login()
if session is None:
print("Failed to login. Exiting.")
return
print("\n=== Alpha Submission Mode ===")
print("Enter alpha IDs one by one. Type 'quit' to exit.")
print("Type 'relogin' to login with different credentials.")
print("Type 'info <alpha_id>' to check alpha details before submitting.")
while True:
alpha_id = input("\nEnter alpha ID (or 'quit' to exit, 'relogin' to change credentials): ").strip()
if alpha_id.lower() == 'quit':
print("Goodbye!")
break
if alpha_id.lower() == 'relogin':
print("\nRe-logging in...")
session = login()
if session is None:
print("Failed to login. Exiting.")
return
continue
if alpha_id.lower().startswith('info '):
info_alpha_id = alpha_id[5:].strip()
if not info_alpha_id:
print("Please provide an alpha ID after 'info'")
continue
print(f"\nChecking details for alpha: {info_alpha_id}")
print("=" * 50)
# Check if alpha exists
exists, alpha_data = check_alpha_exists(session, info_alpha_id)
if exists:
# Get record sets
get_alpha_recordsets(session, info_alpha_id)
# Show some basic alpha info
if alpha_data:
print(f"📋 Alpha Details:")
print(f" ID: {alpha_data.get('id', 'N/A')}")
print(f" Type: {alpha_data.get('type', 'N/A')}")
if 'settings' in alpha_data:
print(f" Has settings: Yes")
if 'regular' in alpha_data:
print(f" Has regular data: Yes")
if 'combo' in alpha_data:
print(f" Has combo data: Yes")
if 'selection' in alpha_data:
print(f" Has selection data: Yes")
print("=" * 50)
continue
if not alpha_id:
print("Please enter a valid alpha ID.")
continue
print(f"\nSubmitting alpha: {alpha_id}")
print("=" * 50)
success = submit_alpha(alpha_id, session)
if success:
print(f"✅ Alpha {alpha_id} processed successfully!")
else:
print(f"❌ Alpha {alpha_id} failed to submit properly.")
print("=" * 50)
if __name__ == "__main__":
main()