Fix Chinese font rendering in all chart outputs
- Add src/font_config.py: centralized font detection that auto-selects from Noto Sans SC > Hiragino Sans GB > STHeiti > Arial Unicode MS - Replace hardcoded font lists in all 18 modules with unified config - Add .gitignore for __pycache__, .DS_Store, venv, etc. - Regenerate all 70 charts with correct Chinese rendering Previously, 7 modules (fft, wavelet, acf, fractal, hurst, indicators, patterns) had no Chinese font config at all, causing □□□ rendering. The remaining 11 modules used a hardcoded fallback list that didn't prioritize the best available system font. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
31
.gitignore
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
*.egg-info/
|
||||
*.egg
|
||||
dist/
|
||||
build/
|
||||
|
||||
# Virtual environments
|
||||
.venv/
|
||||
venv/
|
||||
env/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Testing
|
||||
.pytest_cache/
|
||||
.coverage
|
||||
htmlcov/
|
||||
|
||||
# Jupyter
|
||||
.ipynb_checkpoints/
|
||||
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 203 KiB After Width: | Height: | Size: 201 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 218 KiB After Width: | Height: | Size: 205 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 104 KiB |
|
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 149 KiB |
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 107 KiB After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 149 KiB |
|
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 169 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 99 KiB |
|
Before Width: | Height: | Size: 652 KiB After Width: | Height: | Size: 655 KiB |
|
Before Width: | Height: | Size: 517 KiB After Width: | Height: | Size: 514 KiB |
|
Before Width: | Height: | Size: 294 KiB After Width: | Height: | Size: 290 KiB |
|
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 350 KiB After Width: | Height: | Size: 348 KiB |
|
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 132 KiB |
|
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 114 KiB After Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 98 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 96 KiB |
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 119 KiB |
|
Before Width: | Height: | Size: 118 KiB After Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 133 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 280 KiB After Width: | Height: | Size: 278 KiB |
|
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 234 KiB After Width: | Height: | Size: 231 KiB |
|
Before Width: | Height: | Size: 229 KiB After Width: | Height: | Size: 229 KiB |
|
Before Width: | Height: | Size: 222 KiB After Width: | Height: | Size: 222 KiB |
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 216 KiB After Width: | Height: | Size: 215 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 785 KiB After Width: | Height: | Size: 810 KiB |
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
@@ -15,8 +15,23 @@ BTC/USDT 价格规律性分析 — 综合结论报告
|
||||
----------------------------------------------------------------------
|
||||
模块 得分 强度 发现数
|
||||
----------------------------------------------------------------------
|
||||
fft 0.00 none 0
|
||||
fractal 0.00 none 0
|
||||
power_law 0.00 none 0
|
||||
wavelet 0.00 none 0
|
||||
acf 0.00 none 0
|
||||
returns 0.00 none 0
|
||||
volatility 0.00 none 0
|
||||
hurst 0.00 none 0
|
||||
volume_price 0.00 none 0
|
||||
time_series 0.00 none 0
|
||||
causality 0.00 none 0
|
||||
calendar 0.00 none 0
|
||||
halving 0.00 none 0
|
||||
indicators 0.00 none 0
|
||||
patterns 0.00 none 0
|
||||
clustering 0.00 none 0
|
||||
anomaly 0.00 none 0
|
||||
----------------------------------------------------------------------
|
||||
|
||||
## 强证据规律(可重复、有经济意义):
|
||||
@@ -26,8 +41,23 @@ patterns 0.00 none 0
|
||||
(无)
|
||||
|
||||
## 弱证据/不显著:
|
||||
* indicators
|
||||
* fft
|
||||
* time_series
|
||||
* clustering
|
||||
* patterns
|
||||
* indicators
|
||||
* halving
|
||||
* calendar
|
||||
* causality
|
||||
* volume_price
|
||||
* fractal
|
||||
* hurst
|
||||
* volatility
|
||||
* returns
|
||||
* acf
|
||||
* wavelet
|
||||
* power_law
|
||||
* anomaly
|
||||
|
||||
======================================================================
|
||||
注: 得分基于各模块自报告的统计检验结果。
|
||||
|
||||
@@ -10,6 +10,9 @@ import pandas as pd
|
||||
import matplotlib
|
||||
matplotlib.use('Agg')
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from src.font_config import configure_chinese_font
|
||||
configure_chinese_font()
|
||||
from statsmodels.tsa.stattools import acf, pacf
|
||||
from statsmodels.stats.diagnostic import acorr_ljungbox
|
||||
from pathlib import Path
|
||||
|
||||
@@ -705,9 +705,8 @@ def run_anomaly_analysis(
|
||||
print(f"数据范围: {df.index.min()} ~ {df.index.max()}")
|
||||
print(f"样本数量: {len(df)}")
|
||||
|
||||
# 设置中文字体
|
||||
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei', 'DejaVu Sans']
|
||||
plt.rcParams['axes.unicode_minus'] = False
|
||||
from src.font_config import configure_chinese_font
|
||||
configure_chinese_font()
|
||||
|
||||
# --- 集成异常检测 ---
|
||||
print("\n>>> [1/5] 执行集成异常检测...")
|
||||
|
||||
@@ -12,9 +12,8 @@ from pathlib import Path
|
||||
from itertools import combinations
|
||||
from scipy import stats
|
||||
|
||||
# 中文显示配置
|
||||
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei', 'DejaVu Sans']
|
||||
plt.rcParams['axes.unicode_minus'] = False
|
||||
from src.font_config import configure_chinese_font
|
||||
configure_chinese_font()
|
||||
|
||||
# 星期名称映射(中英文)
|
||||
WEEKDAY_NAMES_CN = {0: '周一', 1: '周二', 2: '周三', 3: '周四',
|
||||
|
||||
@@ -543,9 +543,8 @@ def run_causality_analysis(
|
||||
print(f"因果变量对数: {len(CAUSALITY_PAIRS)}")
|
||||
print(f"总检验次数(含所有滞后): {len(CAUSALITY_PAIRS) * len(TEST_LAGS)}")
|
||||
|
||||
# 设置中文字体
|
||||
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei', 'DejaVu Sans']
|
||||
plt.rcParams['axes.unicode_minus'] = False
|
||||
from src.font_config import configure_chinese_font
|
||||
configure_chinese_font()
|
||||
|
||||
# --- 日线级 Granger 因果检验 ---
|
||||
print("\n>>> [1/4] 执行日线级 Granger 因果检验...")
|
||||
|
||||
@@ -632,9 +632,8 @@ def run_clustering_analysis(df: pd.DataFrame, output_dir: "str | Path" = "output
|
||||
output_dir = Path(output_dir)
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 设置中文字体(macOS)
|
||||
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei', 'DejaVu Sans']
|
||||
plt.rcParams['axes.unicode_minus'] = False
|
||||
from src.font_config import configure_chinese_font
|
||||
configure_chinese_font()
|
||||
|
||||
print("=" * 60)
|
||||
print(" BTC 市场状态聚类与马尔可夫链分析")
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
import matplotlib
|
||||
matplotlib.use("Agg")
|
||||
|
||||
from src.font_config import configure_chinese_font
|
||||
configure_chinese_font()
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
60
src/font_config.py
Normal file
@@ -0,0 +1,60 @@
|
||||
"""
|
||||
统一 matplotlib 中文字体配置。
|
||||
|
||||
所有绘图模块在创建图表前应调用 configure_chinese_font()。
|
||||
"""
|
||||
|
||||
import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.font_manager as fm
|
||||
|
||||
_configured = False
|
||||
|
||||
# 按优先级排列的中文字体候选列表
|
||||
_CHINESE_FONT_CANDIDATES = [
|
||||
'Noto Sans SC', # Google 思源黑体(最佳渲染质量)
|
||||
'Hiragino Sans GB', # macOS 系统自带
|
||||
'STHeiti', # macOS 系统自带
|
||||
'Arial Unicode MS', # macOS/Windows 通用
|
||||
'SimHei', # Windows 黑体
|
||||
'WenQuanYi Micro Hei', # Linux 文泉驿
|
||||
'DejaVu Sans', # 最终回退(不支持中文,但不会崩溃)
|
||||
]
|
||||
|
||||
|
||||
def _find_available_chinese_fonts():
|
||||
"""检测系统中实际可用的中文字体。"""
|
||||
available = []
|
||||
for font_name in _CHINESE_FONT_CANDIDATES:
|
||||
try:
|
||||
path = fm.findfont(
|
||||
fm.FontProperties(family=font_name),
|
||||
fallback_to_default=False
|
||||
)
|
||||
if path and 'LastResort' not in path:
|
||||
available.append(font_name)
|
||||
except Exception:
|
||||
continue
|
||||
return available if available else ['DejaVu Sans']
|
||||
|
||||
|
||||
def configure_chinese_font():
|
||||
"""
|
||||
配置 matplotlib 使用中文字体。
|
||||
|
||||
- 自动检测系统可用的中文字体
|
||||
- 设置 sans-serif 字体族
|
||||
- 修复负号显示问题
|
||||
- 仅在首次调用时执行,后续调用为空操作
|
||||
"""
|
||||
global _configured
|
||||
if _configured:
|
||||
return
|
||||
|
||||
available = _find_available_chinese_fonts()
|
||||
|
||||
plt.rcParams['font.sans-serif'] = available
|
||||
plt.rcParams['axes.unicode_minus'] = False
|
||||
plt.rcParams['font.family'] = 'sans-serif'
|
||||
|
||||
_configured = True
|
||||
@@ -13,6 +13,9 @@
|
||||
import matplotlib
|
||||
matplotlib.use('Agg')
|
||||
|
||||
from src.font_config import configure_chinese_font
|
||||
configure_chinese_font()
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
@@ -10,9 +10,8 @@ import matplotlib.ticker as mticker
|
||||
from pathlib import Path
|
||||
from scipy import stats
|
||||
|
||||
# 中文显示配置
|
||||
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei', 'DejaVu Sans']
|
||||
plt.rcParams['axes.unicode_minus'] = False
|
||||
from src.font_config import configure_chinese_font
|
||||
configure_chinese_font()
|
||||
|
||||
# BTC 减半日期(数据范围 2017-2026 内的两次减半)
|
||||
HALVING_DATES = [
|
||||
|
||||
@@ -15,6 +15,9 @@ Hurst指数分析模块
|
||||
import matplotlib
|
||||
matplotlib.use('Agg')
|
||||
|
||||
from src.font_config import configure_chinese_font
|
||||
configure_chinese_font()
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
import matplotlib
|
||||
matplotlib.use('Agg')
|
||||
|
||||
from src.font_config import configure_chinese_font
|
||||
configure_chinese_font()
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
@@ -8,6 +8,9 @@ K线形态识别与统计验证模块
|
||||
import matplotlib
|
||||
matplotlib.use('Agg')
|
||||
|
||||
from src.font_config import configure_chinese_font
|
||||
configure_chinese_font()
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
@@ -15,9 +15,8 @@ from scipy.optimize import curve_fit
|
||||
from pathlib import Path
|
||||
from typing import Tuple, Dict
|
||||
|
||||
# 中文显示支持
|
||||
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei', 'DejaVu Sans']
|
||||
plt.rcParams['axes.unicode_minus'] = False
|
||||
from src.font_config import configure_chinese_font
|
||||
configure_chinese_font()
|
||||
|
||||
|
||||
def _compute_days_since_start(df: pd.DataFrame) -> np.ndarray:
|
||||
|
||||
@@ -446,9 +446,8 @@ def run_returns_analysis(df: pd.DataFrame, output_dir: str = "output/returns"):
|
||||
# --- 生成可视化 ---
|
||||
print("\n>>> 生成可视化图表...")
|
||||
|
||||
# 设置中文字体(兼容多系统)
|
||||
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei', 'DejaVu Sans']
|
||||
plt.rcParams['axes.unicode_minus'] = False
|
||||
from src.font_config import configure_chinese_font
|
||||
configure_chinese_font()
|
||||
|
||||
plot_histogram_vs_normal(daily_returns, output_dir)
|
||||
plot_qq(daily_returns, output_dir)
|
||||
|
||||
@@ -643,9 +643,8 @@ def run_time_series_analysis(df: pd.DataFrame, output_dir: "str | Path" = "outpu
|
||||
output_dir = Path(output_dir)
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 设置中文字体(macOS)
|
||||
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei', 'DejaVu Sans']
|
||||
plt.rcParams['axes.unicode_minus'] = False
|
||||
from src.font_config import configure_chinese_font
|
||||
configure_chinese_font()
|
||||
|
||||
print("=" * 60)
|
||||
print(" BTC 时间序列预测分析")
|
||||
|
||||
@@ -55,11 +55,8 @@ EVIDENCE_COLORS = {
|
||||
def apply_style():
|
||||
"""应用全局matplotlib样式"""
|
||||
plt.rcParams.update(STYLE_CONFIG)
|
||||
try:
|
||||
plt.rcParams["font.sans-serif"] = ["Arial Unicode MS", "SimHei", "DejaVu Sans"]
|
||||
plt.rcParams["axes.unicode_minus"] = False
|
||||
except Exception:
|
||||
pass
|
||||
from src.font_config import configure_chinese_font
|
||||
configure_chinese_font()
|
||||
|
||||
|
||||
def ensure_dir(path):
|
||||
|
||||
@@ -584,9 +584,8 @@ def run_volatility_analysis(df: pd.DataFrame, output_dir: str = "output/volatili
|
||||
daily_returns = log_returns(df['close'])
|
||||
print(f"日对数收益率样本数: {len(daily_returns)}")
|
||||
|
||||
# 设置中文字体(兼容多系统)
|
||||
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei', 'DejaVu Sans']
|
||||
plt.rcParams['axes.unicode_minus'] = False
|
||||
from src.font_config import configure_chinese_font
|
||||
configure_chinese_font()
|
||||
|
||||
# 固定随机种子以保证杠杆效应散点图采样可复现
|
||||
np.random.seed(42)
|
||||
|
||||
@@ -15,9 +15,8 @@ from statsmodels.tsa.stattools import grangercausalitytests
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
# 中文显示支持
|
||||
plt.rcParams['font.sans-serif'] = ['Arial Unicode MS', 'SimHei', 'DejaVu Sans']
|
||||
plt.rcParams['axes.unicode_minus'] = False
|
||||
from src.font_config import configure_chinese_font
|
||||
configure_chinese_font()
|
||||
|
||||
|
||||
# =============================================================================
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
import matplotlib
|
||||
matplotlib.use('Agg')
|
||||
|
||||
from src.font_config import configure_chinese_font
|
||||
configure_chinese_font()
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import pywt
|
||||
|
||||