{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# backtrader教程\n", "\n", "**by Zoe**\n", "\n", "
\n", "
\n", "
\n", "\n", "backtrader是基于Python的量化框架平台,内置了talib指标库、analyzer分析库等功能,具有回测速度快、易用性高以及扩展性好等特点。相较于网上已有教程,本教程从数据至分析进行了全面的介绍,着重阐述了数据清洗以及框架调整等部分,点出了backtrader的某些不足并提供了解决方法。\n", "
" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from datetime import datetime\n", "import backtrader as bt\n", "import tushare as ts\n", "import pandas as pd\n", "from backtrader.feeds import PandasData" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. 数据加载\n", "### 1.1. data collecting\n", "\n", "backtrader支持Yahoo等online的数据源和本地数据源。大家也可以自己造轮子,接wind,tushare等。\n", "backtrader要求至少含有7列数据,包括datetime,open,high,low,close,volume,openinterest。本文把datetime设为了index。\n", "\n", "本文拉前复权数据,前复权就是从后往前复权,所以复权后最近的价格保持一致。至于后复权,vice versa。有些数据源的前复权机制会有负数的存在,这是因为计算复权的时候是价格减去了分红,而不是用的累计复权因子,https://xueqiu.com/3488649239/62074848 这篇文章已经介绍得很详细,本文就不抛砖引玉了。注意:在用累计因子的情况下,前后复权的选择不影响收益率。" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "# tushare\n", "def get_single_kdata(code, start='2020-01-01', end='2021-08-18', index=False):\n", " df = ts.get_k_data(code, autype='qfq', start=start, end=end, index=index)\n", " df['date'] = pd.to_datetime(df['date'])\n", " df['openinterest'] = 0\n", " return df[['date', 'open', 'high', 'low', 'close', 'volume', 'openinterest']]\n", "\n", "# # yahoo\n", "# def get_single_kdata(code, start='2020-01-01', end='2021-08-18'):\n", "# df = bt.feeds.YahooFinanceData(\n", "# dataname='AAPL',\n", "# fromdate=datetime.strptime(start,'%Y-%m-%d'),\n", "# todate=datetime.strptime(end,'%Y-%m-%d'),\n", "# adjclose=True,\n", "# adjvolume=True)\n", "# return df" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " date open high low close volume openinterest\n", "0 2020-01-02 16.65 16.95 16.55 16.87 1530231.0 0\n", "1 2020-01-03 16.94 17.31 16.92 17.18 1116194.0 0\n", "2 2020-01-06 17.01 17.34 16.91 17.07 862083.0 0\n", "3 2020-01-07 17.13 17.28 16.95 17.15 728607.0 0\n", "4 2020-01-08 17.00 17.05 16.63 16.66 847824.0 0\n" ] } ], "source": [ "secu_lst = ['600000','000001']\n", "kdata = {}\n", "for secu in secu_lst:\n", " kdata[secu] = get_single_kdata(secu)\n", " \n", "print(kdata['000001'].head())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "对于一个策略,股票池常常是不断变化的,不可能只有一个起点和终点。" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " date open high low close volume openinterest\n", "0 2020-02-03 13.99 14.70 13.99 13.99 2259194.0 0\n", "1 2020-02-04 14.05 14.66 14.02 14.60 1706172.0 0\n", "2 2020-02-05 14.59 14.89 14.32 14.63 1491380.0 0\n", "3 2020-02-06 14.81 14.87 14.51 14.77 1185815.0 0\n", "4 2020-02-07 14.60 14.69 14.41 14.62 924852.0 0\n" ] } ], "source": [ "# 如果你想要的不同的start和end\n", "# 自己定义\n", "secu_lst = {'600000':{'start':'2020-01-01','end':'2020-07-18'},\n", " '000001':{'start':'2020-02-01','end':'2020-08-18'}}\n", "kdata = {}\n", "for secu in secu_lst.keys():\n", " kdata[secu] = get_single_kdata(secu, secu_lst[secu]['start'], secu_lst[secu]['end']).reset_index(drop=True)\n", " \n", "print(kdata['000001'].head())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 1.2. data cleansing and processing\n", "\n", "上述股票的日期并没有对齐,造成开始停止日不同的原因还有股池的筛选条件、新股上市、股票停牌等。而backtrader的运作机制next()需要all data都满足最小周期,否则需要调用prenext()。本文建议一个更简单的方法,补充数据使所有股票拥有同样的周期,并同时标注停牌日。" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "start: 2020-01-01, end 2020-08-18\n", "start: 2020-01-02 00:00:00, end 2020-08-18 00:00:00\n" ] } ], "source": [ "# 先拿大盘trading_dates\n", "benchmark_start = min(secu_lst.values(), key=lambda x : x['start'])['start']\n", "benchmark_end = max(secu_lst.values(), key=lambda x : x['end'])['end']\n", "print(f\"start: {benchmark_start}, end {benchmark_end}\")\n", "\n", "benchmark = '399905' # 中证500\n", "kdata['benchmark'] = get_single_kdata(benchmark, benchmark_start, benchmark_end, index=True) \n", "trading_dates = kdata['benchmark']['date'].tolist()\n", "print(f\"start: {trading_dates[0]}, end {trading_dates[-1]}\")" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "000001\n", " open high low close volume openinterest suspend\n", "date \n", "2020-01-02 0.00 0.00 0.00 0.00 0.0 0.0 1\n", "2020-01-03 0.00 0.00 0.00 0.00 0.0 0.0 1\n", "2020-01-06 0.00 0.00 0.00 0.00 0.0 0.0 1\n", "2020-01-07 0.00 0.00 0.00 0.00 0.0 0.0 1\n", "2020-01-08 0.00 0.00 0.00 0.00 0.0 0.0 1\n", "... ... ... ... ... ... ... ...\n", "2020-08-12 14.21 14.50 14.15 14.38 1596812.0 0.0 0\n", "2020-08-13 14.40 14.46 14.14 14.18 837262.0 0.0 0\n", "2020-08-14 14.10 14.51 14.06 14.47 1103216.0 0.0 0\n", "2020-08-17 14.60 15.35 14.55 15.19 3268028.0 0.0 0\n", "2020-08-18 15.20 15.30 14.91 15.15 1350261.0 0.0 0\n", "\n", "[152 rows x 7 columns]\n", "600000\n", " open high low close volume openinterest suspend\n", "date \n", "2020-01-02 12.47 12.64 12.45 12.47 516290.0 0.0 0\n", "2020-01-03 12.57 12.63 12.47 12.60 380188.0 0.0 0\n", "2020-01-06 12.52 12.65 12.42 12.46 410011.0 0.0 0\n", "2020-01-07 12.51 12.60 12.46 12.50 284214.0 0.0 0\n", "2020-01-08 12.41 12.45 12.25 12.32 352405.0 0.0 0\n", "... ... ... ... ... ... ... ...\n", "2020-08-12 11.37 11.39 11.17 11.27 686395.0 0.0 1\n", "2020-08-13 11.37 11.39 11.17 11.27 686395.0 0.0 1\n", "2020-08-14 11.37 11.39 11.17 11.27 686395.0 0.0 1\n", "2020-08-17 11.37 11.39 11.17 11.27 686395.0 0.0 1\n", "2020-08-18 11.37 11.39 11.17 11.27 686395.0 0.0 1\n", "\n", "[152 rows x 7 columns]\n" ] } ], "source": [ "for secu in set(kdata.keys())-set(['benchmark']):\n", " print(secu)\n", " secu_kdata = kdata['benchmark'][['date']].merge(kdata[secu],how='left')\n", " secu_kdata['suspend'] = 0 \n", " secu_kdata.loc[secu_kdata['open'].isnull(), 'suspend'] = 1 # 标记为停盘日\n", " secu_kdata.set_index(['date'], inplace = True) # 设date为index\n", " end = secu_lst[secu]['end']\n", " secu_kdata.fillna(method='ffill',inplace = True) # start后的数据用前日数据进行补充\n", " secu_kdata.fillna(value = 0,inplace = True) #start前的数据用0补充\n", " secu_kdata.loc[(secu_kdata.index > end), 'suspend'] = 1\n", " print(secu_kdata)\n", " kdata[secu] = secu_kdata" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "股票池的筛选也同理,补充数据就ok,但要注意,end是剔除日的后一根bar的日期。\n", "整理成一个class会更清晰一点" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "class GetKdatas(object):\n", " def __init__(self, secu_lst, benchmark='000001'):\n", " \"\"\"\n", " :parameter secu_lst: a dict contained stocks with starts and ends\n", " :parameter benchmark: the name of benchmark\n", " \"\"\"\n", " self.secu_lst = secu_lst\n", " self.benchmark = benchmark\n", "\n", " @staticmethod\n", " def get_single_kdata(code, start='2020-01-01', end='2021-08-18', index=False):\n", " df = ts.get_k_data(code, autype='qfq', start=start, end=end, index=index)\n", " df['date'] = pd.to_datetime(df['date'])\n", " df['openinterest'] = 0\n", " return df[['date', 'open', 'high', 'low', 'close', 'volume', 'openinterest']]\n", "\n", " def get_all_kdata(self):\n", " kdata = {}\n", " for secu in set(self.secu_lst):\n", " secu_kdata = self.get_single_kdata(secu, self.secu_lst[secu]['start'], self.secu_lst[secu]['end'])\n", " kdata[secu] = secu_kdata.reset_index(drop=True)\n", " return kdata\n", "\n", " def merge_period(self):\n", " all_kdata = self.get_all_kdata()\n", " benchmark_start = min(self.secu_lst.values(), key=lambda x: x['start'])['start']\n", " benchmark_end = max(self.secu_lst.values(), key=lambda x: x['end'])['end']\n", " all_kdata['benchmark'] = self.get_single_kdata(self.benchmark, benchmark_start, benchmark_end,True)\n", "\n", " for secu in set(all_kdata.keys()) - set(['benchmark']):\n", " secu_kdata = all_kdata['benchmark'][['date']].merge(all_kdata[secu], how='left')\n", " secu_kdata['suspend'] = 0\n", " secu_kdata.loc[secu_kdata['open'].isnull(), 'suspend'] = 1 # 标记为停盘日\n", " secu_kdata.set_index(['date'], inplace=True) # 设date为index\n", " end = secu_lst[secu]['end']\n", " secu_kdata.fillna(method='ffill', inplace=True) # start后的数据用前日数据进行补充\n", " secu_kdata.fillna(value=0, inplace=True) # start前的数据用0补充\n", " secu_kdata.loc[(secu_kdata.index > end), 'suspend'] = 1\n", " all_kdata[secu] = secu_kdata\n", "\n", " _ = all_kdata.pop('benchmark')\n", " return all_kdata" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'000001': open high low close volume openinterest suspend\n", "date \n", "2020-01-02 0.00 0.00 0.00 0.00 0.0 0.0 1\n", "2020-01-03 0.00 0.00 0.00 0.00 0.0 0.0 1\n", "2020-01-06 0.00 0.00 0.00 0.00 0.0 0.0 1\n", "2020-01-07 0.00 0.00 0.00 0.00 0.0 0.0 1\n", "2020-01-08 0.00 0.00 0.00 0.00 0.0 0.0 1\n", "... ... ... ... ... ... ... ...\n", "2020-08-12 14.21 14.50 14.15 14.38 1596812.0 0.0 0\n", "2020-08-13 14.40 14.46 14.14 14.18 837262.0 0.0 0\n", "2020-08-14 14.10 14.51 14.06 14.47 1103216.0 0.0 0\n", "2020-08-17 14.60 15.35 14.55 15.19 3268028.0 0.0 0\n", "2020-08-18 15.20 15.30 14.91 15.15 1350261.0 0.0 0\n", "\n", "[152 rows x 7 columns], '600000': open high low close volume openinterest suspend\n", "date \n", "2020-01-02 12.47 12.64 12.45 12.47 516290.0 0.0 0\n", "2020-01-03 12.57 12.63 12.47 12.60 380188.0 0.0 0\n", "2020-01-06 12.52 12.65 12.42 12.46 410011.0 0.0 0\n", "2020-01-07 12.51 12.60 12.46 12.50 284214.0 0.0 0\n", "2020-01-08 12.41 12.45 12.25 12.32 352405.0 0.0 0\n", "... ... ... ... ... ... ... ...\n", "2020-08-12 11.37 11.39 11.17 11.27 686395.0 0.0 1\n", "2020-08-13 11.37 11.39 11.17 11.27 686395.0 0.0 1\n", "2020-08-14 11.37 11.39 11.17 11.27 686395.0 0.0 1\n", "2020-08-17 11.37 11.39 11.17 11.27 686395.0 0.0 1\n", "2020-08-18 11.37 11.39 11.17 11.27 686395.0 0.0 1\n", "\n", "[152 rows x 7 columns]}\n" ] } ], "source": [ "secu_lst = {'600000': {'start': '2020-01-01', 'end': '2020-07-18'},\n", " '000001': {'start': '2020-02-01', 'end': '2020-08-18'}}\n", "\n", "kdata = GetKdatas(secu_lst).merge_period()\n", "print(kdata)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n", "### 1.3. data feeding\n", "\n", "接下来就是放入数据,backtrader默认仅datetime,open,high,low,close,volume,openinterest这7列。\n", "
\n", "默认顺序为open (default: 1) , high (default: 2), low (default: 3), close (default: 4), volume (default: 5), openinterest (default: 6)。\n", "
\n", "一定,一定要记得在add时加入*name=secu*,不然回溯股票的代码很麻烦" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "cerebro = bt.Cerebro()\n", "for secu in set(kdata.keys())-set(['benchmark']):\n", " df = PandasData(dataname=kdata[secu], fromdate=kdata[secu].index[0],todate=kdata[secu].index[-1])\n", " cerebro.adddata(df, name=secu) \n", "\n", "# 如果是本地csv,如果你的column是和默认一样的,那从datetime开始都不用写,否则必须指明\n", "# data = btfeeds.GenericCSVData(\n", "# dataname='mydata.csv',\n", "# fromdate=benchmark_start,\n", "# todate=benchmark_end,\n", "# dtformat=('%Y-%m-%d'),\n", "# datetime=0,\n", "# high=1,\n", "# low=2,\n", "# open=3,\n", "# close=4,\n", "# volume=5,\n", "# openinterest=-1\n", "# )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**如果你要增添其他的指标,可以根据以下模块。注意放入数据的时候是PandasData_Extend。如果是load本地文件,就把PandasData换成GenericCSVData,序号从序列8开始。" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "class PandasData_Extend(PandasData):\n", " # Add a 'suspend' line to the inherited ones from the base class\n", " lines = ('suspend',)\n", " # 现在是(open,0), ... , (openinterest,5)这6列,所以增加1\n", " # add the parameter to the parameters inherited from the base class\n", " params = (('suspend', 6),)\n", " \n", " # 如果是csv文档,openinterest 在 GenericCSVData index是7,所以1\n", " # params = (('suspend', 8),)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. 框架介绍\n", "### 2.1. 初始资金、买卖佣金设置\n", "\n", "初始资金默认10000,但众所周知,初始金额的大小会影响整个策略收益。如果过小,会买不进某些大盘股。如果过大,会导致小盘股的买入划价增加很多,甚至无法购买预期数量的小盘股,因此需要设置初始资金。" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "初始资金1000000\n" ] } ], "source": [ "cerebro = bt.Cerebro()\n", "# 设置初始资本为1 million\n", "startcash = 10**6\n", "cerebro.broker.setcash(startcash)\n", "print(f\"初始资金{cerebro.broker.getvalue()}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "费率也是一大问题,券商佣金最低收五元,沪市过户费最低收一元。不要小看这些费率,当你做高频交易的时候,你的利润可能大都被这些费率吃掉了。\n", "
\n", "本文为简略,过户费没有进行对市场的判断。如需,加上判断即可。" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "class CommInfoPro(bt.CommInfoBase):\n", "\n", " params = (\n", " ('stamp_duty', 0.001), # 印花税率\n", " ('stamp_duty_fe', 1), # 最低印花税\n", " ('commission', 0.001), # 佣金率\n", " ('commission_fee', 5), # 最低佣金费\n", " ('stocklike', True), # 股票\n", " ('commtype', bt.CommInfoBase.COMM_PERC), # 按比例收\n", " )\n", "\n", " def _getcommission(self, size, price, pseudoexec):\n", " '''\n", " If size is greater than 0, this indicates a long / buying of shares.\n", " If size is less than 0, it idicates a short / selling of shares.\n", " '''\n", "\n", " if size > 0: # 买入,不考虑印花税\n", " return max(size * price * self.p.commission, self.p.commission_fee)\n", " elif size < 0: # 卖出,考虑印花税\n", " return max(size * price * (self.p.stamp_duty + self.p.commission), self.p.stamp_duty_fe)\n", " else:\n", " return 0 # just in case for some reason the size is 0.\n", "\n", " \n", "cerebro.broker.addcommissioninfo(CommInfoPro())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.2. 下单函数调整\n", "众所周知,A股市以每手100股成交的,所以order size必须为100的整数倍。因此我们在计算的时候也应该注意这一点。一种方法是在backtrader内部修改order函数,在计算amount的时候downcast,因为jupyter不好展示这一步骤。本文通过在order的时候计算这一amount来解决。\n", "\n", "注意:都往下约,保持现金充足" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "400\n", "500\n" ] } ], "source": [ "# 原有\n", "#self.buy(data, amount)\n", "#self.sell(data, amount)\n", "\n", "#改为\n", "def downcast(amount, lot):\n", " return abs(amount//lot*lot)\n", "\n", "print(downcast(418,100))\n", "print(downcast(-418,100))\n", "#self.buy(data, downcast(amount, lot))\n", "#self.sell(data, downcast(amount, lot))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.3. 增加股票信息\n", "\n", "backtrader把股票名设为了data._name,本文不建议通过这种方式进行抽取。最好是在linseries.py里加入装饰器@property\n", "" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [], "source": [ "# in linseries.py\n", "# @property\n", "# def name(self):\n", "# return self._name" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 2.4 data和line的介绍\n", "\n", "line是backtrader的核心,是储存数据的重要形式。多条line组成lines,包括在data feed模块放入的open(开盘价)、close(收盘价)等6条line。可以把line看成是一条时间线,0代表当下的数据,-1代表前一根bar的数据,-2代表前两根bar的数据;同样,1代表后一根bar的数据,2代表后两根bar的数据,以此内推。\n", "\n", "data是line的集合,是line的展现形式。data代表单个个股(商品、债券等)的数据,包括open、close等lines,而多个data组成list形式的datas。在取某只股票的data时,可以通过self.datas[x]或者self.datax,x代表该股票被放入的顺序号(从0开始),也可以通过self.datasbyname[data._name]取得。\n", "\n", "backtrader也提供了指标的不同提取方式。以data0的close举例,包括:self.data0.close[0],self.data0_close[0],self.data0_0[0](第二个0代表close的顺序号0)。" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. 策略回测\n", "### 3.1. 单只股票\n", "接下来就开始单只股票的回测示范。本策略逻辑收盘价>20日均线就买入,否则卖出。\n", "由于指标简单,可以在init里直接,计算节省时间。否则需要在next里每次进行计算\n", "1. self.params.your_param等价于self.params.maperiod\n", "2. self.datas是list格式,因为仅单只股票,所有长度为1。self.datas[0]代表的是这只股票的数据\n", "\n", "\n", "在backtrader的order属性里,也是不包含股票的name。一种方法是修改order的class,本文从简,将name作为参数传入。\n", "
\n", "在买入卖出时,可以根据以下常用方法\n", "1. self.order_target_percent(secu_data, target_pct, name=secu)\n", "2. self.order_target_value(secu_data, target_val, name=secu)\n", "3. self.buy(secu_data, order_amount, name=secu)\n", "4. self.sell(secu_data, order_amount, name=secu)\n", "\n", "注意:本策略是以日为单位运行,以第二日开盘价作为交割价。backtrader的运行机制是以购买的资金/今日收盘价,得到购买数量后第二日开盘买入。所以买卖时为避免第二日开盘价与今日收盘价的差价,不能以全仓买入。 且在最后一天不做任何下单买卖。\n", "
\n", "注意:股票剔除是当下bar做出的决定,但是在后一根bar,也就是end_date进行的买卖。所以一定要放入后一根bar的开盘数据作为交割价,不然有未来函数。在策略购买卖中,需要规范在股票剔除日(end_date)必须卖出。" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "初始资金1000000\n", "2020-02-06, 1000000.00元\n", "2020-02-07, 1000000.00元\n", "2020-02-10, 1000000.00元\n", "2020-02-11, 1000000.00元\n", "2020-02-12, 1000000.00元\n", "2020-02-13, 1000000.00元\n", "2020-02-14, 1000000.00元\n", "2020-02-17, 1000000.00元\n", "2020-02-18, 1000000.00元\n", "2020-02-19, 1000000.00元\n", "2020-02-20, 1000000.00元\n", "2020-02-20, 买600000, price:11.23, amout: 87200.0\n", "2020-02-21, 买入600000, 成交量87200.0,成交价11.23\n", "2020-02-21, 1006094.21元\n", "2020-02-21, 卖600000, price:11.30, pct: 0\n", "2020-02-24, 卖出600000, 成交量-87200.0,成交价11.23\n", "2020-02-24, 999989.21元\n", "2020-02-24, 买600000, price:11.16, amout: 87800.0\n", "2020-02-25, 买入600000, 成交量87800.0,成交价11.07\n", "2020-02-25, 1001735.49元\n", "2020-02-25, 卖600000, price:11.09, pct: 0\n", "2020-02-26, 卖出600000, 成交量-87800.0,成交价11.01\n", "2020-02-26, 994710.49元\n", "2020-02-26, 买600000, price:11.20, amout: 87000.0\n", "2020-02-27, 买入600000, 成交量87000.0,成交价11.20\n", "2020-02-27, 995570.74元\n", "2020-02-27, 卖600000, price:11.21, pct: 0\n", "2020-02-28, 卖出600000, 成交量-87000.0,成交价11.11\n", "2020-02-28, 986869.74元\n", "2020-03-02, 986869.74元\n", "2020-03-02, 买600000, price:11.04, amout: 87600.0\n", "2020-03-03, 买入600000, 成交量87600.0,成交价11.13\n", "2020-03-03, 980727.99元\n", "2020-03-03, 卖600000, price:11.06, pct: 0\n", "2020-03-04, 卖出600000, 成交量-87600.0,成交价11.01\n", "2020-03-04, 976346.99元\n", "2020-03-04, 买600000, price:11.03, amout: 86700.0\n", "2020-03-05, 买入600000, 成交量86700.0,成交价11.08\n", "2020-03-05, 997145.39元\n", "2020-03-05, 卖600000, price:11.32, pct: 0\n", "2020-03-06, 卖出600000, 成交量-86700.0,成交价11.23\n", "2020-03-06, 989341.39元\n", "2020-03-06, 买600000, price:11.12, amout: 87100.0\n", "2020-03-09, 买入600000, 成交量87100.0,成交价11.00\n", "2020-03-09, 970169.81元\n", "2020-03-10, 978008.81元\n", "2020-03-11, 969298.81元\n", "2020-03-12, 957975.81元\n", "2020-03-13, 963201.81元\n", "2020-03-16, 946652.81元\n", "2020-03-17, 939684.81元\n", "2020-03-18, 920522.81元\n", "2020-03-19, 897005.81元\n", "2020-03-20, 910070.81元\n", "2020-03-23, 888295.81元\n", "2020-03-24, 907457.81元\n", "2020-03-25, 915296.81元\n", "2020-03-26, 922264.81元\n", "2020-03-27, 923135.81元\n", "2020-03-30, 926619.81元\n", "2020-03-31, 915296.81元\n", "2020-04-01, 910070.81元\n", "2020-04-02, 919651.81元\n", "2020-04-03, 915296.81元\n", "2020-04-07, 926619.81元\n", "2020-04-08, 923135.81元\n", "2020-04-09, 919651.81元\n", "2020-04-10, 920522.81元\n", "2020-04-10, 卖600000, price:10.21, pct: 0\n", "2020-04-13, 卖出600000, 成交量-87100.0,成交价10.15\n", "2020-04-13, 915295.81元\n", "2020-04-14, 915295.81元\n", "2020-04-14, 买600000, price:10.25, amout: 87500.0\n", "2020-04-15, 买入600000, 成交量87500.0,成交价10.20\n", "2020-04-15, 913536.88元\n", "2020-04-15, 卖600000, price:10.18, pct: 0\n", "2020-04-16, 卖出600000, 成交量-87500.0,成交价10.10\n", "2020-04-16, 906535.88元\n", "2020-04-16, 买600000, price:10.16, amout: 87400.0\n", "2020-04-17, 买入600000, 成交量87400.0,成交价10.18\n", "2020-04-17, 908274.98元\n", "2020-04-17, 卖600000, price:10.20, pct: 0\n", "2020-04-20, 卖出600000, 成交量-87400.0,成交价10.15\n", "2020-04-20, 903903.98元\n", "2020-04-21, 903903.98元\n", "2020-04-22, 903903.98元\n", "2020-04-23, 903903.98元\n", "2020-04-24, 903903.98元\n", "2020-04-27, 903903.98元\n", "2020-04-27, 买600000, price:10.34, amout: 85600.0\n", "2020-04-28, 买入600000, 成交量85600.0,成交价10.36\n", "2020-04-28, 901327.12元\n", "2020-04-28, 卖600000, price:10.33, pct: 0\n", "2020-04-29, 卖出600000, 成交量-85600.0,成交价10.35\n", "2020-04-29, 903038.12元\n", "2020-04-29, 买600000, price:10.61, amout: 83400.0\n", "2020-04-30, 买入600000, 成交量83400.0,成交价10.58\n", "2020-04-30, 907199.29元\n", "2020-04-30, 卖600000, price:10.63, pct: 0\n", "2020-05-06, 卖出600000, 成交量-83400.0,成交价10.44\n", "2020-05-06, 891352.29元\n", "2020-05-06, 买600000, price:10.46, amout: 83500.0\n", "2020-05-07, 买入600000, 成交量83500.0,成交价10.45\n", "2020-05-07, 886333.57元\n", "2020-05-07, 卖600000, price:10.39, pct: 0\n", "2020-05-08, 卖出600000, 成交量-83500.0,成交价10.44\n", "2020-05-08, 890507.57元\n", "2020-05-08, 买600000, price:10.44, amout: 83500.0\n", "2020-05-11, 买入600000, 成交量83500.0,成交价10.44\n", "2020-05-11, 889663.85元\n", "2020-05-11, 卖600000, price:10.43, pct: 0\n", "2020-05-12, 卖出600000, 成交量-83500.0,成交价10.44\n", "2020-05-12, 890497.85元\n", "2020-05-12, 买600000, price:10.34, amout: 84300.0\n", "2020-05-13, 买入600000, 成交量84300.0,成交价10.31\n", "2020-05-13, 896390.16元\n", "2020-05-13, 卖600000, price:10.38, pct: 0\n", "2020-05-14, 卖出600000, 成交量-84300.0,成交价10.32\n", "2020-05-14, 891331.16元\n", "2020-05-14, 买600000, price:10.30, amout: 84800.0\n", "2020-05-15, 买入600000, 成交量84800.0,成交价10.36\n", "2020-05-15, 884538.37元\n", "2020-05-18, 887930.37元\n", "2020-05-18, 卖600000, price:10.32, pct: 0\n", "2020-05-19, 卖出600000, 成交量-84800.0,成交价10.45\n", "2020-05-19, 898953.37元\n", "2020-05-19, 买600000, price:10.35, amout: 85100.0\n", "2020-05-20, 买入600000, 成交量85100.0,成交价10.37\n", "2020-05-20, 902348.55元\n", "2020-05-20, 卖600000, price:10.41, pct: 0\n", "2020-05-21, 卖出600000, 成交量-85100.0,成交价10.44\n", "2020-05-21, 904900.55元\n", "2020-05-21, 买600000, price:10.33, amout: 85800.0\n", "2020-05-22, 买入600000, 成交量85800.0,成交价10.30\n", "2020-05-22, 891163.71元\n", "2020-05-25, 902317.71元\n", "2020-05-26, 902317.71元\n", "2020-05-27, 909181.71元\n", "2020-05-28, 924625.71元\n", "2020-05-28, 卖600000, price:10.53, pct: 0\n", "2020-05-29, 卖出600000, 成交量-85800.0,成交价10.45\n", "2020-05-29, 917760.71元\n", "2020-05-29, 买600000, price:10.57, amout: 85000.0\n", "2020-06-01, 买入600000, 成交量85000.0,成交价10.63\n", "2020-06-01, 919451.68元\n", "2020-06-01, 卖600000, price:10.65, pct: 0\n", "2020-06-02, 卖出600000, 成交量-85000.0,成交价10.58\n", "2020-06-02, 913500.68元\n", "2020-06-02, 买600000, price:10.68, amout: 83800.0\n", "2020-06-03, 买入600000, 成交量83800.0,成交价10.75\n", "2020-06-03, 909301.67元\n", "2020-06-03, 卖600000, price:10.70, pct: 0\n", "2020-06-04, 卖出600000, 成交量-83800.0,成交价10.80\n", "2020-06-04, 917680.67元\n", "2020-06-04, 买600000, price:10.64, amout: 84500.0\n", "2020-06-05, 买入600000, 成交量84500.0,成交价10.68\n", "2020-06-05, 916826.64元\n", "2020-06-05, 卖600000, price:10.67, pct: 0\n", "2020-06-08, 卖出600000, 成交量-84500.0,成交价10.64\n", "2020-06-08, 914290.64元\n", "2020-06-08, 买600000, price:10.61, amout: 84400.0\n", "2020-06-09, 买入600000, 成交量84400.0,成交价10.62\n", "2020-06-09, 919345.68元\n", "2020-06-09, 卖600000, price:10.68, pct: 0\n", "2020-06-10, 卖出600000, 成交量-84400.0,成交价10.71\n", "2020-06-10, 921876.68元\n", "2020-06-10, 买600000, price:10.58, amout: 85300.0\n", "2020-06-11, 买入600000, 成交量85300.0,成交价10.58\n", "2020-06-11, 913337.65元\n", "2020-06-11, 卖600000, price:10.48, pct: 0\n", "2020-06-12, 卖出600000, 成交量-85300.0,成交价10.58\n", "2020-06-12, 921866.65元\n", "2020-06-12, 买600000, price:10.55, amout: 85600.0\n", "2020-06-15, 买入600000, 成交量85600.0,成交价10.41\n", "2020-06-15, 916721.74元\n", "2020-06-16, 925281.74元\n", "2020-06-17, 926993.74元\n", "2020-06-18, 929561.74元\n", "2020-06-19, 938977.74元\n", "2020-06-19, 卖600000, price:10.61, pct: 0\n", "2020-06-22, 卖出600000, 成交量-85600.0,成交价10.57\n", "2020-06-22, 935552.74元\n", "2020-06-22, 买600000, price:10.55, amout: 86900.0\n", "2020-06-23, 买入600000, 成交量86900.0,成交价10.51\n", "2020-06-23, 932936.61元\n", "2020-06-24, 943364.61元\n", "2020-06-24, 卖600000, price:10.60, pct: 0\n", "2020-06-29, 卖出600000, 成交量-86900.0,成交价10.63\n", "2020-06-29, 945970.61元\n", "2020-06-29, 买600000, price:10.57, amout: 87700.0\n", "2020-06-30, 买入600000, 成交量87700.0,成交价10.59\n", "2020-06-30, 945084.32元\n", "2020-06-30, 卖600000, price:10.58, pct: 0\n", "2020-07-01, 卖出600000, 成交量-87700.0,成交价10.59\n", "2020-07-01, 945960.32元\n", "2020-07-01, 买600000, price:10.74, amout: 86300.0\n", "2020-07-02, 买入600000, 成交量86300.0,成交价10.73\n", "2020-07-02, 973567.06元\n", "2020-07-02, 卖600000, price:11.05, pct: 0\n", "2020-07-03, 卖出600000, 成交量-86300.0,成交价11.08\n", "2020-07-03, 976155.06元\n", "2020-07-03, 买600000, price:11.19, amout: 85400.0\n", "2020-07-06, 买入600000, 成交量85400.0,成交价11.30\n", "2020-07-06, 1053005.41元\n", "2020-07-06, 卖600000, price:12.20, pct: 0\n", "2020-07-07, 卖出600000, 成交量-85400.0,成交价12.45\n", "2020-07-07, 1074354.41元\n", "2020-07-07, 买600000, price:12.11, amout: 86900.0\n", "2020-07-08, 买入600000, 成交量86900.0,成交价12.17\n", "2020-07-08, 1071736.84元\n", "2020-07-08, 卖600000, price:12.14, pct: 0\n", "2020-07-09, 卖出600000, 成交量-86900.0,成交价12.26\n", "2020-07-09, 1082163.84元\n", "2020-07-09, 买600000, price:11.99, amout: 88400.0\n", "2020-07-10, 买入600000, 成交量88400.0,成交价11.98\n", "2020-07-10, 1048561.25元\n", "2020-07-10, 卖600000, price:11.60, pct: 0\n", "2020-07-13, 卖出600000, 成交量-88400.0,成交价11.60\n", "2020-07-13, 1048560.25元\n", "2020-07-13, 买600000, price:11.66, amout: 88100.0\n", "2020-07-14, 买入600000, 成交量88100.0,成交价11.61\n", "2020-07-14, 1028287.02元\n", "2020-07-14, 卖600000, price:11.38, pct: 0\n", "2020-07-15, 卖出600000, 成交量-88100.0,成交价11.47\n", "2020-07-15, 1036215.02元\n", "2020-07-15, 买600000, price:11.18, amout: 90800.0\n", "2020-07-16, 买入600000, 成交量90800.0,成交价11.25\n", "2020-07-16, 1031664.80元\n", "2020-07-16, 卖600000, price:11.20, pct: 0\n", "2020-07-17, 卖出600000, 成交量-90800.0,成交价11.37\n", "结束资金: 1047099.8\n" ] } ], "source": [ "class single_strategy(bt.Strategy):\n", " # 全局设定交易策略的参数\n", " params = (\n", " ('maperiod', 20),\n", " )\n", "\n", " def __init__(self):\n", " # 初始化交易指令\n", " self.order = None\n", "\n", " # 添加移动均线指标,内置了talib模块\n", " self.sma = bt.ind.SMA(self.datas[0], period=self.params.maperiod)\n", "\n", " # 可以不要,但如果你数据未对齐,需要在这里检验\n", " def prenext(self):\n", " pass\n", " \n", " def downcast(amount, lot):\n", " return abs(amount//lot*lot)\n", " \n", " \n", " def next(self):\n", " if self.order: # 检查是否有指令等待执行,如果有就不执行这根bar\n", " return\n", " \n", " # 回测最后一天不进行买卖\n", " if self.datas[0].datetime.date(0) == end_date:\n", " return \n", " \n", " # 拿这根bar时期的所有资产价值(如果按日K数据放入,即代表今日的资产价值)\n", " self.log(\"%.2f元\" % self.broker.getvalue()) \n", " if not self.position: # 没有持仓\n", " \n", " # 执行买入条件判断:收盘价格上涨突破20日均线;\n", " # 不要在股票剔除日前一天进行买入\n", " if self.datas[0].close > self.sma and data.datetime.date(1) < end_date:\n", " # 永远不要满仓买入某只股票\n", " order_value = self.broker.getvalue()*0.98\n", " order_amount = downcast(order_value/self.datas[0].close[0], 100)\n", " self.order = self.buy(self.datas[0], size=order_amount, name=self.datas[0]._name)\n", " self.log(f\"买{self.datas[0]._name}, price:{self.datas[0].close[0]:.2f}, amout: {order_amount}\")\n", " # self.order = self.order_target_percent(self.datas[0], 0.98, name=self.datas[0]._name)\n", " # self.log(f\"买{self.datas[0]._name}, price:{self.datas[0].close[0]:.2f}, pct: 0.98\")\n", " else:\n", " \n", " # 执行卖出条件判断:收盘价格跌破20日均线,或者股票剔除\n", " if self.datas[0].close > self.sma or data.datetime.date(1) >= end_date:\n", " # 执行卖出\n", " self.order = self.order_target_percent(self.datas[0], 0, name=self.datas[0]._name)\n", " self.log(f\"卖{self.datas[0]._name}, price:{self.datas[0].close[0]:.2f}, pct: 0\")\n", "\n", " def log(self, txt, dt=None):\n", " ''' 输出日志'''\n", " dt = dt or self.datas[0].datetime.date(0) # 拿现在的日期\n", " print('%s, %s' % (dt.isoformat(), txt))\n", "\n", " def notify_order(self, order):\n", " if order.status in [order.Submitted, order.Accepted]:\n", " # Buy/Sell order submitted/accepted to/by broker - Nothing to do\n", " return\n", "\n", " # Check if an order has been completed\n", " # Attention: broker could reject order if not enough cash\n", " if order.status in [order.Completed, order.Canceled, order.Margin]:\n", " if order.isbuy():\n", " self.log(f\"\"\"买入{order.info['name']}, 成交量{order.executed.size},成交价{order.executed.price:.2f}\"\"\")\n", " elif order.issell():\n", " self.log(f\"\"\"卖出{order.info['name']}, 成交量{order.executed.size},成交价{order.executed.price:.2f}\"\"\")\n", " self.bar_executed = len(self)\n", "\n", " # Write down: no pending order\n", " self.order = None\n", "\n", "\n", "cerebro = bt.Cerebro()\n", "cerebro.addstrategy(single_strategy)\n", "secu_lst = {'600000': {'start': '2020-01-01', 'end': '2020-07-18'}}\n", "df = GetKdatas(secu_lst).merge_period()['600000']\n", "data = PandasData_Extend(dataname=df, fromdate=df.index[0], todate=df.index[-1])\n", "cerebro.adddata(data, name='600000')\n", "end_date = df.index[-1] # 股票剔除日\n", "\n", "# 设置初始资本为1 million\n", "startcash = 10**6\n", "cerebro.broker.setcash(startcash)\n", "print(f\"初始资金{cerebro.broker.getvalue()}\")\n", "# 设置交易手续费\n", "cerebro.broker.addcommissioninfo(CommInfoPro())\n", "# 运行回测系统\n", "cerebro.run()\n", "# 获取回测结束后的总资金\n", "portvalue = cerebro.broker.getvalue()\n", "# 打印结果\n", "print(f'结束资金: {round(portvalue, 2)}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3.2. 多只股票\n", "策略逻辑和上面的相同。\n", "计算均线的时候用了dict循环计算每只股票的指标。\n", "1. self.getdatanames()按顺序返回所有股票的名称list\n", "2. self.getdatabyname(secu_name):返回该股票的data" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "初始资金1000000\n", "2020-02-06, 1000000.00, []\n", "2020-02-06, 买000001, price:14.77, amout: 32400.0\n", "2020-02-07, 买入000001, 成交量32400.0,成交价14.60\n", "2020-02-07, 1000643.00, [('000001', 32400.0)]\n", "2020-02-10, 996755.00, [('000001', 32400.0)]\n", "2020-02-11, 1006151.00, [('000001', 32400.0)]\n", "2020-02-12, 1005503.00, [('000001', 32400.0)]\n", "2020-02-13, 1001615.00, [('000001', 32400.0)]\n", "2020-02-14, 1013927.00, [('000001', 32400.0)]\n", "2020-02-17, 1024943.00, [('000001', 32400.0)]\n", "2020-02-18, 1019435.00, [('000001', 32400.0)]\n", "2020-02-19, 1020731.00, [('000001', 32400.0)]\n", "2020-02-20, 1032071.00, [('000001', 32400.0)]\n", "2020-02-20, 买600000, price:11.23, amout: 44100.0\n", "2020-02-21, 买入600000, 成交量44100.0,成交价11.23\n", "2020-02-21, 1034829.00, [('000001', 32400.0), ('600000', 44100.0)]\n", "2020-02-24, 1017315.00, [('000001', 32400.0), ('600000', 44100.0)]\n", "2020-02-25, 1008072.00, [('000001', 32400.0), ('600000', 44100.0)]\n", "2020-02-26, 1011303.00, [('000001', 32400.0), ('600000', 44100.0)]\n", "2020-02-27, 1015632.00, [('000001', 32400.0), ('600000', 44100.0)]\n", "2020-02-28, 979992.00, [('000001', 32400.0), ('600000', 44100.0)]\n", "2020-02-28, 卖000001, price:14.50, pct: 0\n", "2020-02-28, 卖600000, price:10.85, pct: 0\n", "2020-03-02, 卖出000001, 成交量-32400.0,成交价14.55\n", "2020-03-02, 卖出600000, 成交量-44100.0,成交价10.95\n", "2020-03-02, 986020.00, []\n", "2020-03-02, 买600000, price:11.04, amout: 42800.0\n", "2020-03-03, 买入600000, 成交量42800.0,成交价11.13\n", "2020-03-03, 983019.00, [('600000', 42800.0)]\n", "2020-03-04, 981735.00, [('600000', 42800.0)]\n", "2020-03-05, 994147.00, [('600000', 42800.0)]\n", "2020-03-05, 买000001, price:15.39, amout: 31000.0\n", "2020-03-06, 买入000001, 成交量31000.0,成交价15.18\n", "2020-03-06, 980932.00, [('600000', 42800.0), ('000001', 31000.0)]\n", "2020-03-09, 948400.00, [('600000', 42800.0), ('000001', 31000.0)]\n", "2020-03-09, 卖600000, price:10.78, pct: 0\n", "2020-03-09, 卖000001, price:14.45, pct: 0\n", "2020-03-10, 卖出600000, 成交量-42800.0,成交价10.71\n", "2020-03-10, 卖出000001, 成交量-31000.0,成交价14.38\n", "2020-03-10, 943232.00, []\n", "2020-03-11, 943232.00, []\n", "2020-03-12, 943232.00, []\n", "2020-03-13, 943232.00, []\n", "2020-03-16, 943232.00, []\n", "2020-03-17, 943232.00, []\n", "2020-03-18, 943232.00, []\n", "2020-03-19, 943232.00, []\n", "2020-03-20, 943232.00, []\n", "2020-03-23, 943232.00, []\n", "2020-03-24, 943232.00, []\n", "2020-03-25, 943232.00, []\n", "2020-03-26, 943232.00, []\n", "2020-03-27, 943232.00, []\n", "2020-03-30, 943232.00, []\n", "2020-03-31, 943232.00, []\n", "2020-04-01, 943232.00, []\n", "2020-04-02, 943232.00, []\n", "2020-04-03, 943232.00, []\n", "2020-04-07, 943232.00, []\n", "2020-04-08, 943232.00, []\n", "2020-04-09, 943232.00, []\n", "2020-04-10, 943232.00, []\n", "2020-04-10, 买600000, price:10.21, amout: 44300.0\n", "2020-04-13, 买入600000, 成交量44300.0,成交价10.15\n", "2020-04-13, 941455.00, [('600000', 44300.0)]\n", "2020-04-14, 947657.00, [('600000', 44300.0)]\n", "2020-04-14, 买000001, price:12.86, amout: 35300.0\n", "2020-04-15, 买入000001, 成交量35300.0,成交价12.86\n", "2020-04-15, 944904.00, [('600000', 44300.0), ('000001', 35300.0)]\n", "2020-04-16, 937311.00, [('600000', 44300.0), ('000001', 35300.0)]\n", "2020-04-16, 卖000001, price:12.68, pct: 0\n", "2020-04-17, 卖出000001, 成交量-35300.0,成交价12.77\n", "2020-04-17, 942259.00, [('600000', 44300.0)]\n", "2020-04-17, 买000001, price:12.89, amout: 35000.0\n", "2020-04-20, 买入000001, 成交量35000.0,成交价12.86\n", "2020-04-20, 942817.00, [('600000', 44300.0), ('000001', 35000.0)]\n", "2020-04-20, 卖600000, price:10.11, pct: 0\n", "2020-04-21, 卖出600000, 成交量-44300.0,成交价10.07\n", "2020-04-21, 957144.00, [('000001', 35000.0)]\n", "2020-04-22, 949444.00, [('000001', 35000.0)]\n", "2020-04-23, 949444.00, [('000001', 35000.0)]\n", "2020-04-24, 949794.00, [('000001', 35000.0)]\n", "2020-04-27, 958894.00, [('000001', 35000.0)]\n", "2020-04-27, 买600000, price:10.34, amout: 44500.0\n", "2020-04-28, 买入600000, 成交量44500.0,成交价10.36\n", "2020-04-28, 958254.00, [('000001', 35000.0), ('600000', 44500.0)]\n", "2020-04-29, 988214.00, [('000001', 35000.0), ('600000', 44500.0)]\n", "2020-04-30, 985954.00, [('000001', 35000.0), ('600000', 44500.0)]\n", "2020-05-06, 972789.00, [('000001', 35000.0), ('600000', 44500.0)]\n", "2020-05-07, 966874.00, [('000001', 35000.0), ('600000', 44500.0)]\n", "2020-05-08, 978199.00, [('000001', 35000.0), ('600000', 44500.0)]\n", "2020-05-11, 979504.00, [('000001', 35000.0), ('600000', 44500.0)]\n", "2020-05-12, 968149.00, [('000001', 35000.0), ('600000', 44500.0)]\n", "2020-05-13, 964329.00, [('000001', 35000.0), ('600000', 44500.0)]\n", "2020-05-14, 949219.00, [('000001', 35000.0), ('600000', 44500.0)]\n", "2020-05-14, 卖000001, price:13.30, pct: 0\n", "2020-05-15, 卖出000001, 成交量-35000.0,成交价13.39\n", "2020-05-15, 951478.00, [('600000', 44500.0)]\n", "2020-05-18, 953258.00, [('600000', 44500.0)]\n", "2020-05-19, 954593.00, [('600000', 44500.0)]\n", "2020-05-20, 957263.00, [('600000', 44500.0)]\n", "2020-05-21, 953703.00, [('600000', 44500.0)]\n", "2020-05-22, 945248.00, [('600000', 44500.0)]\n", "2020-05-25, 951033.00, [('600000', 44500.0)]\n", "2020-05-26, 951033.00, [('600000', 44500.0)]\n", "2020-05-27, 954593.00, [('600000', 44500.0)]\n", "2020-05-28, 962603.00, [('600000', 44500.0)]\n", "2020-05-29, 964383.00, [('600000', 44500.0)]\n", "2020-06-01, 967943.00, [('600000', 44500.0)]\n", "2020-06-02, 969278.00, [('600000', 44500.0)]\n", "2020-06-02, 买000001, price:13.55, amout: 34300.0\n", "2020-06-03, 买入000001, 成交量34300.0,成交价13.64\n", "2020-06-03, 966733.00, [('600000', 44500.0), ('000001', 34300.0)]\n", "2020-06-04, 965092.00, [('600000', 44500.0), ('000001', 34300.0)]\n", "2020-06-05, 967113.00, [('600000', 44500.0), ('000001', 34300.0)]\n", "2020-06-08, 965472.00, [('600000', 44500.0), ('000001', 34300.0)]\n", "2020-06-09, 970302.00, [('600000', 44500.0), ('000001', 34300.0)]\n", "2020-06-10, 959678.00, [('600000', 44500.0), ('000001', 34300.0)]\n", "2020-06-11, 941165.00, [('600000', 44500.0), ('000001', 34300.0)]\n", "2020-06-11, 卖000001, price:13.08, pct: 0\n", "2020-06-12, 卖出000001, 成交量-34300.0,成交价12.90\n", "2020-06-12, 938105.00, [('600000', 44500.0)]\n", "2020-06-15, 929205.00, [('600000', 44500.0)]\n", "2020-06-16, 933655.00, [('600000', 44500.0)]\n", "2020-06-17, 934545.00, [('600000', 44500.0)]\n", "2020-06-18, 935880.00, [('600000', 44500.0)]\n", "2020-06-19, 940775.00, [('600000', 44500.0)]\n", "2020-06-22, 938105.00, [('600000', 44500.0)]\n", "2020-06-23, 934990.00, [('600000', 44500.0)]\n", "2020-06-24, 940330.00, [('600000', 44500.0)]\n", "2020-06-29, 938995.00, [('600000', 44500.0)]\n", "2020-06-30, 939440.00, [('600000', 44500.0)]\n", "2020-07-01, 946560.00, [('600000', 44500.0)]\n", "2020-07-01, 买000001, price:13.12, amout: 34600.0\n", "2020-07-02, 买入000001, 成交量34600.0,成交价13.08\n", "2020-07-02, 972460.00, [('600000', 44500.0), ('000001', 34600.0)]\n", "2020-07-03, 1007062.00, [('600000', 44500.0), ('000001', 34600.0)]\n", "2020-07-06, 1101485.00, [('600000', 44500.0), ('000001', 34600.0)]\n", "2020-07-07, 1090560.00, [('600000', 44500.0), ('000001', 34600.0)]\n", "2020-07-08, 1101583.00, [('600000', 44500.0), ('000001', 34600.0)]\n", "2020-07-09, 1086950.00, [('600000', 44500.0), ('000001', 34600.0)]\n", "2020-07-10, 1046413.00, [('600000', 44500.0), ('000001', 34600.0)]\n", "2020-07-13, 1050121.00, [('600000', 44500.0), ('000001', 34600.0)]\n", "2020-07-14, 1030395.00, [('600000', 44500.0), ('000001', 34600.0)]\n", "2020-07-15, 1007309.00, [('600000', 44500.0), ('000001', 34600.0)]\n", "2020-07-16, 1004047.00, [('600000', 44500.0), ('000001', 34600.0)]\n", "2020-07-17, 1006816.00, [('600000', 44500.0), ('000001', 34600.0)]\n", "2020-07-20, 1038355.00, [('600000', 44500.0), ('000001', 34600.0)]\n", "2020-07-21, 1030941.00, [('600000', 44500.0), ('000001', 34600.0)]\n", "2020-07-22, 1031733.00, [('600000', 44500.0), ('000001', 34600.0)]\n", "2020-07-23, 983183.00, [('600000', 44500.0), ('000001', 34600.0)]\n", "2020-07-23, 卖600000, price:10.84, pct: 0\n", "2020-07-23, 卖000001, price:14.01, pct: 0\n", "2020-07-24, 卖出600000, 成交量-44500.0,成交价10.78\n", "2020-07-24, 卖出000001, 成交量-34600.0,成交价13.97\n", "2020-07-24, 979127.00, []\n", "2020-07-27, 979127.00, []\n", "2020-07-28, 979127.00, []\n", "2020-07-29, 979127.00, []\n", "2020-07-30, 979127.00, []\n", "2020-07-31, 979127.00, []\n", "2020-08-03, 979127.00, []\n", "2020-08-04, 979127.00, []\n", "2020-08-05, 979127.00, []\n", "2020-08-06, 979127.00, []\n", "2020-08-07, 979127.00, []\n", "2020-08-10, 979127.00, []\n", "2020-08-10, 买000001, price:13.95, amout: 33600.0\n", "2020-08-11, 买入000001, 成交量33600.0,成交价13.97\n", "2020-08-11, 984498.00, [('000001', 33600.0)]\n", "2020-08-12, 992898.00, [('000001', 33600.0)]\n", "2020-08-13, 986178.00, [('000001', 33600.0)]\n", "2020-08-14, 995922.00, [('000001', 33600.0)]\n", "2020-08-17, 1020114.00, [('000001', 33600.0)]\n", "2020-08-17, 买600000, price:10.84, amout: 45100.0\n", "2020-08-18, 买入600000, 成交量45100.0,成交价10.85\n", "2020-08-18, 1011549.00, [('000001', 33600.0), ('600000', 45100.0)]\n", "2020-08-19, 1004908.00, [('000001', 33600.0), ('600000', 45100.0)]\n", "2020-08-20, 982811.00, [('000001', 33600.0), ('600000', 45100.0)]\n", "2020-08-20, 卖600000, price:10.47, pct: 0\n", "2020-08-21, 卖出600000, 成交量-45100.0,成交价10.56\n", "2020-08-21, 982165.00, [('000001', 33600.0)]\n", "2020-08-24, 982501.00, [('000001', 33600.0)]\n", "2020-08-25, 987205.00, [('000001', 33600.0)]\n", "2020-08-26, 979477.00, [('000001', 33600.0)]\n", "2020-08-27, 982501.00, [('000001', 33600.0)]\n", "2020-08-28, 1005013.00, [('000001', 33600.0)]\n", "2020-08-31, 1003333.00, [('000001', 33600.0)]\n", "2020-09-01, 1005349.00, [('000001', 33600.0)]\n", "2020-09-02, 1011397.00, [('000001', 33600.0)]\n", "2020-09-03, 997285.00, [('000001', 33600.0)]\n", "2020-09-04, 999301.00, [('000001', 33600.0)]\n", "2020-09-07, 998629.00, [('000001', 33600.0)]\n", "2020-09-08, 1015093.00, [('000001', 33600.0)]\n", "2020-09-09, 1007701.00, [('000001', 33600.0)]\n", "2020-09-10, 1012069.00, [('000001', 33600.0)]\n", "2020-09-11, 1000981.00, [('000001', 33600.0)]\n", "2020-09-14, 1010725.00, [('000001', 33600.0)]\n", "2020-09-15, 1012405.00, [('000001', 33600.0)]\n", "2020-09-16, 1015429.00, [('000001', 33600.0)]\n", "2020-09-17, 1019797.00, [('000001', 33600.0)]\n", "2020-09-18, 1036597.00, [('000001', 33600.0)]\n", "2020-09-21, 1029541.00, [('000001', 33600.0)]\n", "2020-09-22, 1019797.00, [('000001', 33600.0)]\n", "2020-09-23, 1021813.00, [('000001', 33600.0)]\n", "2020-09-24, 1004677.00, [('000001', 33600.0)]\n", "2020-09-25, 1007029.00, [('000001', 33600.0)]\n", "2020-09-28, 1011061.00, [('000001', 33600.0)]\n", "2020-09-29, 993925.00, [('000001', 33600.0)]\n", "2020-09-30, 1006357.00, [('000001', 33600.0)]\n", "2020-10-09, 1006693.00, [('000001', 33600.0)]\n", "2020-10-12, 1030885.00, [('000001', 33600.0)]\n", "2020-10-13, 1036261.00, [('000001', 33600.0)]\n", "2020-10-14, 1035253.00, [('000001', 33600.0)]\n", "2020-10-15, 1053061.00, [('000001', 33600.0)]\n", "2020-10-16, 1071205.00, [('000001', 33600.0)]\n", "2020-10-16, 买600000, price:9.72, amout: 52800.0\n", "2020-10-19, 买入600000, 成交量0,成交价0.00\n", "2020-10-19, 1083973.00, [('000001', 33600.0), ('600000', 0.0)]\n", "2020-10-20, 1085989.00, [('000001', 33600.0), ('600000', 0.0)]\n", "2020-10-20, 卖600000, price:9.58, pct: 0\n", "2020-10-21, 1098421.00, [('000001', 33600.0)]\n", "2020-10-21, 买600000, price:9.70, amout: 54300.0\n", "2020-10-22, 买入600000, 成交量0,成交价0.00\n", "2020-10-22, 1086661.00, [('000001', 33600.0), ('600000', 0.0)]\n", "2020-10-23, 1105813.00, [('000001', 33600.0), ('600000', 0.0)]\n", "2020-10-26, 1091365.00, [('000001', 33600.0), ('600000', 0.0)]\n", "2020-10-27, 1093381.00, [('000001', 33600.0), ('600000', 0.0)]\n", "2020-10-27, 卖600000, price:9.48, pct: 0\n", "2020-10-28, 1089013.00, [('000001', 33600.0)]\n", "2020-10-29, 1093717.00, [('000001', 33600.0)]\n", "2020-10-30, 1093045.00, [('000001', 33600.0)]\n", "2020-11-02, 1089013.00, [('000001', 33600.0)]\n", "2020-11-03, 1100101.00, [('000001', 33600.0)]\n", "2020-11-04, 1112197.00, [('000001', 33600.0)]\n", "2020-11-05, 1091365.00, [('000001', 33600.0)]\n", "2020-11-06, 1089349.00, [('000001', 33600.0)]\n", "2020-11-09, 1096069.00, [('000001', 33600.0)]\n", "2020-11-10, 1105141.00, [('000001', 33600.0)]\n", "2020-11-11, 1095061.00, [('000001', 33600.0)]\n", "2020-11-11, 买600000, price:9.56, amout: 54900.0\n", "2020-11-12, 买入600000, 成交量0,成交价0.00\n", "2020-11-12, 1090021.00, [('000001', 33600.0), ('600000', 0.0)]\n", "2020-11-12, 卖000001, price:17.66, pct: 0\n", "2020-11-12, 卖600000, price:9.43, pct: 0\n", "2020-11-13, 卖出000001, 成交量-33600.0,成交价17.42\n", "2020-11-13, 1081956.00, []\n", "2020-11-16, 1081956.00, []\n", "2020-11-17, 1081956.00, []\n", "2020-11-17, 买000001, price:17.83, amout: 29100.0\n", "2020-11-17, 买600000, price:9.53, amout: 54400.0\n", "2020-11-18, 买入000001, 成交量29100.0,成交价17.78\n", "2020-11-18, 买入600000, 成交量54400.0,成交价9.53\n", "2020-11-18, 1110981.64, [('000001', 29100.0), ('600000', 54400.0)]\n", "2020-11-19, 1124506.64, [('000001', 29100.0), ('600000', 54400.0)]\n", "2020-11-20, 1120989.64, [('000001', 29100.0), ('600000', 54400.0)]\n", "2020-11-23, 1155617.64, [('000001', 29100.0), ('600000', 54400.0)]\n", "2020-11-24, 1145331.64, [('000001', 29100.0), ('600000', 54400.0)]\n", "2020-11-25, 1134425.64, [('000001', 29100.0), ('600000', 54400.0)]\n", "2020-11-26, 1156477.64, [('000001', 29100.0), ('600000', 54400.0)]\n", "2020-11-27, 1174265.64, [('000001', 29100.0), ('600000', 54400.0)]\n", "2020-11-30, 1167813.64, [('000001', 29100.0), ('600000', 54400.0)]\n", "2020-12-01, 1189890.64, [('000001', 29100.0), ('600000', 54400.0)]\n", "2020-12-02, 1174948.64, [('000001', 29100.0), ('600000', 54400.0)]\n", "2020-12-03, 1171785.64, [('000001', 29100.0), ('600000', 54400.0)]\n", "2020-12-04, 1160993.64, [('000001', 29100.0), ('600000', 54400.0)]\n", "2020-12-07, 1139852.64, [('000001', 29100.0), ('600000', 54400.0)]\n", "2020-12-08, 1133488.64, [('000001', 29100.0), ('600000', 54400.0)]\n", "2020-12-08, 卖000001, price:18.71, pct: 0\n", "2020-12-09, 卖出000001, 成交量-29100.0,成交价18.79\n", "2020-12-09, 1132007.64, [('600000', 54400.0)]\n", "2020-12-10, 1127111.64, [('600000', 54400.0)]\n", "2020-12-11, 1120583.64, [('600000', 54400.0)]\n", "2020-12-14, 1123303.64, [('600000', 54400.0)]\n", "2020-12-15, 1121127.64, [('600000', 54400.0)]\n", "2020-12-16, 1117863.64, [('600000', 54400.0)]\n", "2020-12-17, 1124391.64, [('600000', 54400.0)]\n", "2020-12-18, 1123847.64, [('600000', 54400.0)]\n", "2020-12-21, 1123303.64, [('600000', 54400.0)]\n", "2020-12-22, 1115687.64, [('600000', 54400.0)]\n", "2020-12-23, 1112423.64, [('600000', 54400.0)]\n", "2020-12-24, 1112423.64, [('600000', 54400.0)]\n", "2020-12-25, 1114055.64, [('600000', 54400.0)]\n", "2020-12-28, 1113511.64, [('600000', 54400.0)]\n", "2020-12-28, 买000001, price:18.85, amout: 28300.0\n", "2020-12-29, 买入000001, 成交量28300.0,成交价18.87\n", "2020-12-29, 1119820.30, [('600000', 54400.0), ('000001', 28300.0)]\n", "2020-12-29, 卖600000, price:9.53, pct: 0\n", "2020-12-30, 卖出600000, 成交量-54400.0,成交价9.52\n", "2020-12-30, 1120124.30, [('000001', 28300.0)]\n", "2020-12-31, 1124086.30, [('000001', 28300.0)]\n", "2021-01-04, 1103144.30, [('000001', 28300.0)]\n", "2021-01-05, 1090975.30, [('000001', 28300.0)]\n", "2021-01-06, 1130312.30, [('000001', 28300.0)]\n", "2021-01-06, 买600000, price:9.82, amout: 55200.0\n", "2021-01-07, 买入600000, 成交量55200.0,成交价9.83\n", "2021-01-07, 1138824.88, [('000001', 28300.0), ('600000', 55200.0)]\n", "2021-01-08, 1138513.88, [('000001', 28300.0), ('600000', 55200.0)]\n", "2021-01-11, 1145784.88, [('000001', 28300.0), ('600000', 55200.0)]\n", "2021-01-12, 1168298.88, [('000001', 28300.0), ('600000', 55200.0)]\n", "2021-01-13, 1163120.88, [('000001', 28300.0), ('600000', 55200.0)]\n", "2021-01-14, 1149777.88, [('000001', 28300.0), ('600000', 55200.0)]\n", "2021-01-15, 1176026.88, [('000001', 28300.0), ('600000', 55200.0)]\n", "2021-01-18, 1232968.88, [('000001', 28300.0), ('600000', 55200.0)]\n", "2021-01-19, 1227196.88, [('000001', 28300.0), ('600000', 55200.0)]\n", "2021-01-20, 1220939.88, [('000001', 28300.0), ('600000', 55200.0)]\n", "2021-01-21, 1213595.88, [('000001', 28300.0), ('600000', 55200.0)]\n", "2021-01-22, 1197999.88, [('000001', 28300.0), ('600000', 55200.0)]\n", "2021-01-25, 1207153.88, [('000001', 28300.0), ('600000', 55200.0)]\n", "2021-01-25, 卖600000, price:9.72, pct: 0\n", "2021-01-26, 卖出600000, 成交量-55200.0,成交价9.69\n", "2021-01-26, 1202100.88, [('000001', 28300.0)]\n", "2021-01-27, 1222193.88, [('000001', 28300.0)]\n", "2021-01-28, 1214552.88, [('000001', 28300.0)]\n", "2021-01-29, 1222476.88, [('000001', 28300.0)]\n", "2021-01-29, 买600000, price:9.96, amout: 58900.0\n", "2021-02-01, 买入600000, 成交量0,成交价0.00\n", "2021-02-01, 1263794.88, [('000001', 28300.0), ('600000', 0.0)]\n", "2021-02-02, 1227853.88, [('000001', 28300.0), ('600000', 0.0)]\n", "2021-02-03, 1275114.88, [('000001', 28300.0), ('600000', 0.0)]\n", "2021-02-04, 1265209.88, [('000001', 28300.0), ('600000', 0.0)]\n", "2021-02-05, 1274548.88, [('000001', 28300.0), ('600000', 0.0)]\n", "2021-02-08, 1270869.88, [('000001', 28300.0), ('600000', 0.0)]\n", "2021-02-09, 1264643.88, [('000001', 28300.0), ('600000', 0.0)]\n", "2021-02-10, 1243135.88, [('000001', 28300.0), ('600000', 0.0)]\n", "2021-02-18, 1256719.88, [('000001', 28300.0), ('600000', 0.0)]\n", "2021-02-19, 1243984.88, [('000001', 28300.0), ('600000', 0.0)]\n", "2021-02-22, 1202383.88, [('000001', 28300.0), ('600000', 0.0)]\n", "2021-02-22, 卖000001, price:22.38, pct: 0\n", "2021-02-23, 卖出000001, 成交量-28300.0,成交价22.38\n", "2021-02-23, 1202382.88, [('600000', 0.0)]\n", "2021-02-24, 1202382.88, [('600000', 0.0)]\n", "2021-02-25, 1202382.88, [('600000', 0.0)]\n", "2021-02-26, 1202382.88, [('600000', 0.0)]\n", "2021-03-01, 1202382.88, [('600000', 0.0)]\n", "2021-03-02, 1202382.88, [('600000', 0.0)]\n", "2021-03-03, 1202382.88, [('600000', 0.0)]\n", "2021-03-04, 1202382.88, [('600000', 0.0)]\n", "2021-03-05, 1202382.88, [('600000', 0.0)]\n", "2021-03-08, 1202382.88, [('600000', 0.0)]\n", "2021-03-09, 1202382.88, [('600000', 0.0)]\n", "2021-03-10, 1202382.88, [('600000', 0.0)]\n", "2021-03-11, 1202382.88, [('600000', 0.0)]\n", "2021-03-12, 1202382.88, [('600000', 0.0)]\n", "2021-03-15, 1202382.88, [('600000', 0.0)]\n", "2021-03-16, 1202382.88, [('600000', 0.0)]\n", "2021-03-17, 1202382.88, [('600000', 0.0)]\n", "2021-03-18, 1202382.88, [('600000', 0.0)]\n", "2021-03-19, 1202382.88, [('600000', 0.0)]\n", "2021-03-22, 1202382.88, [('600000', 0.0)]\n", "2021-03-23, 1202382.88, [('600000', 0.0)]\n", "2021-03-24, 1202382.88, [('600000', 0.0)]\n", "2021-03-25, 1202382.88, [('600000', 0.0)]\n", "2021-03-26, 1202382.88, [('600000', 0.0)]\n", "2021-03-29, 1202382.88, [('600000', 0.0)]\n", "2021-03-29, 买000001, price:21.49, amout: 26800.0\n", "2021-03-30, 买入000001, 成交量26800.0,成交价21.38\n", "2021-03-30, 1217117.15, [('600000', 0.0), ('000001', 26800.0)]\n", "2021-03-31, 1219261.15, [('600000', 0.0), ('000001', 26800.0)]\n", "2021-04-01, 1213097.15, [('600000', 0.0), ('000001', 26800.0)]\n", "2021-04-02, 1205593.15, [('600000', 0.0), ('000001', 26800.0)]\n", "2021-04-02, 卖600000, price:10.76, pct: 0\n", "2021-04-06, 1210417.15, [('000001', 26800.0)]\n", "2021-04-07, 1209345.15, [('000001', 26800.0)]\n", "2021-04-08, 1207201.15, [('000001', 26800.0)]\n", "2021-04-09, 1200233.15, [('000001', 26800.0)]\n", "2021-04-12, 1184153.15, [('000001', 26800.0)]\n", "2021-04-13, 1186297.15, [('000001', 26800.0)]\n", "2021-04-14, 1183349.15, [('000001', 26800.0)]\n", "2021-04-15, 1175041.15, [('000001', 26800.0)]\n", "2021-04-16, 1172361.15, [('000001', 26800.0)]\n", "2021-04-19, 1196213.15, [('000001', 26800.0)]\n", "2021-04-20, 1210685.15, [('000001', 26800.0)]\n", "2021-04-21, 1246061.15, [('000001', 26800.0)]\n", "2021-04-22, 1245257.15, [('000001', 26800.0)]\n", "2021-04-23, 1253565.15, [('000001', 26800.0)]\n", "2021-04-26, 1244185.15, [('000001', 26800.0)]\n", "2021-04-27, 1244185.15, [('000001', 26800.0)]\n", "2021-04-28, 1255173.15, [('000001', 26800.0)]\n", "2021-04-29, 1261605.15, [('000001', 26800.0)]\n", "2021-04-30, 1253565.15, [('000001', 26800.0)]\n", "2021-05-06, 1259193.15, [('000001', 26800.0)]\n", "2021-05-07, 1273933.15, [('000001', 26800.0)]\n", "2021-05-10, 1268841.15, [('000001', 26800.0)]\n", "2021-05-11, 1259997.15, [('000001', 26800.0)]\n", "2021-05-12, 1260533.15, [('000001', 26800.0)]\n", "2021-05-13, 1247669.15, [('000001', 26800.0)]\n", "2021-05-14, 1254369.15, [('000001', 26800.0)]\n", "2021-05-17, 1261873.15, [('000001', 26800.0)]\n", "2021-05-18, 1269913.15, [('000001', 26800.0)]\n", "2021-05-19, 1261873.15, [('000001', 26800.0)]\n", "2021-05-20, 1267769.15, [('000001', 26800.0)]\n", "2021-05-21, 1258925.15, [('000001', 26800.0)]\n", "2021-05-24, 1258657.15, [('000001', 26800.0)]\n", "2021-05-25, 1288673.15, [('000001', 26800.0)]\n", "2021-05-25, 买600000, price:10.32, amout: 59900.0\n", "2021-05-26, 买入600000, 成交量59900.0,成交价10.31\n", "2021-05-26, 1302050.97, [('000001', 26800.0), ('600000', 59900.0)]\n", "2021-05-27, 1292560.97, [('000001', 26800.0), ('600000', 59900.0)]\n", "2021-05-28, 1288382.97, [('000001', 26800.0), ('600000', 59900.0)]\n", "2021-05-31, 1275550.97, [('000001', 26800.0), ('600000', 59900.0)]\n", "2021-06-01, 1263254.97, [('000001', 26800.0), ('600000', 59900.0)]\n", "2021-06-02, 1264247.97, [('000001', 26800.0), ('600000', 59900.0)]\n", "2021-06-03, 1261630.97, [('000001', 26800.0), ('600000', 59900.0)]\n", "2021-06-03, 卖000001, price:23.77, pct: 0\n", "2021-06-04, 卖出000001, 成交量-26800.0,成交价23.99\n", "2021-06-04, 1267525.97, [('600000', 59900.0)]\n", "2021-06-04, 买000001, price:24.54, amout: 24700.0\n", "2021-06-07, 买入000001, 成交量24700.0,成交价24.48\n", "2021-06-07, 1263073.92, [('600000', 59900.0), ('000001', 24700.0)]\n", "2021-06-08, 1273546.92, [('600000', 59900.0), ('000001', 24700.0)]\n", "2021-06-09, 1270520.92, [('600000', 59900.0), ('000001', 24700.0)]\n", "2021-06-09, 卖600000, price:10.21, pct: 0\n", "2021-06-10, 卖出600000, 成交量-59900.0,成交价10.21\n", "2021-06-10, 1259404.92, [('000001', 24700.0)]\n", "2021-06-10, 买600000, price:10.23, amout: 59000.0\n", "2021-06-11, 买入600000, 成交量59000.0,成交价10.24\n", "2021-06-11, 1234767.88, [('000001', 24700.0), ('600000', 59000.0)]\n", "2021-06-11, 卖000001, price:23.37, pct: 0\n", "2021-06-11, 卖600000, price:10.17, pct: 0\n", "2021-06-15, 卖出000001, 成交量-24700.0,成交价23.35\n", "2021-06-15, 卖出600000, 成交量-59000.0,成交价10.15\n", "2021-06-15, 1233091.88, []\n", "2021-06-16, 1233091.88, []\n", "2021-06-17, 1233091.88, []\n", "2021-06-18, 1233091.88, []\n", "2021-06-21, 1233091.88, []\n", "2021-06-22, 1233091.88, []\n", "2021-06-23, 1233091.88, []\n", "2021-06-24, 1233091.88, []\n", "2021-06-25, 1233091.88, []\n", "2021-06-28, 1233091.88, []\n", "2021-06-29, 1233091.88, []\n", "2021-06-30, 1233091.88, []\n", "2021-07-01, 1233091.88, []\n", "2021-07-02, 1233091.88, []\n", "2021-07-05, 1233091.88, []\n", "2021-07-06, 1233091.88, []\n", "2021-07-07, 1233091.88, []\n", "2021-07-08, 1233091.88, []\n", "2021-07-09, 1233091.88, []\n", "2021-07-12, 1233091.88, []\n", "2021-07-13, 1233091.88, []\n", "2021-07-14, 1233091.88, []\n", "2021-07-15, 1233091.88, []\n", "2021-07-16, 1233091.88, []\n", "2021-07-16, 买600000, price:9.98, amout: 59300.0\n", "2021-07-19, 买入600000, 成交量59300.0,成交价9.98\n", "2021-07-19, 1234864.96, [('600000', 59300.0)]\n", "2021-07-20, 1233678.96, [('600000', 59300.0)]\n", "2021-07-21, 1233678.96, [('600000', 59300.0)]\n", "2021-07-22, 1233678.96, [('600000', 59300.0)]\n", "2021-07-23, 1233678.96, [('600000', 59300.0)]\n", "2021-07-26, 1233678.96, [('600000', 59300.0)]\n", "2021-07-27, 1233678.96, [('600000', 59300.0)]\n", "2021-07-28, 1233678.96, [('600000', 59300.0)]\n", "2021-07-29, 1233678.96, [('600000', 59300.0)]\n", "2021-07-30, 1233678.96, [('600000', 59300.0)]\n", "2021-08-02, 1233678.96, [('600000', 59300.0)]\n", "2021-08-03, 1233678.96, [('600000', 59300.0)]\n", "2021-08-04, 1233678.96, [('600000', 59300.0)]\n", "2021-08-05, 1233678.96, [('600000', 59300.0)]\n", "2021-08-06, 1233678.96, [('600000', 59300.0)]\n", "2021-08-09, 1233678.96, [('600000', 59300.0)]\n", "2021-08-10, 1233678.96, [('600000', 59300.0)]\n", "2021-08-10, 买000001, price:19.73, amout: 30000.0\n", "2021-08-11, 买入000001, 成交量30000.0,成交价19.99\n", "2021-08-11, 1228272.97, [('600000', 59300.0), ('000001', 30000.0)]\n", "2021-08-11, 卖600000, price:9.99, pct: 0\n", "2021-08-12, 卖出600000, 成交量-59300.0,成交价10.01\n", "2021-08-12, 1231857.97, [('000001', 30000.0)]\n", "2021-08-13, 1231857.97, [('000001', 30000.0)]\n", "2021-08-16, 1233657.97, [('000001', 30000.0)]\n", "2021-08-17, 1225257.97, [('000001', 30000.0)]\n", "结束资金: 1253757.97\n" ] } ], "source": [ "class multi_strategy(bt.Strategy):\n", " # 全局设定交易策略的参数\n", " params = (\n", " ('maperiod', 20),\n", " )\n", "\n", " def __init__(self):\n", " # 初始化交易指令\n", " self.order = None\n", " self.buy_lst = []\n", "\n", " # 添加移动均线指标,内置了talib模块\n", " # 循环计算每只股票的指标\n", " self.sma = {x: bt.ind.SMA(self.getdatabyname(x), period=self.p.maperiod) for x in self.getdatanames()}\n", "\n", " def prenext(self):\n", " pass\n", " \n", " \n", " def downcast(self, amount, lot):\n", " return abs(amount//lot*lot)\n", " \n", " \n", " def next(self):\n", " if self.order: # 检查是否有指令等待执行,\n", " return\n", "\n", " # 回测最后一天不进行买卖\n", " if self.datas[0].datetime.date(0) == end_date:\n", " return \n", " \n", " # 检查是否持仓\n", " self.log(f'{self.broker.getvalue():.2f}, {[(x, self.getpositionbyname(x).size) for x in self.buy_lst]}')\n", " if len(self.buy_lst) < 2: # 没有持仓\n", " for secu in set(self.getdatanames()) - set(self.buy_lst):\n", " data = self.getdatabyname(secu)\n", " # 执行买入条件判断:收盘价格上涨突破20日均线\n", " # 不要在股票剔除日前一天进行买入\n", " if data.close > self.sma[secu] and \\\n", " data.datetime.date(1) < pd.Timestamp(secu_lst[secu]['end']):\n", " # 执行买入\n", " order_value = self.broker.getvalue()*0.48\n", " order_amount = self.downcast(order_value/data.close[0], 100)\n", " self.order = self.buy(data, size=order_amount, name=secu)\n", " self.log(f\"买{secu}, price:{data.close[0]:.2f}, amout: {order_amount}\")\n", " self.buy_lst.append(secu)\n", " elif self.position:\n", " now_lst = []\n", " for secu in self.buy_lst:\n", " data = self.getdatabyname(secu)\n", " # 执行卖出条件判断:收盘价格跌破20日均线,或者股票剔除\n", " if (data.close < self.sma[secu]) or \\\n", " (data.datetime.date(1) >= pd.Timestamp(secu_lst[secu]['end'])):\n", " # 执行卖出\n", " self.order = self.order_target_percent(data, 0, name=secu)\n", " # 也可以用 self.sell(data, size = self.getposition(data).size)\n", " # or self.sell(data, size = self.getpositionbyname(secu).size)\n", " self.log(f\"卖{secu}, price:{data.close[0]:.2f}, pct: 0\")\n", " continue\n", " now_lst.append(secu)\n", " self.buy_lst = now_lst.copy()\n", "\n", " def log(self, txt, dt=None):\n", " ''' 输出日志'''\n", " dt = dt or self.datas[0].datetime.date(0)\n", " print('%s, %s' % (dt.isoformat(), txt))\n", "\n", " def notify_order(self, order):\n", " if order.status in [order.Submitted, order.Accepted]:\n", " # Buy/Sell order submitted/accepted to/by broker - Nothing to do\n", " return\n", "\n", " # Check if an order has been completed\n", " # Attention: broker could reject order if not enough cash\n", " if order.status in [order.Completed, order.Canceled, order.Margin]:\n", " if order.isbuy():\n", " self.log(f\"\"\"买入{order.info['name']}, 成交量{order.executed.size},成交价{order.executed.price:.2f}\"\"\")\n", " elif order.issell():\n", " self.log(f\"\"\"卖出{order.info['name']}, 成交量{order.executed.size},成交价{order.executed.price:.2f}\"\"\")\n", " self.bar_executed = len(self)\n", "\n", " # Write down: no pending order\n", " self.order = None\n", "\n", " \n", "secu_lst = {'600000': {'start': '2020-01-01', 'end': '2021-07-20'},\n", " '000001': {'start': '2020-02-01', 'end': '2021-08-18'}}\n", "# 拿对齐的数据\n", "kdata = GetKdatas(secu_lst).merge_period()\n", "kdata = dict(sorted(kdata.items()))\n", "\n", "# 开始回测\n", "cerebro = bt.Cerebro()\n", "cerebro.addstrategy(multi_strategy)\n", "\n", "for secu in kdata.keys():\n", " df = kdata[secu]\n", " data = PandasData_Extend(dataname=df, fromdate=df.index[0], todate=df.index[-1])\n", " cerebro.adddata(data, name=secu)\n", "end_date = df.index[-1]\n", "\n", "# 设置初始资本为1 million\n", "startcash = 10**6\n", "cerebro.broker.setcash(startcash)\n", "print(f\"初始资金{cerebro.broker.getvalue()}\")\n", "# 设置交易手续费\n", "cerebro.broker.addcommissioninfo(CommInfoPro())\n", "\n", "# 加入指标\n", "cerebro.addanalyzer(bt.analyzers.SharpeRatio, _name='_sharpe')\n", "cerebro.addanalyzer(bt.analyzers.AnnualReturn, _name='_annrtn')\n", "cerebro.addanalyzer(bt.analyzers.DrawDown, _name='_dd')\n", "cerebro.addanalyzer(bt.analyzers.PyFolio, _name='_pyfolio')\n", "# 运行回测系统\n", "thestrats = cerebro.run()\n", "# 获取回测结束后的总资金\n", "portvalue = cerebro.broker.getvalue()\n", "# 打印结果\n", "print(f'结束资金: {round(portvalue, 2)}')" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. 策略评估\n", "下图较为详细得展示了各个买入点和卖出点,方便我们进行买入卖出点的直观判断" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "application/javascript": [ "/* Put everything inside the global mpl namespace */\n", "window.mpl = {};\n", "\n", "\n", "mpl.get_websocket_type = function() {\n", " if (typeof(WebSocket) !== 'undefined') {\n", " return WebSocket;\n", " } else if (typeof(MozWebSocket) !== 'undefined') {\n", " return MozWebSocket;\n", " } else {\n", " alert('Your browser does not have WebSocket support. ' +\n", " 'Please try Chrome, Safari or Firefox ≥ 6. ' +\n", " 'Firefox 4 and 5 are also supported but you ' +\n", " 'have to enable WebSockets in about:config.');\n", " };\n", "}\n", "\n", "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n", " this.id = figure_id;\n", "\n", " this.ws = websocket;\n", "\n", " this.supports_binary = (this.ws.binaryType != undefined);\n", "\n", " if (!this.supports_binary) {\n", " var warnings = document.getElementById(\"mpl-warnings\");\n", " if (warnings) {\n", " warnings.style.display = 'block';\n", " warnings.textContent = (\n", " \"This browser does not support binary websocket messages. \" +\n", " \"Performance may be slow.\");\n", " }\n", " }\n", "\n", " this.imageObj = new Image();\n", "\n", " this.context = undefined;\n", " this.message = undefined;\n", " this.canvas = undefined;\n", " this.rubberband_canvas = undefined;\n", " this.rubberband_context = undefined;\n", " this.format_dropdown = undefined;\n", "\n", " this.image_mode = 'full';\n", "\n", " this.root = $('
');\n", " this._root_extra_style(this.root)\n", " this.root.attr('style', 'display: inline-block');\n", "\n", " $(parent_element).append(this.root);\n", "\n", " this._init_header(this);\n", " this._init_canvas(this);\n", " this._init_toolbar(this);\n", "\n", " var fig = this;\n", "\n", " this.waiting = false;\n", "\n", " this.ws.onopen = function () {\n", " fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n", " fig.send_message(\"send_image_mode\", {});\n", " if (mpl.ratio != 1) {\n", " fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n", " }\n", " fig.send_message(\"refresh\", {});\n", " }\n", "\n", " this.imageObj.onload = function() {\n", " if (fig.image_mode == 'full') {\n", " // Full images could contain transparency (where diff images\n", " // almost always do), so we need to clear the canvas so that\n", " // there is no ghosting.\n", " fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n", " }\n", " fig.context.drawImage(fig.imageObj, 0, 0);\n", " };\n", "\n", " this.imageObj.onunload = function() {\n", " fig.ws.close();\n", " }\n", "\n", " this.ws.onmessage = this._make_on_message_function(this);\n", "\n", " this.ondownload = ondownload;\n", "}\n", "\n", "mpl.figure.prototype._init_header = function() {\n", " var titlebar = $(\n", " '
');\n", " var titletext = $(\n", " '
');\n", " titlebar.append(titletext)\n", " this.root.append(titlebar);\n", " this.header = titletext[0];\n", "}\n", "\n", "\n", "\n", "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n", "\n", "}\n", "\n", "\n", "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n", "\n", "}\n", "\n", "mpl.figure.prototype._init_canvas = function() {\n", " var fig = this;\n", "\n", " var canvas_div = $('
');\n", "\n", " canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n", "\n", " function canvas_keyboard_event(event) {\n", " return fig.key_event(event, event['data']);\n", " }\n", "\n", " canvas_div.keydown('key_press', canvas_keyboard_event);\n", " canvas_div.keyup('key_release', canvas_keyboard_event);\n", " this.canvas_div = canvas_div\n", " this._canvas_extra_style(canvas_div)\n", " this.root.append(canvas_div);\n", "\n", " var canvas = $('');\n", " canvas.addClass('mpl-canvas');\n", " canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n", "\n", " this.canvas = canvas[0];\n", " this.context = canvas[0].getContext(\"2d\");\n", "\n", " var backingStore = this.context.backingStorePixelRatio ||\n", "\tthis.context.webkitBackingStorePixelRatio ||\n", "\tthis.context.mozBackingStorePixelRatio ||\n", "\tthis.context.msBackingStorePixelRatio ||\n", "\tthis.context.oBackingStorePixelRatio ||\n", "\tthis.context.backingStorePixelRatio || 1;\n", "\n", " mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n", "\n", " var rubberband = $('');\n", " rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n", "\n", " var pass_mouse_events = true;\n", "\n", " canvas_div.resizable({\n", " start: function(event, ui) {\n", " pass_mouse_events = false;\n", " },\n", " resize: function(event, ui) {\n", " fig.request_resize(ui.size.width, ui.size.height);\n", " },\n", " stop: function(event, ui) {\n", " pass_mouse_events = true;\n", " fig.request_resize(ui.size.width, ui.size.height);\n", " },\n", " });\n", "\n", " function mouse_event_fn(event) {\n", " if (pass_mouse_events)\n", " return fig.mouse_event(event, event['data']);\n", " }\n", "\n", " rubberband.mousedown('button_press', mouse_event_fn);\n", " rubberband.mouseup('button_release', mouse_event_fn);\n", " // Throttle sequential mouse events to 1 every 20ms.\n", " rubberband.mousemove('motion_notify', mouse_event_fn);\n", "\n", " rubberband.mouseenter('figure_enter', mouse_event_fn);\n", " rubberband.mouseleave('figure_leave', mouse_event_fn);\n", "\n", " canvas_div.on(\"wheel\", function (event) {\n", " event = event.originalEvent;\n", " event['data'] = 'scroll'\n", " if (event.deltaY < 0) {\n", " event.step = 1;\n", " } else {\n", " event.step = -1;\n", " }\n", " mouse_event_fn(event);\n", " });\n", "\n", " canvas_div.append(canvas);\n", " canvas_div.append(rubberband);\n", "\n", " this.rubberband = rubberband;\n", " this.rubberband_canvas = rubberband[0];\n", " this.rubberband_context = rubberband[0].getContext(\"2d\");\n", " this.rubberband_context.strokeStyle = \"#000000\";\n", "\n", " this._resize_canvas = function(width, height) {\n", " // Keep the size of the canvas, canvas container, and rubber band\n", " // canvas in synch.\n", " canvas_div.css('width', width)\n", " canvas_div.css('height', height)\n", "\n", " canvas.attr('width', width * mpl.ratio);\n", " canvas.attr('height', height * mpl.ratio);\n", " canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n", "\n", " rubberband.attr('width', width);\n", " rubberband.attr('height', height);\n", " }\n", "\n", " // Set the figure to an initial 600x600px, this will subsequently be updated\n", " // upon first draw.\n", " this._resize_canvas(600, 600);\n", "\n", " // Disable right mouse context menu.\n", " $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n", " return false;\n", " });\n", "\n", " function set_focus () {\n", " canvas.focus();\n", " canvas_div.focus();\n", " }\n", "\n", " window.setTimeout(set_focus, 100);\n", "}\n", "\n", "mpl.figure.prototype._init_toolbar = function() {\n", " var fig = this;\n", "\n", " var nav_element = $('
');\n", " nav_element.attr('style', 'width: 100%');\n", " this.root.append(nav_element);\n", "\n", " // Define a callback function for later on.\n", " function toolbar_event(event) {\n", " return fig.toolbar_button_onclick(event['data']);\n", " }\n", " function toolbar_mouse_event(event) {\n", " return fig.toolbar_button_onmouseover(event['data']);\n", " }\n", "\n", " for(var toolbar_ind in mpl.toolbar_items) {\n", " var name = mpl.toolbar_items[toolbar_ind][0];\n", " var tooltip = mpl.toolbar_items[toolbar_ind][1];\n", " var image = mpl.toolbar_items[toolbar_ind][2];\n", " var method_name = mpl.toolbar_items[toolbar_ind][3];\n", "\n", " if (!name) {\n", " // put a spacer in here.\n", " continue;\n", " }\n", " var button = $('