独立配置

This commit is contained in:
2025-11-12 15:08:56 +08:00
parent 8ab5d83b1a
commit 1193dc2f69
9 changed files with 51 additions and 46 deletions
-10
View File
@@ -3,15 +3,10 @@ import configparser
from pathlib import Path from pathlib import Path
import sys import sys
# miniQMTPath = r'D:\\Programs\\DTQMT_MN\\userdata_mini' # miniQMT软件的安装路径
miniQMTPath = r'D:\\Programs\\DTQMT\\userdata_mini' # miniQMT软件的安装路径 miniQMTPath = r'D:\\Programs\\DTQMT\\userdata_mini' # miniQMT软件的安装路径
# miniQMTPath = '' # miniQMTPath = ''
# grid_price = [11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1] # 网格价格设置,从高到低
grid_price:List[float] = [] # 网格价格设置,从高到低
grid_volume:int = 100 # 每个网格的交易手数
account_no:str = '99082560' account_no:str = '99082560'
console_log = True console_log = True
# account_no:str = '89009170' # 交易账号
def get_config_path() -> Path: def get_config_path() -> Path:
"""获取配置文件的正确路径(兼容开发环境和打包后的可执行文件)""" """获取配置文件的正确路径(兼容开发环境和打包后的可执行文件)"""
@@ -31,8 +26,6 @@ def create_default_config():
config = configparser.ConfigParser() config = configparser.ConfigParser()
config['config'] = { config['config'] = {
'miniQMTPath': r'D:/Programs/QMT/userdata_mini', 'miniQMTPath': r'D:/Programs/QMT/userdata_mini',
'grid_price': '11.0,10.0,9.0,8.0,7.0,6.0,5.0,4.0,3.0,2.0,1.0,0.0',
'grid_volume': '100',
'account_no': '00000000' 'account_no': '00000000'
} }
config_path = get_config_path() config_path = get_config_path()
@@ -53,7 +46,4 @@ def initConfig():
config = configparser.ConfigParser() config = configparser.ConfigParser()
config.read(config_path, encoding='utf-8') config.read(config_path, encoding='utf-8')
miniQMTPath = config.get('config','miniQMTPath') miniQMTPath = config.get('config','miniQMTPath')
str_list = config.get('config','grid_price').split(',')
grid_price = [float(item) for item in str_list]
grid_volume = config.getint('config','grid_volume')
account_no = config.get('config','account_no') account_no = config.get('config','account_no')
+2 -10
View File
@@ -1,8 +1,7 @@
import tkinter as tk import tkinter as tk
from tkinter import ttk from tkinter import ttk
from core.logger import LogLevel, PrintLog, LogData from core.logger import LogLevel, PrintLog
from core.sfgrid.sfgrid_ui import TradeTargetUI from core.sfgrid.sfgrid_ui import TradeTargetUI
from core.eventbus import event_bus, EventPrintLog
class MainWindow: class MainWindow:
@@ -17,13 +16,10 @@ class MainWindow:
self.strategy_frames = {} self.strategy_frames = {}
# 日志面板可见性标志 # 日志面板可见性标志
self.log_visible = False self.log_visible = False
# 创建界面 # 创建界面
print(f'创建界面')
self.create_ui() self.create_ui()
# 订阅日志事件
event_bus.subscribe(EventPrintLog, self.on_log_event)
def create_ui(self): def create_ui(self):
"""创建UI界面""" """创建UI界面"""
@@ -212,10 +208,6 @@ class MainWindow:
if result: if result:
self.root.destroy() self.root.destroy()
def on_log_event(self, log_data: LogData):
"""处理日志事件"""
self.add_log(log_data.level, log_data.message)
def run(self): def run(self):
"""运行程序""" """运行程序"""
self.root.mainloop() self.root.mainloop()
+2
View File
@@ -131,6 +131,8 @@ class QmtV(XtQuantTraderCallback):
def startMarketDataSubscription(self): def startMarketDataSubscription(self):
self.subscriptionId = xtdata.subscribe_whole_quote(['SH', 'SZ'], self.onDataUpdate) self.subscriptionId = xtdata.subscribe_whole_quote(['SH', 'SZ'], self.onDataUpdate)
PrintLog(LogLevel.INFO, f'- [市场数据订阅成功-{self.subscriptionId}]')
def stopMarketDataSubscription(self): def stopMarketDataSubscription(self):
PrintLog(LogLevel.INFO, '- 停止市场数据订阅') PrintLog(LogLevel.INFO, '- 停止市场数据订阅')
-2
View File
@@ -4,8 +4,6 @@ ActionEventAddTradeTarget = "add_trade_target"
ResultEventTradeTargetAdded = "trade_target_added" ResultEventTradeTargetAdded = "trade_target_added"
ActionEventDeleteTradeTarget = "delete_trade_target" ActionEventDeleteTradeTarget = "delete_trade_target"
ResultEventTradeTargetDeleted = "trade_target_deleted" ResultEventTradeTargetDeleted = "trade_target_deleted"
# 网格修正事件
ActionEventGridFix = "grid_fix"
# 定义事件处理函数 # 定义事件处理函数
ActionEventEnableTrade = "enable_trade" ActionEventEnableTrade = "enable_trade"
+2
View File
@@ -0,0 +1,2 @@
GridTypePercentage = "Percentage"
GridTypeFixed = "PriceBreak"
+22
View File
@@ -19,8 +19,30 @@ class SFGridTradeTarget(BaseModel):
status = IntegerField(default=0) # 0表示新标的,1表示已建初始仓,正常交易中 status = IntegerField(default=0) # 0表示新标的,1表示已建初始仓,正常交易中
enabled = BooleanField(default=False) # 是否启动交易线程 enabled = BooleanField(default=False) # 是否启动交易线程
grid_start_price = FloatField(default=10.0) # 基线价格
grid_size = FloatField(default=0.1) # 网格价位差
grid_volume = IntegerField(default=100) # 网格交易量
grid_upper_count = IntegerField(default=1) # 基线价格上方网格数
grid_lower_count = IntegerField(default=10) # 基线价格下方网格数
def targetName(self): def targetName(self):
return f'{self.stock_name}[{self.stock_code}]' return f'{self.stock_name}[{self.stock_code}]'
def getPriceGrid(self) -> list:
self.priceGrid: list = []
# 网格大小,数量
if self.priceGrid is None or len(self.priceGrid) == 0:
for i in range(self.grid_upper_count): # type: ignore
upperPrice = self.grid_start_price + (self.grid_upper_count - i) * self.grid_size
self.priceGrid.append(upperPrice)
self.priceGrid.append(self.grid_start_price)
for i in range(self.grid_lower_count): # type: ignore 5
lowerPrice = self.grid_start_price - (i + 1) * self.grid_size
self.priceGrid.append(lowerPrice)
return self.priceGrid
db.create_tables([SFGridTradeTarget]) db.create_tables([SFGridTradeTarget])
+8 -8
View File
@@ -40,7 +40,7 @@ class SFGridStrategy:
else: # 已建仓 else: # 已建仓
# 交易阶段,检查仓位,检查现有订单 # 交易阶段,检查仓位,检查现有订单
print(f" |- 标的{self.tradeTarget.targetName()}已有仓位或非初始状态 无需建初始仓 当前仓位: {self.tradeTarget.current_position} 状态: {self.tradeTarget.status}") print(f" |- 标的{self.tradeTarget.targetName()}已有仓位或非初始状态 无需建初始仓 当前仓位: {self.tradeTarget.current_position} 状态: {self.tradeTarget.status}")
minRequirePosition:int = config.grid_volume * int(self.tradeTarget.grid_index) # type: ignore minRequirePosition:int = self.tradeTarget.grid_volume * int(self.tradeTarget.grid_index) # type: ignore
if minRequirePosition <= int(self.tradeTarget.current_position): # type: ignore if minRequirePosition <= int(self.tradeTarget.current_position): # type: ignore
print(f' |- 仓位检查: 持仓需求充足, (gridVolume*gridIndex)={minRequirePosition}, 当前持仓:{self.tradeTarget.current_position}') print(f' |- 仓位检查: 持仓需求充足, (gridVolume*gridIndex)={minRequirePosition}, 当前持仓:{self.tradeTarget.current_position}')
else: else:
@@ -74,16 +74,16 @@ class SFGridStrategy:
index: int = self.tradeTarget.grid_index # pyright: ignore[reportAssignmentType] index: int = self.tradeTarget.grid_index # pyright: ignore[reportAssignmentType]
orderRemark= "" orderRemark= ""
gridBasePrice = -1 if index>=len(config.grid_price) or index < 0 else config.grid_price[int(index)] # pyright: ignore[reportArgumentType] gridBasePrice = -1 if index>=len(inTradeTarget.getPriceGrid()) or index < 0 else inTradeTarget.getPriceGrid()[int(index)] # pyright: ignore[reportArgumentType]
if self.tradeTarget.enabled and self.tradeTarget.status == 0 and lastPrice <= config.grid_price[1]: # 已启用,未建仓,建仓 if self.tradeTarget.enabled and self.tradeTarget.status == 0 and lastPrice <= config.grid_price[1]: # 已启用,未建仓,建仓
orderPrice = config.grid_price[index] orderPrice = inTradeTarget.getPriceGrid()[index]
orderType = xtconstant.STOCK_BUY orderType = xtconstant.STOCK_BUY
orderRemark = OrderTypeInit orderRemark = OrderTypeInit
if self.tradeTarget.enabled and self.tradeTarget.status == 1: # 已启用,已建仓,网格单 if self.tradeTarget.enabled and self.tradeTarget.status == 1: # 已启用,已建仓,网格单
lowPrice = -1 if index+1>=len(config.grid_price) else config.grid_price[int(index) + 1] # pyright: ignore[reportArgumentType] lowPrice = -1 if index+1>=len(inTradeTarget.getPriceGrid()) else inTradeTarget.getPriceGrid()[int(index) + 1] # pyright: ignore[reportArgumentType]
highPrice = config.grid_price[index - 1] highPrice = inTradeTarget.getPriceGrid()[index - 1]
if lastPrice <= lowPrice: # 下下方多单 if lastPrice <= lowPrice: # 下下方多单
orderPrice = lowPrice orderPrice = lowPrice
@@ -196,11 +196,11 @@ class SFGridStrategy:
buyIdx: int = self.tradeTarget.grid_index + 1 # pyright: ignore[reportAssignmentType] buyIdx: int = self.tradeTarget.grid_index + 1 # pyright: ignore[reportAssignmentType]
sellIdx: int = self.tradeTarget.grid_index - 1 sellIdx: int = self.tradeTarget.grid_index - 1
if self.tradeTarget.grid_index > 0: # 可以下空单 if self.tradeTarget.grid_index > 0: # 可以下空单
self.tradeTarget.plan_sell_price = float(config.grid_price[sellIdx]) # pyright: ignore[reportAttributeAccessIssue] self.tradeTarget.plan_sell_price = float(self.tradeTarget.getPriceGrid()[sellIdx]) # pyright: ignore[reportAttributeAccessIssue]
else: else:
self.tradeTarget.plan_sell_price = -1.0 # type: ignore self.tradeTarget.plan_sell_price = -1.0 # type: ignore
if self.tradeTarget.grid_index < len(config.grid_price) - 1: if self.tradeTarget.grid_index < len(self.tradeTarget.getPriceGrid()) - 1:
self.tradeTarget.plan_buy_price = float(config.grid_price[buyIdx]) # pyright: ignore[reportAttributeAccessIssue] self.tradeTarget.plan_buy_price = float(self.tradeTarget.getPriceGrid()[buyIdx]) # pyright: ignore[reportAttributeAccessIssue]
else: else:
self.tradeTarget.plan_buy_price = -1.0 # pyright: ignore[reportAttributeAccessIssue] self.tradeTarget.plan_buy_price = -1.0 # pyright: ignore[reportAttributeAccessIssue]
else: else:
+14 -15
View File
@@ -8,7 +8,7 @@ import time
from core import constants from core import constants
import core.eventbus as eBus import core.eventbus as eBus
from core.logger import LogLevel, PrintLog from core.logger import LogLevel, PrintLog
from core.sfgrid.bus_events import ActionEventAddTradeTarget, ActionEventDeleteTradeTarget, ActionEventDisableTrade, ActionEventEnableTrade, ActionEventGridFix, EventTradeTargetUpdate, ResultEventTradeTargetAdded, ResultEventTradeTargetDeleted from core.sfgrid.bus_events import ActionEventAddTradeTarget
from core.sfgrid.model import SFGridTradeTarget from core.sfgrid.model import SFGridTradeTarget
import configparser import configparser
import config import config
@@ -48,6 +48,7 @@ class TradeTargetUI(ttk.Frame):
id:int = self.stockCodeIdMap[stock_code] id:int = self.stockCodeIdMap[stock_code]
tradeTarget = self.tradeTargetData[id] tradeTarget = self.tradeTargetData[id]
lastPrice = float("{:.3f}".format(tickData['lastPrice'])) lastPrice = float("{:.3f}".format(tickData['lastPrice']))
print(f'股票代码: {stock_code} {id}, 市场数据更新 {lastPrice}')
tradeTarget.market_price = lastPrice # type: ignore tradeTarget.market_price = lastPrice # type: ignore
self.updateTradeTarget(tradeTarget) self.updateTradeTarget(tradeTarget)
stock_controller: SFGridStrategy = self.strategy_ctrl[id] stock_controller: SFGridStrategy = self.strategy_ctrl[id]
@@ -85,7 +86,7 @@ class TradeTargetUI(ttk.Frame):
for id, tradeTarget in self.tradeTargetData.items(): for id, tradeTarget in self.tradeTargetData.items():
status = "新建" if tradeTarget.status == 0 else "已建初始仓" status = "新建" if tradeTarget.status == 0 else "已建初始仓"
PrintLog(LogLevel.INFO, f' [序号-{id}] 股票代码: {tradeTarget.stock_code}-{tradeTarget.stock_name} 当前持仓: {qmtv.getStockPosition(tradeTarget.stock_code)} 网格索引: {tradeTarget.grid_index} 基准价格 {config.grid_price[tradeTarget.grid_index]} 状态: {status} 启用交易线程: {'自动交易中' if tradeTarget.enabled else '交易已停止'}') # type: ignore PrintLog(LogLevel.INFO, f' [序号-{id}] 股票代码: {tradeTarget.stock_code}-{tradeTarget.stock_name} 当前持仓: {qmtv.getStockPosition(tradeTarget.stock_code)} 网格索引: {tradeTarget.grid_index} 基准价格 {tradeTarget.getPriceGrid()[tradeTarget.grid_index]} 状态: {status} 启用交易线程: {'自动交易中' if tradeTarget.enabled else '交易已停止'}') # type: ignore
tradeTarget.current_position = qmtv.getStockPosition(tradeTarget.stock_code) # type: ignore tradeTarget.current_position = qmtv.getStockPosition(tradeTarget.stock_code) # type: ignore
result = tradeTarget.save() result = tradeTarget.save()
@@ -143,10 +144,10 @@ class TradeTargetUI(ttk.Frame):
command=self.pause_selected_trade, width=12).pack(side=tk.LEFT, padx=2) command=self.pause_selected_trade, width=12).pack(side=tk.LEFT, padx=2)
ttk.Button(toolbar_frame, text=" 添加标的", ttk.Button(toolbar_frame, text=" 添加标的",
command=self.add_trade_target, width=12).pack(side=tk.LEFT, padx=2) command=self.add_trade_target, width=12).pack(side=tk.LEFT, padx=2)
ttk.Button(toolbar_frame, text="🛠 网格配置",
command=self.grid_settings, width=12).pack(side=tk.LEFT, padx=2)
ttk.Button(toolbar_frame, text="🗑 删除标的", ttk.Button(toolbar_frame, text="🗑 删除标的",
command=self.delete_selected_trade, width=12).pack(side=tk.LEFT, padx=2) command=self.delete_selected_trade, width=12).pack(side=tk.LEFT, padx=2)
ttk.Button(toolbar_frame, text="🛠 网格修正",
command=self.grid_correction, width=12).pack(side=tk.LEFT, padx=2)
# 添加抽屉按钮到工具栏最右侧 # 添加抽屉按钮到工具栏最右侧
self.toggle_market_monitor_btn = ttk.Button(toolbar_frame, text="◀▶", self.toggle_market_monitor_btn = ttk.Button(toolbar_frame, text="◀▶",
@@ -246,7 +247,7 @@ class TradeTargetUI(ttk.Frame):
column_configs = { column_configs = {
"时间": (50, tk.CENTER), "时间": (50, tk.CENTER),
"股票名称": (80, tk.CENTER), "股票名称": (80, tk.CENTER),
"最新价格": (80, tk.CENTER) "最新价格": (50, tk.CENTER)
} }
for col in columns: for col in columns:
@@ -303,7 +304,8 @@ class TradeTargetUI(ttk.Frame):
values = [ values = [
time_str, time_str,
data['stock_name']+f"{stock_code}", data['stock_name']+f"{stock_code}",
f"{data['last_price']:.3f}" f"{data['last_price']:.3f}",
stock_code
] ]
self.market_table.insert('', tk.END, values=values) self.market_table.insert('', tk.END, values=values)
@@ -320,9 +322,10 @@ class TradeTargetUI(ttk.Frame):
if selected: if selected:
item = selected[0] item = selected[0]
values = self.market_table.item(item)['values'] values = self.market_table.item(item)['values']
stock_code = values[1] print(values)
stock_name = values[2] stock_name = values[1]
last_price = values[3] last_price = values[2]
stock_code = values[3]
# 检查是否已在交易池中 # 检查是否已在交易池中
is_in_trade_pool = any(target.stock_code == stock_code for target in self.tradeTargetData.values()) is_in_trade_pool = any(target.stock_code == stock_code for target in self.tradeTargetData.values())
@@ -501,7 +504,7 @@ class TradeTargetUI(ttk.Frame):
del self.tradeTargetData[id] del self.tradeTargetData[id]
del self.strategy_ctrl[id] del self.strategy_ctrl[id]
del self.stockCodeIdMap[str(target.stock_code)] del self.stockCodeIdMap[target.stock_code] # type: ignore
# 添加日志 # 添加日志
PrintLog(LogLevel.INFO, f"交易标的已删除,ID: {id} {target.targetName()}") PrintLog(LogLevel.INFO, f"交易标的已删除,ID: {id} {target.targetName()}")
except Exception as e: except Exception as e:
@@ -898,7 +901,7 @@ class TradeTargetUI(ttk.Frame):
ttk.Button(button_frame, text="💾 保存配置", command=save_settings, width=15).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="💾 保存配置", command=save_settings, width=15).pack(side=tk.LEFT, padx=5)
ttk.Button(button_frame, text="❌ 取消", command=cancel_settings, width=15).pack(side=tk.LEFT, padx=5) ttk.Button(button_frame, text="❌ 取消", command=cancel_settings, width=15).pack(side=tk.LEFT, padx=5)
def grid_correction(self): def grid_settings(self):
"""网格修正功能""" """网格修正功能"""
target = self.get_selected_target() target = self.get_selected_target()
if not target: if not target:
@@ -1061,10 +1064,6 @@ class TradeTargetUI(ttk.Frame):
if not result: if not result:
return return
# 发布网格修正事件,传递GridFixData对象
grid_fix_data = GridFixData(new_grid_index, target)
eBus.event_bus.publish(ActionEventGridFix, grid_fix_data)
# 关闭窗口 # 关闭窗口
window.destroy() window.destroy()
View File