def test_TurtleStrategy_4(): symbol = '123456' holds = { symbol: [ TurtleStrategy.Hold( symbol, datetime.date(1999, 1, 1), 10, # 买入价 100, # 持仓数量 9, # 止损价 11, # 止盈价 10.5 # 下一个仓位价格 ) ] } ts = TurtleStrategy('', holds=holds) assert 1 == len(ts.holds[symbol]) # 虽然有持仓,但是没有任何的卖出点标记,也没有到止盈/止损/加仓点位 assert not ts.on_check_sell(datetime.date(2000, 1, 1), symbol, 10.2, 1000, 0, 10) # 没有到达止盈点位 assert 0 == ts.on_calc_sell_amount(datetime.date(2000, 1, 1), symbol, 10.99, 1000, 0, 10) # 到达止盈点位 assert 100 == ts.on_calc_sell_amount(datetime.date(2000, 1, 1), symbol, 11, 1000, 0, 10) assert 0 == len(ts.holds[symbol])
def test_TurtleStrategy_1(): symbol = '123456' holds = { symbol: [ TurtleStrategy.Hold( symbol, datetime.date(1999, 1, 1), 10, #买入价 100, #持仓数量 9, #止损价 11, #止盈价 10.5 #下一个仓位价格 ) ] } ts = TurtleStrategy('', holds=holds) # 当前时间不包含在卖出点字典中-开始 assert 1 == len(ts.holds[symbol]) assert 0 == ts.on_calc_sell_amount(datetime.date(2000, 1, 1), symbol, 10.2, 1000, 0, 10) assert not ts.on_check_sell(datetime.date(2000, 1, 1), symbol, 10.2, 1000, 0, 10) assert 1 == len(ts.holds[symbol])
def test_TurtleStrategy_7(): """买卖在同一天时,不更新止盈/下一个买点价格""" symbol = '123456' holds = { symbol: [ TurtleStrategy.Hold( symbol, datetime.date(1999, 1, 1), 10, # 买入价 100, # 持仓数量 9, # 止损价 11, # 止盈价 10.5 # 下一个仓位价格 ), TurtleStrategy.Hold( symbol, datetime.date(1999, 2, 1), 20, # 买入价 200, # 持仓数量 18, # 止损价 25, # 止盈价 30 # 下一个仓位价格 ) ] } ts = TurtleStrategy(colname='atr5', update_price_onsameday=False, holds=holds, buy_dict={symbol: [datetime.date(2000, 1, 1)]}) assert ts.holds[symbol][-1].stopprofit_price == 25 assert ts.holds[symbol][-1].next_price == 30 row = pd.Series({'atr5': 0.05}) ts.on_buy_sell_on_same_day(datetime.date(2000, 1, 1), symbol, 50, row=row, verbose=2) new_stoploss, new_stopprofit, new_next = ts.calc_price(50, row=row) assert ts.holds[symbol][-1].stopprofit_price == 25 assert ts.holds[symbol][-1].next_price == 30 assert not ts.holds[symbol][-1].stopprofit_price == new_stopprofit assert not ts.holds[symbol][-1].next_price == new_next
def test_TurtleStrategy_5(): symbol = '123456' holds = { symbol: [ TurtleStrategy.Hold( symbol, datetime.date(1999, 1, 1), 10, # 买入价 100, # 持仓数量 9, # 止损价 11, # 止盈价 10.5 # 下一个仓位价格 ) ] } ts = TurtleStrategy('', holds=holds, buy_dict={symbol: [datetime.date(2000, 1, 1)]}, sell_dict={symbol: [datetime.date(2000, 1, 2)]}) assert 100 == ts.on_calc_buy_amount(datetime.date(2000, 1, 1), symbol, 20, 10000) assert 2 == len(ts.holds[symbol]) assert 100 == ts.holds[symbol][0].amount assert 10 == ts.holds[symbol][0].price assert 100 == ts.holds[symbol][-1].amount assert 20 == ts.holds[symbol][-1].price assert ts.on_check_sell(datetime.date(2000, 1, 2), symbol, 15, 9999, 300, -1) assert ts.on_check_sell(datetime.date(2000, 1, 2), symbol, 15, 9999, -1, -1) assert 200 == ts.on_calc_sell_amount(datetime.date(2000, 1, 2), symbol, 15, 9999, -1, -1) assert 0 == len(ts.holds[symbol])
def test_TurtleStrategy(): ts = TurtleStrategy(colname='atr5') assert ts.stoploss_point == 2 assert ts.stopprofit_point == 10 assert ts.next_point == 1 row = pd.Series({'atr5': 0.05}) ls, lf, n = ts.calc_price(1, row=row) assert ls == (1 - ts.stoploss_point * 0.05) assert lf == (1 + ts.stopprofit_point * 0.05) assert n == (1 + ts.next_point * 0.05) print(">>> from finance_tools_py.backtest import TurtleStrategy") print(">>> ts = TurtleStrategy(colname='atr5')") print(">>> row = pd.Series({'atr5': 0.05})") print(">>> ts.calc_price(1, row=row") print("(0.9, 1.5, 1.05)") ts = TurtleStrategy(colname='', stoploss_point=0.5, stopprofit_point=20, next_point=2) assert ts.stoploss_point == 0.5 assert ts.stopprofit_point == 20 assert ts.next_point == 2 ls, lf, n = ts.calc_price(1, row=row) assert ls == -1 assert lf == -1 assert n == -1 # 只有一笔持仓时的判断-开始 ts = TurtleStrategy( colname='atr5', max_amount={'0': 100}, verbose=2, buy_dict={'0': [datetime.date(2000, 1, 1), datetime.date(2000, 1, 2)]}) ts.on_calc_buy_amount(datetime.date(2000, 1, 1), '0', 1, 1000, row=row, verbose=2) print(ts.holds['0'][0]) # 超过最大持仓线时不再购买 assert not ts.on_check_buy( datetime.date(2000, 1, 2), '0', 1.01, 500, row=row, verbose=2) print(ts.holds['0'][0]) assert not ts.on_check_buy( datetime.date(2000, 1, 2), '0', 1.1, 500, row=row, verbose=2) # 没有达到止损线,不卖 assert not ts.on_check_sell( datetime.date(2000, 1, 3), '0', 0.95, 0, 0, 0, row=row, verbose=2) # 低于止损线,卖出 assert ts.on_check_sell(datetime.date(2000, 1, 3), '0', 0.89, 0, 0, 0, row=row, verbose=2) # 低于止盈,不卖 assert not ts.on_check_sell( datetime.date(2000, 1, 3), '0', 1.49, 0, 0, 0, row=row, verbose=2) # 超过止盈,卖出 assert ts.on_check_sell(datetime.date(2000, 1, 3), '0', 1.51, 0, 0, 0, row=row, verbose=2) # 止损数量 assert 100 == ts.on_calc_sell_amount(datetime.date(2000, 1, 3), '0', 0, 0, 0, 0, row=row, verbose=2) # 只有一笔持仓时的判断-结束 # 多笔持仓时的判断-开始 ts = TurtleStrategy(colname='atr5', max_amount={'0': 100}, verbose=2, buy_dict={ '0': [ datetime.date(2000, 1, 1), datetime.date(2000, 1, 2), datetime.date(2000, 1, 3) ] }) ts.on_calc_buy_amount(datetime.date(2000, 1, 1), '0', 1, 1000, row=row, verbose=2) ts.on_calc_buy_amount(datetime.date(2000, 1, 2), '0', 2, 1000, row=row, verbose=2) print(ts.holds['0'][0]) print(ts.holds['0'][1]) # 超过最大持仓线时不再购买 assert not ts.on_check_buy( datetime.date(2000, 1, 2), '0', 1.01, 500, row=row, verbose=2) print(ts.holds['0'][0]) assert not ts.on_check_buy( datetime.date(2000, 1, 2), '0', 1.1, 500, row=row, verbose=2) # 止损数量 assert 100 == ts.on_calc_sell_amount(datetime.date(2000, 1, 3), '0', 1.8, 0, 0, 0, row=row, verbose=2) print(ts.holds['0'][0]) assert 100 == ts.holds['0'][0].amount
def test_TurtleStrategy_MaxDays(): symbol = '123456' holds = { symbol: [ TurtleStrategy.Hold( symbol, datetime.date(1999, 1, 1), 10, # 买入价 100, # 持仓数量 9, # 止损价 11, # 止盈价 10.5 # 下一个仓位价格 ), TurtleStrategy.Hold( symbol, datetime.date(1999, 1, 2), 20, # 买入价 100, # 持仓数量 9, # 止损价 11, # 止盈价 10.5 # 下一个仓位价格 ), ] } ts = TurtleStrategy( colname='', holds=holds, ) assert ts._get_overdue(symbol, datetime.date(2222, 1, 1)) is None ts = TurtleStrategy( colname='', holds=holds, max_days=10, ) assert not ts._get_overdue(symbol, datetime.date(1999, 1, 9)) assert not ts._get_overdue(symbol, datetime.date(1999, 1, 10)) assert len(ts._get_overdue(symbol, datetime.date(1999, 1, 11))) > 0 assert ts.on_calc_sell_amount(datetime.date(1999, 1, 9), symbol, 10, 0, 0, 0, verbose=2) == 0 assert sum([h.amount for h in ts.holds[symbol]]) == 200 assert ts.on_calc_sell_amount(datetime.date(1999, 1, 10), symbol, 10, 0, 0, 0, verbose=2) == 0 assert sum([h.amount for h in ts.holds[symbol]]) == 200 assert ts.on_calc_sell_amount(datetime.date(1999, 1, 11), symbol, 10, 0, 0, 0, verbose=2) == 100 assert sum([h.amount for h in ts.holds[symbol]]) == 100 assert ts.holds[symbol][0].price == 20
def test_TurtleStrategy_4(): symbol = '123456' holds = { symbol: [ TurtleStrategy.Hold( symbol, datetime.date(1999, 1, 1), 10, # 买入价 100, # 持仓数量 9, # 止损价 11, # 止盈价 10.5 # 下一个仓位价格 ) ] } ts = TurtleStrategy('', holds=holds, buy_dict={symbol: [datetime.date(2000, 1, 1)]}) assert 1 == len(ts.holds[symbol]) # 没有到达加仓点位 assert not ts.on_check_buy( datetime.date(2000, 1, 1), symbol, 10.49, 1000, verbose=2) # 到达加仓点位 assert ts.on_check_buy(datetime.date(2000, 1, 1), symbol, 10.50, 1000, verbose=2) # 没有加仓点位 assert 0 == ts.on_calc_buy_amount(datetime.date(2000, 1, 1), symbol, 10.49, 1000, verbose=2) # 到达购买点,但是资金不足 assert 0 == ts.on_calc_buy_amount(datetime.date(2000, 1, 1), symbol, 10.50, 1000, verbose=2) # 到达购买点,资金充足 assert 100 == ts.on_calc_buy_amount(datetime.date(2000, 1, 1), symbol, 10.50, 1200, verbose=2) assert 2 == len(ts.holds[symbol]) assert ts.holds[symbol][-1].amount == 100 assert ts.holds[symbol][-1].stoploss_price == -1 assert ts.holds[symbol][-1].stopprofit_price == -1 assert ts.holds[symbol][-1].next_price == -1 # 到达加仓点位 assert ts.on_check_buy(datetime.date(2000, 1, 1), symbol, 10.50, 1000) assert 100 == ts.on_calc_buy_amount(datetime.date(2000, 1, 1), symbol, 10.50, 1200, verbose=2) # 到达加仓点位 assert ts.on_check_buy(datetime.date(2000, 1, 1), symbol, 10.50, 1000) assert 100 == ts.on_calc_buy_amount(datetime.date(2000, 1, 1), symbol, 10.50, 1200, verbose=2) # 到达加仓点位。但是到达持仓限制 assert not ts.on_check_buy( datetime.date(2000, 1, 1), symbol, 10.50, 1000, verbose=2)
def test_all_years_single_symbol(symbol, init_cash=10000, start_year=2005, end_year=2007, verbose=2, show_report=True, show_plot=True, cbs=cbs, show_history=False, min_amount=100, single_max=400, **kwargs): """ Args: init_cash: start_year (int): 开始计算年份。 end_year (int): 结束计算年份。 Returns: report (dict): BackTest字典。key值为年份。 datas (dict): 回测用的数据源字典。key值为年份。 buys (dict): 买点字典。key值为年份。 sells (dict): 卖点字典。key值为年份。 """ hold = pd.DataFrame() report = {} datas = {} buys = {} sells = {} h = {} from tqdm import tqdm for year in tqdm(range(start_year, end_year)): df_symbol_year = ret_datas[ (ret_datas.index.get_level_values(0) == symbol) & (ret_datas.index.get_level_values(1) <= '{}-12-31'.format( year))] s = Simulation(df_symbol_year.reset_index(), symbol, callbacks=cbs) s.simulate() df_symbol_years = s.data df_symbol_years.sort_values('date', inplace=True) # 遍历股票,对每支股票进行数据处理-结束 buy_dict = df_symbol_years[ df_symbol_years['opt'] == 1].reset_index().groupby('code')[ 'date'].apply(lambda x: x.dt.to_pydatetime()).to_dict() sell_dict = df_symbol_years[ df_symbol_years['opt'] == 0].reset_index().groupby('code')[ 'date'].apply(lambda x: x.dt.to_pydatetime()).to_dict() buys[year] = buy_dict sells[year] = sell_dict df_symbol_years = df_symbol_years[ df_symbol_years['date'] >= '{}-01-01'.format(year)] datas[year] = df_symbol_years if verbose == 2: print('起止日期:{:%Y-%m-%d}~{:%Y-%m-%d}'.format( min(df_symbol_years['date']), max(df_symbol_years['date']))) # print('数据量:{}'.format(len(df_symbol_years))) ts = TurtleStrategy( colname='atr_20', buy_dict=buy_dict, sell_dict=sell_dict, holds=h, max_amount=single_max, min_amount=min_amount, ) bt = BackTest(df_symbol_years, init_cash=init_cash, init_hold=hold, live_start_date=datetime.datetime(year, 1, 1), callbacks=[ts]) bt.calc_trade_history(verbose=2) report[year] = bt h = ts.holds if h: hs = [] for k, v in h.items(): for v1 in v: hs.append( pd.DataFrame({ 'code': [k], 'amount': [v1.amount], 'price': [v1.price], 'buy_date': [v1.date], 'stoploss_price': [v1.stoploss_price], 'stopprofit_price': [v1.stopprofit_price], 'next_price': [v1.next_price], })) hold = pd.concat(hs) if hs else pd.DataFrame() init_cash = bt.available_cash if show_report: print(bt.report(show_history=show_history)) if show_report or show_plot: rp = bt.profit_loss_df() if show_report: print(rp) return report, datas, buys, sells
def all_years(fulldata, cbs, init_cash=10000, start_year=2005, end_year=2020, lookback=1, verbose=2, show_report=True, show_plot=True, tb_kwgs={}, top=10, show_history=False, **kwargs): """逐年度对流动性最大的n支股票进行回测。 采用 :py:class:`finance_tools_py.backtest.TurtleStrategy` 进行回测 Args: fulldata (:py:class:`pandas.DataFrame`): 完整的原始数据。 index[0]为股票代码,index[1]为日期。 init_cash: top (int): 选取流动性最大的n值股票。默认为10。 流动性计算参考 :py:func:`finance_tools_py.calc.fluidity` start_year (int): 开始计算年份。 end_year (int): 结束计算年份。 lookback (int): 回看几年的数据,用来计算流动性。当lookback==1时,实际会回看 cbs ([:py:class:`finance_tools_py.simulation.callbacks.CallBack`]): 对数据进行模拟填充时的回调。参考 :py:class:`finance_tools_py.simulation.Simulation` 中的`callbacks`参数。 tb_kwgs (dict): :py:class:`finance_tools_py.backtest.TurtleStrategy` 的参数集合。 unit_percent (float): 计算头寸单元时使用的基准,默认为1%。 fixed_unit (bool): 是否使用固定金额(init_cash)作为计算头寸单元的标的。默认为True。 如果为False的话,会在每年开始时,使用上一年度的总资产(:py:attr:`finance_tools_py.backtest.BackTest.total_assets_cur`)结合`unit_percent`进行运算。 如果是第一年则使用`init_cash`结合`unit_percent`进行运算。 Returns: - dict: BackTest字典。key值为年份。 - dict: 回测用的数据源字典。key值为年份。 - dict: 买点字典。key值为年份。 - dict: 卖点字典。key值为年份。 """ hold = pd.DataFrame() report = {} datas = {} buys = {} sells = {} h = {} tb_kwgs_copy = copy.deepcopy(tb_kwgs) baseValue = init_cash * kwargs.get('unit_percent', 0.01) #计算头寸单元时使用的基准 lookbacks = [] years = [] lookback = lookback - 1 for i in range(start_year, end_year): if i + lookback + 1 <= end_year: lookbacks.append([i, i + lookback]) years.append(i + lookback + 1) if verbose == 2: for lookback, year in zip(lookbacks, years): print(lookback, year) _top_dict = {} _every_year_dict = {} if 'symbol' not in fulldata.columns: fulldata['symbol'] = fulldata.index.get_level_values(0) if 'datetime' not in fulldata.columns: fulldata['datetime'] = fulldata.index.get_level_values(1) for look, year in tqdm(zip(lookbacks, years)): # 取 year 年的n支流动性最大的股票-开始 year_df = fulldata[ (fulldata['datetime'] <= datetime.date(look[-1], 12, 31)) & (fulldata['datetime'] >= datetime.date(look[0], 1, 1))] # # year_df = fluidity(year_df) # # #计算所有的头寸单元 # year_df.dropna(inplace=True) # year_df['unit'] = year_df.apply(lambda row: int(position_unit(row['close'], row['atr_20'], baseValue)/100), axis=1) # year_df=year_df[year_df['unit']>0] # #计算所有的头寸单元 # # top_year = year_df[:top] if top > 0 else year_df tb_kwgs_copy['min_amount'] = {} tb_kwgs_copy['max_amount'] = {} top_year = [] for v in fluidity(year_df).index.values: c = _cache(v) _s_data = None if c not in _top_dict or _top_dict[c] is None: df_symbol = year_df[year_df['symbol'] == v] s = Simulation(df_symbol.reset_index(), v, callbacks=[ATR(20)]) #TODO s.simulate() s.data.dropna(inplace=True) _s_data = s.data.copy() _top_dict[c] = _s_data.copy() else: _s_data = _top_dict[c] if _s_data.empty: continue _s_data['unit'] = _s_data.apply(lambda row: position_unit( row['close'], row[tb_kwgs_copy['colname']], baseValue), axis=1) m = int(_s_data.iloc[-1]['unit'] / 100) * 100 if m > 0: tb_kwgs_copy['min_amount'][v] = m tb_kwgs_copy['max_amount'][v] = m * 4 top_year.append(v) else: if verbose > 0: print('{}-根据时间 {:%Y-%m-%d}~{:%Y-%m-%d} 计算头寸单位大小为0'.format( v, year_df['datetime'][0], year_df['datetime'][-1])) if len(top_year) >= top: break ls = list( set( list(top_year) + list(hold['code'].unique() if not hold.empty else []))) if not ls: if verbose > 0: print('{}无任何可跟踪的股票'.format(year)) continue # 取 year 年的10支流动性最大的股票-结束 # print('{}年的10支流动性最大的股票'.format(year)) # 遍历股票,对每支股票进行数据处理-开始 df_symbol_years = [] for symbol in ls: c = _cache(symbol, look[0], year) _s_data = None if c not in _every_year_dict or _every_year_dict[c] is None: df_symbol_year = fulldata[ (fulldata['symbol'] == symbol) & (fulldata['datetime'] <= datetime.date(year, 12, 31)) & (fulldata['datetime'] >= datetime.date(look[0], 1, 1))] s = Simulation(df_symbol_year.reset_index(), symbol, callbacks=cbs) s.simulate() _s_data = s.data.copy() else: _s_data = _every_year_dict[c] df_symbol_years.append(_s_data) df_symbol_years = pd.concat(df_symbol_years) df_symbol_years.sort_values('date', inplace=True) # 遍历股票,对每支股票进行数据处理-结束 buy_dict = df_symbol_years[df_symbol_years['opt'] == 1].reset_index( ).groupby('code')['date'].apply( lambda x: x.dt.to_pydatetime()).to_dict() sell_dict = df_symbol_years[df_symbol_years['opt'] == 0].reset_index( ).groupby('code')['date'].apply( lambda x: x.dt.to_pydatetime()).to_dict() if not hold.empty: for onlysell in set(hold['code'].to_list()).difference( set(top_year)): if onlysell in buy_dict: del buy_dict[onlysell] buys[year] = buy_dict sells[year] = sell_dict df_symbol_years = df_symbol_years[ df_symbol_years['date'] >= '{}-01-01'.format(year)] datas[year] = df_symbol_years if verbose == 2: print('起止日期:{:%Y-%m-%d}~{:%Y-%m-%d}'.format( min(df_symbol_years['date']), max(df_symbol_years['date']))) # print('数据量:{}'.format(len(df_symbol_years))) ts = TurtleStrategy(buy_dict=buy_dict, sell_dict=sell_dict, holds=h, **tb_kwgs_copy) bt = BackTest(df_symbol_years, init_cash=init_cash, init_hold=hold, live_start_date=datetime.datetime(year, 1, 1), callbacks=[ts]) bt.calc_trade_history(verbose=verbose) report[year] = bt if not kwargs.get('fixed_unit', True): baseValue = bt.total_assets_cur * kwargs.get('unit_percent', 0.01) # 计算头寸单元时使用的基准 h = ts.holds if h: hs = [] for k, v in h.items(): for v1 in v: hs.append( pd.DataFrame({ 'code': [k], 'amount': [v1.amount], 'price': [v1.price], 'buy_date': [v1.date], 'stoploss_price': [v1.stoploss_price], 'stopprofit_price': [v1.stopprofit_price], 'next_price': [v1.next_price], })) hold = pd.concat(hs) if hs else pd.DataFrame() init_cash = bt.available_cash if show_report: print(bt.report(show_history=show_history)) if show_report or show_plot: rp = bt.profit_loss_df() if show_report: print(rp) if show_plot: fig, axes = plt.subplots(1, 2, figsize=(10, 3)) Utils.plt_win_rate(rp, ax=axes[0]) Utils.plt_pnl_ratio(rp, ax=axes[1]) plt.gcf().autofmt_xdate() plt.show() df_profit = pd.concat([x.profit_loss_df() for x in report.values()]) return df_profit, report, datas, buys, sells
def all_years_single_symbol(symbol, fulldata, cbs, init_cash=10000, start_year=2005, end_year=2007, verbose=2, show_report=True, show_plot=True, show_history=False, tb_kwgs={}, **kwargs): """逐年度对单一股票进行回测。 采用 :py:class:`finance_tools_py.backtest.TurtleStrategy` 进行回测 Args: fulldata (:py:class:`pandas.DataFrame`): 完整的原始数据。 index[0]为股票代码,index[1]为日期。 init_cash: start_year (int): 开始计算年份。 end_year (int): 结束计算年份。 cbs ([:py:class:`finance_tools_py.simulation.callbacks.CallBack`]): 对数据进行模拟填充时的回调。参考 :py:class:`finance_tools_py.simulation.Simulation` 中的`callbacks`参数。 tb_kwgs (dict): :py:class:`finance_tools_py.backtest.TurtleStrategy` 的参数集合。 Returns: - dict: BackTest字典。key值为年份。 - dict: 回测用的数据源字典。key值为年份。 - dict: 买点字典。key值为年份。 - dict: 卖点字典。key值为年份。 """ hold = pd.DataFrame() report = {} datas = {} buys = {} sells = {} h = {} tb_kwgs_copy = copy.deepcopy(tb_kwgs) for year in tqdm(range(start_year, end_year)): df_symbol_year = fulldata[ (fulldata.index.get_level_values(0) == symbol) & (fulldata.index.get_level_values(1) <= '{}-12-31'.format(year))] s = Simulation(df_symbol_year.reset_index(), symbol, callbacks=cbs) s.simulate() df_symbol_years = s.data df_symbol_years.sort_values('date', inplace=True) # 遍历股票,对每支股票进行数据处理-结束 buy_dict = df_symbol_years[df_symbol_years['opt'] == 1].reset_index( ).groupby('code')['date'].apply( lambda x: x.dt.to_pydatetime()).to_dict() sell_dict = df_symbol_years[df_symbol_years['opt'] == 0].reset_index( ).groupby('code')['date'].apply( lambda x: x.dt.to_pydatetime()).to_dict() buys[year] = buy_dict sells[year] = sell_dict df_symbol_years = df_symbol_years[ df_symbol_years['date'] >= '{}-01-01'.format(year)] datas[year] = df_symbol_years if verbose == 2: print('起止日期:{:%Y-%m-%d}~{:%Y-%m-%d}'.format( min(df_symbol_years['date']), max(df_symbol_years['date']))) # print('数据量:{}'.format(len(df_symbol_years))) ts = TurtleStrategy(buy_dict=buy_dict, sell_dict=sell_dict, holds=h, **tb_kwgs_copy) bt = BackTest(df_symbol_years, init_cash=init_cash, init_hold=hold, live_start_date=datetime.datetime(year, 1, 1), callbacks=[ts]) bt.calc_trade_history(verbose=2) report[year] = bt h = ts.holds if h: hs = [] for k, v in h.items(): for v1 in v: hs.append( pd.DataFrame({ 'code': [k], 'amount': [v1.amount], 'price': [v1.price], 'buy_date': [v1.date], 'stoploss_price': [v1.stoploss_price], 'stopprofit_price': [v1.stopprofit_price], 'next_price': [v1.next_price], })) hold = pd.concat(hs) if hs else pd.DataFrame() init_cash = bt.available_cash if show_report: print(bt.report(show_history=show_history)) if show_report or show_plot: rp = bt.profit_loss_df() if show_report: print(rp) if show_plot: fig, axes = plt.subplots(1, 2, figsize=(10, 3)) Utils.plt_win_rate(rp, ax=axes[0]) Utils.plt_pnl_ratio(rp, ax=axes[1]) plt.gcf().autofmt_xdate() plt.show() return report, datas, buys, sells