feat: optimize trade_server
This commit is contained in:
parent
9a25c330a1
commit
1397ddbfd6
@ -1,6 +1,6 @@
|
||||
Flask
|
||||
Flask-Limiter
|
||||
pywin32
|
||||
requests
|
||||
schedule
|
||||
virtualenv
|
||||
pytesseract
|
23
src/config.py
Normal file
23
src/config.py
Normal 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
|
@ -3,6 +3,51 @@ import threading
|
||||
import time
|
||||
from real_trader import RealTrader
|
||||
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):
|
||||
@ -15,29 +60,30 @@ def run_daily(time_str, job_func):
|
||||
|
||||
def run_pending_tasks():
|
||||
while True:
|
||||
try:
|
||||
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
|
||||
threading.Thread(target=run_pending_tasks).start()
|
||||
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__)
|
||||
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"])
|
||||
@ -48,6 +94,7 @@ def health_check():
|
||||
@app.route("/yu/buy", methods=["POST"])
|
||||
def buy():
|
||||
"""Buy an item with given parameters."""
|
||||
logger.info("Received buy request")
|
||||
# Get data from request body
|
||||
data = request.get_json()
|
||||
code = data.get("code")
|
||||
@ -55,23 +102,33 @@ def buy():
|
||||
amount_str = data.get("amount")
|
||||
|
||||
try:
|
||||
if not all([code, price_str, amount_str]):
|
||||
raise ValueError("Missing required parameters")
|
||||
|
||||
price = float(price_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)
|
||||
logger.info(f"Buy order result: {result}")
|
||||
|
||||
response = {"success": True, "data": result}
|
||||
return jsonify(response), 200
|
||||
except ValueError:
|
||||
abort(
|
||||
400,
|
||||
description="Invalid format for one or more of the following args: code, price, amount.",
|
||||
)
|
||||
except ValueError as e:
|
||||
logger.error(f"Invalid request parameters: {str(e)}")
|
||||
abort(400, description=str(e))
|
||||
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"])
|
||||
def sell():
|
||||
"""Sell an item with given parameters."""
|
||||
logger.info("Received sell request")
|
||||
# Get data from request body
|
||||
data = request.get_json()
|
||||
code = data.get("code")
|
||||
@ -79,76 +136,118 @@ def sell():
|
||||
amount_str = data.get("amount")
|
||||
|
||||
try:
|
||||
if not all([code, price_str, amount_str]):
|
||||
raise ValueError("Missing required parameters")
|
||||
|
||||
price = float(price_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)
|
||||
logger.info(f"Sell order result: {result}")
|
||||
|
||||
response = {"success": True, "data": result}
|
||||
return jsonify(response), 200
|
||||
except ValueError:
|
||||
abort(
|
||||
400,
|
||||
description="Invalid format for one or more of the following args: code, price, amount.",
|
||||
)
|
||||
except ValueError as e:
|
||||
logger.error(f"Invalid request parameters: {str(e)}")
|
||||
abort(400, description=str(e))
|
||||
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"])
|
||||
def cancel(entrust_no):
|
||||
print(f"cancel: {entrust_no}")
|
||||
logger.info(f"Received cancel request for entrust_no={entrust_no}")
|
||||
try:
|
||||
result = trader.cancel(entrust_no)
|
||||
logger.info(f"Cancel result: {result}")
|
||||
|
||||
response = {"success": True, "data": result}
|
||||
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"])
|
||||
def get_balance():
|
||||
"""Get the balance of the account."""
|
||||
logger.info("Received balance request")
|
||||
try:
|
||||
balance = trader.get_balance()
|
||||
print(f"balance: {balance}")
|
||||
logger.info(f"Balance: {balance}")
|
||||
|
||||
response = {"success": True, "data": balance}
|
||||
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"])
|
||||
def get_positions():
|
||||
"""Get the positions of the account."""
|
||||
logger.info("Received positions request")
|
||||
try:
|
||||
positions = trader.get_positions()
|
||||
logger.info(f"Positions: {positions}")
|
||||
|
||||
response = {"success": True, "data": positions}
|
||||
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"])
|
||||
def get_today_trades():
|
||||
"""Get the today's trades of the account."""
|
||||
logger.info("Received today trades request")
|
||||
try:
|
||||
trades = trader.get_today_trades()
|
||||
logger.info(f"Today trades: {trades}")
|
||||
|
||||
response = {"success": True, "data": trades}
|
||||
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"])
|
||||
def get_today_entrust():
|
||||
"""Get the today's entrust of the account."""
|
||||
logger.info("Received today entrust request")
|
||||
try:
|
||||
entrust = trader.get_today_entrust()
|
||||
logger.info(f"Today entrust: {entrust}")
|
||||
|
||||
response = {"success": True, "data": entrust}
|
||||
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"])
|
||||
def refresh():
|
||||
"""Refresh the account."""
|
||||
logger.info("Received refresh request")
|
||||
try:
|
||||
trader.refresh()
|
||||
logger.info("Account data refreshed successfully")
|
||||
|
||||
response = {"success": True, "data": "Account data refreshed successfully."}
|
||||
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__":
|
||||
PORT = 9527
|
||||
print(f"Server listening on port {PORT}")
|
||||
app.run(debug=False, host="0.0.0.0", port=PORT)
|
||||
logger.info(f"Server starting on {Config.HOST}:{Config.PORT}")
|
||||
app.run(debug=Config.DEBUG, host=Config.HOST, port=Config.PORT)
|
||||
|
Loading…
x
Reference in New Issue
Block a user