def is_k_down_break_m10(code, date): ''' 查看当日收盘价是否上穿10日均线 parameter: code: 股票代码 date: 日期 return: True/False ''' current_daily = daily_collection.find_one({ 'code': code, 'date': date }, projection={ 'code': True, '_id': False, 'close': True }) if current_daily is None: print('code: %s have no data in date: %s' % (code, date)) return False daily_cursor = daily_collection.find( { 'code': code, 'date': { '$lte': date }, 'index': False }, sort=[('date', DESCENDING)], limit=11, projection={ 'code': True, 'close': True, 'date': True, '_id': False }).hint([('code', 1), ('date', -1)]) closes = [daily['close'] for daily in daily_cursor] closes.reverse() if closes is None: print('data before 10 days of %s is None, code: %s' % (date, code)) return False if len(closes) < 11: print('K line is not enough: code %s, date: %s' % (code, date)) return False last_close_2_last_ma10 = compare_close_2_ma10(closes[0:10], current_daily) current_close_2_current_ma10 = compare_close_2_ma10( closes[1:], current_daily) if last_close_2_last_ma10 == 1 and current_close_2_current_ma10 == -1: return True else: return False
def evaluate_stock_pool(begin_date, end_date): ''' 评价股票池的的alpha值,基准选为沪深300 ''' # 得到每一期的股票池信息 adjust_dates, codes_dict = stock_pool(begin_date, end_date) # 得到第一组和最后一组的日期 first_phase_date = adjust_dates[0] last_phase_date = adjust_dates[-1] # 计算沪深300的收益率 hs300_begin_value = daily_collection.find_one({ 'code': '000300', 'index': True, 'date': first_phase_date })['close'] df_profit = pd.DataFrame(columns=['profit', 'hs300']) df_profit.loc[datetime.strptime(adjust_dates[0], '%Y-%m-%d')] = { 'profit': 1, 'hs300': 1 } # 计算每一期相对于上一期的收益率 net_value = 1 for _index in range(1, len(adjust_dates) - 1): last_adjust_date = adjust_dates[_index - 1] current_adjust_date = adjust_dates[_index] # 上一期的股票代码 last_phase_codes = codes_dict[last_adjust_date] buy_cursor = daily_collection.find( { 'code': { '$in': last_phase_codes }, 'date': last_adjust_date, 'index': False }, projection={ 'code': True, 'close': True, '_id': False }).hint([('code', 1), ('date', -1)]) code_buy_close_dict = dict() for buy_daily in buy_cursor: code = buy_daily['code'] code_buy_close_dict[code] = buy_daily['close'] sell_cursor = daily_collection.find( { 'code': { '$in': last_phase_codes }, 'date': current_adjust_date, 'index': False }, projection={ 'code': True, 'close': True, '_id': False }).hint([('code', 1), ('date', -1)]) profit_sum = 0 count = 0 for sell_daily in sell_cursor: _code = sell_daily['code'] if _code in code_buy_close_dict: buy_close = code_buy_close_dict[_code] sell_close = sell_daily['close'] profit_sum += (sell_close - buy_close) / buy_close count += 1 if count > 0: profit = round(profit_sum / count, 4) hs300_current_value = daily_collection.find_one({ 'code': '000300', 'index': True, 'date': current_adjust_date })['close'] # 计算净值和累计收益 net_value = net_value * (1 + profit) dt_current_adjust_date = datetime.strptime(current_adjust_date, '%Y-%m-%d') df_profit.loc[dt_current_adjust_date] = { 'profit': round((net_value), 4), 'hs300': round((hs300_current_value - hs300_begin_value) / hs300_begin_value + 1, 4) } # 绘制图片 df_profit.plot(title='Stock Pool Evaluation Result', grid=True, kind='line') plt.show()
def backtest(begin_date, end_date, fun_sell, fun_buy): ''' 回测系统 parameter: fun_sell: 卖出信号 fun_buy: 买入信号函数 ''' # 设置初始值 cash = 1E7 single_positon = 2E5 df_profit = pd.DataFrame(columns=['net_value', 'profit', 'hs300']) # 得到回测日期 all_dates = get_trading_dates(begin_date, end_date) adjust_dates, date_codes_dict = stock_pool(begin_date, end_date) hs300_begin_value = daily_collection.find_one( { 'code': '000300', 'index': True, 'date': adjust_dates[0] }, projection={ 'close': True, '_id': False })['close'] holding_code_dict = dict() last_date = None this_phase_codes = None last_phase_codes = None to_be_bought_codes = set() to_be_sold_codes = set() for _date in all_dates: print('Back test begin at: %s' % _date) before_sell_holding_codes = list(holding_code_dict.keys()) # 对于每一个回测日期处理复权 if last_date is not None and len(before_sell_holding_codes) > 0: # produce_au(before_sell_holding_codes) last_daily_cursor = daily_collection.find( { 'code': { '$in': before_sell_holding_codes }, 'date': last_date }, projection={ 'code': True, '_id': False, 'au_factor': True }).hint([('code', 1), ('date', 1)]) code_last_aufactor_dict = dict() for last_daily in last_daily_cursor: code_last_aufactor_dict[ last_daily['code']] = last_daily['au_factor'] current_daily_cursor = daily_collection.find( { 'code': { '$in': before_sell_holding_codes }, 'date': _date }, projection={ 'code': True, '_id': False, 'au_factor': True }).hint([('code', 1), ('date', 1)]) for current_daily in current_daily_cursor: current_aufactor = current_daily['au_factor'] code = current_daily['code'] before_volume = holding_code_dict[code]['volume'] if code in code_last_aufactor_dict: last_aufactor = code_last_aufactor_dict[code] after_volume = int(before_volume * (current_aufactor / last_aufactor)) holding_code_dict[code]['volume'] = after_volume print( 'hold volume adjust: code: %s, %6d, %10.6f, %6d, %10.6f' % (code, before_volume, last_aufactor, after_volume, current_aufactor)) # 卖出上一期持仓股 # print('to sell stocks: %s' % to_be_sold_codes, flush=True) if len(to_be_sold_codes) > 0: sell_daily_cursor = daily_collection.find( { 'code': { '$in': list(to_be_sold_codes) }, 'date': _date, 'index': True, 'is_trading': True }, projection={ 'open': True, 'code': True, '_id': False }).hint([('code', 1), ('date', -1)]) for sell_daily in sell_daily_cursor: sell_code = sell_daily['code'] if sell_code in before_sell_holding_codes: holding_stock = before_sell_holding_codes[code] sell_price = sell_daily['open'] holding_volume = holding_stock['volume'] sell_amount = holding_volume * sell_price cash += sell_amount cost = holding_stock['cost'] single_profit = (sell_amount - cost) * 100 / cost print('sell: %s, %6d, %6.2f, %8.2f, %4.2f' % (code, holding_volume, sell_price, sell_amount, single_profit)) del holding_code_dict[code] to_be_sold_codes.remove(code) print('cash after sell: %10.2f' % cash) # 买入这一期股票 # print('to buy stocks: ', to_be_bought_codes, flush=True) if len(to_be_bought_codes) > 0: buy_daily_cursor = daily_collection.find( { 'code': { '$in': list(to_be_bought_codes) }, 'date': _date, 'index': False, 'is_trading': True }, projection={ 'code': True, '_id': False, 'open': True }).hint([('code', 1), ('date', -1)]) for buy_daily in buy_daily_cursor: if cash > single_positon: code = buy_daily['code'] buy_price = buy_daily['open'] buy_volume = int( int(single_positon / buy_price) / 100) * 100 buy_amount = buy_price * buy_volume cash -= buy_amount holding_code_dict[code] = { 'volume': buy_volume, 'last_value': buy_amount, 'cost': buy_amount } print('buy %s, %6d, %6.2f, %8.2f' % (code, buy_volume, buy_price, buy_amount)) print('cash after buy: %10.2f' % cash) # 计算收益率 holding_codes = list(holding_code_dict.keys()) if _date in adjust_dates: print('stock pool adjust date: %s' % _date) if this_phase_codes is not None: last_phase_codes = this_phase_codes this_phase_codes = date_codes_dict[_date] # print(this_phase_codes, flush=True) if last_phase_codes is not None: out_codes = find_out_stocks(last_phase_codes, this_phase_codes) for out_code in out_codes: if out_code in holding_code_dict: to_be_sold_codes.add(out_code) for holding_code in holding_codes: if fun_sell(holding_code, _date): to_be_sold_codes.add(holding_code) to_be_bought_codes.clear() if this_phase_codes is not None: for _code in this_phase_codes: if _code not in holding_codes and fun_buy(_code, _date): to_be_bought_codes.add(_code) # 计算持仓股票市值 total_value = 0 holding_daily_cursor = daily_collection.find( { 'code': { '$in': holding_codes }, 'date': _date, 'index': False }, projection={ 'code': True, '_id': False, 'close': True }).hint([('code', 1), ('date', -1)]) for holding_daily in holding_daily_cursor: code = holding_daily['code'] holding_stock = holding_code_dict[code] value = holding_daily['close'] * holding_stock['volume'] total_value += value profit = (value - holding_stock['cost']) * 100 / holding_stock['cost'] one_day_profit = (value - holding_stock['last_value'] ) * 100 / holding_stock['last_value'] holding_stock['last_value'] = value # print('holding stocks: %s, %10.2f, %4.2f, %4.2f' % # (code, value, profit, one_day_profit)) # 计算总资产 total_capital = total_value + cash # 计算基准收益 hs300_current_value = daily_collection.find_one( { 'code': '000300', 'date': _date, 'index': True }, projection={ 'code': True, 'close': True, '_id': False })['close'] print('after close, cash: %10.2f, total_capital: %10.2f' % (cash, total_capital)) dt_date = datetime.strptime(_date, '%Y-%m-%d') df_profit.loc[dt_date] = { 'net_value': round(total_capital / 1e7, 2), 'profit': round(100 * (total_capital - 1e7) / 1e7, 2), 'hs300': round( 100 * (hs300_current_value - hs300_begin_value) / hs300_begin_value, 2) } drawdown = compute_drawdown(df_profit['net_value']) annual_profit, sharpe_ratio = compute_sharpe_ratio(df_profit['net_value']) print( 'Backtest result: %s - %s, annual_profit: %7.3f, maxdrawdown:%7.3f, sharpe ratio: %4.2f' % (begin_date, end_date, annual_profit, drawdown, sharpe_ratio)) df_profit.plot(title='Backtest Result', y=['profit', 'hs300'], kind='line') plt.show()