trade calculator tcalc.py

代码片

# -*- coding: utf-8 -*-

import sys
import getopt
# from qsutil import gspace as gs
from qsutil import pkl

fname='c:\\GTJA\\RichEZ\\newVer\\cnt.pkl'
# cn_dict = gs.pkl.pkl_load(fname)    
cn_dict = pkl.pkl_load(fname)    

class CBondTransaction(object):
    '''可转债日内轮回交易pnl计数器
    python 小程序的脚本可以直接在shell(命令行)解释器里运行:
        1. open anaconda powershell window
        2. PS > cd d:\algolab
        3. 在powershell命令行解释器里运行脚本:
            PS > python  tcalc.py
        3a. 或者在python解释器里运行脚本:
            PS > python 
            >>> import tcalc as cbc
            >>> cbc.CBondTransaction().trade()
        4. enter 4 'input' by the prompt: 
            可转债6位代码, 买入量(张数), 买入价, 卖出价
            code
            volume, buy_price
            sell_price
        5. Result will be displayed like below!!!

Result:
-------
market_id code__ name____ volume ave_b_price  sell_price    pnl   pnl%   comm
.sz       123177 测绘转债   120      149.900      150.920 118.79   0.66   3.61    
    '''
    
    # 定义类属性之: 券商佣金率, 最小佣金
    sz_comm  = 1.0/10000
    sh_comm  = 0.2/10000
    etf_comm = 0.5/10000
    stk_comm = 2.0/10000
    
    # sh_comm_min = 1.0
    cb_comm_min =  1.0
    stk_comm_min = 5.0
    
    stk_tax = 5/10000
    
    def __init__(self, code=None):
        '''类的实例化方法:
            self: 表示类的实例自身, python约定俗成地用self这个保留字
        '''
        self.code=code #设置证券代码属性, 并检查其合规性
        if self.code==None:
            self.code = str(input('please input 六位可转债代码: '))

        if  type(self.code)==int:
            self.code=str(self.code)
        
        assert len(self.code)==6, '证券代码不是6位数.' #提取断言可转债代码是否正确

    def _get_comm_rate(self):
        ''' 依据代码的字头确定品种, 设置相应的佣金率
        '5' 字头的为上海场内基金
        '15'字头的为深圳场内基金
        '12'字头的为深圳可转债
        '11'字头的为上海可转债
        '''
        # set stype, mkt_id, comm_rate, comm_min
        if self.code[0]=='5':
            self.stype, self.mkt_id = 'etf', '.sh'
            self.comm_rate, self.comm_min = self.etf_comm, 0.0
        elif self.code[:2]=='15':
            self.stype, self.mkt_id = 'etf', '.sz'
            self.comm_rate, self.comm_min = self.etf_comm, 0.0
            
        elif self.code[:2]=='12':
            self.stype, self.mkt_id = 'sz_cb', '.sz'
            self.comm_rate, self.comm_min = self.sz_comm, self.cb_comm_min
        elif self.code[:2] == '11':
            self.stype, self.mkt_id = 'sh_cb', '.sh'
            self.comm_rate, self.comm_min = self.sh_comm, self.cb_comm_min
        
        elif self.code[:2] == '60':
            self.stype, self.mkt_id = 'sh_stk', '.sh'
            self.comm_rate, self.comm_min = self.stk_comm, self.stk_comm_min
        elif self.code[:2] == '00':
            self.stype, self.mkt_id = 'sz_stk', '.sz'
            self.comm_rate, self.comm_min = self.stk_comm, self.stk_comm_min
        

    def _buy(self, vp_pair=None):
        '''  
        多次买入时, 需要用分号隔开. 比如有两次买入时可以这样输入:
             90,118.546;30,118.22  
             表示: 
                 开仓: 买入90张, 成交价118.546
                 补仓: 买入30张, 成交价118.220
        单次卖出时, 如果是多笔交割的, 也需要用分号隔开. 比如甩货卖出时分为两笔交割,可以这样输入:
             90,118.546;30,118.22  
             表示: 
                 第一笔交割: 卖出90张, 成交价118.546
                 第二笔交割: 卖出30张, 成交价118.220
        '''
        self._get_comm_rate()
        # self.market_id='.sz' if self.code[:2]=='12' else '.sh'

        if (vp_pair==None):
            # vp_pair = input('please input pair of (buy_volume(张), buy_price): ')
            vp_pair = input('请输入进场量价对(逗号为分界符, 多笔交割时用分号为分界符): ')
        vp_pair = [vp.split(',') for vp in vp_pair.split(';')]
        vp_pair = [(int(vp[0]), float(vp[1])) for vp in vp_pair]
        # print(vp_pair)
        
        self.volume, self.bamount, self.buy_cf, self.bcomm = 0, 0, 0, 0
        # 买入时, 当有多笔交割时, 汇总出: 总量 总金额 佣金 和均价
        for volume, bprice in vp_pair:
            amount = volume * bprice
            bcomm = amount * self.comm_rate
            self.volume += volume
            self.bamount += amount
            self.bcomm += bcomm
        if 'cb' in self.stype: # 可转债, 佣金最低为1元
            self.bcomm = max(self.cb_comm_min, self.bcomm)
        elif 'stk' in self.stype: # 股票, 佣金最低为5元
            self.bcomm = max(self.stk_comm_min, self.bcomm)
        self.buy_cf = -self.bamount - self.bcomm
        self.bprice = self.bamount / self.volume # 平均买入价(未考虑佣金)
        # self.bprice = -self.buy_cf / self.volume  # 持仓成本

    def _sell(self, vp_pair=None):
        '''
        输入卖出时的量价对vp, 可以采用下面的三种方式的任意一种方式:
            多笔交割: 40,115.447;20,115.448;10,115.45
            单笔交割: 115.888
            单笔交割: 70,115.888
        '''
        if vp_pair==None:
            # volume=int(input('please input buy volume:'))
            vp_pair = input('请输入出场量价对或者卖出价(逗号为分界符, 多笔交割时用分号作分界符):  ')
        vp_pair = [vp.split(',') for vp in vp_pair.split(';')]
        if len(vp_pair)>1:  # 一次委托单包括不同价格的多笔交割
            vp_pair = [(int(vp[0]), float(vp[1])) for vp in vp_pair]
            volume = sum([e[0] for e in vp_pair]) # 总量
            amount = sum([e[0] * e[1] for e in vp_pair]) # 总金额
            assert volume==self.volume, '卖出的数量不等于买入的数量!!!'
            self.sprice = amount/volume # 平均卖出价
        elif len(vp_pair)==1:  # 委托单是单笔交割的或者是对应同一价格的多笔交割
            vp_pair=vp_pair[0]
            if len(vp_pair)>1:  # 当输入是两个数字时, 被识别为: 量和价
                volume, self.sprice = int(vp_pair[0]), float(vp_pair[1])
                assert volume==self.volume, '卖出的数量不等于买入的数量!!!'
            else:
                self.sprice =  float(vp_pair[0]) # 当输入是单一数字时, 仅仅被识别为卖出价
                volume = self.volume             # 交割量被认为等于买入量
        
        # amount = self.volume * self.sprice
        amount = volume * self.sprice
        self.scomm = amount * self.comm_rate
        if 'cb' in self.stype: # 可转债, 佣金最低为1元
            self.scomm = max(self.cb_comm_min, self.scomm)
        elif 'stk' in self.stype: # 股票, 佣金最低为5元
            self.scomm = max(self.stk_comm_min, self.scomm)
            self.stax = amount * self.stk_tax # 卖出股票需要缴纳印花税
        self.sell_cf = (amount - self.scomm - self.stax) if 'stk' in self.stype else  (amount - self.scomm )
        
    def trade(self, buy_vp=None, sell_vp=None):
        self._buy(vp_pair=buy_vp)
        self._sell(vp_pair=sell_vp)
        self.pnl     = round(sum((self.buy_cf, self.sell_cf)), 2)
        self.pnl_pct = round((self.pnl / -self.buy_cf) * 100, 2)
        self.totcomm = round(sum((self.bcomm, self.scomm)), 2)
        self.tax = self.stax if 'stk' in self.stype else 0.0
        self.name = cn_dict[self.code+self.mkt_id]
        print('Result:')
        print('-------')
        print('market_id code__ name____ volume ave_b_price  sell_price    pnl   pnl%   comm    tax')
        print(' '.join([
            f'{self.mkt_id:9s}', 
            f'{self.code}', 
            f'{self.name}', 
            f'{self.volume:5d}',
            f'{self.bprice:12.3f}',
            f'{self.sprice:12.3f}', 
            f'{self.pnl:6.2f}',
            f'{self.pnl_pct:6.2f}',
            f'{self.totcomm:6.2f}',
            f'{self.tax:6.2f}',
            ]))
        
        return self


def print_help():
    print(
"""
功能简述: 可转债一轮交易的专用计算器. 运行时支持携带可选参数.
    在shell窗口里的运行方法:
        python [{0}] ... [-h] [-c code | -b buy_vp | -s sell_vp]  
    在IPython控制台窗口里的运行方法:
        %run   [{0}] ... [-h] [-c code | -b buy_vp | -s sell_vp]  

参数说明:
-c     : 六位证券代码
-b     : 买入时的量价对数据
-s     : 卖出时的量价对数据
-h     : 帮助信息

应用例子:
    获取帮助:
        # 在powershell的命令行窗口里
        PS> python cbond.py  -h
        
        # 在spyder的IPython控制台窗口里:
        In [12]:  %run cbond_calc.py  -h
    
    运行脚本:
        PS> python tcalc.py  -c  110052 -b  20,189.99 -s 191.23
        PS> python tcalc.py  -c  110052 -b  20,189.99;40,189.55 -s 191.23
         
        In [12]: %run tcalc.py  -c  110052 -b  20,189.99 -s 191.23
        In [12]: %run tcalc.py  -c  110052 -b  20,189.99;30,189.78 -s 191.23
        In [12]: %run tcalc.py  -c  110052 -b  20,189.99;30,189.78 -s 10,191.23;40,191.347

        %run tcalc.py -c 110077 -b "3600,5.01" -s 5.05
        %run tcalc.py -c 123177 -b "3600,5.01" -s 5.05
        %run tcalc.py -c 513330 -b "3600,5.01" -s 5.05
        %run tcalc.py -c 000539 -b "3600,5.01" -s 5.05
        
    备注:
        用到的代码表资源文件: 'c:\\GTJA\\RichEZ\\newVer\\cnt.pkl'.
        该文件应该经常更新(至少每月一次).
""".format(sys.argv[0]))


if __name__ == '__main__':
    opts, args = getopt.getopt(sys.argv[1:],"hc:b:s:")
    # print(opts) #附带的参数都被解析为字符串的类型, 元组的列表 
    # [('-c', '110052'), ('-b', '20,189.99'), ('-s', '191.23')]
    
    code, buy_vp, sell_vp = "", "", ""
    
    for op,value in opts:
        if op == '-c':
            code = value
        elif op == "-b":
            buy_vp = value
        elif op == "-s":
            sell_vp = value
        elif op == '-h':
            print_help()
            sys.exit()
    # print(code, buy_vp, sell_vp)
    
    t = CBondTransaction(code=code)
    t.trade(buy_vp, sell_vp)
    
posted @ 2024-01-31 23:08  duanqs  阅读(22)  评论(0编辑  收藏  举报