├── APIData ├── AccountClass ├── CashClass ├── PositionsClass ├── README.md ├── RunClass ├── ScheduleClass ├── test双均线策略 └── 必读!! /APIData: -------------------------------------------------------------------------------- 1 | import tushare as ts 2 | import pandas as pd 3 | import numpy as np 4 | import datetime 5 | import time 6 | import xlrd 7 | import xlwt 8 | import calendar 9 | import os 10 | import talib 11 | import matplotlib as mpl 12 | import matplotlib.pyplot as plt 13 | import openpyxl 14 | import xlsxwriter 15 | 16 | ts.set_token('在tushare金融社区获取您的token') 17 | pro = ts.pro_api() 18 | 19 | def toExcel(path, sheet_name, df): 20 | ''' 21 | 不覆盖原有表写入数据 22 | :param path: 路径 23 | :param sheet_name: 新表名字 24 | :param df: 数据 25 | :return: 26 | ''' 27 | # 如果没有则创建 28 | try: 29 | writer = pd.ExcelWriter(path,engine='openpyxl') 30 | book = openpyxl.load_workbook(writer.path) 31 | # 如果已存在表,就返回 32 | try: 33 | ws = book[sheet_name] 34 | return 0 35 | except: 36 | pass 37 | except: 38 | workbook = xlsxwriter.Workbook(path) 39 | workbook.close() 40 | writer = pd.ExcelWriter(path,engine='openpyxl') 41 | book = openpyxl.load_workbook(writer.path) 42 | 43 | try: 44 | ws = book["Sheet1"] 45 | book.remove(ws) 46 | except: 47 | pass 48 | writer.book = book 49 | df.to_excel(writer, sheet_name) 50 | writer.save() 51 | 52 | ######################################################################################################## 53 | # 以下是关于交易日期的函数 54 | def tradeDayReadPro(path, year): 55 | ''' 56 | 通过接口获取交易日期(按年接收数据) 57 | :param path: 保存的文档位置 58 | :param year: 选择的年份 59 | :return: 60 | ''' 61 | start_day = str(year) + '0101' 62 | end_day = str(year) + '1231' 63 | date_year = pro.trade_cal(exchange='', start_date=start_day, end_date=end_day) 64 | date_year.set_index('cal_date',inplace=True) 65 | toExcel(path, sheet_name=str(year), df=date_year) 66 | 67 | 68 | # 根据特定日期,读取交易日期,并判断其是否是交易日 69 | def searchTradeDayAPI(start_date, end_date): 70 | ''' 71 | 对外接口API,查询一段时间的交易日期 72 | :param start_date: 起始日期 73 | :param end_date: 截至日期 74 | :return: 交易日期表格(dataframe)索引为日期,0休市,1开市 75 | ''' 76 | workbook = xlrd.open_workbook(r'D:\程序化交易系统实现\data\交易日期.xlsx') 77 | # 取得需要查询的表格 78 | start_year = start_date[0:4] 79 | end_year = end_date[0:4] 80 | count = int(end_year) - int(start_year) # 需要查询的年数 81 | trade_df = pd.DataFrame(columns=['is_open']) 82 | 83 | # 如果没有相应的年份,则下载 84 | sheets = workbook.sheet_names() 85 | for i in range(int(start_year), int(end_year)+1): 86 | if str(i) not in sheets: 87 | print("正在下载"+str(i)+"年的交易日期数据") 88 | tradeDayReadPro(r'D:\程序化交易系统实现\data\交易日期.xlsx', i) 89 | # 重新打开 90 | workbook = xlrd.open_workbook(r'D:\程序化交易系统实现\data\交易日期.xlsx') 91 | j = 0 92 | if count == 0: 93 | sheet_year = workbook.sheet_by_name(start_year) 94 | # 将date转为datetime用于计算是第几天 95 | start_date_dt = datetime.datetime.strptime(start_date, '%Y%m%d') 96 | end_date_dt = datetime.datetime.strptime(end_date, '%Y%m%d') 97 | start_row = int(start_date_dt.strftime('%j')) 98 | end_row = int(end_date_dt.strftime('%j')) 99 | # 取出数据 100 | for i in range(start_row, end_row + 1): 101 | rows = sheet_year.row_values(i) 102 | trade_df.loc[rows[0]] = [rows[2]] 103 | else: 104 | sheet_year = workbook.sheet_by_name(start_year) 105 | # 将date转为datetime用于计算是第几天 106 | start_date_dt = datetime.datetime.strptime(start_date, '%Y%m%d') 107 | start_row = int(start_date_dt.strftime('%j')) 108 | # 取出数据 109 | if calendar.isleap(int(start_year)) == True: 110 | total = 366 111 | else: 112 | total = 365 113 | for i in range(start_row, total): 114 | rows = sheet_year.row_values(i) 115 | trade_df.loc[rows[0]] = [rows[2]] 116 | while True: 117 | j += 1 118 | if j == count: 119 | sheet_year = workbook.sheet_by_name(end_year) 120 | end_date_dt = datetime.datetime.strptime(end_date, '%Y%m%d') 121 | end_row = int(end_date_dt.strftime('%j')) 122 | for i in range(0, end_row + 1): 123 | rows = sheet_year.row_values(i) 124 | trade_df.loc[rows[0]] = [rows[2]] 125 | break 126 | else: 127 | now_year = str(int(start_year) + j) 128 | if calendar.isleap(int(now_year)) == True: 129 | total = 367 130 | else: 131 | total = 366 132 | sheet_year = workbook.sheet_by_name(now_year) 133 | for i in range(2, total): 134 | rows = sheet_year.row_values(i) 135 | trade_df.loc[rows[0]] = [rows[2]] 136 | return trade_df 137 | 138 | # searchTradeDayAPI("20150103", "20190203") 139 | 140 | ##################################################################################################### 141 | # 以下是股票每日行情数据获取函数 142 | def stocksDailyReadPro(path, year, symbol): 143 | ''' 144 | 获取股票一年的每日行情数据 145 | :param path: 需要读入的文件名称 146 | :param year: 读取的年份 147 | :param symbol: 股票代码 148 | :return: 149 | ''' 150 | start_day = str(year) + '0101' 151 | end_day = str(year) + '1231' 152 | stocks_df = pro.daily(ts_code=symbol, start_date=start_day, end_date=end_day) 153 | stocks_df = stocks_df.iloc[::-1] 154 | stocks_df.set_index('trade_date',inplace=True) 155 | toExcel(path, sheet_name=str(year), df=stocks_df) 156 | 157 | # 读取股票日线行情并写入 158 | def stocksDaliyReadSomePro(symbol_list, str_year): 159 | ''' 160 | 进行多股票三年的股票日线行情并写入 161 | :param symbol_list: 股票列表 162 | :return: 163 | ''' 164 | for symbol in symbol_list: 165 | path = 'D:\\程序化交易系统实现\\data\\股票日线行情指标\\' + symbol + '.xlsx' 166 | stocksDailyReadPro(path, str_year, symbol) 167 | time.sleep(0.1) 168 | 169 | # 获取股票日线行情数据,为今后服务 170 | def searchStocksDailyAPI(symbol, start_date, end_date): 171 | ''' 172 | 对外接口API,获取股票日线行情数据 173 | :param symbol: 股票代码 174 | :param start_date: 起始日期 175 | :param end_date: 截至日期 176 | :return: 返回获取股票日线行情数据(dataframe),索引是日期 177 | ''' 178 | path = 'D:\\程序化交易系统实现\\data\\股票日线行情指标\\' + symbol + '.xlsx' 179 | 180 | # 如果该股票不存在,则下载 181 | try: 182 | workbook = xlrd.open_workbook(path) 183 | except: 184 | print("无"+symbol+"股票数据,正在下载") 185 | workbook = xlsxwriter.Workbook(path) 186 | workbook.close() 187 | start_year = start_date[0:4] 188 | end_year = end_date[0:4] 189 | for i in range(int(start_year),int(end_year)+1): 190 | stocksDaliyReadSomePro([symbol], str_year=str(i)) 191 | workbook = xlrd.open_workbook(path) 192 | 193 | # 取得需要查询的表格 194 | start_year = start_date[0:4] 195 | end_year = end_date[0:4] 196 | 197 | # 如果没有相应的年份,则下载 198 | sheets = workbook.sheet_names() 199 | for i in range(int(start_year), int(end_year)+1): 200 | if str(i) not in sheets: 201 | print("正在下载"+symbol+"股票第"+str(i)+"年的交易日期数据") 202 | stocksDaliyReadSomePro([symbol], str_year=str(i)) 203 | # 重新打开 204 | workbook = xlrd.open_workbook(path) 205 | sheet_year = workbook.sheet_by_name(str(int(start_year))) 206 | data_df = pd.DataFrame(columns=sheet_year.row_values(0)[1:]) 207 | 208 | count = int(end_year) - int(start_year) # 需要查询的年数 209 | j = 0 210 | # 获取开始到截至的日期这段时间的日期列表 211 | dates = [] 212 | dt = datetime.datetime.strptime(start_date, "%Y%m%d") 213 | date = start_date[:] 214 | while date <= end_date: 215 | dates.append(date) 216 | dt = dt + datetime.timedelta(1) 217 | date = dt.strftime("%Y%m%d") 218 | # 开始查询 219 | while True: 220 | now_year = str(int(start_year)+j) 221 | sheet_year = workbook.sheet_by_name(now_year) 222 | row_amount = sheet_year.nrows 223 | for i in range(1 , row_amount): 224 | row_data = sheet_year.row_values(i) 225 | if row_data[0] in dates: 226 | data_df.loc[row_data[0]] = row_data[1:] 227 | j += 1 228 | if j > count:break 229 | return data_df 230 | 231 | ############################################################################################ 232 | # 以下是获取股票每日指标数据 233 | def searchStocksDailyBasicPro(path, year, symbol): 234 | ''' 235 | 获取股票一年的每日指标数据 236 | :param path: 需要读入的文件名称 237 | :param year: 读取的年份 238 | :param symbol: 股票代码 239 | :return: 240 | ''' 241 | start_day = str(year) + '0101' 242 | end_day = str(year) + '1231' 243 | stocks_df = pro.daily_basic(ts_code=symbol, start_date=start_day, end_date=end_day) 244 | stocks_df = stocks_df.iloc[::-1] 245 | stocks_df.set_index('trade_date', inplace=True) 246 | toExcel(path, str(year), stocks_df) 247 | 248 | def stocksDaliyBasicReadSomePro(trade_code_list, str_year): 249 | ''' 250 | 进行多股票三年的每日指标数据 251 | :param symbol: 股票列表 252 | :return: 253 | ''' 254 | for symbol in trade_code_list: 255 | path = 'D:\\程序化交易系统实现\\data\\股票每日指标数据\\' + symbol + '.xlsx' 256 | searchStocksDailyBasicPro(path, str_year, symbol) 257 | time.sleep(0.1) 258 | 259 | # 获取股票每日指标数据,为今后服务 260 | def searchStocksDaliyBasicAPI(symbol, start_date, end_date): 261 | ''' 262 | 对外接口API,获取股票每日指标数据 263 | :param symbol: 股票代码 264 | :param start_date: 起始日期 265 | :param end_date: 截至日期 266 | :return: 返回获取股票每日指标数据(dataframe),索引是日期 267 | ''' 268 | path = 'D:\\程序化交易系统实现\\data\\股票每日指标数据\\' + symbol + '.xlsx' 269 | # 如果该股票不存在,则下载 270 | try: 271 | workbook = xlrd.open_workbook(path) 272 | except: 273 | print("无"+symbol+"股票数据,正在下载") 274 | start_year = start_date[0:4] 275 | end_year = end_date[0:4] 276 | for i in range(int(start_year),int(end_year)+1): 277 | stocksDaliyBasicReadSomePro([symbol], str_year=str(i)) 278 | workbook = xlrd.open_workbook(path) 279 | 280 | # 取得需要查询的表格 281 | start_year = start_date[0:4] 282 | end_year = end_date[0:4] 283 | 284 | # 如果没有相应的年份,则下载 285 | sheets = workbook.sheet_names() 286 | for i in range(int(start_year), int(end_year)+1): 287 | if str(i) not in sheets: 288 | print("正在下载"+symbol+"股票第"+str(i)+"年的交易日期数据") 289 | stocksDaliyBasicReadSomePro([symbol], str_year=str(i)) 290 | # 重新打开 291 | workbook = xlrd.open_workbook(path) 292 | count = int(end_year) - int(start_year) # 需要查询的年数 293 | j = 0 294 | 295 | sheet_year = workbook.sheet_by_name(str(int(start_year))) 296 | data_df = pd.DataFrame(columns=sheet_year.row_values(0)[1:]) 297 | # print(data_df) 298 | 299 | dates = [] 300 | dt = datetime.datetime.strptime(start_date, "%Y%m%d") 301 | date = start_date[:] 302 | while date <= end_date: 303 | dates.append(date) 304 | dt = dt + datetime.timedelta(1) 305 | date = dt.strftime("%Y%m%d") 306 | 307 | while True: 308 | now_year = str(int(start_year) + j) 309 | sheet_year = workbook.sheet_by_name(now_year) 310 | row_amount = sheet_year.nrows 311 | for i in range(1, row_amount): 312 | row_data = sheet_year.row_values(i) 313 | if row_data[0] in dates: 314 | data_df.loc[row_data[0]] = row_data[1:] 315 | j += 1 316 | if j > count: break 317 | return data_df 318 | 319 | ############################################################################# 320 | # 以下是获取指数每日成分股 321 | def searchIndexDailyComPro(path, year, symbol): 322 | ''' 323 | 获取股票一年的每日指数成分 324 | :param path: 需要读入的文件名称 325 | :param year: 读取的年份 326 | :param symbol: 指数代码 327 | :return: 328 | ''' 329 | start_day = str(year) + '0101' 330 | end_day = str(year) + '1231' 331 | stocks_df = pro.index_weight(index_code=symbol, start_date=start_day, end_date=end_day) 332 | stocks_df.set_index('trade_date', inplace=True) 333 | stocks_df = stocks_df.iloc[::-1] 334 | toExcel(path, str(year), stocks_df) 335 | 336 | def stocksIndexDaliyComSomePro(indx_code_list, str_year): 337 | ''' 338 | 进行多股票三年的每日指标数据 339 | :param indx_code_list: 指数名称列表 340 | :return: 341 | ''' 342 | for symbol in indx_code_list: 343 | path = 'D:\\程序化交易系统实现\\data\\指数每日成分股\\' + symbol + '.xlsx' 344 | searchIndexDailyComPro(path, str_year, symbol) 345 | 346 | def searchDate(date): 347 | ''' 348 | 更改查询日期 349 | :param date: 原查询日期 350 | :return: 更改后的查询日期 351 | ''' 352 | year = date[0:4] 353 | mounth = date[4:6] 354 | if int(mounth) <= 4 : mounth = '05' 355 | search_staret_date = year + mounth + '01' 356 | search_end_date = year + mounth + '30' 357 | df = searchTradeDayAPI(search_staret_date,search_end_date) 358 | for i in range(0, len(df)): 359 | search_date = str(int(search_staret_date)+ i) 360 | if df.loc[search_date]['is_open'] == 1: 361 | return search_date 362 | 363 | # 获取指数每日成分股数据,为今后服务 364 | def searchIndxDaliyComAPI(symbol, date): 365 | ''' 366 | 对外接口API,获取股票每日指标数据 367 | :param symbol: 指数代码 368 | :param date: 查询日期 369 | :return: 返回获取指数每日成分股(dataframe),索引是股票代码 370 | ''' 371 | path = 'D:\\程序化交易系统实现\\data\\指数每日成分股\\' + symbol + '.xlsx' 372 | workbook = xlrd.open_workbook(path) 373 | # 取得需要查询的表格 374 | year = date[0:4] 375 | # 如果没有相应的年份,则下载 376 | sheets = workbook.sheet_names() 377 | if year not in sheets: 378 | print("正在下载"+symbol+"指数第"+year+"年的数据") 379 | stocksIndexDaliyComSomePro([symbol], year) 380 | # 重新打开 381 | workbook = xlrd.open_workbook(path) 382 | sheet = workbook.sheet_by_name(year) 383 | search_date = searchDate(date) # 获取需要查询的日期 384 | row = sheet.nrows 385 | stocks_com_df = pd.DataFrame(columns=[ 'weight']) 386 | for i in range(1,row): 387 | rows = sheet.row_values(i) 388 | if rows[0] == search_date: 389 | stocks_com_df.loc[rows[2]] = [rows[3]] 390 | return stocks_com_df 391 | 392 | ######################################################### 393 | # 一下是获取指数日线行情数据 394 | def searchIndexDailyPro(path, year, symbol): 395 | ''' 396 | 获取股票一年的指数日线行情数据 397 | :param path: 需要读入的文件名称 398 | :param year: 读取的年份 399 | :param symbol: 指数代码 400 | :return: 401 | ''' 402 | start_day = str(year) + '0101' 403 | end_day = str(year) + '1231' 404 | stocks_df = pro.index_daily(ts_code=symbol, start_date=start_day, end_date=end_day) 405 | stocks_df.set_index('trade_date', inplace=True) 406 | stocks_df = stocks_df.iloc[::-1] 407 | toExcel(path, str(year), stocks_df) 408 | 409 | def stocksIndexDaliySomePro(indx_code_list, str_year): 410 | ''' 411 | 进行多指数三年的指数日线行情数据 412 | :param indx_code_list: 指数名称列表 413 | :return: 414 | ''' 415 | for symbol in indx_code_list: 416 | path = 'D:\\程序化交易系统实现\\data\\指数日线行情\\' + symbol + '.xlsx' 417 | searchIndexDailyPro(path,str_year, symbol) 418 | 419 | # 获取指数日线行情数据,为今后服务 420 | def searchIndexDaliyAPI(symbol, start_date, end_date): 421 | ''' 422 | 对外接口API,获取指数日线行情数据 423 | :param symbol: 指数代码 424 | :param start_date: 起始日期 425 | :param end_date: 截至日期 426 | :return: 返回获取指数每日指标数据(dataframe),索引是日期 427 | ''' 428 | path = 'D:\\程序化交易系统实现\\data\\指数日线行情\\' + symbol + '.xlsx' 429 | workbook = xlrd.open_workbook(path) 430 | # 取得需要查询的表格 431 | start_year = start_date[0:4] 432 | end_year = end_date[0:4] 433 | 434 | # 如果没有相应的年份,则下载 435 | sheets = workbook.sheet_names() 436 | for i in range(int(start_year), int(end_year)+1): 437 | if str(i) not in sheets: 438 | print("正在下载"+symbol+"指数"+str(i)+"年的交易日期数据") 439 | stocksIndexDaliySomePro([symbol], str_year=str(i)) 440 | # 重新打开 441 | workbook = xlrd.open_workbook(path) 442 | count = int(end_year) - int(start_year) # 需要查询的年数 443 | data_df = pd.DataFrame(columns=['ts_code', 'close','open' ,'high' ,'low' ,'pre_close','change','pct_chg','vol', 'amount']) 444 | j = 0 445 | 446 | dates = [] 447 | dt = datetime.datetime.strptime(start_date, "%Y%m%d") 448 | date = start_date[:] 449 | while date <= end_date: 450 | dates.append(date) 451 | dt = dt + datetime.timedelta(1) 452 | date = dt.strftime("%Y%m%d") 453 | 454 | while True: 455 | now_year = str(int(start_year) + j) 456 | sheet_year = workbook.sheet_by_name(now_year) 457 | row_amount = sheet_year.nrows 458 | for i in range(1, row_amount): 459 | row_data = sheet_year.row_values(i) 460 | if row_data[0] in dates: 461 | data_df.loc[row_data[0]] = row_data[1:11] 462 | j += 1 463 | if j > count: break 464 | return data_df 465 | 466 | ########################################################################### 467 | # 以下为得到股票的移动平均线 468 | def SMA_API(symbol, start_date, end_date, timeperiod = 5, return_type = 1): 469 | ''' 470 | 得到移动平均线 471 | :param symbol: 代码 472 | :param start_date: 开始日期 473 | :param end_date: 截止日期 474 | :param timeperiod: 移动窗口 475 | :param return_type:输出类型(1:array,其他:dataframe) 476 | :return: 1:array,其他:dataframe 477 | ''' 478 | df = searchStocksDailyAPI(symbol,start_date,end_date) 479 | if len(df) count:break 591 | return data_df 592 | -------------------------------------------------------------------------------- /AccountClass: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import datetime 4 | import time 5 | import xlrd 6 | import xlwt 7 | import calendar 8 | import APIData 9 | from CashClass import Cash 10 | 11 | now_time = str(datetime.datetime.now().strftime('%Y%m%d')) 12 | 13 | 14 | class Account: 15 | ''' 16 | 用查询总资产,股票价格,指数价格,交易日期,进行股票买卖(按数量买卖,按现有资金比例买卖,按总资产比例买卖) 17 | ''' 18 | 19 | def __init__(self, id = "kunyu", initial=1000000, tax=0.003, date=now_time): 20 | ''' 21 | 初始化 22 | :param id: 账户名称,默认kunyu 23 | :param initial:账户初始资金 默认一百万 24 | :param date: 账户当前日期 默认当前时间(格式:20191123) 25 | :param tax: 交易税率 默认0.003 26 | ''' 27 | self.inital = initial 28 | self.date = date 29 | self.tax = tax 30 | self.cash = Cash(initial) 31 | self.assert_dict = {} 32 | 33 | def searchTotalAssets(self): 34 | ''' 35 | 查询账户总资产 36 | :return: 账户总资产 37 | ''' 38 | return self.cash.searchTotalAssets() 39 | 40 | def searchTotalAmount(self): 41 | ''' 42 | 查询股票总资产 43 | :return: 股票总资产 44 | ''' 45 | stocks_df = self.cash.searchStocksTable() 46 | total_amount = stocks_df['amount'].sum() 47 | return total_amount 48 | 49 | def searchStockTodayClose(self, symbol): 50 | ''' 51 | 查询当天股票价格(收盘价) 52 | :param symbol: 股票代码 53 | :param date: 查询日期 54 | :return: -1未有数据 price股票收盘价 55 | ''' 56 | price_df = APIData.searchStocksDailyAPI(symbol, self.date, self.date) 57 | # print(price_df) 58 | try: 59 | close = price_df.iloc[0]['close'] 60 | except IndexError: 61 | return -1 62 | else: 63 | return close 64 | 65 | def searchStocksMarketData(self, symbol, start_date, end_date, 66 | fields='open,high,close,pre_close,change,pct_chg,vol,amount'): 67 | ''' 68 | 查询股票行情数据 69 | :param symbol: 股票代码 70 | :param start_date: 起始日期 71 | :param end_date: 截止日期 72 | :param fields: 需要查询的内容 73 | :return: 股票数据列表(dataframe),索引为日期 74 | ''' 75 | stocks_df = APIData.searchStocksDailyAPI(symbol, start_date, end_date) 76 | ts_code = ['ts_code'] 77 | fields_list = fields.split(',') 78 | ts_code.extend(fields_list) 79 | return stocks_df[ts_code] 80 | 81 | def searchStocksBasicData(self, symbol, start_date, end_date, 82 | fields='ts_code,turnover_rate,volume_ratio,pe,pb'): 83 | ''' 84 | 查询股票基本面数据 85 | :param symbol: 股票代码 86 | :param start_date: 起始日期 87 | :param end_date: 截止日期 88 | :param fields: 需要查询的内容 89 | :return: 股票数据列表(dataframe),索引为日期 90 | ''' 91 | stocks_df = APIData.searchStocksDaliyBasicAPI(symbol, start_date, end_date) 92 | ts_code = ['ts_code'] 93 | fields_list = fields.split(',') 94 | ts_code.extend(fields_list) 95 | return stocks_df[ts_code] 96 | 97 | def searchIndexToadyPrice(self, symbol): 98 | ''' 99 | 查询当天指数价格(收盘价) 100 | :param symbol: 指数代码 101 | :param date: 查询日期 102 | :return: -1未有数据 price指数价格 103 | ''' 104 | price_df = APIData.searchIndexDaliyAPI(symbol, self.date, self.date) 105 | try: 106 | close = price_df.loc[0]['close'] 107 | except IndexError: 108 | return -1 109 | else: 110 | return close 111 | 112 | def searchIndexComponent(self, symbol): 113 | ''' 114 | 查询指数成分股,以及其权重 115 | :param symbol: 指数代码 116 | :return: 指数成分列表(dataframe),含权重,索引为股票代码 117 | ''' 118 | stocks_df = APIData.searchIndxDaliyComAPI(symbol,self.date) 119 | return stocks_df 120 | 121 | def searchIndexDaliy(self, symbol, start_date, end_date): 122 | ''' 123 | 查询指数收益率 124 | :param symbol: 指数代码 125 | :param start_date: 起始日期 126 | :param end_date: 截止日期 127 | :return: 查询指数收益率(索引为日期) 128 | ''' 129 | price_df = APIData.searchIndexDaliyAPI(symbol, start_date, end_date) 130 | return price_df 131 | 132 | def searchIsTradeDay(self, date): 133 | ''' 134 | 查询某天是否为交易日期 135 | :param date:查询的日期 136 | :return: 0休市,1开市 137 | ''' 138 | trade_df = APIData.searchTradeDayAPI(date, date) 139 | flag = trade_df.loc[date]['is_open'] 140 | return int(flag) 141 | 142 | def searchTradeDay(self, start_date, end_date): 143 | ''' 144 | 查询一段时间的交易日期 145 | :param start_date: 起始日期 146 | :param end_date: 截至日期 147 | :return: 交易日期表格(dataframe)索引为日期,0休市,1开市 148 | ''' 149 | trade_df = APIData.searchTradeDayAPI(start_date, end_date) 150 | return trade_df 151 | 152 | def orderByVolume(self, symbol, volume, side): 153 | ''' 154 | 按照数量买卖 155 | :param symbol: 股票代码 156 | :param volume: 交易数量 157 | :param side: 0买 1卖 158 | :return: 159 | ''' 160 | price = self.searchStockTodayClose(symbol) 161 | if price == -1: 162 | print(self.date + ' '+ symbol + "当天没有数据") 163 | else: 164 | hand_amount = int(volume / 100) 165 | volume = hand_amount * 100 166 | if volume == 0: 167 | print(self.date + ' ' + symbol + "交易不足一手") 168 | else: 169 | if side == 0: 170 | flag = self.cash.buyStocks(symbol, volume * price, volume, price, self.tax) 171 | if flag == -1: 172 | print(self.date + ' '+ symbol + "由于资金不足,购买失败") 173 | if flag == 0: 174 | print(self.date + ' '+ symbol + "购买成功,现有资金%f" % (self.cash.cash)) 175 | if side == 1: 176 | flag = self.cash.sellStocks(symbol, volume * price, volume, price, self.tax+0.001) 177 | if flag == -2: 178 | print(self.date + ' '+ symbol + "未持有") 179 | if flag == -1: 180 | print(self.date + ' '+ symbol + "数量不足") 181 | if flag == 0: 182 | print(self.date + ' '+ symbol + "出售成功,现有资金%f" % (self.cash.cash)) 183 | 184 | def orderByPercentCash(self, symbol, percent, side): 185 | ''' 186 | 安装现有现金比例买卖 187 | :param symbol: 股票代码 188 | :param percent: 比例 189 | :param side: 0买1卖 190 | :return: 191 | ''' 192 | price = self.searchStockTodayClose(symbol) 193 | if price == -1: 194 | print(self.date + ' '+ symbol + "当天没有数据") 195 | else: 196 | amount = self.cash.searchCash() * percent 197 | volume = amount / price 198 | hand_amount = int(volume / 100) 199 | volume = hand_amount * 100 200 | if volume == 0: 201 | print(self.date + ' ' + symbol+ "交易不足一手") 202 | else: 203 | if side == 0: 204 | flag = self.cash.buyStocks(symbol, volume * price, volume, price, self.tax) 205 | if flag == -1: 206 | print(self.date + ' '+ symbol + "由于资金不足,购买失败") 207 | if flag == 0: 208 | print(self.date + ' '+ symbol + "购买成功,现有资金%f" % (self.cash.cash)) 209 | if side == 1: 210 | flag = self.cash.sellStocks(symbol, volume * price, volume, price, self.tax+0.001) 211 | if flag == -2: 212 | print(self.date + ' '+ symbol + "未持有") 213 | if flag == -1: 214 | print(self.date + ' '+ symbol + "数量不足") 215 | if flag == 0: 216 | print(self.date + ' '+ symbol + "出售成功,现有资金%f" % (self.cash.cash)) 217 | 218 | def orderByPersentTotal(self, symbol, percent): 219 | ''' 220 | 按照总资产的比例买卖 221 | :param symbol: 股票代码 222 | :param percent: 比例 223 | :return: 224 | ''' 225 | price = self.searchStockTodayClose(symbol) 226 | if price == -1: 227 | print(self.date + ' '+ symbol + "当天没有数据") 228 | else: 229 | amount = self.cash.searchTotalAssets() * percent 230 | stocks_df = self.cash.searchStocksTable() 231 | stocks_list = stocks_df.index 232 | if symbol not in stocks_list: 233 | volume = amount / price 234 | hand_amount = int(volume / 100) 235 | volume = hand_amount * 100 236 | if volume == 0: 237 | print(self.date + ' ' + symbol + "交易不足一手") 238 | else: 239 | flag = self.cash.buyStocks(symbol, volume * price, volume, price, self.tax) 240 | if flag == -1: 241 | print(self.date + ' ' + symbol + "由于资金不足,购买失败") 242 | if flag == 0: 243 | print(self.date + ' ' + symbol + "购买成功,现有资金%f" % (self.cash.cash)) 244 | if symbol in stocks_list: 245 | amount_now = stocks_df.loc[symbol]['amount'] 246 | amount_buy = amount - amount_now 247 | if amount_buy > 0: 248 | volume = amount_buy / price 249 | hand_amount = int(volume / 100) 250 | volume = hand_amount * 100 251 | if volume == 0: 252 | print(self.date + ' ' + symbol+ "交易不足一手") 253 | else: 254 | flag = self.cash.buyStocks(symbol, volume * price, volume, price, self.tax) 255 | if flag == -1: 256 | print(self.date + ' '+ symbol + "由于资金不足,购买失败") 257 | if flag == 0: 258 | print(self.date + ' '+ symbol + "购买成功,现有资金%f" % (self.cash.cash)) 259 | if amount_buy < 0: 260 | amount_buy = 0 - amount_buy 261 | volume = amount_buy / price 262 | hand_amount = volume / 100 263 | volume = hand_amount * 100 264 | if volume == 0: 265 | print(self.date + ' ' + symbol+ "交易不足一手") 266 | else: 267 | flag = self.cash.sellStocks(symbol, volume * price, volume, price, self.tax+0.001) 268 | if flag == -2: 269 | print(self.date + ' '+ symbol + "未持有") 270 | if flag == -1: 271 | print(self.date + ' '+ symbol + "数量不足") 272 | if flag == 0: 273 | print(self.date + ' '+ symbol + "出售成功,现有资金%f" % (self.cash.cash)) 274 | 275 | def updateStocksTable(self): 276 | ''' 277 | 更新股票列表 278 | :return: 279 | ''' 280 | stocks_df = self.cash.searchStocksTable() 281 | stocks_list = list(stocks_df.index) 282 | for symbol in stocks_list: 283 | price = self.searchStockTodayClose(symbol) 284 | if price == -1: 285 | continue 286 | self.cash.updateStocksTable(symbol, price) 287 | self.assert_dict[self.date] = stocks_df 288 | 289 | def SMA(self, symbol, timeperiod=5, return_type=1): 290 | ''' 291 | 得出股票平均移动线 292 | :param symbol: 股票代码 293 | :param timeperiod: 移动窗口(默认为5) 294 | :param return_type: 1 返回array,其他返回dataframe 295 | :return: 1 返回array,其他返回dataframe 296 | ''' 297 | end_date = datetime.datetime.strptime(self.date, '%Y%m%d') 298 | start_date = end_date - datetime.timedelta(days=20) 299 | start_date = start_date.strftime("%Y%m%d") 300 | data = APIData.SMA_API(symbol, start_date, self.date, timeperiod, return_type) 301 | return data 302 | 303 | def searchShibor(self, start_date, end_date): 304 | ''' 305 | 获取上海银行间同业拆放利率数据 306 | :param start_date: 起始日期 307 | :param end_date: 截止日期 308 | :return: dataframe 索引为日期 309 | ''' 310 | df = APIData.searchShiborAPI(start_date, end_date) 311 | return df 312 | 313 | # 314 | # a = Account(1, 100000,tax=0.001, date='20160120') 315 | # a.orderByPercentCash('000001.SZ',0.1,0) 316 | # a.orderByPercentCash('000001.SZ',0.1,1) 317 | # df1 = a.searchStocksMarketData('000001.SZ','20180102','20180128',fields='open') 318 | # print(df1) 319 | # df2 = a.searchTradeDay('20180101' , '20180125') 320 | # print(df2) 321 | # df3 = a.searchStocksBasicData('000001.SZ', '20180102','20180128', fields='turnover_rate,volume_ratio,pe,pb') 322 | # print(df3) 323 | # df4 = a.searchIsTradeDay('20181111') 324 | # print(df4) 325 | # df5 = a.searchTradeDay('20160101', '20180202') 326 | # print(df5) 327 | # df6 = a.searchIndexComponent('399300.SZ') 328 | # print(df6) 329 | # df7 = a.searchIndexDaliy('399300.SZ','20160102','20180102') 330 | # print(df7) 331 | # df8 = a.searchShibor('20161126','20171126') 332 | # print(df8) 333 | # df9 = a.SMA('000001.SZ',10) 334 | # print(df9) 335 | -------------------------------------------------------------------------------- /CashClass: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | from PositionsClass import Positions 4 | 5 | class Cash(object): 6 | ''' 7 | 用来查询现金,查询总资产,现金与持仓之间的转换,position全部功能 8 | ''' 9 | def __init__(self, initial_funds): 10 | ''' 11 | 初始化,构建position对象 12 | :param initial_funds: 初始资金 13 | ''' 14 | self.cash = initial_funds 15 | self.position = Positions() 16 | 17 | def searchCash(self): 18 | ''' 19 | 查询现金 20 | :return: 现金 21 | ''' 22 | return self.cash 23 | 24 | def searchTotalAssets(self): 25 | ''' 26 | 查询总资产 27 | :return: 总资产 28 | ''' 29 | stocks_df = self.position.searchStocksTable() 30 | stocks_sum = stocks_df['amount'].sum() 31 | stocks_sum = round(stocks_sum, 2) 32 | return stocks_sum + self.cash 33 | 34 | def searchStocksTable(self): 35 | ''' 36 | 查询持仓列表 37 | :return: 持仓列表 38 | ''' 39 | stocks_table = self.position.searchStocksTable() 40 | return stocks_table 41 | 42 | def buyStocks(self, symbol, amount, volume, price, tax=0.003): 43 | ''' 44 | 买股票 45 | :param symbol: 股票代码 46 | :param amount: 股票总额 47 | :param volume: 股票数量 48 | :param price: 股票单价 49 | :param tax: 手续费率,手续费 = tax * amount 50 | :return: -1资金不足,0购买成功 51 | ''' 52 | # 手续费小于5元时,取5元 53 | if tax * amount < 5: 54 | taxamount = 5 55 | else: 56 | taxamount = tax * amount 57 | if self.cash < amount + taxamount: 58 | self.cash = round(self.cash, 2) 59 | return -1 60 | else: 61 | self.position.buyStocks(symbol, amount, volume, price) 62 | total = amount + taxamount 63 | total = round(total ,2) 64 | self.cash -= total 65 | self.cash = round(self.cash, 2) 66 | return 0 67 | 68 | def sellStocks(self, symbol, amount, volume, price, tax=0.004): 69 | ''' 70 | 卖股票 71 | :param symbol: 股票代码 72 | :param amount: 股票总额 73 | :param volume: 股票数量 74 | :param price: 股票单价 75 | :param tax: 手续费率,手续费 = tax * amount 76 | :return: -2为未持有股票,-1为股票数量或总市值不足,0为卖出成功并将资金转给现金 77 | ''' 78 | # 手续费小于5元时,取5元 79 | if tax * amount < 5: 80 | taxamount = 5 81 | else: 82 | taxamount = tax * amount 83 | 84 | amount = self.position.sellStocks(symbol, amount, volume, price) 85 | if amount == -2: 86 | return -2 87 | elif amount == -1: 88 | return -1 89 | else: 90 | total = amount - taxamount 91 | total = round(total ,2) 92 | self.cash += total 93 | self.cash = round(self.cash, 2) 94 | return 0 95 | 96 | def updateStocksTable(self, symbol, price): 97 | ''' 98 | 更新股票列表 99 | :param symbol: 股票代码 100 | :param price: 股票单价 101 | :return: 102 | ''' 103 | self.position.updateStocksTable(symbol,price) 104 | 105 | # c = Cash(100000000) 106 | # flag0 = c.buyStocks('hs300',98564,98564,1,0.03) 107 | # print(c.searchCash()) 108 | # flag1 = c.buyStocks('hs400',1000000,10,100000,0.01) 109 | # flag3 = c.sellStocks('hs300',10,10,1,0.01) 110 | # print(c.searchStocksTable()) 111 | -------------------------------------------------------------------------------- /PositionsClass: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | 4 | class Positions(object): 5 | ''' 6 | 持仓对象,用于买卖股票,更新持仓情况 7 | ''' 8 | def __init__(self): 9 | ''' 10 | 构建持仓表格 11 | ''' 12 | self.stocks_table = pd.DataFrame(columns=['amount', 'volume', 'price']) 13 | 14 | def buyStocks(self, symbol, amount, volume, price): 15 | ''' 16 | 买股票 17 | :param symbol: 股票代码 18 | :param amount: 股票总额 19 | :param volume: 股票数量 20 | :param price: 股票单价 21 | :return: 22 | ''' 23 | index_list = list(self.stocks_table.index) 24 | # 如果没有持有股票则直接添加 25 | if symbol not in index_list: 26 | self.stocks_table.loc[symbol] = [amount, volume, price] 27 | # 如果原来有股票,则加上 28 | else: 29 | self.stocks_table.loc[symbol] ['amount'] += amount 30 | self.stocks_table.loc[symbol] ['volume'] += volume 31 | self.stocks_table.loc[symbol] ['price'] = price 32 | 33 | def sellStocks(self, symbol, amount, volume, price): 34 | ''' 35 | 卖股票 36 | :param symbol: 股票代码 37 | :param amount: 股票总额 38 | :param volume: 股票数量 39 | :param price: 股票单价 40 | :return: -2为未持有股票,-1为股票数量或金额不足,amount为卖出股票的金额 41 | ''' 42 | index_list = list(self.stocks_table.index) 43 | # 未持有股票 44 | if symbol not in index_list: 45 | return -2 46 | else: 47 | # 股票数量不足 48 | if self.stocks_table.loc[symbol]['amount'] < amount or self.stocks_table.loc[symbol]['volume'] < volume: 49 | return -1 50 | else: 51 | pre_amount = self.stocks_table.loc[symbol]['amount'] 52 | self.stocks_table.loc[symbol]['amount'] -= amount 53 | self.stocks_table.loc[symbol]['volume'] -= volume 54 | # 如果股票正好卖光,返回原有金额 55 | if self.stocks_table.loc[symbol]['amount'] == 0 or self.stocks_table.loc[symbol]['volume'] == 0: 56 | self.stocks_table = self.stocks_table.drop(symbol, axis=0) 57 | return pre_amount 58 | # 如果股票还有剩余,返回卖出金额 59 | else: 60 | self.stocks_table.loc[symbol]['price'] = price 61 | self.stocks_table.loc[symbol]['amount'] = price * self.stocks_table.loc[symbol]['volume'] 62 | return amount 63 | 64 | def updateStocksTable(self, symbol, price): 65 | ''' 66 | 更新股票列表 67 | :param symbol: 股票代码 68 | :param price: 股票单价 69 | :return: 70 | ''' 71 | self.stocks_table.loc[symbol]['price'] = price 72 | self.stocks_table.loc[symbol]['amount'] = price * self.stocks_table.loc[symbol]['volume'] 73 | 74 | def searchStocksTable(self): 75 | ''' 76 | 查询持仓列表 77 | :return: 持仓列表 78 | ''' 79 | return self.stocks_table 80 | 81 | # p = Positions() 82 | # p.buyStocks('hs300',10,10,1) 83 | # p.buyStocks('hs400',50,20,2.5) 84 | # amount = p.sellStocks('hs300',9,9,1) 85 | # print(p.searchStocksTable()) 86 | # ''' 87 | # amount volume price 88 | # hs400 50 20 2.5 89 | # ''' 90 | 91 | 92 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 利用Tushare金融大数据社区的数据进行回测 2 | 1. 首先必须在tushare社区申请账户(网址:https://tushare.pro/register?reg=325331),获取token,将其复制进API文件中的“ts.set_token”函数中。本人tushare ID为325331 3 | 2. 所有py文件放在一个目录下,最好为“D:\程序化交易系统实现\”,必须提前创建“D:\程序化交易系统实现\data\股票每日指标数据”、“D:\程序化交易系统实现\data\股票日线行情指标”、 4 | “D:\程序化交易系统实现\data\指数每日成分股”和“D:\程序化交易系统实现\data\指数日线行情”四个文件夹用于存储下载的数据。当下载时间超时时(如每秒访问次数超过200),之前下载数据的已经 5 | 保存至data目录下,无需重新下载,只需要重新运行回测文件即可。 6 | 3. 所有数据都是在回测时候下载的,即边回测边下载数据(如果之前已经下载过了,就不许重复下载了),您也可以在API文件中提前下载数据。 7 | 4. 回测文件格式如‘test.py’所示,其中‘account’类的用法详见‘AccountClass.py’文件。 8 | 5. 本人学业繁忙,所编程序难免有不足之处,如有bug,还请您即时反应,QQ:952866954 9 | -------------------------------------------------------------------------------- /RunClass: -------------------------------------------------------------------------------- 1 | import ScheduleClass 2 | from AccountClass import Account 3 | import pandas as pd 4 | import numpy as np 5 | import datetime 6 | import time 7 | 8 | class Run(object): 9 | 10 | def __init__(self, account_id, initial_fund, start_date, end_date, tax): 11 | self.account = Account(account_id, initial_fund, tax, start_date) 12 | self.schedule = ScheduleClass.Schedule(self.account, start_date, end_date) 13 | 14 | def before_trading(self, before_trading): 15 | before_trading(self.account) 16 | 17 | def trading(self, fields, trading): 18 | self.schedule.scheduleRun(fields,trading) 19 | 20 | def after_trading(self, after_trading): 21 | after_trading(self.account) 22 | 23 | -------------------------------------------------------------------------------- /ScheduleClass: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import datetime 4 | import time 5 | from AccountClass import Account 6 | import matplotlib as mpl 7 | import matplotlib.pyplot as plt 8 | 9 | class Schedule(object): 10 | 11 | def __init__(self, account, start_date, end_date): 12 | ''' 13 | 初始化 14 | :param account: 账户类 15 | :param start_date: 回测起始日期 16 | :param end_date: 回测终止日期 17 | ''' 18 | self.account = account 19 | self.start_date = start_date 20 | self.end_date = end_date 21 | self.return_df = pd.DataFrame(columns=['total_assert', 'total_amount', 'cash', 'return']) 22 | 23 | def dateRange(self, start_date, end_date): 24 | ''' 25 | 获取自然日期列表 26 | :param start_date: 起始日期 27 | :param end_date: 终止日期 28 | :return: 自然日期列表(日期为字符串) 29 | ''' 30 | dates = [] 31 | dt = datetime.datetime.strptime(start_date, "%Y%m%d") 32 | date = start_date[:] 33 | while date <= end_date: 34 | dates.append(date) 35 | dt = dt + datetime.timedelta(1) 36 | date = dt.strftime("%Y%m%d") 37 | return dates 38 | 39 | def scheduleRun(self, fields, function): 40 | ''' 41 | 自定义时间函数 42 | :param fields: 1d为每日执行,1m为每月执行 43 | :param function: 执行函数名 44 | :return: 45 | ''' 46 | datelist = self.dateRange(self.start_date, self.end_date) 47 | self.account.date = datelist[0] # 使得账户日期为初始日期 48 | if fields == '1d': 49 | length = len(datelist) 50 | self.date_length = length # 用于计算年化收益率 51 | for date in datelist: 52 | flag = self.account.searchIsTradeDay(date) # 查询是否是交易日期,是进行function交易 53 | self.account.date = date 54 | if flag == 1: 55 | function(self.account) 56 | self.assetsStatus() 57 | 58 | if fields == '1m': 59 | length = len(datelist) 60 | self.date_length = length # 用于计算年化收益率 61 | i = 0 62 | self.account.date = datelist[0] 63 | while True: 64 | if i % 30 == 0: 65 | while True: 66 | flag = self.account.searchIsTradeDay(datelist[i]) 67 | if flag == 0: 68 | self.account.date = datelist[i] 69 | i += 1 70 | if i >= length: break 71 | if flag == 1: 72 | function(self.account) 73 | self.account.date = datelist[i] 74 | self.assetsStatus() 75 | break 76 | else: 77 | self.account.date = datelist[i] 78 | flag = self.account.searchIsTradeDay(datelist[i]) 79 | if flag == 1: 80 | self.assetsStatus() 81 | i += 1 82 | if i >= length:break 83 | self.assetsResult() 84 | 85 | def assetsStatus(self): 86 | ''' 87 | 获取自身资产列表 88 | :return: 获取自身资产列表,索引为日期 89 | ''' 90 | # 'total_assert', 'total_amount', 'cash', 'return' 91 | self.account.updateStocksTable() 92 | date = self.account.date 93 | total_assert = self.account.searchTotalAssets() 94 | cash = self.account.cash.searchCash() 95 | total_amount = self.account.searchTotalAmount() 96 | return_rate = total_assert /self.account.inital - 1 97 | self.return_df.loc[date]= [total_assert, total_amount, cash, return_rate] 98 | return self.return_df 99 | 100 | def assetsResult(self): 101 | ''' 102 | 获取结果列表 103 | :return: 在资产列表上增加hs300收益率, 并得出年华收益率, 夏普比率, 最大回撤区间 104 | ''' 105 | self.result_df = self.return_df 106 | # 求hs300的涨幅度(array) 107 | hs300_df = self.account.searchIndexDaliy('399300.SZ',self.start_date, self.end_date) 108 | hs300_list = [] 109 | for i in range(0,len(hs300_df)): 110 | hs300_list.append(hs300_df.iloc[i]['close'] / hs300_df.iloc[0]['close'] - 1) 111 | self.result_df['hs300_return'] = hs300_list 112 | self.result_df['hs300'] = hs300_df['close'] 113 | # 计算年化收益率 114 | self.rate_year = self.result_df.iloc[-1]['return'] / self.date_length * 365 115 | print('年化收益率:%f' % self.rate_year) 116 | # 首先计算日回报率 117 | total_assert_list = list(self.result_df['total_assert']) 118 | return_pct_change_list = [] 119 | for i in range(len(total_assert_list)): 120 | if i == 0: 121 | continue 122 | return_pct_change_list.append(total_assert_list[i] / total_assert_list[i - 1] - 1) 123 | # 计算夏普比例 124 | return_pct_change_array =np.array(return_pct_change_list) 125 | self.ASR = return_pct_change_array.mean() - (0.04/252) 126 | self.ASR = self.ASR / return_pct_change_array.std() * np.sqrt(252) 127 | print('夏普比率:%f' % self.ASR) 128 | # 计算最大回撤收益率,用总资产来计算 129 | self.max_rate = self.maxDrawDown(total_assert_list) 130 | date_list = self.return_df.index 131 | print('最大回撤率:%f,从%s开始,%s结束' % (self.max_rate[0],date_list[self.max_rate[1]],date_list[self.max_rate[2]])) 132 | return self.result_df 133 | 134 | def maxDrawDown(self, return_list): 135 | """最大回撤率""" 136 | maxac = np.zeros(len(return_list)) 137 | b = return_list[0] 138 | for i in range(0, len(return_list)): # 遍历数组,当其后一项大于前一项时,赋值给b 139 | if return_list[i] > b: 140 | b = return_list[i] 141 | maxac[i] = b 142 | # print(maxac) 143 | i = np.argmax((maxac - return_list) / maxac) # 结束位置 144 | if i == 0: 145 | return [0 , 0 , 0] 146 | j = np.argmax(return_list[:i]) # 开始位置 147 | return [(return_list[j] - return_list[i]) / (return_list[j]), j , i] 148 | 149 | def plotResult(self): 150 | ''' 151 | 画出图像 152 | :return: 画出收益率曲线,hs300 和 自身收益率 153 | ''' 154 | # 设置结果列表(日期,收益率,hs300收益率) 155 | result_df = pd.DataFrame(index=self.result_df.index) 156 | result_df['hs300_return'] = self.result_df['hs300_return'] 157 | result_df['return_rate'] = self.result_df['return'] 158 | result_df.plot() 159 | plt.show() 160 | 161 | 162 | 163 | # account = Account('1', 100000) 164 | # Run = Schedule(account,'20171201', '20180102') 165 | # 166 | # def function(account): 167 | # print('今天是 '+ account.date) 168 | # account.orderByPercentCash('000001.SZ', 0.1, 0) 169 | # account.orderByPercentCash('000002.SZ', 0.1, 0) 170 | # print( '今天结束') 171 | # 172 | # Run.scheduleRun('1m',function) 173 | # Run.plotResult() 174 | # 175 | 176 | 177 | 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /test双均线策略: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import datetime 4 | import time 5 | import RunClass 6 | 7 | def before_trading(account): 8 | account.stocks_df = account.searchIndexComponent('399300.SZ') # 获取沪深300的成分股 9 | account.stocks_df = account.stocks_df.sort_values(by = 'weight', ascending=False)[0:10] # 所占比重的前10为标的股 10 | def trading(account): 11 | for symbol in account.stocks_df.index: 12 | sma5 = account.SMA(symbol) # 计算每个股票的sma5 13 | sma10 = account.SMA(symbol,10) # 计算每个股票的sma10 14 | if sma5[-1] > sma10[-1] and sma5[-2] < sma10[-2]: # 均线判断 15 | account.orderByPersentTotal(symbol,0.1) # 按现有现金的0.1购买该股票 16 | else: 17 | print(account.date + ' '+ symbol + "不符合要求") 18 | def after_trading(account): 19 | run.schedule.plotResult() # 展示结果 20 | 21 | if __name__ == '__main__': 22 | run = RunClass.Run("1", 1000000, '20160126', '20161126', 0.003) # 设置账户必要参数,账户名字、初始金额、开始日期、结束日期、交易费率 23 | run.before_trading(before_trading) # 回测前的函数‘before_trading’ 24 | run.trading('1m', trading) # ‘1m’代表月回测,‘1d’代表日回测,函数为‘trading’ 25 | run.after_trading(after_trading) # 回测后展示结果 26 | -------------------------------------------------------------------------------- /必读!!: -------------------------------------------------------------------------------- 1 | # python-利用Tushare金融大数据社区的数据就行回测 2 | 1. 首先必须在tushare社区申请账户(网址:https://tushare.pro/register?reg=325331),获取token,将其复制进API文件中的“ts.set_token”函数中,本人tushare ID为325331。 3 | 2. 所有py文件放在一个目录下,最好为“D:\程序化交易系统实现\”,必须提前创建“D:\程序化交易系统实现\data\股票每日指标数据”、“D:\程序化交易系统实现\data\股票日线行情指标”、 4 | “D:\程序化交易系统实现\data\指数每日成分股”和“D:\程序化交易系统实现\data\指数日线行情”四个文件夹用于存储下载的数据。当下载时间超时时(如每秒访问次数超过200),之前下载数据的已经 5 | 保存至data目录下,无需重新下载,只需要重新运行回测文件即可。 6 | 3. 所有数据都是在回测时候下载的,即边回测边下载数据(如果之前已经下载过了,就不许重复下载了),您也可以在API文件中提前下载数据。 7 | 4. 回测文件格式如‘test.py’所示,其中‘account’类的用法详见‘AccountClass.py’文件。 8 | 5. 本人学业繁忙,所编程序难免有不足之处,如有bug,还请您即时反应,QQ:952866954 9 | --------------------------------------------------------------------------------