一字涨停策略简单实现

策略分享
标签: #<Tag:0x00007f4ce8db9750>

(bluexxxx) #1

策略简介:昨日一字涨停,今日开盘买入,明日开盘卖出

思想很简单,实现了一下,策略效果并不是很好。大家觉得有哪些改进空间:

克隆策略
In [2]:
start_date = '2016-01-01'
end_date = '2017-09-14'
instrument = D.instruments(start_date=start_date, end_date=end_date)
df =D.history_data(instrument, start_date, end_date, ['high', 'low','price_limit_status' ,'amount'])
raw_df = df[(df['price_limit_status']==3)&(df['high']==df['low'])&(df['amount']>=10000)]   # 选出一字涨停的股票
result = raw_df.groupby('date').apply(lambda df: list(df['instrument']))  # 整理出每日一字涨停的股票列表
In [5]:
def initialize(context):
    context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5)) # 设置手续费
    context.hold_days = dict()
    context.max_hold = 25  # 一共持有25个股票
def handle_data(context, data):
    dt = data.current_dt.strftime('%Y-%m-%d')
    stock_hold_now = [equity.symbol for equity in context.portfolio.positions]  # 目前持有的股票
    # 当日买入股票列表 
    try:
        buy = result.ix[dt] 
    except KeyError as e:
        buy = []
    weight = 1 / context.max_hold  # 先采取固定比例 
    for st in buy:  # 达到买入条件就买入
        sid = context.symbol(st)
        cur_position = context.portfolio.positions[sid].amount
        if cur_position == 0 and data.can_trade(sid):
            order_target_percent(sid, weight)
            context.hold_days[st] = context.trading_day_index     # 记录买入时间
    for st in stock_hold_now:   # 持仓大于1天就卖出
        sid = context.symbol(st)
        cur_position = context.portfolio.positions[sid].amount
        if cur_position > 0 and data.can_trade(sid) and context.trading_day_index - context.hold_days[st] > 1:
             order_target_percent(sid, 0)     
m = M.trade.v2(  # 执行回测
    instruments=instrument,
    start_date=start_date,
    end_date=end_date,
    initialize=initialize,
    handle_data=handle_data,
    order_price_field_buy='open',
    order_price_field_sell='open',
    capital_base=20000000,
    benchmark='000300.INDX',
)
[2017-09-15 17:38:40.251471] INFO: bigquant: backtest.v7 开始运行..
[2017-09-15 17:39:24.190981] INFO: Performance: Simulated 417 trading days out of 417.
[2017-09-15 17:39:24.192149] INFO: Performance: first open: 2016-01-04 14:30:00+00:00
[2017-09-15 17:39:24.193045] INFO: Performance: last close: 2017-09-14 19:00:00+00:00
  • 收益率-73.97%
  • 年化收益率-55.67%
  • 基准收益率2.65%
  • 阿尔法-0.59
  • 贝塔0.29
  • 夏普比率-3.2
  • 收益波动率18.76%
  • 信息比率-2.59
  • 最大回撤74.09%
[2017-09-15 17:39:26.704450] INFO: bigquant: backtest.v7 运行完成[46.45298s].

(飞天猫大浩) #2

这个策略有点意思。。。可以拿来做空啊


(神龙斗士) #3

做了一个可视化版本

克隆策略

    {"Description":"实验创建于2017/9/17","Summary":"","Graph":{"EdgesInternal":[{"DestinationInputPortId":"-129:options_data","SourceOutputPortId":"-124:data_1"}],"ModuleNodes":[{"Id":"-124","ModuleId":"BigQuantSpace.cached.cached-v3","ModuleParameters":[{"Name":"run","Value":"def bigquant_run(input_1, input_2, input_3):\n start_date = '2016-01-01'\n end_date = '2017-09-14'\n instrument = D.instruments(start_date=start_date, end_date=end_date)\n df = D.history_data(instrument, start_date, end_date, ['high', 'low', 'price_limit_status', 'amount'])\n\n # 选出一字涨停的股票\n selected_df = df[(df['price_limit_status'] == 3) & (df['high'] == df['low']) & (df['amount'] >= 10000)]\n\n return Outputs(data_1=DataSource.write_df(selected_df[['date', 'instrument']]), data_2=None, data_3=None)\n","ValueType":"Literal","LinkedGlobalParameter":null}],"InputPortsInternal":[{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"input_1","NodeId":"-124"},{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"input_2","NodeId":"-124"},{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"input_3","NodeId":"-124"}],"OutputPortsInternal":[{"Name":"data_1","NodeId":"-124","OutputType":null},{"Name":"data_2","NodeId":"-124","OutputType":null},{"Name":"data_3","NodeId":"-124","OutputType":null}],"UsePreviousResults":true,"moduleIdForCode":1,"IsPartOfPartialRun":null,"Comment":"数据计算:一字涨停的股票列表","CommentCollapsed":false},{"Id":"-129","ModuleId":"BigQuantSpace.trade.trade-v3","ModuleParameters":[{"Name":"start_date","Value":"","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"end_date","Value":"","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"handle_data","Value":"# 回测引擎:每日数据处理函数,每天执行一次\ndef bigquant_run(context, data):\n today = data.current_dt.strftime('%Y-%m-%d')\n # 目前持有的股票\n stock_hold_now = [equity.symbol for equity in context.portfolio.positions]\n # 当日买入股票列表 \n try:\n buy = list(context.selected_df[context.selected_df.date == today].instrument)\n except KeyError as e:\n buy = []\n\n weight = 1 / context.max_hold # 先采取固定比例 \n for st in buy: # 达到买入条件就买入\n sid = context.symbol(st)\n cur_position = context.portfolio.positions[sid].amount\n if cur_position == 0 and data.can_trade(sid):\n order_target_percent(sid, weight)\n context.hold_days[st] = context.trading_day_index # 记录买入时间\n for st in stock_hold_now: # 持仓大于1天就卖出\n sid = context.symbol(st)\n cur_position = context.portfolio.positions[sid].amount\n if cur_position > 0 and data.can_trade(sid) and context.trading_day_index - context.hold_days[st] > 1:\n order_target_percent(sid, 0)\n","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"prepare","Value":"# 回测引擎:准备数据,只执行一次\ndef bigquant_run(context):\n df = context.options['data'].read_df()\n # 设置 instruments / start_date / end_date\n context.instruments = list(set(df['instrument']))\n context.start_date = df['date'].min()\n context.end_date = df['date'].max()\n # context.selected_df 后面可以直接使用\n context.selected_df = df\n","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"initialize","Value":"# 回测引擎:初始化函数,只执行一次\ndef bigquant_run(context):\n # 系统已经设置了默认的交易手续费和滑点,要修改手续费可使用如下函数\n context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))\n context.max_hold = 20\n context.hold_days = {}\n","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"before_trading_start","Value":"","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"volume_limit","Value":0.025,"ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"order_price_field_buy","Value":"open","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"order_price_field_sell","Value":"open","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"capital_base","Value":1000000,"ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"benchmark","Value":"000300.SHA","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"auto_cancel_non_tradable_orders","Value":"True","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"data_frequency","Value":"daily","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"plot_charts","Value":"True","ValueType":"Literal","LinkedGlobalParameter":null},{"Name":"backtest_only","Value":"False","ValueType":"Literal","LinkedGlobalParameter":null}],"InputPortsInternal":[{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"instruments","NodeId":"-129"},{"DataSourceId":null,"TrainedModelId":null,"TransformModuleId":null,"Name":"options_data","NodeId":"-129"}],"OutputPortsInternal":[{"Name":"raw_perf","NodeId":"-129","OutputType":null}],"UsePreviousResults":false,"moduleIdForCode":2,"IsPartOfPartialRun":null,"Comment":"","CommentCollapsed":true}],"SerializedClientData":"<?xml version='1.0' encoding='utf-16'?><DataV1 xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'><Meta /><NodePositions><NodePosition Node='-124' Position='333,60,200,200'/><NodePosition Node='-129' Position='331,221,200,200'/></NodePositions><NodeGroups /></DataV1>"},"IsDraft":true,"ParentExperimentId":null,"WebService":{"IsWebServiceExperiment":false,"Inputs":[],"Outputs":[],"Parameters":[{"Name":"交易日期","Value":"","ParameterDefinition":{"Name":"交易日期","FriendlyName":"交易日期","DefaultValue":"","ParameterType":"String","HasDefaultValue":true,"IsOptional":true,"ParameterRules":[],"HasRules":false,"MarkupType":0,"CredentialDescriptor":null}}],"WebServiceGroupId":null,"SerializedClientData":"<?xml version='1.0' encoding='utf-16'?><DataV1 xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'><Meta /><NodePositions></NodePositions><NodeGroups /></DataV1>"},"DisableNodesUpdate":false,"Category":"user","Tags":[],"IsPartialRun":false}
    In [17]:
    # 本代码由可视化策略环境自动生成 2017年9月18日 00:54
    # 本代码单元只能在可视化模式下编辑。您也可以拷贝代码,粘贴到新建的代码单元或者策略,然后修改。
    
    
    def m1_run_bigquant_run(input_1, input_2, input_3):
        start_date = '2016-01-01'
        end_date = '2017-09-14'
        instrument = D.instruments(start_date=start_date, end_date=end_date)
        df = D.history_data(instrument, start_date, end_date, ['high', 'low', 'price_limit_status', 'amount'])
    
        # 选出一字涨停的股票
        selected_df = df[(df['price_limit_status'] == 3) & (df['high'] == df['low']) & (df['amount'] >= 10000)]
    
        return Outputs(data_1=DataSource.write_df(selected_df[['date', 'instrument']]), data_2=None, data_3=None)
    
    m1 = M.cached.v3(
        run=m1_run_bigquant_run
    )
    
    # 回测引擎:每日数据处理函数,每天执行一次
    def m2_handle_data_bigquant_run(context, data):
        today = data.current_dt.strftime('%Y-%m-%d')
        # 目前持有的股票
        stock_hold_now = [equity.symbol for equity in context.portfolio.positions]
        # 当日买入股票列表 
        try:
            buy = list(context.selected_df[context.selected_df.date == today].instrument)
        except KeyError as e:
            buy = []
    
        weight = 1 / context.max_hold  # 先采取固定比例 
        for st in buy:  # 达到买入条件就买入
            sid = context.symbol(st)
            cur_position = context.portfolio.positions[sid].amount
            if cur_position == 0 and data.can_trade(sid):
                order_target_percent(sid, weight)
                context.hold_days[st] = context.trading_day_index     # 记录买入时间
        for st in stock_hold_now:   # 持仓大于1天就卖出
            sid = context.symbol(st)
            cur_position = context.portfolio.positions[sid].amount
            if cur_position > 0 and data.can_trade(sid) and context.trading_day_index - context.hold_days[st] > 1:
                 order_target_percent(sid, 0)
    
    # 回测引擎:准备数据,只执行一次
    def m2_prepare_bigquant_run(context):
        df = context.options['data'].read_df()
        # 设置 instruments / start_date / end_date
        context.instruments = list(set(df['instrument']))
        context.start_date = df['date'].min()
        context.end_date = df['date'].max()
        # context.selected_df 后面可以直接使用
        context.selected_df = df
    
    # 回测引擎:初始化函数,只执行一次
    def m2_initialize_bigquant_run(context):
        # 系统已经设置了默认的交易手续费和滑点,要修改手续费可使用如下函数
        context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
        context.max_hold = 20
        context.hold_days = {}
    
    m2 = M.trade.v3(
        options_data=m1.data_1,
        start_date='',
        end_date='',
        handle_data=m2_handle_data_bigquant_run,
        prepare=m2_prepare_bigquant_run,
        initialize=m2_initialize_bigquant_run,
        volume_limit=0.025,
        order_price_field_buy='open',
        order_price_field_sell='open',
        capital_base=1000000,
        benchmark='000300.SHA',
        auto_cancel_non_tradable_orders=True,
        data_frequency='daily',
        plot_charts=True,
        backtest_only=False
    )
    
    [2017-09-18 00:43:08.906125] INFO: bigquant: cached.v3 开始运行..
    [2017-09-18 00:43:08.909793] INFO: bigquant: 命中缓存
    [2017-09-18 00:43:08.910617] INFO: bigquant: cached.v3 运行完成[0.004536s].
    [2017-09-18 00:43:08.926162] INFO: bigquant: backtest.v7 开始运行..
    [2017-09-18 00:43:34.466465] INFO: Performance: Simulated 417 trading days out of 417.
    [2017-09-18 00:43:34.467509] INFO: Performance: first open: 2016-01-04 14:30:00+00:00
    [2017-09-18 00:43:34.468381] INFO: Performance: last close: 2017-09-14 19:00:00+00:00
    
    • 收益率-79.34%
    • 年化收益率-61.45%
    • 基准收益率2.65%
    • 阿尔法-0.65
    • 贝塔0.35
    • 夏普比率-2.74
    • 收益波动率24.06%
    • 信息比率-2.43
    • 最大回撤80.67%
    [2017-09-18 00:43:36.276636] INFO: bigquant: backtest.v7 运行完成[27.35043s].
    
    In [18]:
    m1.data_1.read_df().head()
    
    Out[18]:
    date instrument
    448 2016-01-04 000971.SZA
    720 2016-01-04 002247.SZA
    940 2016-01-04 002468.SZA
    1241 2016-01-04 002777.SZA
    1243 2016-01-04 002780.SZA

    (jiangxiaoyu610) #4

    我没观察过,但是我想封了一字板之后有两种情况,第一种是继续封板,这样的股票上涨非常强势,但是没有办法买进.第二种,不再封板,可能说明股票已经没有能力再维持涨停板或者上涨趋势了,所以买入很可能会赶上短暂回调或者干脆反转下跌.
    不过这些只是我的想象,楼主可以做一个统计分析,看看一字涨停之后走势的概率分布.


    (maida) #5

    说明开板得赶紧走,别迷恋