简介:一套基于Django的股票市值管理系统完整源码包,面向希望掌握Django Web开发全流程的Python开发者,也适合需要实现港股、美股账户交易、分红及新股管理等业务场景的学习者。项目围绕Django的Models、Views、Templates、URLconfs等核心组件展开,包含用户认证、权限控制、表单处理、数据库交互等功能,体现了从后台模型设计到前端页面渲染的完整工程结构。资源包共2030个文件,以svg、scss、css、js等前端样式与交互文件为主,辅以html页面模板和42个py后端源码,整体体积8.95MB,便于快速下载查阅。已有217人学习浏览,适合作为课程设计或Django进阶练手参考,可在此基础上扩展真实行情接口或报表模块,提升项目的完整性与实用性。 市面上Django项目源码不少,但真正围绕“股票市值管理”这个具体业务场景、把数据模型和计算逻辑讲清楚的不算多。前阵子我拿到一套基于Django的股票市值管理系统源码,从项目结构到核心业务代码完整过了一遍,有些设计思路值得拿出来聊聊。这个系统解决的痛点很明确:个人投资者手里握着好几只股票,每天盯盘、记录成本价、算浮动盈亏,光靠Excel表格维护容易乱,尤其是持仓多了以后,想快速看总市值、按行业统计盈亏,手动更新数据既费时又容易出错。Django做这种内部工具类的Web应用非常合适,自带Admin后台、ORM和用户认证,省掉一大半重复造轮子的事。
这篇文章我就按实际拆解源码的思路来写,从数据模型设计、市值计算逻辑、后台管理配置到部署运行,把整套系统的核心环节串一遍。适合正在学Django的开发者拿来当项目实战参考,也适合有股票持仓管理需求的技术型投资者自己改着用。
1. 项目整体设计与技术选型思路
1.1 为什么选Django做这类管理系统
先说我拿到源码后的第一感受:整个项目用Django来搭,选型上是非常稳妥的。股票市值管理系统的核心其实是数据管理——股票的买入卖出记录、持仓数量、最新价格、市值快照,这些数据之间有明确的关系,而且需要做增删改查和统计汇总。Django在这类场景下的优势非常明显。
自带的后台管理功能特别省事。基础数据录入、修改、删除这些操作,在Django Admin里配置好以后直接就能用,不需要额外写一套页面。自己做网站的时候最烦的就是给内部工具写前端页面,但Django的Admin只花少量代码就能得到完整的管理界面,而且支持搜索、筛选、分页,这对管理几十只股票的数据来说绰绰有余。
ORM对数据关系的处理也让人省心。股票和持仓记录、持仓和交易流水之间天然是外键关系,Django的ORM用Python对象直接操作,不用写SQL。比如要查某只股票当前的总持仓量,用ORM一行代码就能搞定。更重要的是,后续如果想加功能,比如按行业汇总、按时间段看收益曲线,Django的QuerySet能直接做聚合查询,扩展空间很大。
还有一点是这个系统用到了用户体系。虽然个人使用不需要,但Django内置的认证系统天然支持多用户隔离,如果以后想发给朋友一起用,或者部署到服务器上让多个人各自管理自己的持仓,框架层面已经支持了,不需要额外改造。
1.2 系统核心模块梳理
翻完源码之后,我整理了一下这个系统的模块划分。整体上分成了三层:
数据接入层:负责股票基本信息和价格的维护。源码里主要通过两个途径,一个是后台手动录入股票代码和名称,另一个是价格字段留了接口,可以手动更新,也可以定时抓取第三方行情数据。
业务逻辑层:主要包含持仓管理、交易记录管理和市值计算。这一层是整个系统的核心,所有关于盈亏的计算都在这里完成。持仓扣减、加仓、市值重算这些操作都有对应的Service层方法封装。
展示层:Django Admin作为主要操作界面,另外还有一组前端页面用于展示汇总数据。考虑到实际使用场景,这套源码的重点放在后台上,前端展示相对简单,但够用。
这个模块划分整体上是清晰的,符合Django MTV模式的最佳实践。它没有为了追求架构上的“高大上”而引入复杂的东西,就是用框架默认的方式组织代码。对于这套系统的场景来说,实用比炫技重要得多。
2. 数据模型设计 — 系统的地基
2.1 股票基础信息怎么建模
数据模型是整个系统的地基,我花了不少时间在读懂这块代码上。先看股票信息表,源码里的模型设计比较常规但很实用:
from django.db import models class Stock(models.Model): code = models.CharField(max_length=10, unique=True, verbose_name="股票代码") name = models.CharField(max_length=50, verbose_name="股票名称") industry = models.CharField(max_length=50, blank=True, verbose_name="所属行业") current_price = models.DecimalField(max_digits=10, decimal_places=2, default=0, verbose_name="最新价") update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间") class Meta: verbose_name = "股票信息" verbose_name_plural = verbose_name ordering = ['code'] def __str__(self): return f"{self.name}({self.code})"这里有两个设计细节值得注意。第一,股票代码用了unique=True做唯一约束,这保证了一只股票只会在表里出现一次。实际使用中,无论是手动录入还是从行情接口导入,都要以代码作为唯一标识,不然容易产生脏数据。第二,current_price字段单独存放在股票表里,这个设计很常见,因为最新价是每只股票的一个”状态“,单独存下来方便统一更新。
DecimalField而不是FloatField这个选择非常讲究。股票价格涉及金额计算,浮点数在计算机里会引入微小的精度误差,累加起来可能造成盈亏数字不准确。DecimalField能精确到分,这是金融类应用的基本要求,源码里在这个细节上没有踩坑。
2.2 持仓和交易记录模型
接下来是持仓表,用来记录当前持有某只股票的数量和成本:
class Position(models.Model): user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="用户") stock = models.ForeignKey(Stock, on_delete=models.CASCADE, verbose_name="股票") quantity = models.IntegerField(default=0, verbose_name="持仓数量") cost_price = models.DecimalField(max_digits=10, decimal_places=2, default=0, verbose_name="成本价") update_time = models.DateTimeField(auto_now=True, verbose_name="更新时间") class Meta: verbose_name = "持仓信息" verbose_name_plural = verbose_name unique_together = ('user', 'stock') def __str__(self): return f"{self.user} - {self.stock.name}"持仓表和股票表是多对一关系,一只股票可以被多个用户持有,但每个用户对同一只股票只能有一条持仓记录,这就是unique_together的作用。从实际业务看,一个用户持有一只股票,正常情况下只需要一条当前持仓记录,历史交易则记录在下面的交易流水表里,形成了“持仓是状态、流水是历史”的清晰结构。
cost_price字段记录的是持仓成本价。这里要特别注意,成本价不是简单等于买入价,它是加权平均后的结果。比如分两次买入同一只股票,第一次100元买100股,第二次120元买100股,那成本价就是(100×100 + 120×100) / 200 = 110元。源码里在更新持仓时会重新计算加权成本,这样浮动盈亏的计算才有意义。
交易记录表的结构是另一个重点:
class TradeRecord(models.Model): BUY = 'BUY' SELL = 'SELL' DIRECTION_CHOICES = [ (BUY, '买入'), (SELL, '卖出'), ] user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="用户") stock = models.ForeignKey(Stock, on_delete=models.CASCADE, verbose_name="股票") direction = models.CharField(max_length=4, choices=DIRECTION_CHOICES, verbose_name="交易方向") price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="成交价格") quantity = models.IntegerField(verbose_name="成交数量") trade_time = models.DateTimeField(auto_now_add=True, verbose_name="成交时间") note = models.CharField(max_length=200, blank=True, verbose_name="备注")对每笔交易,direction区分买入和卖出,trade_time记录时间,note可以用来写点备注信息,比如操作理由。通过FinanceService统一处理买卖逻辑,而不是在视图里直接修改持仓,好处是业务规则集中在一处,后续加手续费、税费计算时只需改一个地方。
2.3 市值与盈亏计算的核心逻辑
市值计算是这套系统的灵魂。源码里把一个很重要的方法放在了模型里:
class Stock(models.Model): # ... 字段省略 @property def market_value(self): """单只股票总市值""" return self.current_price * self.get_total_position() def get_total_position(self): """获取所有用户对该股票的总持仓""" return self.position_set.aggregate(total=models.Sum('quantity'))['total'] or 0这里用Python的@property装饰器把市值变成一个只读属性,每次访问都会实时计算,保证了数据的一致性。因为市值是随着股价变化的,如果用一个固定的数据库字段存储,股价更新后必须同步更新,很容易漏掉。用计算属性就能永远拿最新值,这个思路值得借鉴。不过要注意代价是每次计算都会做一次查询,在数据量不大时没问题,但如果持仓记录几万条,就需要缓存来优化了。
资金和收益统计这块也写得很清楚:
- 持仓成本:
持仓数量 × 成本价 - 当前市值:
持仓数量 × 最新价 - 浮动盈亏:
当前市值 - 持仓成本 - 收益率:
浮动盈亏 / 持仓成本
这几个公式看起来简单,但实际编码时有讲究。比如在计算总收益时,源码里先通过Sum聚合查询把总市值和总成本算出来,再进行浮盈亏计算,这个顺序保证了数据的一致性。
3. 关键功能实现与实操细节
3.1 后台管理的配置技巧
这套系统把大量功能都放在了Django Admin里,因此admin.py的配置质量直接影响使用体验。看源码时我发现它用了一个很实用的注册方式:
from django.contrib import admin from .models import Stock, Position, TradeRecord @admin.register(Stock) class StockAdmin(admin.ModelAdmin): list_display = ('code', 'name', 'industry', 'current_price', 'update_time') search_fields = ('code', 'name') list_filter = ('industry',) readonly_fields = ('update_time',) @admin.register(Position) class PositionAdmin(admin.ModelAdmin): list_display = ('user', 'stock', 'quantity', 'cost_price', 'update_time') list_filter = ('user', 'stock__industry') search_fields = ('stock__code', 'stock__name', 'user__username') @admin.register(TradeRecord) class TradeRecordAdmin(admin.ModelAdmin): list_display = ('user', 'stock', 'direction', 'price', 'quantity', 'trade_time') list_filter = ('direction', 'user') search_fields = ('stock__name', 'stock__code') date_hierarchy = 'trade_time'这几个配置技巧实际用起来很顺手。list_display定义了列表页展示哪些列,关键是Position里用了stock__industry,这是Django的跨表查询语法,可以展示关联表里的字段,不用额外写方法。list_filter让后台界面出现筛选栏,比如按行业筛选、按交易方向筛选,数据多了以后非常实用。date_hierarchy在交易记录页面上方生成了一个按日期快速导航的层级筛选器,查某天的交易记录直接点日期就能过滤。
3.2 买卖成交的核心处理流程
源码里的买入和卖出操作不是直接改持仓,而是通过一个服务类完成的。这部分是理解整个系统的关键:
class FinanceService: @staticmethod def buy(user, stock, price, quantity, note=""): with transaction.atomic(): TradeRecord.objects.create( user=user, stock=stock, direction='BUY', price=price, quantity=quantity, note=note ) position, created = Position.objects.get_or_create( user=user, stock=stock, defaults={'quantity': 0, 'cost_price': 0} ) total_cost = position.cost_price * position.quantity + price * quantity new_quantity = position.quantity + quantity position.cost_price = total_cost / new_quantity position.quantity = new_quantity position.save() @staticmethod def sell(user, stock, price, quantity, note=""): with transaction.atomic(): position = Position.objects.get(user=user, stock=stock) if position.quantity < quantity: raise ValueError("持仓不足,无法卖出") TradeRecord.objects.create( user=user, stock=stock, direction='SELL', price=price, quantity=quantity, note=note ) position.quantity -= quantity if position.quantity == 0: position.delete() else: position.save()transaction.atomic()的使用是这里最该强调的。一笔交易同时涉及交易记录创建和持仓更新,两个操作必须同时成功或同时失败,否则会出现交易流水和持仓数据对不上的问题。比如“交易记录创建成功但持仓没更新”这种脏数据,在账户里查流水是有记录,但持仓却多出一部分,影响非常大。原子性保证了这两个操作要么都完成,要么都不执行。
卖出操作里的position.quantity < quantity校验也值得一说。这是防止卖出超过持仓数量的保护性判断,如果不加这个校验,持仓可能会变成负数,后面的市值计算就会出错。源码里在服务层抛异常而不是在视图层做判断,好处是所有调用这个服务的地方都能受到保护,不管是Admin操作还是自己写的接口。
3.3 股价更新与市值刷新怎么做
这个系统支持两种更新股价的方式:手动在Admin后台修改Stock.current_price,以及通过代码批量更新。源码里提供了一个函数用来应对批量更新场景:
import requests def update_stock_prices(): """从行情接口批量拉取最新价格""" stocks = Stock.objects.all() for stock in stocks: try: # 这是示例结构,实际使用时替换为真实行情API url = f"https://api.example.com/stock/{stock.code}" resp = requests.get(url, timeout=5) data = resp.json() stock.current_price = data['price'] stock.update_time = timezone.now() stock.save() except Exception as e: print(f"更新 {stock.code} 失败: {e}")这里timeout=5很关键,调用外部接口时必须设置超时时间,否则某个股票接口无响应会卡住整个更新流程。循环里加 try-except 让单只股票失败不影响其他股票的更新。更新函数里没做并发控制,实际使用时建议用Django的定时任务或cron定期调用,避免多人操作时出现冲突。
注意这段代码里的第三方API只是示例。实际操作中,行情数据的获取方式要看你自己的数据源。可以是免费的公开接口,也可以是自己爬的数据,甚至可以手动维护价格。股票价格数据本身有滞后性,做个人市值监控完全够用,但要搞清楚你的数据源更新频率,避免看到的是好几天前的价格。
4. 部署运行与常见问题排查
4.1 环境准备和启动步骤
这套系统的部署方式很标准,不复杂,按流程来基本不会出错。先用pip install django装好Django,然后在项目根目录执行数据库迁移,然后创建管理员账号就能启动。
# 1. 创建虚拟环境(推荐) python3 -m venv venv source venv/bin/activate # Windows上执行 venv\Scripts\activate # 2. 安装依赖 pip install django requests # 3. 数据库迁移 python manage.py makemigrations python manage.py migrate # 4. 创建超级用户 python manage.py createsuperuser # 5. 启动开发服务器 python manage.py runserver 0.0.0.0:8000访问http://127.0.0.1:8000/admin,用刚才创建的账号登录,就能进入管理界面开始录股票、录交易了。如果只需要本机用,runserver就够了。要部署到服务器长期运行,建议用Gunicorn加Nginx,开发服务器不适合生产环境,并发能力和安全性都达不到要求。
4.2 新手容易踩的坑
数据库迁移报错。拿到源码后第一件事是迁移数据库,但这个步骤经常会遇到模型字段命名或数据库依赖的问题。常见的一种是报“字段已存在”或者“外键关联不存在的表”。这种一般出现在源码在别处跑过迁移、数据库表结构有残留的情况,或者是 Django 版本差异导致迁移记录不兼容。最稳妥的办法是删掉数据库文件(开发环境SQLite的话直接删掉db.sqlite3),重新执行迁移。不要在生产环境上这么干,数据会全丢。如果是部署到服务器,建议先把数据库导出备份。
中文乱码问题。Django 默认字符集是 UTF-8,数据库迁移后表结构已经带上了正确编码。但如果你手动导入数据,比如通过SQL脚本导入股票列表,遇到乱码多半是SQL文件本身编码不是UTF-8,需另存为UTF-8格式后再导入。
时区问题。Django 项目创建时settings.py里默认开了USE_TZ=True,同时TIME_ZONE默认值是UTC。记账的时候,交易时间会显示成UTC时间,比国内时间早8小时,看起来总觉得不对。改成TIME_ZONE = 'Asia/Shanghai'即可,日志和业务时间就都和本地时间一致了。
静态文件404。部署到服务器之后,Admin后台界面的CSS样式加载不出来,页面上只有白底黑字,这是因为Django不会自动提供静态文件服务。需要在settings.py里配置STATIC_ROOT,然后运行python manage.py collectstatic把静态文件收集到指定目录,再让Nginx处理好这个目录的转发,后台样式就正常了。
Admin后台点进去找不到股票信息。看到Stock表里没内容可以理解,如果录入了股票但列表看不到,先检查右上角的筛选条件,再检查是否是操作时选错了模型入口。这类问题基本都是使用层面的,不是代码Bug。
4.3 从实际使用看这套系统的可扩展方向
系统跑起来之后,我试着从使用者角度想了一些扩展方向,跟各位分享下思路:
加入自选股预警功能。在Stock模型里加一个threshold_price字段,当current_price跌破或涨破这个阈值时,通过Django的信号机制发送通知。思路是每次价格更新后,检查是否触发预警条件。Django的post_save信号很适合做这件事,不用改太多现有代码。
增加市值快照表。目前的市值是实时计算的,但历史市值走势就没法追溯。可以增加一张MarketSnapshot表,记录每个交易日结束时的持仓和市值数据。跑个定时任务,每天15:00收盘后自动记录一份快照,时间久了就能画出个人的资产曲线。
做持仓导入导出。现在每笔交易都是手动录的,如果本身有券商的交易流水,格式统一后可以做成Excel导入功能,Django结合openpyxl库能比较轻松地实现。
多账户支持。源码已经带了用户体系,但单个用户可以建立多个投资账户,比如一个账户做长线、一个做短线。本质是在Position和TradeRecord里加一个account外键,改动不大,但维度多了一层,做收益对比的时候会很方便。
5. 写在最后的一点体会
这套基于Django的股票市值管理系统源码,整体质量在个人项目里算不错的。它没有堆砌花哨的技术,而是靠清晰的数据模型、规整的业务逻辑和完善的后台配置,落地了一个真正能用的工具。对正在学Django的开发者来说,看这套代码能学到数据建模思路、ORM聚合查询、Admin高级配置、事务处理这些实战技巧;对投资者来说,部署起来后日常管理持仓记录、跟踪盈亏,确实比Excel顺手得多。
我实际跑通这套系统后最大的感触是:Django做这类业务系统,真正花时间的不是写代码本身,而是把数据模型设计好。模型理顺了,后面的查询、统计、展示都是水到渠成的事。这套源码恰好把地基打得比较扎实,哪怕把前端的几个页面全部重写,核心的模型和服务层代码依然可以直接复用。如果你们在找一套能二次开发、不会越改越乱的Django入门项目,这套源码是个不错的参考模板。
本文还有配套的精品资源,点击获取