老钱庄股票论坛

 找回密码
 5秒快速注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

关闭
关闭
   扫一扫关注老钱庄微信号 关闭
扫一扫关注老钱庄财经微信号 关闭
老钱庄股票论坛 门户 查看主题

用了这个策略,你能赚个盆满钵满!!(源码、收益图)

发布者: Amber-investor | 发布时间: 2018-2-9 13:37| 查看数: 923| 评论数: 2|帖子模式

最近总有人问我一个问题,你的策略真的能赚钱吗?
呵呵
对于这种人,我只想说,虽然你有一颗想赚钱的心,但却没有一个能赚钱的命
....
现在据我入行那时起也有七八年,我也算是见证中国量化行业垒砌起半壁江山的老人了
什么是策略?
就算你现在问我,我也无法给你一个确切的回答。通俗来说,能赚钱,就可以。
所以江湖上流行有很多很奇葩的策略:比如朋友圈选股PM2.5选股、算卦选股……
今天和大家分享一个羊驼策略(你没看错,就是草泥马),听上去很玄吧?然而它真的有用:
羊驼策略
来源于甘肃卫视的《马上知道》节目,节目2014年向观众展示了用羊驼来选股,即每天卖掉持有的股票中收益率最差的一只,然后让羊驼随机选入一只股票来买,结果收货颇丰。
策略实现
1、设置参数,包括股票池,调仓周期 holdingPeriod (代码中为tc),收益率计算周期 holdingPeriod (代码中为N),每次持仓股票数目为 num_stocks ,每次换仓换股数量 change_No等。
2、 计算股票池和持仓中所有股票的上一周期的收益率:
收益率=昨天的收盘价−(returnPeriod+1)天前的收盘价(returnPeriod+1)天前的收盘价
3、将可行股票池内的股票按照上一周期(returnPeriod)收益率排序。将目前持仓股票按照上一周期(holdingPeriod)收益率排序。
4、卖出当前持仓中收益率最低的 change_No 支股票,卖出股票得到的现金和原来库存现金等金额买入回测期收益排名最差的 change_No 支股票。如果持仓中的某支股票在持仓中排在收益率最差 change_No 中,同时又在所有可行股票池最差收益率排名 change_No 中,则仅调仓不换股。
5、每 holdingPeriod 天进行 Rebalance ,调整持仓中股票到手中所有股票市值/ num_stocks 。
交易标的:A股
调仓周期:1天
回测时间:2014.01.01~ 2018.01.01
回测时长:4年
下面是我得到的回测分析报告包括业绩分析、收益归因、资产配置等,都是平台原图~
下面是我的股票分析,包括收益曲线、行业偏差、风险敞口等。
收益曲线
总结:
这次做的羊驼策略整体效果算是比较好的,回测时期为2014年1月1号至2018年1月1号,年化收益41.4%,超额收益88.8%,最大回撤-47%,夏普比率0.674, 波动率41.10%, 胜率71%, 用这个策略来进行选股真的是完爆其他啊!!!!
ps:之前有人问我关于使用的平台的问题,因为目前公司签有保密协议,所以我用的不是我们公司内部的系统。关于回测平台我个人觉得就跟穿衣服一样,哪个用起来舒服用哪个。我现在主要是考虑到隐私信息泄露的问题,用的是一个叫Quant desk平台,不用担心策略泄露了。
别的做想下载的童鞋我给你们一个下载链接吧,不用再私信我了http://www.yunkuanke.com/#/download
有问题的朋友也可以多多留言呀,我们一起交流交流~下次准备做个海龟策略的分享,小伙伴们有什么建议都可以告诉我哦~
给你们的源码
from CloudQuant import SDKCoreEngine
from CloudQuant import AssetType
from CloudQuant import QuoteCycle

import numpy as np
import time     #使用时间戳
import pdb
import pandas as pd
import math

np.seterr(invalid='ignore')

config = {
    'username': 'XX',  # 用户名
    'password': 'XX',  # 密码
    'rootpath': 'C:\cStrategy', # 客户端所在路径
    'assetType': AssetType.Stock,
    'initCapitalStock': 10000000,  # 初始资金
    'startDate': 20140101,      # 交易开始日期
    'endDate': 20180101,        # 交易结束日期
    'cycle': QuoteCycle.D,      # 回测粒度为日线
    'strategyName': 'yangtuo',    # 策略名
    'stockFeeRate': 0.0015,      # 手续费率
    'logfile': ' yangtuo.log',     # 在策略中调用log方法所生成的日志文件名
    'strategyID': '123456',
    'dealByVolume': True,
    'allowForTodayFactors': ['LZ_CN_STKA_SLCIND_STOP_FLAG',  # 停牌因子
                             'LZ_CN_STKA_SLCIND_ST_FLAG',  # st因子
                             'LZ_CN_STKA_SLCIND_TRADEDAYCOUNT',  # 新股上市天数
                             ' LZ_CN_STKA_CMFTR_CUM_FACTOR ',  # 复权因子
                             'LZ_CN_STKA_QUOTE_TOPEN',  # 开盘价
                             'LZ_CN_STKA_INDEX_CSI500WEIGHT ',  # 中证500成分股权重
                             ' LZ_CN_STKA_INDEX_HS300WEIGHT ']
}


# 选择市值在10亿以下,价格在40以下 正在交易的股票
# 每日随机买入m支
# 卖出持仓列表中收益率最低n支
# 保证持仓总数为固定值


# config = {
#     'username': 'tip',
#     'password': 'test123',
#     'rootpath': 'd:/cStrategy/',  # 客户端所在路径
#     'assetType': ['stock'],
#     'initCapitalStock': INIT_CAP,
#     'startDate': START_DATE,
#     'endDate': END_DATE,
#     'cycle': 1,  # 回放粒度为分钟线
#     'executeMode': 'D',
#     'feeRate': 0.001,
#     'feeLimit': 5,
#     'strategyName': '羊驼日线',  # 策略名
#     'logfile': 'lamaday.log',
#     'dealByVolume': False,
# }

POSITION_NUM = 30  # 总持有股票支数


def initial(sdk):
    #sdk.prepareData(['LZ_CN_VAL_A_TCAP', 'LZ_CN_QUOTE_TCLOSE'])
    pass

def initPerDay(sdk):
    cap = np.array(sdk.getFactorData('LZ_CN_STKA_VAL_A_TCAP')[-1])  # 读入最近市值
    cps = np.array(sdk.getFactorData('LZ_CN_STKA_QUOTE_TCLOSE')[-1])  # 读入最近收盘价
    # 设定选股池
    conditions = [cap < (10 ** (9 - 4)),  # 市值小于 10亿
        cps != 0,  # 收盘价不为0
        cps < 40]  # 收盘价小于40
    condition = reduce(lambda a, b: np.logical_and(a, b), conditions)
    # 读取持仓
    positions = sdk.getPositions()
    positionStocks = [i.code for i in positions]

    # 订阅所有在选股池或已持仓的股票行情
    stockPool = list(set(np.array(sdk.getStockList())[condition]) | set(positionStocks))
    #sdk.subscribeQuote(stockPool)
    sdk.setGlobal('POOL', stockPool)

    # 设置已购入的股票列表 已购入股票本日内的不用再次购入
    sdk.setGlobal('BOUGHT_STOCK', [])
    # 保存收益率
    sdk.setGlobal('RET', {stk: 1.0 for stk in positionStocks})


# 购买股票函数 参数为 购买股票支数 选股条件函数
def buyStocks(sdk, num, quotes):
    stockPool = sdk.getGlobal('POOL')  # 读取符合条件的股票列表
    quoteStocks = quotes.keys()
    boughtStock = sdk.getGlobal('BOUGHT_STOCK')
    # 如果存在已买过的股票则重新选择
    stockPool = list(set(stockPool) and set(quoteStocks) - set(boughtStock))
    # 调用选股函数选股
    stockToBuy = randomStocks(stockPool, num)
    # 剩余现金作为购买预算 各支股票平均分配预算
    budget = sdk.getAccountInfo().availableCash * 0.1 / POSITION_NUM
    sdk.sdklog(budget, 'budget')
    sdk.sdklog(stockToBuy, 'buy')
    orders = []
    for buyStock in stockToBuy:
        buyPrice = quotes[buyStock].high  # 购买价格为当前价
        buyAmount = int(budget / (buyPrice * 100)) * 100  # 预算除购买价格作为购入量
        if buyPrice > 0 and buyAmount > 0:
            orders.append([buyStock, buyPrice, buyAmount, 'BUY'])  # 委托购买
            boughtStock.append(buyStock)
    if(orders):
        sdk.makeOrders(orders)

    sdk.sdklog(orders, 'buy order')  # 将购买计入日志
    sdk.setGlobal('BOUGHT_STOCK', boughtStock)


# 随机选股函数 返回选择的股票列表
def randomStocks(stocklist, num):
    if len(stocklist) < num:
        return stocklist
    else:
        return np.random.choice(stocklist, num, False).tolist()  # 从中随机选择要购买的股票


# 卖出股票函数
def sellStocks(sdk, num, quotes):
    positions = np.array(sdk.getPositions())  # 查持仓
    ret = sdk.getGlobal('RET')
    quoteStocks = quotes.keys()

    # 滤除取不到行情的股票
    positionStocksFiltered = [i for i in ret.items() if i[0] in quoteStocks]
    # 用最新成交价和昨日收盘价计算收益率
    rateOfReturnSortedIndex = sorted(positionStocksFiltered, key=lambda x: x[1])  # 按收益排序
    stockToSell = [i[0] for i in rateOfReturnSortedIndex[:num]]  # 选收益最低n支

    # 卖出
    orders = []
    for pos in positions:
        if pos.code in stockToSell:
            sellPrice = quotes[pos.code].low  # 设置出售价格为当前价
            sellAmount = pos.optPosition
            if sellPrice > 0 and sellAmount > 0:
                orders.append([pos.code, sellPrice, sellAmount, 'SELL'])  # 委托出售
            # 将已出售的股票标记
            ret.pop(pos.code)
    sdk.makeOrders(orders)
    sdk.setGlobal('RET', ret)
    sdk.sdklog(orders, 'sell')  # 将出售记入日志


def strategy(sdk):
    # 读取行情
    sdk.sdklog(len(sdk.getGlobal('POOL')), 'quote')
    quotes = sdk.getQuotes(sdk.getGlobal('POOL'))
    positionList = sdk.getPositions()  # 获取持仓列表

    # 计算所有持仓的收益
    ret = sdk.getGlobal('RET')
    for pos in sdk.getPositions():
        if pos.code in quotes and quotes[pos.code].volume > 0:
            ret[pos.code] = quotes[pos.code].low / (pos.totalCost / pos.totalPosition)
        else:
            ret[pos.code] = 1
    sdk.setGlobal('RET', ret)
    sdk.sdklog(ret, 'ret')

    # 如果持仓数大于计划持仓数
    if len(positionList) >= POSITION_NUM:
        # 卖出持仓中最低收益率的股票
        sellStocks(sdk, len(positionList) - POSITION_NUM + 1, quotes)

    # 当实际持仓数不足计划的持仓数时
    if len(positionList) < POSITION_NUM:
        # 用随机选股买入
        buyStocks(sdk, POSITION_NUM - len(positionList), quotes)



def main():
    config['initial'] = initial
    config['strategy'] = strategy
    config['preparePerDay'] = initPerDay
    t0 = time.time()
    SDKCoreEngine(**config).run()
    t1 = time.time()
    print "start from", time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(t0)), ", end in", time.strftime(
        '%Y-%m-%d %H:%M:%S', time.localtime(t1)), ". total time took", t1 - t0, " seconds"


if __name__ == "__main__":
    main()


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?5秒快速注册

x

最新评论

Amber-investor 发表于 2018-2-24 13:59
本帖最后由 Amber-investor 于 2018-2-24 15:03 编辑

想下载回测平台的朋友们不要再私信我了哈,给你们链接:回测平台
润才 发表于 2018-3-3 11:05
关于老钱庄 | 广告合作 | 商务合作 | 意见反馈 | 免责声明 | 友情链接
    经营性网站备案信息 安徽黄埔网络科
技有限公司 安徽大时代投
资咨询有限公司 可信网站 网上交易保障中心

信息产业部信息备案:闽ICP备13021446号-4 茂名市公安局网警备案:4409023010511号
老钱庄股票论坛网友发表的帖子纯属个人意见,老钱庄股票论坛不负任何责任! 股市有风险,投资需谨慎!
共同建设网络精神文明,欢迎广大网友举报论坛上的不良信息,我们会在第一时间内及时处理!

[ 广告服务咨询QQ:2806911298 ] [ 合作联系电话 :15396270234 联系QQ : 800025923 ]

值班QQ: 800025923 值班电话:0592-5962326

小黑屋|手机版|Archiver|老钱庄

GMT+8, 2018-5-23 07:23 , Processed in 0.113780 second(s), 24 queries .

Powered by Discuz! X3.3

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表