import copy
|
import threading
|
import time
|
|
import wx
|
|
import local_api
|
from local_api.limit_up_manager import KPLLimitUpDataManager
|
from local_api.log_module import log_export
|
from local_api.util import tool, gui_util
|
|
APP_TITLE = "低吸"
|
|
__symbol_name_dict = {}
|
|
# 系统启动时间
|
system_start_time = tool.get_now_time_str()
|
|
|
def get_symbols_names(symbols):
|
request_symbols = []
|
name_dict = {}
|
for symbol in symbols:
|
if symbol not in __symbol_name_dict:
|
request_symbols.append(symbol)
|
else:
|
name_dict[symbol] = __symbol_name_dict.get(symbol)
|
|
name_dict_temp = local_api.trade_api.get_symbol_names(request_symbols)
|
for symbol in name_dict_temp:
|
name_dict[symbol] = name_dict_temp[symbol]
|
return name_dict
|
|
|
class MainFrame(wx.Frame):
|
mark_lines = {}
|
|
def __init__(self):
|
"""
|
初始化函数
|
"""
|
wx.Frame.__init__(self, None, -1, APP_TITLE, style=wx.DEFAULT_FRAME_STYLE,
|
size=(1000, 500))
|
self.__init_view()
|
self.__init_data()
|
|
def __init_view(self):
|
root_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
left_sizer = wx.BoxSizer(wx.VERTICAL)
|
right_sizer = wx.BoxSizer(wx.VERTICAL)
|
root_sizer.Add(left_sizer, 1, wx.EXPAND | wx.TOP | wx.LEFT | wx.BOTTOM, 5)
|
root_sizer.Add(right_sizer, 0, wx.EXPAND | wx.ALL, 5)
|
self.SetSizer(root_sizer)
|
|
self.system_log_ctrl = wx.TextCtrl(self, size=wx.Size(250, -1), style=wx.TE_MULTILINE | wx.TE_READONLY)
|
self.system_log_ctrl.AppendText("---系统日志区域---")
|
right_sizer.Add(self.system_log_ctrl, 1)
|
|
self.trade_log_ctrl = wx.TextCtrl(self, size=wx.Size(250, -1), style=wx.TE_MULTILINE | wx.TE_READONLY)
|
self.trade_log_ctrl.AppendText("---交易日志区域---")
|
right_sizer.Add(self.trade_log_ctrl, 1)
|
|
self.main_panel = wx.Panel(self, size=wx.Size(-1, -1))
|
self.main_panel.SetBackgroundColour(wx.Colour(255, 255, 255))
|
left_sizer.Add(self.main_panel, 1, wx.EXPAND | wx.TOP | wx.LEFT | wx.BOTTOM, 5)
|
self.SetBackgroundColour(wx.Colour(225, 225, 225))
|
|
# -------添加操作按钮--------
|
panel_sizer = wx.BoxSizer(wx.VERTICAL)
|
# 板块与涨停代码数量
|
sizer = wx.BoxSizer(wx.HORIZONTAL)
|
self.btn_request_blocks = wx.Button(self.main_panel, 101, "刷新板块")
|
sizer.Add(self.btn_request_blocks, 0, wx.TOP | wx.LEFT, 5)
|
self.label_block_info = wx.StaticText(self.main_panel)
|
sizer.Add(self.label_block_info, 0, wx.TOP | wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5)
|
panel_sizer.Add(sizer)
|
|
sizer = wx.BoxSizer(wx.HORIZONTAL)
|
self.btn_repaire_limit_up_task = wx.Button(self.main_panel, 102, "修复涨停")
|
sizer.Add(self.btn_repaire_limit_up_task, 0, wx.TOP | wx.LEFT, 5)
|
self.label_limit_up_info = wx.StaticText(self.main_panel, size=wx.Size(100, -1))
|
sizer.Add(self.label_limit_up_info, 0, wx.TOP | wx.LEFT | wx.ALIGN_CENTER_VERTICAL, 5)
|
self.label_limit_up_info.SetLabelText("涨停列表更新:")
|
panel_sizer.Add(sizer)
|
self.list_ctrl_position = wx.ListCtrl(self.main_panel, style=wx.LC_REPORT, size=wx.Size(-1, 300))
|
panel_sizer.Add(wx.StaticText(self.main_panel, label="持仓:"), 0, wx.LEFT | wx.TOP, 5)
|
panel_sizer.Add(self.list_ctrl_position, 0, wx.EXPAND | wx.ALL, 5)
|
# (p["symbol"], p["amount"], p["volume"], p["available"], p["cost"], p["price"],)
|
|
self.list_ctrl_position.InsertColumn(0, "代码")
|
self.list_ctrl_position.InsertColumn(1, "名称")
|
self.list_ctrl_position.InsertColumn(2, "持仓金额")
|
self.list_ctrl_position.InsertColumn(3, "持仓数量")
|
self.list_ctrl_position.InsertColumn(4, "可用数量")
|
self.list_ctrl_position.InsertColumn(5, "持仓成本")
|
self.list_ctrl_position.InsertColumn(6, "现价")
|
self.list_ctrl_position.InsertColumn(7, "委卖量")
|
self.list_ctrl_position.InsertColumn(8, "委卖价格")
|
self.main_panel.SetSizer(panel_sizer)
|
|
# 卖出面板
|
sell_panel = wx.Panel(self.main_panel, size=wx.Size(-1, 50))
|
sell_pannel_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
sell_panel.SetSizer(sell_pannel_sizer)
|
sizer1 = wx.BoxSizer(wx.VERTICAL)
|
sizer1.Add(wx.StaticText(sell_panel, label="代码:"))
|
self.label_sell_code = wx.StaticText(sell_panel, label="000000", size=wx.Size(100, -1))
|
sizer1.Add(self.label_sell_code)
|
sell_pannel_sizer.Add(sizer1)
|
|
sizer1 = wx.BoxSizer(wx.VERTICAL)
|
sizer1.Add(wx.StaticText(sell_panel, label="可用持仓:"))
|
self.label_sell_avaiable_volume = wx.StaticText(sell_panel, label="0", size=wx.Size(70, -1))
|
sizer1.Add(self.label_sell_avaiable_volume)
|
sell_pannel_sizer.Add(sizer1)
|
|
sizer1 = wx.BoxSizer(wx.VERTICAL)
|
sizer1.Add(wx.StaticText(sell_panel, label="卖出量:"))
|
sizer_volume = wx.BoxSizer(wx.HORIZONTAL)
|
self.edit_sell_volume = wx.TextCtrl(sell_panel, size=wx.Size(70, -1))
|
sizer_volume.Add(self.edit_sell_volume, 0, wx.RIGHT, 10)
|
gui_util.GuiViewManager.create_sell_volume_percent(sizer_volume, sell_panel,
|
lambda x: self.edit_sell_volume.SetValue(str(int(
|
self.label_sell_avaiable_volume.GetLabel()) // x // 100 * 100)))
|
sizer1.Add(sizer_volume)
|
sell_pannel_sizer.Add(sizer1)
|
|
sizer1 = wx.BoxSizer(wx.HORIZONTAL)
|
sizer1.Add(wx.StaticText(sell_panel), 1)
|
self.btn_cancel_sell = wx.Button(sell_panel, label="撤卖", size=wx.Size(50, 30))
|
|
sizer1.Add(self.btn_cancel_sell, 0, wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, 10)
|
self.btn_sell = wx.Button(sell_panel, label="卖出", size=wx.Size(50, 30))
|
|
sizer1.Add(self.btn_sell, 0, wx.ALIGN_CENTER_VERTICAL, 0)
|
sell_pannel_sizer.Add(sizer1, 1, wx.EXPAND)
|
|
panel_sizer.Add(sell_panel, 0, wx.ALL | wx.EXPAND, 5)
|
|
self.btn_request_blocks.Bind(wx.EVT_BUTTON, self.__refresh_blocks)
|
self.btn_repaire_limit_up_task.Bind(wx.EVT_BUTTON, self.__repaire_limit_up_task)
|
|
self.btn_sell.Bind(wx.EVT_BUTTON, self.__sell)
|
|
on_item_selected = lambda event: self.__show_sell_data(event.GetIndex())
|
|
self.list_ctrl_position.Bind(wx.EVT_LIST_ITEM_SELECTED, on_item_selected)
|
|
self.btn_cancel_sell.Bind(wx.EVT_BUTTON, lambda x: self.__cancel_order())
|
|
# 设置快捷键
|
entries = [
|
# wx.AcceleratorEntry(wx.ACCEL_CTRL, ord('W'), 901),
|
wx.AcceleratorEntry(wx.ACCEL_NORMAL, wx.WXK_F4, 901),
|
wx.AcceleratorEntry(wx.ACCEL_NORMAL, wx.WXK_F5, 902)
|
]
|
# Create an accelerator table
|
accel_table = wx.AcceleratorTable(entries)
|
self.SetAcceleratorTable(accel_table)
|
self.Bind(wx.EVT_MENU, self.quick_sell, id=901)
|
self.Bind(wx.EVT_MENU, lambda x: self.__cancel_order(), id=902)
|
|
self.__start_task()
|
|
def quick_sell(self, event):
|
self.__sell(event)
|
|
def __cancel_order(self):
|
for symbol in self.delegates_dict:
|
for d in self.delegates_dict[symbol]:
|
local_api.trade_api.cancel_order(d[1], d[2], blocking=False)
|
|
def __show_sell_data(self, index):
|
if index < 0:
|
return
|
self.label_sell_code.SetLabel(self.positions[index][0])
|
available_volume = self.__compute_available_volume(self.positions[index])
|
self.label_sell_avaiable_volume.SetLabel(str(available_volume))
|
sell_percent = gui_util.GuiViewManager.get_sell_volume_percent()
|
self.edit_sell_volume.SetValue(str(available_volume // sell_percent // 100 * 100))
|
|
def __refresh_blocks(self, event):
|
def update_blocks():
|
local_api.update_blocks()
|
wx.CallAfter(lambda: self.btn_request_blocks.SetLabel("刷新板块"))
|
|
self.btn_request_blocks.SetLabel("刷新板块...")
|
threading.Thread(target=update_blocks, daemon=True).start()
|
|
def __repaire_limit_up_task(self, event):
|
KPLLimitUpDataManager().repaire_task()
|
wx.MessageBox("修复成功")
|
|
def __sell(self, event):
|
volume = self.edit_sell_volume.GetValue()
|
if not volume.isdigit():
|
wx.MessageBox("量输入错误")
|
return
|
print("卖出量:", volume)
|
symbol = self.label_sell_code.GetLabel()
|
code = symbol.split(".")[1]
|
tick = local_api.latest_tick_dict.get(code)
|
# 收盘价,现价
|
if tick:
|
price, pre_price = tick[2], tick[1]
|
price = tool.get_buy_min_price(price)
|
result = local_api.trade_api.sell(code, int(volume), price)
|
print("卖出结果:", result)
|
print("持仓:", local_api.trade_api.position_by_symbol(symbol))
|
else:
|
wx.MessageBox("没有获取到tick数据")
|
|
def __compute_available_volume(self, data):
|
"""
|
计算可用量:总量-委托量
|
:param data:
|
:return:
|
"""
|
available_volume = self.__compute_total_volume(data)
|
# 委托量
|
dvolume = 0
|
if data[0] in self.delegates_dict:
|
delegates_list = self.delegates_dict[data[0]]
|
if delegates_list:
|
dvolume = sum([x[3] for x in delegates_list])
|
available_volume -= dvolume
|
return available_volume
|
|
def __compute_total_volume(self, data):
|
"""
|
计算总量
|
:param data:
|
:return:
|
"""
|
total_volume = copy.deepcopy(data[2])
|
if data[0] in self.deal_dict:
|
deal_list = self.deal_dict[data[0]]
|
deal_volume = 0
|
if deal_list:
|
for d in deal_list:
|
if d[1] == 2 and tool.trade_time_sub(d[5], system_start_time) > 0:
|
deal_volume += d[3]
|
total_volume -= deal_volume
|
return total_volume
|
|
def __start_task(self):
|
def __show_blocks():
|
while True:
|
try:
|
today_count, before_count = local_api.get_blocks_count_info()
|
st = f"有板块代码数: 今日-{today_count} 昨日-{before_count}"
|
wx.CallAfter(lambda: self.label_block_info.SetLabelText(st))
|
except:
|
pass
|
finally:
|
time.sleep(1)
|
|
def __show_limit_up_list_info():
|
while True:
|
try:
|
info = KPLLimitUpDataManager().get_current_limitup_info()
|
if info is None:
|
st = f"涨停列表:尚未更新"
|
else:
|
st = f"涨停列表:{info[0]}-{len(info[1])}个"
|
wx.CallAfter(lambda: self.label_limit_up_info.SetLabelText(st))
|
except:
|
pass
|
finally:
|
time.sleep(1)
|
|
def __get_positions():
|
def __show_positions(datas):
|
self.positions = datas
|
# self.list_ctrl_position
|
self.list_ctrl_position.DeleteAllItems()
|
symbols_names_dict = get_symbols_names([x[0] for x in datas])
|
for data in datas:
|
code = data[0].split(".")[1]
|
total_volume = self.__compute_total_volume(data)
|
available_volume = self.__compute_available_volume(data)
|
index = self.list_ctrl_position.InsertItem(self.list_ctrl_position.GetItemCount(), data[0])
|
# 委托量
|
dvolume = 0
|
if data[0] in self.delegates_dict:
|
delegates_list = self.delegates_dict[data[0]]
|
if delegates_list:
|
dvolume = sum([x[3] for x in delegates_list])
|
self.list_ctrl_position.SetItem(index, 8, f'{delegates_list[-1][4]}')
|
|
self.list_ctrl_position.SetItem(index, 1, symbols_names_dict.get(data[0]))
|
self.list_ctrl_position.SetItem(index, 2, str(round(data[1], 2)))
|
self.list_ctrl_position.SetItem(index, 3, str(total_volume))
|
self.list_ctrl_position.SetItem(index, 4, str(available_volume))
|
self.list_ctrl_position.SetItem(index, 5, str(round(data[4], 2)))
|
tick = local_api.latest_tick_dict.get(code)
|
if tick:
|
self.list_ctrl_position.SetItem(index, 6, str(tick[2]))
|
else:
|
self.list_ctrl_position.SetItem(index, 6, str(data[5]))
|
|
self.list_ctrl_position.SetItem(index, 7, f'{dvolume}')
|
|
while True:
|
try:
|
positions = local_api.trade_api.positions(blocking=True)
|
print("持仓:", positions)
|
wx.CallAfter(lambda: __show_positions(
|
[(p["symbol"], p["amount"], p["volume"], p["available_now"], p["cost"], p["price"],) for p in
|
positions]))
|
|
# wx.CallAfter(lambda: self.label_limit_up_info.SetLabelText(st))
|
except:
|
pass
|
finally:
|
time.sleep(3)
|
|
def __get_delegates():
|
# 获取委托
|
while True:
|
try:
|
delegates = local_api.trade_api.get_unfinish_orders(blocking=True)
|
delegates = [(x["symbol"], x["cl_ord_id"], x["account_id"], x["volume"], x["price"]) for x in
|
delegates]
|
delegates_dict = {}
|
for x in delegates:
|
if x[0] not in delegates_dict:
|
delegates_dict[x[0]] = []
|
delegates_dict[x[0]].append(x)
|
self.delegates_dict = delegates_dict
|
# wx.CallAfter(lambda: self.label_limit_up_info.SetLabelText(st))
|
except:
|
pass
|
finally:
|
time.sleep(2)
|
|
def __get_deal_list():
|
# 获取委托
|
while True:
|
try:
|
deals = local_api.trade_api.get_execution_reports(blocking=True)
|
deals = [(x["symbol"], x["side"], x["price"], x["volume"], x["amount"],
|
x["created_at"].strftime('%H:%M:%S')) for x in
|
deals]
|
deal_dict = {}
|
for x in deals:
|
if x[0] not in deal_dict:
|
deal_dict[x[0]] = []
|
deal_dict[x[0]].append(x)
|
self.deal_dict = deal_dict
|
# wx.CallAfter(lambda: self.label_limit_up_info.SetLabelText(st))
|
except:
|
pass
|
finally:
|
time.sleep(2)
|
|
"""
|
开始任务
|
:return:
|
"""
|
|
threading.Thread(target=__show_blocks, daemon=True).start()
|
threading.Thread(target=__show_limit_up_list_info, daemon=True).start()
|
threading.Thread(target=__get_positions, daemon=True).start()
|
threading.Thread(target=__get_delegates, daemon=True).start()
|
threading.Thread(target=__get_deal_list, daemon=True).start()
|
|
def __init_data(self):
|
|
self.delegates_dict = {}
|
self.deal_dict = {}
|
logs = log_export.load_system_logs()
|
self.system_log_ctrl.Clear()
|
for l in logs:
|
self.system_log_ctrl.AppendText(f"{l[0]} - {l[1]}\n")
|
logs = log_export.load_trade_logs()
|
self.trade_log_ctrl.Clear()
|
for l in logs:
|
self.trade_log_ctrl.AppendText(f"{l[0]} - {l[1]}\n")
|
|
|
class MainApp(wx.App):
|
|
def __init_data(self):
|
pass
|
|
def OnInit(self):
|
self.SetAppName("低吸")
|
self.__init_data()
|
self.frame = MainFrame()
|
self.frame.Show()
|
return True
|