887 lines
32 KiB
Python
887 lines
32 KiB
Python
|
#!/usr/bin/env python
|
|||
|
# -*- coding: utf-8 -*-
|
|||
|
"""
|
|||
|
@Time : 2020-03-26 16:17
|
|||
|
@Author : Zdanjuan
|
|||
|
@Site :
|
|||
|
@File : hy_clienttrader.py
|
|||
|
@Software: PyCharm
|
|||
|
"""
|
|||
|
import pywinauto
|
|||
|
from pywinauto import clipboard
|
|||
|
from pywinauto import keyboard
|
|||
|
import pandas as pd
|
|||
|
from skimage import io
|
|||
|
import time
|
|||
|
import io as mio
|
|||
|
import win32api
|
|||
|
import re
|
|||
|
import requests
|
|||
|
|
|||
|
from PIL import Image
|
|||
|
import win32com.client
|
|||
|
import getpass
|
|||
|
import pytesseract
|
|||
|
import warnings
|
|||
|
import pyautogui
|
|||
|
import datetime
|
|||
|
from sqlalchemy import create_engine
|
|||
|
from tools import *
|
|||
|
from connect_wifi import connect_wifi
|
|||
|
from iptest import get_proxy_ip,set_global_proxy,get_ip_data
|
|||
|
|
|||
|
warnings.filterwarnings('ignore')
|
|||
|
|
|||
|
pc_name111 = getpass.getuser() #返回用户名
|
|||
|
|
|||
|
shell = win32com.client.Dispatch("WScript.Shell") #使用启动独立的进程
|
|||
|
shortcut = shell.CreateShortCut(f"C:\\Users\\{pc_name111}\\Desktop\\申万宏源同花顺专业版.lnk")
|
|||
|
|
|||
|
path1 = shortcut.Targetpath
|
|||
|
path = path1
|
|||
|
|
|||
|
scripts_path = os.path.dirname(os.path.realpath(__file__))
|
|||
|
root_path = scripts_path[:scripts_path.find(project_name)+len(project_name)]
|
|||
|
class HYClientTrader:
|
|||
|
"""
|
|||
|
基于同花顺委托下单程序的"宏源客户端"自动交易程序
|
|||
|
"""
|
|||
|
|
|||
|
def __init__(self, account_name, exe_path=path):
|
|||
|
self.log = Logger(f'{root_path}/logs',self.account_name)
|
|||
|
self.path = exe_path
|
|||
|
self.BALANCE_CONTROL_ID_GROUP = {
|
|||
|
# "资金余额": 0x3F4,
|
|||
|
"可用金额": 0x3f8,
|
|||
|
# "可取金额": 0x3f9,
|
|||
|
"股票市值": 0x3f6,
|
|||
|
"总资产": 0x3f7,
|
|||
|
}
|
|||
|
self.trade_control_id_group = {
|
|||
|
's1': 0x3FD, 's2': 0x3FE, 's3': 0x3FF, 's4': 0x409, 's5': 0x408,
|
|||
|
'b1': 0x3FA, 'b2': 0x401, 'b3': 0x402, 'b4': 0x40B, 'b5': 0x40C,
|
|||
|
}
|
|||
|
self.BALANCE_MENU_PATH = ["查询[F4]", "资金股票"]
|
|||
|
self.today = datetime.datetime.today().strftime('%Y-%m-%d')
|
|||
|
self.account_name = account_name
|
|||
|
self.table_df = []
|
|||
|
# self.is_bond = is_bond # 0:股票 1:债券
|
|||
|
self.is_buy = 1
|
|||
|
self.verify_code_wrong = 0
|
|||
|
self.position = {'当日成交':(106,348),'中心':(333,333)}
|
|||
|
# 获取债券列表
|
|||
|
engine_iddb = create_engine(
|
|||
|
'mysql+pymysql://cn_ainvest_db:cn_ainvest_sd3a1@rm-2zewagytttzk6f24xno.mysql.rds.aliyuncs.com:3306/iddb')
|
|||
|
df_bond = pd.read_sql(f'select * from conbond_basic_info', engine_iddb)
|
|||
|
self.df_list = df_bond.drop_duplicates('Bond_Ticker')['Bond_Ticker'].values.tolist()
|
|||
|
# 用户券商信息
|
|||
|
sql = f"select * from ainvest_usercount where username='{account_name}'"
|
|||
|
df_count = self.download_data_from_db(sql, 'ai_strategy_update_iddb')
|
|||
|
self.securities_name = str(list(df_count['securities_username'])[0])
|
|||
|
self.securities_password = str(list(df_count['securities_password'])[0])
|
|||
|
self.communication_password = str(list(df_count['communication_password'])[0])
|
|||
|
|
|||
|
# 转换股票代码
|
|||
|
|
|||
|
|
|||
|
def tranTicker(self, x):
|
|||
|
x = str(x)
|
|||
|
|
|||
|
if len(x) == 8:
|
|||
|
pass
|
|||
|
else:
|
|||
|
if len(x) == 6:
|
|||
|
if x.startswith('6'):
|
|||
|
x = 'SH' + x
|
|||
|
else:
|
|||
|
x = 'SZ' + x
|
|||
|
else:
|
|||
|
num = 6 - len(x)
|
|||
|
|
|||
|
x = 'SZ' + num * '0' + x
|
|||
|
return x
|
|||
|
|
|||
|
# 从数据库获取数据
|
|||
|
|
|||
|
|
|||
|
def download_data_from_db(self, sql, schema_name):
|
|||
|
"""
|
|||
|
create connection with DB
|
|||
|
阿里云数据库连接
|
|||
|
:param sql: 查询语句
|
|||
|
:param schema_name: 数据库名称
|
|||
|
:return: 查询结果数据
|
|||
|
"""
|
|||
|
cnx = create_engine(
|
|||
|
'mysql+pymysql://cn_ainvest_db:cn_ainvest_sd3a1@rm-2zewagytttzk6f24xno.'
|
|||
|
'mysql.rds.aliyuncs.com:3306/%s?charset=utf8' %
|
|||
|
schema_name, encoding="utf-8", echo=False)
|
|||
|
df = pd.read_sql(sql, con=cnx)
|
|||
|
return df
|
|||
|
|
|||
|
|
|||
|
def get_open_price(self, tick):
|
|||
|
str_ticker = tick.lower()
|
|||
|
price_all = requests.get('http://hq.sinajs.cn/list=' + str_ticker).text
|
|||
|
price_ticker = str(re.findall(r'var hq_str_(.*?);', price_all))
|
|||
|
# 开盘价
|
|||
|
open_price = float(price_ticker.split(',')[1])
|
|||
|
# 昨日收盘价
|
|||
|
price_pre_close = float(price_ticker.split(',')[2])
|
|||
|
# 实时股价
|
|||
|
price_real_time = float(price_ticker.split(',')[3])
|
|||
|
return open_price
|
|||
|
def exit(self):
|
|||
|
self._app.kill()
|
|||
|
|
|||
|
def login(self):
|
|||
|
|
|||
|
try:
|
|||
|
self._app = pywinauto.Application().connect(
|
|||
|
path=self.path, timeout=1
|
|||
|
)
|
|||
|
# print('连接成功')
|
|||
|
a.exit()
|
|||
|
except Exception:
|
|||
|
pass
|
|||
|
|
|||
|
# wait login window ready
|
|||
|
# while True:
|
|||
|
# try:
|
|||
|
# self._app.top_window().Edit1.wait("ready")
|
|||
|
# break
|
|||
|
# except RuntimeError:
|
|||
|
# pass
|
|||
|
broker = '申万宏源'
|
|||
|
|
|||
|
start_time = time.time()
|
|||
|
reset_proxy_to_default() # 初始化proxy信息
|
|||
|
|
|||
|
# 重置mac
|
|||
|
set_mac = SetMac()
|
|||
|
set_mac.run()
|
|||
|
if not connect_wifi('ZTE_A5DB9D', '1234567890'): # 'Redmi K40','123456789'
|
|||
|
self.log.error("无法建立网络连接,请检查配置")
|
|||
|
else:
|
|||
|
self.log.info("网络已就绪")
|
|||
|
|
|||
|
get_ip_times = 0
|
|||
|
writer = ExcelDataWriter() # 初始化ip_records 表格
|
|||
|
used_ip = writer.get_unavailable_ips(broker, account_name)
|
|||
|
while get_ip_times < 3:
|
|||
|
try:
|
|||
|
item = get_ip_data() # 获取动态IP
|
|||
|
if item['ip'] in used_ip:
|
|||
|
get_ip_times += 1
|
|||
|
else:
|
|||
|
self.log.info(f'代理IP已经获取{item}')
|
|||
|
break
|
|||
|
except Exception as e:
|
|||
|
self.log.error(f'获取IP失败,请检查网络连接{e}')
|
|||
|
time.sleep(5)
|
|||
|
# raise ConnectionError(f"获取IP失败,请检查网络连接。错误详情: {e}")
|
|||
|
|
|||
|
# 设置全局代理
|
|||
|
proxy_ip = item['ip']
|
|||
|
proxy_port = item['port']
|
|||
|
set_global_proxy(proxy_ip, proxy_port, enable=True)
|
|||
|
|
|||
|
# exit_ip = get_proxy_ip(proxy_ip,proxy_port)
|
|||
|
time_with_change_proxy = time.time() - start_time
|
|||
|
self.log.info('全局代理设置成功')
|
|||
|
|
|||
|
insert_data = {'as_of_date': datetime.datetime.fromtimestamp(start_time).strftime('%Y-%m-%d %H:%M:%S.%f'),
|
|||
|
'broker': broker,
|
|||
|
'account': account_name,
|
|||
|
'proxy_ip': proxy_ip,
|
|||
|
'proxy_port': proxy_port,
|
|||
|
'ip_location': item['prov'] + ':' + item['city'],
|
|||
|
# 'exit_ip': exit_ip,
|
|||
|
'ip_cross_check_result': '一致',
|
|||
|
'ip_switch_time': time_with_change_proxy,
|
|||
|
# 'ip_expire_time':item['expire']
|
|||
|
'ip_survival_time': '3min'
|
|||
|
}
|
|||
|
# 写入数据表
|
|||
|
writer = ExcelDataWriter() # 初始化ip_records 表格
|
|||
|
writer.write_data(insert_data)
|
|||
|
|
|||
|
|
|||
|
self._app = pywinauto.Application().start(self.path)
|
|||
|
time.sleep(0.1)
|
|||
|
self._app.top_window().Edit1.type_keys(self.securities_name)
|
|||
|
time.sleep(0.1)
|
|||
|
self._app.top_window().Edit2.type_keys(self.securities_password)
|
|||
|
time.sleep(0.1)
|
|||
|
|
|||
|
# while True:
|
|||
|
time.sleep(1)
|
|||
|
self._app.top_window().set_focus()
|
|||
|
self._app.top_window().Edit1.wait("ready")
|
|||
|
|
|||
|
# 输入验证码
|
|||
|
id_code = self.verify_code()
|
|||
|
time.sleep(0.2)
|
|||
|
self._app.top_window().Edit3.type_keys('{BACKSPACE}' * 6)
|
|||
|
time.sleep(0.2)
|
|||
|
self._app.top_window().Edit3.type_keys(id_code)
|
|||
|
time.sleep(0.2)
|
|||
|
pywinauto.keyboard.send_keys("{ENTER}")
|
|||
|
time.sleep(0.5)
|
|||
|
# 验证码错误的情况
|
|||
|
try:
|
|||
|
self._app.top_window().set_focus()
|
|||
|
result_idcode = self._app.top_window().window(control_id=0x3EC, class_name='Static').window_text()
|
|||
|
if '验证码错误' in result_idcode:
|
|||
|
self._app.top_window().window(control_id=0x2, class_name="Button").click()
|
|||
|
time.sleep(0.5)
|
|||
|
# 重新获取id_code
|
|||
|
id_code = self.verify_code()
|
|||
|
time.sleep(0.2)
|
|||
|
self._app.top_window().Edit3.type_keys('{BACKSPACE}' * 6)
|
|||
|
time.sleep(0.2)
|
|||
|
self._app.top_window().Edit3.type_keys(id_code)
|
|||
|
time.sleep(0.2)
|
|||
|
pywinauto.keyboard.send_keys("{ENTER}")
|
|||
|
time.sleep(0.5)
|
|||
|
except Exception as e:
|
|||
|
pass
|
|||
|
time.sleep(2)
|
|||
|
self.main_wnd = self._app.window(title='网上股票交易系统5.0')
|
|||
|
# self.main_wnd = self._app.top_window()
|
|||
|
# 关闭广告
|
|||
|
try:
|
|||
|
self._app.top_window().set_focus()
|
|||
|
time.sleep(0.5)
|
|||
|
pywinauto.keyboard.send_keys("{ENTER}")
|
|||
|
time.sleep(0.2)
|
|||
|
pywinauto.keyboard.send_keys("{ENTER}")
|
|||
|
except Exception as e:
|
|||
|
pass
|
|||
|
time.sleep(0.5)
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
# 转换股票代码
|
|||
|
def tranTicker(self, x):
|
|||
|
|
|||
|
x = str(x)
|
|||
|
|
|||
|
if len(x) == 8:
|
|||
|
pass
|
|||
|
else:
|
|||
|
if len(x) == 6:
|
|||
|
if x.startswith('6'):
|
|||
|
x = 'SH' + x
|
|||
|
else:
|
|||
|
x = 'SZ' + x
|
|||
|
else:
|
|||
|
num = 6 - len(x)
|
|||
|
|
|||
|
x = 'SZ' + num * '0' + x
|
|||
|
return x
|
|||
|
|
|||
|
# 窗口最小化
|
|||
|
def minimize(self):
|
|||
|
self.main_wnd.minimize()
|
|||
|
|
|||
|
# 获取验证码
|
|||
|
def verify_code(self):
|
|||
|
time.sleep(1)
|
|||
|
self._app.top_window().Static.click()
|
|||
|
time.sleep(0.2)
|
|||
|
self._app.top_window().window(control_id=0x5DB, class_name='Static').capture_as_image().save(
|
|||
|
r'temp.png')
|
|||
|
time.sleep(0.2)
|
|||
|
im = io.imread(r'temp.png')
|
|||
|
id_code = pytesseract.image_to_string(im, lang='eng',
|
|||
|
config='--psm 6 --oem 3 -c tessedit_char_whitelist=0123456789').strip()
|
|||
|
# id_code = pytesseract.image_to_string(Image.open('temp.png'))
|
|||
|
id_code = id_code.replace(' ', '')
|
|||
|
return id_code
|
|||
|
|
|||
|
# 复制数据时获取验证码
|
|||
|
def get_copy_date_verify_code(self):
|
|||
|
time.sleep(1)
|
|||
|
self._app.top_window().Static.click()
|
|||
|
time.sleep(0.2)
|
|||
|
self._app.top_window().window(control_id=0x965, class_name='Static').capture_as_image().save(
|
|||
|
r'copy.png')
|
|||
|
time.sleep(0.2)
|
|||
|
im = io.imread(r'copy.png')
|
|||
|
id_code = pytesseract.image_to_string(im, lang='eng',
|
|||
|
config='--psm 6 --oem 3 -c tessedit_char_whitelist=0123456789').strip()
|
|||
|
# id_code = pytesseract.image_to_string(Image.open('temp.png'))
|
|||
|
id_code = id_code.replace(' ', '')
|
|||
|
return id_code
|
|||
|
|
|||
|
# 买
|
|||
|
def buy(self, stock_no, price, num):
|
|||
|
time.sleep(0.5)
|
|||
|
# 点击买入
|
|||
|
pyautogui.press('f1')
|
|||
|
time.sleep(0.2)
|
|||
|
pyautogui.press('f1')
|
|||
|
|
|||
|
self.__trade(stock_no, price, num)
|
|||
|
|
|||
|
def __trade(self, stock_no, price, amount):
|
|||
|
time.sleep(0.2)
|
|||
|
|
|||
|
# 设置股票代码
|
|||
|
self.main_wnd.window(control_id=0x408, class_name="Edit").click()
|
|||
|
self.main_wnd.window(control_id=0x408, class_name="Edit").set_text(str(stock_no))
|
|||
|
time.sleep(2)
|
|||
|
|
|||
|
# # 无法判断市场类型
|
|||
|
# try:
|
|||
|
# self._app.top_window().set_focus()
|
|||
|
# result_a = self._app.top_window().window(control_id=0xBBA, class_name='Static').window_text()
|
|||
|
# result_b = self._app.top_window().window(control_id=0x7AF, class_name='Button').window_text()
|
|||
|
# result_c = self._app.top_window().window(control_id=0x7CD, class_name='Button').window_text()
|
|||
|
# if '该证券代码在多个市场存在,客户端无法判' in result_a:
|
|||
|
# if ticker_name in result_b:
|
|||
|
# self._app.top_window().window(control_id=0x7AF, class_name="Button").click()
|
|||
|
# time.sleep(0.5)
|
|||
|
# elif ticker_name in result_c:
|
|||
|
# self._app.top_window().window(control_id=0x7CD, class_name="Button").click()
|
|||
|
# time.sleep(0.5)
|
|||
|
# except Exception as e:
|
|||
|
# pass
|
|||
|
|
|||
|
if price in ['b1','b2','b3','b4','b5','s1','s2','s3','s4','s5']:
|
|||
|
price = float(self.main_wnd.window(control_id=self.trade_control_id_group[price], class_name="Static").window_text())
|
|||
|
|
|||
|
|
|||
|
# 设置价格
|
|||
|
self.main_wnd.window(control_id=0x409, class_name="Edit").set_text(str(price))
|
|||
|
time.sleep(0.5)
|
|||
|
|
|||
|
# # 判断股票还是债券(1:债券)
|
|||
|
# if self.is_bond == 1:
|
|||
|
# # 买
|
|||
|
# unit = self.main_wnd.window(control_id=0x579, class_name="Static").window_text()
|
|||
|
# if unit[-2] == '股':
|
|||
|
# amount = int(int(amount) / 10)
|
|||
|
# # 卖
|
|||
|
# if self.is_buy == 0:
|
|||
|
# sell_num = int(self.main_wnd.window(control_id=0x40E, class_name="Static").window_text())
|
|||
|
# if sell_num < int(amount):
|
|||
|
# amount = int(int(amount) / 10)
|
|||
|
# print('卖出数量已减少10倍')
|
|||
|
|
|||
|
# 设置股数目
|
|||
|
self.main_wnd.window(control_id=0x40A, class_name="Edit").set_text(str(amount))
|
|||
|
time.sleep(0.5)
|
|||
|
|
|||
|
# 点击卖出or买入
|
|||
|
self.main_wnd.window(control_id=0x3EE, class_name="Button").click()
|
|||
|
time.sleep(1)
|
|||
|
|
|||
|
# 小数后只能填2位情况
|
|||
|
self._app.top_window().set_focus()
|
|||
|
result_1 = self._app.top_window().window(control_id=0x410, class_name='Static').window_text()
|
|||
|
if result_1 == '委托价格的小数部分应为 2 位,是否继续?':
|
|||
|
# 点击“否”
|
|||
|
self._app.top_window().window(control_id=0x7, class_name='Button').click()
|
|||
|
# 保留两位小数截断
|
|||
|
price = str(price)[:-1]
|
|||
|
# 重新设置价格
|
|||
|
self.main_wnd.window(control_id=0x409, class_name="Edit").set_text(str(price))
|
|||
|
time.sleep(0.5)
|
|||
|
# 点击卖出or买入
|
|||
|
self.main_wnd.window(control_id=0x3EE, class_name="Button").click()
|
|||
|
time.sleep(1)
|
|||
|
self._app.top_window().set_focus()
|
|||
|
result_1 = self._app.top_window().window(control_id=0x410, class_name='Static').window_text()
|
|||
|
else:
|
|||
|
# 摘取合同信息
|
|||
|
self.__parse_contract(result_1)
|
|||
|
time.sleep(0.2)
|
|||
|
# # 摘取合同信息
|
|||
|
# self._app.top_window().set_focus()
|
|||
|
# result_1 = self._app.top_window().window(control_id=0x410, class_name='Static').window_text()
|
|||
|
# self.__parse_contract(result_1)
|
|||
|
# time.sleep(0.2)
|
|||
|
|
|||
|
# 确认买入
|
|||
|
pywinauto.keyboard.send_keys("{ENTER}")
|
|||
|
time.sleep(0.5)
|
|||
|
|
|||
|
# 摘取合同信息2
|
|||
|
result_2 = self._app.top_window().window(control_id=0x3EC, class_name='Static').window_text()
|
|||
|
print(result_2)
|
|||
|
|
|||
|
# 确认合同
|
|||
|
pywinauto.keyboard.send_keys("{ENTER}")
|
|||
|
time.sleep(0.5)
|
|||
|
|
|||
|
# 卖
|
|||
|
def sell(self, stock_no, price, num):
|
|||
|
time.sleep(0.5)
|
|||
|
# 点击卖出
|
|||
|
# pywinauto.keyboard.send_keys("{F2}")
|
|||
|
pyautogui.press('f2')
|
|||
|
time.sleep(0.5)
|
|||
|
pyautogui.press('f2')
|
|||
|
self.is_buy = 0
|
|||
|
self.__trade(stock_no, price, num)
|
|||
|
|
|||
|
# 撤单
|
|||
|
def get_cancel_entrust(self, ticker):
|
|||
|
# # 进入撤单页面
|
|||
|
# # self._app.top_window().set_focus()
|
|||
|
# time.sleep(0.5)
|
|||
|
# pyautogui.press('f1')
|
|||
|
# time.sleep(0.5)
|
|||
|
# pyautogui.press('f3')
|
|||
|
|
|||
|
pyautogui.press('f5')
|
|||
|
time.sleep(1)
|
|||
|
# 删除框内ticker
|
|||
|
self.main_wnd.window(control_id=0x3421A0, class_name='Edit').click()
|
|||
|
for i in range(7):
|
|||
|
pywinauto.keyboard.send_keys("{DELETE}")
|
|||
|
|
|||
|
# 输入ticker
|
|||
|
self.main_wnd.window(control_id=0x3421A0, class_name='Edit').type_keys(ticker)
|
|||
|
time.sleep(0.2)
|
|||
|
# 选中ticker
|
|||
|
pywinauto.keyboard.send_keys("{ENTER}")
|
|||
|
time.sleep(0.1)
|
|||
|
# 点击撤单
|
|||
|
self.main_wnd.window(control_id=0x44B, class_name='Button').click()
|
|||
|
time.sleep(0.1)
|
|||
|
# 确认撤单
|
|||
|
pywinauto.keyboard.send_keys("{ENTER}")
|
|||
|
time.sleep(0.2)
|
|||
|
# result = self._app.top_window().window(control_id=0x3EC, class_name='Static').window_text()
|
|||
|
# print(result)
|
|||
|
pywinauto.keyboard.send_keys("{ENTER}")
|
|||
|
time.sleep(0.2)
|
|||
|
pywinauto.keyboard.send_keys("{ESC}")
|
|||
|
time.sleep(1)
|
|||
|
|
|||
|
# 撤单
|
|||
|
def cancel_entrust(self):
|
|||
|
# # 进入撤单页面
|
|||
|
# # self._app.top_window().set_focus()
|
|||
|
# time.sleep(0.5)
|
|||
|
# pyautogui.press('f1')
|
|||
|
# time.sleep(0.5)
|
|||
|
# pyautogui.press('f3')
|
|||
|
|
|||
|
pyautogui.press('f5')
|
|||
|
time.sleep(1)
|
|||
|
# 一键全撤
|
|||
|
self.main_wnd.window(control_id=0x7531, class_name='Button').click()
|
|||
|
self._app.top_window().set_focus()
|
|||
|
# self.main_wnd.window(control_id=0x6, class_name='Button').click()
|
|||
|
# time.sleep(1)
|
|||
|
# 确认买入
|
|||
|
pywinauto.keyboard.send_keys("{ENTER}")
|
|||
|
time.sleep(0.5)
|
|||
|
|
|||
|
# 获取未成交单
|
|||
|
def get_unsettled_trades(self):
|
|||
|
time.sleep(0.5)
|
|||
|
pyautogui.press('f1')
|
|||
|
time.sleep(0.5)
|
|||
|
pyautogui.press('f3')
|
|||
|
time.sleep(0.5)
|
|||
|
# 刷新页面
|
|||
|
pyautogui.press('f5')
|
|||
|
time.sleep(0.2)
|
|||
|
pyautogui.press('f5')
|
|||
|
time.sleep(0.2)
|
|||
|
# 复制表格数据
|
|||
|
self._copy_data()
|
|||
|
if self.verify_code_wrong == 1: # 验证码错误时将self.verify_code_wrong置1
|
|||
|
self.get_today_trades()
|
|||
|
self.verify_code_wrong = 0 # 验证码正确时将self.verify_code_wrong置0
|
|||
|
self._data_to_df()
|
|||
|
self.df_unsettled = self.df
|
|||
|
return self.df_unsettled
|
|||
|
|
|||
|
# 获取当日实际持仓
|
|||
|
def get_position(self):
|
|||
|
"""获取资金情况"""
|
|||
|
pyautogui.press('f1')
|
|||
|
time.sleep(0.5)
|
|||
|
pyautogui.press('f4')
|
|||
|
time.sleep(0.5)
|
|||
|
# 刷新页面
|
|||
|
pyautogui.press('f5')
|
|||
|
time.sleep(0.2)
|
|||
|
pyautogui.press('f5')
|
|||
|
time.sleep(0.2)
|
|||
|
# 复制表格数据
|
|||
|
self._copy_data()
|
|||
|
if self.verify_code_wrong == 1: # 验证码错误时将self.verify_code_wrong置1
|
|||
|
self.get_today_trades()
|
|||
|
self.verify_code_wrong = 0 # 验证码正确时将self.verify_code_wrong置0
|
|||
|
self._data_to_df()
|
|||
|
self.df_position = self.df
|
|||
|
|
|||
|
# 处理self.df_position格式
|
|||
|
del (self.df_position['交易市场'])
|
|||
|
del (self.df_position['股东代码'])
|
|||
|
del (self.df_position['成本金额'])
|
|||
|
del (self.df_position['买入冻结'])
|
|||
|
del (self.df_position['卖出冻结'])
|
|||
|
del (self.df_position['在途卖出'])
|
|||
|
del (self.df_position['在途买入'])
|
|||
|
del (self.df_position['Unnamed: 18'])
|
|||
|
del (self.df_position['股份余额'])
|
|||
|
del (self.df_position['币种'])
|
|||
|
|
|||
|
|
|||
|
self.df_position.rename(columns={"证券代码": "Ticker",
|
|||
|
"证券名称": "Ticker_name",
|
|||
|
"当前数量": "Number_transactions",
|
|||
|
"可卖数量": "cash",
|
|||
|
"冻结数量": "frozen_quantity",
|
|||
|
"参考成本价": "cost_price",
|
|||
|
"最新价": "market_price",
|
|||
|
"最新市值": "market_value",
|
|||
|
"参考盈亏": "profit_and_loss",
|
|||
|
"盈亏比例(%)": "profit_loss_ratio",
|
|||
|
}, inplace=True)
|
|||
|
return self.df_position
|
|||
|
|
|||
|
# 获取资金情况
|
|||
|
def get_balance(self):
|
|||
|
"""获取资金情况"""
|
|||
|
pyautogui.press('f1')
|
|||
|
time.sleep(0.5)
|
|||
|
pyautogui.press('f4')
|
|||
|
time.sleep(0.5)
|
|||
|
# 刷新页面
|
|||
|
pyautogui.press('f5')
|
|||
|
time.sleep(0.2)
|
|||
|
pyautogui.press('f5')
|
|||
|
time.sleep(0.2)
|
|||
|
|
|||
|
result = {}
|
|||
|
for key, control_id in self.BALANCE_CONTROL_ID_GROUP.items():
|
|||
|
result[key] = float(self.main_wnd.window(control_id=control_id, class_name="Static").window_text())
|
|||
|
return result
|
|||
|
|
|||
|
# 获取左边窗口
|
|||
|
def _get_left_handle(self):
|
|||
|
while True:
|
|||
|
try:
|
|||
|
self.left_handle = self.main_wnd.window(control_id=0x81, class_name='SysTreeView32')
|
|||
|
self.left_handle.wait('ready', 2) # sometime can't find handle ready, must retry
|
|||
|
break
|
|||
|
except Exception as ex:
|
|||
|
print(ex)
|
|||
|
pass
|
|||
|
|
|||
|
# 复制数据
|
|||
|
def _copy_data(self):
|
|||
|
|
|||
|
# 复制表格数据
|
|||
|
keyboard.send_keys('^C')
|
|||
|
# 获取验证码
|
|||
|
id_code = self.get_copy_date_verify_code()
|
|||
|
time.sleep(0.2)
|
|||
|
self._app.top_window().Edit.type_keys('{BACKSPACE}' * 6)
|
|||
|
time.sleep(0.2)
|
|||
|
self._app.top_window().Edit3.type_keys(id_code)
|
|||
|
time.sleep(0.2)
|
|||
|
pywinauto.keyboard.send_keys("{ENTER}")
|
|||
|
time.sleep(1)
|
|||
|
# 若验证码错误
|
|||
|
try:
|
|||
|
result_id = self._app.top_window().window(control_id=0x966, class_name='Static').window_text()
|
|||
|
if '验证码错误' in result_id:
|
|||
|
self._app.top_window().window(control_id=0x2, class_name='Button').click()
|
|||
|
self.verify_code_wrong = 1
|
|||
|
else:
|
|||
|
self.verify_code_wrong = 0
|
|||
|
except Exception as e:
|
|||
|
self.verify_code_wrong = 0
|
|||
|
pass
|
|||
|
|
|||
|
# 复制的数据转为DataFrame
|
|||
|
def _data_to_df(self):
|
|||
|
# 数据转为Dateframe
|
|||
|
data_table = clipboard.GetData()
|
|||
|
df_table = pd.read_csv(mio.StringIO(data_table), delimiter='\t', na_filter=False)
|
|||
|
table_dict = df_table.to_dict('records')
|
|||
|
if len(table_dict) == 0:
|
|||
|
self.df = []
|
|||
|
return
|
|||
|
else:
|
|||
|
data = []
|
|||
|
for i in table_dict:
|
|||
|
data.append(pd.DataFrame.from_dict(i, orient='index').T)
|
|||
|
self.df = pd.concat(data, ignore_index=True)
|
|||
|
|
|||
|
# 获取委托单
|
|||
|
def get_order(self):
|
|||
|
|
|||
|
# 点击当日委托
|
|||
|
path = ['查询[F4]', '当日委托']
|
|||
|
self._get_left_handle() # 获取self.left_handle
|
|||
|
self.left_handle.get_item(path, exact=False).click_input(button=u'left', double=False, wheel_dist=0,
|
|||
|
pressed=u'')
|
|||
|
# x, y = win32api.GetCursorPos()
|
|||
|
# pywinauto.mouse.click(button='left', coords=(x, y + 100))
|
|||
|
|
|||
|
# 刷新页面
|
|||
|
pyautogui.press('f5')
|
|||
|
time.sleep(0.2)
|
|||
|
pyautogui.press('f5')
|
|||
|
time.sleep(0.2)
|
|||
|
|
|||
|
self._copy_data()
|
|||
|
if self.verify_code_wrong == 1: # 验证码错误时将self.verify_code_wrong置1
|
|||
|
self.get_order()
|
|||
|
self.verify_code_wrong = 0 # 验证码正确时将self.verify_code_wrong置0
|
|||
|
self._data_to_df()
|
|||
|
# print(self.df)
|
|||
|
return self.df
|
|||
|
|
|||
|
# 获取当日成交
|
|||
|
def get_today_trades(self):
|
|||
|
"""
|
|||
|
返回: self.table_df:汇总前表格
|
|||
|
self.df: 汇总后表格
|
|||
|
"""
|
|||
|
time.sleep(1)
|
|||
|
# 点击当日成交
|
|||
|
path = ['查询[F4]', '当日成交']
|
|||
|
self._get_left_handle() # 获取self.left_handle
|
|||
|
self.left_handle.get_item(path, exact=False).click_input(button=u'left', double=True, wheel_dist=0,
|
|||
|
pressed=u'')
|
|||
|
# x_1, y_1 = win32api.GetCursorPos()
|
|||
|
# pywinauto.mouse.click(button='left', coords=(x_1, y_1 + 50))
|
|||
|
# x_2, y_2 = win32api.GetCursorPos()
|
|||
|
# pywinauto.mouse.click(button='left', coords=(x_2, y_2 + 25))
|
|||
|
|
|||
|
# 关闭广告
|
|||
|
try:
|
|||
|
self._app.top_window().set_focus()
|
|||
|
time.sleep(0.5)
|
|||
|
pywinauto.keyboard.send_keys("{ESC}")
|
|||
|
time.sleep(0.5)
|
|||
|
# pywinauto.keyboard.send_keys("{ENTER}")
|
|||
|
except Exception as e:
|
|||
|
pass
|
|||
|
|
|||
|
# 刷新页面
|
|||
|
pyautogui.press('f5')
|
|||
|
time.sleep(0.2)
|
|||
|
pyautogui.press('f5')
|
|||
|
time.sleep(0.2)
|
|||
|
# 数据转为DataFrame
|
|||
|
self._copy_data()
|
|||
|
if self.verify_code_wrong == 1: # 验证码错误时将self.verify_code_wrong置1
|
|||
|
self.get_today_trades()
|
|||
|
self.verify_code_wrong = 0 # 验证码正确时将self.verify_code_wrong置0
|
|||
|
self._data_to_df()
|
|||
|
if len(self.df) == 0:
|
|||
|
return
|
|||
|
|
|||
|
self.table_df = self.df # 获取汇总前self.table_df
|
|||
|
|
|||
|
# 窗口最大化
|
|||
|
try:
|
|||
|
self.main_wnd.maximize()
|
|||
|
except Exception as e:
|
|||
|
pass
|
|||
|
time.sleep(0.5)
|
|||
|
|
|||
|
# 数据汇总
|
|||
|
pyautogui.click(self.position['中心'])
|
|||
|
time.sleep(0.1)
|
|||
|
|
|||
|
# 右键
|
|||
|
pyautogui.rightClick() # 右键
|
|||
|
time.sleep(0.2)
|
|||
|
|
|||
|
# 点击常见汇总
|
|||
|
pyautogui.press('down')
|
|||
|
time.sleep(0.2)
|
|||
|
pyautogui.press('down')
|
|||
|
time.sleep(0.2)
|
|||
|
pyautogui.press('enter')
|
|||
|
time.sleep(0.2)
|
|||
|
# 点击代码+操作
|
|||
|
pyautogui.press('down')
|
|||
|
time.sleep(0.2)
|
|||
|
pyautogui.press('enter')
|
|||
|
time.sleep(0.2)
|
|||
|
|
|||
|
self._copy_data()
|
|||
|
if self.verify_code_wrong == 1: # 验证码错误时将self.verify_code_wrong置1
|
|||
|
self.get_today_trades()
|
|||
|
self.verify_code_wrong = 0 # 验证码正确时将self.verify_code_wrong置0
|
|||
|
self._data_to_df() # 获取self.df
|
|||
|
|
|||
|
return self.df
|
|||
|
|
|||
|
|
|||
|
# 交易记录入库(一)
|
|||
|
def save_trades_sql(self):
|
|||
|
As_Of_Date = datetime.datetime.today().strftime('%Y-%m-%d')
|
|||
|
self.get_today_trades()
|
|||
|
|
|||
|
if len(self.df) == 0:
|
|||
|
print('无当日成交---------------------')
|
|||
|
# return False
|
|||
|
else:
|
|||
|
# 通过展示所有字段,摘取所需字段
|
|||
|
need_info_df = self.df[['证券代码', '证券名称', '买卖标志', '成交数量', '成交价格']]
|
|||
|
|
|||
|
need_info_df['Account_name'] = self.account_name
|
|||
|
need_info_df['As_Of_Date'] = As_Of_Date
|
|||
|
need_info_df['买卖标志'] = need_info_df['买卖标志'].apply(
|
|||
|
lambda x: '卖' if x == '卖出' else x)
|
|||
|
need_info_df['买卖标志'] = need_info_df['买卖标志'].apply(
|
|||
|
lambda x: '卖' if x == '证券卖出' else x)
|
|||
|
need_info_df['买卖标志'] = need_info_df['买卖标志'].apply(
|
|||
|
lambda x: '买' if x == '证券买入' else x)
|
|||
|
need_info_df['买卖标志'] = need_info_df['买卖标志'].apply(
|
|||
|
lambda x: '买' if x == '买入' else x)
|
|||
|
need_info_df.rename(columns={"证券代码": "Ticker",
|
|||
|
"证券名称": "Ticker_name",
|
|||
|
"买卖标志": "Operate",
|
|||
|
"成交数量": "Number_transactions",
|
|||
|
"成交价格": "Average_price",
|
|||
|
}, inplace=True)
|
|||
|
need_info_df['Ticker'] = need_info_df['Ticker'].map(
|
|||
|
lambda x: 'SH' + str("%06d" % int(x)) if str("%06d" % int(x))[0] == '6' or str("%06d" % int(x))[
|
|||
|
0] == '5' else 'SZ' + str("%06d" % int(x)))
|
|||
|
|
|||
|
|
|||
|
# 去掉债券前面的SH/SZ
|
|||
|
need_info_df['Ticker'] = need_info_df['Ticker'].apply(
|
|||
|
lambda x: x.strip('SH').strip('SZ') if x.strip('SH').strip('SZ') in self.df_list else x)
|
|||
|
|
|||
|
pd.set_option('display.max_columns', None)
|
|||
|
pd.set_option('display.max_rows', None)
|
|||
|
# print(need_info_df)
|
|||
|
|
|||
|
# 入库
|
|||
|
try:
|
|||
|
return need_info_df
|
|||
|
# engine_auto_update_web_strategy = create_engine('mysql+pymysql://cn_ainvest_db:cn_ainvest_sd3a1@rm-2zewagytttzk6f24xno.mysql.rds.aliyuncs.com:3306/auto_update_web_strategy', encoding="utf-8", echo=False)
|
|||
|
# need_info_df.to_sql('daily_transaction_record',engine_auto_update_web_strategy,if_exists='append',index=False,chunksize=1000)
|
|||
|
print(self.account_name,"入库成功!")
|
|||
|
except Exception as e:
|
|||
|
# print(e)
|
|||
|
print(self.account_name,"入库失败!!!!!!!!!!!!!!!!!!!!!!!!!!")
|
|||
|
|
|||
|
|
|||
|
|
|||
|
# 交易记录存表(二)
|
|||
|
def save_trade_table(self):
|
|||
|
|
|||
|
# 获取当日成交
|
|||
|
As_Of_Date = datetime.datetime.today().strftime('%Y%m%d')
|
|||
|
As_Of_Date = As_Of_Date[-4:]
|
|||
|
# df = self.get_today_trades()
|
|||
|
if len(self.df) == 0:
|
|||
|
return
|
|||
|
else:
|
|||
|
# 填写备注
|
|||
|
df_trade = self._get_df_trade()
|
|||
|
# pc_name111 = getpass.getuser() # 返回用户名
|
|||
|
# df_trade = pd.read_excel(f'C:\\Users\\{pc_name111}\\Desktop\\{self.today}_股票交易列表.xls')
|
|||
|
df_trade['用户名'] = df_trade['用户名'].map(lambda x:str(x))
|
|||
|
df_user = df_trade[df_trade['用户名'].isin([self.account_name])]
|
|||
|
df_user = df_user.drop_duplicates('股票代码')
|
|||
|
df_user['股票代码'] = df_user['股票代码'].map(lambda x: x.strip('SH').strip('SZ'))
|
|||
|
self.df['证券代码'] = self.df['证券代码'].map(lambda x: str(x))
|
|||
|
strategy_ticker_list = df_user['股票代码'].tolist()
|
|||
|
|
|||
|
self.df['备注'] = '客户操作'
|
|||
|
self.df.loc[self.df['证券代码'].isin(strategy_ticker_list), '备注'] = '策略选股'
|
|||
|
|
|||
|
# 填写成交时间
|
|||
|
df_time = self.table_df.drop_duplicates('证券代码')
|
|||
|
time_ticker_list = df_time['证券代码'].tolist()
|
|||
|
self.df['证券代码'] = self.df['证券代码'].map(lambda x:int(x))
|
|||
|
for i in time_ticker_list:
|
|||
|
df_ticker_one = self.table_df[self.table_df['证券代码'].isin([i])]
|
|||
|
time = max(df_ticker_one['成交时间'])
|
|||
|
self.df.loc[self.df['证券代码'].isin([i]), '成交时间'] = time
|
|||
|
|
|||
|
# 存储入表
|
|||
|
return self.df
|
|||
|
# self.df.to_excel(f'C:\\Users\\{pc_name111}\\Desktop\\{self.account_name}_{As_Of_Date}.xls')
|
|||
|
# self.df.to_excel(f'C:\\Users\\{pc_name111}\\Desktop\\{As_Of_Date}_交易记录\\{self.account_name}_{As_Of_Date}.xls')
|
|||
|
print('存表成功')
|
|||
|
|
|||
|
# 读取交易指令表
|
|||
|
def _get_df_trade(self):
|
|||
|
engine_user_center = create_engine(
|
|||
|
'mysql+pymysql://cn_ainvest_db:cn_ainvest_sd3a1@rm-2zewagytttzk6f24xno.mysql.rds.aliyuncs.com:3306/user_center',
|
|||
|
encoding="utf-8", echo=False)
|
|||
|
today = str(datetime.date.today())
|
|||
|
df_trade = pd.read_sql(f'select * from trade_instruction where As_Of_Date = "{today}"', engine_user_center)
|
|||
|
return df_trade
|
|||
|
|
|||
|
# 解析合同
|
|||
|
@staticmethod
|
|||
|
def __parse_contract(result):
|
|||
|
""" 解析买入卖出的结果 """
|
|||
|
|
|||
|
# 股东帐号:A217477596
|
|||
|
# 证券代码:600000
|
|||
|
# 买入价格:3.330
|
|||
|
# 买入数量:100
|
|||
|
result_list = result.split('\n')
|
|||
|
for i in result_list[:-1]:
|
|||
|
print(i,end=', ')
|
|||
|
|
|||
|
|
|||
|
if __name__ == '__main__':
|
|||
|
account_name = str('18242094506')
|
|||
|
is_bond = 0
|
|||
|
# user = str('802102044749')
|
|||
|
user = '1910004175'
|
|||
|
password = str('210419')
|
|||
|
a = HYClientTrader(account_name)
|
|||
|
a.login()
|
|||
|
|
|||
|
# # 最小化
|
|||
|
# a.minimize()
|
|||
|
|
|||
|
# # 买入
|
|||
|
# a.buy('600000', '3.333','100')
|
|||
|
|
|||
|
# # 卖出
|
|||
|
# a.sell('600835', '14.45','900')
|
|||
|
|
|||
|
# # 撤单
|
|||
|
# a.cancel_entrust()
|
|||
|
|
|||
|
# # 获取未成交单
|
|||
|
# print(a.get_unsettled_trades())
|
|||
|
|
|||
|
# # 获取当日实际持仓
|
|||
|
# print(a.get_position())
|
|||
|
|
|||
|
# # 获取资金情况
|
|||
|
# print(a.balance())
|
|||
|
|
|||
|
# 获取委托单
|
|||
|
# print(a.get_order())
|
|||
|
|
|||
|
# # 获取当日成交
|
|||
|
# a.get_today_trades()
|
|||
|
# print(a.df)
|
|||
|
|
|||
|
# # 交易记录入库
|
|||
|
# a.save_trades_sql()
|
|||
|
|
|||
|
# # 交易记录存表
|
|||
|
# a.save_trade_table()
|
|||
|
|
|||
|
|