|
4 | 4 | 从 czsc.svc 模块中提取的 WeightBacktest 相关的绘图代码,按功能整理 |
5 | 5 | """ |
6 | 6 |
|
| 7 | +import re |
7 | 8 | from typing import Union, Tuple |
8 | 9 | import numpy as np |
9 | 10 | import pandas as pd |
|
32 | 33 | # ==================== 辅助函数 ==================== |
33 | 34 |
|
34 | 35 |
|
| 36 | +TABLE_LIGHT_TEXT = "#f8fafc" |
| 37 | +TABLE_DARK_TEXT = "#0f172a" |
| 38 | + |
| 39 | + |
| 40 | +def _parse_rgb_color(color: str) -> Tuple[int, int, int, float] | None: |
| 41 | + """解析 rgb/rgba/hex 颜色字符串。""" |
| 42 | + if not isinstance(color, str): |
| 43 | + return None |
| 44 | + |
| 45 | + value = color.strip() |
| 46 | + if value.startswith("#"): |
| 47 | + hex_color = value[1:] |
| 48 | + if len(hex_color) == 3: |
| 49 | + hex_color = "".join(ch * 2 for ch in hex_color) |
| 50 | + if len(hex_color) == 6: |
| 51 | + return int(hex_color[0:2], 16), int(hex_color[2:4], 16), int(hex_color[4:6], 16), 1.0 |
| 52 | + return None |
| 53 | + |
| 54 | + match = re.fullmatch( |
| 55 | + r"rgba?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)(?:\s*,\s*(\d+(?:\.\d+)?))?\s*\)", |
| 56 | + value, |
| 57 | + ) |
| 58 | + if not match: |
| 59 | + return None |
| 60 | + |
| 61 | + red, green, blue = (int(float(match.group(index))) for index in range(1, 4)) |
| 62 | + alpha = float(match.group(4)) if match.group(4) is not None else 1.0 |
| 63 | + return red, green, blue, alpha |
| 64 | + |
| 65 | + |
| 66 | +def _get_table_text_color(background_color: str, default_color: str) -> str: |
| 67 | + """根据背景亮度返回更易读的文字颜色。""" |
| 68 | + parsed = _parse_rgb_color(background_color) |
| 69 | + if parsed is None: |
| 70 | + return default_color |
| 71 | + |
| 72 | + red, green, blue, alpha = parsed |
| 73 | + if alpha <= 0: |
| 74 | + return default_color |
| 75 | + |
| 76 | + brightness = (red * 299 + green * 587 + blue * 114) / 1000 |
| 77 | + return TABLE_DARK_TEXT if brightness >= 150 else TABLE_LIGHT_TEXT |
| 78 | + |
| 79 | + |
| 80 | +def _get_default_table_text_color(template: TemplateType) -> str: |
| 81 | + """根据模板推断表格默认文字颜色。""" |
| 82 | + return TABLE_LIGHT_TEXT if "dark" in str(template).lower() else TABLE_DARK_TEXT |
| 83 | + |
| 84 | + |
35 | 85 | def _calculate_drawdown( |
36 | 86 | returns: pd.Series, |
37 | 87 | fillna: bool = True |
@@ -529,14 +579,18 @@ def plot_colored_table( |
529 | 579 | row_height = kwargs.get("row_height", 30) |
530 | 580 | border_color = kwargs.get("border_color", COLOR_BORDER) |
531 | 581 | header_bgcolor = kwargs.get("header_bgcolor", COLOR_HEADER_BG) |
| 582 | + default_text_color = _get_default_table_text_color(template) |
| 583 | + header_text_color = _get_table_text_color(header_bgcolor, default_text_color) |
532 | 584 |
|
533 | 585 | # 准备数据和颜色 |
534 | 586 | cell_values = [] |
535 | 587 | cell_colors = [] |
| 588 | + cell_font_colors = [] |
536 | 589 |
|
537 | 590 | # 处理索引列 |
538 | 591 | cell_values.append(df.index.tolist()) |
539 | 592 | cell_colors.append([header_bgcolor] * len(df)) |
| 593 | + cell_font_colors.append([header_text_color] * len(df)) |
540 | 594 |
|
541 | 595 | # 处理数据列 |
542 | 596 | for col in df.columns: |
@@ -565,23 +619,25 @@ def plot_colored_table( |
565 | 619 | colors = px.colors.sample_colorscale("RdYlGn_r", sample_vals) |
566 | 620 |
|
567 | 621 | cell_colors.append(colors) |
| 622 | + cell_font_colors.append([_get_table_text_color(str(color), default_text_color) for color in colors]) |
568 | 623 | else: |
569 | 624 | cell_colors.append(['rgba(0,0,0,0)'] * len(series)) |
| 625 | + cell_font_colors.append([default_text_color] * len(series)) |
570 | 626 |
|
571 | 627 | fig = go.Figure(data=[go.Table( |
572 | 628 | header=dict( |
573 | 629 | values=headers, |
574 | 630 | fill_color=header_bgcolor, |
575 | 631 | align='center', |
576 | | - font=dict(color='white', size=12), |
| 632 | + font=dict(color=header_text_color, size=12), |
577 | 633 | height=row_height, |
578 | 634 | line=dict(color=border_color, width=1) |
579 | 635 | ), |
580 | 636 | cells=dict( |
581 | 637 | values=cell_values, |
582 | 638 | fill_color=cell_colors, |
583 | 639 | align='center', |
584 | | - font=dict(color='white', size=12), |
| 640 | + font=dict(color=cell_font_colors, size=12), |
585 | 641 | height=row_height, |
586 | 642 | line=dict(color=border_color, width=1) |
587 | 643 | ) |
|
0 commit comments