feat: optimize trade_server
This commit is contained in:
parent
9a25c330a1
commit
1397ddbfd6
@ -1,6 +1,6 @@
|
|||||||
Flask
|
Flask
|
||||||
|
Flask-Limiter
|
||||||
pywin32
|
pywin32
|
||||||
requests
|
requests
|
||||||
schedule
|
schedule
|
||||||
virtualenv
|
pytesseract
|
||||||
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
|
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)
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user