Jelajahi Sumber

RPA:萝卜投研

hsun 3 minggu lalu
induk
melakukan
c77139ac3a
3 mengubah file dengan 420 tambahan dan 1 penghapusan
  1. 4 1
      .gitignore
  2. 169 0
      rpa_radish_research_refresh.py
  3. 247 0
      ths_wind.py

+ 4 - 1
.gitignore

@@ -1,2 +1,5 @@
 /.idea
-/venv
+/venv
+/html2img/html
+/html2img/img
+/html2img/.idea

+ 169 - 0
rpa_radish_research_refresh.py

@@ -0,0 +1,169 @@
+import hug
+from bottle import run
+import pyautogui
+import random
+import time
+import os
+import win32gui
+import win32con
+import autoit
+import datetime
+
+# 配置参数
+CONFIG = {
+    "exe_path": r"C:\Program Files (x86)\Microsoft Office\Office15\EXCEL.EXE",
+    "no_auth_button": {"x": 1150, "y": 481},
+    "source_button": {"x": 495, "y": 37},
+    "refresh_icon_button": {"x": 583, "y": 112},
+    "refresh_all_button": {"x": 616, "y": 157},
+    "random_click_range": {"min_x": 450, "max_x": 650, "min_y": 450, "max_y": 650},
+    "get_data_timeout_time": 20,
+    "screen_resolution": {"width": 1920, "height": 1080},  # 默认分辨率
+}
+
+hug.API(__name__).http.output_format = hug.output_format.json
+
+
+def scale_coordinates(x, y):
+    """根据屏幕分辨率动态缩放坐标"""
+    screen_width, screen_height = pyautogui.size()
+    scale_x = screen_width / CONFIG["screen_resolution"]["width"]
+    scale_y = screen_height / CONFIG["screen_resolution"]["height"]
+    return int(x * scale_x), int(y * scale_y)
+
+
+def random_click(min_x, max_x, min_y, max_y, duration=0.5):
+    """随机点击指定范围内的坐标"""
+    point_x = random.randint(min_x, max_x)
+    point_y = random.randint(min_y, max_y)
+    pyautogui.moveTo(point_x, point_y, duration)
+    pyautogui.click()
+    print(f"随机点击:X={point_x}, Y={point_y}")
+    return point_x, point_y
+
+
+def open_excel_file(file_path):
+    """使用 Excel 打开文件,并动态等待窗口激活"""
+    try:
+        file_name_ext = os.path.basename(file_path)
+        print(f"尝试打开文件:{file_name_ext}")
+
+        # 启动 Excel 并打开文件
+        autoit.run(f'{CONFIG["exe_path"]} "{file_path}"')
+
+        time.sleep(15)
+
+        # 动态等待窗口激活,最多等待 20 秒
+        # if not autoit.win_wait_active(file_name_ext, timeout=20):
+        #     print(f"窗口激活超时:{file_name_ext}")
+        #     return False
+
+        # 最大化窗口
+        hwnd = win32gui.GetForegroundWindow()
+        win32gui.ShowWindow(hwnd, win32con.SW_MAXIMIZE)
+        time.sleep(1)  # 短暂等待窗口最大化完成
+
+        print(f"成功打开并最大化窗口:{file_name_ext}")
+    except Exception as e:
+        print(f"打开文件失败:{e}")
+        return False
+    return True
+
+
+@hug.get('/radish_research/server')
+def radish_research_server():
+    return 1
+
+
+@hug.get('/radish_research/refresh')
+def radish_research_refresh(FilePath):
+    pyautogui.FAILSAFE = False
+    FilePath = FilePath.replace('"', '')
+
+    # 检查文件路径
+    if not os.path.exists(FilePath) or ".xlsx" not in FilePath \
+            or "~$" in FilePath or "template" in FilePath:
+        print(f"文件无效:{FilePath}")
+        return False
+
+    # 按钮坐标
+    no_auth_button = CONFIG["no_auth_button"]["x"], CONFIG["no_auth_button"]["y"]
+    source_button = CONFIG["source_button"]["x"], CONFIG["source_button"]["y"]
+    refresh_icon_button = CONFIG["refresh_icon_button"]["x"], CONFIG["refresh_icon_button"]["y"]
+    refresh_all_button = CONFIG["refresh_all_button"]["x"], CONFIG["refresh_all_button"]["y"]
+
+    # 打开桌面并点击空白区域
+    pyautogui.hotkey('win', 'd')
+    move_x, move_y = scale_coordinates(1100, 600)
+    pyautogui.moveTo(move_x, move_y, 0.5)
+    pyautogui.click(move_x, move_y)
+
+    try:
+        # 打开文件
+        if not open_excel_file(FilePath):
+            return False
+
+        # 随机点击单元格区域
+        for _ in range(random.randint(2, 3)):
+            random_click(
+                CONFIG["random_click_range"]["min_x"],
+                CONFIG["random_click_range"]["max_x"],
+                CONFIG["random_click_range"]["min_y"],
+                CONFIG["random_click_range"]["max_y"]
+            )
+            time.sleep(1)
+
+        # 处理无权限弹框
+        print("开始点击无权限按钮")
+        pyautogui.moveTo(*no_auth_button, 0.5)
+        pyautogui.click()
+        time.sleep(2)
+        print("结束点击无权限按钮")
+
+        # 点击顶部萝卜投研按钮
+        print("开始点击数据按钮")
+        pyautogui.moveTo(*source_button, 0.5)
+        pyautogui.click()
+        time.sleep(2)
+        print("结束点击数据按钮")
+
+        # 点击刷新小箭头
+        print("开始点击刷新小箭头")
+        pyautogui.moveTo(*refresh_icon_button, 1)
+        pyautogui.click()
+        time.sleep(1)
+        print("结束点击刷新小箭头")
+
+        # 点击刷新所有页
+        print("开始点击刷新所有页")
+        pyautogui.moveTo(*refresh_all_button, 1)
+        pyautogui.click()
+        time.sleep(1)
+        print("结束点击刷新所有页")
+
+        # 随机点击
+        random_click(
+            CONFIG["random_click_range"]["min_x"],
+            CONFIG["random_click_range"]["max_x"],
+            CONFIG["random_click_range"]["min_y"],
+            CONFIG["random_click_range"]["max_y"]
+        )
+
+        # 等待数据更新
+        time.sleep(CONFIG["get_data_timeout_time"])
+
+        # 保存并关闭文件
+        pyautogui.hotkey('ctrl', 's')
+        time.sleep(1)
+        pyautogui.hotkey('ctrl', 'w')
+
+        return True
+
+    except Exception as e:
+        print(f"发生异常:{e}")
+        return False
+
+
+if __name__ == "__main__":
+    app = __hug__.http.server()
+    run(app=app, reloader=True, port=7007)

+ 247 - 0
ths_wind.py

@@ -0,0 +1,247 @@
+import hug
+from bottle import route, run, NORUN
+from WindPy import w
+import iFinDPy as THS
+import json
+import pandas as pd
+import time
+import os
+import logging
+import sys
+
+hug.API(__name__).http.output_format = hug.output_format.json
+
+username = ""
+password = ""
+
+
+@hug.get('/hz_server')
+def hello():
+    return 1
+    # return 'wind true'
+
+
+# 同花顺登录函数
+def thslogin():
+    # 输入用户的帐号和密码
+    thsLogin = THS.THS_iFinDLogin(username, password)
+    print(thsLogin)
+    if (thsLogin == 0 or thsLogin == -201):
+        print('ths 登录成功')
+        logger.info('ths login success')
+    else:
+        print('ths 登录失败')
+        logger.info('ths login fail')
+
+
+@hug.get('/edbInfo/ths')
+def GetEdbDataByThs(EdbCode, StartDate, EndDate):
+    print("GetEdbDataByThs")
+    logger.info('GetEdbDataByThs')
+    thsEDBDataQuery = THS.THS_EDBQuery(EdbCode, StartDate, EndDate, True)
+    print(thsEDBDataQuery)
+    logger.info(thsEDBDataQuery)
+
+    print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + ":start GetEdbDataByThs")
+    print("thsEDBDataQuery")
+    print(thsEDBDataQuery)
+    thsEDBDataQuery = thsEDBDataQuery.decode("unicode_escape")
+
+    result = json.loads(thsEDBDataQuery)
+    print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + ":end GetEdbDataByThs")
+
+    if result["errorcode"] == -1010:
+        thsLogin = THS.THS_iFinDLogin(username, password)
+        print("登录一次")
+        print(thsLogin)
+        logger.info("ths login")
+        if (thsLogin == 0 or thsLogin == -201):
+            thsEDBDataQuery = THS.THS_EDBQuery(EdbCode, StartDate, EndDate, True)
+            thsEDBDataQuery = thsEDBDataQuery.decode("unicode_escape")
+            result = json.loads(thsEDBDataQuery)
+            logger.info("login success")
+    else:
+        print("已登录")
+
+    return result
+
+
+def has_comma(input_string):
+    return isinstance(input_string, str) and ',' in input_string
+
+
+def process_single_edb_code(StockCode, SingleEdbCode, StartDate, EndDate):
+    thsEDBDataQuery = THS.THS_DS(StockCode, SingleEdbCode, '', '', StartDate, EndDate)
+    print(thsEDBDataQuery)
+    result = json.dumps(ths_data_to_dict(thsEDBDataQuery))
+    return result
+
+
+@hug.get('/edbInfo/ths/ds')
+def GetEdbDataByThsDs(StockCode, EdbCode, StartDate, EndDate):
+    print("THS_DataSequence start")
+    print(EdbCode)
+    result_list = []
+
+    if isinstance(EdbCode, list):
+        for SingleEdbCode in EdbCode:
+            result_list.append(process_single_edb_code(StockCode, SingleEdbCode, StartDate, EndDate))
+            print("THS_DS end")
+        print(result_list)
+        return result_list
+    else:
+        # 如果不包含逗号,直接处理
+        result = process_single_edb_code(StockCode, EdbCode, StartDate, EndDate)
+        print(result)
+        return result
+
+
+@hug.get('/edbInfo/ths/future_good')
+def GetFutureGoodEdbDataByThs(EdbCode, StartDate, EndDate):
+    print("THS_HistoryQuotes start")
+    thsLogin = THS.THS_iFinDLogin("hzmd150", "689719")
+
+    filed = 'lastclose,open,high,low,close,avgprice,change,changeper,volume,amount,hsl,lastsettlement,settlement,zdsettlement,zdfsettlement,ccl,ccbd,zf,zjlx,zjcd'
+    # filed = 'open,high,low,close,volume,amount,settlement,ccl,zjlx'
+    params = 'Interval:D,CPS:1,baseDate:1900-01-01,Currency:YSHB,fill:Previous'
+    # params = 'YSHB;Tradedays'
+    # print(params)
+    thsEDBDataQuery = THS.THS_HistoryQuotes(EdbCode, filed, params, StartDate, EndDate, True)
+    # thsEDBDataQuery = THS.THS_HQ(EdbCode, filed, params, StartDate, EndDate)
+    # thsEDBDataQuery = THS.THS_EDBQuery(EdbCode, StartDate, EndDate, True)
+    print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + ":start GetFutureGoodEdbDataByThs")
+    print("THS_HistoryQuotes end")
+    print(thsEDBDataQuery)
+    thsEDBDataQuery = thsEDBDataQuery.decode("unicode_escape")
+    result = json.loads(thsEDBDataQuery)
+    print(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + ":end GetFutureGoodEdbDataByThs")
+    return result
+
+
+@hug.get('/edbInfo/ths/hf')
+def GetEdbDataByThsDs(codes, indicators, function_para, start_time, end_time):
+    print("codes: {}; indicators: {}; function_para: {}; start_time: {}; end_time: {}".format(codes, indicators,
+                                                                                              function_para, start_time,
+                                                                                              end_time))
+    data_query = THS.THS_DF(codes, indicators, function_para, start_time, end_time)
+    print(data_query)
+    result = json.dumps(ths_data_to_dict(data_query))
+    return result
+
+
+@hug.get('/edbInfo/wind')
+def GetEdbDataByWind(EdbCode, StartDate, EndDate):
+    print("GetEdbDataByWind:", EdbCode)
+    isConnected = w.isconnected()
+    print("isconnected")
+    print(isConnected)
+    if isConnected == False:
+        print("re isconnected")
+        w.start()
+        isConnected = w.isconnected()
+        if isConnected == False:
+            return "{'ErrMsg':'启动Wind接口失败'}"
+
+    print("getdata")
+    option = "Fill=Previous"
+    data = w.edb(EdbCode, StartDate, EndDate, option)
+    print("wind data")
+    df = pd.DataFrame()
+    if data.ErrorCode == -40521010:  # Internet Timeout 超时退出
+        os._exit(0)
+        return "a"
+    df['DT'] = data.Times
+    df['CLOSE'] = data.Data[0]
+    df['ErrorCode'] = data.ErrorCode
+    df = df[['DT', 'CLOSE', 'ErrorCode']]
+    df = df.dropna()
+    json_data = df.to_json()
+    # w.stop()
+    print("wind data end")
+    result = json.loads(json_data)
+    return result
+
+
+@hug.get('/edbInfo/wind/future_good')
+def GetFutureGoodEdbDataByWind(FutureGoodEdbCode, StartDate, EndDate):
+    print("GetFutureGoodEdbDataByWind:", FutureGoodEdbCode)
+    isConnected = w.isconnected()
+    print("isconnected")
+    print(isConnected)
+    if isConnected == False:
+        print("re isconnected")
+        w.start()
+        isConnected = w.isconnected()
+        if isConnected == False:
+            return "{'ErrMsg':'启动Wind接口失败'}"
+
+    print("getdata")
+    option = "Fill=Previous"
+    data = w.edb(FutureGoodEdbCode, StartDate, EndDate, option)
+    data = w.wsd(FutureGoodEdbCode, "trade_code,open,high,low,close,volume,amt,oi,settle", StartDate, EndDate, option)
+    print("wind data")
+    print(data)
+    df = pd.DataFrame()
+
+    if data.ErrorCode == -40521010:  # Internet Timeout 超时退出
+        os._exit(0)
+        return "a"
+
+    df['DT'] = data.Times
+    df['TRADE_CODE'] = data.Data[0]
+    df['OPEN'] = data.Data[1]
+    df['HIGH'] = data.Data[2]
+    df['LOW'] = data.Data[3]
+    df['CLOSE'] = data.Data[4]
+    df['VOLUME'] = data.Data[5]
+    df['AMT'] = data.Data[6]
+    df['OI'] = data.Data[7]
+    df['SETTLE'] = data.Data[8]
+    df['ErrorCode'] = data.ErrorCode
+    df = df[['DT', 'TRADE_CODE', 'OPEN', 'HIGH', 'LOW', 'CLOSE', 'VOLUME', 'AMT', 'OI', 'SETTLE', 'ErrorCode']]
+    df = df.dropna()
+    json_data = df.to_json()
+    # w.stop()
+    print("wind data end")
+    result = json.loads(json_data)
+    return result
+
+
+@hug.get('/edbInfo/wind/wsd')
+def GetEdbDataWindWsd(StockCode, EdbCode, StartDate, EndDate):
+    print("GetEdbDataByWind:", EdbCode)
+    isConnected = w.isconnected()
+    print("isconnected")
+    print(isConnected)
+    if isConnected == False:
+        print("re isconnected")
+        w.start()
+        isConnected = w.isconnected()
+        if isConnected == False:
+            return "{'ErrMsg':'启动Wind接口失败'}"
+
+    option = "Fill=Previous"
+    wsd_data = w.wsd(StockCode, EdbCode, StartDate, EndDate, option)
+
+    if wsd_data.ErrorCode == -40521010:  # Internet Timeout 超时退出
+        os._exit(0)
+        return "a"
+
+    fm = pd.DataFrame(wsd_data.Data, index=wsd_data.Fields, columns=wsd_data.Times)
+    json_data = fm.to_json()
+    result = json.loads(json_data)
+    return result
+
+
+if __name__ == "__main__":
+    # wind 登录
+    wStart = w.start()
+    if wStart.ErrorCode != 0:
+        print("启动万得API接口失败")
+    print(wStart)
+
+    # ths登录函数
+    thslogin()
+
+    app = __hug__.http.server()
+    run(app=app, reloader=True, port=7000)