fiewolf1000 commited on
Commit
352c373
·
verified ·
1 Parent(s): 4a918c0

Update update_predictions.py

Browse files
Files changed (1) hide show
  1. update_predictions.py +32 -147
update_predictions.py CHANGED
@@ -28,19 +28,21 @@ Config = {
28
  "PREDICTION_CACHE": os.path.join("/tmp", "predictions_cache"),
29
  "CHART_PATH": os.path.join("/tmp", "prediction_chart.png"),
30
  "HTML_PATH": os.path.join("/tmp", "index.html"),
31
- # 核心修改:用“最后推理业务日”替代原IS_TODAY_INFERENCED(记录具体日期而非布尔值)
32
  "LAST_INFERENCED_BUSINESS_DATE": None,
 
 
 
 
33
  "CACHED_RESULTS": {
34
  "close_preds": None,
35
  "volume_preds": None,
36
  "v_close_preds": None,
37
- "upside_prob": None,
38
- "vol_amp_prob": None,
39
  "hist_df_for_plot": None
40
  }
41
  }
42
 
43
- # 补充定义中文字体路径(此时Config已完全定义)
44
  Config["CHINESE_FONT_PATH"] = os.path.join(Config["REPO_PATH"], "fonts", "wqy-microhei.ttf")
45
 
46
  # 创建必要目录
@@ -54,7 +56,6 @@ def get_china_time():
54
  return datetime.now(china_tz)
55
 
56
 
57
- # -------------------------- 新增:业务日判断函数(核心修改) --------------------------
58
  def get_business_info():
59
  """
60
  基于北京时间20点分界,返回当前业务信息
@@ -96,11 +97,10 @@ def load_local_model():
96
  return predictor
97
 
98
 
99
- # -------------------------- 修改:数据获取日期(基于业务日) --------------------------
100
  def fetch_stock_data():
101
  """获取股票数据(基于业务日更新,中国时间),添加数据获取日志"""
102
  china_now = get_china_time()
103
- current_business_date, _ = get_business_info() # 核心修改:用业务日作为数据结束日期
104
  end_date = current_business_date.strftime("%Y-%m-%d")
105
  need_points = Config["VOL_WINDOW"] + Config["VOL_WINDOW"] # 历史数据+波动率计算窗口
106
 
@@ -184,7 +184,7 @@ def make_prediction(df, predictor):
184
  infer_time = time.time() - begin_time
185
  print(f"[{get_china_time():%Y-%m-%d %H:%M:%S}] 推理完成,耗时{infer_time:.2f}秒")
186
 
187
- # 波动率预测复用收盘价预测结果(保持原逻辑)
188
  close_preds_volatility = close_preds_main
189
  return close_preds_main, volume_preds_main, close_preds_volatility
190
 
@@ -222,41 +222,36 @@ def create_plot():
222
  china_now = get_china_time()
223
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 开始生成预测图表(适配低版本matplotlib字体)")
224
 
225
- # 从缓存获取数据(原有逻辑不变)
226
  hist_df_for_plot = Config["CACHED_RESULTS"]["hist_df_for_plot"]
227
  close_preds = Config["CACHED_RESULTS"]["close_preds"]
228
  volume_preds = Config["CACHED_RESULTS"]["volume_preds"]
229
 
230
- # -------------------------- 新增:创建画布和子图 --------------------------
231
  fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8), sharex=True)
232
- # -----------------------------------------------------------------------------
233
 
234
- # -------------------------- 修正:低版本matplotlib字体处理 --------------------------
235
  from matplotlib.font_manager import FontProperties
236
  font_path = Config["CHINESE_FONT_PATH"]
237
 
238
  # 检查字体文件是否存在
239
  if os.path.exists(font_path):
240
- # 直接通过FontProperties指定字体文件路径(兼容低版本matplotlib)
241
  chinese_font = FontProperties(fname=font_path)
242
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 成功加载.ttf字体:{font_path}")
243
  else:
244
- # 字体文件不存在时的 fallback 逻辑
245
  chinese_font = FontProperties(family='SimHei', size=10)
246
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 字体文件不存在,使用系统默认字体:SimHei")
247
 
248
- # 全局设置字体(确保坐标轴刻度等默认文本也能显示中文)
249
  plt.rcParams["font.family"] = ["sans-serif"]
250
  plt.rcParams["font.sans-serif"] = ["WenQuanYi Micro Hei", "SimHei", "Heiti TC"]
251
  plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
252
- # -----------------------------------------------------------------------------
253
 
254
- # 绘图时,为所有中文文本显式指定字体(关键)
255
- # 1. 价格子图
256
  hist_time = hist_df_for_plot['timestamps']
257
  ax1.plot(hist_time, hist_df_for_plot['close'], color='#00274C', linewidth=1.5)
258
  mean_preds = close_preds.mean(axis=1)
259
- # 生成预测时间序列(假设预测是在历史最后一个时间之后的24个交易日)
260
  last_hist_time = hist_time.max()
261
  pred_time = pd.date_range(start=last_hist_time + pd.Timedelta(days=1), periods=Config["PRED_HORIZON"], freq='B')
262
  ax1.plot(pred_time, mean_preds, color='#FF6B00', linestyle='-')
@@ -266,12 +261,11 @@ def create_plot():
266
  ax1.set_title(f'{Config["STOCK_CODE"]} 上证指数概率预测(未来{Config["PRED_HORIZON"]}个交易日)',
267
  fontsize=16, weight='bold', fontproperties=chinese_font)
268
  ax1.set_ylabel('价格(元)', fontsize=12, fontproperties=chinese_font)
269
- # 图例指定字体
270
  ax1.legend(['上证指数(后复权)', '预测均价', '预测区间(最小-最大)'],
271
  fontsize=10, prop=chinese_font)
272
  ax1.grid(True, which='both', linestyle='--', linewidth=0.5)
273
 
274
- # 2. 成交量子图(同理指定字体)
275
  ax2.bar(hist_time, hist_df_for_plot['volume']/1e8, color='#00A86B', width=0.6)
276
  ax2.bar(pred_time, volume_preds.mean(axis=1)/1e8, color='#FF6B00', width=0.6)
277
  ax2.set_ylabel('成交量(亿手)', fontsize=12, fontproperties=chinese_font)
@@ -297,110 +291,6 @@ def create_plot():
297
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 图表生成完成,保存路径:{chart_path}")
298
 
299
 
300
- def update_html():
301
- """更新HTML页面,复用当前业务日缓存的指标,添加HTML更新日志"""
302
- china_now = get_china_time()
303
- current_business_date, _ = get_business_info()
304
- print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 开始更新HTML页面(业务日:{current_business_date})...")
305
-
306
- # 1. 从缓存获取指标(增加空值判断,避免报错)
307
- upside_prob = Config["CACHED_RESULTS"].get("upside_prob")
308
- vol_amp_prob = Config["CACHED_RESULTS"].get("vol_amp_prob")
309
-
310
- # 处理缓存为空的情况
311
- if upside_prob is None or vol_amp_prob is None:
312
- print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 警告:缓存中未找到指标数据,无法更新HTML")
313
- return
314
-
315
- # 格式化指标(保留1位小数百分比)
316
- upside_prob_str = f'{upside_prob:.1%}'
317
- vol_amp_prob_str = f'{vol_amp_prob:.1%}'
318
- now_cn_str = china_now.strftime('%Y-%m-%d %H:%M:%S')
319
-
320
- # 2. 初始化HTML(不存在则创建基础模板)
321
- html_path = Path(Config["HTML_PATH"])
322
- src_html_path = Config["REPO_PATH"] / "templates" / "index.html"
323
-
324
- if not html_path.exists():
325
- html_path.parent.mkdir(parents=True, exist_ok=True)
326
- if src_html_path.exists():
327
- # 复制项目模板
328
- import shutil
329
- shutil.copy2(src_html_path, html_path)
330
- print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 从项目模板复制HTML:{src_html_path} -> {html_path}")
331
- else:
332
- # 创建基础中文HTML(确保指标对应的id与正则匹配)
333
- base_html = """
334
- <!DOCTYPE html>
335
- <html>
336
- <head>
337
- <title>清华大模型Kronos上证指数预测</title>
338
- <style>
339
- body { max-width: 1200px; margin: 0 auto; padding: 20px; font-family: "WenQuanYi Micro Hei", Arial; }
340
- .metric { margin: 20px 0; padding: 10px; background: #f5f5f5; border-radius: 5px; }
341
- .metric-value { font-size: 1.2em; color: #0066cc; }
342
- img { max-width: 100%; height: auto; }
343
- h1 { color: #333; }
344
- </style>
345
- </head>
346
- <body>
347
- <h1>清华大学K线大模型Kronos上证指数(sh.000001)概率预测</h1>
348
- <p>最后更新时间(中国时间):<strong id="update-time">未更新</strong></p>
349
- <p>同 步 网 站:<strong><a href="http://15115656.top" target="_blank">火狼工具站</a></strong></p>
350
- <div class="metric">
351
- <p>24个交易日上涨概率:<span class="metric-value" id="upside-prob">--%</span></p>
352
- </div>
353
- <div class="metric">
354
- <p>波动率放大概率:<span class="metric-value" id="vol-amp-prob">--%</span></p>
355
- </div>
356
- <div><img src="/prediction_chart.png" alt="上证指数预测图表"></div>
357
- </body>
358
- </html>
359
- """
360
- with open(html_path, 'w', encoding='utf-8') as f:
361
- f.write(base_html)
362
- print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 在/tmp创建基础HTML:{html_path}")
363
-
364
- # 3. 读取HTML内容(确保读取成功)
365
- try:
366
- with open(html_path, 'r', encoding='utf-8') as f:
367
- content = f.read()
368
- except Exception as e:
369
- print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 读取HTML失败:{str(e)}")
370
- return
371
-
372
- # 4. 正则替换(关键:确保re.sub()参数完整)
373
- # 替换更新时间
374
- content = re.sub(
375
- pattern=r'(<strong id="update-time">).*?(</strong>)',
376
- repl=lambda m: f'{m.group(1)}{now_cn_str}{m.group(2)}',
377
- string=content
378
- )
379
- # 替换上涨概率(id="upside-prob",与HTML模板对应)
380
- content = re.sub(
381
- pattern=r'(<span class="metric-value" id="upside-prob">).*?(</span>)',
382
- repl=lambda m: f'{m.group(1)}{upside_prob_str}{m.group(2)}',
383
- string=content
384
- )
385
- # 替换波动率放大概率(id="vol-amp-prob",与HTML模板对应)
386
- content = re.sub(
387
- pattern=r'(<span class="metric-value" id="vol-amp-prob">).*?(</span>)',
388
- repl=lambda m: f'{m.group(1)}{vol_amp_prob_str}{m.group(2)}',
389
- string=content
390
- )
391
-
392
- # 5. 写入更新后的HTML
393
- try:
394
- with open(html_path, 'w', encoding='utf-8') as f:
395
- f.write(content)
396
- print(f"[{china_now:%Y-%m-%d %H:%M:%S}] HTML更新完成,路径:{html_path}")
397
- # 验证替换结果(调试用)
398
- print(f"[DEBUG] 上涨概率更新为:{upside_prob_str}")
399
- print(f"[DEBUG] 波动率概率更新为:{vol_amp_prob_str}")
400
- except Exception as e:
401
- print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 写入HTML失败:{str(e)}")
402
-
403
-
404
  def git_commit_and_push():
405
  """Git提交(仅当Git存在时执行),添加Git操作日志"""
406
  china_now = get_china_time()
@@ -418,7 +308,7 @@ def git_commit_and_push():
418
  # 执行Git操作
419
  try:
420
  os.chdir(Config["REPO_PATH"])
421
- # 复制图表和HTML到Git跟踪目录(若需要)
422
  chart_src = Config["CHART_PATH"]
423
  chart_dst = Config["REPO_PATH"] / "prediction_chart.png"
424
  html_src = Config["HTML_PATH"]
@@ -452,7 +342,6 @@ def git_commit_and_push():
452
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] Git权限错误:{str(e)},跳过Git操作")
453
 
454
 
455
- # -------------------------- 修改:主任务逻辑(基于业务日判断) --------------------------
456
  def main_task(model):
457
  """主任务:控制基于20点分界的业务日推理逻辑,同业务日复用缓存"""
458
  china_now = get_china_time()
@@ -461,13 +350,11 @@ def main_task(model):
461
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 开始执行主任务")
462
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 当前业务日:{current_business_date}(北京时间{'20点后' if is_after_20h else '20点前'})")
463
 
464
- # 核心修改:判断当前业务日是否已推理(而非自然日)
465
  if Config["LAST_INFERENCED_BUSINESS_DATE"] == current_business_date:
466
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 当前业务日({current_business_date})已完成推理,直接复用缓存结果")
467
- # 复用缓存生成图表和HTML
468
  create_plot()
469
- update_html()
470
- git_commit_and_push()
471
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 主任务完成(复用缓存)")
472
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] " + "="*60 + "\n")
473
  return
@@ -491,24 +378,25 @@ def main_task(model):
491
  "close_preds": close_preds,
492
  "volume_preds": volume_preds,
493
  "v_close_preds": v_close_preds,
494
- "upside_prob": upside_prob,
495
- "vol_amp_prob": vol_amp_prob,
496
  "hist_df_for_plot": hist_df_for_plot
497
  }
498
- # 核心修改:标记当前业务日已推理(而非布尔值)
 
 
 
 
 
 
499
  Config["LAST_INFERENCED_BUSINESS_DATE"] = current_business_date
500
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 业务日({current_business_date})推理结果已缓存,同业务日后续调用将复用")
501
 
502
  # 5. 生成图表
503
  create_plot()
504
 
505
- # 6. 更新HTML
506
- update_html()
507
-
508
- # 7. Git提交
509
  git_commit_and_push()
510
 
511
- # 8. 内存回收
512
  del df_full, df_for_model, hist_df_for_metrics
513
  gc.collect()
514
 
@@ -524,7 +412,6 @@ def main_task(model):
524
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] " + "="*60 + "\n")
525
 
526
 
527
- # -------------------------- 修改:定时器逻辑(从0点改为20点触发) --------------------------
528
  def run_scheduler(model):
529
  """定时器:基于北京时间20点分界触发任务,其他时间5分钟检查一次"""
530
  china_tz = timezone("Asia/Shanghai")
@@ -534,22 +421,20 @@ def run_scheduler(model):
534
  china_now = get_china_time()
535
  current_business_date, is_after_20h = get_business_info()
536
 
537
- # 核心修改:计算下次执行时间(20点触发)
538
  if is_after_20h:
539
- # 已过当天20点 → 下次执行时间为次日20点
540
  next_exec_date = (china_now + timedelta(days=1)).date()
541
  else:
542
- # 未过当天20点 → 下次执行时间为当天20点
543
  next_exec_date = china_now.date()
544
 
545
- # 构造下次执行时间(20:00:05,留5秒缓冲避免毫秒级误差)
546
  next_exec_time = datetime.combine(
547
  next_exec_date,
548
  datetime.strptime("20:00:05", "%H:%M:%S").time(),
549
  tzinfo=china_tz
550
  )
551
 
552
- # 计算等待时间(秒),最小等待5分钟(防止时间计算错误导致负数)
553
  sleep_seconds = (next_exec_time - china_now).total_seconds()
554
  sleep_seconds = max(sleep_seconds, 300)
555
 
@@ -566,7 +451,6 @@ def run_scheduler(model):
566
  # 到达执行时间,触发主任务
567
  try:
568
  main_task(model)
569
- # 无需重置业务日标记(下次判断基于新业务日)
570
  except Exception as e:
571
  print(f"[{get_china_time():%Y-%m-%d %H:%M:%S}] 定时器触发任务失败:{str(e)}")
572
  import traceback
@@ -587,4 +471,5 @@ if __name__ == '__main__':
587
  main_task(loaded_model)
588
 
589
  # 启动定时器(中国时间每天20点执行)
590
- run_scheduler(loaded_model)
 
 
28
  "PREDICTION_CACHE": os.path.join("/tmp", "predictions_cache"),
29
  "CHART_PATH": os.path.join("/tmp", "prediction_chart.png"),
30
  "HTML_PATH": os.path.join("/tmp", "index.html"),
31
+ # 核心配置:记录最后推理业务日
32
  "LAST_INFERENCED_BUSINESS_DATE": None,
33
+ # 新增:供Flask读取的预测指标
34
+ "upside_prob": None, # 上涨概率
35
+ "vol_amp_prob": None, # 波动率放大概率
36
+ "update_time": None, # 最后更新时间
37
  "CACHED_RESULTS": {
38
  "close_preds": None,
39
  "volume_preds": None,
40
  "v_close_preds": None,
 
 
41
  "hist_df_for_plot": None
42
  }
43
  }
44
 
45
+ # 补充定义中文字体路径
46
  Config["CHINESE_FONT_PATH"] = os.path.join(Config["REPO_PATH"], "fonts", "wqy-microhei.ttf")
47
 
48
  # 创建必要目录
 
56
  return datetime.now(china_tz)
57
 
58
 
 
59
  def get_business_info():
60
  """
61
  基于北京时间20点分界,返回当前业务信息
 
97
  return predictor
98
 
99
 
 
100
  def fetch_stock_data():
101
  """获取股票数据(基于业务日更新,中国时间),添加数据获取日志"""
102
  china_now = get_china_time()
103
+ current_business_date, _ = get_business_info() # 用业务日作为数据结束日期
104
  end_date = current_business_date.strftime("%Y-%m-%d")
105
  need_points = Config["VOL_WINDOW"] + Config["VOL_WINDOW"] # 历史数据+波动率计算窗口
106
 
 
184
  infer_time = time.time() - begin_time
185
  print(f"[{get_china_time():%Y-%m-%d %H:%M:%S}] 推理完成,耗时{infer_time:.2f}秒")
186
 
187
+ # 波动率预测复用收盘价预测结果
188
  close_preds_volatility = close_preds_main
189
  return close_preds_main, volume_preds_main, close_preds_volatility
190
 
 
222
  china_now = get_china_time()
223
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 开始生成预测图表(适配低版本matplotlib字体)")
224
 
225
+ # 从缓存获取数据
226
  hist_df_for_plot = Config["CACHED_RESULTS"]["hist_df_for_plot"]
227
  close_preds = Config["CACHED_RESULTS"]["close_preds"]
228
  volume_preds = Config["CACHED_RESULTS"]["volume_preds"]
229
 
230
+ # 创建画布和子图
231
  fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8), sharex=True)
 
232
 
233
+ # 低版本matplotlib字体处理
234
  from matplotlib.font_manager import FontProperties
235
  font_path = Config["CHINESE_FONT_PATH"]
236
 
237
  # 检查字体文件是否存在
238
  if os.path.exists(font_path):
 
239
  chinese_font = FontProperties(fname=font_path)
240
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 成功加载.ttf字体:{font_path}")
241
  else:
 
242
  chinese_font = FontProperties(family='SimHei', size=10)
243
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 字体文件不存在,使用系统默认字体:SimHei")
244
 
245
+ # 全局设置字体
246
  plt.rcParams["font.family"] = ["sans-serif"]
247
  plt.rcParams["font.sans-serif"] = ["WenQuanYi Micro Hei", "SimHei", "Heiti TC"]
248
  plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
 
249
 
250
+ # 价格子图
 
251
  hist_time = hist_df_for_plot['timestamps']
252
  ax1.plot(hist_time, hist_df_for_plot['close'], color='#00274C', linewidth=1.5)
253
  mean_preds = close_preds.mean(axis=1)
254
+ # 生成预测时间序列
255
  last_hist_time = hist_time.max()
256
  pred_time = pd.date_range(start=last_hist_time + pd.Timedelta(days=1), periods=Config["PRED_HORIZON"], freq='B')
257
  ax1.plot(pred_time, mean_preds, color='#FF6B00', linestyle='-')
 
261
  ax1.set_title(f'{Config["STOCK_CODE"]} 上证指数概率预测(未来{Config["PRED_HORIZON"]}个交易日)',
262
  fontsize=16, weight='bold', fontproperties=chinese_font)
263
  ax1.set_ylabel('价格(元)', fontsize=12, fontproperties=chinese_font)
 
264
  ax1.legend(['上证指数(后复权)', '预测均价', '预测区间(最小-最大)'],
265
  fontsize=10, prop=chinese_font)
266
  ax1.grid(True, which='both', linestyle='--', linewidth=0.5)
267
 
268
+ # 成交量子图
269
  ax2.bar(hist_time, hist_df_for_plot['volume']/1e8, color='#00A86B', width=0.6)
270
  ax2.bar(pred_time, volume_preds.mean(axis=1)/1e8, color='#FF6B00', width=0.6)
271
  ax2.set_ylabel('成交量(亿手)', fontsize=12, fontproperties=chinese_font)
 
291
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 图表生成完成,保存路径:{chart_path}")
292
 
293
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
  def git_commit_and_push():
295
  """Git提交(仅当Git存在时执行),添加Git操作日志"""
296
  china_now = get_china_time()
 
308
  # 执行Git操作
309
  try:
310
  os.chdir(Config["REPO_PATH"])
311
+ # 复制图表和HTML到Git跟踪目录
312
  chart_src = Config["CHART_PATH"]
313
  chart_dst = Config["REPO_PATH"] / "prediction_chart.png"
314
  html_src = Config["HTML_PATH"]
 
342
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] Git权限错误:{str(e)},跳过Git操作")
343
 
344
 
 
345
  def main_task(model):
346
  """主任务:控制基于20点分界的业务日推理逻辑,同业务日复用缓存"""
347
  china_now = get_china_time()
 
350
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 开始执行主任务")
351
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 当前业务日:{current_business_date}(北京时间{'20点后' if is_after_20h else '20点前'})")
352
 
353
+ # 判断当前业务日是否已推理
354
  if Config["LAST_INFERENCED_BUSINESS_DATE"] == current_business_date:
355
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 当前业务日({current_business_date})已完成推理,直接复用缓存结果")
356
+ # 复用缓存生成图表
357
  create_plot()
 
 
358
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 主任务完成(复用缓存)")
359
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] " + "="*60 + "\n")
360
  return
 
378
  "close_preds": close_preds,
379
  "volume_preds": volume_preds,
380
  "v_close_preds": v_close_preds,
 
 
381
  "hist_df_for_plot": hist_df_for_plot
382
  }
383
+
384
+ # 核心修改:将指标存入Config供Flask读取
385
+ Config["upside_prob"] = round(upside_prob * 100, 1) # 转换为百分比并保留1位小数
386
+ Config["vol_amp_prob"] = round(vol_amp_prob * 100, 1)
387
+ Config["update_time"] = china_now.strftime('%Y-%m-%d %H:%M:%S')
388
+
389
+ # 标记当前业务日已推理
390
  Config["LAST_INFERENCED_BUSINESS_DATE"] = current_business_date
391
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] 业务日({current_business_date})推理结果已缓存,同业务日后续调用将复用")
392
 
393
  # 5. 生成图表
394
  create_plot()
395
 
396
+ # 6. Git提交
 
 
 
397
  git_commit_and_push()
398
 
399
+ # 7. 内存回收
400
  del df_full, df_for_model, hist_df_for_metrics
401
  gc.collect()
402
 
 
412
  print(f"[{china_now:%Y-%m-%d %H:%M:%S}] " + "="*60 + "\n")
413
 
414
 
 
415
  def run_scheduler(model):
416
  """定时器:基于北京时间20点分界触发任务,其他时间5分钟检查一次"""
417
  china_tz = timezone("Asia/Shanghai")
 
421
  china_now = get_china_time()
422
  current_business_date, is_after_20h = get_business_info()
423
 
424
+ # 计算下次执行时间(20点触发)
425
  if is_after_20h:
 
426
  next_exec_date = (china_now + timedelta(days=1)).date()
427
  else:
 
428
  next_exec_date = china_now.date()
429
 
430
+ # 构造下次执行时间(20:00:05,留5秒缓冲)
431
  next_exec_time = datetime.combine(
432
  next_exec_date,
433
  datetime.strptime("20:00:05", "%H:%M:%S").time(),
434
  tzinfo=china_tz
435
  )
436
 
437
+ # 计算等待时间(秒),最小等待5分钟
438
  sleep_seconds = (next_exec_time - china_now).total_seconds()
439
  sleep_seconds = max(sleep_seconds, 300)
440
 
 
451
  # 到达执行时间,触发主任务
452
  try:
453
  main_task(model)
 
454
  except Exception as e:
455
  print(f"[{get_china_time():%Y-%m-%d %H:%M:%S}] 定时器触发任务失败:{str(e)}")
456
  import traceback
 
471
  main_task(loaded_model)
472
 
473
  # 启动定时器(中国时间每天20点执行)
474
+ run_scheduler(loaded_model)
475
+