图:
股票自选助手
这是一个基于 Django 开发的 A 股自选股票信息查看系统。系统使用 akshare 库获取实时股票数据,支持添加、删除和更新股票信息。
功能特点
- 支持添加自选股票
- 实时显示股票价格和涨跌幅
- 一键更新所有股票数据
- 支持删除不需要的股票
- 使用中国时区显示更新时间
- 支持简体中文界面
技术栈
- Python 3.8+
- Django 5.0.1
- akshare(A股数据获取)
- Bootstrap 5.1.3(前端界面)
- SQLite(数据存储)
安装步骤
-
克隆项目到本地:
git clone [项目地址]
cd stock_tracker -
创建并激活虚拟环境(可选但推荐):
python -m venv venv
Windows
venv\Scripts\activate
Linux/Mac
source venv/bin/activate
-
安装依赖包:
pip install django akshare pandas
-
初始化数据库:
python manage.py migrate
-
启动开发服务器:
python manage.py runserver
-
访问系统:
打开浏览器,访问 http://127.0.0.1:8000
使用说明
添加股票
- 在输入框中输入股票代码,支持以下格式:
-
- 直接输入代码:
600519
(系统会自动判断沪深市场) - 带后缀格式:
- 直接输入代码:
-
-
- 上证股票:
600519.SS
- 深证股票:
000001.SZ
- 上证股票:
-
- 点击"添加"按钮将股票添加到自选列表
更新股票数据
- 点击"更新价格"按钮可以一次性更新所有股票的最新数据
- 系统会显示更新成功和失败的股票数量
删除股票
- 点击每个股票行右侧的"删除"按钮可以将股票从自选列表中移除
项目结构
stock_tracker/
├── manage.py
├── stock_tracker/ # 项目配置目录
│ ├── __init__.py
│ ├── settings.py # 项目设置
│ ├── urls.py # URL配置
│ └── wsgi.py
└── stocks/ # 股票应用目录
├── __init__.py
├── models.py # 数据模型
├── views.py # 视图函数
├── urls.py # 应用URL配置
└── templates/ # 模板文件
└── stocks/
└── stock_list.html
开发说明
数据模型
Stock
模型包含以下字段:
symbol
: 股票代码name
: 股票名称current_price
: 当前价格change_percent
: 涨跌幅last_updated
: 最后更新时间
主要视图函数
stock_list
: 显示股票列表add_stock
: 添加新股票remove_stock
: 删除股票update_prices
: 更新股票价格
注意事项
- 时区设置:
-
- 系统使用中国时区 (Asia/Shanghai)
- 所有时间显示均为本地时间
- 数据更新:
-
- 使用 akshare 获取实时数据
- 支持批量更新所有股票
- 错误处理:
-
- 系统会显示详细的错误信息
- 包含股票代码格式提示
维护和更新
- 数据库备份:
-
- 定期备份 SQLite 数据库文件
- 依赖更新:
-
- 定期检查并更新依赖包
- 特别注意 akshare 的更新
代码:
stocks\models.py
python
from django.db import models
class Stock(models.Model):
symbol = models.CharField(max_length=10, unique=True)
name = models.CharField(max_length=100)
current_price = models.DecimalField(max_digits=10, decimal_places=2, null=True)
change_percent = models.DecimalField(max_digits=5, decimal_places=2, null=True)
last_updated = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.symbol} - {self.name}"
stocks\views.py
python
from django.shortcuts import render, redirect
from django.contrib import messages
from .models import Stock
import akshare as ak
from datetime import datetime
import pandas as pd
def stock_list(request):
stocks = Stock.objects.all().order_by('symbol')
return render(request, 'stocks/stock_list.html', {'stocks': stocks})
def add_stock(request):
if request.method == 'POST':
symbol = request.POST.get('symbol', '').upper()
try:
# 处理股票代码格式
if symbol.endswith('.SZ'):
code = symbol.replace('.SZ', '')
market = 'sz'
elif symbol.endswith('.SS'):
code = symbol.replace('.SS', '')
market = 'sh'
else:
code = symbol
market = 'sh' if code.startswith('6') else 'sz'
# 获取实时行情
stock_info = ak.stock_zh_a_spot_em()
stock_data = stock_info[stock_info['代码'] == code]
if stock_data.empty:
messages.error(request, f'找不到股票 {symbol} 的信息。请确保:\n1. 股票代码格式正确\n2. 对于上证股票,可以添加.SS后缀\n3. 对于深证股票,可以添加.SZ后缀')
return redirect('stock_list')
# 获取第一行数据
stock_row = stock_data.iloc[0]
stock_obj, created = Stock.objects.get_or_create(
symbol=symbol,
defaults={'name': stock_row['名称']}
)
# 更新股票信息
stock_obj.current_price = float(stock_row['最新价'])
stock_obj.change_percent = float(stock_row['涨跌幅'])
stock_obj.save()
if created:
messages.success(request, f'成功添加股票 {symbol}({stock_row["名称"]})')
else:
messages.success(request, f'成功更新股票 {symbol} 的信息')
except Exception as e:
messages.error(request, f'添加股票时出错: {str(e)}\n建议:\n1. 检查股票代码格式\n2. 确保网络连接正常')
return redirect('stock_list')
def remove_stock(request, symbol):
try:
stock = Stock.objects.get(symbol=symbol)
stock.delete()
messages.success(request, f'已删除股票 {symbol}')
except Stock.DoesNotExist:
messages.error(request, f'找不到股票 {symbol}')
return redirect('stock_list')
def update_prices(request):
success_count = 0
error_count = 0
stocks = Stock.objects.all()
try:
# 获取所有A股实时行情
stock_info = ak.stock_zh_a_spot_em()
for stock in stocks:
try:
# 处理股票代码格式
if stock.symbol.endswith('.SZ'):
code = stock.symbol.replace('.SZ', '')
elif stock.symbol.endswith('.SS'):
code = stock.symbol.replace('.SS', '')
else:
code = stock.symbol
# 查找对应的股票数据
stock_data = stock_info[stock_info['代码'] == code]
if not stock_data.empty:
stock_row = stock_data.iloc[0]
stock.current_price = float(stock_row['最新价'])
stock.change_percent = float(stock_row['涨跌幅'])
stock.save()
success_count += 1
else:
error_count += 1
except:
error_count += 1
continue
except Exception as e:
messages.error(request, f'更新价格时出错: {str(e)}')
return redirect('stock_list')
if success_count > 0:
messages.success(request, f'成功更新 {success_count} 支股票的价格')
if error_count > 0:
messages.warning(request, f'有 {error_count} 支股票更新失败')
return redirect('stock_list')
stocks\urls.py
python
from django.urls import path
from . import views
urlpatterns = [
path('', views.stock_list, name='stock_list'),
path('add/', views.add_stock, name='add_stock'),
path('remove/<str:symbol>/', views.remove_stock, name='remove_stock'),
path('update/', views.update_prices, name='update_prices'),
]
stocks\templates\stocks\stock_list.html
html
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>自选股票</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container mt-4">
<h1 class="mb-4">我的自选股票</h1>
{% if messages %}
<div class="messages">
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
<!-- 添加新股票的表单 -->
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">添加新股票</h5>
<form method="post" action="{% url 'add_stock' %}" class="row g-3">
{% csrf_token %}
<div class="col-auto">
<input type="text" name="symbol" class="form-control" placeholder="输入股票代码" required>
</div>
<div class="col-auto">
<button type="submit" class="btn btn-primary">添加</button>
</div>
</form>
</div>
</div>
<!-- 股票列表 -->
<div class="card">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="card-title">股票列表</h5>
<a href="{% url 'update_prices' %}" class="btn btn-success">更新价格</a>
</div>
{% if stocks %}
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>代码</th>
<th>名称</th>
<th>当前价格</th>
<th>涨跌幅</th>
<th>最后更新</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{% for stock in stocks %}
<tr>
<td>{{ stock.symbol }}</td>
<td>{{ stock.name }}</td>
<td>{{ stock.current_price }}</td>
<td class="{% if stock.change_percent > 0 %}text-success{% elif stock.change_percent < 0 %}text-danger{% endif %}">
{{ stock.change_percent|floatformat:2 }}%
</td>
<td>{{ stock.last_updated|date:"Y-m-d H:i:s" }}</td>
<td>
<a href="{% url 'remove_stock' stock.symbol %}" class="btn btn-danger btn-sm" onclick="return confirm('确定要删除这支股票吗?')">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p class="text-center">暂无自选股票,请添加。</p>
{% endif %}
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
stock_tracker\urls.py
python
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('stocks.urls')),
]
stock_tracker\settings.py
python
INSTALLED_APPS = [
。。。
'stocks',
]
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_TZ = True
AKShare 使用说明
AKShare 是一个优秀的开源财经数据接口库,用于获取中国金融市场数据。本项目主要使用其 A 股数据接口。
安装方法
pip install akshare
基本使用
import akshare as ak
A股数据获取
1. 实时行情数据
获取所有 A 股实时行情数据:
# 获取所有A股实时行情
stock_info = ak.stock_zh_a_spot_em()
# 返回的数据包含以下字段:
# - 代码: 股票代码
# - 名称: 股票名称
# - 最新价: 当前价格
# - 涨跌幅: 涨跌百分比
# - 涨跌额: 价格变动
# - 成交量: 成交股数
# - 成交额: 成交金额
# - 振幅: 价格振幅
# - 最高: 最高价
# - 最低: 最低价
# - 今开: 开盘价
# - 昨收: 昨日收盘价
2. 个股历史数据
获取单个股票的历史数据:
# 获取股票历史数据
stock_history = ak.stock_zh_a_hist(symbol="000001", period="daily", start_date="20240101", end_date="20240110")
# 参数说明:
# - symbol: 股票代码(不带市场后缀)
# - period: 周期(daily-日线,weekly-周线,monthly-月线)
# - start_date: 开始日期
# - end_date: 结束日期
3. 股票基本信息
获取股票的基本信息:
# 获取股票基本信息
stock_info = ak.stock_individual_info_em(symbol="000001")
# 返回数据包含:
# - 股票代码
# - 股票简称
# - 行业
# - 总市值
# - 流通市值
# - 等基本面信息
本项目中的使用
在本项目中,我们主要使用了以下功能:
-
获取实时行情:
从 views.py 中的实现
def add_stock(request):
# 获取实时行情数据
stock_info = ak.stock_zh_a_spot_em()
# 查找特定股票
stock_data = stock_info[stock_info['代码'] == code]if not stock_data.empty: # 获取股票信息 stock_row = stock_data.iloc[0] current_price = float(stock_row['最新价']) change_percent = float(stock_row['涨跌幅'])
-
批量更新价格:
从 views.py 中的实现
def update_prices(request):
# 一次获取所有A股数据
stock_info = ak.stock_zh_a_spot_em()for stock in stocks: # 查找对应的股票数据 stock_data = stock_info[stock_info['代码'] == code] if not stock_data.empty: # 更新价格信息 stock_row = stock_data.iloc[0] stock.current_price = float(stock_row['最新价']) stock.change_percent = float(stock_row['涨跌幅'])
注意事项
- 数据限制:
-
- 接口访问可能有频率限制
- 建议适当控制请求频率
- 考虑数据缓存机制
- 代码格式:
-
- A股代码格式:6位数字
- 上证股票以 6 开头
- 深证股票以 0 或 3 开头
- 错误处理:
-
- 注意处理网络异常
- 处理数据为空的情况
- 处理数值转换异常
常见问题
- 数据获取失败:
-
- 检查网络连接
- 确认股票代码格式
- 查看是否触发频率限制
- 数据不准确:
-
- 确认是否在交易时间
- 检查数据更新时间
- 验证股票代码正确性
相关资源
更新记录
- 2024-01-10: 首次创建文档
- 使用 akshare 1.15.68 版本
- 主要实现 A 股实时数据获取功能