添加在休市期间模拟买入卖出

This commit is contained in:
zhiyong 2025-04-29 16:59:22 +08:00
parent c185fccd24
commit 24be80288a
4 changed files with 184 additions and 3 deletions

View File

@ -6,6 +6,9 @@ class Config:
HOST = os.getenv('TRADE_SERVER_HOST', '0.0.0.0') HOST = os.getenv('TRADE_SERVER_HOST', '0.0.0.0')
DEBUG = False DEBUG = False
# Trading settings
TRADE_TIMEOUT = int(os.getenv('TRADE_TIMEOUT', 3)) # 交易超时时间(秒)
# Trading hours # Trading hours
MARKET_OPEN_TIME = "09:20" MARKET_OPEN_TIME = "09:20"
MARKET_ACTIVE_TIME = "09:15" MARKET_ACTIVE_TIME = "09:15"

51
src/example_trade.py Normal file
View File

@ -0,0 +1,51 @@
import requests
# 服务器地址
URL = "http://localhost:9527/yu"
def buy(code: str, price: float, amount: int) -> dict:
"""买入股票
Args:
code: 股票代码例如 "601988.SH"
price: 买入价格
amount: 买入数量
Returns:
dict: 交易结果
"""
data = {
"code": code,
"price": str(price),
"amount": str(amount)
}
response = requests.post(f"{URL}/buy", json=data)
return response.json()
def sell(code: str, price: float, amount: int) -> dict:
"""卖出股票
Args:
code: 股票代码例如 "601988.SH"
price: 卖出价格
amount: 卖出数量
Returns:
dict: 交易结果
"""
data = {
"code": code,
"price": str(price),
"amount": str(amount)
}
response = requests.post(f"{URL}/sell", json=data)
return response.json()
if __name__ == "__main__":
# 示例买入中国银行1000股价格3.45
result = buy("601988.SH", 3.45, 1000)
print("买入结果:", result)
# 示例卖出中国银行1000股价格3.48
result = sell("601988.SH", 3.48, 1000)
print("卖出结果:", result)

View File

@ -9,8 +9,39 @@ from config import Config
import logging import logging
import os import os
from logging.handlers import RotatingFileHandler from logging.handlers import RotatingFileHandler
import concurrent.futures
from concurrent.futures import TimeoutError
# 配置模拟交易日志
def setup_simulation_logger():
sim_logger = logging.getLogger('trade_simulation')
sim_logger.setLevel(logging.INFO)
# 确保没有重复的处理器
for handler in sim_logger.handlers[:]:
sim_logger.removeHandler(handler)
# 文件处理器
log_dir = Config.LOG_DIR
if not os.path.exists(log_dir):
os.makedirs(log_dir)
file_handler = RotatingFileHandler(
os.path.join(log_dir, 'trade_simulation.log'),
maxBytes=Config.LOG_MAX_BYTES,
backupCount=Config.LOG_BACKUP_COUNT
)
file_handler.setFormatter(logging.Formatter('%(asctime)s - %(message)s'))
sim_logger.addHandler(file_handler)
# 控制台处理器
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter('%(asctime)s - %(message)s'))
sim_logger.addHandler(console_handler)
return sim_logger
# 配置日志 # 配置日志
def setup_logger(): def setup_logger():
log_dir = Config.LOG_DIR log_dir = Config.LOG_DIR
@ -48,6 +79,7 @@ def setup_logger():
return logger return logger
logger = setup_logger() logger = setup_logger()
sim_logger = setup_simulation_logger()
def run_daily(time_str, job_func): def run_daily(time_str, job_func):
@ -107,8 +139,33 @@ def buy():
if price <= 0 or amount <= 0: if price <= 0 or amount <= 0:
raise ValueError("Price and amount must be positive") raise ValueError("Price and amount must be positive")
# 检查交易状态
should_simulate = True
simulation_reason = "未知原因"
if trader is None:
simulation_reason = "交易实例未初始化"
else:
try:
if not trader.is_trading_time():
simulation_reason = "当前为非交易时段"
else:
should_simulate = False
except Exception as e:
simulation_reason = f"检查交易时间发生错误: {str(e)}"
if should_simulate:
# 记录模拟交易信息
sim_message = f"模拟买入 - {simulation_reason} - 代码: {code}, 价格: {price}, 数量: {amount}"
sim_logger.info(sim_message)
return jsonify({"success": True, "data": {"order_id": "simulation", "message": sim_message}}), 200
# 实际交易
logger.info(f"Executing buy order: code={code}, price={price}, amount={amount}") logger.info(f"Executing buy order: code={code}, price={price}, amount={amount}")
result = trader.buy(code, price, amount) result = execute_with_timeout(trader.buy, Config.TRADE_TIMEOUT, code, price, amount)
if result is None:
logger.error(f"Buy order timeout after {Config.TRADE_TIMEOUT} seconds")
return jsonify({"success": False, "error": "Operation timeout"}), 408
logger.info(f"Buy order result: {result}") logger.info(f"Buy order result: {result}")
response = {"success": True, "data": result} response = {"success": True, "data": result}
@ -141,8 +198,33 @@ def sell():
if price <= 0 or amount <= 0: if price <= 0 or amount <= 0:
raise ValueError("Price and amount must be positive") raise ValueError("Price and amount must be positive")
# 检查交易状态
should_simulate = True
simulation_reason = "未知原因"
if trader is None:
simulation_reason = "交易实例未初始化"
else:
try:
if not trader.is_trading_time():
simulation_reason = "当前为非交易时段"
else:
should_simulate = False
except Exception as e:
simulation_reason = f"检查交易时间发生错误: {str(e)}"
if should_simulate:
# 记录模拟交易信息
sim_message = f"模拟卖出 - {simulation_reason} - 代码: {code}, 价格: {price}, 数量: {amount}"
sim_logger.info(sim_message)
return jsonify({"success": True, "data": {"order_id": "simulation", "message": sim_message}}), 200
# 实际交易
logger.info(f"Executing sell order: code={code}, price={price}, amount={amount}") logger.info(f"Executing sell order: code={code}, price={price}, amount={amount}")
result = trader.sell(code, price, amount) result = execute_with_timeout(trader.sell, Config.TRADE_TIMEOUT, code, price, amount)
if result is None:
logger.error(f"Sell order timeout after {Config.TRADE_TIMEOUT} seconds")
return jsonify({"success": False, "error": "Operation timeout"}), 408
logger.info(f"Sell order result: {result}") logger.info(f"Sell order result: {result}")
response = {"success": True, "data": result} response = {"success": True, "data": result}
@ -230,6 +312,16 @@ def get_today_entrust():
abort(500, description="Internal server error") abort(500, description="Internal server error")
# 超时处理函数
def execute_with_timeout(func, timeout, *args, **kwargs):
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as executor:
future = executor.submit(func, *args, **kwargs)
try:
return future.result(timeout=timeout)
except TimeoutError:
return None
if __name__ == "__main__": if __name__ == "__main__":
logger.info(f"Server starting on {Config.HOST}:{Config.PORT}") logger.info(f"Server starting on {Config.HOST}:{Config.PORT}")
app.run(debug=Config.DEBUG, host=Config.HOST, port=Config.PORT) app.run(debug=Config.DEBUG, host=Config.HOST, port=Config.PORT)

View File

@ -6,7 +6,8 @@ from config import Config
from xtquant.xttrader import XtQuantTrader from xtquant.xttrader import XtQuantTrader
from xtquant.xttype import StockAccount from xtquant.xttype import StockAccount
from xtquant import xtconstant from xtquant import xtconstant
from xtquant.xtdata import get_instrument_detail from xtquant.xtdata import get_instrument_detail, get_trading_time
import datetime as dt
# 日志配置 # 日志配置
LOG_DIR = "log" LOG_DIR = "log"
@ -186,6 +187,40 @@ class XtTrader:
result = self.xt_trader.cancel_order_stock(self.account, int(entrust_no)) result = self.xt_trader.cancel_order_stock(self.account, int(entrust_no))
return {"cancel_result": result} return {"cancel_result": result}
def is_trading_time(self):
"""判断当前是否为交易时间,使用中国银行(601988.SH)作为判断标的
Returns:
bool: True 表示当前为交易时间False 表示当前休市
"""
try:
# 使用中国银行股票作为判断标的
code = "601988.SH"
# 获取交易时段
trading_periods = get_trading_time(code)
if not trading_periods:
logger.error("获取交易时段失败")
return False
# 获取当前时间
now = dt.datetime.now().time()
current_seconds = now.hour * 3600 + now.minute * 60 + now.second
# 检查是否在任一交易时段内
for period in trading_periods:
start_time = period[0] # 开始时间(秒)
end_time = period[1] # 结束时间(秒)
period_type = period[2] # 交易类型2-开盘竞价3-连续交易8-收盘竞价9-盘后定价)
if start_time <= current_seconds <= end_time and period_type in [2, 3, 8]:
return True
return False
except Exception as e:
logger.error(f"判断交易时间发生错误: {str(e)}")
return False
if __name__ == "__main__": if __name__ == "__main__":
trader = XtTrader() trader = XtTrader()
trader.login() trader.login()