feat: optimize trade_server

This commit is contained in:
zhiyong 2025-01-05 22:51:40 +08:00
parent 9a25c330a1
commit 1397ddbfd6
3 changed files with 174 additions and 52 deletions

View File

@ -1,6 +1,6 @@
Flask Flask
Flask-Limiter
pywin32 pywin32
requests requests
schedule schedule
virtualenv pytesseract
pytesseract

23
src/config.py Normal file
View File

@ -0,0 +1,23 @@
import os
class Config:
# Server settings
PORT = int(os.getenv('TRADE_SERVER_PORT', 9527))
HOST = os.getenv('TRADE_SERVER_HOST', '0.0.0.0')
DEBUG = False
# Trading hours
MARKET_OPEN_TIME = "09:00"
MARKET_ACTIVE_TIME = "09:15"
MARKET_CLOSE_TIME = "15:30"
# Logging
LOG_DIR = "logs"
LOG_LEVEL = "INFO"
LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
LOG_MAX_BYTES = 10 * 1024 * 1024 # 10MB
LOG_BACKUP_COUNT = 5
# API Rate limiting
RATE_LIMIT_REQUESTS = 100
RATE_LIMIT_PERIOD = 60 # seconds

View File

@ -3,6 +3,51 @@ import threading
import time import time
from real_trader import RealTrader from real_trader import RealTrader
from flask import Flask, request, abort, jsonify from flask import Flask, request, abort, jsonify
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from config import Config
import logging
import os
from logging.handlers import RotatingFileHandler
# 配置日志
def setup_logger():
log_dir = Config.LOG_DIR
try:
if not os.path.exists(log_dir):
os.makedirs(log_dir)
logger = logging.getLogger('trade_server')
logger.info(f"Created logs directory at: {os.path.abspath(log_dir)}")
except Exception as e:
print(f"Error creating logs directory: {str(e)}")
# 如果无法创建目录,使用当前目录
log_dir = "."
logger = logging.getLogger('trade_server')
logger.setLevel(getattr(logging, Config.LOG_LEVEL))
# 确保没有重复的处理器
for handler in logger.handlers[:]:
logger.removeHandler(handler)
# 文件处理器
file_handler = RotatingFileHandler(
os.path.join(log_dir, 'trade_server.log'),
maxBytes=Config.LOG_MAX_BYTES,
backupCount=Config.LOG_BACKUP_COUNT
)
file_handler.setFormatter(logging.Formatter(Config.LOG_FORMAT))
logger.addHandler(file_handler)
# 添加控制台处理器
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter(Config.LOG_FORMAT))
logger.addHandler(console_handler)
return logger
logger = setup_logger()
def run_daily(time_str, job_func): def run_daily(time_str, job_func):
@ -15,29 +60,30 @@ def run_daily(time_str, job_func):
def run_pending_tasks(): def run_pending_tasks():
while True: while True:
schedule.run_pending() try:
time.sleep(1) schedule.run_pending()
time.sleep(1)
except Exception as e:
logger.error(f"Error in scheduler: {str(e)}")
# Run the task scheduler in a new thread # Run the task scheduler in a new thread
threading.Thread(target=run_pending_tasks).start() threading.Thread(target=run_pending_tasks).start()
trader = RealTrader() trader = RealTrader()
### test code start
trader.login()
# time.sleep(5)
# trader.logout()
### test code end
# 打开并登陆交易软件
run_daily("09:00", lambda: trader.login())
# 激活交易软件
run_daily("09:15", lambda: trader.get_balance())
# 关闭交易软件
run_daily("15:30", lambda: trader.logout())
# 添加请求频率限制
app = Flask(__name__) app = Flask(__name__)
limiter = Limiter(
app=app,
key_func=get_remote_address,
default_limits=[f"{Config.RATE_LIMIT_REQUESTS} per {Config.RATE_LIMIT_PERIOD} seconds"]
)
# 使用配置文件中的时间
run_daily(Config.MARKET_OPEN_TIME, lambda: trader.login())
run_daily(Config.MARKET_ACTIVE_TIME, lambda: trader.get_balance())
run_daily(Config.MARKET_CLOSE_TIME, lambda: trader.logout())
@app.route("/yu/healthcheck", methods=["GET"]) @app.route("/yu/healthcheck", methods=["GET"])
@ -48,6 +94,7 @@ def health_check():
@app.route("/yu/buy", methods=["POST"]) @app.route("/yu/buy", methods=["POST"])
def buy(): def buy():
"""Buy an item with given parameters.""" """Buy an item with given parameters."""
logger.info("Received buy request")
# Get data from request body # Get data from request body
data = request.get_json() data = request.get_json()
code = data.get("code") code = data.get("code")
@ -55,23 +102,33 @@ def buy():
amount_str = data.get("amount") amount_str = data.get("amount")
try: try:
if not all([code, price_str, amount_str]):
raise ValueError("Missing required parameters")
price = float(price_str) price = float(price_str)
amount = int(amount_str) amount = int(amount_str)
print(f"buy: {code}, {price}, {amount}")
if price <= 0 or amount <= 0:
raise ValueError("Price and amount must be positive")
logger.info(f"Executing buy order: code={code}, price={price}, amount={amount}")
result = trader.buy(code, price, amount) result = trader.buy(code, price, amount)
logger.info(f"Buy order result: {result}")
response = {"success": True, "data": result} response = {"success": True, "data": result}
return jsonify(response), 200 return jsonify(response), 200
except ValueError: except ValueError as e:
abort( logger.error(f"Invalid request parameters: {str(e)}")
400, abort(400, description=str(e))
description="Invalid format for one or more of the following args: code, price, amount.", except Exception as e:
) logger.error(f"Error processing buy request: {str(e)}")
abort(500, description="Internal server error")
@app.route("/yu/sell", methods=["POST"]) @app.route("/yu/sell", methods=["POST"])
def sell(): def sell():
"""Sell an item with given parameters.""" """Sell an item with given parameters."""
logger.info("Received sell request")
# Get data from request body # Get data from request body
data = request.get_json() data = request.get_json()
code = data.get("code") code = data.get("code")
@ -79,76 +136,118 @@ def sell():
amount_str = data.get("amount") amount_str = data.get("amount")
try: try:
if not all([code, price_str, amount_str]):
raise ValueError("Missing required parameters")
price = float(price_str) price = float(price_str)
amount = int(amount_str) amount = int(amount_str)
print(f"sell: {code}, {price}, {amount}")
if price <= 0 or amount <= 0:
raise ValueError("Price and amount must be positive")
logger.info(f"Executing sell order: code={code}, price={price}, amount={amount}")
result = trader.sell(code, price, amount) result = trader.sell(code, price, amount)
logger.info(f"Sell order result: {result}")
response = {"success": True, "data": result} response = {"success": True, "data": result}
return jsonify(response), 200 return jsonify(response), 200
except ValueError: except ValueError as e:
abort( logger.error(f"Invalid request parameters: {str(e)}")
400, abort(400, description=str(e))
description="Invalid format for one or more of the following args: code, price, amount.", except Exception as e:
) logger.error(f"Error processing sell request: {str(e)}")
abort(500, description="Internal server error")
@app.route("/yu/cancel/<entrust_no>", methods=["DELETE"]) @app.route("/yu/cancel/<entrust_no>", methods=["DELETE"])
def cancel(entrust_no): def cancel(entrust_no):
print(f"cancel: {entrust_no}") logger.info(f"Received cancel request for entrust_no={entrust_no}")
result = trader.cancel(entrust_no) try:
result = trader.cancel(entrust_no)
logger.info(f"Cancel result: {result}")
response = {"success": True, "data": result} response = {"success": True, "data": result}
return jsonify(response), 200 return jsonify(response), 200
except Exception as e:
logger.error(f"Error processing cancel request: {str(e)}")
abort(500, description="Internal server error")
@app.route("/yu/balance", methods=["GET"]) @app.route("/yu/balance", methods=["GET"])
def get_balance(): def get_balance():
"""Get the balance of the account.""" """Get the balance of the account."""
balance = trader.get_balance() logger.info("Received balance request")
print(f"balance: {balance}") try:
balance = trader.get_balance()
logger.info(f"Balance: {balance}")
response = {"success": True, "data": balance} response = {"success": True, "data": balance}
return jsonify(response), 200 return jsonify(response), 200
except Exception as e:
logger.error(f"Error processing balance request: {str(e)}")
abort(500, description="Internal server error")
@app.route("/yu/positions", methods=["GET"]) @app.route("/yu/positions", methods=["GET"])
def get_positions(): def get_positions():
"""Get the positions of the account.""" """Get the positions of the account."""
positions = trader.get_positions() logger.info("Received positions request")
try:
positions = trader.get_positions()
logger.info(f"Positions: {positions}")
response = {"success": True, "data": positions} response = {"success": True, "data": positions}
return jsonify(response), 200 return jsonify(response), 200
except Exception as e:
logger.error(f"Error processing positions request: {str(e)}")
abort(500, description="Internal server error")
@app.route("/yu/todaytrades", methods=["GET"]) @app.route("/yu/todaytrades", methods=["GET"])
def get_today_trades(): def get_today_trades():
"""Get the today's trades of the account.""" """Get the today's trades of the account."""
trades = trader.get_today_trades() logger.info("Received today trades request")
try:
trades = trader.get_today_trades()
logger.info(f"Today trades: {trades}")
response = {"success": True, "data": trades} response = {"success": True, "data": trades}
return jsonify(response), 200 return jsonify(response), 200
except Exception as e:
logger.error(f"Error processing today trades request: {str(e)}")
abort(500, description="Internal server error")
@app.route("/yu/todayentrust", methods=["GET"]) @app.route("/yu/todayentrust", methods=["GET"])
def get_today_entrust(): def get_today_entrust():
"""Get the today's entrust of the account.""" """Get the today's entrust of the account."""
entrust = trader.get_today_entrust() logger.info("Received today entrust request")
try:
entrust = trader.get_today_entrust()
logger.info(f"Today entrust: {entrust}")
response = {"success": True, "data": entrust} response = {"success": True, "data": entrust}
return jsonify(response), 200 return jsonify(response), 200
except Exception as e:
logger.error(f"Error processing today entrust request: {str(e)}")
abort(500, description="Internal server error")
@app.route("/yu/refresh", methods=["GET"]) @app.route("/yu/refresh", methods=["GET"])
def refresh(): def refresh():
"""Refresh the account.""" """Refresh the account."""
trader.refresh() logger.info("Received refresh request")
try:
trader.refresh()
logger.info("Account data refreshed successfully")
response = {"success": True, "data": "Account data refreshed successfully."} response = {"success": True, "data": "Account data refreshed successfully."}
return jsonify(response), 200 return jsonify(response), 200
except Exception as e:
logger.error(f"Error processing refresh request: {str(e)}")
abort(500, description="Internal server error")
if __name__ == "__main__": if __name__ == "__main__":
PORT = 9527 logger.info(f"Server starting on {Config.HOST}:{Config.PORT}")
print(f"Server listening on port {PORT}") app.run(debug=Config.DEBUG, host=Config.HOST, port=Config.PORT)
app.run(debug=False, host="0.0.0.0", port=PORT)