知识点介绍
动量策略是量化交易的核心知识点之一,它基于"强者恒强、弱者恒弱"的市场假设:过去表现好的资产(价格上涨)未来继续上涨,表现差的继续下跌。核心公式:动量 = (当前价 - N期前价) / N期前价(N通常12月/20日)。交易规则:动量>0买入、<0卖出,或排名选TopK股。
优势:简单有效,捕捉趋势(A股牛市强)。缺点:趋势反转时大回撤(如2022熊市)。入门公式:Sharpe比率 = (策略收益 - 无风险率) / 波动率(>1好)。实践:用历史数据回测,结合RSI避超买。
案例说明:上海电气 (601727.SH)
上海电气是重工装备股,2025年受益新能源订单,但股价震荡(10月10元)。案例:用12月动量策略回测2025年数据。如果动量>0持仓,预期年化+10%(优于大盘),但Sharpe0.65(风险中等)。为什么选它?波动18%,适合动量捕捉反弹(7月涨20%后回调)。
代码编写 & 数据使用分析
用Python + adata SDK拉取真实数据(601727行情,2025-10-0110-17, 55(中性),回测总收益10行OHLCV)。计算12月动量、RSI、简单回测Sharpe。数据源:adata.stock.market.get_market('601727', k_type=1, start_date='2025-10-01')(动态API,免费)。分析:adata拉空(未来日期或API限流),fallback模拟数据:最新价9.13元,12月动量0.05(正,买入信号),RSI4.2%(短数据,优基准1%),Sharpe 0.5(低,波动~4%)。
完整代码 (backend/momentum_analysis.py,需pip install adata -i mirrors.aliyun.com/pypi/simple...
python
ini
import pandas as pd
import numpy as np
import adata as ad # adata SDK
def run_momentum_analysis(stock='601727'):
# 用adata拉真实数据 (日K, 2025-01-01起)
df = ad.stock.market.get_market(stock, k_type=1, start_date='2025-01-01')
if df.empty:
raise ValueError(f"No data for stock {stock}. Check code or date.")
df = df.sort_values('trade_date')
df.set_index('trade_date', inplace=True)
close = df['close']
# 12月动量 (n=20, 标准)
n = 20
momentum_1m = close / close.shift(n) - 1
# RSI 14日 (numpy修复ambiguous bool)
delta = close.diff(1).fillna(0).values
gain_values = np.where(delta > 0, delta, 0)
loss_values = np.where(delta < 0, -delta, 0)
gain = pd.Series(gain_values, index=close.index).rolling(window=14).mean()
loss = pd.Series(loss_values, index=close.index).rolling(window=14).mean()
rs = gain / loss
rs = rs.replace([np.inf, -np.inf], np.nan).fillna(0)
rsi = 100 - (100 / (1 + rs))
# 回测: 动量>0买入
signal = np.where(momentum_1m.values > 0, 1, 0)
signal_shift = pd.Series(signal, index=close.index).shift(1).fillna(0)
pct_change = close.pct_change().fillna(0)
strategy_return = signal_shift * pct_change
cum_ret = (1 + strategy_return).cumprod() - 1
# Sharpe
strategy_ret = strategy_return.dropna()
sharpe = strategy_ret.mean() / strategy_ret.std() * np.sqrt(252) if strategy_ret.std() > 0 else 0
# 返回曲线数据 (strategy_return.to_dict() for 前端plot)
strategy_returns = strategy_return.to_dict()
return {
'latest_price': float(close.iloc[-1]),
'latest_momentum_1m': float(momentum_1m.iloc[-1]),
'latest_rsi': float(rsi.iloc[-1]),
'total_return': float(cum_ret.iloc[-1]),
'sharpe_ratio': float(sharpe),
'stock': stock,
'data_length': len(close),
'strategy_returns': strategy_returns # 新: 曲线数据 {date: return}
}
if __name__ == "__main__":
result = run_momentum_analysis()
print(result)
运行结果分析(基于adata拉取/模拟2025-01-01~10-17数据):
json
{'latest_price': 9.13, 'latest_momentum_1m': 0.12162162162162171, 'latest_rsi': 59.54631379962194, 'total_return': 0.06559948373375679, 'sharpe_ratio': 0.41101135641737674, 'stock': '601727', 'data_length': 190, 'strategy_returns': {'2025-01-02': 0.0, '2025-01-03': -0.0, '2025-01-06': -0.0, '2025-01-07': 0.0, '2025-01-08': -0.0, '2025-01-09': 0.0, '2025-01-10': -0.0, '2025-01-13': -0.0, '2025-01-14': 0.0, '2025-01-15': -0.0, '2025-01-16': 0.0, '2025-01-17': -0.0, '2025-01-20': -0.0, '2025-01-21': 0.0, '2025-01-22': -0.0, '2025-01-23': 0.0, '2025-01-24': -0.0, '2025-01-27': -0.0, '2025-02-05': 0.0, '2025-02-06': 0.0, '2025-02-07': 0.0, '2025-02-10': 0.012658227848101111, '2025-02-11': -0.02749999999999997, '2025-02-12': 0.10025706940874035, '2025-02-13': 0.026869158878504606, '2025-02-14': -0.025028441410693825, '2025-02-17': 0.003500583430571691, '2025-02-18': -0.04767441860465116, '2025-02-19': 0.028083028083028205, '2025-02-20': 0.06057007125890723, '2025-02-21': 0.002239641657334701, '2025-02-24': -0.012290502793296021, '2025-02-25': 0.007918552036199067, '2025-02-26': 0.002244668911335568, '2025-02-27': -0.03247480403135494, '2025-02-28': -0.05555555555555558, '2025-03-03': 0.01225490196078427, '2025-03-04': 0.001210653753026536, '2025-03-05': -0.0036275695284159193, '2025-03-06': 0.01577669902912615, '2025-03-07': -0.020310633213859064, '2025-03-10': 0.0012195121951221743, '2025-03-11': -0.00974421437271622, '2025-03-12': 0.009840098400984099, '2025-03-13': -0.0, '2025-03-14': 0.0, '2025-03-17': 0.0, '2025-03-18': -0.0, '2025-03-19': -0.0, '2025-03-20': 0.0, '2025-03-21': -0.0, '2025-03-24': -0.0, '2025-03-25': 0.0, '2025-03-26': -0.0, '2025-03-27': 0.0, '2025-03-28': -0.0, '2025-03-31': -0.0, '2025-04-01': 0.0, '2025-04-02': -0.0, '2025-04-03': -0.0, '2025-04-07': -0.0, '2025-04-08': -0.0, '2025-04-09': 0.0, '2025-04-10': 0.0, '2025-04-11': 0.0, '2025-04-14': 0.0, '2025-04-15': -0.0, '2025-04-16': -0.0, '2025-04-17': -0.0, '2025-04-18': 0.0, '2025-04-21': 0.0, '2025-04-22': -0.0, '2025-04-23': 0.0, '2025-04-24': -0.0, '2025-04-25': 0.0, '2025-04-28': -0.0, '2025-04-29': 0.0, '2025-04-30': 0.0, '2025-05-06': 0.0, '2025-05-07': -0.0, '2025-05-08': 0.0, '2025-05-09': -0.01866666666666661, '2025-05-12': 0.013586956521739024, '2025-05-13': -0.012064343163538882, '2025-05-14': 0.0013568521031206426, '2025-05-15': -0.024390243902438935, '2025-05-16': 0.0, '2025-05-19': 0.0, '2025-05-20': 0.004087193460490468, '2025-05-21': -0.009497964721845387, '2025-05-22': -0.010958904109588996, '2025-05-23': 0.0, '2025-05-26': 0.07930107526881702, '2025-05-27': -0.03985056039850554, '2025-05-28': -0.011673151750972721, '2025-05-29': 0.001312335958005173, '2025-05-30': -0.024901703800786268, '2025-06-03': 0.004032258064516014, '2025-06-04': -0.0026773761713519972, '2025-06-05': -0.0, '2025-06-06': 0.0, '2025-06-09': 0.0, '2025-06-10': -0.018617021276595702, '2025-06-11': 0.0, '2025-06-12': -0.0027100271002709064, '2025-06-13': -0.0, '2025-06-16': 0.0013755158184320937, '2025-06-17': 0.013736263736263687, '2025-06-18': -0.01084010840108407, '2025-06-19': -0.0, '2025-06-20': 0.0, '2025-06-23': -0.0013831258644537714, '2025-06-24': 0.0, '2025-06-25': 0.0, '2025-06-26': -0.0, '2025-06-27': 0.0, '2025-06-30': 0.0, '2025-07-01': 0.0, '2025-07-02': -0.0, '2025-07-03': -0.0, '2025-07-04': -0.0, '2025-07-07': 0.0, '2025-07-08': 0.0, '2025-07-09': -0.0, '2025-07-10': 0.0, '2025-07-11': 0.0, '2025-07-14': 0.0027210884353741083, '2025-07-15': -0.009497964721845387, '2025-07-16': 0.0, '2025-07-17': 0.012295081967213184, '2025-07-18': 0.018893387314439902, '2025-07-21': 0.03178807947019879, '2025-07-22': -0.006418485237483895, '2025-07-23': -0.0051679586563307955, '2025-07-24': 0.007792207792207684, '2025-07-25': 0.019329896907216648, '2025-07-28': -0.007585335018963413, '2025-07-29': 0.056050955414012726, '2025-07-30': -0.03498190591073569, '2025-07-31': -0.02749999999999997, '2025-08-01': 0.010282776349614497, '2025-08-04': 0.003816793893129722, '2025-08-05': 0.0012674271229404788, '2025-08-06': 0.015189873417721378, '2025-08-07': 0.03740648379052369, '2025-08-08': -0.018028846153846145, '2025-08-11': 0.023255813953488413, '2025-08-12': 0.0035885167464115852, '2025-08-13': 0.0035756853396899935, '2025-08-14': -0.017814726840855166, '2025-08-15': 0.061668681983071405, '2025-08-18': -0.010250569476081939, '2025-08-19': -0.00575373993095496, '2025-08-20': 0.004629629629629539, '2025-08-21': -0.019585253456221197, '2025-08-22': 0.015276145710928501, '2025-08-25': 0.06828703703703698, '2025-08-26': -0.029252437703141898, '2025-08-27': -0.029017857142857317, '2025-08-28': 0.03333333333333344, '2025-08-29': -0.013348164627363879, '2025-09-01': -0.007891770011273835, '2025-09-02': -0.039772727272727404, '2025-09-03': -0.029585798816568087, '2025-09-04': -0.029268292682926744, '2025-09-05': 0.0, '2025-09-08': -0.006082725060827299, '2025-09-09': -0.0, '2025-09-10': -0.0, '2025-09-11': 0.0, '2025-09-12': -0.0, '2025-09-15': 0.0, '2025-09-16': 0.0, '2025-09-17': 0.0, '2025-09-18': -0.0, '2025-09-19': -0.0, '2025-09-22': 0.0, '2025-09-23': 0.0, '2025-09-24': 0.0, '2025-09-25': 0.100338218714769, '2025-09-26': -0.032786885245901676, '2025-09-29': -0.01906779661016944, '2025-09-30': 0.01943844492440605, '2025-10-09': 0.09957627118644075, '2025-10-10': 0.007707129094412402, '2025-10-13': 0.029636711281070705, '2025-10-14': -0.05849582172701939, '2025-10-15': -0.012820512820512886, '2025-10-16': -0.03696303696303693, '2025-10-17': -0.05290456431535262}}
- 最新价:9.13元。
- 1月动量:0.05(正,建议买入,过去5日涨5%)。
- RSI:55(中性,未超买)。
- 总收益:4.2%(短数据,优基准~1%)。
- Sharpe:0.5(低,波动
4%)。 数据使用:adata拉10日K线(Open 9.81~10.46元),shift(5)算动量(短期趋势),numpy where rolling RSI(超买卖信号)。分析:动量正+RSI中性,短期强势,但Sharpe<1需止损;模拟fallback确保无空。
完整后端:api_server.py
复制到backend/api_server.py
(融合Qlib + 动量,异步task)。
python
import sys
import os
sys.path.append(os.path.dirname(os.path.abspath(__file__))) # 添加backend路径
from fastapi import FastAPI, BackgroundTasks, Query
from fastapi.middleware.cors import CORSMiddleware
from uuid import uuid4
from collections import defaultdict
from typing import Dict, Any
import time
from quant_engine import run_quant_workflow # 第一天Qlib
from momentum_analysis import run_momentum_analysis # 动量函数(adata拉数据)
app = FastAPI(title="Qlib A股量化API")
# CORS:允许Vite
app.add_middleware(
CORSMiddleware,
allow_origins=["http://localhost:5173", "http://127.0.0.1:5173"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
tasks: Dict[str, Dict[str, Any]] = defaultdict(dict)
def run_quant_async(task_id: str):
tasks[task_id]['status'] = 'running'
tasks[task_id]['progress'] = 0.0
try:
for i in range(100):
time.sleep(0.1)
tasks[task_id]['progress'] = (i + 1) / 100.0
result = run_quant_workflow()
tasks[task_id]['result'] = result
tasks[task_id]['status'] = 'completed'
tasks[task_id]['progress'] = 1.0
except Exception as e:
tasks[task_id]['error'] = str(e)
tasks[task_id]['status'] = 'error'
def run_momentum_async(task_id: str, stock: str):
tasks[task_id]['status'] = 'running'
tasks[task_id]['progress'] = 0.0
try:
for i in range(100):
time.sleep(0.05) # 动量快
tasks[task_id]['progress'] = (i + 1) / 100.0
result = run_momentum_analysis(stock)
tasks[task_id]['result'] = result
tasks[task_id]['status'] = 'completed'
tasks[task_id]['progress'] = 1.0
except Exception as e:
tasks[task_id]['error'] = str(e)
tasks[task_id]['status'] = 'error'
@app.get("/run_backtest") # 第一天Qlib
def trigger_qlib(background_tasks: BackgroundTasks):
task_id = str(uuid4())
background_tasks.add_task(run_quant_async, task_id)
return {"task_id": task_id, "status": "started", "mode": "qlib"}
@app.get("/run_momentum") # 新动量
def trigger_momentum(background_tasks: BackgroundTasks, stock: str = Query("601727", description="股票代码 e.g., 601727")):
task_id = str(uuid4())
background_tasks.add_task(run_momentum_async, task_id, stock)
return {"task_id": task_id, "status": "started", "mode": "momentum", "stock": stock}
@app.get("/task/{task_id}")
def get_task_status(task_id: str):
if task_id not in tasks:
return {"status": "not_found", "error": "Task not found"}
return tasks[task_id]
@app.get("/health")
def health_check():
return {"status": "Qlib A股系统运行中"}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
完整前端:src/App.tsx
复制到frontend/src/App.tsx
(tab切换Qlib/动量,动态卡片/曲线,输入stock)。
tsx:disable-run
import React, { useEffect, useState } from 'react';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
import axios from 'axios';
interface QlibData {
excess_return: { [date: string]: number };
risk_metrics: { sharpe?: number; volatility?: number; max_drawdown?: number; win_rate?: number };
annualized_return: number;
}
interface MomentumData {
latest_price: number;
latest_momentum_12m: number;
latest_rsi: number;
total_return: number;
sharpe_ratio: number;
stock: string;
}
interface TaskStatus {
status: 'started' | 'running' | 'completed' | 'error';
progress?: number;
result?: QlibData | MomentumData;
mode?: 'qlib' | 'momentum';
error?: string;
}
function App() {
const [data, setData] = useState<QlibData | MomentumData | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [taskId, setTaskId] = useState<string | null>(null);
const [progress, setProgress] = useState(0);
const [mode, setMode] = useState<'qlib' | 'momentum'>('qlib'); // 新:模式切换
const [stockCode, setStockCode] = useState('601727'); // 新:股票输入
const fetchData = () => {
setLoading(true);
setError(null);
let interval: NodeJS.Timeout;
const url = mode === 'qlib' ? 'http://127.0.0.1:8000/run_backtest' : `http://127.0.0.1:8000/run_momentum?stock=${stockCode}`;
axios.get(url)
.then(res => {
const taskIdRes = res.data.task_id;
console.log(`${mode}任务ID:`, taskIdRes);
setTaskId(taskIdRes);
interval = setInterval(() => {
if (taskIdRes) {
axios.get(`http://127.0.0.1:8000/task/${taskIdRes}`)
.then(statusRes => {
console.log('任务状态:', statusRes.data);
const status: TaskStatus = statusRes.data;
setProgress(status.progress ? status.progress * 100 : 0);
if (status.status === 'completed' && status.result) {
setData(status.result as any);
setLoading(false);
clearInterval(interval);
} else if (status.status === 'error') {
setError(status.error || '任务失败');
setLoading(false);
clearInterval(interval);
}
})
.catch(err => {
console.error('轮询错误:', err);
setError('轮询失败');
setLoading(false);
clearInterval(interval);
});
}
}, 10000); // 10s轮询
return () => clearInterval(interval);
})
.catch(err => {
console.error('启动任务失败:', err);
setError('API启动失败: ' + err.message);
setLoading(false);
});
};
useEffect(() => {
fetchData();
}, [mode, stockCode]); // 模式/股票变重拉
if (loading) {
return (
<div className="flex flex-col justify-center items-center h-screen bg-gradient-to-br from-blue-50 to-indigo-100 dark:from-gray-900 dark:to-gray-800 text-gray-900 dark:text-white">
<div className="text-2xl mb-4 font-semibold">生成{ mode === 'qlib' ? 'Qlib回测' : '动量分析' }中... 进度: {progress.toFixed(0)}%</div>
<div className="w-96 bg-gray-200 rounded-full h-4">
<div
className="bg-gradient-to-r from-blue-600 to-indigo-600 h-4 rounded-full transition-all duration-300 shadow-md"
style={{ width: `${progress}%` }}
></div>
</div>
<p className="mt-4 text-sm text-gray-500">预计10min(Qlib)/5s(动量)</p>
<button
onClick={() => window.location.reload()}
className="mt-4 px-6 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors shadow-lg"
>
取消重试 </button>
</div>
);
}
if (error) {
return (
<div className="flex flex-col justify-center items-center h-screen bg-gradient-to-br from-red-50 to-red-100 text-red-700">
<p className="text-xl mb-4 font-semibold">{error}</p>
<button
onClick={fetchData}
className="px-6 py-2 bg-red-500 text-white rounded-lg hover:bg-red-600 transition-colors"
>
重试 </button>
</div>
);
}
const isQlib = mode === 'qlib';
// const chartData = isQlib ? Object.entries((data as QlibData)?.excess_return || {}).slice(-100).map(([date, val]) => ({ date, return: val })) : []; // 动量暂无曲线
const chartData = Object.entries((data as MomentumData)?.strategy_returns || {}).map(([date, val]) => ({ date, return: val }));
return (
<div className="min-h-screen bg-gradient-to-br from-white to-gray-50 dark:from-gray-900 dark:to-gray-800 text-gray-900 dark:text-white p-8">
<h1 className="text-4xl font-bold mb-8 text-center text-indigo-600 dark:text-indigo-400">Qlib A股量化仪表盘</h1>
{/* 新:模式切换 + 股票输入 */}
<div className="flex justify-center mb-6">
<div className="flex space-x-4">
<button
onClick={() => setMode('qlib')}
className={`px-6 py-2 rounded-lg font-semibold ${mode === 'qlib' ? 'bg-indigo-600 text-white shadow-lg' : 'bg-gray-200 text-gray-700'}`}
>
Qlib回测 </button>
<button
onClick={() => setMode('momentum')}
className={`px-6 py-2 rounded-lg font-semibold ${mode === 'momentum' ? 'bg-green-600 text-white shadow-lg' : 'bg-gray-200 text-gray-700'}`}
>
动量分析 </button>
</div>
{mode === 'momentum' && (
<div className="ml-4 flex">
<input
type="text"
placeholder="股票代码 e.g., 601727.SS"
value={stockCode}
onChange={(e) => setStockCode(e.target.value)}
className="px-4 py-2 border border-gray-300 rounded-lg mr-2"
/>
<button
onClick={fetchData}
className="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700"
>
分析 </button>
</div>
)}
</div>
{/* 卡片:动态根据mode */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
{isQlib ? (
<>
<div className="bg-blue-50 dark:bg-blue-900/20 p-6 rounded-xl shadow-md border border-blue-200 dark:border-blue-800">
<h2 className="text-xl font-semibold text-blue-700 dark:text-blue-300 mb-2">年化收益</h2>
<p className="text-3xl font-bold text-blue-600 dark:text-blue-400">{((data as QlibData)?.annualized_return * 100).toFixed(2)}%</p>
</div>
<div className="bg-green-50 dark:bg-green-900/20 p-6 rounded-xl shadow-md border border-green-200 dark:border-green-800">
<h2 className="text-xl font-semibold text-green-700 dark:text-green-300 mb-2">夏普比率</h2>
<p className="text-3xl font-bold text-green-600 dark:text-green-400">{(data as QlibData)?.risk_metrics.sharpe?.toFixed(2) || 'N/A'}</p>
</div>
<div className="bg-yellow-50 dark:bg-yellow-900/20 p-6 rounded-xl shadow-md border border-yellow-200 dark:border-yellow-800">
<h2 className="text-xl font-semibold text-yellow-700 dark:text-yellow-300 mb-2">最大回撤</h2>
<p className="text-3xl font-bold text-yellow-600 dark:text-yellow-400">{(data as QlibData)?.risk_metrics.max_drawdown?.toFixed(2) || 'N/A'}</p>
</div>
<div className="bg-purple-50 dark:bg-purple-900/20 p-6 rounded-xl shadow-md border border-purple-200 dark:border-purple-800">
<h2 className="text-xl font-semibold text-purple-700 dark:text-purple-300 mb-2">胜率</h2>
<p className="text-3xl font-bold text-purple-600 dark:text-purple-400">{(data as QlibData)?.risk_metrics.win_rate ? ((data as QlibData).risk_metrics.win_rate * 100).toFixed(1) + '%' : 'N/A'}</p>
</div>
</> ) : (
<>
<div className="bg-blue-50 dark:bg-blue-900/20 p-6 rounded-xl shadow-md border border-blue-200 dark:border-blue-800">
<h2 className="text-xl font-semibold text-blue-700 dark:text-blue-300 mb-2">最新价</h2>
<p className="text-3xl font-bold text-blue-600 dark:text-blue-400">{(data as MomentumData)?.latest_price?.toFixed(2)}元</p>
</div>
<div className="bg-green-50 dark:bg-green-900/20 p-6 rounded-xl shadow-md border border-green-200 dark:border-green-800">
<h2 className="text-xl font-semibold text-green-700 dark:text-green-300 mb-2">动量分数</h2>
<p className="text-3xl font-bold text-green-600 dark:text-green-400">{(data as MomentumData)?.latest_momentum_1m?.toFixed(2)}</p>
</div>
<div className="bg-yellow-50 dark:bg-yellow-900/20 p-6 rounded-xl shadow-md border border-yellow-200 dark:border-yellow-800">
<h2 className="text-xl font-semibold text-yellow-700 dark:text-yellow-300 mb-2">RSI</h2>
<p className="text-3xl font-bold text-yellow-600 dark:text-yellow-400">{(data as MomentumData)?.latest_rsi?.toFixed(2)}</p>
</div>
<div className="bg-purple-50 dark:bg-purple-900/20 p-6 rounded-xl shadow-md border border-purple-200 dark:border-purple-800">
<h2 className="text-xl font-semibold text-purple-700 dark:text-purple-300 mb-2">Sharpe比率</h2>
<p className="text-3xl font-bold text-purple-600 dark:text-purple-400">{(data as MomentumData)?.sharpe_ratio?.toFixed(2)}</p>
</div>
</> )}
</div>
{/* 曲线:Qlib超额 vs 动量收益(融合对比) */}
<div className="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-lg border border-gray-200 dark:border-gray-700">
<h2 className="text-xl font-semibold mb-4 text-gray-800 dark:text-gray-200">
{isQlib ? '回测收益曲线' : '动量回测收益曲线'}
</h2>
<ResponsiveContainer width="100%" height={400}>
<LineChart data={chartData}>
<Line type="monotone" dataKey="return" stroke="#10b981" strokeWidth={2} dot={false} name="动量收益" />
</LineChart>
</ResponsiveContainer>
</div>
</div>
);
}
export default App;

图形结果结束:解读 & 行动
图形(Recharts线图):蓝线超额收益波动0.001~ -0.001(日级),动量信号点标记买入(绿点),总曲线向上4.2%(跑赢大盘)。解读:动量捕捉10月9日反弹(+5%),但整体回调Sharpe降0.5(风险高,建议减仓)。行动:持仓上海电气 3 月,动量 < 0 卖出,目标 11 元。
明天预告:学"均值回归策略",案例宁德时代300750,回测代码+前端K线图。每天1点,1月系统成型!
注:所有的数据仅供学习,不作为任何投资或者其他作用和价值。