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:
+25
-65
@@ -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 '{}';
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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”:
|
||||
|
||||
@@ -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))
|
||||
]
|
||||
|
||||
|
||||
@@ -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))
|
||||
]
|
||||
|
||||
|
||||
@@ -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))
|
||||
]
|
||||
|
||||
|
||||
@@ -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))
|
||||
]
|
||||
|
||||
|
||||
@@ -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 # 反转RSI(RSI越低,评分越高)
|
||||
|
||||
|
||||
composite_score = momentum_score * 0.7 + rsi_score * 0.3
|
||||
|
||||
|
||||
scores[symbol] = composite_score
|
||||
|
||||
# === 可选: 手动指定排序 ===
|
||||
|
||||
@@ -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
|
||||
#
|
||||
#
|
||||
# ============================================================
|
||||
|
||||
# === 参数声明 (会在前端表单中显示) ===
|
||||
|
||||
@@ -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 # 需要成交量放大
|
||||
|
||||
|
||||
Reference in New Issue
Block a user