Files
python/limit_up.py
T
2026-04-27 21:50:21 +08:00

134 lines
4.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import pandas as pd
import numpy as np
from sqlalchemy import create_engine, text
def init_db(engine):
"""
初始化数据库表结构
"""
create_table_sql = """
CREATE TABLE IF NOT EXISTS `stock_limit_data` (
`id` BIGINT AUTO_INCREMENT PRIMARY KEY,
`trade_date` DATE NOT NULL COMMENT '交易日期',
`symbol` VARCHAR(10) NOT NULL COMMENT '股票代码',
`ts_code` VARCHAR(20) NOT NULL COMMENT 'TS股票代码',
`name` VARCHAR(50) NOT NULL COMMENT '股票名称',
`status` TINYINT NOT NULL COMMENT '状态:1-涨停,2-炸板',
`limit_price` DECIMAL(16, 4) COMMENT '计算出的理论涨停价',
`close` DECIMAL(16, 4) COMMENT '实际收盘价',
`market` VARCHAR(20) COMMENT '所属板块',
UNIQUE KEY `uniq_date_code` (`trade_date`, `ts_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
"""
with engine.begin() as conn:
conn.execute(text(create_table_sql))
def analyze_and_save_limits(start_date, end_date):
"""
计算并在数据库中落地涨停、炸板详情
"""
# 数据库连接配置
engine = create_engine(
"mysql+pymysql://yangfan@8.159.129.156:10836/level?charset=utf8mb4",
connect_args={
"password": "aA%8023321088"
},
pool_pre_ping=True
)
init_db(engine)
# 1. 获取行情数据
query = f"""
SELECT
d.trade_date, d.symbol, d.ts_code, b.name,
d.high, d.close, d.pre_close, b.market
FROM stock_daily d
JOIN stock_basic b ON d.ts_code = b.ts_code
WHERE d.trade_date >= '{start_date}'
AND d.trade_date <= '{end_date}'
AND d.pre_close IS NOT NULL
ORDER BY d.trade_date ASC
"""
try:
df = pd.read_sql(query, engine)
if df.empty:
print(f"⚠️ 区间 [{start_date} , {end_date}] 内未获取到数据。")
return
# 强制类型转换
df['pre_close'] = pd.to_numeric(df['pre_close'], errors='coerce')
df['high'] = pd.to_numeric(df['high'], errors='coerce')
df['close'] = pd.to_numeric(df['close'], errors='coerce')
# 2. 涨停价计算规则 (已取消 ST 判定)
def calc_limit(row):
m = str(row['market'])
p = row['pre_close']
if pd.isna(p) or p <= 0: return 0.0
# 仅保留板块判定
if '创业板' in m or '科创板' in m:
ratio = 1.2
elif '北交所' in m:
ratio = 1.3
else:
ratio = 1.1 # 普通主板(含ST股现在也按10%计算)
# 采用标准四舍五入
return round(p * ratio, 2)
df['limit_price'] = df.apply(calc_limit, axis=1)
# 3. 筛选逻辑:利用 abs 差值判断,解决浮点数精度问题
# 只要最高价触及理论涨停价(误差0.01以内)即视为触板
is_at_limit = (df['high'] - df['limit_price']).abs() < 0.011
is_close_at_limit = (df['close'] - df['limit_price']).abs() < 0.011
df['is_limit'] = is_at_limit & is_close_at_limit
df['is_broken'] = is_at_limit & (~is_close_at_limit)
# 分离结果
limit_df = df[df['is_limit']].copy()
limit_df['status'] = 1
broken_df = df[df['is_broken']].copy()
broken_df['status'] = 2
save_df = pd.concat([limit_df, broken_df])
if not save_df.empty:
final_df = save_df[[
'trade_date', 'symbol', 'ts_code', 'name',
'status', 'limit_price', 'close', 'market'
]]
# 4. 事务入库:先删后插
with engine.begin() as conn:
target_dates = final_df['trade_date'].astype(str).unique()
date_list = ", ".join([f"'{d}'" for d in target_dates])
delete_query = f"DELETE FROM stock_limit_data WHERE trade_date IN ({date_list})"
conn.execute(text(delete_query))
final_df.to_sql('stock_limit_data', conn, if_exists='append', index=False)
print(f"✅ 处理成功!涉及交易日: {len(target_dates)} 天")
print(f"📊 记录详情: 总计 {len(final_df)} 条 (涨停: {len(limit_df)} / 炸板: {len(broken_df)})")
else:
print("ℹ️ 该时段内全市场无涨停或炸板记录。")
except Exception as e:
print(f"❌ 运行失败: {e}")
if __name__ == "__main__":
# 配置日期区间
START_DATE = '2024-09-23'
END_DATE = '2026-04-27'
analyze_and_save_limits(START_DATE, END_DATE)