独立配置
This commit is contained in:
@@ -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
@@ -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,12 +16,9 @@ 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):
|
||||||
@@ -211,10 +207,6 @@ class MainWindow:
|
|||||||
result = messagebox.askyesno("确认退出", "确定要退出系统吗?")
|
result = messagebox.askyesno("确认退出", "确定要退出系统吗?")
|
||||||
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):
|
||||||
"""运行程序"""
|
"""运行程序"""
|
||||||
|
|||||||
@@ -130,6 +130,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, '- 停止市场数据订阅')
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
GridTypePercentage = "Percentage"
|
||||||
|
GridTypeFixed = "PriceBreak"
|
||||||
@@ -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])
|
||||||
@@ -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:
|
||||||
|
|||||||
+15
-16
@@ -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
|
||||||
@@ -47,7 +47,8 @@ class TradeTargetUI(ttk.Frame):
|
|||||||
if stock_code in self.stockCodeIdMap:
|
if stock_code in self.stockCodeIdMap:
|
||||||
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()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user