本帖最后由 老刘1号 于 2023-3-3 12:32 编辑
论坛正好有python板块,发来玩玩
回测环境:聚宽量化 | import talib | | from prettytable import PrettyTable | | import pandas | | import datetime | | import time | | from jqdata import * | | | | | | def initialize(context): | | | | g.buy_stock_count = 20 | | | | | | set_benchmark('000300.XSHG') | | | | set_option('use_real_price', True) | | | | set_option("avoid_future_data", True) | | | | set_order_cost( | | OrderCost( | | open_tax =0, | | close_tax =0.001, | | open_commission =0.0003, | | close_commission=0.0003, | | min_commission =5), | | type ='stock') | | set_slippage(FixedSlippage(0.02)) | | | | log.set_level('order', 'error') | | | | run_daily(daily,'open') | | | | def daily(context): | | adjust_position(context, select_stocks(context)) | | | | def select_stocks(context): | | | | df=finance.run_query( | | query(finance.STK_HK_HOLD_INFO)\ | | .filter(finance.STK_HK_HOLD_INFO.day>=context.previous_date)\ | | .filter(finance.STK_HK_HOLD_INFO.link_id.in_([310001,310002]))) | | stocks = [ { | | 'code' : code, | | 'name' : name, | | 'number' : share_number, | | 'price' : price, | | 'value' : share_number * price | | } for code, name, share_number, price in zip( | | list(df.code), \ | | list(df.name), \ | | list(df.share_number), \ | | list(get_price( | | security=list(df.code), | | panel=False, | | start_date=context.previous_date, | | end_date=context.previous_date, | | frequency='daily', | | fields='close', | | skip_paused=False, | | fq='pre')['close']) | | )] | | | | stocks = sorted(stocks, key = lambda stock: stock['value'], reverse=True) | | | | | | | | return [stock['code'] for stock in stocks][:g.buy_stock_count] | | | | def filter_paused_and_st_stock(stock_list): | | current_data = get_current_data() | | return [stock for stock in stock_list if not current_data[stock].paused | | and not current_data[stock].is_st and 'ST' not in current_data[stock]. | | name and '*' not in current_data[stock].name and '退' not in current_data[stock].name] | | | | def adjust_position(context, buy_stocks): | | | | if len(context.portfolio.positions)>0: | | last_prices = history(1, '1m', 'close', security_list=list(context.portfolio.positions.keys())) | | for stock in list(context.portfolio.positions.keys()): | | if stock not in buy_stocks : | | curr_data = get_current_data() | | if last_prices[stock][-1] < curr_data[stock].high_limit: | | order_target_value(stock, 0) | | | | for stock in buy_stocks: | | position_count = len(context.portfolio.positions) | | if g.buy_stock_count > position_count: | | value = context.portfolio.cash / (g.buy_stock_count - position_count) | | if context.portfolio.positions[stock].total_amount == 0: | | stock_price = get_price( | | security=stock, | | panel=False, | | start_date=context.previous_date, | | end_date=context.previous_date, | | frequency='daily', | | fields='close', | | skip_paused=False, | | fq='pre')['close'] | | order(stock, (((context.portfolio.available_cash+context.portfolio.positions_value+ context.portfolio.returns)/g.buy_stock_count)/stock_price)//100*100) | | | | def show_stock(stock): | | ''' | | 获取股票代码的显示信息 | | :param stock: 股票代码,例如: '603822.SH' | | :return: str,例如:'603822 嘉澳环保' | | ''' | | return "%s %s" % (stock[:6], get_security_info(stock).display_name) | | | | def get_portfolio_info_text(context,new_stocks,op_sfs=[0]): | | | | sub_str = '' | | table = PrettyTable(["仓号","股票", "持仓", "当前价", "盈亏率","持仓比"]) | | for sf_id in range(len(context.subportfolios)): | | cash = context.subportfolios[sf_id].cash | | p_value = context.subportfolios[sf_id].positions_value | | total_values = p_value +cash | | if sf_id in op_sfs: | | sf_id_str = str(sf_id) + ' *' | | else: | | sf_id_str = str(sf_id) | | for stock in list(context.subportfolios[sf_id].long_positions.keys()): | | position = context.subportfolios[sf_id].long_positions[stock] | | if sf_id in op_sfs and stock in new_stocks: | | stock_str = show_stock(stock) + ' *' | | else: | | stock_str = show_stock(stock) | | stock_raite = (position.total_amount * position.price) / total_values * 100 | | table.add_row([sf_id_str, | | stock_str, | | position.total_amount, | | position.price, | | "%.2f%%"%((position.price - position.avg_cost) / position.avg_cost * 100), | | "%.2f%%"%(stock_raite)] | | ) | | if sf_id < len(context.subportfolios) - 1: | | table.add_row(['----','---------------','-----','----','-----','-----']) | | sub_str += '[仓号: %d] [总值:%d] [持股数:%d] [仓位:%.2f%%] \n'%(sf_id, total_values, | | len(context.subportfolios[sf_id].long_positions), p_value*100/(cash+p_value)) | | log.info('持仓详情:\n' + sub_str + str(table))COPY |
回测数据 | 回测时间 | | 2022-02-08 20:20:53 | | 回测区间 | | 2017-03-20 - 2022-02-07 | | 策略收益 | | 129.20% | | 策略年化收益 | | 19.07% | | 超额收益 | | 70.43% | | 基准收益 | | 34.48% | | 阿尔法 | | 0.128 | | 贝塔 | | 0.915 | | 夏普比率 | | 0.759 | | 胜率 | | 0.846 | | 盈亏比 | | 13.917 | | 最大回撤 | | 28.29% | | 索提诺比率 | | 1.082 | | 日均超额收益 | | 0.05% | | 超额收益最大回撤 | | 13.77% | | 超额收益夏普比率 | | 0.857 | | 日胜率 | | 0.536 | | 盈利次数 | | 11 | | 亏损次数 | | 2 | | 信息比率 | | 1.376 | | 策略波动率 | | 0.199 | | 基准波动率 | | 0.193 | | 最大回撤区间 | | 2021/02/10,2022/01/28COPY |
|