完善UI操作逻辑

This commit is contained in:
2025-11-06 18:13:10 +08:00
parent 1ee8f0426e
commit d988f5eb48
9 changed files with 188 additions and 151 deletions
+4
View File
@@ -10,8 +10,12 @@ ActionDisableMarketData = "disable_market_data"
MarketDataEnabled = "market_data_enabled"
MarketDataDisabled = "market_data_disabled"
# 删除交易标的事件
ActionEventAddTradeTarget = "add_trade_target"
ResultEventTradeTargetAdded = "trade_target_added"
ActionEventDeleteTradeTarget = "delete_trade_target"
ResultEventTradeTargetDeleted = "trade_target_deleted"
# Pring Log
EventPrintLog = "print_log"
class EventBus:
+19
View File
@@ -0,0 +1,19 @@
from enum import Enum
from core.eventbus import EventPrintLog, event_bus
class LogLevel(Enum):
DEBUG = "DEBUG"
INFO = "INFO"
WARNING = "WARNING"
ERROR = "ERROR"
CRITICAL = "CRITICAL"
class LogData:
def __init__(self, level:LogLevel, message:str):
self.level = level
self.message = message
def PrintLog(level:LogLevel, message:str):
data = LogData(level, message)
event_bus.publish(EventPrintLog, data)
+51 -51
View File
@@ -1,6 +1,6 @@
# coding:utf-8
from core.strategy_db import TradeTarget
from core.eventbus import ActionDisableMarketData, ActionEnableMarketData, ActionEventDeleteTradeTarget, ActionEventDisableTrade, ActionEventEnableTrade, MarketDataUpdate, MarketDataEnabled, MarketDataDisabled, ResultEventTradeDisabled, ResultEventTradeEnabled, ResultEventTradeTargetDeleted, event_bus
from core.eventbus import ActionDisableMarketData, ActionEnableMarketData, ActionEventAddTradeTarget, ActionEventDeleteTradeTarget, ActionEventDisableTrade, ActionEventEnableTrade, MarketDataUpdate, MarketDataEnabled, MarketDataDisabled, ResultEventTradeDisabled, ResultEventTradeEnabled, ResultEventTradeTargetDeleted, event_bus
from xtquant.xttrader import XtQuantTrader
import time
from peewee import ModelSelect
@@ -15,6 +15,7 @@ from xtquant import xtdata
from xtquant.xttrader import XtQuantTraderCallback
import datetime
import core.ui as ui
from core.logger import PrintLog, LogLevel
# 量化核心控制对象
class SFGridController(XtQuantTraderCallback):
@@ -31,8 +32,8 @@ class SFGridController(XtQuantTraderCallback):
self.xt_trader: XtQuantTrader = XtQuantTrader(miniQmtPath, session_id)
self.xt_trader.register_callback(self)
self.xt_trader.start()
connect_result = self.xt_trader.connect()
print(f'- [{'成功' if self.xt_trader.connected else '失败'}]市场交易连接{connect_result}--: {miniQmtPath}')
self.xt_trader.connect()
PrintLog(LogLevel.INFO, f'- [{'成功' if self.xt_trader.connected else '失败'}]市场交易连接: {miniQmtPath}')
if self.xt_trader.connected == False:
self.inited: bool = False
return
@@ -40,9 +41,9 @@ class SFGridController(XtQuantTraderCallback):
self.inited = True
self.account= StockAccount(account_no, 'STOCK')
print(f'- [成功]交易账号对象初始化完成, 账号: {self.account.account_id}') # pyright: ignore[reportAttributeAccessIssue]
PrintLog(LogLevel.INFO, f'- [成功]交易账号对象初始化完成, 账号: {self.account.account_id}') # pyright: ignore[reportAttributeAccessIssue]
subscribe_result = self.xt_trader.subscribe(self.account)
print(f'- [{'成功' if subscribe_result == 0 else '失败'}:{subscribe_result}]交易状态订阅')
PrintLog(LogLevel.INFO, f'- [{'成功' if subscribe_result == 0 else '失败'}:{subscribe_result}]交易状态订阅')
if subscribe_result == 0:
self.inited = True
else:
@@ -52,15 +53,15 @@ class SFGridController(XtQuantTraderCallback):
self.init_instrument_pool(self.xt_trader, self.account) # type: ignore
self.seq = None
print('- [成功]三疯交易系统初始化完成')
# self.startMarketData()
PrintLog(LogLevel.INFO, '- [成功]三疯交易系统初始化完成')
self.startMarketData()
def registerEventHandler(self):
event_bus.subscribe(ActionEventEnableTrade, self.onEnableTrade)
event_bus.subscribe(ActionEventDisableTrade, self.onDisableTrade)
event_bus.subscribe(ActionEnableMarketData, self.onMarketDataEnabled)
event_bus.subscribe(ActionDisableMarketData, self.onMarketDataDisabled)
event_bus.subscribe("add_trade_target", self.onAddTradeTarget)
event_bus.subscribe(ActionEventAddTradeTarget, self.onAddTradeTarget)
event_bus.subscribe(ActionEventDeleteTradeTarget, self.onDeleteTradeTarget)
def onDeleteTradeTarget(self, id: int):
@@ -91,17 +92,20 @@ class SFGridController(XtQuantTraderCallback):
self.appUi.run()
def startMarketData(self):
print('- 启动市场数据订阅')
PrintLog(LogLevel.INFO, '- 启动市场数据订阅')
self.seq = xtdata.subscribe_whole_quote(['SH', 'SZ'], callback=self.onDataUpdate)
if self.seq == -1:
print('- 市场数据订阅失败')
PrintLog(LogLevel.ERROR, '- 市场数据订阅失败')
else:
event_bus.publish(MarketDataEnabled, True)
print(f'- 市场数据订阅成功, 订阅号={self.seq}')
PrintLog(LogLevel.INFO, f'- 市场数据订阅成功, 订阅号={self.seq}')
def stopMarketData(self):
print('- 停止市场数据订阅')
PrintLog(LogLevel.INFO, '- 停止市场数据订阅')
if self.seq is not None and self.seq > 0:
xtdata.unsubscribe_quote(self.seq)
event_bus.publish(MarketDataDisabled, False)
@@ -111,13 +115,13 @@ class SFGridController(XtQuantTraderCallback):
try:
stock_name = getInstrumentName(stock_code)
if not stock_name:
print(f'无法获取股票代码 {stock_code} 的名称,请检查代码是否正确')
PrintLog(LogLevel.ERROR, f'无法获取股票代码 {stock_code} 的名称,请检查代码是否正确')
return
# 检查是否已存在该标的
existing_target = strategy_db.TradeTarget.get_or_none(strategy_db.TradeTarget.stock_code == stock_code)
if existing_target:
print(f'交易标的 {stock_code} {stock_name} 已存在')
PrintLog(LogLevel.INFO, f'交易标的 {stock_code} {stock_name} 已存在')
return
new_target = strategy_db.TradeTarget.create(
@@ -134,7 +138,7 @@ class SFGridController(XtQuantTraderCallback):
current_order_type=''
)
new_target.save()
print(f'新增交易标的 {stock_code} {stock_name}, {new_target.id}')
PrintLog(LogLevel.INFO, f'新增交易标的 {stock_code} {stock_name}, {new_target.id}')
# 刷新标的持仓
pos = getStockPosition(stock_code, self.xt_trader, self.account) # type: ignore
strategy_db.TradeTarget.update(current_position=pos).where(strategy_db.TradeTarget.stock_code == stock_code).execute()
@@ -145,14 +149,14 @@ class SFGridController(XtQuantTraderCallback):
self.stock_trade_ctrl[stock_code] = stockTradeController
except Exception as e:
print(f'新增交易标的失败 {stock_code} {e}')
PrintLog(LogLevel.ERROR, f'新增交易标的失败 {stock_code} {e}')
def del_trade_target(self, id:int):
try:
# 检查标的是否存在
if id not in self.instrument_pool:
print(f"交易标的 ID {id} 不存在")
PrintLog(LogLevel.ERROR, f"交易标的 ID {id} 不存在")
return
target: strategy_db.TradeTarget = self.instrument_pool[id]
@@ -171,23 +175,27 @@ class SFGridController(XtQuantTraderCallback):
# 刷新标的池
self.refresh_targets()
print(f"已删除交易标的: {target.stock_code} - {target.stock_name}")
PrintLog(LogLevel.INFO, f"已删除交易标的: {target.stock_code} - {target.stock_name}")
except Exception as e:
print(f"删除交易标的失败 ID {id}: {str(e)}")
PrintLog(LogLevel.ERROR, f"删除交易标的失败 ID {id}: {str(e)}")
def init_instrument_pool(self, xtTrader:XtQuantTrader, account:StockAccount):
self.refresh_targets()
for id in self.instrument_pool:
target:TradeTarget = self.instrument_pool[id]
status = "新建" if target.status == 0 else "已建初始仓"
PrintLog(LogLevel.INFO, f' [序号-{id}] 股票代码: {target.stock_code}-{target.stock_name} 当前持仓: {getStockPosition(target.stock_code, self.xt_trader, self.account)} 网格索引: {target.grid_index} 基准价格 {sfgrid_constants.grid_price[target.grid_index]} 状态: {status} 启用交易线程: {'自动交易中' if target.enabled else '交易已停止'}') # type: ignore
tradeTarget:strategy_db.TradeTarget = self.instrument_pool[id]
tradeTarget.current_position = getStockPosition(tradeTarget.stock_code, xtTrader, account) # type: ignore
result = tradeTarget.save()
print(f' |- 同步当前持仓信息 {tradeTarget.stock_code}, {tradeTarget.current_position}, result = {result}')
PrintLog(LogLevel.INFO, f' |- 同步[{target.stock_code}-{target.stock_name}]持仓信息[{'成功' if result == 1 else '失败'}]')
stockTradeController = SFGridStrategy(tradeTarget, self.xt_trader, self.account) # type: ignore
self.stock_trade_ctrl[tradeTarget.stock_code] = stockTradeController
event_bus.publish(MarketDataUpdate, tradeTarget)
print(f'- [成功]交易标的信息初始化, 共 {len(self.instrument_pool)} 个标的')
PrintLog(LogLevel.INFO, f'- [成功]交易标的信息初始化, 共 {len(self.instrument_pool)} 个标的')
def refresh_targets(self):
@@ -197,55 +205,47 @@ class SFGridController(XtQuantTraderCallback):
for temp in results:
result :strategy_db.TradeTarget = temp
self.instrument_pool[result.get_id()] = result
self.print_pool()
def print_pool(self):
print("- [信息]标的池信息")
for id in self.instrument_pool:
target: strategy_db.TradeTarget = self.instrument_pool[id]
status = "新建" if target.status == 0 else "已建初始仓"
print(f' [序号-{id}] 股票代码: {target.stock_code}-{target.stock_name} 当前持仓: {getStockPosition(target.stock_code, self.xt_trader, self.account)} 网格索引: {target.grid_index} 基准价格 {sfgrid_constants.grid_price[target.grid_index]} 状态: {status} 启用交易线程: {'自动交易中' if target.enabled else '交易已停止'}') # type: ignore
def print_position_info(self):
positions:list[XtPosition] = self.xt_trader.query_stock_positions(self.account)
if positions:
print("\n- 持仓信息")
PrintLog(LogLevel.INFO, "\n- 持仓信息")
for temp in positions:
pos : XtPosition = temp
if pos.volume <=0:
continue
print(f"股票代码: {pos.stock_code}-{getInstrumentName(pos.stock_code)}")
print(f"总持仓: {pos.volume}")
print(f"可用持仓: {pos.can_use_volume}")
print(f"持仓成本: {pos.avg_price}")
print("---")
PrintLog(LogLevel.INFO, f"股票代码: {pos.stock_code}-{getInstrumentName(pos.stock_code)}")
PrintLog(LogLevel.INFO, f"总持仓: {pos.volume}")
PrintLog(LogLevel.INFO, f"可用持仓: {pos.can_use_volume}")
PrintLog(LogLevel.INFO, f"持仓成本: {pos.avg_price}")
PrintLog(LogLevel.INFO, "---")
else:
print("\n当前无持仓")
PrintLog(LogLevel.INFO, "\n当前无持仓")
def print_account_info(self):
temp = self.xt_trader.query_stock_asset(self.account)
asset: XtAsset = temp # type: ignore
print(f"=== 账户信息 {self.account.account_id} ===") # type: ignore
print(f"可用资金: {asset.cash}")
print(f"总资产: {asset.total_asset}")
print(f"证券市值: {asset.market_value}")
PrintLog(LogLevel.INFO, f"=== 账户信息 {self.account.account_id} ===") # type: ignore
PrintLog(LogLevel.INFO, f"可用资金: {asset.cash}")
PrintLog(LogLevel.INFO, f"总资产: {asset.total_asset}")
PrintLog(LogLevel.INFO, f"证券市值: {asset.market_value}")
def print_stock_orders(self):
orders = self.xt_trader.query_stock_orders(self.account, cancelable_only=True)
if orders:
print("\n=== 委托信息 ===")
PrintLog(LogLevel.INFO, "\n=== 委托信息 ===")
for order in orders:
print(f"委托编号: {order.order_id}")
print(f"股票代码: {order.stock_code} {getInstrumentName(order.stock_code)}")
print(f"委托方向: {order.offset_flag} ")
print(f"委托价格: {order.price}")
print(f"委托数量: {order.order_volume}")
print(f"已成交数量: {order.traded_volume}")
print(f"委托状态: {order.order_status} ")
print("---")
PrintLog(LogLevel.INFO, f"委托编号: {order.order_id}")
PrintLog(LogLevel.INFO, f"股票代码: {order.stock_code} {getInstrumentName(order.stock_code)}")
PrintLog(LogLevel.INFO, f"委托方向: {order.offset_flag} ")
PrintLog(LogLevel.INFO, f"委托价格: {order.price}")
PrintLog(LogLevel.INFO, f"委托数量: {order.order_volume}")
PrintLog(LogLevel.INFO, f"已成交数量: {order.traded_volume}")
PrintLog(LogLevel.INFO, f"委托状态: {order.order_status} ")
PrintLog(LogLevel.INFO, "---")
else:
print("\n当前无委托记录")
PrintLog(LogLevel.INFO, "\n当前无委托记录")
# 初始化指定标的交易控制器
@@ -259,7 +259,7 @@ class SFGridController(XtQuantTraderCallback):
self.instrument_pool[id] = tradeTarget
event_bus.publish(ResultEventTradeEnabled, tradeTarget)
else:
print(f"\t创建标的交易控制器 {tradeTarget.stock_code} {getInstrumentName(tradeTarget.stock_code)}")
PrintLog(LogLevel.INFO, f"\t创建标的交易控制器 {tradeTarget.stock_code} {getInstrumentName(tradeTarget.stock_code)}")
def pause_stock_trade(self, id: int):
+77 -58
View File
@@ -1,10 +1,13 @@
from peewee import IntegerField
from core import strategy_db
from core.eventbus import MarketDataUpdate, event_bus
from core.strategy_db import TradeTarget
from core.strategy_db import OrderTypeBuy, OrderTypeInit, OrderTypeSell, TradeTarget
from core.util import queryPendingOrder
from xtquant import xttrader, xtconstant
from xtquant.xttype import StockAccount, XtOrder, XtOrderResponse, XtTrade
from xtquant.xttype import StockAccount, XtOrder, XtTrade
import sfgrid_constants
import threading
@@ -16,8 +19,9 @@ class SFGridStrategy:
self.xt_trader: xttrader.XtQuantTrader = xt_trader
self.account:StockAccount = account
self.enabledTrading(bool(tradeTarget.enabled)) # 修复类型兼容性问题
event_bus.publish(MarketDataUpdate, self.tradeTarget)
self.dataUpdateLock = threading.Lock()
print(f'标的{self.tradeTarget.targetName()}交易启动状态 {self.tradeTarget.enabled}')
def enabledTrading(self, enabled: bool) -> TradeTarget:
@@ -25,26 +29,11 @@ class SFGridStrategy:
self.saveProxy()
if enabled:
self.refreshPlanPrice()
print(f" |- 标的{self.tradeTarget.targetName()}交易启动, 持仓量:{self.tradeTarget.current_position}")
if self.tradeTarget.status == 0: # 未建仓
print(f" |- 标的{self.tradeTarget.targetName()}初始状态,检查订单")
orders = queryPendingOrder(str(self.tradeTarget.stock_code),self.getName(), self.xt_trader,self.account)
if len([order for order in orders if order.order_type == xtconstant.STOCK_BUY and order.price == sfgrid_constants.grid_price[1]]) > 0:
# 已存在未交易的多单
order = [order for order in orders if order.order_type == xtconstant.STOCK_BUY and order.price == sfgrid_constants.grid_price[1]][0]
print(f' |- 已存在未交易的建仓单,不重复下单:{order.order_id}')
else:
# 建仓
self.tradeTarget.grid_index = 1 # pyright: ignore[reportAttributeAccessIssue]
self.tradeTarget.current_order_no = self.xt_trader.order_stock_async(
self.account,
str(self.tradeTarget.stock_code),
xtconstant.STOCK_BUY,
sfgrid_constants.grid_volume,
xtconstant.FIX_PRICE,
sfgrid_constants.grid_price[int(self.tradeTarget.grid_index)], # type: ignore
self.getName(), strategy_db.OrderTypeInit)
print(f"|- 标的{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name} 建初始仓 买单已发出 InitBuyOrderSeq: {self.tradeTarget.current_order_no} Price: {sfgrid_constants.grid_price[int(self.tradeTarget.grid_index)]} Volume: {sfgrid_constants.grid_volume}\n") # type: ignore
print(f" |- 标的{self.tradeTarget.targetName()}初始状态, 设置网格序号 1")
self.tradeTarget.grid_index = 1 # pyright: ignore[reportAttributeAccessIssue]
else: # 已建仓
# 交易阶段,检查仓位,检查现有订单
print(f" |- 标的{self.tradeTarget.targetName()}已有仓位或非初始状态 无需建初始仓 当前仓位: {self.tradeTarget.current_position} 状态: {self.tradeTarget.status}")
@@ -52,7 +41,9 @@ class SFGridStrategy:
if minRequirePosition <= int(self.tradeTarget.current_position): # type: ignore
print(f' |- 仓位检查: 持仓需求充足, (gridVolume*gridIndex)={minRequirePosition}, 当前持仓:{self.tradeTarget.current_position}')
else:
print(f' |- 仓位检查: 持仓需求不足, (gridVolume*gridIndex)={minRequirePosition}, 当前持仓:{self.tradeTarget.current_position}')
print(f' |- 仓位检查: 持仓需求不足, (gridVolume*gridIndex)={minRequirePosition}, 当前持仓:{self.tradeTarget.current_position}, 交易启动失败')
self.tradeTarget.enabled = False # type: ignore
self.saveProxy()
else:
print(f" |- 标的{self.tradeTarget.targetName()}交易监控暂停")
return self.tradeTarget
@@ -68,61 +59,72 @@ class SFGridStrategy:
self.tradeTarget.market_price = lastPrice # type: ignore
self.saveProxy()
if not(self.tradeTarget.enabled and self.tradeTarget.status == 1): # 建仓,常规自动交易
if not self.tradeTarget.enabled: # 建仓,自动交易暂停
print(f"|- 市价更新[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}] - 未建仓或交易监控暂停,不进行自动交易")
return
self.dataUpdateLock.acquire()
print(f'|- 市价更新[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}] - LOCKED')
try:
index = self.tradeTarget.grid_index
price = -1 if index>=len(sfgrid_constants.grid_price) else sfgrid_constants.grid_price[int(index)] # pyright: ignore[reportArgumentType]
lowPrice = -1 if index+1>=len(sfgrid_constants.grid_price) else sfgrid_constants.grid_price[int(index) + 1] # pyright: ignore[reportArgumentType]
highPrice = sfgrid_constants.grid_price[int(index) - 1] # pyright: ignore[reportArgumentType]
if lastPrice <= lowPrice: # 下下方多单
try:
orderPrice:float = -1
orderType = -1
index: int = self.tradeTarget.grid_index # pyright: ignore[reportAssignmentType]
orderRemark= ""
gridBasePrice = -1 if index>=len(sfgrid_constants.grid_price) or index < 0 else sfgrid_constants.grid_price[int(index)] # pyright: ignore[reportArgumentType]
if self.tradeTarget.enabled and self.tradeTarget.status == 0: # 已启用,未建仓,建仓
orderPrice = sfgrid_constants.grid_price[index]
orderType = xtconstant.STOCK_BUY
orderRemark = OrderTypeInit
if self.tradeTarget.enabled and self.tradeTarget.status == 1: # 已启用,已建仓,网格单
lowPrice = -1 if index+1>=len(sfgrid_constants.grid_price) else sfgrid_constants.grid_price[int(index) + 1] # pyright: ignore[reportArgumentType]
highPrice = sfgrid_constants.grid_price[index - 1]
if lastPrice <= lowPrice: # 下下方多单
orderPrice = lowPrice
orderType = xtconstant.STOCK_BUY
orderRemark = OrderTypeBuy
elif lastPrice >= highPrice: # 下上方空单
orderPrice = highPrice
orderType = xtconstant.STOCK_SELL
orderRemark = OrderTypeSell
if orderType != -1:
orders = queryPendingOrder(str(self.tradeTarget.stock_code), self.getName(), self.xt_trader, self.account)
if len([order for order in orders if order.order_type == xtconstant.STOCK_BUY and order.price == lowPrice]) > 0:
if len([order for order in orders if order.order_type == orderType and order.price == orderPrice]) > 0:
# 已存在未交易的多单
print(f' |- 已存在未交易的多单,不重复下单')
print(f' |- 已存在未交易的{"多单" if orderType == xtconstant.STOCK_BUY else "空单"},不重复下单')
else:
print(f' |- 下网格多单')
print(f' |- 下网格{"多单" if orderType == xtconstant.STOCK_BUY else "空单"}')
self.tradeTarget.current_order_no = self.xt_trader.order_stock_async(
self.account,
str(self.tradeTarget.stock_code),
xtconstant.STOCK_BUY,
orderType,
sfgrid_constants.grid_volume,
xtconstant.FIX_PRICE,
lowPrice,
orderPrice,
self.getName(), # strategy_name
strategy_db.OrderTypeBuy # remark # type: ignore
orderRemark # remark # type: ignore
)
self.tradeTarget.plan_buy_price = float(lowPrice) # type: ignore
print(f' |- 下网格多单号 {self.tradeTarget.current_order_no}, 网格基准价 {price}, 下单价 {lowPrice}, 下单量 {sfgrid_constants.grid_volume}')
elif lastPrice == highPrice: # 下上方空单
orders = queryPendingOrder(str(self.tradeTarget.stock_code), self.getName(), self.xt_trader, self.account)
if len([order for order in orders if order.order_type == xtconstant.STOCK_SELL and order.price == highPrice]) > 0:
# 已存在未交易的空单
print(f' |- 已存在未交易的空单,不重复下单')
else:
print(f' |- 下网格空单')
self.tradeTarget.current_order_no = self.xt_trader.order_stock_async(
self.account,
str(self.tradeTarget.stock_code),
xtconstant.STOCK_SELL,
sfgrid_constants.grid_volume,
xtconstant.FIX_PRICE,
highPrice,
self.getName(),
strategy_db.OrderTypeBuy) # type: ignore
self.tradeTarget.plan_sell_price = float(highPrice) # type: ignore
print(f' |- 下网格空单号 {self.tradeTarget.current_order_no}, 网格基准价 {price}, 下单价 {highPrice}, 下单量 {sfgrid_constants.grid_volume}')
orderTypeName = ""
if orderRemark == OrderTypeBuy:
orderTypeName = "多单"
elif orderRemark == OrderTypeSell:
orderTypeName = "空单"
elif orderRemark == OrderTypeInit:
orderTypeName = "建仓单"
print(f' |- {orderTypeName}委托, 单号 {self.tradeTarget.current_order_no}, 网格基准价 {gridBasePrice}, 下单价 {orderPrice}, 下单量 {sfgrid_constants.grid_volume}')
finally:
print(f'|- 市价更新[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}] - release lock')
self.saveProxy()
self.dataUpdateLock.release()
print(f'|- 市价更新[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}] - END')
def onAsyncOrderResponse(self, order:XtOrder):
print(f' |- 委托回调[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{order.order_id}]:START')
self.dataUpdateLock.acquire()
@@ -153,6 +155,8 @@ class SFGridStrategy:
self.tradeTarget.current_position = int(self.tradeTarget.current_position) + trade.traded_volume # 当前持仓数,账户原有持仓不在策略范围内 # type: ignore
self.tradeTarget.last_trade_price = float(trade.traded_price) # type: ignore
self.tradeTarget.grid_index = 1 # type: ignore
self.tradeTarget.plan_buy_price = float(sfgrid_constants.grid_price[2]) # type: ignore
self.tradeTarget.plan_sell_price = float(sfgrid_constants.grid_price[0]) # type: ignore
self.tradeTarget.status = 1 # type: ignore
self.saveProxy()
print(f"|- 标的{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name} 建初始仓订单ID: {self.tradeTarget.current_order_no}已成交 ")
@@ -164,7 +168,6 @@ class SFGridStrategy:
self.tradeTarget.current_position = int(self.tradeTarget.current_position) - trade.traded_volume # type: ignore
self.tradeTarget.last_trade_price = float(trade.traded_price) # type: ignore
self.tradeTarget.grid_index = int(self.tradeTarget.grid_index) - 1 # type: ignore
self.saveProxy()
print(f"|- 标的{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name} 上涨 卖单已成交 订单ID: {self.tradeTarget.current_order_no} Price: {sfgrid_constants.grid_price[int(self.tradeTarget.grid_index)]} Volume: {sfgrid_constants.grid_volume} 手续费: {trade.commission}\n") # type: ignore
print(f' 成交价: {trade.traded_price} 成交量: {trade.traded_volume}')
print(f' 当前持仓: {self.tradeTarget.current_position}')
@@ -174,7 +177,6 @@ class SFGridStrategy:
self.tradeTarget.current_position = int(self.tradeTarget.current_position) + trade.traded_volume # type: ignore
self.tradeTarget.last_trade_price = float(trade.traded_price) # type: ignore
self.tradeTarget.grid_index = int(self.tradeTarget.grid_index) + 1 # type: ignore
self.saveProxy()
print(f"|- 标的{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name} 下跌 买单已成交 订单ID: {self.tradeTarget.current_order_no} Price: {trade.traded_price} Volume: {sfgrid_constants.grid_volume} 手续费: {trade.commission}")
print(f' 成交价: {trade.traded_price} 成交量: {trade.traded_volume}')
print(f' 当前持仓: {self.tradeTarget.current_position}')
@@ -183,10 +185,27 @@ class SFGridStrategy:
# 打印订单信息和订单状态
print(f'|- 非策略内部订单,或订单状态不满足监控条件 {trade.order_id} {trade.stock_code}-{trade.instrument_name} {trade.commission}')
finally:
self.refreshPlanPrice()
print(f' |- 委托成交通知[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{trade.order_id}]:release lock')
self.dataUpdateLock.release()
print(f' |- 委托成交通知[{self.tradeTarget.stock_code}-{self.tradeTarget.stock_name}-{trade.order_id}]:END')
def refreshPlanPrice(self):
if self.tradeTarget.status == 1:
buyIdx: int = self.tradeTarget.grid_index + 1 # pyright: ignore[reportAssignmentType]
sellIdx: int = self.tradeTarget.grid_index - 1
if self.tradeTarget.grid_index > 0: # 可以下空单
self.tradeTarget.plan_sell_price = float(sfgrid_constants.grid_price[sellIdx]) # pyright: ignore[reportAttributeAccessIssue]
else:
self.tradeTarget.plan_sell_price = -1.0 # type: ignore
if self.tradeTarget.grid_index < len(sfgrid_constants.grid_price) - 1:
self.tradeTarget.plan_buy_price = float(sfgrid_constants.grid_price[buyIdx]) # pyright: ignore[reportAttributeAccessIssue]
else:
self.tradeTarget.plan_buy_price = -1.0 # pyright: ignore[reportAttributeAccessIssue]
else:
self.tradeTarget.plan_buy_price = 10.0 # type: ignore
self.tradeTarget.plan_sell_price = -1.0 # type: ignore
self.saveProxy()
def getName(self):
return "SFGRID"
+1 -1
View File
@@ -13,7 +13,7 @@ class BaseModel(Model):
OrderTypeBuy = f'{xtconstant.STOCK_BUY}' # 买
OrderTypeSell = f'{xtconstant.STOCK_SELL}' # 卖
OrderTypeInit = "0" # 建仓
OrderTypeNone = "None" # 建仓
OrderTypeNone = "None"
# 定义Target类,对应targets表
class TradeTarget(BaseModel):
+34 -33
View File
@@ -3,7 +3,8 @@ from tkinter import ttk, messagebox, filedialog
from datetime import datetime
import threading
import time
from core.eventbus import ActionDisableMarketData, ActionEnableMarketData, ActionEventDeleteTradeTarget, ActionEventDisableTrade, ActionEventEnableTrade, MarketDataUpdate, MarketDataEnabled, MarketDataDisabled, ResultEventTradeDisabled, ResultEventTradeEnabled, ResultEventTradeTargetDeleted, event_bus
import core.eventbus as eBus
from core.logger import LogData, LogLevel
from core.strategy_db import TradeTarget
import configparser
import sfgrid_constants
@@ -28,12 +29,13 @@ class TradeTargetUI:
# 不再自动启动刷新线程,由市场数据开关控制
def registerEventHandler(self):
event_bus.subscribe(MarketDataUpdate, self.onTradeTargetUpdated)
event_bus.subscribe(ResultEventTradeEnabled, self.onTradeEnabled)
event_bus.subscribe(ResultEventTradeDisabled, self.onTradeDisabled)
event_bus.subscribe(MarketDataEnabled, self.onMarketDataToggled)
event_bus.subscribe(MarketDataDisabled, self.onMarketDataToggled)
event_bus.subscribe(ResultEventTradeTargetDeleted, self.onTradeTargetDeleted)
eBus.event_bus.subscribe(eBus.MarketDataUpdate, self.onTradeTargetUpdated)
eBus.event_bus.subscribe(eBus.ResultEventTradeEnabled, self.onTradeEnabled)
eBus.event_bus.subscribe(eBus.ResultEventTradeDisabled, self.onTradeDisabled)
eBus.event_bus.subscribe(eBus.MarketDataEnabled, self.onMarketDataToggled)
eBus.event_bus.subscribe(eBus.MarketDataDisabled, self.onMarketDataToggled)
eBus.event_bus.subscribe(eBus.ResultEventTradeTargetDeleted, self.onTradeTargetDeleted)
eBus.event_bus.subscribe(eBus.EventPrintLog, self.onLog)
def start_refresh_thread(self):
"""启动刷新线程"""
@@ -59,11 +61,11 @@ class TradeTargetUI:
if id in self.data:
del self.data[id]
# 添加日志
self.add_log("INFO", f"交易标的已删除,ID: {id}")
self.add_log(LogLevel.INFO, f"交易标的已删除,ID: {id}")
def onMarketDataToggled(self, data:bool):
self.market_data_enabled = self.market_data_switch_var.get()
self.add_log("INFO", "市场数据监听已" + ("启用" if data else "禁用"))
self.add_log(LogLevel.INFO, "市场数据监听已" + ("启用" if data else "禁用"))
# 同步UI刷新线程状态
if data:
self.start_ui_refresh()
@@ -71,10 +73,10 @@ class TradeTargetUI:
self.stop_ui_refresh()
def onTradeEnabled(self, target:TradeTarget):
self.add_log("INFO", f"交易启用: {target.stock_code} - {target.stock_name}")
self.add_log(LogLevel.INFO, f"交易启用: {target.stock_code} - {target.stock_name}")
def onTradeDisabled(self, target:TradeTarget):
self.add_log("INFO", f"交易禁用: {target.stock_code} - {target.stock_name}")
self.add_log(LogLevel.INFO, f"交易禁用: {target.stock_code} - {target.stock_name}")
def onTradeTargetUpdated(self, target: TradeTarget):
@@ -137,11 +139,11 @@ class TradeTargetUI:
print(f'市场数据监听开关')
self.market_data_enabled = self.market_data_switch_var.get()
if self.market_data_enabled:
event_bus.publish(ActionEnableMarketData, True)
eBus.event_bus.publish(eBus.ActionEnableMarketData, True)
# 同步开启UI刷新线程
self.start_ui_refresh()
else:
event_bus.publish(ActionDisableMarketData, True)
eBus.event_bus.publish(eBus.ActionDisableMarketData, True)
# 同步关闭UI刷新线程
self.stop_ui_refresh()
@@ -150,14 +152,14 @@ class TradeTargetUI:
if not self.refresh_thread_running:
self.refresh_thread_running = True
self.start_refresh_thread()
self.add_log("INFO", "UI刷新线程已启动")
self.add_log(LogLevel.INFO, "UI刷新线程已启动")
def stop_ui_refresh(self):
"""停止UI刷新线程"""
if self.refresh_thread_running:
self.stop_refresh_thread()
self.refresh_thread_running = False
self.add_log("INFO", "UI刷新线程已停止")
self.add_log(LogLevel.INFO, "UI刷新线程已停止")
def create_menu_bar(self):
"""创建菜单栏"""
@@ -330,7 +332,7 @@ class TradeTargetUI:
if selected:
item = selected[0]
values = self.trade_table.item(item)['values']
self.add_log("DEBUG", f"双击查看详情: {values[0]} - {values[1]}")
self.add_log(LogLevel.DEBUG, f"双击查看详情: {values[0]} - {values[1]}")
def get_selected_target(self):
"""获取选中的交易标的"""
@@ -351,10 +353,6 @@ class TradeTargetUI:
return None
def onLog(self, level: str, message: str):
"""接收外部日志消息并显示在日志组件中"""
self.add_log(level, message)
def start_selected_trade(self):
"""启动选中的交易"""
target = self.get_selected_target()
@@ -374,12 +372,12 @@ class TradeTargetUI:
if result:
target.enabled = True # type: ignore
event_bus.publish(ActionEventEnableTrade, target.get_id())
eBus.event_bus.publish(eBus.ActionEventEnableTrade, target.get_id())
# self.add_log("INFO", f"已启动交易: {target.stock_code} - {target.stock_name}")
# messagebox.showinfo("启动成功", f"已启动 {target.stock_code} ({target.stock_name}) 的交易")
def on_trade_enabled(self, target: TradeTarget):
event_bus.publish(ActionEventEnableTrade, target)
eBus.event_bus.publish(eBus.ActionEventEnableTrade, target)
def pause_selected_trade(self):
"""暂停选中的交易"""
@@ -400,7 +398,7 @@ class TradeTargetUI:
if result:
target.enabled = False # type: ignore
event_bus.publish(ActionEventDisableTrade, target.get_id())
eBus.event_bus.publish(eBus.ActionEventDisableTrade, target.get_id())
# self.add_log("INFO", f"已暂停交易: {target.stock_code} - {target.stock_name}")
# messagebox.showinfo("暂停成功", f"已暂停 {target.stock_code} ({target.stock_name}) 的交易")
@@ -421,8 +419,8 @@ class TradeTargetUI:
if result:
# 通过事件总线发出删除动作
event_bus.publish(ActionEventDeleteTradeTarget, target.get_id())
self.add_log("INFO", f"已发送删除请求: {target.stock_code} - {target.stock_name}")
eBus.event_bus.publish(eBus.ActionEventDeleteTradeTarget, target.get_id())
self.add_log(LogLevel.INFO, f"已发送删除请求: {target.stock_code} - {target.stock_name}")
def add_trade_target(self):
"""添加新的交易标的"""
@@ -463,7 +461,7 @@ class TradeTargetUI:
return
# 发布事件通知主控制器添加标的
event_bus.publish("add_trade_target", stock_code)
eBus.event_bus.publish(eBus.ActionEventAddTradeTarget, stock_code)
add_window.destroy()
def cancel_add():
@@ -476,7 +474,7 @@ class TradeTargetUI:
# 绑定回车键确认
stock_code_entry.bind('<Return>', lambda event: confirm_add())
self.add_log("INFO", "点击添加交易标的按钮")
self.add_log(LogLevel.INFO, "点击添加交易标的按钮")
def toggle_log_panel(self):
"""切换日志面板的显示/隐藏"""
@@ -514,18 +512,21 @@ class TradeTargetUI:
values = self.trade_table.item(item)['values']
if values and values[0] in selected_values:
self.trade_table.selection_add(item)
def add_log(self, level, message):
def onLog(self, data:LogData):
self.add_log(data.level, data.message)
def add_log(self, level:LogLevel, message):
"""添加日志记录"""
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self.log_table.insert('', 0, values=(timestamp, level, message))
self.log_table.insert('', 0, values=(timestamp, level.value, message))
def clear_logs(self):
"""清空日志记录"""
# 删除所有日志项
for item in self.log_table.get_children():
self.log_table.delete(item)
self.add_log("INFO", "日志已清空")
self.add_log(LogLevel.DEBUG, "日志已清空")
def system_settings(self):
"""系统设置"""
@@ -813,12 +814,12 @@ class TradeTargetUI:
sfgrid_constants.initConfig()
messagebox.showinfo("成功", f"配置已保存!\n网格价格序列: {grid_price_str}\n部分配置可能需要重启程序后生效。")
self.add_log("INFO", f"系统配置已更新 - 网格数量: {len(grid_prices)}")
self.add_log(LogLevel.INFO, f"系统配置已更新 - 网格数量: {len(grid_prices)}")
settings_window.destroy()
except Exception as e:
messagebox.showerror("错误", f"保存配置失败:{str(e)}")
self.add_log("ERROR", f"保存配置失败: {str(e)}")
self.add_log(LogLevel.ERROR, f"保存配置失败: {str(e)}")
def cancel_settings():
"""取消设置"""