Initial commit: cc-cursor 全链路量化研究平台

7 Sprints 全部完成:
  Sprint 0: 基础设施 (DataManager + MariaDB)
  Sprint 1: 因子引擎 (34因子/12分类)
  Sprint 2: VectorBT 回测 (5策略+截面)
  Sprint 3: Optuna 优化 (+Walk-Forward)
  Sprint 4: ML 模型 (LightGBM+CatBoost)
  Sprint 5: Qwen 情绪因子 (三源新闻+日期对齐)
  Sprint 6: Agent 系统 (4Agent+日报.md/.html)

生产加固 (15项): Tushare双源fallback, SSH自动恢复, pool_pre_ping,
  save_daily先删后插, load_dotenv绝对路径, 日报5d/20d修复,
  RiskAgent改上证指数, 昨日对比+数据截止, mac_report utf8mb4,
  CLAUDE-*.md 9条已知Bug, demo全参数化, djapi数据源归一化,
  indexDatas API修正

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-06-07 15:59:05 +08:00
co-authored by Claude Opus 4.7
commit 271a9343a5
293 changed files with 59598 additions and 0 deletions
+260
View File
@@ -0,0 +1,260 @@
import sys
from django.shortcuts import render
from django.http import HttpResponse
from rest_framework.decorators import api_view
from rest_framework.response import Response
from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiTypes
from .stock.stock_utils import viewFunc_tsCodeAndDate, viewFunc_singleParam
from .stock.stock_basic import getStockBasic, getStockListByIndustry, getStockInfo
from .stock.getStockParam import getStockParam
from .stock.getStockEp import getStockEp_ttm, get_quarterly_eps
from .stock.getIndexs import get_index_daily_data, get_index_by_name
from .stock.stockMargin import getStockMargin, getDailyMargin
from .stock.getStockFina import get_finance_data_range
from .stock.getStockDiv2 import analyze_stock_dividend_and_price
from .stock.xwlbDaily import get_xwlb, get_xwlb_fine
from .stock.getDivData_AK import get_akshare_dividend_yield
from .serializers import (
StockDailySerializer, StockInfoSerializer, IndustryStockSerializer,
StockParamSerializer, StockEpSerializer, QuarterlyEpsSerializer,
IndexInfoSerializer, IndexDailySerializer, MarginDailySerializer,
StockMarginSerializer, FinanceDataSerializer, DividendSerializer,
XwlbNewsSerializer,
)
# === 通用参数定义(复用) ===
_PARAM_TSCODE = OpenApiParameter(name='tscode', type=str, default='000001.SZ',
description='股票代码,如 000001.SZ')
_PARAM_INDEX_CODE = OpenApiParameter(name='tscode', type=str, default='000001.SH',
description='指数代码,如 000001.SH=上证指数, 399001.SZ=深证成指, 399006.SZ=创业板指')
_PARAM_START = OpenApiParameter(name='start_date', type=str, default='20200101',
description='起始日期 yyyyMMdd')
_PARAM_END = OpenApiParameter(name='end_date', type=str, default='20251231',
description='结束日期 yyyyMMdd')
_PARAM_INDEX_NAME = OpenApiParameter(name='index_name', type=str, default='沪深300',
description='指数名称,如 沪深300、上证50')
_PARAM_INDUSTRY = OpenApiParameter(name='industry', type=str, required=True,
description='行业名称,如 银行、半导体')
_PARAM_TRADE_DATE = OpenApiParameter(name='trade_date', type=str,
description='交易日期 yyyyMMdd')
_PARAM_EXCHANGE_ID = OpenApiParameter(name='exchange_id', type=str,
description='交易所代码 SSE/SZSE')
@extend_schema(
responses={200: OpenApiTypes.STR},
description='返回服务器 Python 版本',
tags=['系统'],
)
@api_view(['GET'])
def python_version(request):
return HttpResponse(f"Python Version: {sys.version}")
@extend_schema(exclude=True)
@api_view(['GET'])
def home(request):
return render(request, 'home.html')
@extend_schema(
parameters=[_PARAM_TSCODE, _PARAM_START, _PARAM_END],
responses={200: StockDailySerializer(many=True)},
description='获取个股日线行情数据(开高低收、成交量、成交额)',
tags=['行情'],
)
@api_view(['GET'])
def stockbasic(request):
return viewFunc_tsCodeAndDate(request, getStockBasic)
@extend_schema(
parameters=[_PARAM_INDUSTRY],
responses={200: IndustryStockSerializer(many=True)},
description='按申万行业分类查询成分股列表',
tags=['基础数据'],
)
@api_view(['GET'])
def industrys(request):
return viewFunc_singleParam(request, getStockListByIndustry, param_name='industry')
@extend_schema(
parameters=[_PARAM_TSCODE],
responses={200: StockInfoSerializer()},
description='获取个股基本信息(名称、行业、上市日期、交易所等)',
tags=['基础数据'],
)
@api_view(['GET'])
def stockInfo(request):
return viewFunc_singleParam(request, getStockInfo, param_name='tscode')
@extend_schema(
parameters=[_PARAM_TSCODE, _PARAM_START, _PARAM_END],
responses={200: StockParamSerializer(many=True)},
description='获取个股每日参数(市值、PE/PB/PS、换手率等)',
tags=['行情'],
)
@api_view(['GET'])
def stockparam(request):
return viewFunc_tsCodeAndDate(request, getStockParam)
@extend_schema(
parameters=[_PARAM_TSCODE, _PARAM_START, _PARAM_END],
responses={200: StockEpSerializer(many=True)},
description='获取个股 TTM 每股收益(EPS',
tags=['财务'],
)
@api_view(['GET'])
def stockep(request):
return viewFunc_tsCodeAndDate(request, getStockEp_ttm)
@extend_schema(
parameters=[_PARAM_TSCODE, _PARAM_START, _PARAM_END],
responses={200: QuarterlyEpsSerializer(many=True)},
description='获取个股季度每股收益(EPS),按财报日期对齐',
tags=['财务'],
)
@api_view(['GET'])
def quarterlyEps(request):
return viewFunc_tsCodeAndDate(request, get_quarterly_eps)
@extend_schema(
parameters=[_PARAM_INDEX_NAME],
responses={200: IndexInfoSerializer()},
description='按名称模糊查询指数基本信息',
tags=['指数'],
)
@api_view(['GET'])
def indexByName(request):
index_name = request.GET.get('index_name', '沪深300')
if not index_name:
return Response({'error': '缺少 index_name 参数'}, status=400)
try:
data = get_index_by_name(index_name)
dict_data = data.to_dict(orient='records')
return Response(dict_data)
except ImportError:
return Response({'error': '模块不存在'}, status=500)
except Exception as e:
return Response({'error': str(e)}, status=500)
@extend_schema(
parameters=[_PARAM_INDEX_CODE, _PARAM_START, _PARAM_END],
responses={200: IndexDailySerializer(many=True)},
description='获取指数日线行情数据(含 PE/PB/市值/换手率等扩展指标)',
tags=['指数'],
)
@api_view(['GET'])
def indexDatas(request):
return viewFunc_tsCodeAndDate(request, get_index_daily_data)
@extend_schema(
parameters=[_PARAM_TSCODE, _PARAM_START, _PARAM_END],
responses={200: StockMarginSerializer(many=True)},
description='获取个股融资融券明细数据',
tags=['融资融券'],
)
@api_view(['GET'])
def stockMargin(request):
return viewFunc_tsCodeAndDate(request, getStockMargin)
@extend_schema(
parameters=[_PARAM_TRADE_DATE, _PARAM_START, _PARAM_END, _PARAM_EXCHANGE_ID],
responses={200: MarginDailySerializer(many=True)},
description='获取每日融资融券汇总数据(按交易所)',
tags=['融资融券'],
)
@api_view(['GET'])
def dailyMargin(request):
trade_date = request.GET.get('trade_date', None)
start_date = request.GET.get('start_date', None)
end_date = request.GET.get('end_date', None)
exchange_id = request.GET.get('exchange_id', None)
try:
data = getDailyMargin(trade_date=trade_date, start_date=start_date,
end_date=end_date, exchange_id=exchange_id)
dict_data = data.to_dict(orient='records')
return Response(dict_data)
except ImportError:
return Response({'error': '模块不存在'}, status=500)
except Exception as e:
return Response({'error': str(e)}, status=500)
@extend_schema(
parameters=[_PARAM_TSCODE, _PARAM_START, _PARAM_END],
responses={200: FinanceDataSerializer(many=True)},
description='获取个股财务报表分析数据(资产负债表+利润表+现金流,含运营/资产/负债/回报率指标)',
tags=['财务'],
)
@api_view(['GET'])
def getFinaData(request):
return viewFunc_tsCodeAndDate(request, get_finance_data_range)
@extend_schema(
parameters=[_PARAM_TSCODE, _PARAM_START, _PARAM_END],
responses={200: DividendSerializer(many=True)},
description='获取个股股息率数据(含 TTM 分红、收盘价、股息率)',
tags=['分红'],
)
@api_view(['GET'])
def getDivData(request):
return viewFunc_tsCodeAndDate(request, analyze_stock_dividend_and_price)
@extend_schema(
parameters=[_PARAM_TSCODE, _PARAM_START, _PARAM_END],
responses={200: DividendSerializer(many=True)},
description='获取个股股息率数据(akshare 数据源,无需 token',
tags=['分红'],
)
@api_view(['GET'])
def getDivDataAkshare(request):
return viewFunc_tsCodeAndDate(request, get_akshare_dividend_yield)
def _xwlb_view(request, data_func):
start_date = request.GET.get('start_date', None)
end_date = request.GET.get('end_date', None)
try:
data = data_func(start_date=start_date, end_date=end_date)
dict_data = data.to_dict(orient='records')
status = "success" if dict_data else "Error"
return Response({"status": status, "data": {"news": dict_data}})
except ImportError:
return Response({'error': '模块不存在'}, status=500)
except Exception as e:
return Response({'error': str(e)}, status=500)
@extend_schema(
parameters=[_PARAM_START, _PARAM_END],
responses={200: XwlbNewsSerializer(many=True)},
description='获取新闻联播原始识别文本(ASR 转写结果)',
tags=['新闻联播'],
)
@api_view(['GET'])
def xwlbNews(request):
return _xwlb_view(request, get_xwlb)
@extend_schema(
parameters=[_PARAM_START, _PARAM_END],
responses={200: XwlbNewsSerializer(many=True)},
description='获取新闻联播精编内容(AI 分割+标题提取后的独立新闻)',
tags=['新闻联播'],
)
@api_view(['GET'])
def xwlbFine(request):
return _xwlb_view(request, get_xwlb_fine)