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)
duanqs