Python中投资/股票与期权组合的面向对象设计
我是一名初学者到中级的Python程序员,但我还没有写过应用程序,只写过一些脚本。目前我不太使用面向对象的设计,所以我希望这个项目能帮助我提高这方面的技能。问题是,我不知道从设计的角度该从哪里开始(我知道怎么创建对象和那些东西)。顺便提一下,我是自学的,没有正式的计算机科学教育。
我想尝试写一个程序,用来跟踪我的投资组合中的股票和期权。
我大致知道哪些可以作为对象(比如投资组合、股票、期权等)和方法(比如买入、卖出、更新数据等)。
长仓位是指买入开仓,卖出平仓,而短仓位则是卖出开仓,买入平仓。
portfolio.PlaceOrder(type="BUY", symbol="ABC", date="01/02/2009", price=50.00, qty=100)
portfolio.PlaceOrder(type="SELL", symbol="ABC", date="12/31/2009", price=100.00, qty=25)
portfolio.PlaceOrder(type="SELLSHORT", symbol="XYZ", date="1/2/2009", price=30.00, qty=50)
portfolio.PlaceOrder(type="BUY", symbol="XYZ", date="2/1/2009", price=10.00, qty=50)
那么,一旦调用这个方法,我该如何存储信息呢?起初我想创建一个“仓位”对象,里面有像符号、开盘日期、开盘价格等属性,但考虑到更新仓位以反映销售情况就变得复杂,因为买入和卖出发生在不同的时间和数量上。
- 一次性买入100股,1个价格。卖出4次,4个不同的价格。
- 买入100股。每天卖出1股,持续100天。
- 买入4次,4个不同的价格。一次性卖出全部仓位,1个价格。
一个可能的解决方案是为每一股股票创建一个对象,这样每一股就会有不同的日期和价格。这会不会太占用资源?投资组合可能会有成千上万甚至上百万个小“股”对象。如果你想知道一个仓位的总市值,你需要类似这样的东西:
sum([trade.last_price for trade in portfolio.positions if trade.symbol == "ABC"])
如果你有一个仓位对象,计算就简单多了:
position.last * position.qty
提前感谢你的帮助。从其他帖子来看,显然Stack Overflow是为了“帮助”,而不是“为你写程序”。我觉得我只需要一些方向,指引我走上正确的道路。
附加信息 目的 这个程序会跟踪所有仓位,包括开仓和闭仓;并能够查看详细的盈亏情况。
当我想到详细的盈亏时,我想看到... - 所有的开盘日期(和闭盘日期) - 持有时间 - 开盘价格(闭盘日期) - 自开盘以来的盈亏 - 每天的盈亏
@Senderle
我觉得你可能把“对象”的比喻理解得太字面了,所以试图把一个股票(在某些方面看起来很像对象)变成编程意义上的对象。如果是这样,那就是个错误,我认为这正是juxtapose想表达的观点。
这是我的错误。考虑到“对象”,一个股
对象似乎是个自然的选择。只有当可能有数百万个时,这个想法才显得疯狂。这个周末我会有一些空闲的编码时间,尝试创建一个带数量的对象。
5 个回答
我觉得可以把它分成以下几个部分:
- 持有资产(你现在拥有或欠的每个符号的数量)
- 订单(简单的买入或卖出某个符号的请求,通常是一次性操作)
- 交易(订单的集合)
这样做可以很方便地获取当前的价值、排队订单,还能构建更复杂的订单,同时也能很容易地与后面的数据库数据对象对应起来。
关于你的问题:看起来你对自己的数据模型已经有了比较清晰的想法。不过,我觉得你需要更深入地思考一下这个程序到底想要实现什么。它是要跟踪股票价格的变化吗?下订单,还是建议下订单?或者它只是记录你已经下过的订单?每种用途可能需要不同的处理方式。
话说回来,我不明白为什么你需要为每一股股票都创建一个对象;我不太理解这种做法的理由。即使你想详细记录你的订单历史,也可以只存储汇总数据,比如“在日期z
时,以每股y
美元购买了x
股”。
更合理的做法是创建一个position
对象(或者用Hugh的说法是holding
对象)——每种股票一个,可能还带有一个.order_history
属性,如果你确实需要详细的持股历史。是的,数据库在这种情况下肯定会很有用。
稍微哲学化一下:我觉得你可能把“对象”这个比喻理解得太字面了,所以试图把股票(在某些方面看起来很像对象)变成编程意义上的对象。如果是这样,那就是个错误,我认为这正是juxtapose想表达的观点。
我不同意他关于面向对象设计有缺陷的看法——这可是个很大胆的说法!——但他的回答是对的,因为“对象”(也就是类的实例)几乎和模块**是一样的。它是一些相关函数的集合,这些函数共享某种状态。在类实例中,状态通过self
或this
共享,而在模块中,则通过全局命名空间共享。
在大多数情况下,类实例和模块之间唯一的主要区别是,类实例可以有很多,每个都有自己独立的状态,而模块实例只能有一个。(当然还有其他区别,但大多数时候涉及的技术细节对学习面向对象设计并不重要。)这意味着你可以用类似于思考模块的方式来思考对象,这在这里是个很有用的方法。
**在许多编译语言中,编译模块后生成的文件被称为“对象”文件。我觉得这就是“对象”这个比喻的来源。(我没有确凿的证据!所以如果有人知道得更清楚,欢迎纠正我。)我们常见的面向对象设计的玩具示例——car.drive(mph=50); car.stop(); car.refuel(unleaded, regular)
——我认为是后期形成的,可能会让这个概念有些混淆。
在设计这样的系统时,有两个基本原则你需要记住:
- 消除数据中的重复。没有重复可以确保数据的完整性。
- 保留所有回答任何问题所需的数据,保持最低的详细级别。
根据这些原则,我建议你维护一个交易日志文件。每一笔交易代表某种状态的变化,以及与之相关的所有信息:什么时候、什么、买/卖、多少、多少钱等等。每一笔交易可以用一个记录(这里可以用命名元组)在一个简单的文件中表示。一年的交易记录(甚至5年或10年)应该可以轻松放入内存中的列表中。然后你可以创建一些函数,从这个列表中选择、排序和总结你需要的信息,而且因为它在内存中,所以速度会非常快,比SQL数据库快得多。
当交易日志变得太大或太慢时,你可以计算在某个特定日期(比如年末)你所有持仓的状态,用这个作为下一个时期的初始状态,并把旧的日志文件存档到磁盘上。
你可能还想要一些关于你持仓的辅助信息,比如在某个特定日期的价值/价格,这样你就可以绘制出任何或所有持仓的价值与时间的关系(网上有很多这样的信息来源,比如雅虎财经)。一个包含你每个持仓静态信息的主数据库也会很有用。
我知道这听起来不是很“面向对象”,但面向对象的设计可以用来隐藏系统的详细工作过程,比如用一个TransLog
对象来保存/恢复数据到/从磁盘(保存/打开方法),输入/修改/删除交易;还有其他方法来处理数据并显示有意义的信息。
首先写一个命令行接口的API。当这个工作得让你满意后,如果你愿意,可以继续创建一个图形用户界面(GUI)。
祝你好运,玩得开心!