
简介一套基于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_length10, uniqueTrue, verbose_name股票代码) name models.CharField(max_length50, verbose_name股票名称) industry models.CharField(max_length50, blankTrue, verbose_name所属行业) current_price models.DecimalField(max_digits10, decimal_places2, default0, verbose_name最新价) update_time models.DateTimeField(auto_nowTrue, verbose_name更新时间) class Meta: verbose_name 股票信息 verbose_name_plural verbose_name ordering [code] def __str__(self): return f{self.name}({self.code})这里有两个设计细节值得注意。第一股票代码用了uniqueTrue做唯一约束这保证了一只股票只会在表里出现一次。实际使用中无论是手动录入还是从行情接口导入都要以代码作为唯一标识不然容易产生脏数据。第二current_price字段单独存放在股票表里这个设计很常见因为最新价是每只股票的一个”状态“单独存下来方便统一更新。DecimalField而不是FloatField这个选择非常讲究。股票价格涉及金额计算浮点数在计算机里会引入微小的精度误差累加起来可能造成盈亏数字不准确。DecimalField能精确到分这是金融类应用的基本要求源码里在这个细节上没有踩坑。2.2 持仓和交易记录模型接下来是持仓表用来记录当前持有某只股票的数量和成本class Position(models.Model): user models.ForeignKey(User, on_deletemodels.CASCADE, verbose_name用户) stock models.ForeignKey(Stock, on_deletemodels.CASCADE, verbose_name股票) quantity models.IntegerField(default0, verbose_name持仓数量) cost_price models.DecimalField(max_digits10, decimal_places2, default0, verbose_name成本价) update_time models.DateTimeField(auto_nowTrue, 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_deletemodels.CASCADE, verbose_name用户) stock models.ForeignKey(Stock, on_deletemodels.CASCADE, verbose_name股票) direction models.CharField(max_length4, choicesDIRECTION_CHOICES, verbose_name交易方向) price models.DecimalField(max_digits10, decimal_places2, verbose_name成交价格) quantity models.IntegerField(verbose_name成交数量) trade_time models.DateTimeField(auto_now_addTrue, verbose_name成交时间) note models.CharField(max_length200, blankTrue, 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(totalmodels.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( useruser, stockstock, directionBUY, priceprice, quantityquantity, notenote ) position, created Position.objects.get_or_create( useruser, stockstock, 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(useruser, stockstock) if position.quantity quantity: raise ValueError(持仓不足无法卖出) TradeRecord.objects.create( useruser, stockstock, directionSELL, priceprice, quantityquantity, notenote ) 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 fhttps://api.example.com/stock/{stock.code} resp requests.get(url, timeout5) data resp.json() stock.current_price data[price] stock.update_time timezone.now() stock.save() except Exception as e: print(f更新 {stock.code} 失败: {e})这里timeout5很关键调用外部接口时必须设置超时时间否则某个股票接口无响应会卡住整个更新流程。循环里加 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_TZTrue同时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入门项目这套源码是个不错的参考模板。本文还有配套的精品资源点击获取