10-18

  花了一天时间,根据昨天偷研究的神人账户交割单写的策略,不断调整了参数,回测测试效果惊人的可怕,从07年开始到现在年化140%.

  程序回测结果:

 可以说是相当恐怖了,庆幸自己可以查到如此有价值的数据,感谢FIS。

 测试代码:

'''
配置指定频率的调仓日,在调仓日每日指定时间,计算沪深300指数和中证500指数当前的20日涨
幅,如果2个指数的20日涨幅有一个为正,则进行选股调仓,之后如此循环往复。

止损策略:

    大盘止损:
        1. 每分钟取大盘前130日的最低价和最高价,如果最高大于最低的两倍则清仓,停止交易。
        2. 每分钟判断大盘是否呈现三只黑鸦止损,如果是则当天清仓并停止交易,第二天停止交
           易一天。

    个股止损:
        每分钟判断个股是否从持仓后的最高价回撤幅度,如果超过个股回撤阈值,则平掉该股持仓

    二八止损:
        每日指定时间,计算沪深300指数和中证500指数当前的20日涨幅,如果2个指数涨幅都为负,
        则清仓,重置调仓计数,待下次调仓条件满足再操作

'''

import tradestat
#from blacklist import *

# blacklist.py
# 修改黑名单

# 配置股票黑名单
# 列出当且极不适宜购买的股票
# 注:1. 黑名单有时效性,回测的时候最好不使用,模拟交易建议使用
#     2. 用一模块或者大数据分析收集这类股票,定时更新
def get_blacklist():

    blacklist = ["600656.XSHG","300372.XSHE","600403.XSHG","600421.XSHG","600733.XSHG","300399.XSHE",
                 "600145.XSHG","002679.XSHE","000020.XSHE","002330.XSHE","300117.XSHE","300135.XSHE",
                 "002566.XSHE","002119.XSHE","300208.XSHE","002237.XSHE","002608.XSHE","000691.XSHE",
                 "002694.XSHE","002715.XSHE","002211.XSHE","000788.XSHE","300380.XSHE","300028.XSHE",
                 "000668.XSHE","300033.XSHE","300126.XSHE","300340.XSHE","300344.XSHE","002473.XSHE"]
    return blacklist

def before_trading_start(context):
    log.info("---------------------------------------------")
    #log.info("==> before trading start @ %s", str(context.current_dt))

    # 盘前就判断三黑鸦状态,因为判断的数据为前4日
    g.is_last_day_3_black_crows = is_3_black_crows(g.index_4_stop_loss_by_3_black_crows)
    if g.is_last_day_3_black_crows:
        log.info("==> 前4日已经构成三黑鸦形态")
    pass

def after_trading_end(context):
    #log.info("==> after trading end @ %s", str(context.current_dt))
    g.trade_stat.report(context)

    reset_day_param()
    
    # 得到当前未完成订单
    orders = get_open_orders()
    for _order in orders.values():
        log.info("canceled uncompleted order: %s" %(_order.order_id))
    pass

def initialize(context):
    log.info("==> initialize @ %s", str(context.current_dt))
    
    # 设置手续费率
    set_commission(PerTrade(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
    # 设置基准指数:沪深300指数 '000300.XSHG'
    set_benchmark('000300.XSHG')
    # 设定滑点为百分比
    # 没有调用set_slippage函数, 系统默认的滑点是PriceRelatedSlippage(0.00246)
    #set_slippage(PriceRelatedSlippage(0.004))
    # 使用真实价格回测(模拟盘推荐如此,回测请注释)
    set_option('use_real_price', True)

    # 加载统计模块
    g.trade_stat = tradestat.trade_stat()

    # 配置策略参数
    # 此配置主要为之前的小市值策略,保证之前的收益回撤
    # 如果想要更改,最好新建个函数,调整参数测试其他策略
    # 10日调仓
    # 关闭大盘三乌鸦及高低价止损
    # 关闭个股止盈止损
    # 关闭选股评分
    set_param()

    # 缓存股票持仓后的最高价
    g.last_high = {}

    # 如下参数不能更改
    if g.is_market_stop_loss_by_price:
        # 记录当日是否满足大盘价格止损条件,每日盘后重置
        g.is_day_stop_loss_by_price = False

    # 缓存三黑鸦判断状态
    g.is_last_day_3_black_crows = False
    if g.is_market_stop_loss_by_3_black_crows:
        g.cur_drop_minute_count = 0

    if g.is_rank_stock:
        if g.rank_stock_count > g.pick_stock_count:
            g.rank_stock_count = g.pick_stock_count

    if g.is_stock_stop_loss or g.is_stock_stop_profit:
        # 缓存当日个股250天内最大的3日涨幅,避免当日反复获取,每日盘后清空
        g.pct_change = {}

    # 打印策略参数
    log_param()

def set_param():
    # 调仓频率,单位:日
    g.period = 3
    # 调仓日计数器,单位:日
    g.day_count = 0
    # 配置调仓时间(24小时分钟制)
    g.adjust_position_hour = 14
    g.adjust_position_minute = 50

    # 配置选股参数

    # 备选股票数目
    g.pick_stock_count = 100
    
    # 配置选股参数
    # 是否根据PE选股
    g.pick_by_pe = False
    # 如果根据PE选股,则配置最大和最小PE值
    if g.pick_by_pe:
        g.max_pe = 200
        g.min_pe = 0

    # 是否根据EPS选股
    g.pick_by_eps = True
    # 配置选股最小EPS值
    if g.pick_by_eps:
        g.min_eps = 0
    
    # 配置是否过滤创业板股票
    g.filter_gem = True
    # 配置是否过滤黑名单股票,回测建议关闭,模拟运行时开启
    g.filter_blacklist = True

    # 是否对股票评分
    g.is_rank_stock = True
    if g.is_rank_stock:
        # 参与评分的股票数目
        g.rank_stock_count = 20

    # 买入股票数目
    g.buy_stock_count = 2
    
    # 配置二八指数
    #g.index2 = '000300.XSHG'  # 沪深300指数,表示二,大盘股
    #g.index8 = '000905.XSHG'  # 中证500指数,表示八,小盘股
    g.index2 = '000016.XSHG'  # 上证50指数
    g.index8 = '399333.XSHE'  # 中小板R指数
    #g.index8 = '399006.XSHE'  # 创业板指数
    
    # 判定调仓的二八指数20日增幅
    #g.index_growth_rate_20 = 0.00
    g.index_growth_rate_20 = 0.01

    # 配置是否根据大盘历史价格止损
    # 大盘指数前130日内最高价超过最低价2倍,则清仓止损
    # 注:关闭此止损,收益增加,但回撤会增加
    g.is_market_stop_loss_by_price = True
    if g.is_market_stop_loss_by_price:
        # 配置价格止损判定指数,默认为上证指数,可修改为其他指数
        g.index_4_stop_loss_by_price = '000001.XSHG'

    # 配置三黑鸦判定指数,默认为上证指数,可修改为其他指数
    g.index_4_stop_loss_by_3_black_crows = '000001.XSHG'

    # 配置是否开启大盘三黑鸦止损
    # 个人认为针对大盘判断三黑鸦效果并不好,首先有效三只乌鸦难以判断,准确率实际来看也不好,
    # 其次,分析历史行情看一般大盘出现三只乌鸦的时候,已经严重滞后了,使用其他止损方式可能会更好
    g.is_market_stop_loss_by_3_black_crows = False
    if g.is_market_stop_loss_by_3_black_crows:
        g.dst_drop_minute_count = 10

    # 配置是否个股止损
    g.is_stock_stop_loss =False
    # 配置是否个股止盈
    g.is_stock_stop_profit = False
    
def log_param():
    log.info("调仓日频率: %d日" %(g.period))
    log.info("调仓时间: %s:%s" %(g.adjust_position_hour, g.adjust_position_minute))

    log.info("备选股票数目: %d" %(g.pick_stock_count))

    log.info("是否根据PE选股: %s" %(g.pick_by_pe))
    if g.pick_by_pe:
        log.info("选股最大PE: %s" %(g.max_pe))
        log.info("选股最小PE: %s" %(g.min_pe))

    log.info("是否根据EPS选股: %s" %(g.pick_by_eps))
    if g.pick_by_eps:
        log.info("选股最小EPS: %s" %(g.min_eps))
    
    log.info("是否过滤创业板股票: %s" %(g.filter_gem))
    log.info("是否过滤黑名单股票: %s" %(g.filter_blacklist))
    if g.filter_blacklist:
        log.info("当前股票黑名单:%s" %str(get_blacklist()))

    log.info("是否对股票评分选股: %s" %(g.is_rank_stock))
    if g.is_rank_stock:
        log.info("评分备选股票数目: %d" %(g.rank_stock_count))

    log.info("买入股票数目: %d" %(g.buy_stock_count))

    log.info("二八指数之二: %s - %s" %(g.index2, get_security_info(g.index2).display_name))
    log.info("二八指数之八: %s - %s" %(g.index8, get_security_info(g.index8).display_name))
    log.info("判定调仓的二八指数20日增幅: %.1f%%" %(g.index_growth_rate_20*100))

    log.info("是否开启大盘历史高低价格止损: %s" %(g.is_market_stop_loss_by_price))
    if g.is_market_stop_loss_by_price:
        log.info("大盘价格止损判定指数: %s - %s" %(g.index_4_stop_loss_by_price, get_security_info(g.index_4_stop_loss_by_price).display_name))

    log.info("大盘三黑鸦止损判定指数: %s - %s" %(g.index_4_stop_loss_by_3_black_crows, get_security_info(g.index_4_stop_loss_by_3_black_crows).display_name))
    log.info("是否开启大盘三黑鸦止损: %s" %(g.is_market_stop_loss_by_3_black_crows))
    if g.is_market_stop_loss_by_3_black_crows:
        log.info("三黑鸦止损开启需要当日大盘为跌的分钟计数达到: %d" %(g.dst_drop_minute_count))

    log.info("是否开启个股止损: %s" %(g.is_stock_stop_loss))
    log.info("是否开启个股止盈: %s" %(g.is_stock_stop_profit))

# 重置当日参数,仅针对需要当日需要重置的参数
def reset_day_param():
    if g.is_market_stop_loss_by_price:
        # 重置当日大盘价格止损状态
        g.is_day_stop_loss_by_price = False

    if g.is_stock_stop_loss or g.is_stock_stop_profit:
        # 清空当日个股250天内最大的3日涨幅的缓存
        g.pct_change.clear()

    # 重置三黑鸦状态
    g.is_last_day_3_black_crows = False
    if g.is_market_stop_loss_by_3_black_crows:
        g.cur_drop_minute_count = 0

# 按分钟回测
def handle_data(context, data):
    if g.is_market_stop_loss_by_price:
        if market_stop_loss_by_price(context, g.index_4_stop_loss_by_price):
            return

    if g.is_market_stop_loss_by_3_black_crows:
        if market_stop_loss_by_3_black_crows(context, g.index_4_stop_loss_by_3_black_crows, g.dst_drop_minute_count):
            return

    if g.is_stock_stop_loss:
        stock_stop_loss(context, data)

    if g.is_stock_stop_profit:
        stock_stop_profit(context, data)

    # 获得当前时间
    hour = context.current_dt.hour
    minute = context.current_dt.minute
    
    # 每天下午14:50调仓
    if hour == g.adjust_position_hour and minute == g.adjust_position_minute:
        do_handle_data(context, data)

def do_handle_data(context, data):
    log.info("调仓日计数 [%d]" %(g.day_count))
    
    # 回看指数前20天的涨幅
    gr_index2 = get_growth_rate(g.index2)
    gr_index8 = get_growth_rate(g.index8)
    log.info("当前%s指数的20日涨幅 [%.2f%%]" %(get_security_info(g.index2).display_name, gr_index2*100))
    log.info("当前%s指数的20日涨幅 [%.2f%%]" %(get_security_info(g.index8).display_name, gr_index8*100))

    if gr_index2 <= g.index_growth_rate_20 and gr_index8 <= g.index_growth_rate_20:
        clear_position(context)
        g.day_count = 0
    else: #if  gr_index2 > g.index_growth_rate_20 or ret_index8 > g.index_growth_rate_20:
        if g.day_count % g.period == 0:
            log.info("==> 满足条件进行调仓")
            buy_stocks = pick_stocks(context, data)
            log.info("选股后可买股票: %s" %(buy_stocks))
            adjust_position(context, buy_stocks)
        g.day_count += 1

def market_stop_loss_by_price(context, index):
    # 大盘指数前130日内最高价超过最低价2倍,则清仓止损
    # 基于历史数据判定,因此若状态满足,则当天都不会变化
    # 增加此止损,回撤降低,收益降低

    if not g.is_day_stop_loss_by_price:
        h = attribute_history(index, 160, unit='1d', fields=('close', 'high', 'low'), skip_paused=True)
        low_price_130 = h.low.min()
        high_price_130 = h.high.max()
        if high_price_130 > 2.2 * low_price_130 and h['close'][-1]<h['close'][-4]*1 and  h['close'][-1]> h['close'][-100]:
            # 当日第一次输出日志
            log.info("==> 大盘止损,%s指数前130日内最高价超过最低价2倍, 最高价: %f, 最低价: %f" %(get_security_info(index).display_name, high_price_130, low_price_130))
            g.is_day_stop_loss_by_price = True

    if g.is_day_stop_loss_by_price:
        clear_position(context)
        g.day_count = 0

    return g.is_day_stop_loss_by_price

def market_stop_loss_by_3_black_crows(context, index, n):
    # 前日三黑鸦,累计当日每分钟涨幅<0的分钟计数
    # 如果分钟计数超过一定值,则开始进行三黑鸦止损
    # 避免无效三黑鸦乱止损
    if g.is_last_day_3_black_crows:
        if get_growth_rate(index, 1) < 0:
            g.cur_drop_minute_count += 1

        if g.cur_drop_minute_count >= n:
            if g.cur_drop_minute_count == n:
                log.info("==> 超过三黑鸦止损开始")

            clear_position(context)
            g.day_count = 0
            return True

    return False

def is_3_black_crows(stock):
    # talib.CDL3BLACKCROWS

    # 三只乌鸦说明来自百度百科
    # 1. 连续出现三根阴线,每天的收盘价均低于上一日的收盘
    # 2. 三根阴线前一天的市场趋势应该为上涨
    # 3. 三根阴线必须为长的黑色实体,且长度应该大致相等
    # 4. 收盘价接近每日的最低价位
    # 5. 每日的开盘价都在上根K线的实体部分之内;
    # 6. 第一根阴线的实体部分,最好低于上日的最高价位
    #
    # 算法
    # 有效三只乌鸦描述众说纷纭,这里放宽条件,只考虑1和2
    # 根据前4日数据判断
    # 3根阴线跌幅超过4.5%

    h = attribute_history(stock, 4, '1d', ('close','open'), skip_paused=True, df=False)
    h_close = list(h['close'])
    h_open = list(h['open'])

    if len(h_close) < 4 or len(h_open) < 4:
        return False
 
    # 三根阴线
    if h_close[-4] > h_open[-4] 
        and (h_close[-1] < h_close[-2] and h_close[-2] < h_close[-3]) 
        and (h_close[-1] < h_open[-1] and h_close[-2]< h_open[-2] and h_close[-3] < h_open[-3]) 
        and h_close[-1] / h_close[-3] - 1 < -0.045 and get_current_data(stock)< h_close[-1]*0.995:
        return True
    return False
'''
 # 一阳三阴
    if h_close[-4] > h_open[-4] 
        and (h_close[-1] < h_open[-1] and h_close[-2]< h_open[-2] and h_close[-3] < h_open[-3]):
        #and (h_close[-1] < h_close[-2] and h_close[-2] < h_close[-3]) 
        #and h_close[-1] / h_close[-3] - 1 < -0.045:
        return True
    return False
'''  
'''
def is_3_black_crows(stock, data):
    # talib.CDL3BLACKCROWS
    his =  attribute_history(stock, 2, '1d', ('close','open'), skip_paused=True, df=False)
    closeArray = list(his['close'])
    closeArray.append(data[stock].close)
    openArray = list(his['open'])
    openArray.append(get_current_data()[stock].day_open)

    if closeArray[0]<openArray[0] and closeArray[1]<openArray[1] and closeArray[2]<openArray[2]:
        if closeArray[-1]/closeArray[0]-1>-0.045:
            his2 =  attribute_history(stock, 4, '1d', ('close','open'), skip_paused=True, df=False)
            closeArray1 = his2['close']
            if closeArray[0]/closeArray1[0]-1>0:
                return True
    return False
'''

# 个股止损
def stock_stop_loss(context, data):
    for stock in context.portfolio.positions.keys():
        cur_price = data[stock].close
        xi = attribute_history(stock, 2, '1d', 'high', skip_paused=True)
        ma = xi.max()
        if g.last_high[stock] < cur_price:
            g.last_high[stock] = cur_price
            
        threshold = get_stop_loss_threshold(stock, g.period)
        #log.debug("个股止损阈值, stock: %s, threshold: %f" %(stock, threshold))
        if cur_price < g.last_high[stock] * (1 - threshold):
            log.info("==> 个股止损, stock: %s, cur_price: %f, last_high: %f, threshold: %f" 
                %(stock, cur_price, g.last_high[stock], threshold))

            position = context.portfolio.positions[stock]
            if close_position(position):
                g.day_count = 0

# 个股止盈
def stock_stop_profit(context, data):
    for stock in context.portfolio.positions.keys():
        position = context.portfolio.positions[stock]
        cur_price = data[stock].close
        threshold = get_stop_profit_threshold(stock, g.period)
        #log.debug("个股止盈阈值, stock: %s, threshold: %f" %(stock, threshold))
        if cur_price > position.avg_cost * (1 + threshold):
            log.info("==> 个股止盈, stock: %s, cur_price: %f, avg_cost: %f, threshold: %f" 
                %(stock, cur_price, g.last_high[stock], threshold))

            position = context.portfolio.positions[stock]
            if close_position(position):
                g.day_count = 0

# 获取个股前n天的m日增幅值序列
# 增加缓存避免当日多次获取数据
def get_pct_change(security, n, m):
    pct_change = None
    if security in g.pct_change.keys():
        pct_change = g.pct_change[security]
    else:
        h = attribute_history(security, n, unit='1d', fields=('close'), skip_paused=True)
        pct_change = h['close'].pct_change(m) # 3日的百分比变比(即3日涨跌幅)
        g.pct_change[security] = pct_change
    return pct_change
        
# 计算个股回撤止损阈值
# 即个股在持仓n天内能承受的最大跌幅
# 算法:(个股250天内最大的n日跌幅 + 个股250天内平均的n日跌幅)/2
# 返回正值
def get_stop_loss_threshold(security, n = 3):
    pct_change = get_pct_change(security, 250, n)
    #log.debug("pct of security [%s]: %s", pct)
    maxd = pct_change.min()
    #maxd = pct[pct<0].min()
    avgd = pct_change.mean()
    #avgd = pct[pct<0].mean()
    # maxd和avgd可能为正,表示这段时间内一直在增长,比如新股
    bstd = (maxd + avgd) / 2

    # 数据不足时,计算的bstd为nan
    if not isnan(bstd):
        if bstd != 0:
            return abs(bstd)
        else:
            # bstd = 0,则 maxd <= 0
            if maxd < 0:
                # 此时取最大跌幅
                return abs(maxd)

    return 0.099 # 默认配置回测止损阈值最大跌幅为-9.9%,阈值高貌似回撤降低

# 计算个股止盈阈值
# 算法:个股250天内最大的n日涨幅
# 返回正值
def get_stop_profit_threshold(security, n = 3):
    pct_change = get_pct_change(security, 250, n)
    maxr = pct_change.max()
    
    # 数据不足时,计算的maxr为nan
    # 理论上maxr可能为负
    if (not isnan(maxr)) and maxr != 0:
        return abs(maxr)
    return 0.30 # 默认配置止盈阈值最大涨幅为20%

# 获取股票n日以来涨幅,根据当前价计算
# n 默认20日
def get_growth_rate(security, n=20):
    lc = get_close_price(security, n)
    #c = data[security].close
    c = get_close_price(security, 1, '1m')
    
    if not isnan(lc) and not isnan(c) and lc != 0:
        return (c - lc) / lc
    else:
        log.error("数据非法, security: %s, %d日收盘价: %f, 当前价: %f" %(security, n, lc, c))
        return 0

# 获取前n个单位时间当时的收盘价
def get_close_price(security, n, unit='1d'):
    return attribute_history(security, n, unit, ('close'), True)['close'][0]

# 开仓,买入指定价值的证券
# 报单成功并成交(包括全部成交或部分成交,此时成交量大于0),返回True
# 报单失败或者报单成功但被取消(此时成交量等于0),返回False
def open_position(security, value):
    order = order_target_value_(security, value)
    if order != None and order.filled > 0:
        # 报单成功并有成交则初始化最高价
        cur_price = get_close_price(security, 1, '1m')
        g.last_high[security] = cur_price
        return True
    return False

# 平仓,卖出指定持仓
# 平仓成功并全部成交,返回True
# 报单失败或者报单成功但被取消(此时成交量等于0),或者报单非全部成交,返回False
def close_position(position):
    security = position.security
    order = order_target_value_(security, 0) # 可能会因停牌失败
    if order != None:
        if order.filled > 0:
            # 只要有成交,无论全部成交还是部分成交,则统计盈亏
            g.trade_stat.watch(security, order.filled, position.avg_cost, position.price)

        if order.status == OrderStatus.held and order.filled == order.amount:
            # 全部成交则删除相关证券的最高价缓存
            if security in g.last_high:
                g.last_high.pop(security)
            else:
                log.warn("last high price of %s not found" %(security))
            return True

    return False

# 清空卖出所有持仓
def clear_position(context):
    if context.portfolio.positions:
        log.info("==> 清仓,卖出所有股票")
        for stock in context.portfolio.positions.keys():
            position = context.portfolio.positions[stock]
            close_position(position)

# 自定义下单
# 根据Joinquant文档,当前报单函数都是阻塞执行,报单函数(如order_target_value)返回即表示报单完成
# 报单成功返回报单(不代表一定会成交),否则返回None
def order_target_value_(security, value):
    if value == 0:
        log.debug("Selling out %s" % (security))
    else:
        log.debug("Order %s to value %f" % (security, value))
        
    # 如果股票停牌,创建报单会失败,order_target_value 返回None
    # 如果股票涨跌停,创建报单会成功,order_target_value 返回Order,但是报单会取消
    # 部成部撤的报单,聚宽状态是已撤,此时成交量>0,可通过成交量判断是否有成交
    return order_target_value(security, value)


# 过滤停牌股票
def filter_paused_stock(stock_list):
    current_data = get_current_data()
    return [stock for stock in stock_list if not current_data[stock].paused]

# 过滤ST及其他具有退市标签的股票
def filter_st_stock(stock_list):
    current_data = get_current_data()
    return [stock for stock in stock_list 
        if 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 filter_limitup_stock(context, stock_list):
    last_prices = history(1, unit='1m', field='close', security_list=stock_list)
    current_data = get_current_data()
    
    # 已存在于持仓的股票即使涨停也不过滤,避免此股票再次可买,但因被过滤而导致选择别的股票
    return [stock for stock in stock_list if stock in context.portfolio.positions.keys() 
        or last_prices[stock][-1] < current_data[stock].high_limit]
    #return [stock for stock in stock_list if stock in context.portfolio.positions.keys() 
    #    or last_prices[stock][-1] < current_data[stock].high_limit * 0.995]

# 过滤跌停的股票
def filter_limitdown_stock(context, stock_list):
    last_prices = history(1, unit='1m', field='close', security_list=stock_list)
    current_data = get_current_data()
    
    return [stock for stock in stock_list if stock in context.portfolio.positions.keys() 
        or last_prices[stock][-1] > current_data[stock].low_limit]
    #return [stock for stock in stock_list if last_prices[stock][-1] > current_data[stock].low_limit]
    #return [stock for stock in stock_list if stock in context.portfolio.positions.keys() 
    #    or last_prices[stock][-1] > current_data[stock].low_limit * 1.005]
    
# 过滤黑名单股票
def filter_blacklist_stock(context, stock_list):
    blacklist = get_blacklist()
    return [stock for stock in stock_list if stock not in blacklist]

# 过滤创业版股票
def filter_gem_stock(context, stock_list):
    return [stock for stock in stock_list if stock[0:3] != '300']

# 过滤20日增长率为负的股票
def filter_by_growth_rate(stock_list, n):
    return [stock for stock in stock_list if get_growth_rate(stock, n) > 0]

# 股票评分
def rank_stocks(data, stock_list):
    dst_stocks = {}
    for stock in stock_list:
        h = attribute_history(stock, 130, unit='1d', fields=('close', 'high', 'low'), skip_paused=True)
        low_price_130 = h.low.min()
        high_price_130 = h.high.max()

        avg_15 = data[stock].mavg(15, field='close')
        cur_price = data[stock].close

        #avg_15 = h['close'][-15:].mean()
        #cur_price = get_close_price(stock, 1, '1m')

        score = (cur_price-low_price_130) + (cur_price-high_price_130) + (cur_price-avg_15)
        #score = ((cur_price-low_price_130) + (cur_price-high_price_130) + (cur_price-avg_15)) / cur_price
        dst_stocks[stock] = score
        
    df = pd.DataFrame(dst_stocks.values(), index=dst_stocks.keys())
    df.columns = ['score']
    df = df.sort(columns='score', ascending=True)
    return df.index

'''
# 过滤新股
def filter_new_stock(stock_list):
    stocks = get_all_securities(['stock'])
    stocks = stocks[(context.current_dt.date() - stocks.start_date) > datetime.timedelta(60)].index
'''

# 选股
# 选取指定数目的小市值股票,再进行过滤,最终挑选指定可买数目的股票
def pick_stocks(context, data):
    q = None
    if g.pick_by_pe:
        if g.pick_by_eps:
            q = query(valuation.code).filter(
                indicator.eps > g.min_eps,
                valuation.pe_ratio > g.min_pe,
                valuation.pe_ratio < g.max_pe
            ).order_by(
                valuation.market_cap.asc()
            ).limit(
                g.pick_stock_count
            )
        else:
            q = query(valuation.code).filter(
                valuation.pe_ratio > g.min_pe,
                valuation.pe_ratio < g.max_pe
            ).order_by(
                valuation.market_cap.asc()
            ).limit(
                g.pick_stock_count
            )
    else:
        if g.pick_by_eps:
            q = query(valuation.code).filter(
                indicator.eps > g.min_eps
            ).order_by(
                valuation.market_cap.asc()
            ).limit(
                g.pick_stock_count
            )
        else:
            q = query(valuation.code).order_by(
                valuation.market_cap.asc()
            ).limit(
                g.pick_stock_count
            )
    
    df = get_fundamentals(q)
    stock_list = list(df['code'])

    if g.filter_gem:
        stock_list = filter_gem_stock(context, stock_list)
        
    if g.filter_blacklist:
        stock_list = filter_blacklist_stock(context, stock_list)
        
    stock_list = filter_paused_stock(stock_list)
    stock_list = filter_st_stock(stock_list)
    stock_list = filter_limitup_stock(context, stock_list)
    stock_list = filter_limitdown_stock(context, stock_list)

    # 根据20日股票涨幅过滤效果不好,故注释
    #stock_list = filter_by_growth_rate(stock_list, 15)
    
    if g.is_rank_stock:
        if len(stock_list) > g.rank_stock_count:
            stock_list = stock_list[:g.rank_stock_count]

        #log.debug("评分前备选股票: %s" %(stock_list))
        if len(stock_list) > 0:
            stock_list = rank_stocks(data, stock_list)
        #log.debug("评分后备选股票: %s" %(stock_list))
    
    # 选取指定可买数目的股票
    if len(stock_list) > g.buy_stock_count:
        stock_list = stock_list[:g.buy_stock_count]
    return stock_list

# 根据待买股票创建或调整仓位
# 对于因停牌等原因没有卖出的股票则继续持有
# 始终保持持仓数目为g.buy_stock_count
def adjust_position(context, buy_stocks):
    for stock in context.portfolio.positions.keys():
        if stock not in buy_stocks:
            log.info("stock [%s] in position is not buyable" %(stock))
            position = context.portfolio.positions[stock]
            close_position(position)
        else:
            log.info("stock [%s] is already in position" %(stock))
    
    # 根据股票数量分仓
    # 此处只根据可用金额平均分配购买,不能保证每个仓位平均分配
    position_count = len(context.portfolio.positions)
    if g.buy_stock_count > position_count:
        value = context.portfolio.cash / (g.buy_stock_count - position_count)

        for stock in buy_stocks:
            if context.portfolio.positions[stock].total_amount == 0:
                if open_position(stock, value):
                    if len(context.portfolio.positions) == g.buy_stock_count:
                        break
原文地址:https://www.cnblogs.com/xiaohengheng/p/7686376.html