python 读取交割单自动计算每日账户资产

该程序使用Python读取CSV格式的交割单,计算指定日期的账户资产。通过遍历交割单数据,筛选指定日期前的记录,结合证券代码和日期查询收盘价格,计算持仓市值和账户现金,最终得出账户资产总值。程序适用于沪深A股,支持手动补充非交易日或非A股的收盘价格。
摘要由CSDN通过智能技术生成

        最近工作需要,编写了一个程序,实现了使用python读取交割单信息,自动计算每日账户资产。可以为账户收益率计算节省大量时间。

    交割单为csv文件。可以将交易APP中的历史交割单数据导出后进行格式修改使用。

    程序计算量比较小,计算结果直接显示即可。

实现原理:

  1. 将交割单数据从后向前一条一条读取。
  2. 交割日期大于指定日期的不显示,直接跳过。
  3. 交割日期小于等于指定日期的记录,第一条记录中的资金余额为账户资金余额。
  4. 每次新碰到一个证券代码,对应的证券余额是持仓证券的数量,如果不是新的证券代码,说明前面已经显示过了,这样重复证券代码的记录直接忽略。
  5. 证券数量大于0的记录,显示证券代码,证券名称和证券数量。证券数量为零的证券说明已经清仓了,不需要显示。
  6. 根据证券代码和指定日期,查询到对应的收盘价格。根据持仓数量和收盘价格可以算出证券市值。
  7. 持仓市值加上资金余额即账户收盘资产。

注意几点:

  1. 交割单中的证券余额和资金余额要准确。如果有误,需要手工调制。尤其是有红股和分红数据时。
  2. 根据证券代码和指定日期或者收盘价格有限制,只能自动获取A股上市股票的交易日收盘价格。如果指定日期是非交易日,baostock无法获得非交易日的收盘价格。这时候需要更换为交易日的日期。如果是基金,可以将基金对应日期的净值单独存在文件中,这样程序读取文件中的收盘净值即可。

不多说了,直接上代码。


'''
Created on 2021年8月26日

@author: yangs
此程序用于计算证券账户指定日期的资产总值。

第一步:读取对账单到dataframe中,
第二步:取指定日期之后的记录,重复的证券代码的,只显示最后一条记录。数量为0的也不显示。
第三步:根据证券代码和指定日期,获得收盘价格。股票的收盘价格从baostock中获取,并从手动表中补充其他的价格
第四步:计算出收盘后的账户资产值

数据源文件位置需要根据实际情况调整,缺省是当前目录
计算结果只用于显示。本程序较小,运行速度很快,直接显示内容。
'''

import pandas as pd
from datetime import datetime
import baostock as bs

  
class MyTools(object):
    """
    工具类
    """
    def __init__(self):
        pass
    
    def read_tradeFile(self, full_file_name): 
        '''
        读取对账单文件。
        返回格式: dataframe 
        对账单为csv文件,格式如下。
    其中要用到的数据包括交收日期,证券代码,证券余额,资金余额。其他列数据暂时不用
序号,交收日期,证券代码,证券名称,交易类别,成交价格,成交数量,证券余额,成交金额,资金发生数,资金余额,,流水序号,业务标志,业务名称,发生金额,后资金额,货币类别,备注,,,,,,,,,,,,,费用合计,净佣金,规费,印花税,过户费,币种,合同号,资金账号,股东代码
1,2014-06-09,,,银行转存,,,,5475,5475.00 ,5475.00 ,,9,2041,银行转存,5475,5475,人民币,银行返回码[0000]返回信息[[0000]交易成功]|转账成功,,,,,,,,,,,,,,,,,,,,,
2,2014-06-16,,,银行转取,,,,-5475,-5475.00 ,0.00 ,,7,2042,银行转取,-5475,0,人民币,银行返回码[0000]返回信息[[0000]交易成功]|转账成功,,,,,,,,,,,,,,,,,,,,
        
        '''
        tmpdata = pd.read_csv( full_file_name )
        return tmpdata
    
    def read_text_file(self, full_file_name ):
        """
        读取文本文件,返回文件内容。
        返回格式:字符串
        """
        with open(full_file_name, encoding = "utf-8", mode = "r") as f:
            text_file_content = f.read()
        return text_file_content
    
    
    def get_curr_price_manual(self, stock_code, check_date):
        """
        从csv文件中读取证券代码指定日期的收盘价格
        csv文件名称是证券代码加上"收盘价格.csv",每种证券一个文件。
        返回格式:字符串
        csv文件格式如下:日期必须是 2016-06-14 的格式。
code,date,price,name
159902,2016-06-14,3.0970 ,中小板
159902,2016-05-26,3.0490 ,中小板

        """
        file_name = ""+stock_code+"收盘价格.csv" 

        manual_data = self.read_text_file(file_name)
        
        find_str = stock_code+","+check_date
        position = manual_data.find(find_str)
        start_position = position+len(find_str)+1
        end_position = manual_data.find(",",start_position )-1
        close_price = manual_data[start_position : end_position ]
        
        return close_price

        
    def get_spec_date_closeprice(self, stock_code, curr_date):
        """
        获取沪深A股 stock_code 指定日期 curr_date 的收盘价格
        返回值:收盘价格,如果找不到收盘价格,就返回数值0
        """
        stock_code1 = int( stock_code ) #证券代码转为整数
        stock_code2 = "{:0>6d}".format(stock_code1) # 整数转为6位前补0的字串
        first_char = stock_code2[0:1]
        if(first_char == "6"):
            full_stock_code = "sh."+stock_code2
        else:
            full_stock_code = "sz."+stock_code2
                
        rs = bs.query_history_k_data_plus(full_stock_code,
            "date,code,close,preclose,volume,amount,adjustflag,turn,tradestatus,pctChg,isST",
            start_date=curr_date, end_date=curr_date,
            frequency="d", adjustflag="3")
        
        #### 打印结果集 ####
        data_list = []
        while (rs.error_code == '0') & rs.next():
            # 获取一条记录,将记录合并在一起
            data_list.append(rs.get_row_data())
        result = pd.DataFrame(data_list, columns=rs.fields)
        
        #### 结果集输出到csv文件 ####  
        tmpdata = 0
        if( len(result)>0 ):
            tmpdata = result.iloc[0,2]
        return float(tmpdata)  
        
        
class InvestAccount(object):
    '''
    投资账户类
    '''

    def __init__(self, account_name, trade_log_file, create_date):
                
        '''
        初始化账户
        '''
        self.account_name = account_name # 账户名称
        self.notes = ""        # 账户备注信息
        self.creat_date = create_date  # 账户设立日期
        self.close_date = ""   # 账户关闭日期
        self.source_data = pd.DataFrame() #账户交易数据
        
        self.total_asset = 0     #账户资产
        self.cash = 0            #账户现金
        self.net_asset_value = 0 #净值      
        self.return_value = 0    #账户收益值
        self.invest_item = []
        
        my_tools = MyTools()
        
        self.source_data = my_tools.read_tradeFile( trade_log_file )
    
    def show_tradelog(self):
        """
        显示读取的历史交易信息
        """
        print("\n显示历史交易记录\n")
        print(self.source_data)
        print(" =================== 历史交易记录显示完毕 =================== ")   
   
        
    def show_account_info(self,check_date):
        """
        显示账户指定日期的净值。指定日期必须是交易日期。如果不是交易日期,程序会出现异常报错或者数据计算错误。
        
        """
        curr_check_date = datetime.strptime(check_date, '%Y-%m-%d') #将字串转为日期格式
        
        print("\n当前账户名称:\t",self.account_name, end="\t")
        print("检查日期点是:\t", check_date)
        tmpdata = self.source_data
        
        my_tools = MyTools()
        
        # 取指定日期之前的值,包括指定日期
        # 注意,以下的显示是从后向前检查的
        i=tmpdata.shape[0]
        inv_item = []
        total_asset = 0
        
        firstdisplay = 1
        
        while(i>0): #从后先前显示
            i=i-1
            tmp1 = datetime.strptime(tmpdata.iloc[i,1], '%Y-%m-%d') #将字串转为日期格式
            if(tmp1 > curr_check_date ): #日期之后的部分不显示
                continue
            else:
                stock_code = tmpdata.iloc[i,2]
                if(stock_code not in inv_item): #不在已显示列表中的显示,在已显示列表中的不显示
                    inv_item.append(stock_code) #将要显示的代码添加到已显示列表中,后续就不再显示了
                    if(firstdisplay == 1): #是第一个显示,取账户资金余额的数值
                        firstdisplay = 0
                        print("账户资金余额:\t",tmpdata.iloc[i,10])
                        total_asset = total_asset + float(tmpdata.iloc[i,10] )
                        print("当前持仓证券信息:")
                        print("序号\t 证券代码 \t 证券名称 \t 持仓数量 \t 收盘价 \t 参考市值")
                    
                    if(tmpdata.iloc[i,7] > 0.0): # 证券代码数量不为0才显示
                        stock_code = int(tmpdata.iloc[i,2])
                        stock_code = "{:0>6d}".format(stock_code) # 整数转为6位前补0的字串
                        
                        stock_name = tmpdata.iloc[i,3]
                        stock_hold_number = tmpdata.iloc[i,7]
                        print(i,"\t",stock_code,"\t",stock_name,"\t",stock_hold_number, end="\t") #序号在调试的时候有用
                        
                        # 获取价格
                        curr_price = my_tools.get_spec_date_closeprice(stock_code,check_date)
                        
                        if(curr_price ==0):
                            curr_price = my_tools.get_curr_price_manual(stock_code,check_date)
                        print(curr_price, end="\t")
                        print('%.2f' % ( float(stock_hold_number)*float(curr_price)) ,end="\t")
                        total_asset = total_asset + float(stock_hold_number)*float(curr_price)
#                         print('%.2f' % total_asset,end="\t" )
                        print(" ")
                            
        print("账户名称:\t",self.account_name,"\t", check_date,end="\t")
        print('%.2f' % total_asset )
        print("========================== 账户信息显示完毕 ========================= \n\n")


if __name__ == '__main__':
    """
    主程序...
    """
    lg = bs.login()
 
    account_name = "国泰君安"
    trade_log_file = "2006-2015国泰君安账户对账单V2.csv"
    create_date = "2006-08-23"
    gtja_account = InvestAccount(account_name,trade_log_file,create_date)
    
    
    account_name = "广发高新"
    trade_log_file = "2014-2016广发证券账户对账单.csv"
    create_date = "2014-05-28"
    gfgx_account = InvestAccount(account_name,trade_log_file,create_date)
    
    #程序将计算下列日期的收盘市值
    dates= """
2015-05-11
2017-12-04
2017-12-05
2017-12-06
2017-12-07
2017-12-08
"""
    datelist = dates.splitlines(False)
   
    i=1
    for item in datelist:
        if(len(item) > 1):
            gtja_account.show_account_info( item )
            gfgx_account.show_account_info( item )
        i=i+1
    
    bs.logout()
    print("L33 恭喜恭喜,程序运行正常结束。")

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值