Refactor code for improved readability and consistency

- Cleaned up whitespace and formatting in various files including http.py, language.py, logger.py, safe_exec.py, and SQL migration scripts.
- Consolidated import statements and removed unnecessary blank lines.
- Updated logging configuration for better clarity.
- Enhanced the safe execution code with improved error handling and logging.
- Removed commented-out code and unnecessary variables in backfill_zero_trades.py and other scripts.
- Added a pyproject.toml for Ruff and Vulture configuration.
- Introduced requirements-dev.txt for development dependencies.
- Removed commented-out stock entries in init.sql for cleaner migration scripts.
This commit is contained in:
dienakdz
2026-04-09 14:30:51 +07:00
parent 103055b3df
commit 87f2845483
157 changed files with 19026 additions and 17773 deletions
+25 -65
View File
@@ -4,46 +4,6 @@ This document records version updates, new features, bug fixes, and database mig
---
## 2026-04-07 — 数据库:`qd_market_symbols` 补充 A股 / H股热门标的
已在 **Docker** 内对运行中的 PostgreSQL 执行完毕(`INSERT 0 20`)。**新库**若使用当前仓库中的 `migrations/init.sql` 初始化,已包含同批种子数据,无需重复执行。
**在已有库上手动执行(等价 SQL,可重复执行,`ON CONFLICT DO NOTHING`):**
```sql
INSERT INTO qd_market_symbols (market, symbol, name, exchange, currency, is_active, is_hot, sort_order) VALUES
('CNStock', '600519', '贵州茅台', 'SSE', 'CNY', 1, 1, 100),
('CNStock', '600036', '招商银行', 'SSE', 'CNY', 1, 1, 99),
('CNStock', '601318', '中国平安', 'SSE', 'CNY', 1, 1, 98),
('CNStock', '600900', '长江电力', 'SSE', 'CNY', 1, 1, 97),
('CNStock', '601899', '紫金矿业', 'SSE', 'CNY', 1, 1, 96),
('CNStock', '000858', '五粮液', 'SZSE', 'CNY', 1, 1, 95),
('CNStock', '000333', '美的集团', 'SZSE', 'CNY', 1, 1, 94),
('CNStock', '002594', '比亚迪', 'SZSE', 'CNY', 1, 1, 93),
('CNStock', '300750', '宁德时代', 'SZSE', 'CNY', 1, 1, 92),
('CNStock', '000001', '平安银行', 'SZSE', 'CNY', 1, 1, 91),
('HKStock', '00700', '腾讯控股', 'HKEX', 'HKD', 1, 1, 100),
('HKStock', '09988', '阿里巴巴-W', 'HKEX', 'HKD', 1, 1, 99),
('HKStock', '03690', '美团-W', 'HKEX', 'HKD', 1, 1, 98),
('HKStock', '01810', '小米集团-W', 'HKEX', 'HKD', 1, 1, 97),
('HKStock', '00939', '建设银行', 'HKEX', 'HKD', 1, 1, 96),
('HKStock', '01299', '友邦保险', 'HKEX', 'HKD', 1, 1, 95),
('HKStock', '02318', '中国平安', 'HKEX', 'HKD', 1, 1, 94),
('HKStock', '00388', '香港交易所', 'HKEX', 'HKD', 1, 1, 93),
('HKStock', '00883', '中国海洋石油', 'HKEX', 'HKD', 1, 1, 92),
('HKStock', '01398', '工商银行', 'HKEX', 'HKD', 1, 1, 91)
ON CONFLICT (market, symbol) DO NOTHING;
```
**Docker 一行示例(文件需 UTF-8):**
```bash
docker cp backend_api_python/migrations/<your>.sql quantdinger-db:/tmp/migrate.sql
docker compose exec -T postgres psql -U quantdinger -d quantdinger -f /tmp/migrate.sql
```
---
## V3.0.1 (2026-04-05) — Frontend / docs
- **Front-end version**: `QuantDinger-Vue-src/package.json`, footer display and `frontend/VERSION` are unified to **3.0.1**.
@@ -228,7 +188,7 @@ CREATE TABLE IF NOT EXISTS qd_polymarket_markets (
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
SELECT 1 FROM information_schema.columns
WHERE table_name = 'qd_polymarket_markets' AND column_name = 'slug'
) THEN
ALTER TABLE qd_polymarket_markets ADD COLUMN slug VARCHAR(255);
@@ -568,14 +528,14 @@ See `docs/CROSS_SECTIONAL_STRATEGY_GUIDE_CN.md` or `docs/CROSS_SECTIONAL_STRATEG
-- are stored in the trading_config JSON field, not as separate database columns.
-- This migration only adds the last_rebalance_at timestamp field which is needed for rebalancing logic.
DO $$
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'qd_strategies_trading'
SELECT 1 FROM information_schema.columns
WHERE table_name = 'qd_strategies_trading'
AND column_name = 'last_rebalance_at'
) THEN
ALTER TABLE qd_strategies_trading
ALTER TABLE qd_strategies_trading
ADD COLUMN last_rebalance_at TIMESTAMP;
RAISE NOTICE 'Added last_rebalance_at column to qd_strategies_trading';
ELSE
@@ -761,7 +721,7 @@ CREATE TABLE IF NOT EXISTS qd_analysis_memory (
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
SELECT 1 FROM information_schema.columns
WHERE table_name = 'qd_analysis_memory' AND column_name = 'raw_result'
) THEN
ALTER TABLE qd_analysis_memory ADD COLUMN raw_result JSONB;
@@ -772,7 +732,7 @@ END $$;
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
SELECT 1 FROM information_schema.columns
WHERE table_name = 'qd_analysis_memory' AND column_name = 'user_id'
) THEN
ALTER TABLE qd_analysis_memory ADD COLUMN user_id INT;
@@ -820,64 +780,64 @@ DO $$
BEGIN
-- Purchase count
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
SELECT 1 FROM information_schema.columns
WHERE table_name = 'qd_indicator_codes' AND column_name = 'purchase_count'
) THEN
ALTER TABLE qd_indicator_codes ADD COLUMN purchase_count INTEGER DEFAULT 0;
END IF;
-- Average rating
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
SELECT 1 FROM information_schema.columns
WHERE table_name = 'qd_indicator_codes' AND column_name = 'avg_rating'
) THEN
ALTER TABLE qd_indicator_codes ADD COLUMN avg_rating DECIMAL(3,2) DEFAULT 0;
END IF;
-- Rating count
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
SELECT 1 FROM information_schema.columns
WHERE table_name = 'qd_indicator_codes' AND column_name = 'rating_count'
) THEN
ALTER TABLE qd_indicator_codes ADD COLUMN rating_count INTEGER DEFAULT 0;
END IF;
-- View count
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
SELECT 1 FROM information_schema.columns
WHERE table_name = 'qd_indicator_codes' AND column_name = 'view_count'
) THEN
ALTER TABLE qd_indicator_codes ADD COLUMN view_count INTEGER DEFAULT 0;
END IF;
-- Review status
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
SELECT 1 FROM information_schema.columns
WHERE table_name = 'qd_indicator_codes' AND column_name = 'review_status'
) THEN
ALTER TABLE qd_indicator_codes ADD COLUMN review_status VARCHAR(20) DEFAULT 'approved';
UPDATE qd_indicator_codes SET review_status = 'approved' WHERE publish_to_community = 1;
END IF;
-- Review note
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
SELECT 1 FROM information_schema.columns
WHERE table_name = 'qd_indicator_codes' AND column_name = 'review_note'
) THEN
ALTER TABLE qd_indicator_codes ADD COLUMN review_note TEXT DEFAULT '';
END IF;
-- Reviewed at
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
SELECT 1 FROM information_schema.columns
WHERE table_name = 'qd_indicator_codes' AND column_name = 'reviewed_at'
) THEN
ALTER TABLE qd_indicator_codes ADD COLUMN reviewed_at TIMESTAMP;
END IF;
-- Reviewed by
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
SELECT 1 FROM information_schema.columns
WHERE table_name = 'qd_indicator_codes' AND column_name = 'reviewed_by'
) THEN
ALTER TABLE qd_indicator_codes ADD COLUMN reviewed_by INTEGER;
@@ -891,15 +851,15 @@ DO $$
BEGIN
-- Token version (for single-client login)
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
SELECT 1 FROM information_schema.columns
WHERE table_name = 'qd_users' AND column_name = 'token_version'
) THEN
ALTER TABLE qd_users ADD COLUMN token_version INTEGER DEFAULT 1;
END IF;
-- Notification settings
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
SELECT 1 FROM information_schema.columns
WHERE table_name = 'qd_users' AND column_name = 'notification_settings'
) THEN
ALTER TABLE qd_users ADD COLUMN notification_settings TEXT DEFAULT '{}';
+10 -10
View File
@@ -34,24 +34,24 @@
### 参数说明
- **cs_strategy_type**:
- **cs_strategy_type**:
- `'single'`: 单标的策略(默认,原有功能)
- `'cross_sectional'`: 截面策略
- **symbol_list**:
- **symbol_list**:
- 标的列表,格式为 `["Market:SYMBOL", ...]`
- 例如:`["Crypto:BTC/USDT", "Crypto:ETH/USDT"]`
- **portfolio_size**:
- **portfolio_size**:
- 持仓组合大小,即同时持有的标的数量
- 例如:10 表示同时持有10个标的
- **long_ratio**:
- **long_ratio**:
- 做多比例,0-1之间的浮点数
- 例如:0.5 表示50%做多,50%做空
- 例如:1.0 表示100%做多(不做空)
- **rebalance_frequency**:
- **rebalance_frequency**:
- 调仓频率
- `'daily'`: 每日调仓
- `'weekly'`: 每周调仓
@@ -74,7 +74,7 @@ for symbol, df in data.items():
# 计算每个标的的因子值
# 例如:动量因子
momentum = (df['close'].iloc[-1] / df['close'].iloc[-20] - 1) * 100
# 例如:RSI指标
def calculate_rsi(prices, period=14):
delta = prices.diff()
@@ -83,9 +83,9 @@ for symbol, df in data.items():
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
return rsi.iloc[-1]
rsi = calculate_rsi(df['close'], 14)
# 综合评分(可以根据需要调整权重)
score = momentum * 0.6 + (100 - rsi) * 0.4
scores[symbol] = score
@@ -175,7 +175,7 @@ scores = {}
for symbol, df in data.items():
# 20周期动量
momentum = (df['close'].iloc[-1] / df['close'].iloc[-20] - 1) * 100
# RSI
delta = df['close'].diff()
gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
@@ -183,7 +183,7 @@ for symbol, df in data.items():
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
rsi_value = rsi.iloc[-1]
# 综合评分
score = momentum * 0.7 + (100 - rsi_value) * 0.3
scores[symbol] = score
+10 -10
View File
@@ -34,24 +34,24 @@ When creating or editing a strategy, add the following parameters to `trading_co
### Parameter Description
- **cs_strategy_type**:
- **cs_strategy_type**:
- `'single'`: Single-symbol strategy (default, original functionality)
- `'cross_sectional'`: Cross-sectional strategy
- **symbol_list**:
- **symbol_list**:
- List of symbols, format: `["Market:SYMBOL", ...]`
- Example: `["Crypto:BTC/USDT", "Crypto:ETH/USDT"]`
- **portfolio_size**:
- **portfolio_size**:
- Portfolio size, i.e., the number of symbols to hold simultaneously
- Example: 10 means holding 10 symbols at the same time
- **long_ratio**:
- **long_ratio**:
- Long ratio, a float between 0 and 1
- Example: 0.5 means 50% long, 50% short
- Example: 1.0 means 100% long (no short positions)
- **rebalance_frequency**:
- **rebalance_frequency**:
- Rebalancing frequency
- `'daily'`: Daily rebalancing
- `'weekly'`: Weekly rebalancing
@@ -74,7 +74,7 @@ for symbol, df in data.items():
# Calculate factor values for each symbol
# Example: Momentum factor
momentum = (df['close'].iloc[-1] / df['close'].iloc[-20] - 1) * 100
# Example: RSI indicator
def calculate_rsi(prices, period=14):
delta = prices.diff()
@@ -83,9 +83,9 @@ for symbol, df in data.items():
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
return rsi.iloc[-1]
rsi = calculate_rsi(df['close'], 14)
# Composite score (adjust weights as needed)
score = momentum * 0.6 + (100 - rsi) * 0.4
scores[symbol] = score
@@ -175,7 +175,7 @@ scores = {}
for symbol, df in data.items():
# 20-period momentum
momentum = (df['close'].iloc[-1] / df['close'].iloc[-20] - 1) * 100
# RSI
delta = df['close'].diff()
gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
@@ -183,7 +183,7 @@ for symbol, df in data.items():
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
rsi_value = rsi.iloc[-1]
# Composite score
score = momentum * 0.7 + (100 - rsi_value) * 0.3
scores[symbol] = score
+1 -1
View File
@@ -91,7 +91,7 @@ docker-compose up -d --build
> - 启动:
> `docker-compose up -d --build`
> **Windows PowerShell**:
> **Windows PowerShell**:
> - 复制后端配置:
> `Copy-Item backend_api_python\env.example -Destination backend_api_python\.env`
> - 如果需要更多高级配置,直接查看这个文件下半部分的 “Advanced / rarely changed”:
+4 -4
View File
@@ -98,13 +98,13 @@ For charting, you often want to place the signal icon slightly above or below th
```python
# Place Buy marker 0.5% below the Low
buy_marks = [
df['low'].iloc[i] * 0.995 if df['buy'].iloc[i] else None
df['low'].iloc[i] * 0.995 if df['buy'].iloc[i] else None
for i in range(len(df))
]
# Place Sell marker 0.5% above the High
sell_marks = [
df['high'].iloc[i] * 1.005 if df['sell'].iloc[i] else None
df['high'].iloc[i] * 1.005 if df['sell'].iloc[i] else None
for i in range(len(df))
]
```
@@ -166,12 +166,12 @@ df['sell'] = sell
# -----------------------
# Calculate marker positions
buy_marks = [
df['low'].iloc[i] * 0.995 if buy.iloc[i] else None
df['low'].iloc[i] * 0.995 if buy.iloc[i] else None
for i in range(len(df))
]
sell_marks = [
df['high'].iloc[i] * 1.005 if sell.iloc[i] else None
df['high'].iloc[i] * 1.005 if sell.iloc[i] else None
for i in range(len(df))
]
+4 -4
View File
@@ -98,13 +98,13 @@ df['sell'] = condition_sell.fillna(False)
```python
# 将买入标记放在最低价下方 0.5% 处
buy_marks = [
df['low'].iloc[i] * 0.995 if df['buy'].iloc[i] else None
df['low'].iloc[i] * 0.995 if df['buy'].iloc[i] else None
for i in range(len(df))
]
# 将卖出标记放在最高价上方 0.5% 处
sell_marks = [
df['high'].iloc[i] * 1.005 if df['sell'].iloc[i] else None
df['high'].iloc[i] * 1.005 if df['sell'].iloc[i] else None
for i in range(len(df))
]
```
@@ -166,12 +166,12 @@ df['sell'] = sell
# -----------------------
# 计算标记位置
buy_marks = [
df['low'].iloc[i] * 0.995 if buy.iloc[i] else None
df['low'].iloc[i] * 0.995 if buy.iloc[i] else None
for i in range(len(df))
]
sell_marks = [
df['high'].iloc[i] * 1.005 if sell.iloc[i] else None
df['high'].iloc[i] * 1.005 if sell.iloc[i] else None
for i in range(len(df))
]
+4 -4
View File
@@ -98,13 +98,13 @@ df['sell'] = condition_sell.fillna(False)
```python
# 買いマーカーを安値の 0.5% 下に配置
buy_marks = [
df['low'].iloc[i] * 0.995 if df['buy'].iloc[i] else None
df['low'].iloc[i] * 0.995 if df['buy'].iloc[i] else None
for i in range(len(df))
]
# 売りマーカーを高値の 0.5% 上に配置
sell_marks = [
df['high'].iloc[i] * 1.005 if df['sell'].iloc[i] else None
df['high'].iloc[i] * 1.005 if df['sell'].iloc[i] else None
for i in range(len(df))
]
```
@@ -166,12 +166,12 @@ df['sell'] = sell
# -----------------------
# マーカー位置を計算
buy_marks = [
df['low'].iloc[i] * 0.995 if buy.iloc[i] else None
df['low'].iloc[i] * 0.995 if buy.iloc[i] else None
for i in range(len(df))
]
sell_marks = [
df['high'].iloc[i] * 1.005 if sell.iloc[i] else None
df['high'].iloc[i] * 1.005 if sell.iloc[i] else None
for i in range(len(df))
]
+4 -4
View File
@@ -98,13 +98,13 @@ df['sell'] = condition_sell.fillna(False)
```python
# 매수 마커를 저가보다 0.5% 아래에 배치
buy_marks = [
df['low'].iloc[i] * 0.995 if df['buy'].iloc[i] else None
df['low'].iloc[i] * 0.995 if df['buy'].iloc[i] else None
for i in range(len(df))
]
# 매도 마커를 고가보다 0.5% 위에 배치
sell_marks = [
df['high'].iloc[i] * 1.005 if df['sell'].iloc[i] else None
df['high'].iloc[i] * 1.005 if df['sell'].iloc[i] else None
for i in range(len(df))
]
```
@@ -166,12 +166,12 @@ df['sell'] = sell
# -----------------------
# 마커 위치 계산
buy_marks = [
df['low'].iloc[i] * 0.995 if buy.iloc[i] else None
df['low'].iloc[i] * 0.995 if buy.iloc[i] else None
for i in range(len(df))
]
sell_marks = [
df['high'].iloc[i] * 1.005 if sell.iloc[i] else None
df['high'].iloc[i] * 1.005 if sell.iloc[i] else None
for i in range(len(df))
]
+4 -4
View File
@@ -98,13 +98,13 @@ df['sell'] = condition_sell.fillna(False)
```python
# 將買入標記放在最低價下方 0.5% 處
buy_marks = [
df['low'].iloc[i] * 0.995 if df['buy'].iloc[i] else None
df['low'].iloc[i] * 0.995 if df['buy'].iloc[i] else None
for i in range(len(df))
]
# 將賣出標記放在最高價上方 0.5% 處
sell_marks = [
df['high'].iloc[i] * 1.005 if df['sell'].iloc[i] else None
df['high'].iloc[i] * 1.005 if df['sell'].iloc[i] else None
for i in range(len(df))
]
```
@@ -166,12 +166,12 @@ df['sell'] = sell
# -----------------------
# 計算標記位置
buy_marks = [
df['low'].iloc[i] * 0.995 if buy.iloc[i] else None
df['low'].iloc[i] * 0.995 if buy.iloc[i] else None
for i in range(len(df))
]
sell_marks = [
df['high'].iloc[i] * 1.005 if sell.iloc[i] else None
df['high'].iloc[i] * 1.005 if sell.iloc[i] else None
for i in range(len(df))
]
@@ -3,17 +3,17 @@
# Cross-Sectional Strategy Indicator Example
# Momentum + RSI Composite Score
# ============================================================
#
#
# 使用方法:
# 1. 在交易助手中创建截面策略
# 2. 选择此指标作为策略指标
# 3. 配置标的列表、持仓大小、做多比例等参数
#
#
# 评分逻辑:
# - 动量因子 (20周期): 价格变化率,越高越好
# - RSI指标 (14周期): 反转RSI值,越低越好(100 - RSI
# - 综合评分: 70% 动量 + 30% RSI反转值
#
#
# ============================================================
# 截面策略指标
@@ -28,11 +28,11 @@ for symbol, df in data.items():
if len(df) < 20:
scores[symbol] = 0
continue
# === 1. 计算动量因子 (20周期) ===
# 动量 = (当前价格 / 20周期前价格 - 1) * 100
momentum = (df['close'].iloc[-1] / df['close'].iloc[-20] - 1) * 100
# === 2. 计算RSI指标 (14周期) ===
def calculate_rsi(prices, period=14):
"""计算RSI指标"""
@@ -42,18 +42,18 @@ for symbol, df in data.items():
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
return rsi.iloc[-1]
rsi_value = calculate_rsi(df['close'], 14)
# === 3. 综合评分 ===
# 动量越高 = 评分越高
# RSI越低(超卖)= 评分越高(100 - RSI)
# 权重: 70% 动量 + 30% RSI反转值
momentum_score = momentum
rsi_score = 100 - rsi_value # 反转RSIRSI越低,评分越高)
composite_score = momentum_score * 0.7 + rsi_score * 0.3
scores[symbol] = composite_score
# === 可选: 手动指定排序 ===
+2 -2
View File
@@ -2,13 +2,13 @@
# 双均线策略 (支持外部参数配置)
# Dual Moving Average Strategy with External Parameters
# ============================================================
#
#
# 使用方法:
# 1. 在交易助手中选择此指标
# 2. 根据不同币种配置不同参数
# - BTC/USDT: sma_short=5, sma_long=10
# - ETH/USDT: sma_short=5, sma_long=20
#
#
# ============================================================
# === 参数声明 (会在前端表单中显示) ===
+3 -3
View File
@@ -2,12 +2,12 @@
# 多指标组合策略 (均线+RSI+MACD)
# Multi-Indicator Composite Strategy
# ============================================================
#
#
# 使用方法:
# 1. 可配置均线周期、RSI阈值等参数
# 2. 买入条件: RSI超卖 + MACD金叉 + 成交量放大
# 3. 卖出条件: RSI超买 或 MACD死叉
#
#
# ============================================================
# === 参数声明 ===
@@ -78,7 +78,7 @@ buy = ma_golden | rsi_buy # 均线金叉 或 RSI超卖
if use_macd:
buy = buy & (macd > macd_signal) # 需要MACD向上
if use_volume:
buy = buy & volume_up # 需要成交量放大