zhitou_trade/autobasic/anxin_clienttrader.py
2025-07-10 15:55:34 +08:00

658 lines
22 KiB
Python

# -*- coding: utf-8 -*-
"""
Created on Fri Aug 5 15:29:12 2022
@author: MINGL
"""
import pandas as pd
import pyautogui
import pywinauto
from pywinauto import clipboard
from pywinauto import keyboard
from skimage import io
import time
import io as mio
import pytesseract
import warnings
import datetime
from tools import *
from config import ssid,password
warnings.filterwarnings('ignore')
from pathlib import Path
root_path = Path(__file__).parent.parent
path = r'C:\\安翼金融终端\\AXTrade.exe'
engine = create_engine(
'mysql+pymysql://cn_ainvest_db:cn_ainvest_sd3a1@rm-2zewagytttzk6f24xno.mysql.rds.aliyuncs.com:3306/',
encoding="utf-8", echo=False)
class AXClientTrader:
"""
基于同花顺委托下单程序的"平安证券客户端"自动交易程序
"""
def __init__(self, account_name, exe_path=path):
self.path = exe_path
self.account_name = account_name
self.log = Logger(f'{root_path}/logs',self.account_name)
# 用户券商信息
sql = f"select * from ainvest_usercount where username='{account_name}'"
df_count = download_data_from_db(sql, 'ai_strategy_update_iddb')
self.securities_name = str(df_count['securities_username'].tolist()[0])
self.securities_password = str(df_count['securities_password'].tolist()[0])
self.communication_password = str(df_count['communication_password'].tolist()[0])
def exit(self):
'''
结束进程
Returns
-------
None.
'''
self._app.kill()
def _close_prompt_windows(self):
time.sleep(1)
for window in self._app.windows(class_name="#32770"):
if window.window_text() != '国投交易*':
window.close()
time.sleep(0.1)
def login(self):
'''
启动软件并登录
Returns
-------
None.
'''
account_name = self.account_name
broker = '安信证券'
#软件启动判断,若有进行中进程,先关闭再重启
try:
self._app = pywinauto.Application().connect(
path=self.path, timeout=1
)
self.exit()
self.log.warn('There are running programs, and there are processes in retreat')
except Exception:
pass
#启动软件进程,输入账号密码
self.log.info('Start login')
# 重置mac并尝试连接WiFi
set_mac = SetMac()
set_mac.run()
connect_wifi(ssid, password)
# 获取动态IP
new_mac = set_mac.get_macinfos()
proxy_ip,proxy_port = get_proxy_ip(broker,new_mac,account_name)
#设置全局代理
set_proxy(proxy_ip,proxy_port)
self.log.info('全局代理设置成功')
self._app = pywinauto.Application().start(self.path)
while True:
try:
self._app.window(title_re='安翼交易登录.*').wait("ready")
break
except RuntimeError:
pass
time.sleep(0.5)
self._app.window(title_re='安翼交易登录.*').window(control_id=0x507, class_name='Edit').type_keys(self.securities_name)
time.sleep(0.1)
self._app.window(title_re='安翼交易登录.*').window(control_id=0x49F, class_name='Edit').type_keys(self.securities_password)
time.sleep(0.1)
pywinauto.keyboard.send_keys("{ENTER}")
time.sleep(2)
#关闭弹窗
# for i in range(3):
# self._turn_off_advertising()
tryrun(self._app.window(title_re='国投交易.*').wait("ready"))
# pywinauto.keyboard.send_keys("{ESC}")
self._close_prompt_windows()
#设置主窗口及菜单窗口
self.main_wnd = self._app.window(title='国投交易')
self.left_wnd = self.main_wnd.window(class_name='SysTreeView32', control_id=0x4AD)
def _turn_off_advertising(self):
'''
关闭弹窗
Returns
-------
None.
'''
try:
self._app.top_window().set_focus()
time.sleep(0.5)
pywinauto.keyboard.send_keys("{ESC}")
# pywinauto.keyboard.send_keys("{ENTER}")
# pywinauto.keyboard.send_keys("{ESC}")
except Exception as e:
pass
def verify_code(self,control_id):
'''
获取登录验证码
Returns
-------
id_code : TYPE
DESCRIPTION.
'''
time.sleep(0.5)
self._app.top_window().Static.click()
time.sleep(0.2)
self._app.top_window().window(control_id=control_id, class_name='Static').capture_as_image().save(
f'{root_path}/imgs/temp.png')
time.sleep(0.2)
im = io.imread(f'{root_path}/imgs/temp.png')
id_code = pytesseract.image_to_string(im, lang='eng',
config='--psm 6 --oem 3 -c tessedit_char_whitelist=0123456789').strip()
id_code = id_code.replace(' ', '')
return id_code
def buy(self, stock_no, price, num):
'''
买入股票
Parameters
----------
stock_no : TYPE
DESCRIPTION.
price : TYPE
DESCRIPTION.
num : TYPE
DESCRIPTION.
Returns
-------
None.
'''
self.left_wnd.select(['ÂòÈë[F1]'])
self.__trade(stock_no, price, num)
def sell(self, stock_no, price, num):
'''
卖出股票
Parameters
----------
stock_no : TYPE
DESCRIPTION.
price : TYPE
DESCRIPTION.
num : TYPE
DESCRIPTION.
Returns
-------
None.
'''
self.left_wnd.select(['Âô³ö[F2]'])
self.__trade(stock_no, price, num)
def get_cancel_entrust(self, ticker):
'''
按股票撤单
Parameters
----------
ticker : TYPE
DESCRIPTION.
Returns
-------
None.
'''
self.left_wnd.select(['撤单[F3]'])
time.sleep(1)
# 删除框内ticker
self.main_wnd.window(control_id=0xD14, class_name='Edit').click()
for i in range(7):
pywinauto.keyboard.send_keys("{DELETE}")
# 输入ticker
self.main_wnd.window(control_id=0xD14, 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()
self.log.info(result)
pywinauto.keyboard.send_keys("{ENTER}")
time.sleep(0.2)
pywinauto.keyboard.send_keys("{ESC}")
def cancel_entrust(self):
'''
全撤
Returns
-------
None.
'''
# # 进入撤单页面
self.left_wnd.select(['撤单[F3]'])
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=0x44B, class_name='Button').click()
time.sleep(1)
pywinauto.keyboard.send_keys("{ENTER}")
time.sleep(0.5)
pywinauto.keyboard.send_keys("{ENTER}")
def get_unsettled_trades(self):
'''
获取未成交单
Returns
-------
one_df : TYPE
DESCRIPTION.
'''
self.left_wnd.select(['撤单[F3]'])
self._copy_data()
one_df =self._data_to_df()
if len(one_df) == 0:
pass
else:
one_df['证券代码'] = one_df['证券代码'].apply(lambda x: tranTicker(x.strip('=').strip('"')))
return one_df
def _output_data(self):
self._app.top_window().set_focus()
time.sleep(1)
self._app.top_window().set_focus()
# try:
# print(self._app.window(title_re='提示').window_text())
# except:
# pass
self._app.window(title_re='另存为').window(control_id = 0x3E9,class_name = 'Edit',found_index = 0).type_keys(str(datetime.date.today()).replace("-","")+'结果明细'+self.account_name)
# print("4")
time.sleep(1)
pywinauto.keyboard.send_keys("{ENTER}")
# self._app.window(control_id=0x0, class_name='#32770').window(control_id=0x1, class_name='Button').click()
# print("5")
self._app['提示'].set_focus()
time.sleep(0.5)
pywinauto.keyboard.send_keys('^Y')
self._app.top_window().set_focus()
pywinauto.keyboard.send_keys('{ENTER}')
time.sleep(2)
# df = pd.read_excel(fr'C:\Users\MINGL\Downloads\股票明细.xls',skiprows=1)
def get_balance(self):
'''
获取资金情况
Returns
-------
record : TYPE
DESCRIPTION.
'''
self.left_wnd.select(['³Ö²Ö[F4]'])
time.sleep(2)
balance_dict_id = {
"总资产": 0xAB6,
"可用金额": 0xAC8,
"股票市值": 0xAB5,
}
self.main_wnd = self._app.window(title='国投交易')
result = {}
for key, control_id in balance_dict_id.items():
self.main_wnd.window(control_id=0x0, class_name='#32770', found_index=0).window(control_id=control_id,
class_name="Static").click()
result[key] = self.main_wnd.window(control_id = 0x0,class_name = '#32770',found_index = 0).window(control_id=control_id, class_name="Static").window_text()
print(self.main_wnd.window(control_id = 0x0,class_name = '#32770',found_index = 0).window(control_id=control_id, class_name="Static").window_text())
record = pd.DataFrame.from_dict(result, orient='index').T
record = record[['可用金额', '股票市值', '总资产']]
record.rename(columns={'可用金额': 'cash', '股票市值': 'Position_amount', '总资产': 'Total_account'}, inplace=True)
record['As_Of_Date'] = datetime.date.today()
record['Account_Name'] = self.account_name
return record
def get_position(self):
'''
获取持仓情况
Returns
-------
df_position : TYPE
DESCRIPTION.
'''
self.left_wnd.select(['³Ö²Ö[F4]'])
# 复制表格数据
time.sleep(1)
self.main_wnd.window(control_id=0x436, class_name="Button", found_index=0).click()
self._app.top_window().set_focus()
time.sleep(1)
self._output_data()
df = pd.read_excel(fr'C:\Users\Administrator\Documents\Downloads\{str(datetime.date.today()).replace("-","")}结果明细{self.account_name}.xls', header=1)
df_position = df
# 处理self.df_position格式
df_position = df_position[['证券代码', '证券名称', '证券总额', '可用数量', '盈亏成本价', '最新价', '参考市值', '参考盈亏',
'参考盈亏率', '冻结数量']]
# # ANXIN
df_position['参考盈亏率'] = df_position['参考盈亏率'].apply(lambda x:x.replace('%',''))
df_position.rename(columns={'证券代码': 'Ticker', '证券名称': 'Ticker_name', '证券总额': 'Number_transactions',
'可用数量': 'cash', '盈亏成本价': 'cost_price', '最新价': 'market_price',
'参考市值': 'market_value', '参考盈亏': 'profit_and_loss', '参考盈亏率': 'profit_loss_ratio',
'冻结数量': 'frozen_quantity'}, inplace=True)
df_position['Ticker'] = df_position['Ticker'].apply(lambda x: tranTicker(x))
# print(df_position)
return df_position
def get_order(self):
'''
获取委托情况
Returns
-------
TYPE
DESCRIPTION.
'''
self.left_wnd.select(['查询[F4]','当日委托'])
self._copy_data()
return self._data_to_df()
def get_today_trades(self):
'''
获取当日成交
Returns
-------
TYPE
DESCRIPTION.
'''
self.left_wnd.select(['³É½»'])
time.sleep(2)
# self.main_wnd.window(control_id=0xE9A, class_name="Button").click()
time.sleep(1)
pyautogui.click(982, 144)
time.sleep(2)
# print('4')
pyautogui.click(991, 166)
time.sleep(1)
# pyautogui.click(1872, 137)
# self.main_wnd.window(control_id=0xE9A, class_name="Button", found_index=1).click()
# print('5')
# self.main_wnd.window(control_id=0xE9A, class_name="Button").click()
# self.control_wnd = self.main_wnd.window(class_name='AxControl2021', control_id=0xA).click()
# time.sleep(1)
# self.control_wnd.select(['成交'])
self._output_data()
# df = pd.read_csv(fr'C:\Users\Administrator\Documents\Downloads\成交结果明细.csv', encoding='gbk', engine='python')
df = pd.read_excel(fr'C:\Users\Administrator\Documents\Downloads\{str(datetime.date.today()).replace("-","")}结果明细{self.account_name}.xls')
if len(df) == 0:
self.log.info('--------------------------------------无当日成交--------------------------------------')
return pd.DataFrame()
else:
df = df[['成交时间','证券代码','证券名称','方向','成交数量','成交价格']]
df['Account_Name'] = self.account_name
df['As_Of_Date'] = datetime.date.today()
df.rename(columns={'成交时间': 'Trade_time', '证券代码': 'Ticker', '证券名称': 'Ticker_name', '方向': 'Operate',
'成交数量': 'Number_transactions',
'成交价格': 'Average_price'}, inplace=True)
df['Operate'] = df['Operate'].apply(lambda x: '' if '' in x else '')
df['Ticker'] = df['Ticker'].apply(lambda x: tranTicker(str(x)))
return df
def cash_in_out(self):
engine = create_engine(
'mysql+pymysql://cn_ainvest_db:cn_ainvest_sd3a1@rm-2zewagytttzk6f24xno.mysql.rds.aliyuncs.com:3306/',
encoding="utf-8", echo=False)
self.left_wnd.select(['Ãû֧תÕË'])
today = datetime.date.today()
# today = datetime.date(2024,10,7)
trade_date_list = \
pd.read_sql(fr'select As_Of_Date from ai_strategy.trade_days where As_Of_Date < "{today}"', engine)[
'As_Of_Date'].to_list()
if today + datetime.timedelta(days=-1) in trade_date_list:
pass
else:
self.main_wnd.maximize()
date = trade_date_list[-1:][0] + datetime.timedelta(days=1)
print(date)
time.sleep(1)
pyautogui.click(889, 171)
pyautogui.write(str(date.year))
pyautogui.click(921, 173)
pyautogui.write(str(date.month))
pyautogui.click(944, 171)
pyautogui.write(str(date.day))
self.main_wnd.window(control_id=0x47A, class_name="Button").click()
time.sleep(0.5)
# self.main_wnd.window(control_id=0x41D, class_name="Button").click()
pyautogui.click(1889, 171)
time.sleep(0.5)
pyautogui.click(1882, 191)
time.sleep(1)
# self.main_wnd.window(control_id=0xC8, class_name="AfxWnd140s",found_index = 0).click()
self._output_data()
# df = pd.read_csv(fr'C:\Users\Administrator\Documents\Downloads\成交结果明细.csv', encoding='gbk', engine='python')
df = pd.read_excel(
fr'C:\Users\Administrator\Documents\Downloads\{str(datetime.date.today()).replace("-", "")}结果明细{self.account_name}.xls')
if len(df) == 0:
self.log.info('--------------------------------------无存取款--------------------------------------')
return pd.DataFrame()
else:
df_new = df[['转账日期', '转账金额', '业务名称']]
df_new = df_new[(df_new['转账金额'] != 0) &((df_new['业务名称'] =='资金转出')|(df_new['业务名称'] =='资金转入'))]
print(df_new)
df_new.columns = df_new.columns.str.strip()
df_new_rename = df_new.rename(columns={'转账金额': 'Deposit_Withdraw', '转账日期': 'As_Of_Date'})[
['As_Of_Date', 'Deposit_Withdraw']]
df_new_rename['Account_name'] = self.account_name
return df_new_rename
def __trade(self, stock_no, price, amount):
'''
交易输入函数
Parameters
----------
stock_no : TYPE
DESCRIPTION.
price : TYPE
DESCRIPTION.
amount : TYPE
DESCRIPTION.
Returns
-------
None.
'''
time.sleep(0.2)
# 设置股票代码
self.main_wnd.window(control_id=0x3E9, class_name="Edit").click()
self.main_wnd.window(control_id=0x3E9, class_name="Edit").set_text(str(stock_no))
time.sleep(0.5)
# 设置价格
self.main_wnd.window(control_id=0x46A, class_name="Edit").set_text(str(price))
time.sleep(0.5)
# 设置股数目
self.main_wnd.window(control_id=0x46D, class_name="Edit").set_text(str(amount))
time.sleep(1)
# 点击卖出or买入
self.main_wnd.window(control_id=0x0, class_name='#32770',found_index = 0).window(control_id=0x4C9, class_name="Button").click()
time.sleep(1)
#获取弹窗返回的信息
# self._app.top_window().set_focus()
try:
one_text = self._app.window(control_id=0x0, class_name='#32770',found_index=0).window_text()
self.log.info(one_text.replace('/n', ' '))
print('111111',one_text)
except:
self.main_wnd.window(control_id=0x0, class_name='#32770', found_index=0).window(control_id=0x4C9,
class_name="Button").click()
time.sleep(1)
one_text = self._app.window(control_id=0x0, class_name='#32770',found_index=0).window_text()
self.log.info(one_text.replace('/n', ' '))
print('222222',one_text)
if '委托价格的小数部分应该为2位' in one_text:
# 点击“否”
self.log.warn(one_text.replace('/n',' '))
self._app.top_window().window(control_id=0x7, class_name='Button').click()
# 保留两位小数截断
price = str(price)[:-1]
# 重新设置价格
self.main_wnd.window(control_id=0x46A, class_name="Edit").set_text(str(price))
time.sleep(0.5)
# 点击卖出or买入
self.main_wnd.window(control_id=0x4C9, class_name="Button").click()
time.sleep(1)
self._app.top_window().set_focus()
one_text = self._app.top_window().window(control_id=0x410, class_name='Static').window_text()
else:
time.sleep(0.2)
# 确认买入
# self._app.top_window().set_focus()
self._app.window(control_id=0x0, class_name='#32770',found_index = 0).window(control_id=0x1, class_name='Button',found_index = 0).click()
# pywinauto.keyboard.send_keys("{ENTER}")
time.sleep(0.5)
self._app.window(control_id=0x0, class_name='#32770',found_index = 0).window(control_id=0x1, class_name='Button',found_index = 0).click()
# pywinauto.keyboard.send_keys("{ENTER}")
def _copy_data(self):
'''
拷贝数据函数
Returns
-------
None.
'''
while True:
keyboard.send_keys('^C')
# 获取验证码
id_code = self.verify_code(0x965)
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=0x966, class_name='Static').window_text()
self.log.softInfo(result_idcode)
if '验证码错误' in result_idcode:
self.log.warn('Verification code error')
self._app.top_window().window(control_id=0x2, class_name="Button").click()
time.sleep(0.5)
else:
break
except Exception:
break
def _data_to_df(self):
'''
剪切板转dataframe
Returns
-------
TYPE
DESCRIPTION.
'''
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:
return pd.DataFrame()
else:
data = []
for i in table_dict:
data.append(pd.DataFrame.from_dict(i, orient='index').T)
return pd.concat(data, ignore_index=True)
if __name__ == '__main__':
account_name = str('13582859240')
a = AXClientTrader(account_name)
a.login()
# 买入
# a.buy('600000', '1','100')
#
# # 卖出
# a.sell('600000', '12.45','900')
# a.sell('600000', '12.45','900')
# a.sell('600000', '12.45','900')
# a.sell('600000', '12.45','900')
# a.sell('600000', '12.45','900')
# a.sell('600000', '12.45','900')
# a.sell('600000', '12.45','900')
# a.sell('600000', '12.45','900')
# a.sell('600000', '12.45','900')
# a.sell('600000', '12.45','900')
# a.sell('600000', '12.45','900')
# a.sell('600000', '12.45','900')
# a.sell('600000', '12.45','900')
# a.sell('600000', '12.45','900')
# a.sell('600000', '12.45','900')
#
# 获取资金情况
# print(a.get_balance())
# #
# # 获取当日实际持仓
# print(a.get_position())
#
# # 获取当日成交
# print(a.get_today_trades())
# print(a.cash_in_out())