From 313e9e229d6dffc70b0ae0f313d2998e1af4e723 Mon Sep 17 00:00:00 2001 From: workpc Date: Thu, 20 Mar 2025 14:41:21 +0800 Subject: [PATCH] =?UTF-8?q?=E7=9F=B3=E6=B2=B9=E7=84=A6=E9=93=9D=E7=94=A8?= =?UTF-8?q?=E8=B0=83=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../沥青/沥青定性模型每日推送-ytj.ipynb | 4 +- .../液化石油气/化工品价格预测准确率.xlsx | Bin 6512 -> 0 bytes aisenzhecode/液化石油气/液化气价格预测.py | 101 +++- .../液化石油气/液化气价格预测ytj.ipynb | 119 +++-- aisenzhecode/液化石油气/液化气数据.xlsx | Bin 0 -> 104172 bytes config_shiyoujiao.py | 320 ------------- config_shiyoujiao_lvyong.py | 408 ++++++++++++++++ config_shiyoujiao_puhuo.py | 319 +++++++++++++ lib/dataread.py | 71 ++- main_juxiting.py | 3 +- main_shiyoujiao_lvyong.py | 445 ++++++++++++++++++ ..._shiyoujiao.py => main_shiyoujiao_puhuo.py | 237 +++++++--- 12 files changed, 1587 insertions(+), 440 deletions(-) delete mode 100644 aisenzhecode/液化石油气/化工品价格预测准确率.xlsx create mode 100644 aisenzhecode/液化石油气/液化气数据.xlsx delete mode 100644 config_shiyoujiao.py create mode 100644 config_shiyoujiao_lvyong.py create mode 100644 config_shiyoujiao_puhuo.py create mode 100644 main_shiyoujiao_lvyong.py rename main_shiyoujiao.py => main_shiyoujiao_puhuo.py (57%) diff --git a/aisenzhecode/沥青/沥青定性模型每日推送-ytj.ipynb b/aisenzhecode/沥青/沥青定性模型每日推送-ytj.ipynb index 6b87d61..2a88fb9 100644 --- a/aisenzhecode/沥青/沥青定性模型每日推送-ytj.ipynb +++ b/aisenzhecode/沥青/沥青定性模型每日推送-ytj.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -303,7 +303,7 @@ " 复盘分析后发现2024-7月开始,开工率数据从0.28 变为了28 ,改为下面的判断规则\n", " '''\n", " if df1.loc[1,'70号沥青开工率'] / 100 > 0.3:\n", - " a = (df1.loc[1,'70号沥青开工率'] / 100 -0.2)*5/0.1\n", + " a = -(df1.loc[1,'70号沥青开工率'] / 100 -0.2)*5/0.1\n", " else :\n", " a = 0\n", " b = df1.loc[1,'资金因素']\n", diff --git a/aisenzhecode/液化石油气/化工品价格预测准确率.xlsx b/aisenzhecode/液化石油气/化工品价格预测准确率.xlsx deleted file mode 100644 index 31df76925dd5712052e684193746b834f6f32f2c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6512 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 HLI3+7Rdh)V diff --git a/aisenzhecode/液化石油气/液化气价格预测.py b/aisenzhecode/液化石油气/液化气价格预测.py index 384c57f..f4acfff 100644 --- a/aisenzhecode/液化石油气/液化气价格预测.py +++ b/aisenzhecode/液化石油气/液化气价格预测.py @@ -61,7 +61,8 @@ login_push_data = { "funcOperation": "获取token" } -read_file_path_name = "液化气数据.xls" +# read_file_path_name = "液化气数据.xls" +read_file_path_name = "液化气数据.xlsx" one_cols = [] two_cols = [] @@ -183,6 +184,88 @@ def upload_data_to_system(token_push, date): print('预测值:', data['data'][0]['dataValue']) +def getLogToken(): + login_res = requests.post(url=login_url, json=login_data, timeout=(3, 5)) + text = json.loads(login_res.text) + if text["status"]: + token = text["data"]["accessToken"] + else: + print("获取认证失败") + token = None + return token + + +def updateYesterdayExcelData(date='', token=None): + # 使用pandas读取Excel文件 + df = pd.read_excel(read_file_path_name, engine='openpyxl') + + # 获取第二行的数据作为列名 + one_cols = df.iloc[0, :].tolist() + + # 获取当前日期的前一天 + if date == '': + previous_date = (datetime.now() - timedelta(days=1) + ).strftime('%Y-%m-%d') + else: + # 字符串转日期 + previous_date = (datetime.strptime(date, "%Y-%m-%d") - + timedelta(days=1)).strftime('%Y-%m-%d') + + cur_time, cur_time2 = getNow(previous_date) + search_data = { + "data": { + "date": cur_time, + "dataItemNoList": one_cols[1:] + }, + "funcModule": "数据项", + "funcOperation": "查询" + } + headers = {"Authorization": token} + search_res = requests.post( + url=search_url, headers=headers, json=search_data, timeout=(3, 5)) + print('数据请求结果:') + print(search_res.text) + search_value = json.loads(search_res.text)["data"] + if search_value: + datas = search_value + else: + datas = None + + append_rows = [cur_time2] + dataItemNo_dataValue = {} + for data_value in datas: + if "dataValue" not in data_value: + print(data_value) + dataItemNo_dataValue[data_value["dataItemNo"]] = "" + else: + dataItemNo_dataValue[data_value["dataItemNo"] + ] = data_value["dataValue"] + for value in one_cols[1:]: + if value in dataItemNo_dataValue: + append_rows.append(dataItemNo_dataValue[value]) + else: + append_rows.append("") + + print('更新数据前') + print(df.tail(1)) + # 检查日期是否已存在于数据中 + if previous_date not in df['日期'].values: + # 将新的数据添加到DataFrame中 + new_row = pd.DataFrame([append_rows], columns=df.columns.tolist()) + df = pd.concat([df, new_row], ignore_index=True) + else: + # 更新现有数据 + print('日期存在,即将更新') + print('新数据', append_rows[1:]) + df.loc[df['日期'] == previous_date, + df.columns.tolist()[1:]] = append_rows[1:] + + print('更新数据后') + print(df.tail(1)) + # 使用pandas保存Excel文件 + df.to_excel("液化气数据.xls", index=False, engine='openpyxl') + + price_list = [] @@ -553,14 +636,12 @@ def save_xls_2(append_rows): start_date = datetime(2025, 3, 10) end_date = datetime(2025, 3, 20) +token = getLogToken() while start_date < end_date: - # 更新昨日数据 - start_1(start_date) - date = start_date.strftime('%Y%m%d') - # 获取当日数据,预测数据,并上传 - start(date) - # time.sleep(1) - start_date += timedelta(days=1) + date = start_date.strftime('%Y-%m-%d') + updateYesterdayExcelData(date, token=token) + # start(date) + # # time.sleep(1) + # start_1(start_date) + # start_date += timedelta(days=1) time.sleep(5) - -# print(price_list) diff --git a/aisenzhecode/液化石油气/液化气价格预测ytj.ipynb b/aisenzhecode/液化石油气/液化气价格预测ytj.ipynb index 1f487b0..b8c3e8b 100644 --- a/aisenzhecode/液化石油气/液化气价格预测ytj.ipynb +++ b/aisenzhecode/液化石油气/液化气价格预测ytj.ipynb @@ -2,9 +2,17 @@ "cells": [ { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "WARNING:tensorflow:From C:\\Users\\EDY\\AppData\\Roaming\\Python\\Python311\\site-packages\\keras\\src\\losses.py:2976: The name tf.losses.sparse_softmax_cross_entropy is deprecated. Please use tf.compat.v1.losses.sparse_softmax_cross_entropy instead.\n", + "\n" + ] + }, { "data": { "text/html": [ @@ -79,9 +87,6 @@ "import random\n", "import time\n", "\n", - "\n", - "\n", - "\n", "from plotly import __version__\n", "from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot\n", "\n", @@ -501,7 +506,70 @@ " pickle.dump(grid_search_XGB, file)\n", "\n", "\n", - " \n", + "def updateYesterdayExcelData(date='', token=None):\n", + " # 使用pandas读取Excel文件\n", + " df = pd.read_excel(read_file_path_name, engine='openpyxl')\n", + "\n", + " # 获取第二行的数据作为列名\n", + " one_cols = df.iloc[0,:].tolist()\n", + "\n", + " # 获取当前日期的前一天\n", + " if date == '':\n", + " previous_date = (datetime.now() - timedelta(days=1)).strftime('%Y-%m-%d')\n", + " else:\n", + " # 字符串转日期\n", + " previous_date = (datetime.strptime(date, \"%Y-%m-%d\")-timedelta(days=1)).strftime('%Y-%m-%d')\n", + " \n", + "\n", + " cur_time, cur_time2 = getNow(previous_date)\n", + " search_data = {\n", + " \"data\": {\n", + " \"date\": cur_time,\n", + " \"dataItemNoList\": one_cols[1:]\n", + " },\n", + " \"funcModule\": \"数据项\",\n", + " \"funcOperation\": \"查询\"\n", + " }\n", + " headers = {\"Authorization\": token}\n", + " search_res = requests.post(url=search_url, headers=headers, json=search_data, timeout=(3, 5))\n", + " search_value = json.loads(search_res.text)[\"data\"]\n", + " if search_value:\n", + " datas = search_value\n", + " else:\n", + " datas = None\n", + "\n", + " append_rows = [cur_time2]\n", + " dataItemNo_dataValue = {}\n", + " for data_value in datas:\n", + " if \"dataValue\" not in data_value:\n", + " print(data_value)\n", + " dataItemNo_dataValue[data_value[\"dataItemNo\"]] = \"\"\n", + " else:\n", + " dataItemNo_dataValue[data_value[\"dataItemNo\"]] = data_value[\"dataValue\"]\n", + " for value in one_cols[1:]:\n", + " if value in dataItemNo_dataValue:\n", + " append_rows.append(dataItemNo_dataValue[value])\n", + " else:\n", + " append_rows.append(\"\")\n", + "\n", + " print('更新数据前')\n", + " print(df.tail(1))\n", + " # 检查日期是否已存在于数据中\n", + " if previous_date not in df['日期'].values:\n", + " # 将新的数据添加到DataFrame中\n", + " new_row = pd.DataFrame([append_rows], columns=df.columns.tolist())\n", + " df = pd.concat([df, new_row], ignore_index=True)\n", + " else:\n", + " # 更新现有数据\n", + " print('日期存在,即将更新')\n", + " print('新数据',append_rows[1:])\n", + " df.loc[df['日期'] == previous_date, df.columns.tolist()[1:]] = append_rows[1:]\n", + "\n", + " print('更新数据后')\n", + " print(df.tail(1))\n", + " # 使用pandas保存Excel文件\n", + " df.to_excel(\"液化气数据.xls\", index=False, engine='openpyxl')\n", + "\n", "\n", "def read_xls_data_bak():\n", " global one_cols, two_cols\n", @@ -772,7 +840,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 1, "metadata": { "scrolled": true }, @@ -818,43 +886,32 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 2, "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "获取到的数据项ID['YHQMXBB|C01100008|STRIKE_PRICE', 'C01100008|CORTED_VALUE', 'C01100008|AUCTION_MAX_PRICE', 'C01100008|AMOUNT', 'ICE_CL0_LAST_YEDAY_PRICE', '100028046|LISTING_PRICE', 'C01100008|PLAN_SALE', '91370200163576944B|C01100008|STRIKE_PRICE', '9137078672073757X8|C01100008|STRIKE_PRICE', '91370500674526498A|C01100008|STRIKE_PRICE', '91370305773165341A|C01100008|STRIKE_PRICE', '91370521164880008P|C01100008|STRIKE_PRICE', '91370321164425136B|C01100008|STRIKE_PRICE', 'SD|GC|ZDW|LIST_PRICE', '370500|ISOBUTANE|LIST_PRICE', 'SD|YT|SG|LIST_PRICE', '91110000710926094P|C01100008|SUPPLY_MERE', '91110000710932515R|C01100008|SUPPLY_MERE', '91370500674526498A|C01100008|SUPPLY_MERE', '91370321164425136B|C01100008|SUPPLY_MERE', 'C01100008|OTHER|SUPPLY_MERE', 'SD|WJH|DEMANDS', 'C01100008|SUY_DED_DAP', 'C01100008|EFFECTIVE_STOCK', '912102117169477344|C01100008|STRIKE_PRICE', '91110304102767480H|C01100008|STRIKE_PRICE', '91130193670310403L|C01100008|STRIKE_PRICE', 'HD|LPG|IMPORT_PRICE', 'SD|WJH|SALES_PRICE']\n", - "获取的token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhcGlfZGV2IiwidGgiOiI4YTQ1NzdkYmQ5MTk2NzU3NThkNTc5OTlhMWU4OTFmZSIsImx0IjoiYXBpIiwiaXNzIjoiIiwidG0iOiJQQyIsImV4cCI6MTc0MjMzMDEyMSwianRpIjoiMmI5ZmUzNTA5YjNmNGU4OTkzMjRiNzU1MzQ4ODlkNTQifQ.nezcKMQq4GnNoHKwvIOEe-1pK0Oz3LliiM8yYjOMG8c\n", - "补充20250228数据\n", - "数据项查询参数search_data:\n", - "{'data': {'date': '20250228', 'dataItemNoList': ['C01100008|CORTED_VALUE', 'C01100008|AUCTION_MAX_PRICE', 'C01100008|AMOUNT', 'ICE_CL0_LAST_YEDAY_PRICE', '100028046|LISTING_PRICE', 'C01100008|PLAN_SALE', '91370200163576944B|C01100008|STRIKE_PRICE', '9137078672073757X8|C01100008|STRIKE_PRICE', '91370500674526498A|C01100008|STRIKE_PRICE', '91370305773165341A|C01100008|STRIKE_PRICE', '91370521164880008P|C01100008|STRIKE_PRICE', '91370321164425136B|C01100008|STRIKE_PRICE', 'SD|GC|ZDW|LIST_PRICE', '370500|ISOBUTANE|LIST_PRICE', 'SD|YT|SG|LIST_PRICE', '91110000710926094P|C01100008|SUPPLY_MERE', '91110000710932515R|C01100008|SUPPLY_MERE', '91370500674526498A|C01100008|SUPPLY_MERE', '91370321164425136B|C01100008|SUPPLY_MERE', 'C01100008|OTHER|SUPPLY_MERE', 'SD|WJH|DEMANDS', 'C01100008|SUY_DED_DAP', 'C01100008|EFFECTIVE_STOCK', '912102117169477344|C01100008|STRIKE_PRICE', '91110304102767480H|C01100008|STRIKE_PRICE', '91130193670310403L|C01100008|STRIKE_PRICE', 'HD|LPG|IMPORT_PRICE', 'SD|WJH|SALES_PRICE']}, 'funcModule': '数据项', 'funcOperation': '查询'}\n", - "数据项查询结果search_res:\n", - "{\"confirmFlg\":false,\"data\":[{\"dataDate\":\"20250228\",\"dataItemNo\":\"100028046|LISTING_PRICE\",\"dataValue\":8441.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"370500|ISOBUTANE|LIST_PRICE\",\"dataValue\":5380.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"91110000710926094P|C01100008|SUPPLY_MERE\",\"dataValue\":1300.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"91110000710932515R|C01100008|SUPPLY_MERE\"},{\"dataDate\":\"20250228\",\"dataItemNo\":\"91110304102767480H|C01100008|STRIKE_PRICE\",\"dataValue\":5150.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"91130193670310403L|C01100008|STRIKE_PRICE\",\"dataValue\":5150.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"912102117169477344|C01100008|STRIKE_PRICE\",\"dataValue\":4670.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"91370200163576944B|C01100008|STRIKE_PRICE\",\"dataValue\":5300.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"91370305773165341A|C01100008|STRIKE_PRICE\",\"dataValue\":5600.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"91370321164425136B|C01100008|STRIKE_PRICE\",\"dataValue\":5500.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"91370321164425136B|C01100008|SUPPLY_MERE\",\"dataValue\":200.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"91370500674526498A|C01100008|STRIKE_PRICE\",\"dataValue\":5488.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"91370500674526498A|C01100008|SUPPLY_MERE\",\"dataValue\":175.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"91370521164880008P|C01100008|STRIKE_PRICE\",\"dataValue\":5455.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"C01100008|AMOUNT\",\"dataValue\":342.72000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"C01100008|AUCTION_MAX_PRICE\",\"dataValue\":5500.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"C01100008|CORTED_VALUE\",\"dataValue\":5500.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"C01100008|EFFECTIVE_STOCK\",\"dataValue\":-550.20000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"C01100008|OTHER|SUPPLY_MERE\",\"dataValue\":5000.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"C01100008|PLAN_SALE\",\"dataValue\":500.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"C01100008|SUY_DED_DAP\",\"dataValue\":-50.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"HD|LPG|IMPORT_PRICE\",\"dataValue\":5400.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"ICE_CL0_LAST_YEDAY_PRICE\",\"dataValue\":73.35000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"SD|GC|ZDW|LIST_PRICE\",\"dataValue\":5250.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"SD|WJH|DEMANDS\",\"dataValue\":8500.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"SD|WJH|SALES_PRICE\",\"dataValue\":8400.00000000},{\"dataDate\":\"20250228\",\"dataItemNo\":\"SD|YT|SG|LIST_PRICE\",\"dataValue\":6050.00000000}],\"status\":true}\n", - "数据项查询结果: [{'dataDate': '20250228', 'dataItemNo': '100028046|LISTING_PRICE', 'dataValue': 8441.0}, {'dataDate': '20250228', 'dataItemNo': '370500|ISOBUTANE|LIST_PRICE', 'dataValue': 5380.0}, {'dataDate': '20250228', 'dataItemNo': '91110000710926094P|C01100008|SUPPLY_MERE', 'dataValue': 1300.0}, {'dataDate': '20250228', 'dataItemNo': '91110000710932515R|C01100008|SUPPLY_MERE'}, {'dataDate': '20250228', 'dataItemNo': '91110304102767480H|C01100008|STRIKE_PRICE', 'dataValue': 5150.0}, {'dataDate': '20250228', 'dataItemNo': '91130193670310403L|C01100008|STRIKE_PRICE', 'dataValue': 5150.0}, {'dataDate': '20250228', 'dataItemNo': '912102117169477344|C01100008|STRIKE_PRICE', 'dataValue': 4670.0}, {'dataDate': '20250228', 'dataItemNo': '91370200163576944B|C01100008|STRIKE_PRICE', 'dataValue': 5300.0}, {'dataDate': '20250228', 'dataItemNo': '91370305773165341A|C01100008|STRIKE_PRICE', 'dataValue': 5600.0}, {'dataDate': '20250228', 'dataItemNo': '91370321164425136B|C01100008|STRIKE_PRICE', 'dataValue': 5500.0}, {'dataDate': '20250228', 'dataItemNo': '91370321164425136B|C01100008|SUPPLY_MERE', 'dataValue': 200.0}, {'dataDate': '20250228', 'dataItemNo': '91370500674526498A|C01100008|STRIKE_PRICE', 'dataValue': 5488.0}, {'dataDate': '20250228', 'dataItemNo': '91370500674526498A|C01100008|SUPPLY_MERE', 'dataValue': 175.0}, {'dataDate': '20250228', 'dataItemNo': '91370521164880008P|C01100008|STRIKE_PRICE', 'dataValue': 5455.0}, {'dataDate': '20250228', 'dataItemNo': 'C01100008|AMOUNT', 'dataValue': 342.72}, {'dataDate': '20250228', 'dataItemNo': 'C01100008|AUCTION_MAX_PRICE', 'dataValue': 5500.0}, {'dataDate': '20250228', 'dataItemNo': 'C01100008|CORTED_VALUE', 'dataValue': 5500.0}, {'dataDate': '20250228', 'dataItemNo': 'C01100008|EFFECTIVE_STOCK', 'dataValue': -550.2}, {'dataDate': '20250228', 'dataItemNo': 'C01100008|OTHER|SUPPLY_MERE', 'dataValue': 5000.0}, {'dataDate': '20250228', 'dataItemNo': 'C01100008|PLAN_SALE', 'dataValue': 500.0}, {'dataDate': '20250228', 'dataItemNo': 'C01100008|SUY_DED_DAP', 'dataValue': -50.0}, {'dataDate': '20250228', 'dataItemNo': 'HD|LPG|IMPORT_PRICE', 'dataValue': 5400.0}, {'dataDate': '20250228', 'dataItemNo': 'ICE_CL0_LAST_YEDAY_PRICE', 'dataValue': 73.35}, {'dataDate': '20250228', 'dataItemNo': 'SD|GC|ZDW|LIST_PRICE', 'dataValue': 5250.0}, {'dataDate': '20250228', 'dataItemNo': 'SD|WJH|DEMANDS', 'dataValue': 8500.0}, {'dataDate': '20250228', 'dataItemNo': 'SD|WJH|SALES_PRICE', 'dataValue': 8400.0}, {'dataDate': '20250228', 'dataItemNo': 'SD|YT|SG|LIST_PRICE', 'dataValue': 6050.0}]\n", - "{'dataDate': '20250228', 'dataItemNo': '91110000710932515R|C01100008|SUPPLY_MERE'}\n", - "添加的行: ['20250228', '', 5500.0, 5500.0, 342.72, 73.35, 8441.0, 500.0, 5300.0, '', 5488.0, 5600.0, 5455.0, 5500.0, 5250.0, 5380.0, 6050.0, 1300.0, '', 175.0, 200.0, 5000.0, 8500.0, -50.0, -550.2, 4670.0, 5150.0, 5150.0, 5400.0, 8400.0]\n", - "Index(['Date', 'Price', '修正价', '竞拍最高价', '液化石油气|发货量', '昨日布伦特价格', '昨日92#汽油价格',\n", - " '计划出货量', '青岛石化', '中化工-昌邑', '海科瑞林', '鑫泰石化|液化石油气|成交价', '垦利价格', '汇丰价格',\n", - " '正丁烷', '异丁烷价格', '顺酐', '中石化供应量', '中化工供应量', '海科供应量', '汇丰供应量', '京博和其他供应量',\n", - " '烷基化需求量', '昨日烷基化价差', '我司库存', '东北-大连石化', '华北-燕山石化', '华北-石家庄炼化',\n", - " '昨日原料气价格', '烷基化油销售价格'],\n", - " dtype='object')\n", - "保存数据时发生错误: 'Date'\n" + "ename": "NameError", + "evalue": "name 'datetime' is not defined", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mNameError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[2], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m start_date \u001b[38;5;241m=\u001b[39m datetime(\u001b[38;5;241m2025\u001b[39m, \u001b[38;5;241m3\u001b[39m, \u001b[38;5;241m1\u001b[39m)\n\u001b[0;32m 2\u001b[0m end_date \u001b[38;5;241m=\u001b[39m datetime(\u001b[38;5;241m2025\u001b[39m, \u001b[38;5;241m3\u001b[39m, \u001b[38;5;241m12\u001b[39m)\n\u001b[0;32m 4\u001b[0m \u001b[38;5;28;01mwhile\u001b[39;00m start_date \u001b[38;5;241m<\u001b[39m end_date:\n", + "\u001b[1;31mNameError\u001b[0m: name 'datetime' is not defined" ] } ], "source": [ "start_date = datetime(2025, 3, 1)\n", - "end_date = datetime(2025, 3, 2)\n", + "end_date = datetime(2025, 3, 12)\n", "\n", "while start_date < end_date:\n", " date = start_date.strftime('%Y%m%d')\n", + " updateYesterdayExcelData(date)\n", " # start(date)\n", - " # time.sleep(1)\n", - " start_1(start_date)\n", - " start_date += timedelta(days=1)\n", + " # # time.sleep(1)\n", + " # start_1(start_date)\n", + " # start_date += timedelta(days=1)\n", " time.sleep(5)\n", "\n", "# print(price_list)" diff --git a/aisenzhecode/液化石油气/液化气数据.xlsx b/aisenzhecode/液化石油气/液化气数据.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..0652c5ce93a0c5ac50f18ab3a4b2ef61c7fa3e7a GIT binary patch literal 104172 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 VUJBuctoH##BLV#eV*!MM{tq9iSquOG literal 0 HcmV?d00001 diff --git a/config_shiyoujiao.py b/config_shiyoujiao.py deleted file mode 100644 index c6a6a5d..0000000 --- a/config_shiyoujiao.py +++ /dev/null @@ -1,320 +0,0 @@ -import logging -import os -import logging.handlers -import datetime -from lib.tools import MySQLDB,SQLiteHandler - - -# eta 接口token -APPID = "XNLDvxZHHugj7wJ7" -SECRET = "iSeU4s6cKKBVbt94htVY1p0sqUMqb2xa" - -# eta 接口url -sourcelisturl = 'http://10.189.2.78:8108/v1/edb/source/list' -classifylisturl = 'http://10.189.2.78:8108/v1/edb/classify/list?ClassifyType=' -uniquecodedataurl = 'http://10.189.2.78:8108/v1/edb/data?UniqueCode=4991c37becba464609b409909fe4d992&StartDate=2024-02-01' -classifyidlisturl = 'http://10.189.2.78:8108/v1/edb/list?ClassifyId=' -edbcodedataurl = 'http://10.189.2.78:8108/v1/edb/data?EdbCode=' -edbdatapushurl = 'http://10.189.2.78:8108/v1/edb/push' -edbdeleteurl = 'http://10.189.2.78:8108/v1/edb/business/edb/del' -edbbusinessurl = 'http://10.189.2.78:8108/v1/edb/business/data/del' -edbcodelist = ['ID01385938','lmcads03 lme comdty', -'GC1 COMB Comdty', -'C2404171822', -'dxy curncy', -'S5443199 ', -'S5479800', -'S5443108', -'H7358586', -'LC3FM1 INDEX', -'CNY REGN Curncy', -'s0105897', -'M0067419', -'M0066351', -'S0266372', -'S0266438', -'S0266506', -'ID01384463'] - -# 临时写死用指定的列,与上面的edbcode对应,后面更改 -edbnamelist = [ - 'ds','y', - 'LME铜价', - '黄金连1合约', - 'Brent-WTI', - '美元指数', - '甲醇鲁南价格', - '甲醇太仓港口价格', - '山东丙烯主流价', - '丙烷(山东)', - 'FEI丙烷 M1', - '在岸人民币汇率', - '南华工业品指数', - 'PVC期货主力', - 'PE期货收盘价', -'PP连续-1月', -'PP连续-5月', -'PP连续-9月', -'PP:拉丝:L5E89:出厂价:华北(第二区域):内蒙古久泰新材料(日)' - ] - -edbcodenamedict = { -'ID01385938':'PP:拉丝:1102K:市场价:青州:国家能源宁煤(日)', -'ID01384463':'PP:拉丝:L5E89:出厂价:华北(第二区域):内蒙古久泰新材料(日)', -'lmcads03 lme comdty':'LME铜价', -'GC1 COMB Comdty':'黄金连1合约', -'C2404171822':'Brent-WTI', -'dxy curncy':'美元指数', -'S5443199 ':'甲醇鲁南价格', -'S5479800':'甲醇太仓港口价格', -'S5443108':'山东丙烯主流价', -'H7358586':'丙烷(山东)', -'LC3FM1 INDEX':'FEI丙烷 M1', -'CNY REGN Curncy':'在岸人民币汇率', -'s0105897':'南华工业品指数', -'M0067419':'PVC期货主力', -'M0066351':'PE期货收盘价', -'S0266372':'PP连续-1月', -'S0266438':'PP连续-5月', -'S0266506':'PP连续-9月', - -} - -# eta自有数据指标编码 -modelsindex = { - 'NHITS': 'SELF0000077', - 'Informer':'SELF0000078', - 'LSTM':'SELF0000079', - 'iTransformer':'SELF0000080', - 'TSMixer':'SELF0000081', - 'TSMixerx':'SELF0000082', - 'PatchTST':'SELF0000083', - 'RNN':'SELF0000084', - 'GRU':'SELF0000085', - 'TCN':'SELF0000086', - 'BiTCN':'SELF0000087', - 'DilatedRNN':'SELF0000088', - 'MLP':'SELF0000089', - 'DLinear':'SELF0000090', - 'NLinear':'SELF0000091', - 'TFT':'SELF0000092', - 'FEDformer':'SELF0000093', - 'StemGNN':'SELF0000094', - 'MLPMultivariate':'SELF0000095', - 'TiDE':'SELF0000096', - 'DeepNPTS':'SELF0000097' - } - - - -# eta 上传预测结果的请求体,后面发起请求的时候更改 model datalist 数据 -data = { - "IndexCode": "", - "IndexName": "价格预测模型", - "Unit": "无", - "Frequency": "日度", - "SourceName": f"价格预测", - "Remark": 'ddd', - "DataList": [ - { - "Date": "2024-05-02", - "Value": 333444 - } - ] - } - -# eta 分类 -# level:3才可以获取到数据,所以需要人工把能源化工下所有的level3级都找到 - # url = 'http://10.189.2.78:8108/v1/edb/list?ClassifyId=1214' - #ParentId ":1160, 能源化工 - # ClassifyId ":1214,原油 3912 石油焦 - #ParentId ":1214,",就是原油下所有的数据。 -ClassifyId = 3707 - - - -############################################################################################################### 变量定义--测试环境 -server_host = '192.168.100.53' - -login_pushreport_url = f"http://{server_host}:8080/jingbo-dev/api/server/login" -upload_url = f"http://{server_host}:8080/jingbo-dev/api/analysis/reportInfo/researchUploadReportSave" -upload_warning_url = f"http://{server_host}:8080/jingbo-dev/api/basicBuiness/crudeOilWarning/save" -query_data_list_item_nos_url = f"http://{server_host}:8080/jingbo-dev/api/warehouse/dwDataItem/queryDataListItemNos" - -login_data = { - "data": { - "account": "api_test", - # "password": "MmVmNzNlOWI0MmY0ZDdjZGUwNzE3ZjFiMDJiZDZjZWU=", # Shihua@123456 - "password": "ZTEwYWRjMzk0OWJhNTlhYmJlNTZlMDU3ZjIwZjg4M2U=", # 123456 - "tenantHashCode": "8a4577dbd919675758d57999a1e891fe", - "terminal": "API" - }, - "funcModule": "API", - "funcOperation": "获取token" -} - -upload_data = { - "funcModule":'研究报告信息', - "funcOperation":'上传聚烯烃PP价格预测报告', - "data":{ - "groupNo":'000128', # 用户组编号 - "ownerAccount":'arui', #报告所属用户账号 - "reportType":'OIL_PRICE_FORECAST', # 报告类型,固定为OIL_PRICE_FORECAST - "fileName": '2000-40-5-50--100-原油指标数据.xlsx-Brent活跃合约--2024-09-06-15-01-29-预测报告.pdf', #文件名称 - "fileBase64": '' ,#文件内容base64 - "categoryNo":'yyjgycbg', # 研究报告分类编码 - "smartBusinessClassCode":'JXTJGYCBG', #分析报告分类编码 - "reportEmployeeCode":"E40116", # 报告人 - "reportDeptCode" :"D0044" ,# 报告部门 - "productGroupCode":"RAW_MATERIAL" # 商品分类 - } -} - - -warning_data = { - "groupNo":'000128', # 用户组编号 - "funcModule":'原油特征停更预警', - "funcOperation":'原油特征停更预警', - "data":{ - 'WARNING_TYPE_NAME':'特征数据停更预警', - 'WARNING_CONTENT':'', - 'WARNING_DATE':'' - } -} - -query_data_list_item_nos_data = { - "funcModule": "数据项", - "funcOperation": "查询", - "data": { - "dateStart":"20200101", - "dateEnd":"20241231", - "dataItemNoList":["Brentzdj","Brentzgj"] # 数据项编码,代表 brent最低价和最高价 - } -} - - -# 北京环境数据库 -host = '192.168.101.27' -port = 3306 -dbusername ='root' -password = '123456' -dbname = 'jingbo_test' -table_name = 'v_tbl_crude_oil_warning' - - -### 开关 -is_train = False # 是否训练 -is_debug = True # 是否调试 -is_eta = True # 是否使用eta接口 -is_market = False # 是否通过市场信息平台获取特征 ,在is_eta 为true 的情况下生效 -is_timefurture = True # 是否使用时间特征 -is_fivemodels = False # 是否使用之前保存的最佳的5个模型 -is_edbcode = False # 特征使用edbcoding列表中的 -is_edbnamelist = False # 自定义特征,对应上面的edbnamelist -is_update_eta = False # 预测结果上传到eta -is_update_report = True # 是否上传报告 -is_update_warning_data = False # 是否上传预警数据 -is_del_corr = 0.6 # 是否删除相关性高的特征,取值为 0-1 ,0 为不删除,0.6 表示删除相关性小于0.6的特征 -is_del_tow_month = True # 是否删除两个月不更新的特征 - - - -# 连接到数据库 -db_mysql = MySQLDB(host=host, user=dbusername, password=password, database=dbname) -db_mysql.connect() -print("数据库连接成功",host,dbname,dbusername) - - -# 数据截取日期 -start_year = 2020 # 数据开始年份 -end_time = '' # 数据截取日期 -freq = 'B' # 时间频率,"D": 天 "W": 周"M": 月"Q": 季度"A": 年 "H": 小时 "T": 分钟 "S": 秒 "B": 工作日 -delweekenday = True if freq == 'B' else False # 是否删除周末数据 -is_corr = False # 特征是否参与滞后领先提升相关系数 -add_kdj = False # 是否添加kdj指标 -if add_kdj and is_edbnamelist: - edbnamelist = edbnamelist+['K','D','J'] - -### 模型参数 -y = 'AVG-金能大唐久泰青州' -avg_cols = [ - 'PP:拉丝:1102K:出厂价:青州:国家能源宁煤(日)', - 'PP:拉丝:L5E89:出厂价:华北(第二区域):内蒙古久泰新材料(日)', - 'PP:拉丝:L5E89:出厂价:河北、鲁北:大唐内蒙多伦(日)', - 'PP:拉丝:HP550J:市场价:青岛:金能化学(日)' -] -offsite = 80 -offsite_col = ['PP:拉丝:HP550J:市场价:青岛:金能化学(日)'] -horizon =5 # 预测的步长 -input_size = 40 # 输入序列长度 -train_steps = 50 if is_debug else 1000 # 训练步数,用来限定epoch次数 -val_check_steps = 30 # 评估频率 -early_stop_patience_steps = 5 # 早停的耐心步数 -# --- 交叉验证用的参数 -test_size = 200 # 测试集大小,定义100,后面使用的时候重新赋值 -val_size = test_size # 验证集大小,同测试集大小 - -### 特征筛选用到的参数 -k = 100 # 特征筛选数量,如果是0或者值比特征数量大,代表全部特征 -corr_threshold = 0.6 # 相关性大于0.6的特征 -rote = 0.06 # 绘图上下界阈值 - -### 计算准确率 -weight_dict = [0.4,0.15,0.1,0.1,0.25] # 权重 - - -### 文件 -data_set = '石油焦指标数据.xlsx' # 数据集文件 -dataset = 'shiyoujiaodataset' # 数据集文件夹 - -# 数据库名称 -db_name = os.path.join(dataset,'jbsh_juxiting.db') -sqlitedb = SQLiteHandler(db_name) -sqlitedb.connect() - -settings = f'{input_size}-{horizon}-{train_steps}--{k}-{data_set}-{y}' -# 获取日期时间 -# now = datetime.datetime.now().strftime('%Y%m%d%H%M%S') # 获取当前日期时间 -now = datetime.datetime.now().strftime('%Y-%m-%d') # 获取当前日期时间 -reportname = f'PP大模型预测报告--{end_time}.pdf' # 报告文件名 -reportname = reportname.replace(':', '-') # 替换冒号 -if end_time == '': - end_time = now -### 邮件配置 -username='1321340118@qq.com' -passwd='wgczgyhtyyyyjghi' -# recv=['liurui_test@163.com','52585119@qq.com'] -recv=['liurui_test@163.com'] -# recv=['liurui_test@163.com'] -title='reportname' -content=y+'预测报告请看附件' -file=os.path.join(dataset,'reportname') -# file=os.path.join(dataset,'14-7-50--100-原油指标数据.xlsx-Brent连1合约价格--20240731175936-预测报告.pdf') -ssl=True - - -### 日志配置 - -# 创建日志目录(如果不存在) -log_dir = 'logs' -if not os.path.exists(log_dir): - os.makedirs(log_dir) - -# 配置日志记录器 -logger = logging.getLogger('my_logger') -logger.setLevel(logging.INFO) - -# 配置文件处理器,将日志记录到文件 -file_handler = logging.handlers.RotatingFileHandler(os.path.join(log_dir, 'pricepredict.log'), maxBytes=1024 * 1024, backupCount=5) -file_handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) - -# 配置控制台处理器,将日志打印到控制台 -console_handler = logging.StreamHandler() -console_handler.setFormatter(logging.Formatter('%(message)s')) - -# 将处理器添加到日志记录器 -logger.addHandler(file_handler) -logger.addHandler(console_handler) - -# logger.info('当前配置:'+settings) - diff --git a/config_shiyoujiao_lvyong.py b/config_shiyoujiao_lvyong.py new file mode 100644 index 0000000..895a9b7 --- /dev/null +++ b/config_shiyoujiao_lvyong.py @@ -0,0 +1,408 @@ +import logging +import os +import logging.handlers +import datetime +from lib.tools import MySQLDB, SQLiteHandler + + +# eta 接口token +APPID = "XNLDvxZHHugj7wJ7" +SECRET = "iSeU4s6cKKBVbt94htVY1p0sqUMqb2xa" + +# eta 接口url +sourcelisturl = 'http://10.189.2.78:8108/v1/edb/source/list' +classifylisturl = 'http://10.189.2.78:8108/v1/edb/classify/list?ClassifyType=' +uniquecodedataurl = 'http://10.189.2.78:8108/v1/edb/data?UniqueCode=4991c37becba464609b409909fe4d992&StartDate=2024-02-01' +classifyidlisturl = 'http://10.189.2.78:8108/v1/edb/list?ClassifyId=' +edbcodedataurl = 'http://10.189.2.78:8108/v1/edb/data?EdbCode=' +edbdatapushurl = 'http://10.189.2.78:8108/v1/edb/push' +edbdeleteurl = 'http://10.189.2.78:8108/v1/edb/business/edb/del' +edbbusinessurl = 'http://10.189.2.78:8108/v1/edb/business/data/del' + +edbcodenamedict = { + 'C2403283369': '预赔阳极加工利润(高端)', + 'C2403285560': '预培阳极加工利润(低端)', + 'C2403288616': '低硫石油焦煅烧利润', + 'S6949656': '平均价:氧化铝:一级:全国', + 'S5807052': '氧化铝:一级:贵阳', + 'S5443355': '市场价:煤沥青:河北地区', + 'S5443357': '市场价:煤沥青:山西地区', + 'W000294': '国内主要港口石油焦出货量(隆重)', + 'W000293': '日照港库存(隆重)', + 'W000292': '港口总库存(隆重)', + 'W000283': '主营石油焦产量(隆重)', + 'W000282': '地炼石油焦产量(隆重)', + 'W000281': '中国石油焦产量(隆重)', + 'W000280': '主营石油焦开工负荷率(隆重)', + 'W000279': '地炼石油焦开工负荷率(隆重)', + 'ID00150273': '石油焦:1 # :市场低端价:东北地区(日)', + 'ID00150281': '石油焦:1 # :市场主流价:东北地区(日)', + 'ID00150277': '石油焦:1 # :市场高端价:东北地区(日)', + 'ID00150289': '石油焦:2 # :市场低端价:华东地区(日)', + 'ID00150285': '石油焦:2 # :市场低端价:西北地区(日)', + 'ID00150313': '石油焦:2 # :市场主流价:华东地区(日)', + 'ID00150309': '石油焦:2 # :市场主流价:西北地区(日)', + 'ID00150301': '石油焦:2 # :市场高端价:华东地区(日)', + 'ID00150297': '石油焦:2 # :市场高端价:西北地区(日)', + 'ID00150321': '石油焦:2 # A:市场低端价:山东(日)', + 'ID00150329': '石油焦:2 # A:市场主流价:山东(日)', + 'ID00150325': '石油焦:2 # A:市场高端价:山东(日)', + 'ID00150337': '石油焦:2 # B:市场低端价:华南地区(日)', + 'ID00150341': '石油焦:2 # B:市场低端价:华中地区(日)', + 'ID00150361': '石油焦:2 # B:市场主流价:华南地区(日)', + 'ID00150365': '石油焦:2 # B:市场主流价:华中地区(日)', + 'ID00150349': '石油焦:2 # B:市场高端价:华南地区(日)', + 'ID00150353': '石油焦:2 # B:市场高端价:华中地区(日)', + 'ID00150333': '石油焦:2 # B:市场低端价:山东(日)', + 'ID00150357': '石油焦:2 # B:市场主流价:山东(日)', + 'ID00150345': '石油焦:2 # B:市场高端价:山东(日)', + 'ID00150369': '石油焦:3 # :市场低端价:华中地区(日)', + 'ID00150373': '石油焦:3 # :市场高端价:华中地区(日)', + 'ID00150385': '石油焦:3 # A:市场高端价:山东(日)', + 'ID00150393': '石油焦:3 # B:市场低端价:华东地区(日)', + 'ID00150409': '石油焦:3 # B:市场主流价:华东地区(日)', + 'ID00150401': '石油焦:3 # B:市场高端价:华东地区(日)', + 'ID00146589': '海绵焦:4 # :出厂价:华中地区:洛阳石化(日)', + 'ID01242846': '石油焦:4 # B:挂牌价:华北地区:中石化燕山(日)', + 'ID01300358': '石油焦:3 # C:市场低端价:山东(日)', + 'ID01300357': '石油焦:3 # C:市场高端价:山东(日)', + 'ID00150377': '石油焦:3 # :市场主流价:华中地区(日)', + 'ID01387643': '煅烧焦:低硫:0.5 % S:市场价:东北地区(日)', + 'ID01387646': '煅烧焦:低硫:3.5 % S:市场价:东北地区(日)', + 'ID01387660': '煅烧焦:中硫:3 % S,400V:市场价:山东(日)', + 'ID00150381': '石油焦:3 # A:市场低端价:山东(日)', + 'ID00150397': '石油焦:3 # B:市场低端价:山东(日)', + 'ID00150405': '石油焦:3 # B:市场高端价:山东(日)', + 'ID00146545': '海绵焦:3B:出厂价:山东:山东东明(日)', + 'B3e90b34e4b9e7a6ea3': '石油焦市场均价(元/吨)', + 'B6b5c53b270a3af12ac': '石油焦1 # 市场均价(元/吨)', + 'B10721189a11c209a20': '石油焦2 # 市场均价(元/吨)', + 'B6accfa9d2bf4735a50': '石油焦3 # 市场均价(元/吨)', + 'B8a0ab5357569c385a9': '石油焦海绵焦市场均价(元/吨)', + 'B19dcf45e22fbfd3e43': '石油焦海绵焦东北1 # A焦(低端)(元/吨)(百川)', + 'B5832a62d1e0fba50b6': '石油焦海绵焦东北1 # A焦(高端)(元/吨)(百川)', + 'B1de4fba026d4609cc7': '石油焦海绵焦东北1 # B焦(低端)(元/吨)(百川)', + 'B38f89180736172490d': '石油焦海绵焦东北1 # B焦(高端)(元/吨)(百川)', + 'B4f847871674c3d77f2': '石油焦海绵焦山东地炼1 # -3#焦(低端)(元/吨)', + 'B1aefb8a64a5200adbd': '石油焦海绵焦山东地炼1 # -3#焦(高端)(元/吨)', + 'B1df7d0afbfedfb628a': '煅烧焦东北低硫(高端S < 0.5)(元/吨)', + 'B5f8f9859635876da28': '煅烧焦东北低硫(低端S < 0.5)(元/吨)', + 'B2342a8c5a39fa00348': '煅烧焦华北中硫(高端S < 3.0,钒 < 400)(元/吨)', + 'B051f27900397c6a35f': '煅烧焦山东中硫(高端S < 3.0,钒 < 400)(元/吨)', + 'Be2a8050a48e86cae1f': '煅烧焦华东中硫(高端S < 3.0,钒 < 400)(元/吨)', + 'B4a1811938f85065f6a': '煅烧焦华中中硫(高端S < 3.0,钒 < 400)(元/吨)', + 'Bc197d4834ef7fb98ec': '煅烧焦华东高硫(高端S < 3.5,钒 < 400)(元/吨)', + 'B62be5dbdb8c6454530': '煅烧焦低硫参考价格(元/吨)(百川)', + 'Bdd813140bffc4edfa6': '煅烧焦中硫微量市场均价(元/吨)(百川)', + 'B185a597decfc71915a': '预焙阳极山东低端(元/吨)(百川)', + 'B1bcde6130de031bd42': '山西 改质沥青(元/吨)', + 'Bb9f4a1f6dd32b4ad8a': '山东 改质沥青(元/吨)', + 'C2411261557491549': '石油焦市场均价(元/吨)/4DMA', + 'C2411271143174617': '石油焦市场均价(元/吨)/9DMA', + 'ID01387649': '煅烧焦:中硫:3 % S,350V:市场价:华东地区(日)', + 'ID01387655': '煅烧焦:中硫:3 % S,350V:市场价:山东(日)', + 'RE00010076': '煅烧焦:低硫:生产毛利:东北地区(周)', + 'B9d1acaf80383683da3': '石油焦总产量(周)(吨)', + 'Bdaa719a38936c8dd76': '石油焦开工率(周)( % )', + 'B9459d549a332b200e7': '石油焦行业总库存(周)(吨)', + 'Bce6e098b9518370cff': '石油焦工厂库存(周)(吨)', + 'B577ce2809772779710': '石油焦市场库存(周)(吨)', + 'B5d8c564c62f3e6b77f': '石油焦成本(周)(吨)', + 'B43baa98bcaa06c11a5': '石油焦利润(周)(吨)', + 'Bdd0c1361d94081211c': '煅烧石油焦总产量(周)(吨)', + 'B65315111fa28951b1e': '煅烧石油焦开工率(周)( % )', + 'B2aff5f2632a20027d0': '煅烧石油焦行业总库存(周)(吨)', + 'B29fbd31128cd71b212': '煅烧石油焦工厂库存(周)(吨)', + 'B7a88313a89d1261c53': '煅烧石油焦成本(周)(吨)', + 'Bd4fa36b4decec0aafa': '煅烧石油焦利润(周)(吨)', + 'B9bd80eac7df81ffbd4': '预焙阳极总产量(周)(吨)', + 'B27074786605f4660d2': '预焙阳极开工率(周)( % )', + 'Bdc2a5985ecb56b6a0c': '预焙阳极行业总库存(周)(吨)', + 'Bce8511f899e487e5b6': '预焙阳极工厂库存(周)(吨)', + 'B13ec89105bd866a2bd': '预焙阳极成本(周)(吨)', + 'B66c3abcfa15a2e611c': '预焙阳极利润(周)(吨)', + 'Bf7efe3200f9abc0453': '电解铝开工率(周)( % )', + 'Be193166f347267b1a7': '电解铝行业总库存(周)(吨)', + 'Baa744fc97769353175': '电解铝工厂库存(周)(吨)', + 'Bf9654603913cfc5282': '电解铝市场库存(周)(吨)', + 'Bef1535c96da0d70fbc': '电解铝利润(周)(吨)', + 'B7d1d0b24316d49cbdc': '煤沥青总产量(周)(吨)', + 'B4303fb002ea1c214da': '煤沥青开工率(周)( % )', + 'Be9a470c97e9efe660c': '煤沥青行业总库存(周)(吨)', + 'B50d4d87f6b78bca587': '煤沥青工厂库存(周)(吨)', + 'B46cc7d0a90155b5bfd': '煅烧焦山东高硫(高端S < 3.5,普货)(元/吨)' +} + +edbcodelist = edbcodenamedict.keys() +# 临时写死用指定的列,与上面的edbcode对应,后面更改 +edbnamelist = ['ds', 'y']+[edbcodenamedict[edbcodename] + for edbcodename in edbcodelist] + +# eta自有数据指标编码,石油焦铝用还没新增,暂且留空 +modelsindex = { +} + +# 百川数据指标编码 +baicangidnamedict = { + '1588348470396480000': '石油焦滨州-友泰', + '1588348470396480000.00': '石油焦东营-海科瑞林', + '1588348470396480000.00': '石油焦东营-华联2', + '1588348470396480000.00': '石油焦东营-华联3', + '1588348470396480000.00': '石油焦东营-联合', + '1588348470396480000.00': '石油焦东营-联合3', + '1588348470396480915': '石油焦淄博-汇丰', + '1588348470396480888': '石油焦沧州-鑫海', + '1588348470396480917': '石油焦东营-万通', + '1588348470396480925': '石油焦东营-齐润', + '1588348470396481084': '石油焦东营-尚能4', + '1588348470396480930': '石油焦潍坊-寿光鲁清', + '1588348470396480929': '石油焦滨州-鑫岳', +} + + +# eta 上传预测结果的请求体,后面发起请求的时候更改 model datalist 数据 +data = { + "IndexCode": "", + "IndexName": "价格预测模型", + "Unit": "无", + "Frequency": "日度", + "SourceName": f"价格预测", + "Remark": 'ddd', + "DataList": [ + { + "Date": "2024-05-02", + "Value": 333444 + } + ] +} + +# eta 分类 +# level:3才可以获取到数据,所以需要人工把能源化工下所有的level3级都找到 +# url = 'http://10.189.2.78:8108/v1/edb/list?ClassifyId=1214' +# ParentId ":1160, 能源化工 +# ClassifyId ":1214,原油 3912 石油焦 +# ParentId ":1214,",就是原油下所有的数据。 +ClassifyId = 3707 + + +# 变量定义--测试环境 +server_host = '192.168.100.53' # 内网 +# server_host = '183.242.74.28' # 外网 +login_pushreport_url = f"http://{server_host}:8080/jingbo-dev/api/server/login" +# 上传报告 +upload_url = f"http://{server_host}:8080/jingbo-dev/api/analysis/reportInfo/researchUploadReportSave" +# 停更预警 +upload_warning_url = f"http://{server_host}:8080/jingbo-dev/api/basicBuiness/crudeOilWarning/save" +# 查询数据项编码 +query_data_list_item_nos_url = f"http://{server_host}:8080/jingbo-dev/api/warehouse/dwDataItem/queryDataListItemNos" +# 上传数据项值 +push_data_value_list_url = f"http://{server_host}:8080/jingbo-dev/api/dw/dataValue/pushDataValueList" + +login_data = { + "data": { + "account": "api_test", + # "password": "MmVmNzNlOWI0MmY0ZDdjZGUwNzE3ZjFiMDJiZDZjZWU=", # Shihua@123456 + "password": "ZTEwYWRjMzk0OWJhNTlhYmJlNTZlMDU3ZjIwZjg4M2U=", # 123456 + "tenantHashCode": "8a4577dbd919675758d57999a1e891fe", + "terminal": "API" + }, + "funcModule": "API", + "funcOperation": "获取token" +} + +upload_data = { + "groupNo": '', # 用户组id + "funcModule": '研究报告信息', + "funcOperation": '上传原油价格预测报告', + "data": { + "ownerAccount": 'arui', # 报告所属用户账号 + "reportType": 'OIL_PRICE_FORECAST', # 报告类型,固定为OIL_PRICE_FORECAST + "fileName": '2000-40-5-50--100-原油指标数据.xlsx-Brent活跃合约--2024-09-06-15-01-29-预测报告.pdf', # 文件名称 + "fileBase64": '', # 文件内容base64 + "categoryNo": 'yyjgycbg', # 研究报告分类编码 + "smartBusinessClassCode": 'YCJGYCBG', # 分析报告分类编码 + "reportEmployeeCode": "E40116", # 报告人 + "reportDeptCode": "D0044", # 报告部门 + "productGroupCode": "RAW_MATERIAL" # 商品分类 + } +} + + +warning_data = { + "groupNo": '', # 用户组id + "funcModule": '原油特征停更预警', + "funcOperation": '原油特征停更预警', + "data": { + 'WARNING_TYPE_NAME': '特征数据停更预警', + 'WARNING_CONTENT': '', + 'WARNING_DATE': '' + } +} + +query_data_list_item_nos_data = { + "funcModule": "数据项", + "funcOperation": "查询", + "data": { + "dateStart": "20200101", + "dateEnd": "20241231", + "dataItemNoList": ["Brentzdj", "Brentzgj"] # 数据项编码,代表 brent最低价和最高价 + } +} + +push_data_value_list_data = { + "funcModule": "数据表信息列表", + "funcOperation": "新增", + "data": [ + {"dataItemNo": "91230600716676129", + "dataDate": "20230113", + "dataStatus": "add", + "dataValue": 100.11 + }, + {"dataItemNo": "91230600716676129P|ETHYL_BEN|CAPACITY", + "dataDate": "20230113", + "dataStatus": "add", + "dataValue": 100.55 + }, + {"dataItemNo": "91230600716676129P|ETHYL_BEN|CAPACITY", + "dataDate": "20230113", + "dataStatus": "add", + "dataValue": 100.55 + } + ] +} +# 八大维度数据项编码 +bdwd_items = { + # 'ciri': 'yyycbdwdcr', + # 'benzhou': 'yyycbdwdbz', + # 'cizhou': 'yyycbdwdcz', + # 'gezhou': 'yyycbdwdgz', + # 'ciyue': 'yyycbdwdcy', + # 'cieryue': 'yyycbdwdcey', + # 'cisanyue': 'yyycbdwdcsy', + # 'cisiyue': 'yyycbdwdcsiy', +} + +# 北京环境数据库 +host = '192.168.101.27' +port = 3306 +dbusername = 'root' +password = '123456' +dbname = 'jingbo_test' +table_name = 'v_tbl_crude_oil_warning' +baichuan_table_name = 'V_TBL_BAICHUAN_YINGFU_VALUE' +# select BAICHUAN_ID, DATA_DATE, DATA_VALUE from V_TBL_BAICHUAN_YINGFU_VALUE where BAICHUAN_ID in ('1588348470396475286', '1666') +# 开关 +is_train = True # 是否训练 +is_debug = False # 是否调试 +is_eta = True # 是否使用eta接口 +is_market = False # 是否通过市场信息平台获取特征 ,在is_eta 为true 的情况下生效 +is_timefurture = True # 是否使用时间特征 +is_fivemodels = False # 是否使用之前保存的最佳的5个模型 +is_edbcode = False # 特征使用edbcoding列表中的 +is_edbnamelist = False # 自定义特征,对应上面的edbnamelist +is_update_eta = False # 预测结果上传到eta +is_update_report = True # 是否上传报告 +is_update_warning_data = False # 是否上传预警数据 +is_update_predict_value = True # 是否上传预测值到市场信息平台 +is_del_corr = 0.6 # 是否删除相关性高的特征,取值为 0-1 ,0 为不删除,0.6 表示删除相关性小于0.6的特征 +is_del_tow_month = True # 是否删除两个月不更新的特征 + + +# 连接到数据库 +db_mysql = MySQLDB(host=host, user=dbusername, + password=password, database=dbname) +db_mysql.connect() +print("数据库连接成功", host, dbname, dbusername) + + +# 数据截取日期 +start_year = 2020 # 数据开始年份 +end_time = '' # 数据截取日期 +freq = 'B' # 时间频率,"D": 天 "W": 周"M": 月"Q": 季度"A": 年 "H": 小时 "T": 分钟 "S": 秒 "B": 工作日 +delweekenday = True if freq == 'B' else False # 是否删除周末数据 +is_corr = False # 特征是否参与滞后领先提升相关系数 +add_kdj = False # 是否添加kdj指标 +if add_kdj and is_edbnamelist: + edbnamelist = edbnamelist+['K', 'D', 'J'] + +# 模型参数 +y = 'B46cc7d0a90155b5bfd' +avg_cols = [ + +] +offsite = 80 +offsite_col = [] +horizon = 5 # 预测的步长 +input_size = 40 # 输入序列长度 +train_steps = 50 if is_debug else 1000 # 训练步数,用来限定epoch次数 +val_check_steps = 30 # 评估频率 +early_stop_patience_steps = 5 # 早停的耐心步数 +# --- 交叉验证用的参数 +test_size = 200 # 测试集大小,定义100,后面使用的时候重新赋值 +val_size = test_size # 验证集大小,同测试集大小 + +# 特征筛选用到的参数 +k = 100 # 特征筛选数量,如果是0或者值比特征数量大,代表全部特征 +corr_threshold = 0.6 # 相关性大于0.6的特征 +rote = 0.06 # 绘图上下界阈值 + +# 计算准确率 +weight_dict = [0.4, 0.15, 0.1, 0.1, 0.25] # 权重 + + +# 文件 +data_set = '石油焦铝用指标数据.xlsx' # 数据集文件 +dataset = 'shiyoujiaolvyongdataset' # 数据集文件夹 + +# 数据库名称 +db_name = os.path.join(dataset, 'jbsh_shiyoujiao_lvyong.db') +sqlitedb = SQLiteHandler(db_name) +sqlitedb.connect() + +settings = f'{input_size}-{horizon}-{train_steps}--{k}-{data_set}-{y}' +# 获取日期时间 +# now = datetime.datetime.now().strftime('%Y%m%d%H%M%S') # 获取当前日期时间 +now = datetime.datetime.now().strftime('%Y-%m-%d') # 获取当前日期时间 +reportname = f'石油焦铝用大模型预测报告--{end_time}.pdf' # 报告文件名 +reportname = reportname.replace(':', '-') # 替换冒号 +if end_time == '': + end_time = now +# 邮件配置 +username = '1321340118@qq.com' +passwd = 'wgczgyhtyyyyjghi' +# recv=['liurui_test@163.com','52585119@qq.com'] +recv = ['liurui_test@163.com'] +# recv=['liurui_test@163.com'] +title = 'reportname' +content = y+'预测报告请看附件' +file = os.path.join(dataset, 'reportname') +# file=os.path.join(dataset,'14-7-50--100-原油指标数据.xlsx-Brent连1合约价格--20240731175936-预测报告.pdf') +ssl = True + + +# 日志配置 + +# 创建日志目录(如果不存在) +log_dir = 'logs' +if not os.path.exists(log_dir): + os.makedirs(log_dir) + +# 配置日志记录器 +logger = logging.getLogger('my_logger') +logger.setLevel(logging.INFO) + +# 配置文件处理器,将日志记录到文件 +file_handler = logging.handlers.RotatingFileHandler(os.path.join( + log_dir, 'pricepredict.log'), maxBytes=1024 * 1024, backupCount=5) +file_handler.setFormatter(logging.Formatter( + '%(asctime)s - %(name)s - %(levelname)s - %(message)s')) + +# 配置控制台处理器,将日志打印到控制台 +console_handler = logging.StreamHandler() +console_handler.setFormatter(logging.Formatter('%(message)s')) + +# 将处理器添加到日志记录器 +logger.addHandler(file_handler) +logger.addHandler(console_handler) + +# logger.info('当前配置:'+settings) diff --git a/config_shiyoujiao_puhuo.py b/config_shiyoujiao_puhuo.py new file mode 100644 index 0000000..e271eb1 --- /dev/null +++ b/config_shiyoujiao_puhuo.py @@ -0,0 +1,319 @@ +import logging +import os +import logging.handlers +import datetime +from lib.tools import MySQLDB, SQLiteHandler + + +# eta 接口token +APPID = "XNLDvxZHHugj7wJ7" +SECRET = "iSeU4s6cKKBVbt94htVY1p0sqUMqb2xa" + +# eta 接口url +sourcelisturl = 'http://10.189.2.78:8108/v1/edb/source/list' +classifylisturl = 'http://10.189.2.78:8108/v1/edb/classify/list?ClassifyType=' +uniquecodedataurl = 'http://10.189.2.78:8108/v1/edb/data?UniqueCode=4991c37becba464609b409909fe4d992&StartDate=2024-02-01' +classifyidlisturl = 'http://10.189.2.78:8108/v1/edb/list?ClassifyId=' +edbcodedataurl = 'http://10.189.2.78:8108/v1/edb/data?EdbCode=' +edbdatapushurl = 'http://10.189.2.78:8108/v1/edb/push' +edbdeleteurl = 'http://10.189.2.78:8108/v1/edb/business/edb/del' +edbbusinessurl = 'http://10.189.2.78:8108/v1/edb/business/data/del' +edbcodelist = ['ID01385938', 'lmcads03 lme comdty', + 'GC1 COMB Comdty', + 'C2404171822', + 'dxy curncy', + 'S5443199 ', + 'S5479800', + 'S5443108', + 'H7358586', + 'LC3FM1 INDEX', + 'CNY REGN Curncy', + 's0105897', + 'M0067419', + 'M0066351', + 'S0266372', + 'S0266438', + 'S0266506', + 'ID01384463'] + +# 临时写死用指定的列,与上面的edbcode对应,后面更改 +edbnamelist = [ + 'ds', 'y', + 'LME铜价', + '黄金连1合约', + 'Brent-WTI', + '美元指数', + '甲醇鲁南价格', + '甲醇太仓港口价格', + '山东丙烯主流价', + '丙烷(山东)', + 'FEI丙烷 M1', + '在岸人民币汇率', + '南华工业品指数', + 'PVC期货主力', + 'PE期货收盘价', + 'PP连续-1月', + 'PP连续-5月', + 'PP连续-9月', + 'PP:拉丝:L5E89:出厂价:华北(第二区域):内蒙古久泰新材料(日)' +] + +edbcodenamedict = { + 'ID01385938': 'PP:拉丝:1102K:市场价:青州:国家能源宁煤(日)', + 'ID01384463': 'PP:拉丝:L5E89:出厂价:华北(第二区域):内蒙古久泰新材料(日)', + 'lmcads03 lme comdty': 'LME铜价', + 'GC1 COMB Comdty': '黄金连1合约', + 'C2404171822': 'Brent-WTI', + 'dxy curncy': '美元指数', + 'S5443199 ': '甲醇鲁南价格', + 'S5479800': '甲醇太仓港口价格', + 'S5443108': '山东丙烯主流价', + 'H7358586': '丙烷(山东)', + 'LC3FM1 INDEX': 'FEI丙烷 M1', + 'CNY REGN Curncy': '在岸人民币汇率', + 's0105897': '南华工业品指数', + 'M0067419': 'PVC期货主力', + 'M0066351': 'PE期货收盘价', + 'S0266372': 'PP连续-1月', + 'S0266438': 'PP连续-5月', + 'S0266506': 'PP连续-9月', + +} + +# eta自有数据指标编码 +modelsindex = { + 'NHITS': 'SELF0000077', + 'Informer': 'SELF0000078', + 'LSTM': 'SELF0000079', + 'iTransformer': 'SELF0000080', + 'TSMixer': 'SELF0000081', + 'TSMixerx': 'SELF0000082', + 'PatchTST': 'SELF0000083', + 'RNN': 'SELF0000084', + 'GRU': 'SELF0000085', + 'TCN': 'SELF0000086', + 'BiTCN': 'SELF0000087', + 'DilatedRNN': 'SELF0000088', + 'MLP': 'SELF0000089', + 'DLinear': 'SELF0000090', + 'NLinear': 'SELF0000091', + 'TFT': 'SELF0000092', + 'FEDformer': 'SELF0000093', + 'StemGNN': 'SELF0000094', + 'MLPMultivariate': 'SELF0000095', + 'TiDE': 'SELF0000096', + 'DeepNPTS': 'SELF0000097' +} + + +# eta 上传预测结果的请求体,后面发起请求的时候更改 model datalist 数据 +data = { + "IndexCode": "", + "IndexName": "价格预测模型", + "Unit": "无", + "Frequency": "日度", + "SourceName": f"价格预测", + "Remark": 'ddd', + "DataList": [ + { + "Date": "2024-05-02", + "Value": 333444 + } + ] +} + +# eta 分类 +# level:3才可以获取到数据,所以需要人工把能源化工下所有的level3级都找到 +# url = 'http://10.189.2.78:8108/v1/edb/list?ClassifyId=1214' +# ParentId ":1160, 能源化工 +# ClassifyId ":1214,原油 3912 石油焦 +# ParentId ":1214,",就是原油下所有的数据。 +ClassifyId = 3707 + + +# 变量定义--测试环境 +server_host = '192.168.100.53' + +login_pushreport_url = f"http://{server_host}:8080/jingbo-dev/api/server/login" +upload_url = f"http://{server_host}:8080/jingbo-dev/api/analysis/reportInfo/researchUploadReportSave" +upload_warning_url = f"http://{server_host}:8080/jingbo-dev/api/basicBuiness/crudeOilWarning/save" +query_data_list_item_nos_url = f"http://{server_host}:8080/jingbo-dev/api/warehouse/dwDataItem/queryDataListItemNos" + +login_data = { + "data": { + "account": "api_test", + # "password": "MmVmNzNlOWI0MmY0ZDdjZGUwNzE3ZjFiMDJiZDZjZWU=", # Shihua@123456 + "password": "ZTEwYWRjMzk0OWJhNTlhYmJlNTZlMDU3ZjIwZjg4M2U=", # 123456 + "tenantHashCode": "8a4577dbd919675758d57999a1e891fe", + "terminal": "API" + }, + "funcModule": "API", + "funcOperation": "获取token" +} + +upload_data = { + "funcModule": '研究报告信息', + "funcOperation": '上传聚烯烃PP价格预测报告', + "data": { + "groupNo": '000128', # 用户组编号 + "ownerAccount": 'arui', # 报告所属用户账号 + "reportType": 'OIL_PRICE_FORECAST', # 报告类型,固定为OIL_PRICE_FORECAST + "fileName": '2000-40-5-50--100-原油指标数据.xlsx-Brent活跃合约--2024-09-06-15-01-29-预测报告.pdf', # 文件名称 + "fileBase64": '', # 文件内容base64 + "categoryNo": 'yyjgycbg', # 研究报告分类编码 + "smartBusinessClassCode": 'JXTJGYCBG', # 分析报告分类编码 + "reportEmployeeCode": "E40116", # 报告人 + "reportDeptCode": "D0044", # 报告部门 + "productGroupCode": "RAW_MATERIAL" # 商品分类 + } +} + + +warning_data = { + "groupNo": '000128', # 用户组编号 + "funcModule": '原油特征停更预警', + "funcOperation": '原油特征停更预警', + "data": { + 'WARNING_TYPE_NAME': '特征数据停更预警', + 'WARNING_CONTENT': '', + 'WARNING_DATE': '' + } +} + +query_data_list_item_nos_data = { + "funcModule": "数据项", + "funcOperation": "查询", + "data": { + "dateStart": "20200101", + "dateEnd": "20241231", + "dataItemNoList": ["Brentzdj", "Brentzgj"] # 数据项编码,代表 brent最低价和最高价 + } +} + + +# 北京环境数据库 +host = '192.168.101.27' +port = 3306 +dbusername = 'root' +password = '123456' +dbname = 'jingbo_test' +table_name = 'v_tbl_crude_oil_warning' +baichuan_table_name = 'V_TBL_BAICHUAN_YINGFU_VALUE' +# select BAICHUAN_ID, DATA_DATE, DATA_VALUE from V_TBL_BAICHUAN_YINGFU_VALUE where BAICHUAN_ID in ('1588348470396475286', '1666') +# 开关 +is_train = False # 是否训练 +is_debug = True # 是否调试 +is_eta = True # 是否使用eta接口 +is_market = False # 是否通过市场信息平台获取特征 ,在is_eta 为true 的情况下生效 +is_timefurture = True # 是否使用时间特征 +is_fivemodels = False # 是否使用之前保存的最佳的5个模型 +is_edbcode = False # 特征使用edbcoding列表中的 +is_edbnamelist = False # 自定义特征,对应上面的edbnamelist +is_update_eta = False # 预测结果上传到eta +is_update_report = True # 是否上传报告 +is_update_warning_data = False # 是否上传预警数据 +is_del_corr = 0.6 # 是否删除相关性高的特征,取值为 0-1 ,0 为不删除,0.6 表示删除相关性小于0.6的特征 +is_del_tow_month = True # 是否删除两个月不更新的特征 + + +# 连接到数据库 +db_mysql = MySQLDB(host=host, user=dbusername, + password=password, database=dbname) +db_mysql.connect() +print("数据库连接成功", host, dbname, dbusername) + + +# 数据截取日期 +start_year = 2020 # 数据开始年份 +end_time = '' # 数据截取日期 +freq = 'B' # 时间频率,"D": 天 "W": 周"M": 月"Q": 季度"A": 年 "H": 小时 "T": 分钟 "S": 秒 "B": 工作日 +delweekenday = True if freq == 'B' else False # 是否删除周末数据 +is_corr = False # 特征是否参与滞后领先提升相关系数 +add_kdj = False # 是否添加kdj指标 +if add_kdj and is_edbnamelist: + edbnamelist = edbnamelist+['K', 'D', 'J'] + +# 模型参数 +y = 'AVG-金能大唐久泰青州' +avg_cols = [ + 'PP:拉丝:1102K:出厂价:青州:国家能源宁煤(日)', + 'PP:拉丝:L5E89:出厂价:华北(第二区域):内蒙古久泰新材料(日)', + 'PP:拉丝:L5E89:出厂价:河北、鲁北:大唐内蒙多伦(日)', + 'PP:拉丝:HP550J:市场价:青岛:金能化学(日)' +] +offsite = 80 +offsite_col = ['PP:拉丝:HP550J:市场价:青岛:金能化学(日)'] +horizon = 5 # 预测的步长 +input_size = 40 # 输入序列长度 +train_steps = 50 if is_debug else 1000 # 训练步数,用来限定epoch次数 +val_check_steps = 30 # 评估频率 +early_stop_patience_steps = 5 # 早停的耐心步数 +# --- 交叉验证用的参数 +test_size = 200 # 测试集大小,定义100,后面使用的时候重新赋值 +val_size = test_size # 验证集大小,同测试集大小 + +# 特征筛选用到的参数 +k = 100 # 特征筛选数量,如果是0或者值比特征数量大,代表全部特征 +corr_threshold = 0.6 # 相关性大于0.6的特征 +rote = 0.06 # 绘图上下界阈值 + +# 计算准确率 +weight_dict = [0.4, 0.15, 0.1, 0.1, 0.25] # 权重 + + +# 文件 +data_set = '石油焦指标数据.xlsx' # 数据集文件 +dataset = 'shiyoujiaodataset' # 数据集文件夹 + +# 数据库名称 +db_name = os.path.join(dataset, 'jbsh_juxiting.db') +sqlitedb = SQLiteHandler(db_name) +sqlitedb.connect() + +settings = f'{input_size}-{horizon}-{train_steps}--{k}-{data_set}-{y}' +# 获取日期时间 +# now = datetime.datetime.now().strftime('%Y%m%d%H%M%S') # 获取当前日期时间 +now = datetime.datetime.now().strftime('%Y-%m-%d') # 获取当前日期时间 +reportname = f'PP大模型预测报告--{end_time}.pdf' # 报告文件名 +reportname = reportname.replace(':', '-') # 替换冒号 +if end_time == '': + end_time = now +# 邮件配置 +username = '1321340118@qq.com' +passwd = 'wgczgyhtyyyyjghi' +# recv=['liurui_test@163.com','52585119@qq.com'] +recv = ['liurui_test@163.com'] +# recv=['liurui_test@163.com'] +title = 'reportname' +content = y+'预测报告请看附件' +file = os.path.join(dataset, 'reportname') +# file=os.path.join(dataset,'14-7-50--100-原油指标数据.xlsx-Brent连1合约价格--20240731175936-预测报告.pdf') +ssl = True + + +# 日志配置 + +# 创建日志目录(如果不存在) +log_dir = 'logs' +if not os.path.exists(log_dir): + os.makedirs(log_dir) + +# 配置日志记录器 +logger = logging.getLogger('my_logger') +logger.setLevel(logging.INFO) + +# 配置文件处理器,将日志记录到文件 +file_handler = logging.handlers.RotatingFileHandler(os.path.join( + log_dir, 'pricepredict.log'), maxBytes=1024 * 1024, backupCount=5) +file_handler.setFormatter(logging.Formatter( + '%(asctime)s - %(name)s - %(levelname)s - %(message)s')) + +# 配置控制台处理器,将日志打印到控制台 +console_handler = logging.StreamHandler() +console_handler.setFormatter(logging.Formatter('%(message)s')) + +# 将处理器添加到日志记录器 +logger.addHandler(file_handler) +logger.addHandler(console_handler) + +# logger.info('当前配置:'+settings) diff --git a/lib/dataread.py b/lib/dataread.py index 8895ff4..84b85cf 100644 --- a/lib/dataread.py +++ b/lib/dataread.py @@ -797,14 +797,14 @@ def datachuli(df_zhibiaoshuju, df_zhibiaoliebiao, datecol='date', end_time='', y if config.is_del_tow_month: current_date = datetime.datetime.now() two_months_ago = current_date - timedelta(days=180) - config.logger.info(f'删除两月不更新特征前数据量:{df.shape}') + config.logger.info(f'删除半年不更新特征前数据量:{df.shape}') columns_to_drop = [] for clo in df.columns: if check_column(df, clo, two_months_ago): columns_to_drop.append(clo) df = df.drop(columns=columns_to_drop) - config.logger.info(f'删除两月不更新特征后数据量:{df.shape}') + config.logger.info(f'删除半年不更新特征后数据量:{df.shape}') # 衍生时间特征 if is_timefurture: @@ -1604,6 +1604,7 @@ class EtaReader(): f'Error: {response.status_code}, {response.text}') # 主动抛出异常 raise Exception(f'Error: {response.status_code}, {response.text}') + # 原油数据获取 def get_eta_api_yuanyou_data(self, data_set, dataset=''): ''' @@ -1790,6 +1791,7 @@ class EtaReader(): df_zhibiaoshuju = df1.copy() df_zhibiaoliebiao = df.copy() return df_zhibiaoshuju, df_zhibiaoliebiao + # 聚烯烃PP数据获取 def get_eta_api_pp_data(self, data_set, dataset=''): today = datetime.date.today().strftime("%Y-%m-%d") @@ -2012,6 +2014,71 @@ class EtaReader(): # 主动抛出异常 raise Exception(f'Error: {response.status_code}, {response.text}') + # 石油焦铝用数据获取 + def get_eta_api_shiyoujiao_lvyong_data(self, data_set, dataset=''): + today = datetime.date.today().strftime("%Y-%m-%d") + + # 定义你的headers,这里可以包含多个参数 + self.headers = { + 'nonce': self.signature.nonce, # 例如,一个认证令牌 + # 自定义的header参数 + 'timestamp': str(self.signature.timestamp), + 'appid': self.signature.APPID, # 另一个自定义的header参数 + 'signature': self.signature.signature + } + + # 从列表数据中获取指标名称,判断指标名称频度是否为日 ,如果是,则获取UniqueCode,然后获取指标数据,保存到xlat文件中的sheet表。 + + ''' + df = sheetname 指标列表,存储 指标分类-指标名称-指标id-频度 + df1 = sheetname 指标数据 ,存储 时间-指标名称1-指标名称2... + + ''' + + # 构建新的DataFrame df df1 + df = pd.DataFrame(columns=['指标分类', '指标名称', '指标id', '频度']) + df1 = pd.DataFrame(columns=['DataTime']) + + # 外网环境无法访问,请确认是否为内网环境 + try: + # 发送GET请求 获取指标分类列表 + response = requests.get(self.classifylisturl, headers=self.headers) + except requests.exceptions.RequestException as e: + raise Exception(f"请求失败,请确认是否为内网环境: {e}", "\033[0m") + # 找到列表中不在指标列中的指标id,保存成新的list + new_list = [ + item for item in self.edbcodelist if item not in df['指标id'].tolist()] + config.logger.info(new_list) + # 遍历new_list,获取指标数据,保存到df1 + for item in new_list: + config.logger.info(item) + # 将item 加入到 df['指标id']中 + try: + itemname = config.edbcodenamedict[item] + except: + itemname = item + + df1 = self.edbcodegetdata(df1, item, itemname) + df = pd.concat([df, pd.DataFrame( + {'指标分类': '其他', '指标名称': itemname, '指标id': item, '频度': '其他'}, index=[0])]) + + # 按时间排序 + df1.sort_values('DataTime', inplace=True, ascending=False) + df1.rename(columns={'DataTime': 'date'}, inplace=True) + # df1.dropna(inplace=True) + # 去掉大于今天日期的行 + df1 = df1[df1['date'] <= datetime.datetime.now().strftime('%Y-%m-%d')] + config.logger.info(df1.head()) + # config.logger.info(f'{df1.head()}') + # 保存到xlsx文件的sheet表 + with pd.ExcelWriter(os.path.join(dataset, data_set)) as file: + df1.to_excel(file, sheet_name='指标数据', index=False) + df.to_excel(file, sheet_name='指标列表', index=False) + + df_zhibiaoshuju = df1.copy() + df_zhibiaoliebiao = df.copy() + return df_zhibiaoshuju, df_zhibiaoliebiao + def get_market_data(end_time, df): """ diff --git a/main_juxiting.py b/main_juxiting.py index 9126724..2b878e3 100644 --- a/main_juxiting.py +++ b/main_juxiting.py @@ -340,7 +340,8 @@ def predict_main(): logger.info('模型训练完成') logger.info('训练数据绘图ing') - model_results3 = model_losss_juxiting(sqlitedb, end_time=global_config['end_time'],is_fivemodels=global_config['is_fivemodels']) + model_results3 = model_losss_juxiting( + sqlitedb, end_time=global_config['end_time'], is_fivemodels=global_config['is_fivemodels']) logger.info('训练数据绘图end') # # 模型报告 diff --git a/main_shiyoujiao_lvyong.py b/main_shiyoujiao_lvyong.py new file mode 100644 index 0000000..63bb087 --- /dev/null +++ b/main_shiyoujiao_lvyong.py @@ -0,0 +1,445 @@ +# 读取配置 + +from lib.dataread import * +from config_shiyoujiao_lvyong import * +from lib.tools import SendMail, exception_logger +from models.nerulforcastmodels import ex_Model, model_losss, model_losss_juxiting, brent_export_pdf, tansuanli_export_pdf, pp_export_pdf, model_losss_juxiting +import datetime +import torch +torch.set_float32_matmul_precision("high") + +global_config.update({ + # 核心参数 + 'logger': logger, + 'dataset': dataset, + 'y': y, + 'is_debug': is_debug, + 'is_train': is_train, + 'is_fivemodels': is_fivemodels, + 'settings': settings, + 'weight_dict': weight_dict, + + + # 模型参数 + 'data_set': data_set, + 'input_size': input_size, + 'horizon': horizon, + 'train_steps': train_steps, + 'val_check_steps': val_check_steps, + 'val_size': val_size, + 'test_size': test_size, + 'modelsindex': modelsindex, + 'rote': rote, + 'bdwd_items': bdwd_items, + + # 特征工程开关 + 'is_del_corr': is_del_corr, + 'is_del_tow_month': is_del_tow_month, + 'is_eta': is_eta, + 'is_update_eta': is_update_eta, + 'is_fivemodels': is_fivemodels, + 'is_update_predict_value': is_update_predict_value, + 'early_stop_patience_steps': early_stop_patience_steps, + + # 时间参数 + 'start_year': start_year, + 'end_time': end_time or datetime.datetime.now().strftime("%Y-%m-%d"), + 'freq': freq, # 保持列表结构 + + # 接口配置 + 'login_pushreport_url': login_pushreport_url, + 'login_data': login_data, + 'upload_url': upload_url, + 'upload_warning_url': upload_warning_url, + 'warning_data': warning_data, + + # 查询接口 + 'query_data_list_item_nos_url': query_data_list_item_nos_url, + 'query_data_list_item_nos_data': query_data_list_item_nos_data, + + # 上传数据项 + 'push_data_value_list_url': push_data_value_list_url, + 'push_data_value_list_data': push_data_value_list_data, + + # eta 配置 + 'APPID': APPID, + 'SECRET': SECRET, + 'etadata': data, + 'edbcodelist': edbcodelist, + 'ClassifyId': ClassifyId, + 'edbcodedataurl': edbcodedataurl, + 'classifyidlisturl': classifyidlisturl, + 'edbdatapushurl': edbdatapushurl, + 'edbdeleteurl': edbdeleteurl, + 'edbbusinessurl': edbbusinessurl, + 'ClassifyId': ClassifyId, + 'classifylisturl': classifylisturl, + + # 数据库配置 + 'sqlitedb': sqlitedb, +}) + + +def push_market_value(): + logger.info('发送预测结果到市场信息平台') + # 读取预测数据和模型评估数据 + predict_file_path = os.path.join(config.dataset, 'predict.csv') + model_eval_file_path = os.path.join(config.dataset, 'model_evaluation.csv') + try: + predictdata_df = pd.read_csv(predict_file_path) + top_models_df = pd.read_csv(model_eval_file_path) + except FileNotFoundError as e: + logger.error(f"文件未找到: {e}") + return + + predictdata = predictdata_df.copy() + + # 取模型前十 + top_models = top_models_df['模型(Model)'].head(10).tolist() + + # 计算前十模型的均值 + predictdata_df['top_models_mean'] = predictdata_df[top_models].mean(axis=1) + + # 打印日期和前十模型均值 + print(predictdata_df[['ds', 'top_models_mean']]) + + # 准备要推送的数据 + first_mean = predictdata_df['top_models_mean'].iloc[0] + last_mean = predictdata_df['top_models_mean'].iloc[-1] + # 保留两位小数 + first_mean = round(first_mean, 2) + last_mean = round(last_mean, 2) + + predictdata = [ + { + "dataItemNo": global_config['bdwd_items']['ciri'], + "dataDate": global_config['end_time'].replace('-', ''), + "dataStatus": "add", + "dataValue": first_mean + }, + { + "dataItemNo": global_config['bdwd_items']['benzhou'], + "dataDate": global_config['end_time'].replace('-', ''), + "dataStatus": "add", + "dataValue": last_mean + } + ] + + print(predictdata) + + # 推送数据到市场信息平台 + try: + push_market_data(predictdata) + except Exception as e: + logger.error(f"推送数据失败: {e}") + + +def predict_main(): + """ + 主预测函数,用于从 ETA 获取数据、处理数据、训练模型并进行预测。 + + 参数: + signature (BinanceAPI): Binance API 实例。 + etadata (EtaReader): ETA 数据读取器实例。 + is_eta (bool): 是否从 ETA 获取数据。 + data_set (str): 数据集名称。 + dataset (str): 数据集路径。 + add_kdj (bool): 是否添加 KDJ 指标。 + is_timefurture (bool): 是否添加时间衍生特征。 + end_time (str): 结束时间。 + is_edbnamelist (bool): 是否使用 EDB 名称列表。 + edbnamelist (list): EDB 名称列表。 + y (str): 预测目标列名。 + sqlitedb (SQLiteDB): SQLite 数据库实例。 + is_corr (bool): 是否进行相关性分析。 + horizon (int): 预测时域。 + input_size (int): 输入数据大小。 + train_steps (int): 训练步数。 + val_check_steps (int): 验证检查步数。 + early_stop_patience_steps (int): 早停耐心步数。 + is_debug (bool): 是否调试模式。 + dataset (str): 数据集名称。 + is_train (bool): 是否训练模型。 + is_fivemodels (bool): 是否使用五个模型。 + val_size (float): 验证集大小。 + test_size (float): 测试集大小。 + settings (dict): 模型设置。 + now (str): 当前时间。 + etadata (EtaReader): ETA 数据读取器实例。 + modelsindex (list): 模型索引列表。 + data (str): 数据类型。 + is_eta (bool): 是否从 ETA 获取数据。 + + 返回: + None + """ + end_time = global_config['end_time'] + # 获取数据 + if is_eta: + logger.info('从eta获取数据...') + signature = BinanceAPI(APPID, SECRET) + etadata = EtaReader(signature=signature, + classifylisturl=global_config['classifylisturl'], + classifyidlisturl=global_config['classifyidlisturl'], + edbcodedataurl=global_config['edbcodedataurl'], + edbcodelist=global_config['edbcodelist'], + edbdatapushurl=global_config['edbdatapushurl'], + edbdeleteurl=global_config['edbdeleteurl'], + edbbusinessurl=global_config['edbbusinessurl'], + classifyId=global_config['ClassifyId'], + ) + df_zhibiaoshuju, df_zhibiaoliebiao = etadata.get_eta_api_shiyoujiao_lvyong_data( + data_set=data_set, dataset=dataset) # 原始数据,未处理 + + if is_market: + logger.info('从市场信息平台获取数据...') + try: + # 如果是测试环境,最高价最低价取excel文档 + if server_host == '192.168.100.53': + logger.info('从excel文档获取最高价最低价') + df_zhibiaoshuju = get_high_low_data(df_zhibiaoshuju) + else: + logger.info('从市场信息平台获取数据') + df_zhibiaoshuju = get_market_data( + end_time, df_zhibiaoshuju) + + except: + logger.info('最高最低价拼接失败') + + # 保存到xlsx文件的sheet表 + with pd.ExcelWriter(os.path.join(dataset, data_set)) as file: + df_zhibiaoshuju.to_excel(file, sheet_name='指标数据', index=False) + df_zhibiaoliebiao.to_excel(file, sheet_name='指标列表', index=False) + + # 数据处理 + df = datachuli(df_zhibiaoshuju, df_zhibiaoliebiao, y=global_config['y'], dataset=dataset, add_kdj=add_kdj, is_timefurture=is_timefurture, + end_time=end_time) + + else: + # 读取数据 + logger.info('读取本地数据:' + os.path.join(dataset, data_set)) + df, df_zhibiaoliebiao = getdata(filename=os.path.join(dataset, data_set), y=y, dataset=dataset, add_kdj=add_kdj, + is_timefurture=is_timefurture, end_time=end_time) # 原始数据,未处理 + + # 更改预测列名称 + df.rename(columns={y: 'y'}, inplace=True) + + if is_edbnamelist: + df = df[edbnamelist] + df.to_csv(os.path.join(dataset, '指标数据.csv'), index=False) + # 保存最新日期的y值到数据库 + # 取第一行数据存储到数据库中 + first_row = df[['ds', 'y']].tail(1) + # 判断y的类型是否为float + if not isinstance(first_row['y'].values[0], float): + logger.info(f'{end_time}预测目标数据为空,跳过') + return None + + # 将最新真实值保存到数据库 + if not sqlitedb.check_table_exists('trueandpredict'): + first_row.to_sql('trueandpredict', sqlitedb.connection, index=False) + else: + for row in first_row.itertuples(index=False): + row_dict = row._asdict() + config.logger.info(f'要保存的真实值:{row_dict}') + # 判断ds是否为字符串类型,如果不是则转换为字符串类型 + if isinstance(row_dict['ds'], (pd.Timestamp, datetime.datetime)): + row_dict['ds'] = row_dict['ds'].strftime('%Y-%m-%d') + elif not isinstance(row_dict['ds'], str): + try: + row_dict['ds'] = pd.to_datetime( + row_dict['ds']).strftime('%Y-%m-%d') + except: + logger.warning(f"无法解析的时间格式: {row_dict['ds']}") + # row_dict['ds'] = row_dict['ds'].strftime('%Y-%m-%d') + # row_dict['ds'] = row_dict['ds'].strftime('%Y-%m-%d %H:%M:%S') + check_query = sqlitedb.select_data( + 'trueandpredict', where_condition=f"ds = '{row.ds}'") + if len(check_query) > 0: + set_clause = ", ".join( + [f"{key} = '{value}'" for key, value in row_dict.items()]) + sqlitedb.update_data( + 'trueandpredict', set_clause, where_condition=f"ds = '{row.ds}'") + continue + sqlitedb.insert_data('trueandpredict', tuple( + row_dict.values()), columns=row_dict.keys()) + + # 更新accuracy表的y值 + if not sqlitedb.check_table_exists('accuracy'): + pass + else: + update_y = sqlitedb.select_data( + 'accuracy', where_condition="y is null") + if len(update_y) > 0: + logger.info('更新accuracy表的y值') + # 找到update_y 中ds且df中的y的行 + update_y = update_y[update_y['ds'] <= end_time] + logger.info(f'要更新y的信息:{update_y}') + # try: + for row in update_y.itertuples(index=False): + try: + row_dict = row._asdict() + yy = df[df['ds'] == row_dict['ds']]['y'].values[0] + LOW = df[df['ds'] == row_dict['ds']]['Brentzdj'].values[0] + HIGH = df[df['ds'] == row_dict['ds']]['Brentzgj'].values[0] + sqlitedb.update_data( + 'accuracy', f"y = {yy},LOW_PRICE = {LOW},HIGH_PRICE = {HIGH}", where_condition=f"ds = '{row_dict['ds']}'") + except: + logger.info(f'更新accuracy表的y值失败:{row_dict}') + # except Exception as e: + # logger.info(f'更新accuracy表的y值失败:{e}') + + # 判断当前日期是不是周一 + is_weekday = datetime.datetime.now().weekday() == 0 + if is_weekday: + logger.info('今天是周一,更新预测模型') + # 计算最近60天预测残差最低的模型名称 + model_results = sqlitedb.select_data( + 'trueandpredict', order_by="ds DESC", limit="60") + # 删除空值率为90%以上的列 + if len(model_results) > 10: + model_results = model_results.dropna( + thresh=len(model_results)*0.1, axis=1) + # 删除空行 + model_results = model_results.dropna() + modelnames = model_results.columns.to_list()[2:-1] + for col in model_results[modelnames].select_dtypes(include=['object']).columns: + model_results[col] = model_results[col].astype(np.float32) + # 计算每个预测值与真实值之间的偏差率 + for model in modelnames: + model_results[f'{model}_abs_error_rate'] = abs( + model_results['y'] - model_results[model]) / model_results['y'] + # 获取每行对应的最小偏差率值 + min_abs_error_rate_values = model_results.apply( + lambda row: row[[f'{model}_abs_error_rate' for model in modelnames]].min(), axis=1) + # 获取每行对应的最小偏差率值对应的列名 + min_abs_error_rate_column_name = model_results.apply( + lambda row: row[[f'{model}_abs_error_rate' for model in modelnames]].idxmin(), axis=1) + # 将列名索引转换为列名 + min_abs_error_rate_column_name = min_abs_error_rate_column_name.map( + lambda x: x.split('_')[0]) + # 取出现次数最多的模型名称 + most_common_model = min_abs_error_rate_column_name.value_counts().idxmax() + logger.info(f"最近60天预测残差最低的模型名称:{most_common_model}") + # 保存结果到数据库 + if not sqlitedb.check_table_exists('most_model'): + sqlitedb.create_table( + 'most_model', columns="ds datetime, most_common_model TEXT") + sqlitedb.insert_data('most_model', (datetime.datetime.now().strftime( + '%Y-%m-%d %H:%M:%S'), most_common_model,), columns=('ds', 'most_common_model',)) + + try: + if is_weekday: + # if True: + logger.info('今天是周一,发送特征预警') + # 上传预警信息到数据库 + warning_data_df = df_zhibiaoliebiao.copy() + warning_data_df = warning_data_df[warning_data_df['停更周期'] > 3][[ + '指标名称', '指标id', '频度', '更新周期', '指标来源', '最后更新时间', '停更周期']] + # 重命名列名 + warning_data_df = warning_data_df.rename(columns={'指标名称': 'INDICATOR_NAME', '指标id': 'INDICATOR_ID', '频度': 'FREQUENCY', + '更新周期': 'UPDATE_FREQUENCY', '指标来源': 'DATA_SOURCE', '最后更新时间': 'LAST_UPDATE_DATE', '停更周期': 'UPDATE_SUSPENSION_CYCLE'}) + from sqlalchemy import create_engine + import urllib + global password + if '@' in password: + password = urllib.parse.quote_plus(password) + + engine = create_engine( + f'mysql+pymysql://{dbusername}:{password}@{host}:{port}/{dbname}') + warning_data_df['WARNING_DATE'] = datetime.date.today().strftime( + "%Y-%m-%d %H:%M:%S") + warning_data_df['TENANT_CODE'] = 'T0004' + # 插入数据之前查询表数据然后新增id列 + existing_data = pd.read_sql(f"SELECT * FROM {table_name}", engine) + if not existing_data.empty: + max_id = existing_data['ID'].astype(int).max() + warning_data_df['ID'] = range( + max_id + 1, max_id + 1 + len(warning_data_df)) + else: + warning_data_df['ID'] = range(1, 1 + len(warning_data_df)) + warning_data_df.to_sql( + table_name, con=engine, if_exists='append', index=False) + if is_update_warning_data: + upload_warning_info(len(warning_data_df)) + except: + logger.info('上传预警信息到数据库失败') + + if is_corr: + df = corr_feature(df=df) + + df1 = df.copy() # 备份一下,后面特征筛选完之后加入ds y 列用 + logger.info(f"开始训练模型...") + row, col = df.shape + + now = datetime.datetime.now().strftime('%Y%m%d%H%M%S') + ex_Model(df, + horizon=global_config['horizon'], + input_size=global_config['input_size'], + train_steps=global_config['train_steps'], + val_check_steps=global_config['val_check_steps'], + early_stop_patience_steps=global_config['early_stop_patience_steps'], + is_debug=global_config['is_debug'], + dataset=global_config['dataset'], + is_train=global_config['is_train'], + is_fivemodels=global_config['is_fivemodels'], + val_size=global_config['val_size'], + test_size=global_config['test_size'], + settings=global_config['settings'], + now=now, + etadata=global_config['etadata'], + modelsindex=global_config['modelsindex'], + data=data, + is_eta=global_config['is_eta'], + end_time=global_config['end_time'], + ) + + logger.info('模型训练完成') + + logger.info('训练数据绘图ing') + model_results3 = model_losss(sqlitedb, end_time=end_time) + logger.info('训练数据绘图end') + + # 模型报告 + logger.info('制作报告ing') + title = f'{settings}--{end_time}-预测报告' # 报告标题 + reportname = f'Brent原油大模型日度预测--{end_time}.pdf' # 报告文件名 + reportname = reportname.replace(':', '-') # 替换冒号 + brent_export_pdf(dataset=dataset, num_models=5 if is_fivemodels else 22, time=end_time, + reportname=reportname, sqlitedb=sqlitedb), + + logger.info('制作报告end') + logger.info('模型训练完成') + + push_market_value() + + # # LSTM 单变量模型 + # ex_Lstm(df,input_seq_len=input_size,output_seq_len=horizon,is_debug=is_debug,dataset=dataset) + + # # lstm 多变量模型 + # ex_Lstm_M(df,n_days=input_size,out_days=horizon,is_debug=is_debug,datasetpath=dataset) + + # # GRU 模型 + # # ex_GRU(df) + + # 发送邮件 + # m = SendMail( + # username=username, + # passwd=passwd, + # recv=recv, + # title=title, + # content=content, + # file=max(glob.glob(os.path.join(dataset,'*.pdf')), key=os.path.getctime), + # ssl=ssl, + # ) + # m.send_mail() + + +if __name__ == '__main__': + # global end_time + # # 遍历2024-11-25 到 2024-12-3 之间的工作日日期 + # for i_time in pd.date_range('2024-12-1', '2025-2-26', freq='W'): + # end_time = i_time.strftime('%Y-%m-%d') + # predict_main() + + predict_main() diff --git a/main_shiyoujiao.py b/main_shiyoujiao_puhuo.py similarity index 57% rename from main_shiyoujiao.py rename to main_shiyoujiao_puhuo.py index c706c9f..2d94e58 100644 --- a/main_shiyoujiao.py +++ b/main_shiyoujiao_puhuo.py @@ -1,12 +1,80 @@ # 读取配置 -from lib.dataread import * -from lib.tools import SendMail,exception_logger -from models.nerulforcastmodels import ex_Model_Juxiting,model_losss,model_losss_juxiting,brent_export_pdf,tansuanli_export_pdf,pp_export_pdf,model_losss_juxiting -import glob +from lib.dataread import * +from config_shiyoujiao_puhuo import * +from lib.tools import SendMail, exception_logger +from models.nerulforcastmodels import ex_Model, model_losss_juxiting, tansuanli_export_pdf, pp_export_pdf +import datetime import torch torch.set_float32_matmul_precision("high") +global_config.update({ + # 核心参数 + 'logger': logger, + 'dataset': dataset, + 'y': y, + 'offsite_col': offsite_col, + 'avg_cols': avg_cols, + 'offsite': offsite, + 'edbcodenamedict': edbcodenamedict, + 'is_debug': is_debug, + 'is_train': is_train, + 'is_fivemodels': is_fivemodels, + 'settings': settings, + + + # 模型参数 + 'data_set': data_set, + 'input_size': input_size, + 'horizon': horizon, + 'train_steps': train_steps, + 'val_check_steps': val_check_steps, + 'val_size': val_size, + 'test_size': test_size, + 'modelsindex': modelsindex, + 'rote': rote, + + # 特征工程开关 + 'is_del_corr': is_del_corr, + 'is_del_tow_month': is_del_tow_month, + 'is_eta': is_eta, + 'is_update_eta': is_update_eta, + 'is_fivemodels': is_fivemodels, + 'early_stop_patience_steps': early_stop_patience_steps, + + # 时间参数 + 'start_year': start_year, + 'end_time': end_time or datetime.datetime.now().strftime("%Y-%m-%d"), + 'freq': freq, # 保持列表结构 + + # 接口配置 + 'login_pushreport_url': login_pushreport_url, + 'login_data': login_data, + 'upload_url': upload_url, + 'upload_warning_url': upload_warning_url, + 'warning_data': warning_data, + + # 查询接口 + 'query_data_list_item_nos_url': query_data_list_item_nos_url, + 'query_data_list_item_nos_data': query_data_list_item_nos_data, + + # eta 配置 + 'APPID': APPID, + 'SECRET': SECRET, + 'etadata': data, + 'edbcodelist': edbcodelist, + 'ClassifyId': ClassifyId, + 'edbcodedataurl': edbcodedataurl, + 'classifyidlisturl': classifyidlisturl, + 'edbdatapushurl': edbdatapushurl, + 'edbdeleteurl': edbdeleteurl, + 'edbbusinessurl': edbbusinessurl, + 'ClassifyId': ClassifyId, + 'classifylisturl': classifylisturl, + + # 数据库配置 + 'sqlitedb': sqlitedb, +}) def predict_main(): @@ -72,7 +140,8 @@ def predict_main(): edbdeleteurl=edbdeleteurl, edbbusinessurl=edbbusinessurl, ) - df_zhibiaoshuju, df_zhibiaoliebiao = etadata.get_eta_api_shiyoujiao_data(data_set=data_set, dataset=dataset) # 原始数据,未处理 + df_zhibiaoshuju, df_zhibiaoliebiao = etadata.get_eta_api_shiyoujiao_data( + data_set=data_set, dataset=dataset) # 原始数据,未处理 if is_market: logger.info('从市场信息平台获取数据...') @@ -83,26 +152,26 @@ def predict_main(): df_zhibiaoshuju = get_high_low_data(df_zhibiaoshuju) else: logger.info('从市场信息平台获取数据') - df_zhibiaoshuju = get_market_data(end_time,df_zhibiaoshuju) - - except : + df_zhibiaoshuju = get_market_data( + end_time, df_zhibiaoshuju) + + except: logger.info('最高最低价拼接失败') - + # 保存到xlsx文件的sheet表 - with pd.ExcelWriter(os.path.join(dataset,data_set)) as file: + with pd.ExcelWriter(os.path.join(dataset, data_set)) as file: df_zhibiaoshuju.to_excel(file, sheet_name='指标数据', index=False) df_zhibiaoliebiao.to_excel(file, sheet_name='指标列表', index=False) - - + # 数据处理 df = datachuli_juxiting(df_zhibiaoshuju, df_zhibiaoliebiao, y=y, dataset=dataset, add_kdj=add_kdj, is_timefurture=is_timefurture, - end_time=end_time) + end_time=end_time) else: # 读取数据 logger.info('读取本地数据:' + os.path.join(dataset, data_set)) - df,df_zhibiaoliebiao = getdata_juxiting(filename=os.path.join(dataset, data_set), y=y, dataset=dataset, add_kdj=add_kdj, - is_timefurture=is_timefurture, end_time=end_time) # 原始数据,未处理 + df, df_zhibiaoliebiao = getdata_juxiting(filename=os.path.join(dataset, data_set), y=y, dataset=dataset, add_kdj=add_kdj, + is_timefurture=is_timefurture, end_time=end_time) # 原始数据,未处理 # 更改预测列名称 df.rename(columns={y: 'y'}, inplace=True) @@ -125,31 +194,37 @@ def predict_main(): for row in first_row.itertuples(index=False): row_dict = row._asdict() row_dict['ds'] = row_dict['ds'].strftime('%Y-%m-%d %H:%M:%S') - check_query = sqlitedb.select_data('trueandpredict', where_condition=f"ds = '{row.ds}'") + check_query = sqlitedb.select_data( + 'trueandpredict', where_condition=f"ds = '{row.ds}'") if len(check_query) > 0: - set_clause = ", ".join([f"{key} = '{value}'" for key, value in row_dict.items()]) - sqlitedb.update_data('trueandpredict', set_clause, where_condition=f"ds = '{row.ds}'") + set_clause = ", ".join( + [f"{key} = '{value}'" for key, value in row_dict.items()]) + sqlitedb.update_data( + 'trueandpredict', set_clause, where_condition=f"ds = '{row.ds}'") continue - sqlitedb.insert_data('trueandpredict', tuple(row_dict.values()), columns=row_dict.keys()) + sqlitedb.insert_data('trueandpredict', tuple( + row_dict.values()), columns=row_dict.keys()) # 更新accuracy表的y值 if not sqlitedb.check_table_exists('accuracy'): pass else: - update_y = sqlitedb.select_data('accuracy',where_condition="y is null") + update_y = sqlitedb.select_data( + 'accuracy', where_condition="y is null") if len(update_y) > 0: logger.info('更新accuracy表的y值') # 找到update_y 中ds且df中的y的行 - update_y = update_y[update_y['ds']<=end_time] + update_y = update_y[update_y['ds'] <= end_time] logger.info(f'要更新y的信息:{update_y}') # try: for row in update_y.itertuples(index=False): try: - row_dict = row._asdict() - yy = df[df['ds']==row_dict['ds']]['y'].values[0] - LOW = df[df['ds']==row_dict['ds']]['Brentzdj'].values[0] - HIGH = df[df['ds']==row_dict['ds']]['Brentzgj'].values[0] - sqlitedb.update_data('accuracy', f"y = {yy},LOW_PRICE = {LOW},HIGH_PRICE = {HIGH}", where_condition=f"ds = '{row_dict['ds']}'") + row_dict = row._asdict() + yy = df[df['ds'] == row_dict['ds']]['y'].values[0] + LOW = df[df['ds'] == row_dict['ds']]['Brentzdj'].values[0] + HIGH = df[df['ds'] == row_dict['ds']]['Brentzgj'].values[0] + sqlitedb.update_data( + 'accuracy', f"y = {yy},LOW_PRICE = {LOW},HIGH_PRICE = {HIGH}", where_condition=f"ds = '{row_dict['ds']}'") except: logger.info(f'更新accuracy表的y值失败:{row_dict}') # except Exception as e: @@ -161,10 +236,12 @@ def predict_main(): if is_weekday: logger.info('今天是周一,更新预测模型') # 计算最近60天预测残差最低的模型名称 - model_results = sqlitedb.select_data('trueandpredict', order_by="ds DESC", limit="60") + model_results = sqlitedb.select_data( + 'trueandpredict', order_by="ds DESC", limit="60") # 删除空值率为90%以上的列 if len(model_results) > 10: - model_results = model_results.dropna(thresh=len(model_results)*0.1,axis=1) + model_results = model_results.dropna( + thresh=len(model_results)*0.1, axis=1) # 删除空行 model_results = model_results.dropna() modelnames = model_results.columns.to_list()[2:-1] @@ -172,47 +249,59 @@ def predict_main(): model_results[col] = model_results[col].astype(np.float32) # 计算每个预测值与真实值之间的偏差率 for model in modelnames: - model_results[f'{model}_abs_error_rate'] = abs(model_results['y'] - model_results[model]) / model_results['y'] + model_results[f'{model}_abs_error_rate'] = abs( + model_results['y'] - model_results[model]) / model_results['y'] # 获取每行对应的最小偏差率值 - min_abs_error_rate_values = model_results.apply(lambda row: row[[f'{model}_abs_error_rate' for model in modelnames]].min(), axis=1) + min_abs_error_rate_values = model_results.apply( + lambda row: row[[f'{model}_abs_error_rate' for model in modelnames]].min(), axis=1) # 获取每行对应的最小偏差率值对应的列名 - min_abs_error_rate_column_name = model_results.apply(lambda row: row[[f'{model}_abs_error_rate' for model in modelnames]].idxmin(), axis=1) + min_abs_error_rate_column_name = model_results.apply( + lambda row: row[[f'{model}_abs_error_rate' for model in modelnames]].idxmin(), axis=1) # 将列名索引转换为列名 - min_abs_error_rate_column_name = min_abs_error_rate_column_name.map(lambda x: x.split('_')[0]) + min_abs_error_rate_column_name = min_abs_error_rate_column_name.map( + lambda x: x.split('_')[0]) # 取出现次数最多的模型名称 most_common_model = min_abs_error_rate_column_name.value_counts().idxmax() logger.info(f"最近60天预测残差最低的模型名称:{most_common_model}") # 保存结果到数据库 if not sqlitedb.check_table_exists('most_model'): - sqlitedb.create_table('most_model', columns="ds datetime, most_common_model TEXT") - sqlitedb.insert_data('most_model', (datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'), most_common_model,), columns=('ds', 'most_common_model',)) + sqlitedb.create_table( + 'most_model', columns="ds datetime, most_common_model TEXT") + sqlitedb.insert_data('most_model', (datetime.datetime.now().strftime( + '%Y-%m-%d %H:%M:%S'), most_common_model,), columns=('ds', 'most_common_model',)) try: if is_weekday: - # if True: + # if True: logger.info('今天是周一,发送特征预警') # 上传预警信息到数据库 warning_data_df = df_zhibiaoliebiao.copy() - warning_data_df = warning_data_df[warning_data_df['停更周期']> 3 ][['指标名称', '指标id', '频度','更新周期','指标来源','最后更新时间','停更周期']] + warning_data_df = warning_data_df[warning_data_df['停更周期'] > 3][[ + '指标名称', '指标id', '频度', '更新周期', '指标来源', '最后更新时间', '停更周期']] # 重命名列名 - warning_data_df = warning_data_df.rename(columns={'指标名称': 'INDICATOR_NAME', '指标id': 'INDICATOR_ID', '频度': 'FREQUENCY', '更新周期': 'UPDATE_FREQUENCY', '指标来源': 'DATA_SOURCE', '最后更新时间': 'LAST_UPDATE_DATE', '停更周期': 'UPDATE_SUSPENSION_CYCLE'}) + warning_data_df = warning_data_df.rename(columns={'指标名称': 'INDICATOR_NAME', '指标id': 'INDICATOR_ID', '频度': 'FREQUENCY', + '更新周期': 'UPDATE_FREQUENCY', '指标来源': 'DATA_SOURCE', '最后更新时间': 'LAST_UPDATE_DATE', '停更周期': 'UPDATE_SUSPENSION_CYCLE'}) from sqlalchemy import create_engine import urllib global password if '@' in password: password = urllib.parse.quote_plus(password) - engine = create_engine(f'mysql+pymysql://{dbusername}:{password}@{host}:{port}/{dbname}') - warning_data_df['WARNING_DATE'] = datetime.date.today().strftime("%Y-%m-%d %H:%M:%S") - warning_data_df['TENANT_CODE'] = 'T0004' + engine = create_engine( + f'mysql+pymysql://{dbusername}:{password}@{host}:{port}/{dbname}') + warning_data_df['WARNING_DATE'] = datetime.date.today().strftime( + "%Y-%m-%d %H:%M:%S") + warning_data_df['TENANT_CODE'] = 'T0004' # 插入数据之前查询表数据然后新增id列 existing_data = pd.read_sql(f"SELECT * FROM {table_name}", engine) if not existing_data.empty: max_id = existing_data['ID'].astype(int).max() - warning_data_df['ID'] = range(max_id + 1, max_id + 1 + len(warning_data_df)) + warning_data_df['ID'] = range( + max_id + 1, max_id + 1 + len(warning_data_df)) else: warning_data_df['ID'] = range(1, 1 + len(warning_data_df)) - warning_data_df.to_sql(table_name, con=engine, if_exists='append', index=False) + warning_data_df.to_sql( + table_name, con=engine, if_exists='append', index=False) if is_update_warning_data: upload_warning_info(len(warning_data_df)) except: @@ -227,50 +316,49 @@ def predict_main(): now = datetime.datetime.now().strftime('%Y%m%d%H%M%S') ex_Model_Juxiting(df, - horizon=horizon, - input_size=input_size, - train_steps=train_steps, - val_check_steps=val_check_steps, - early_stop_patience_steps=early_stop_patience_steps, - is_debug=is_debug, - dataset=dataset, - is_train=is_train, - is_fivemodels=is_fivemodels, - val_size=val_size, - test_size=test_size, - settings=settings, - now=now, - etadata=etadata, - modelsindex=modelsindex, - data=data, - is_eta=is_eta, - end_time=end_time, - ) - + horizon=horizon, + input_size=input_size, + train_steps=train_steps, + val_check_steps=val_check_steps, + early_stop_patience_steps=early_stop_patience_steps, + is_debug=is_debug, + dataset=dataset, + is_train=is_train, + is_fivemodels=is_fivemodels, + val_size=val_size, + test_size=test_size, + settings=settings, + now=now, + etadata=etadata, + modelsindex=modelsindex, + data=data, + is_eta=is_eta, + end_time=end_time, + ) logger.info('模型训练完成') - + logger.info('训练数据绘图ing') model_results3 = model_losss_juxiting(sqlitedb) logger.info('训练数据绘图end') - + # 模型报告 logger.info('制作报告ing') - title = f'{settings}--{end_time}-预测报告' # 报告标题 - reportname = f'PP大模型预测报告--{end_time}.pdf' # 报告文件名 - reportname = reportname.replace(':', '-') # 替换冒号 - pp_export_pdf(dataset=dataset,num_models = 5 if is_fivemodels else 22,time=end_time, - reportname=reportname,sqlitedb=sqlitedb), + title = f'{settings}--{end_time}-预测报告' # 报告标题 + reportname = f'PP大模型预测报告--{end_time}.pdf' # 报告文件名 + reportname = reportname.replace(':', '-') # 替换冒号 + pp_export_pdf(dataset=dataset, num_models=5 if is_fivemodels else 22, time=end_time, + reportname=reportname, sqlitedb=sqlitedb), logger.info('制作报告end') logger.info('模型训练完成') # # LSTM 单变量模型 # ex_Lstm(df,input_seq_len=input_size,output_seq_len=horizon,is_debug=is_debug,dataset=dataset) - + # # lstm 多变量模型 # ex_Lstm_M(df,n_days=input_size,out_days=horizon,is_debug=is_debug,datasetpath=dataset) - + # # GRU 模型 # # ex_GRU(df) @@ -281,10 +369,11 @@ def predict_main(): recv=recv, title=title, content=content, - file=max(glob.glob(os.path.join(dataset,'*.pdf')), key=os.path.getctime), + file=max(glob.glob(os.path.join(dataset, '*.pdf')), + key=os.path.getctime), ssl=ssl, ) - # m.send_mail() + # m.send_mail() if __name__ == '__main__': @@ -298,4 +387,4 @@ if __name__ == '__main__': # except: # pass - predict_main() \ No newline at end of file + predict_main()