Step by step股票分析-系统工程
设计功能(xmind):
数据处理流程(Graphviz)

页面设置
第0页 【版块/热点/技术分析/机会分析】
功能设计:
1.按不同的版块/概念分类标准,实时统计当前各版块/概念的状态(涨幅/资金流动/换手/与大盘的关联性等)
https://blog.csdn.net/hzether/article/details/146106733
https://blog.csdn.net/xiaoliouc/article/details/138266559
数据来源:
akshare获取每日热门趋势板块以及股票
https://blog.csdn.net/LML9310/article/details/135479304
衡量市场情绪

第1页 【关注股票的技术/机会/风险分析】
功能设计:
1.根据不同的技术分析指标,自动提取符合要求的股票列表;
2.定向分析关注股票在一定期间的波动率,换手率;
3.个股在一定区间内的走势统计,起点,低点,高点,区间波动率,区间涨幅,所在版块及位置,与大盘的关联度;
4.极端行情分析(超买/超卖)
超买超卖指标
随机指标(KDJ)由 George C.Lane 创制。它综合了动量观念、强弱指标及移动平均线的优点,用来度量股价脱离价格正常范围的变异程度。
KDJ指标考虑的不仅是收盘价,而且有近期的最高价和最低价,这避免了仅考虑收盘价而忽视真正波动幅度的弱点。
(一)、计算公式
公式的推导过程如下所示:
AX=当前收盘价-9天以来的最低价
BX=9天以来的最高价-9天以来的最低价
RSV=AX/BX×100
K=2/3×前一日K+1/3×RSV D=2/3×前一日D+1/3×K J=3D-2K
第一次计算时,前一日的KD皆以50代替 随机指数以K,D,J三个符号表示。
(二)、应用原则:
1、当白色的K值在50以下的低水平,形成一底比一底高的现象,并且K值由下向上连续两次交叉黄色的D值时,股价会产生较大的涨幅。

2、当白色的K值在50以上的高水平,形成一顶比一顶低的现象,并且K值由上向下连续两次交叉黄色的D值时,股价会产生较大的跌幅。

3、白色的K线由下向上交叉黄色的D线失败转而向下探底后,K线再次向上交叉D线,两线所夹的空间叫做向上反转风洞。如上图所示,当出现向上反转风洞时股价将上涨。如下图所示,反之叫做向下反转风洞。出现向下反转风洞时股价将下跌。

4、K值大于80,短期内股价容易向下出现回档;K值小于20,短期内股价容易向上出现反弹;但在极强、极弱行情中K、D指标会在超买、超卖区内徘徊,此时应参考VR、ROC指标以确定走势的强弱。

5、在常态行情中,D值大于80后股价经常向下回跌;D值小于20后股价易于回升。

在极端行情中,D值大于90,股价易产生瞬间回档;D值小于15,股价易产生瞬间反弹(见下图)。这种瞬间回档或反弹不代表行情已经反转。

6、J值信号不常出现,可是一旦出现,可靠性相当高。当J值大于100时,股价会形成头部而出现回落;J值小于0时,股价会形成底部而产生反弹。

第2页 【盯盘及预测分析】
功能设计:
1.个股与指数的收益对比


数据来源:
第3页 【关注股票的财务分析】
功能设计:
数据来源:
1.从新浪获取财务数据:https://zhuanlan.zhihu.com/p/639730857
经过测试,我们发现网址中的callback参数可以去除,不影响返回的数据结果,于是我们得到了所真正需要的URL:
资产负债表:https://quotes.sina.cn/cn/api/openapi.php/CompanyFinanceService.getFinanceReport2022?paperCode=sz300146&source=fzb&type=4&page=1&num=10
利润表:https://quotes.sina.cn/cn/api/openapi.php/CompanyFinanceService.getFinanceReport2022?paperCode=sz300146&source=lrb&type=4&page=1&num=10
现金流量表:https://quotes.sina.cn/cn/api/openapi.php/CompanyFinanceService.getFinanceReport2022?paperCode=sz300146&source=llb&type=4&page=1&num=10
在浏览器中打开上面三个链接,发现返回的都是json数据:
第4页 【投资/持仓分析】
市场情绪:
1.按不同的版块/概念分类标准,实时统计当前各版块/概念的状态(涨幅/资金流动/换手/等)
https://blog.csdn.net/hzether/article/details/146106733
https://blog.csdn.net/xiaoliouc/article/details/138266559
仓位建议:
第5页 【历史回溯分析】
交易历史:
胜率分析
https://tushare.pro/document/search?q=机器量化分析
持仓建议
模型评估与仓位管理:https://tushare.pro/document/1?doc_id=68
数据库搭建
数据库的设计思路(db_readme.md)
数据库需要考虑以下几个方面:
数据库及数据表设计:包括:
- 基础信息库:包括股票基本信息、行业分类信息,概念分类信息(可能有不同的行业分类或概念分类标准);
- 交易数据库:包括每年/月/周/日交易数据;
- 分时数据库:包括每只股票在不同日的分时数据;
- K线数据库:包括每年/月/周/日K线交易数据等;
- 财务数据库:包括每支股票在不同时段的财务数据。
以及 - 数据表结构信息表:记录每个数据表的结构信息,最好能依据这个表来建立相应功能的表。
- 数据表存储信息表:用来记录当前数据库中所有表中已经保存的股票或代码的信息内容,方便调用前查阅数据库中有无相关数据记录,如果没有,就实时去接口拿,如果有,就直接调用。
- 数据库变动信息表:记录每次数据库调用的/修改的情况。
股票实时数据引入
easyquotation 接口,提供新浪/腾讯实时报价,A股/港股
easyquotation 接口范例,点击查看代码
项目地址:https://github.com/shidenggui/easyquotation
代码范例:
# -*- coding: utf-8 -*-
"""
Created on Sun Mar 9 12:15:55 2025
@author: John Cheng
https://github.com/shidenggui/easyquotation
"""
import easyquotation
import pandas as pd
#指定数据源: 新浪 ['sina'] 腾讯 ['tencent', 'qq']
quotation = easyquotation.use('sina')
# 股票单支查询, 支持直接指定前缀,如 'sh000001'
data = quotation.real('162411')
# 多只股票查询
data2 = quotation.real(['000001','000700'])
df = pd.DataFrame(data2)
#同时获取指数和股票
data3 = quotation.stocks(['sh000001', 'sz000001'], prefix=True)
# prefix 参数指定返回的行情字典中的股票代码 key 是否带 sz/sh 前缀
data4 = quotation.market_snapshot(prefix=True)
#腾讯分时图
quotation = easyquotation.use("timekline")
data = quotation.real(['603828'], prefix=True)
数据库设计
用excel文件保存数据库信息,点击查看代码
# -*- coding: utf-8 -*-
"""
Created on Sat Mar 15 19:13:01 2025
@author: CHENGJUN
代码说明(感谢deepseek):
运行代码后,会生成一个名为 stock_database_design.xlsx 的 Excel 文件,包含以下内容:
索引页:包含所有 Sheet 的中文名和英文名。英文名带有超链接,点击可以跳转到对应的 Sheet。列宽自适应。
数据表 Sheet:每个 Sheet 的列宽自适应,同时考虑列名和列内容的宽度,显示清晰。
优点
代码简化:通过遍历和动态生成,减少了重复代码。
通用性增强:如果需要新增表,只需在 index_data 和 table_structures 中添加对应的信息即可。
索引页优先:索引页在最前面创建,方便后续动态生成数据表结构。
动态生成数据表结构:使用索引页的英文名称从 table_structures 字典中动态获取表结构。
遍历写入 Excel 文件:使用 for 循环遍历索引页的英文名称,动态写入对应的 Sheet。
列宽自适应:遍历每个 Sheet 的列,计算列名和列内容的最大长度,并设置列宽为 max(列名长度, 列内容长度) + 2。
超链接:在索引页中为英文名添加超链接,点击可以跳转到对应的 Sheet。
"""
import pandas as pd
# 1. 创建索引页
index_data = {
"序号": [1, 2, 3, 4, 5],
"中文名": ["股票基本信息表", "每日交易数据表", "分时数据表", "K线数据表", "数据库变动信息表"],
"英文名": ["stock_info", "daily_trade_data", "intraday_trade_data", "kline_data", "database_change_log"]
}
df_index = pd.DataFrame(index_data)
# 2. 定义数据表结构(通过索引页的英文名称动态生成)
table_structures = {
"stock_info": {
"中文列名": ["股票ID", "股票代码", "股票名称", "市场", "行业", "上市日期"],
"英文列名": ["stock_id", "stock_code", "stock_name", "market", "industry", "listing_date"],
"数据类型": ["INT", "VARCHAR", "VARCHAR", "VARCHAR", "VARCHAR", "DATE"],
"数据长度": ["-", "10", "50", "10", "50", "-"],
"是否主键": ["是", "否", "否", "否", "否", "否"],
"是否外键": ["否", "否", "否", "否", "否", "否"],
"默认值": ["-", "-", "-", "-", "-", "-"],
"描述": ["股票的唯一标识", "股票代码", "股票名称", "所属市场", "所属行业", "股票上市日期"]
},
"daily_trade_data": {
"中文列名": ["交易ID", "股票ID", "交易日期", "开盘价", "收盘价", "最高价", "最低价", "成交量", "成交额"],
"英文列名": ["trade_id", "stock_id", "trade_date", "open_price", "close_price", "high_price", "low_price", "volume", "turnover"],
"数据类型": ["INT", "INT", "DATE", "DECIMAL", "DECIMAL", "DECIMAL", "DECIMAL", "BIGINT", "DECIMAL"],
"数据长度": ["-", "-", "-", "10,2", "10,2", "10,2", "10,2", "-", "15,2"],
"是否主键": ["是", "否", "否", "否", "否", "否", "否", "否", "否"],
"是否外键": ["否", "是", "否", "否", "否", "否", "否", "否", "否"],
"默认值": ["-", "-", "-", "-", "-", "-", "-", "-", "-"],
"描述": ["交易记录的唯一标识", "关联股票信息表", "交易日期", "开盘价", "收盘价", "最高价", "最低价", "成交量", "成交额"]
},
"intraday_trade_data": {
"中文列名": ["分时数据ID", "股票ID", "交易日期", "交易时间", "价格", "成交量", "成交额"],
"英文列名": ["intraday_id", "stock_id", "trade_date", "trade_time", "price", "volume", "turnover"],
"数据类型": ["INT", "INT", "DATE", "TIME", "DECIMAL", "INT", "DECIMAL"],
"数据长度": ["-", "-", "-", "-", "10,2", "-", "15,2"],
"是否主键": ["是", "否", "否", "否", "否", "否", "否"],
"是否外键": ["否", "是", "否", "否", "否", "否", "否"],
"默认值": ["-", "-", "-", "-", "-", "-", "-"],
"描述": ["分时数据的唯一标识", "关联股票信息表", "交易日期", "交易时间", "交易价格", "成交量", "成交额"]
},
"kline_data": {
"中文列名": ["K线数据ID", "股票ID", "K线类型", "开始日期", "结束日期", "开盘价", "收盘价", "最高价", "最低价", "成交量", "成交额"],
"英文列名": ["kline_id", "stock_id", "kline_type", "start_date", "end_date", "open_price", "close_price", "high_price", "low_price", "volume", "turnover"],
"数据类型": ["INT", "INT", "VARCHAR", "DATE", "DATE", "DECIMAL", "DECIMAL", "DECIMAL", "DECIMAL", "BIGINT", "DECIMAL"],
"数据长度": ["-", "-", "10", "-", "-", "10,2", "10,2", "10,2", "10,2", "-", "15,2"],
"是否主键": ["是", "否", "否", "否", "否", "否", "否", "否", "否", "否", "否"],
"是否外键": ["否", "是", "否", "否", "否", "否", "否", "否", "否", "否", "否"],
"默认值": ["-", "-", "-", "-", "-", "-", "-", "-", "-", "-", "-"],
"描述": ["K线数据的唯一标识", "关联股票信息表", "K线类型(日K、周K等)", "K线开始日期", "K线结束日期", "开盘价", "收盘价", "最高价", "最低价", "成交量", "成交额"]
},
"database_change_log": {
"中文列名": ["日志ID", "变更时间", "变更类型", "表名", "记录ID", "操作用户", "旧数据", "新数据"],
"英文列名": ["log_id", "change_time", "change_type", "table_name", "record_id", "user", "old_data", "new_data"],
"数据类型": ["INT", "TIMESTAMP", "VARCHAR", "VARCHAR", "INT", "VARCHAR", "JSON", "JSON"],
"数据长度": ["-", "-", "20", "50", "-", "50", "-", "-"],
"是否主键": ["是", "否", "否", "否", "否", "否", "否", "否"],
"是否外键": ["否", "否", "否", "否", "否", "否", "否", "否"],
"默认值": ["-", "CURRENT_TIMESTAMP", "-", "-", "-", "-", "-", "-"],
"描述": ["日志的唯一标识", "操作时间", "操作类型(INSERT/UPDATE/DELETE)", "操作的表名", "操作的记录ID", "操作用户", "旧数据(仅UPDATE/DELETE)", "新数据(仅INSERT/UPDATE)"]
}
}
# 3. 将字典转换为 DataFrame
dataframes = {}
for table_name in index_data["英文名"]:
dataframes[table_name] = pd.DataFrame(table_structures[table_name])
# 4. 创建 Excel 文件
with pd.ExcelWriter("stock_database_design.xlsx", engine="xlsxwriter") as writer:
# 写入索引页
df_index.to_excel(writer, sheet_name="索引页", index=False)
# 遍历写入其他 Sheet
for table_name, df in dataframes.items():
df.to_excel(writer, sheet_name=table_name, index=False)
# 获取 workbook 和 worksheet 对象
workbook = writer.book
worksheet_index = writer.sheets["索引页"]
# 为英文名添加超链接
for i, row in enumerate(index_data["英文名"], start=1):
worksheet_index.write_url(f"C{i + 1}", f"internal:'{row}'!A1", string=row)
# 设置所有 Sheet 的列宽为自适应
for sheet_name in writer.sheets:
worksheet = writer.sheets[sheet_name]
# 获取当前 Sheet 的 DataFrame
if sheet_name == "索引页":
df = df_index
else:
df = dataframes[sheet_name]
# 遍历每一列,设置列宽
for col_num, column in enumerate(df.columns):
# 计算列名的长度
col_name_len = len(str(column))
# 计算列内容的最大长度
col_content_len = df[column].astype(str).map(len).max()
# 取最大值,并增加 2 个字符的缓冲
max_len = max(col_name_len, col_content_len) + 6
# 设置列宽
worksheet.set_column(col_num, col_num, max_len)
print("Excel 文件已生成:stock_database_design.xlsx")
数据库结构
通过上一步生成的excel文件,反向生成SQL建表命令,实现表结构的可编辑保存和修改
根据excel文件,反向生成SQL建表命令,点击查看代码
# -*- coding: utf-8 -*-
"""
Created on Sat Mar 15 20:55:25 2025
@author: CHENGJUN
"""
import pandas as pd
import mysql.connector
# 读取 Excel 文件
excel_file = "stock_database_design.xlsx"
sheets = pd.read_excel(excel_file, sheet_name=None)
# 数据库连接配置
db_config = {
"host": "localhost",
"user": "root",
"password": "password",
"database": "stock_db"
}
# 生成 SQL 命令
sql_commands = []
for sheet_name, df in sheets.items():
if sheet_name == "索引页":
continue # 跳过索引页
# 提取表名
table_name = sheet_name
# 提取列信息
columns = []
for _, row in df.iterrows():
column_name = row["英文列名"]
data_type = row["数据类型"]
data_length = row["数据长度"]
is_primary_key = row["是否主键"] == "是"
is_foreign_key = row["是否外键"] == "是"
default_value = row["默认值"]
description = row["描述"] # 获取描述信息
# 处理数据长度
if pd.isna(data_length) or data_length == "-":
column_def = f"{column_name} {data_type}"
else:
column_def = f"{column_name} {data_type}({data_length})"
# 处理主键
if is_primary_key:
column_def += " PRIMARY KEY"
# 处理外键
if is_foreign_key:
referenced_table = "stock_info" # 假设外键都关联到 stock_info 表
column_def += f", FOREIGN KEY ({column_name}) REFERENCES {referenced_table}(stock_id)"
# 处理默认值
if not pd.isna(default_value) and default_value != "-":
column_def += f" DEFAULT {default_value}"
# 添加备注
if not pd.isna(description) and description != "-":
column_def += f" COMMENT '{description}'"
columns.append(column_def)
# 生成 CREATE TABLE 语句
create_table_sql = f"CREATE TABLE {table_name} (\n"
create_table_sql += ",\n".join(columns)
create_table_sql += "\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;"
sql_commands.append(create_table_sql)
# 打印生成的 SQL 命令
print("生成的 SQL 命令:")
for sql in sql_commands:
print(sql)
print("-" * 80)
# 用户选择是否执行 SQL 命令
execute = input("是否立即执行以上 SQL 命令?(y/n): ").strip().lower()
if execute == "y":
# 连接到数据库
conn = mysql.connector.connect(**db_config)
cursor = conn.cursor()
# 执行 SQL 命令
for sql in sql_commands:
try:
cursor.execute(f"DROP TABLE IF EXISTS {sql.split()[2]};") # 如果表已存在,先删除
cursor.execute(sql)
print(f"表 {sql.split()[2]} 创建成功!")
except mysql.connector.Error as err:
print(f"创建表 {sql.split()[2]} 时出错: {err}")
# 提交更改并关闭连接
conn.commit()
cursor.close()
conn.close()
else:
print("SQL 命令未执行。")
SQL生成基础数据库,点击查看代码
-- 交易所信息表
CREATE TABLE exchange_info (
exchange_id INT PRIMARY KEY,
exchange_code VARCHAR(10) NOT NULL UNIQUE,
exchange_name VARCHAR(50) NOT NULL,
country VARCHAR(50),
timezone VARCHAR(30),
trading_hours JSON, -- 存储交易时间段
status TINYINT DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 股票基础信息表
CREATE TABLE stock_info (
stock_id BIGINT PRIMARY KEY,
exchange_id INT,
stock_code VARCHAR(10) NOT NULL,
stock_name VARCHAR(100) NOT NULL,
listing_date DATE,
delisting_date DATE,
is_index TINYINT DEFAULT 0, -- 是否为指数
sector_code VARCHAR(20), -- 行业代码
status TINYINT DEFAULT 1, -- 状态:1-正常交易 2-停牌 3-退市
FOREIGN KEY (exchange_id) REFERENCES exchange_info(exchange_id),
UNIQUE KEY `idx_stock_code` (exchange_id, stock_code)
);
-- 行业分类表
CREATE TABLE sector_info (
sector_id INT PRIMARY KEY,
sector_code VARCHAR(20) NOT NULL UNIQUE,
sector_name VARCHAR(100) NOT NULL,
parent_id INT,
level TINYINT, -- 行业层级
standard VARCHAR(50) -- 分类标准(例如:证监会、申万)
);
-- 日线数据表(按年份分区)
CREATE TABLE daily_quotes (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
stock_id BIGINT NOT NULL,
trade_date DATE NOT NULL,
open_price DECIMAL(18,4),
high_price DECIMAL(18,4),
low_price DECIMAL(18,4),
close_price DECIMAL(18,4),
adj_factor DECIMAL(18,8), -- 复权因子
volume BIGINT, -- 成交量
amount DECIMAL(20,2), -- 成交额
turnover_rate DECIMAL(10,4), -- 换手率
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY `idx_stock_date` (stock_id, trade_date),
INDEX `idx_trade_date` (trade_date)
) PARTITION BY RANGE (YEAR(trade_date)) (
PARTITION p2020 VALUES LESS THAN (2021),
PARTITION p2021 VALUES LESS THAN (2022),
PARTITION p2022 VALUES LESS THAN (2023),
PARTITION p2023 VALUES LESS THAN (2024),
PARTITION p2024 VALUES LESS THAN (2025)
);
-- 分钟线数据表(按日期分区)
CREATE TABLE minute_quotes (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
stock_id BIGINT NOT NULL,
trade_time DATETIME NOT NULL,
open_price DECIMAL(18,4),
high_price DECIMAL(18,4),
low_price DECIMAL(18,4),
close_price DECIMAL(18,4),
volume BIGINT,
amount DECIMAL(20,2),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY `idx_stock_time` (stock_id, trade_time)
) PARTITION BY RANGE (TO_DAYS(trade_time)) (
PARTITION pmax VALUES LESS THAN MAXVALUE
);
另外一段范例:
豆包的数据库生成代码,点击查看
1. 创建数据库
首先,我们需要创建一个名为 stock_database 的数据库:
sql
CREATE DATABASE IF NOT EXISTS stock_database;
USE stock_database;
2. 股票交易数据表
每日交易数据表(daily_trading_data)
该表用于存储股票的每日交易数据,包括股票代码、交易日期、开盘价、最高价、最低价、收盘价、成交量等信息。
sql
CREATE TABLE IF NOT EXISTS daily_trading_data (
id INT AUTO_INCREMENT PRIMARY KEY,
stock_code VARCHAR(20) NOT NULL,
trading_date DATE NOT NULL,
open_price DECIMAL(10, 2) NOT NULL,
high_price DECIMAL(10, 2) NOT NULL,
low_price DECIMAL(10, 2) NOT NULL,
close_price DECIMAL(10, 2) NOT NULL,
volume BIGINT NOT NULL,
UNIQUE KEY unique_stock_date (stock_code, trading_date)
);
分时交易数据表(minute_trading_data)
该表用于存储股票的分时交易数据,与每日交易数据表类似,但时间粒度更细,精确到分钟。
sql
CREATE TABLE IF NOT EXISTS minute_trading_data (
id INT AUTO_INCREMENT PRIMARY KEY,
stock_code VARCHAR(20) NOT NULL,
trading_time DATETIME NOT NULL,
open_price DECIMAL(10, 2) NOT NULL,
high_price DECIMAL(10, 2) NOT NULL,
low_price DECIMAL(10, 2) NOT NULL,
close_price DECIMAL(10, 2) NOT NULL,
volume BIGINT NOT NULL,
UNIQUE KEY unique_stock_time (stock_code, trading_time)
);
K 线交易数据表(k_line_trading_data)
该表用于存储股票的 K 线交易数据,可根据不同的 K 线周期(如 5 分钟、15 分钟、30 分钟等)进行存储。
sql
CREATE TABLE IF NOT EXISTS k_line_trading_data (
id INT AUTO_INCREMENT PRIMARY KEY,
stock_code VARCHAR(20) NOT NULL,
k_line_period VARCHAR(10) NOT NULL, -- 如 '5min', '15min', '30min' 等
start_time DATETIME NOT NULL,
open_price DECIMAL(10, 2) NOT NULL,
high_price DECIMAL(10, 2) NOT NULL,
low_price DECIMAL(10, 2) NOT NULL,
close_price DECIMAL(10, 2) NOT NULL,
volume BIGINT NOT NULL,
UNIQUE KEY unique_stock_period_time (stock_code, k_line_period, start_time)
);
3. 数据库变动信息记录表(database_change_log)
该表用于记录每次数据库调用或修改的情况,包括操作类型(插入、更新、删除等)、操作时间、操作的表名等信息。
sql
CREATE TABLE IF NOT EXISTS database_change_log (
id INT AUTO_INCREMENT PRIMARY KEY,
operation_type VARCHAR(20) NOT NULL, -- 如 'INSERT', 'UPDATE', 'DELETE'
operation_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
table_name VARCHAR(50) NOT NULL,
description TEXT
);
4. 数据表结构信息记录表(table_structure_info)
该表用于保存不同数据表的结构信息,包括表名、字段名、字段个数、字段类型等。
sql
CREATE TABLE IF NOT EXISTS table_structure_info (
id INT AUTO_INCREMENT PRIMARY KEY,
table_name VARCHAR(50) NOT NULL,
column_name VARCHAR(50) NOT NULL,
column_position INT NOT NULL,
column_type VARCHAR(50) NOT NULL
);
5. 插入数据表结构信息示例
为了方便管理,我们可以插入各个表的结构信息到 table_structure_info 表中。以下是插入 daily_trading_data 表结构信息的示例:
sql
INSERT INTO table_structure_info (table_name, column_name, column_position, column_type)
VALUES
('daily_trading_data', 'id', 1, 'INT AUTO_INCREMENT PRIMARY KEY'),
('daily_trading_data', 'stock_code', 2, 'VARCHAR(20) NOT NULL'),
('daily_trading_data', 'trading_date', 3, 'DATE NOT NULL'),
('daily_trading_data', 'open_price', 4, 'DECIMAL(10, 2) NOT NULL'),
('daily_trading_data', 'high_price', 5, 'DECIMAL(10, 2) NOT NULL'),
('daily_trading_data', 'low_price', 6, 'DECIMAL(10, 2) NOT NULL'),
('daily_trading_data', 'close_price', 7, 'DECIMAL(10, 2) NOT NULL'),
('daily_trading_data', 'volume', 8, 'BIGINT NOT NULL');
你可以根据以上代码创建数据库和表结构,然后根据实际需求进行数据的插入、更新和查询操作。同时,每次对数据库进行操作时,可以在 database_change_log 表中记录相应的变动信息。
数据库关系
数据库读写验证
从Streamlit直接引用数据库的数据:st.connection("my_database")
数据库代码
排除6个MySQL自带数据库
#排除6个MySQL自带数据库
sys_db = ['information_schema', 'mysql', 'performance_schema', 'sakila', 'sys', 'world']
db_list = list(set(list(sql_all.Database))-set(sys_db))
数据库log
算法与代码
买卖点分析

筹码分析
参考:https://blog.csdn.net/liuhui244/article/details/145965706
指标分析
指标算法参考:https://www.gupang.com/
图形化界面
Taipy界面(感觉比较完美)
https://docs.taipy.io/en/latest/
https://github.com/Avaiga/taipy
可以使用pip来进行安装:pip install taipy
方案样例:https://docs.taipy.io/en/release-4.0/tutorials/getting_started/
https://docs.taipy.io/en/release-4.0/tutorials/articles/sales_dashboard/
参考:https://github.com/Avaiga/demo-stock-visualization/blob/develop/src/main_markdown.py
Installation
Work with the demo-stock-visualization code
git clone https://github.com/Avaiga/demo-stock-visualization.git
cd demo-stock-visualization
cd src
pip install -r requirements.txt
If you want to run tests, please install Pipenv:
pip install pipenv
git clone https://github.com/Avaiga/demo-stock-visualization.git
cd demo-stock-visualization
pipenv install --dev
pipenv run pytest
绘制K线图
matplotlib
mplfinance
官网:https://github.com/matplotlib/mplfinance
范例:https://blog.csdn.net/weixin_48964486/article/details/126711045
https://blog.csdn.net/Shepherdppz/article/details/117575286
安装 :pip install --upgrade mplfinance
mplfinance高级交互代码
# coding=utf-8
# inter_candle.py
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import mplfinance as mpf
# 读取示例数据
data = pd.read_csv('test_data.csv', index_col=0)
data.index = pd.to_datetime(data.index)
my_color = mpf.make_marketcolors(up='r',
down='g',
edge='inherit',
wick='inherit',
volume='inherit')
my_style = mpf.make_mpf_style(marketcolors=my_color,
figcolor='(0.82, 0.83, 0.85)',
gridcolor='(0.82, 0.83, 0.85)')
# 定义各种字体
title_font = {'fontname': 'pingfang HK',
'size': '16',
'color': 'black',
'weight': 'bold',
'va': 'bottom',
'ha': 'center'}
large_red_font = {'fontname': 'Arial',
'size': '24',
'color': 'red',
'weight': 'bold',
'va': 'bottom'}
large_green_font = {'fontname': 'Arial',
'size': '24',
'color': 'green',
'weight': 'bold',
'va': 'bottom'}
small_red_font = {'fontname': 'Arial',
'size': '12',
'color': 'red',
'weight': 'bold',
'va': 'bottom'}
small_green_font = {'fontname': 'Arial',
'size': '12',
'color': 'green',
'weight': 'bold',
'va': 'bottom'}
normal_label_font = {'fontname': 'pingfang HK',
'size': '12',
'color': 'black',
'weight': 'normal',
'va': 'bottom',
'ha': 'right'}
normal_font = {'fontname': 'Arial',
'size': '12',
'color': 'black',
'weight': 'normal',
'va': 'bottom',
'ha': 'left'}
class InterCandle:
def __init__(self, data, my_style):
self.pressed = False
self.xpress = None
# 初始化交互式K线图对象,历史数据作为唯一的参数用于初始化对象
self.data = data
self.style = my_style
# 设置初始化的K线图显示区间起点为0,即显示第0到第99个交易日的数据(前100个数据)
self.idx_start = 0
self.idx_range = 100
# 设置ax1图表中显示的均线类型
self.avg_type = 'ma'
self.indicator = 'macd'
# 初始化figure对象,在figure上建立三个Axes对象并分别设置好它们的位置和基本属性
self.fig = mpf.figure(style=my_style, figsize=(12, 8), facecolor=(0.82, 0.83, 0.85))
fig = self.fig
self.ax1 = fig.add_axes([0.08, 0.25, 0.88, 0.60])
self.ax2 = fig.add_axes([0.08, 0.15, 0.88, 0.10], sharex=self.ax1)
self.ax2.set_ylabel('volume')
self.ax3 = fig.add_axes([0.08, 0.05, 0.88, 0.10], sharex=self.ax1)
self.ax3.set_ylabel('macd')
# 初始化figure对象,在figure上预先放置文本并设置格式,文本内容根据需要显示的数据实时更新
self.t1 = fig.text(0.50, 0.94, '513100.SH - 纳斯达克指数ETF基金', **title_font)
self.t2 = fig.text(0.12, 0.90, '开/收: ', **normal_label_font)
self.t3 = fig.text(0.14, 0.89, f'', **large_red_font)
self.t4 = fig.text(0.14, 0.86, f'', **small_red_font)
self.t5 = fig.text(0.22, 0.86, f'', **small_red_font)
self.t6 = fig.text(0.12, 0.86, f'', **normal_label_font)
self.t7 = fig.text(0.40, 0.90, '高: ', **normal_label_font)
self.t8 = fig.text(0.40, 0.90, f'', **small_red_font)
self.t9 = fig.text(0.40, 0.86, '低: ', **normal_label_font)
self.t10 = fig.text(0.40, 0.86, f'', **small_green_font)
self.t11 = fig.text(0.55, 0.90, '量(万手): ', **normal_label_font)
self.t12 = fig.text(0.55, 0.90, f'', **normal_font)
self.t13 = fig.text(0.55, 0.86, '额(亿元): ', **normal_label_font)
self.t14 = fig.text(0.55, 0.86, f'', **normal_font)
self.t15 = fig.text(0.70, 0.90, '涨停: ', **normal_label_font)
self.t16 = fig.text(0.70, 0.90, f'', **small_red_font)
self.t17 = fig.text(0.70, 0.86, '跌停: ', **normal_label_font)
self.t18 = fig.text(0.70, 0.86, f'', **small_green_font)
self.t19 = fig.text(0.85, 0.90, '均价: ', **normal_label_font)
self.t20 = fig.text(0.85, 0.90, f'', **normal_font)
self.t21 = fig.text(0.85, 0.86, '昨收: ', **normal_label_font)
self.t22 = fig.text(0.85, 0.86, f'', **normal_font)
fig.canvas.mpl_connect('button_press_event', self.on_press)
fig.canvas.mpl_connect('button_release_event', self.on_release)
fig.canvas.mpl_connect('motion_notify_event', self.on_motion)
fig.canvas.mpl_connect('key_press_event', self.on_key_press)
fig.canvas.mpl_connect('scroll_event', self.on_scroll)
def refresh_plot(self, idx_start, idx_range):
""" 根据最新的参数,重新绘制整个图表
"""
all_data = self.data
plot_data = all_data.iloc[idx_start: idx_start + idx_range]
ap = []
# 添加K线图重叠均线,根据均线类型添加移动均线或布林带线
if self.avg_type == 'ma':
ap.append(mpf.make_addplot(plot_data[['MA5', 'MA10', 'MA20', 'MA60']], ax=self.ax1))
elif self.avg_type == 'bb':
ap.append(mpf.make_addplot(plot_data[['bb-u', 'bb-m', 'bb-l']], ax=self.ax1))
# 添加指标,根据指标类型添加MACD或RSI或DEMA
if self.indicator == 'macd':
ap.append(mpf.make_addplot(plot_data[['macd-m', 'macd-s']], ylabel='macd', ax=self.ax3))
bar_r = np.where(plot_data['macd-h'] > 0, plot_data['macd-h'], 0)
bar_g = np.where(plot_data['macd-h'] <= 0, plot_data['macd-h'], 0)
ap.append(mpf.make_addplot(bar_r, type='bar', color='red', ax=self.ax3))
ap.append(mpf.make_addplot(bar_g, type='bar', color='green', ax=self.ax3))
elif self.indicator == 'rsi':
ap.append(mpf.make_addplot([75] * len(plot_data), color=(0.75, 0.6, 0.6), ax=self.ax3))
ap.append(mpf.make_addplot([30] * len(plot_data), color=(0.6, 0.75, 0.6), ax=self.ax3))
ap.append(mpf.make_addplot(plot_data['rsi'], ylabel='rsi', ax=self.ax3))
else: # indicator == 'dema'
ap.append(mpf.make_addplot(plot_data['dema'], ylabel='dema', ax=self.ax3))
# 绘制图表
mpf.plot(plot_data,
ax=self.ax1,
volume=self.ax2,
addplot=ap,
type='candle',
style=self.style,
datetime_format='%Y-%m',
xrotation=0)
plt.show()
def refresh_texts(self, display_data):
""" 更新K线图上的价格文本
"""
# display_data是一个交易日内的所有数据,将这些数据分别填入figure对象上的文本中
self.t3.set_text(f'{np.round(display_data["open"], 3)} / {np.round(display_data["close"], 3)}')
self.t4.set_text(f'{np.round(display_data["change"], 3)}')
self.t5.set_text(f'[{np.round(display_data["pct_change"], 3)}%]')
self.t6.set_text(f'{display_data.name.date()}')
self.t8.set_text(f'{np.round(display_data["high"], 3)}')
self.t10.set_text(f'{np.round(display_data["low"], 3)}')
self.t12.set_text(f'{np.round(display_data["volume"] / 10000, 3)}')
self.t14.set_text(f'{display_data["value"]}')
self.t16.set_text(f'{np.round(display_data["upper_lim"], 3)}')
self.t18.set_text(f'{np.round(display_data["lower_lim"], 3)}')
self.t20.set_text(f'{np.round(display_data["average"], 3)}')
self.t22.set_text(f'{np.round(display_data["last_close"], 3)}')
# 根据本交易日的价格变动值确定开盘价、收盘价的显示颜色
if display_data['change'] > 0: # 如果今日变动额大于0,即今天价格高于昨天,今天价格显示为红色
close_number_color = 'red'
elif display_data['change'] < 0: # 如果今日变动额小于0,即今天价格低于昨天,今天价格显示为绿色
close_number_color = 'green'
else:
close_number_color = 'black'
self.t3.set_color(close_number_color)
self.t4.set_color(close_number_color)
self.t5.set_color(close_number_color)
def on_press(self, event):
if not (event.inaxes == self.ax1) and (not event.inaxes == self.ax3):
return
if event.button != 1:
return
self.pressed = True
self.xpress = event.xdata
# 切换当前ma类型, 在ma、bb、none之间循环
if event.inaxes == self.ax1 and event.dblclick == 1:
if self.avg_type == 'ma':
self.avg_type = 'bb'
elif self.avg_type == 'bb':
self.avg_type = 'none'
else:
self.avg_type = 'ma'
# 切换当前indicator类型,在macd/dma/rsi/kdj之间循环
if event.inaxes == self.ax3 and event.dblclick == 1:
if self.indicator == 'macd':
self.indicator = 'dma'
elif self.indicator == 'dma':
self.indicator = 'rsi'
elif self.indicator == 'rsi':
self.indicator = 'kdj'
else:
self.indicator = 'macd'
self.ax1.clear()
self.ax2.clear()
self.ax3.clear()
self.refresh_plot(self.idx_start, self.idx_range)
def on_release(self, event):
self.pressed = False
dx = int(event.xdata - self.xpress)
self.idx_start -= dx
if self.idx_start <= 0:
self.idx_start = 0
if self.idx_start >= len(self.data) - 100:
self.idx_start = len(self.data) - 100
def on_motion(self, event):
if not self.pressed:
return
if not event.inaxes == self.ax1:
return
dx = int(event.xdata - self.xpress)
new_start = self.idx_start - dx
# 设定平移的左右界限,如果平移后超出界限,则不再平移
if new_start <= 0:
new_start = 0
if new_start >= len(self.data) - 100:
new_start = len(self.data) - 100
self.ax1.clear()
self.ax2.clear()
self.ax3.clear()
self.refresh_texts(self.data.iloc[new_start])
self.refresh_plot(new_start, self.idx_range)
def on_scroll(self, event):
# 仅当鼠标滚轮在axes1范围内滚动时起作用
scale_factor = 1.0
if event.inaxes != self.ax1:
return
if event.button == 'down':
# 缩小20%显示范围
scale_factor = 0.8
if event.button == 'up':
# 放大20%显示范围
scale_factor = 1.2
# 设置K线的显示范围大小
self.idx_range = int(self.idx_range * scale_factor)
# 限定可以显示的K线图的范围,最少不能少于30个交易日,最大不能超过当前位置与
# K线数据总长度的差
data_length = len(self.data)
if self.idx_range >= data_length - self.idx_start:
self.idx_range = data_length - self.idx_start
if self.idx_range <= 30:
self.idx_range = 30
# 更新图表(注意因为多了一个参数idx_range,refresh_plot函数也有所改动)
self.ax1.clear()
self.ax2.clear()
self.ax3.clear()
self.refresh_texts(self.data.iloc[self.idx_start])
self.refresh_plot(self.idx_start, self.idx_range)
# 键盘按下处理
def on_key_press(self, event):
data_length = len(self.data)
if event.key == 'a': # avg_type, 在ma,bb,none之间循环
if self.avg_type == 'ma':
self.avg_type = 'bb'
elif self.avg_type == 'bb':
self.avg_type = 'none'
elif self.avg_type == 'none':
self.avg_type = 'ma'
elif event.key == 'up': # 向上,看仔细1倍
if self.idx_range > 30:
self.idx_range = self.idx_range // 2
elif event.key == 'down': # 向下,看多1倍标的
if self.idx_range <= data_length - self.idx_start:
self.idx_range = self.idx_range * 2
elif event.key == 'left':
if self.idx_start > self.idx_range:
self.idx_start = self.idx_start - self.idx_range // 2
elif event.key == 'right':
if self.idx_start < data_length - self.idx_range:
self.idx_start = self.idx_start + self.idx_range //2
self.ax1.clear()
self.ax2.clear()
self.ax3.clear()
self.refresh_texts(self.data.iloc[self.idx_start])
self.refresh_plot(self.idx_start, self.idx_range)
if __name__ == '__main__':
candle = InterCandle(data, my_style)
candle.idx_start = 150
candle.idx_range = 100
candle.refresh_texts(data.iloc[249])
candle.refresh_plot(150, 100)
lightweight-charts-python
https://tradingview.github.io/lightweight-charts/
https://lightweight-charts-python.readthedocs.io/en/latest/index.html
在线示例:https://cn.tradingview.com/lightweight-charts/
TradingView 是一款非常流行的交易行情分析软件,而 lightweight-charts 是它提供的精简版 Js 开源库。因为 TradingView 本身就是一款专注交易的软件,lightweight-charts 有这高性能和用户体验友好的特点。
范例:https://www.poloxue.com/posts/2024-05-10-lightweight-charts-python/
KLineChart 功能非常强大,图表也很漂亮 (但不是Python的,是Typescript语言)
https://klinecharts.com/guide/introduction
https://github.com/klinecharts/KLineChart
https://klinecharts.com/guide/quick-start.html
漂亮的范例网站:https://preview.klinecharts.com/
Typescript语言学习:https://www.runoob.com/typescript/ts-tutorial.html
运行此类代码的沙盒:https://jsfiddle.net/
https://www.runoob.com/try/runcode.php?filename=ts-hw&type=typescript
https://codesandbox.io/p/sandbox/28pqvd?file=%2Findex.js
https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fklinecharts%2FKLineChart
百度pyecharts
https://github.com/pyecharts/pyecharts/
图形参考样式

来源:https://zhuanlan.zhihu.com/p/668340915


来源:https://zhuanlan.zhihu.com/p/671331761
风险热图

来源:https://zhuanlan.zhihu.com/p/673231914
必备导入库
标准库
NumPy ( numpy):这是 Python 中科学计算的基础包。它提供对数组(如数学数组)的支持,以及对这些数组进行操作的数学函数的集合。
Pandas ( pandas):强大的数据操作和分析工具。在我们的例子中,以结构化且高效的方式(通过 DataFrame 对象)处理股票价格等金融数据至关重要。
Pandas 技术分析 ( pandas_ta):一个易于使用的库,扩展了 Pandas 的技术分析功能。它帮助我们直接根据股价数据计算布林线。
SciPy是基于NumPy的用于科学计算的第三方库。与NumPy相比,它提供了其他功能,包括scipy.stats统计分析。
数据接口库
参考我的另一个文档:pyhon可用在线金融数据汇总
tushare
akshare
证券宝baostock(免费)
必盈数据
https://www.biyingapi.com/
API文档:https://ad.biyingapi.com/apidoc.html
数据分类标准:https://www.biyingapi.com/licencelt.html
算法库
PyGAD ( pygad):这是一个用于构建遗传算法的库。它简化了遗传算法的创建和运行,我们用它来优化我们的交易策略。
PyAlgoTrade
PyAlgoTrade 是一个使用历史数据回溯测试交易策略的 Python 库。它允许您根据技术指标定义和测试交易策略,例如移动平均线、RSI、MACD 等。
它支持多种数据源,包括 Yahoo! 财经、Google 财经和 Alpha Vantage 等。PyAlgoTrade 提供了一个事件驱动的框架,可以轻松创建自定义交易策略。
Zipline
Zipline 是一个开源 Python 库,用于回测交易算法。它由 Quantopian 开发,该公司提供开发和测试量化交易策略的平台。
Zipline 提供了一个事件驱动的回测框架,允许用户定义交易算法并根据历史数据进行测试。它支持多种数据源,包括 CSV 文件、Pandas DataFrames 以及 Yahoo Finance 和 Quandl 等外部数据提供商。
Zipline 的主要功能之一是它允许您在真实的市场模拟中测试您的算法,包括交易成本、滑点和其他可能影响交易性能的因素。
Zipline 还提供了用于分析回测结果和可视化绩效指标的工具,例如回报、回撤和风险调整绩效指标。
PyPortfolioOpt
PyPortfolioOpt 是一个用于投资组合优化的 Python 库。它提供了一系列基于预期回报、风险和多元化等因素优化投资组合的算法。它还包括用于投资组合分析和可视化的工具。
wondertrader
https://github.com/wondertrader/wtpy
其它库
TQDM ( tqdm):在我们训练模型时显示进度条的库。
tesseract-ocr解决验证码识别
在操作账户的时候有时候会跳出输入“图形验证码”窗口,上面显示一些不规则(字符稍加扭曲变换得到的)的字符。tesseract-ocr(Optical Character Recognition)能够扫描字符,根据字符形状将其翻译成电子文本的过程。因此需要安装tesseract-ocr自动识别验证码。
tesseract下载地址:https://digi.bib.uni-mannheim.de/tesseract/
中国传统智慧
sxtwl_cpp
https://pypi.org/project/sxtwl/
https://github.com/allanpk716/BaZiHelper
https://blog.csdn.net/panaimin/article/details/8544489
sxtwl_cpp是参考寿星天文历并使用C++实现日历库。因为其依据天文历法算法实现,故其可查询范围广(BC722年以后与实历相符,支持1800年以前及2200年以后的日历查询)。支持Android、IOS、Windows、MacOS、Linux等平台。使用swig暴露接口给python,lua,java等语言使用。
Python API文档自动生成工具

Docker布署
整个股票系统发布:参考文章:https://blog.csdn.net/li901101123/article/details/145656187
股票交易规则
江恩十二条
江恩十二条买卖规则是江恩经过45年在华尔街投资经验的总结,并一直指导江恩成功的操作。
十二条买卖规则简介如下:
1、判断市势,再做出买卖决定
2、在单底、双底或三底水平线附近入市买入
3、根据市场波动的百分比买卖
4、根据三星期上升趋势或下跌趋势,决定买卖
5、把握市场分段波动规律
6、利用5或7点波动买卖
7、关注成交量
8、考虑时间因素
9、当出现新低或新高时买入
10、判断大势趋势的转向,决定买卖方向
11、寻找最安全的买卖点
12、发现快速市场的价位波动
炒股之成交量口诀
放量上涨,必然回调;
放量下跌,必然反弹;
缩量上涨,继续上涨;
缩量下跌,还会下跌;
放量不涨,顶部已现;
缩量不跌,底部已见;
顶部无量下跌,后续再创新高;
顶部放量下跌,后续继续调整。
炒股经验谈

浙公网安备 33010602011771号