添加在休市期间模拟买入卖出
This commit is contained in:
parent
c185fccd24
commit
24be80288a
@ -6,6 +6,9 @@ class Config:
|
||||
HOST = os.getenv('TRADE_SERVER_HOST', '0.0.0.0')
|
||||
DEBUG = False
|
||||
|
||||
# Trading settings
|
||||
TRADE_TIMEOUT = int(os.getenv('TRADE_TIMEOUT', 3)) # 交易超时时间(秒)
|
||||
|
||||
# Trading hours
|
||||
MARKET_OPEN_TIME = "09:20"
|
||||
MARKET_ACTIVE_TIME = "09:15"
|
||||
|
51
src/example_trade.py
Normal file
51
src/example_trade.py
Normal 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)
|
@ -9,8 +9,39 @@ from config import Config
|
||||
import logging
|
||||
import os
|
||||
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():
|
||||
log_dir = Config.LOG_DIR
|
||||
@ -48,6 +79,7 @@ def setup_logger():
|
||||
return logger
|
||||
|
||||
logger = setup_logger()
|
||||
sim_logger = setup_simulation_logger()
|
||||
|
||||
|
||||
def run_daily(time_str, job_func):
|
||||
@ -106,9 +138,34 @@ def buy():
|
||||
|
||||
if price <= 0 or amount <= 0:
|
||||
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}")
|
||||
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}")
|
||||
|
||||
response = {"success": True, "data": result}
|
||||
@ -140,9 +197,34 @@ def sell():
|
||||
|
||||
if price <= 0 or amount <= 0:
|
||||
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}")
|
||||
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}")
|
||||
|
||||
response = {"success": True, "data": result}
|
||||
@ -230,6 +312,16 @@ def get_today_entrust():
|
||||
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__":
|
||||
logger.info(f"Server starting on {Config.HOST}:{Config.PORT}")
|
||||
app.run(debug=Config.DEBUG, host=Config.HOST, port=Config.PORT)
|
||||
|
@ -6,7 +6,8 @@ from config import Config
|
||||
from xtquant.xttrader import XtQuantTrader
|
||||
from xtquant.xttype import StockAccount
|
||||
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"
|
||||
@ -186,6 +187,40 @@ class XtTrader:
|
||||
result = self.xt_trader.cancel_order_stock(self.account, int(entrust_no))
|
||||
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__":
|
||||
trader = XtTrader()
|
||||
trader.login()
|
||||
|
Loading…
x
Reference in New Issue
Block a user