经典的股票/期货量化交易策略,拿来即用的Python策略源码(1)

声明:本文策略源码均来自掘金量化示例策略库,仅供参考!

一、股票策略


1.多因子选股

# coding=utf-8

from__future__importprint_function, absolute_import, unicode_literals

importnumpyasnp

fromgm.apiimport*

frompandasimportDataFrame

'''

本策略每隔1个月定时触发,根据Fama-French三因子模型对每只股票进行回归,得到其alpha值。

假设Fama-French三因子模型可以完全解释市场,则alpha为负表明市场低估该股,因此应该买入。

策略思路:

计算市场收益率、个股的账面市值比和市值,并对后两个进行了分类,

根据分类得到的组合分别计算其市值加权收益率、SMB和HML.

对各个股票进行回归(假设无风险收益率等于0)得到alpha值.

选取alpha值小于0并为最小的10只股票进入标的池

平掉不在标的池的股票并等权买入在标的池的股票

回测数据:SHSE.000300的成份股

回测时间:2017-07-01 08:00:00到2017-10-01 16:00:00

'''

definit(context):

# 每月第一个交易日的09:40 定时执行algo任务

schedule(schedule_func=algo, date_rule='1m', time_rule='09:40:00')

print(order_target_percent(symbol='SHSE.600000', percent=0.5, order_type=OrderType_Market,

                        position_side=PositionSide_Long))

# 数据滑窗

context.date =20

# 设置开仓的最大资金量

context.ratio =0.8

# 账面市值比的大/中/小分类

context.BM_BIG =3.0

context.BM_MID =2.0

context.BM_SMA =1.0

# 市值大/小分类

context.MV_BIG =2.0

context.MV_SMA =1.0

# 计算市值加权的收益率,MV为市值的分类,BM为账目市值比的分类

defmarket_value_weighted(stocks, MV, BM):

    select = stocks[(stocks.NEGOTIABLEMV == MV) & (stocks.BM == BM)]

market_value = select['mv'].values

    mv_total = np.sum(market_value)

mv_weighted = [mv / mv_totalformvinmarket_value]

stock_return = select['return'].values

# 返回市值加权的收益率的和

    return_total = []

foriinrange(len(mv_weighted)):

        return_total.append(mv_weighted[i] * stock_return[i])

    return_total = np.sum(return_total)

returnreturn_total

defalgo(context):

# 获取上一个交易日的日期

last_day = get_previous_trading_date(exchange='SHSE', date=context.now)

# 获取沪深300成份股

context.stock300 = get_history_constituents(index='SHSE.000300', start_date=last_day,

end_date=last_day)[0]['constituents'].keys()

# 获取当天有交易的股票

    not_suspended = get_history_instruments(symbols=context.stock300, start_date=last_day, end_date=last_day)

not_suspended = [item['symbol']foriteminnot_suspendedifnotitem['is_suspended']]

fin = get_fundamentals(table='tq_sk_finindic', symbols=not_suspended, start_date=last_day, end_date=last_day,

fields='PB,NEGOTIABLEMV', df=True)

# 计算账面市值比,为P/B的倒数

fin['PB'] = (fin['PB'] **-1)

# 计算市值的50%的分位点,用于后面的分类

size_gate = fin['NEGOTIABLEMV'].quantile(0.50)

# 计算账面市值比的30%和70%分位点,用于后面的分类

bm_gate = [fin['PB'].quantile(0.30), fin['PB'].quantile(0.70)]

    fin.index = fin.symbol

    x_return = []

# 对未停牌的股票进行处理

forsymbolinnot_suspended:

# 计算收益率

close = history_n(symbol=symbol, frequency='1d', count=context.date +1, end_time=last_day, fields='close',

skip_suspended=True, fill_missing='Last', adjust=ADJUST_PREV, df=True)['close'].values

stock_return = close[-1] / close[0] -1

pb = fin['PB'][symbol]

market_value = fin['NEGOTIABLEMV'][symbol]

# 获取[股票代码. 股票收益率, 账面市值比的分类, 市值的分类, 流通市值]

ifpb < bm_gate[0]:

ifmarket_value < size_gate:

                label = [symbol, stock_return, context.BM_SMA, context.MV_SMA, market_value]

else:

                label = [symbol, stock_return, context.BM_SMA, context.MV_BIG, market_value]

elifpb < bm_gate[1]:

ifmarket_value < size_gate:

                label = [symbol, stock_return, context.BM_MID, context.MV_SMA, market_value]

else:

                label = [symbol, stock_return, context.BM_MID, context.MV_BIG, market_value]

elifmarket_value < size_gate:

            label = [symbol, stock_return, context.BM_BIG, context.MV_SMA, market_value]

else:

            label = [symbol, stock_return, context.BM_BIG, context.MV_BIG, market_value]

iflen(x_return) ==0:

            x_return = label

else:

            x_return = np.vstack([x_return, label])

stocks = DataFrame(data=x_return, columns=['symbol','return','BM','NEGOTIABLEMV','mv'])

    stocks.index = stocks.symbol

columns = ['return','BM','NEGOTIABLEMV','mv']

forcolumnincolumns:

        stocks[column] = stocks[column].astype(np.float64)

# 计算SMB.HML和市场收益率

# 获取小市值组合的市值加权组合收益率

    smb_s = (market_value_weighted(stocks, context.MV_SMA, context.BM_SMA) +

            market_value_weighted(stocks, context.MV_SMA, context.BM_MID) +

market_value_weighted(stocks, context.MV_SMA, context.BM_BIG)) /3

# 获取大市值组合的市值加权组合收益率

    smb_b = (market_value_weighted(stocks, context.MV_BIG, context.BM_SMA) +

            market_value_weighted(stocks, context.MV_BIG, context.BM_MID) +

market_value_weighted(stocks, context.MV_BIG, context.BM_BIG)) /3

    smb = smb_s - smb_b

# 获取大账面市值比组合的市值加权组合收益率

hml_b = (market_value_weighted(stocks, context.MV_SMA,3) +

market_value_weighted(stocks, context.MV_BIG, context.BM_BIG)) /2

# 获取小账面市值比组合的市值加权组合收益率

    hml_s = (market_value_weighted(stocks, context.MV_SMA, context.BM_SMA) +

market_value_weighted(stocks, context.MV_BIG, context.BM_SMA)) /2

    hml = hml_b - hml_s

close = history_n(symbol='SHSE.000300', frequency='1d', count=context.date +1,

end_time=last_day, fields='close', skip_suspended=True,

fill_missing='Last', adjust=ADJUST_PREV, df=True)['close'].values

market_return = close[-1] / close[0] -1

    coff_pool = []

# 对每只股票进行回归获取其alpha值

forstockinstocks.index:

x_value = np.array([[market_return], [smb], [hml], [1.0]])

y_value = np.array([stocks['return'][stock]])

# OLS估计系数

coff = np.linalg.lstsq(x_value.T, y_value)[0][3]

        coff_pool.append(coff)

# 获取alpha最小并且小于0的10只的股票进行操作(若少于10只则全部买入)

stocks['alpha'] = coff_pool

stocks = stocks[stocks.alpha <0].sort_values(by='alpha').head(10)

    symbols_pool = stocks.index.tolist()

    positions = context.account().positions()

# 平不在标的池的股票

forpositioninpositions:

symbol = position['symbol']

ifsymbolnotinsymbols_pool:

order_target_percent(symbol=symbol, percent=0, order_type=OrderType_Market,

                                position_side=PositionSide_Long)

print('市价单平不在标的池的', symbol)

# 获取股票的权重

    percent = context.ratio / len(symbols_pool)

# 买在标的池中的股票

forsymbolinsymbols_pool:

        order_target_percent(symbol=symbol, percent=percent, order_type=OrderType_Market,

                            position_side=PositionSide_Long)

print(symbol,'以市价单调多仓到仓位', percent)

if__name__ =='__main__':

'''

    strategy_id策略ID,由系统生成

    filename文件名,请与本文件名保持一致

    mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST

    token绑定计算机的ID,可在系统设置-密钥管理中生成

    backtest_start_time回测开始时间

    backtest_end_time回测结束时间

    backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST

    backtest_initial_cash回测初始资金

    backtest_commission_ratio回测佣金比例

    backtest_slippage_ratio回测滑点比例

    '''

run(strategy_id='strategy_id',

filename='main.py',

        mode=MODE_BACKTEST,

token='token_id',

backtest_start_time='2017-07-01 08:00:00',

backtest_end_time='2017-10-01 16:00:00',

        backtest_adjust=ADJUST_PREV,

backtest_initial_cash=10000000,

backtest_commission_ratio=0.0001,

backtest_slippage_ratio=0.0001)

2.日内回转交易

# coding=utf-8

from__future__importprint_function, absolute_import, unicode_literals

try:

importtalib

except:

print('请安装TA-Lib库')

fromgm.apiimport*

'''

本策略首先买入SHSE.600000股票10000股

随后根据60s的数据来计算MACD(12,26,9)线,并在MACD>0的时候买入100股,MACD<0的时候卖出100股

但每日操作的股票数不超过原有仓位,并于收盘前把仓位调整至开盘前的仓位

回测数据为:SHSE.600000的60s数据

回测时间为:2017-09-01 08:00:00到2017-10-01 16:00:00

'''

definit(context):

# 设置标的股票

context.symbol ='SHSE.600000'

# 用于判定第一个仓位是否成功开仓

context.first =0

# 订阅浦发银行, bar频率为1min

subscribe(symbols=context.symbol, frequency='60s', count=35)

# 日内回转每次交易100股

context.trade_n =100

# 获取昨今天的时间

context.day = [0,0]

# 用于判断是否触发了回转逻辑的计时

context.ending =0

defon_bar(context, bars):

bar = bars[0]

ifcontext.first ==0:

# 最开始配置仓位

# 需要保持的总仓位

context.total =10000

# 购买10000股浦发银行股票

        order_volume(symbol=context.symbol, volume=context.total, side=PositionSide_Long,

                    order_type=OrderType_Market, position_effect=PositionEffect_Open)

print(context.symbol,'以市价单开多仓10000股')

context.first =1.

day = bar.bob.strftime('%Y-%m-%d')

context.day[-1] = day[-2:]

# 每天的仓位操作

context.turnaround = [0,0]

return

# 更新最新的日期

day = bar.bob.strftime('%Y-%m-%d %H:%M:%S')

context.day[0] = bar.bob.day

# 若为新的一天,获取可用于回转的昨仓

ifcontext.day[0] != context.day[-1]:

context.ending =0

context.turnaround = [0,0]

ifcontext.ending ==1:

return

# 若有可用的昨仓则操作

ifcontext.total >=0:

# 获取时间序列数据

symbol = bar['symbol']

recent_data = context.data(symbol=symbol, frequency='60s', count=35, fields='close')

# 计算MACD线

macd = talib.MACD(recent_data['close'].values)[0][-1]

# 根据MACD>0则开仓,小于0则平仓

ifmacd >0:

# 多空单向操作都不能超过昨仓位,否则最后无法调回原仓位

ifcontext.turnaround[0] + context.trade_n < context.total:

# 计算累计仓位

context.turnaround[0] += context.trade_n

                order_volume(symbol=context.symbol, volume=context.trade_n, side=PositionSide_Long,

                            order_type=OrderType_Market, position_effect=PositionEffect_Open)

print(symbol,'市价单开多仓', context.trade_n,'股')

elifmacd <0:

ifcontext.turnaround[1] + context.trade_n < context.total:

context.turnaround[1] += context.trade_n

                order_volume(symbol=context.symbol, volume=context.trade_n, side=PositionSide_Short,

                            order_type=OrderType_Market, position_effect=PositionEffect_Close)

print(symbol,'市价单平多仓', context.trade_n,'股')

# 临近收盘时若仓位数不等于昨仓则回转所有仓位

ifday[11:16] =='14:55'orday[11:16] =='14:57':

            position = context.account().position(symbol=context.symbol, side=PositionSide_Long)

ifposition['volume'] != context.total:

                order_target_volume(symbol=context.symbol, volume=context.total, order_type=OrderType_Market,

                                    position_side=PositionSide_Long)

print('市价单回转仓位操作...')

context.ending =1

# 更新过去的日期数据

context.day[-1] = context.day[0]

if__name__ =='__main__':

'''

    strategy_id策略ID,由系统生成

    filename文件名,请与本文件名保持一致

    mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST

    token绑定计算机的ID,可在系统设置-密钥管理中生成

    backtest_start_time回测开始时间

    backtest_end_time回测结束时间

    backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST

    backtest_initial_cash回测初始资金

    backtest_commission_ratio回测佣金比例

    backtest_slippage_ratio回测滑点比例

    '''

run(strategy_id='strategy_id',

filename='main.py',

        mode=MODE_BACKTEST,

token='token_id',

backtest_start_time='2017-09-01 08:00:00',

backtest_end_time='2017-10-01 16:00:00',

        backtest_adjust=ADJUST_PREV,

backtest_initial_cash=2000000,

backtest_commission_ratio=0.0001,

backtest_slippage_ratio=0.0001)

3.指数增强

# coding=utf-8

from__future__importprint_function, absolute_import, unicode_literals

importnumpyasnp

fromgm.apiimport*

frompandasimportDataFrame

'''

本策略以0.8为初始权重跟踪指数标的沪深300中权重大于0.35%的成份股.

个股所占的百分比为(0.8*成份股权重)*100%.然后根据个股是否:

1.连续上涨5天 2.连续下跌5天

来判定个股是否为强势股/弱势股,并对其把权重由0.8调至1.0或0.6

回测时间为:2017-07-01 08:50:00到2017-10-01 17:00:00

'''

definit(context):

# 资产配置的初始权重,配比为0.6-0.8-1.0

context.ratio =0.8

# 获取沪深300当时的成份股和相关数据

stock300 = get_history_constituents(index='SHSE.000300', start_date='2017-06-30', end_date='2017-06-30')[0][

'constituents']

    stock300_symbol = []

    stock300_weight = []

forkeyinstock300:

# 保留权重大于0.35%的成份股

if(stock300[key] /100) >0.0035:

            stock300_symbol.append(key)

stock300_weight.append(stock300[key] /100)

context.stock300 = DataFrame([stock300_weight], columns=stock300_symbol, index=['weight']).T

print('选择的成分股权重总和为: ', np.sum(stock300_weight))

subscribe(symbols=stock300_symbol, frequency='1d', count=5, wait_group=True)

defon_bar(context, bars):

# 若没有仓位则按照初始权重开仓

forbarinbars:

symbol = bar['symbol']

        position = context.account().position(symbol=symbol, side=PositionSide_Long)

ifnotposition:

buy_percent = context.stock300['weight'][symbol] * context.ratio

            order_target_percent(symbol=symbol, percent=buy_percent, order_type=OrderType_Market,

                                position_side=PositionSide_Long)

print(symbol,'以市价单开多仓至仓位:', buy_percent)

else:

# 获取过去5天的价格数据,若连续上涨则为强势股,权重+0.2;若连续下跌则为弱势股,权重-0.2

recent_data = context.data(symbol=symbol, frequency='1d', count=5, fields='close')['close'].tolist()

ifall(np.diff(recent_data) >0):

buy_percent = context.stock300['weight'][symbol] * (context.ratio +0.2)

                order_target_percent(symbol=symbol, percent=buy_percent, order_type=OrderType_Market,

                                    position_side=PositionSide_Long)

print('强势股', symbol,'以市价单调多仓至仓位:', buy_percent)

elifall(np.diff(recent_data) <0):

buy_percent = context.stock300['weight'][symbol] * (context.ratio -0.2)

                order_target_percent(symbol=symbol, percent=buy_percent, order_type=OrderType_Market,

                                    position_side=PositionSide_Long)

print('弱势股', symbol,'以市价单调多仓至仓位:', buy_percent)

if__name__ =='__main__':

'''

    strategy_id策略ID,由系统生成

    filename文件名,请与本文件名保持一致

    mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST

    token绑定计算机的ID,可在系统设置-密钥管理中生成

    backtest_start_time回测开始时间

    backtest_end_time回测结束时间

    backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST

    backtest_initial_cash回测初始资金

    backtest_commission_ratio回测佣金比例

    backtest_slippage_ratio回测滑点比例

    '''

run(strategy_id='strategy_id',

filename='main.py',

        mode=MODE_BACKTEST,

token='token_id',

backtest_start_time='2017-07-01 08:50:00',

backtest_end_time='2017-10-01 17:00:00',

        backtest_adjust=ADJUST_PREV,

backtest_initial_cash=10000000,

backtest_commission_ratio=0.0001,

backtest_slippage_ratio=0.0001)

4.行业轮动

# coding=utf-8

from__future__importprint_function, absolute_import, unicode_literals

importnumpyasnp

fromgm.apiimport*

'''

本策略每隔1个月定时触发计算SHSE.000910.SHSE.000909.SHSE.000911.SHSE.000912.SHSE.000913.SHSE.000914

(300工业.300材料.300可选.300消费.300医药.300金融)这几个行业指数过去

20个交易日的收益率并选取了收益率最高的指数的成份股获取并获取了他们的市值数据

随后把仓位调整至市值最大的5只股票上

回测数据为:SHSE.000910.SHSE.000909.SHSE.000911.SHSE.000912.SHSE.000913.SHSE.000914和他们的成份股

回测时间为:2017-07-01 08:00:00到2017-10-01 16:00:00

'''

definit(context):

# 每月第一个交易日的09:40 定时执行algo任务

schedule(schedule_func=algo, date_rule='1m', time_rule='09:40:00')

# 用于筛选的行业指数

context.index = ['SHSE.000910','SHSE.000909','SHSE.000911','SHSE.000912','SHSE.000913','SHSE.000914']

# 用于统计数据的天数

context.date =20

# 最大下单资金比例

context.ratio =0.8

defalgo(context):

# 获取当天的日期

    today = context.now

# 获取上一个交易日

last_day = get_previous_trading_date(exchange='SHSE', date=today)

    return_index = []

# 获取并计算行业指数收益率

foriincontext.index:

return_index_his = history_n(symbol=i, frequency='1d', count=context.date, fields='close,bob',

fill_missing='Last', adjust=ADJUST_PREV, end_time=last_day, df=True)

return_index_his = return_index_his['close'].values

return_index.append(return_index_his[-1] / return_index_his[0] -1)

# 获取指定数内收益率表现最好的行业

    sector = context.index[np.argmax(return_index)]

print('最佳行业指数是: ', sector)

# 获取最佳行业指数成份股

symbols = get_history_constituents(index=sector, start_date=last_day, end_date=last_day)[0]['constituents'].keys()

# 获取当天有交易的股票

    not_suspended_info = get_history_instruments(symbols=symbols, start_date=today, end_date=today)

not_suspended_symbols = [item['symbol']foriteminnot_suspended_infoifnotitem['is_suspended']]

# 获取最佳行业指数成份股的市值,从大到小排序并选取市值最大的5只股票

fin = get_fundamentals(table='tq_sk_finindic', symbols=not_suspended_symbols, start_date=last_day,

end_date=last_day, limit=5, fields='NEGOTIABLEMV', order_by='-NEGOTIABLEMV', df=True)

fin.index = fin['symbol']

# 计算权重

percent =1.0/ len(fin.index) * context.ratio

# 获取当前所有仓位

    positions = context.account().positions()

# 如标的池有仓位,平不在标的池的仓位

forpositioninpositions:

symbol = position['symbol']

ifsymbolnotinfin.index:

order_target_percent(symbol=symbol, percent=0, order_type=OrderType_Market,

                                position_side=PositionSide_Long)

print('市价单平不在标的池的', symbol)

# 对标的池进行操作

forsymbolinfin.index:

        order_target_percent(symbol=symbol, percent=percent, order_type=OrderType_Market,

                            position_side=PositionSide_Long)

print(symbol,'以市价单调整至仓位', percent)

if__name__ =='__main__':

'''

    strategy_id策略ID,由系统生成

    filename文件名,请与本文件名保持一致

    mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST

    token绑定计算机的ID,可在系统设置-密钥管理中生成

    backtest_start_time回测开始时间

    backtest_end_time回测结束时间

    backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST

    backtest_initial_cash回测初始资金

    backtest_commission_ratio回测佣金比例

    backtest_slippage_ratio回测滑点比例

    '''

run(strategy_id='strategy_id',

filename='main.py',

        mode=MODE_BACKTEST,

token='token_id',

backtest_start_time='2017-07-01 08:00:00',

backtest_end_time='2017-10-01 16:00:00',

        backtest_adjust=ADJUST_PREV,

backtest_initial_cash=10000000,

backtest_commission_ratio=0.0001,

backtest_slippage_ratio=0.0001)

5.集合竞价选股

# coding=utf-8

from__future__importprint_function, absolute_import, unicode_literals

fromgm.apiimport*

'''

本策略通过获取SHSE.000300沪深300的成份股数据并统计其30天内

开盘价大于前收盘价的天数,并在该天数大于阈值10的时候加入股票池

随后对不在股票池的股票平仓并等权配置股票池的标的,每次交易间隔1个月.

回测数据为:SHSE.000300在2015-01-15的成份股

回测时间为:2017-07-01 08:00:00到2017-10-01 16:00:00

'''

definit(context):

# 每月第一个交易日的09:40 定时执行algo任务

schedule(schedule_func=algo, date_rule='1m', time_rule='09:40:00')

# context.count_bench累计天数阙值

context.count_bench =10

# 用于对比的天数

context.count =30

# 最大交易资金比例

context.ratio =0.8

defalgo(context):

# 获取当前时间

    now = context.now

# 获取上一个交易日

last_day = get_previous_trading_date(exchange='SHSE', date=now)

# 获取沪深300成份股

context.stock300 = get_history_constituents(index='SHSE.000300', start_date=last_day,

end_date=last_day)[0]['constituents'].keys()

# 获取当天有交易的股票

    not_suspended_info = get_history_instruments(symbols=context.stock300, start_date=now, end_date=now)

not_suspended_symbols = [item['symbol']foriteminnot_suspended_infoifnotitem['is_suspended']]

    trade_symbols = []

ifnotnot_suspended_symbols:

print('没有当日交易的待选股票')

return

forstockinnot_suspended_symbols:

recent_data = history_n(symbol=stock, frequency='1d', count=context.count, fields='pre_close,open',

fill_missing='Last', adjust=ADJUST_PREV, end_time=now, df=True)

diff = recent_data['open'] - recent_data['pre_close']

# 获取累计天数超过阙值的标的池.并剔除当天没有交易的股票

iflen(diff[diff >0]) >= context.count_bench:

            trade_symbols.append(stock)

print('本次股票池有股票数目: ', len(trade_symbols))

# 计算权重

percent =1.0/ len(trade_symbols) * context.ratio

# 获取当前所有仓位

    positions = context.account().positions()

# 如标的池有仓位,平不在标的池的仓位

forpositioninpositions:

symbol = position['symbol']

ifsymbolnotintrade_symbols:

order_target_percent(symbol=symbol, percent=0, order_type=OrderType_Market,

                                position_side=PositionSide_Long)

print('市价单平不在标的池的', symbol)

# 对标的池进行操作

forsymbolintrade_symbols:

        order_target_percent(symbol=symbol, percent=percent, order_type=OrderType_Market,

                            position_side=PositionSide_Long)

print(symbol,'以市价单调整至权重', percent)

if__name__ =='__main__':

'''

    strategy_id策略ID,由系统生成

    filename文件名,请与本文件名保持一致

    mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST

    token绑定计算机的ID,可在系统设置-密钥管理中生成

    backtest_start_time回测开始时间

    backtest_end_time回测结束时间

    backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST

    backtest_initial_cash回测初始资金

    backtest_commission_ratio回测佣金比例

    backtest_slippage_ratio回测滑点比例

    '''

run(strategy_id='strategy_id',

filename='main.py',

        mode=MODE_BACKTEST,

token='token_id',

backtest_start_time='2017-07-01 08:00:00',

backtest_end_time='2017-10-01 16:00:00',

        backtest_adjust=ADJUST_PREV,

backtest_initial_cash=10000000,

backtest_commission_ratio=0.0001,

backtest_slippage_ratio=0.0001)

6.机器学习

# coding=utf-8

from__future__importprint_function, absolute_import, unicode_literals

fromdatetimeimportdatetime

importnumpyasnp

fromgm.apiimport*

importsys

try:

fromsklearnimportsvm

except:

print('请安装scikit-learn库和带mkl的numpy')

sys.exit(-1)

'''

本策略选取了七个特征变量组成了滑动窗口长度为15天的训练集,随后训练了一个二分类(上涨/下跌)的支持向量机模型.

若没有仓位则在每个星期一的时候输入标的股票近15个交易日的特征变量进行预测,并在预测结果为上涨的时候购买标的.

若已经持有仓位则在盈利大于10%的时候止盈,在星期五损失大于2%的时候止损.

特征变量为:1.收盘价/均值2.现量/均量3.最高价/均价4.最低价/均价5.现量6.区间收益率7.区间标准差

训练数据为:SHSE.600000浦发银行,时间从2016-03-01到2017-06-30

回测时间为:2017-07-01 09:00:00到2017-10-01 09:00:00

'''

definit(context):

# 订阅浦发银行的分钟bar行情

context.symbol ='SHSE.600000'

subscribe(symbols=context.symbol, frequency='60s')

start_date ='2016-03-01'# SVM训练起始时间

end_date ='2017-06-30'# SVM训练终止时间

# 用于记录工作日

# 获取目标股票的daily历史行情

recent_data = history(context.symbol, frequency='1d', start_time=start_date, end_time=end_date, fill_missing='last',

df=True)

days_value = recent_data['bob'].values

days_close = recent_data['close'].values

    days = []

# 获取行情日期列表

print('准备数据训练SVM')

foriinrange(len(days_value)):

days.append(str(days_value[i])[0:10])

    x_all = []

    y_all = []

forindexinrange(15, (len(days) -5)):

# 计算三星期共15个交易日相关数据

start_day = days[index -15]

        end_day = days[index]

data = history(context.symbol, frequency='1d', start_time=start_day, end_time=end_day, fill_missing='last',

df=True)

close = data['close'].values

max_x = data['high'].values

min_n = data['low'].values

amount = data['amount'].values

        volume = []

foriinrange(len(close)):

            volume_temp = amount[i] / close[i]

            volume.append(volume_temp)

close_mean = close[-1] / np.mean(close)# 收盘价/均值

volume_mean = volume[-1] / np.mean(volume)# 现量/均量

max_mean = max_x[-1] / np.mean(max_x)# 最高价/均价

min_mean = min_n[-1] / np.mean(min_n)# 最低价/均价

vol = volume[-1]# 现量

return_now = close[-1] / close[0]# 区间收益率

std = np.std(np.array(close), axis=0)# 区间标准差

# 将计算出的指标添加到训练集X

# features用于存放因子

        features = [close_mean, volume_mean, max_mean, min_mean, vol, return_now, std]

        x_all.append(features)

# 准备算法需要用到的数据

foriinrange(len(days_close) -20):

ifdays_close[i +20] > days_close[i +15]:

label =1

else:

label =0

        y_all.append(label)

x_train = x_all[:-1]

y_train = y_all[:-1]

# 训练SVM

context.clf = svm.SVC(C=1.0, kernel='rbf', degree=3, gamma='auto', coef0=0.0, shrinking=True, probability=False,

tol=0.001, cache_size=200, verbose=False, max_iter=-1,

decision_function_shape='ovr', random_state=None)

    context.clf.fit(x_train, y_train)

print('训练完成!')

defon_bar(context, bars):

bar = bars[0]

# 获取当前年月日

today = bar.bob.strftime('%Y-%m-%d')

# 获取数据并计算相应的因子

# 于星期一的09:31:00进行操作

# 当前bar的工作日

weekday = datetime.strptime(today,'%Y-%m-%d').isoweekday()

# 获取模型相关的数据

# 获取持仓

    position = context.account().position(symbol=context.symbol, side=PositionSide_Long)

# 如果bar是新的星期一且没有仓位则开始预测

ifnotpositionandweekday ==1:

# 获取预测用的历史数据

data = history_n(symbol=context.symbol, frequency='1d', end_time=today, count=15,

fill_missing='last', df=True)

close = data['close'].values

train_max_x = data['high'].values

train_min_n = data['low'].values

train_amount = data['amount'].values

        volume = []

foriinrange(len(close)):

            volume_temp = train_amount[i] / close[i]

            volume.append(volume_temp)

close_mean = close[-1] / np.mean(close)

volume_mean = volume[-1] / np.mean(volume)

max_mean = train_max_x[-1] / np.mean(train_max_x)

min_mean = train_min_n[-1] / np.mean(train_min_n)

vol = volume[-1]

return_now = close[-1] / close[0]

std = np.std(np.array(close), axis=0)

# 得到本次输入模型的因子

        features = [close_mean, volume_mean, max_mean, min_mean, vol, return_now, std]

features = np.array(features).reshape(1,-1)

prediction = context.clf.predict(features)[0]

# 若预测值为上涨则开仓

ifprediction ==1:

# 获取昨收盘价

context.price = close[-1]

# 把浦发银行的仓位调至95%

order_target_percent(symbol=context.symbol, percent=0.95, order_type=OrderType_Market,

                                position_side=PositionSide_Long)

print('SHSE.600000以市价单开多仓到仓位0.95')

# 当涨幅大于10%,平掉所有仓位止盈

elifpositionandbar.close / context.price >=1.10:

        order_close_all()

print('SHSE.600000以市价单全平多仓止盈')

# 当时间为周五并且跌幅大于2%时,平掉所有仓位止损

elifpositionandbar.close / context.price <1.02andweekday ==5:

        order_close_all()

print('SHSE.600000以市价单全平多仓止损')

if__name__ =='__main__':

'''

    strategy_id策略ID,由系统生成

    filename文件名,请与本文件名保持一致

    mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST

    token绑定计算机的ID,可在系统设置-密钥管理中生成

    backtest_start_time回测开始时间

    backtest_end_time回测结束时间

    backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST

    backtest_initial_cash回测初始资金

    backtest_commission_ratio回测佣金比例

    backtest_slippage_ratio回测滑点比例

    '''

run(strategy_id='strategy_id',

filename='main.py',

        mode=MODE_BACKTEST,

token='token_id',

backtest_start_time='2017-07-01 09:00:00',

backtest_end_time='2017-10-01 09:00:00',

        backtest_adjust=ADJUST_PREV,

backtest_initial_cash=10000000,

backtest_commission_ratio=0.0001,

backtest_slippage_ratio=0.0001)

7.配对交易

# coding=utf-8

from__future__importprint_function, absolute_import, unicode_literals

fromgm.apiimport*

importnumpyasnp

definit(context):

#获得N日股票交易数据

context.N=5

#选择一对股票

context.stock=['SZSE.000651','SZSE.000333']

# 每个交易日的09:40 定时执行algo任务

schedule(schedule_func=algo, date_rule='1d', time_rule='09:40:00')

defalgo(context):

# 获取上一个交易日的日期

last_day = get_previous_trading_date(exchange='SHSE', date=context.now)

# 获取当天有交易的股票,似乎无法同时获得两只股票的数据,所以只能麻烦一点

not_suspended = get_history_instruments(symbols=context.stock[0], start_date=last_day, end_date=last_day)

a = len([item['symbol']foriteminnot_suspendedifnotitem['is_suspended']])

not_suspended = get_history_instruments(symbols=context.stock[1], start_date=last_day,end_date=last_day)

b = len([item['symbol']foriteminnot_suspendedifnotitem['is_suspended']])

#如果有一支停牌,就跳过

ifa+b<2:

return

#获得交易数据

prices1 = history_n(symbol=context.stock[0], frequency='1d', count=context.N, end_time=last_day, fields='close',

skip_suspended=True,

fill_missing=None, adjust=ADJUST_PREV, adjust_end_time='', df=True)

prices2=history_n(symbol=context.stock[1], frequency='1d', count=context.N, end_time=last_day, fields='close',

skip_suspended=True,

fill_missing=None, adjust=ADJUST_PREV, adjust_end_time='', df=True)

p1=list(prices1['close'])

p2=list(prices2['close'])

spread = np.array(p1[:-1]) - np.array(p2[:-1])

# 计算布林带的上下轨

up = np.mean(spread) +2* np.std(spread)

down = np.mean(spread) -2* np.std(spread)

# 计算最新价差

spread_now = p1[-1] - p2[-1]

# 无交易时若价差上(下)穿布林带上(下)轨则做空(多)价差

position_s1_long = context.account().position(symbol=context.stock[0], side=PositionSide_Long)

position_s2_long = context.account().position(symbol=context.stock[1], side=PositionSide_Long)

ifnotposition_s1_longandnotposition_s2_long:

ifspread_now > up:

order_target_percent(symbol=context.stock[1], percent=0.5, order_type=OrderType_Market,

                                position_side=PositionSide_Long)

ifspread_now < down:

order_target_percent(symbol=context.stock[0], percent=0.5, order_type=OrderType_Market,

                                position_side=PositionSide_Long)

# 价差回归时平仓

elifposition_s2_long:

ifspread_now <= up:

            order_close_all()

elifposition_s1_long:

ifspread_now >= down:

            order_close_all()

if__name__ =='__main__':

'''

    strategy_id策略ID,由系统生成

    filename文件名,请与本文件名保持一致

    mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST

    token绑定计算机的ID,可在系统设置-密钥管理中生成

    backtest_start_time回测开始时间

    backtest_end_time回测结束时间

    backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST

    backtest_initial_cash回测初始资金

    backtest_commission_ratio回测佣金比例

    backtest_slippage_ratio回测滑点比例

    '''

run(strategy_id='73bb5bf2-a536-11e8-bd52-9cd21ef04ea9',

filename='配对交易.py',

        mode=MODE_BACKTEST,

token='c395247a76e8a5caeee699d668d6f550213bc418',

backtest_start_time='2014-01-01 08:00:00',

backtest_end_time='2018-08-01 16:00:00',

        backtest_adjust=ADJUST_PREV,

backtest_initial_cash=10000000,

backtest_commission_ratio=0.0001,

backtest_slippage_ratio=0.0001)

8.GARP策略

# coding=utf-8

from__future__importprint_function, absolute_import, unicode_literals

fromgm.apiimport*

fromsklearnimportpreprocessing

'''

策略思路:

1、公司的资产负债率小于等于 25%

2、公司每股净现金大于 0

3、当前股价与每股自由现金流量比小于 10(市现率)

4、在所有股票中取市盈率排倒数30%的股票(首先PE必须大于0)

5、PEG=市盈率/净利润增长率<0.5

回测数据:SHSE.000906的成份股

回测时间:2016-01-01 08:00:00到2018-01-01 16:00:00

'''

definit(context):

# 每月第一个交易日的09:40 定时执行algo任务

schedule(schedule_func=algo, date_rule='1m', time_rule='09:40:00')

defalgo(context):

# 获取上一个交易日的日期

last_day = get_previous_trading_date(exchange='SHSE', date=context.now)

# 获取沪深300成份股

stock800 = get_history_constituents(index='SHSE.000906', start_date=last_day,

end_date=last_day)[0]['constituents'].keys()

# 获取当天有交易的股票

    not_suspended = get_history_instruments(symbols=stock800, start_date=last_day, end_date=last_day)

not_suspended = [item['symbol']foriteminnot_suspendedifnotitem['is_suspended']]

df = get_fundamentals(table='deriv_finance_indicator', symbols=not_suspended, start_date=last_day, end_date=last_day,

fields='ASSLIABRT,NCFPS,NPGRT', df=True)

fin=get_fundamentals(table='trading_derivative_indicator', symbols=not_suspended, start_date=last_day, end_date=last_day,

fields='PCLFY,PELFY', df=True)

df['PCLFY']=fin['PCLFY']

df['PELFY'] = fin['PELFY']

# 除去空值

    df = df.dropna()

df['PEG']=df['PELFY']/df['NPGRT']

    df.index=df.symbol

deldf['symbol'],df['pub_date'],df['end_date']

    print(df)

# 选出PEG小于0.5的部分

df = df[df['PEG'] <0.5]

# 选出债务总资产比小于0.25的部分

df = df[df["ASSLIABRT"] <25]

# 选出每股净现金大于 0的部分

df = df[df["NCFPS"] >0]

# 选出市盈率大于零的部分

df = df[df['PELFY'] >0]

# 选出市现率小于10的部分

df = df[df['PCLFY'] <10]

    print(df)

# 剔除市盈率较高的股票(即剔除3分位数以后的股票)

iflen(df)<4:

        symbols_pool = list(df.index)

else:

df = df[(df['PELFY'] < df['PELFY'].quantile(0.3))]

        symbols_pool = list(df.index)

    print(symbols_pool)

    order_close_all()

    long=len(symbols_pool)

iflong==0:

return

# 获取股票的权重

percent =1/ long

# 买在标的池中的股票

forsymbolinsymbols_pool:

        order_target_percent(symbol=symbol, percent=percent, order_type=OrderType_Market,

                            position_side=PositionSide_Long)

#print(symbol, '以市价单调多仓到仓位', percent)

if__name__ =='__main__':

'''

    strategy_id策略ID,由系统生成

    filename文件名,请与本文件名保持一致

    mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST

    token绑定计算机的ID,可在系统设置-密钥管理中生成

    backtest_start_time回测开始时间

    backtest_end_time回测结束时间

    backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST

    backtest_initial_cash回测初始资金

    backtest_commission_ratio回测佣金比例

    backtest_slippage_ratio回测滑点比例

    '''

run(strategy_id='73bb5bf2-a536-11e8-bd52-9cd21ef04ea9',

filename='GARP.py',

        mode=MODE_BACKTEST,

token='c395247a76e8a5caeee699d668d6f550213bc418',

backtest_start_time='2016-01-01 08:00:00',

backtest_end_time='2018-01-01 16:00:00',

        backtest_adjust=ADJUST_PREV,

backtest_initial_cash=10000000,

backtest_commission_ratio=0.0001,

backtest_slippage_ratio=0.0001)

9.alpha对冲(股票+期货)

# coding=utf-8

from__future__importprint_function, absolute_import, unicode_literals

fromgm.apiimport*

'''

本策略每隔1个月定时触发计算SHSE.000300成份股的过去的EV/EBITDA并选取EV/EBITDA大于0的股票

随后平掉排名EV/EBITDA不在最小的30的股票持仓并等权购买EV/EBITDA最小排名在前30的股票

并用相应的CFFEX.IF对应的真实合约等额对冲

回测数据为:SHSE.000300和他们的成份股和CFFEX.IF对应的真实合约

回测时间为:2017-07-01 08:00:00到2017-10-01 16:00:00

'''

definit(context):

# 每月第一个交易日09:40:00的定时执行algo任务

schedule(schedule_func=algo, date_rule='1m', time_rule='09:40:00')

# 设置开仓在股票和期货的资金百分比(期货在后面自动进行杠杆相关的调整)

context.percentage_stock =0.4

context.percentage_futures =0.4

defalgo(context):

# 获取当前时刻

    now = context.now

# 获取上一个交易日

last_day = get_previous_trading_date(exchange='SHSE', date=now)

# 获取沪深300成份股

stock300 = get_history_constituents(index='SHSE.000300', start_date=last_day,

end_date=last_day)[0]['constituents'].keys()

# 获取上一个工作日的CFFEX.IF对应的合约

index_futures = get_continuous_contracts(csymbol='CFFEX.IF', start_date=last_day, end_date=last_day)[-1]['symbol']

# 获取当天有交易的股票

    not_suspended_info = get_history_instruments(symbols=stock300, start_date=now, end_date=now)

not_suspended_symbols = [item['symbol']foriteminnot_suspended_infoifnotitem['is_suspended']]

# 获取成份股EV/EBITDA大于0并为最小的30个

fin = get_fundamentals(table='tq_sk_finindic', symbols=not_suspended_symbols,

start_date=now, end_date=now, fields='EVEBITDA',

filter='EVEBITDA>0', order_by='EVEBITDA', limit=30, df=True)

    fin.index = fin.symbol

# 获取当前仓位

    positions = context.account().positions()

# 平不在标的池或不为当前股指期货主力合约对应真实合约的标的

forpositioninpositions:

symbol = position['symbol']

sec_type = get_instrumentinfos(symbols=symbol)[0]['sec_type']

# 若类型为期货且不在标的池则平仓

ifsec_type == SEC_TYPE_FUTUREandsymbol != index_futures:

order_target_percent(symbol=symbol, percent=0, order_type=OrderType_Market,

                                position_side=PositionSide_Short)

print('市价单平不在标的池的', symbol)

elifsymbolnotinfin.index:

order_target_percent(symbol=symbol, percent=0, order_type=OrderType_Market,

                                position_side=PositionSide_Long)

print('市价单平不在标的池的', symbol)

# 获取股票的权重

    percent = context.percentage_stock / len(fin.index)

# 买在标的池中的股票

forsymbolinfin.index:

        order_target_percent(symbol=symbol, percent=percent, order_type=OrderType_Market,

                            position_side=PositionSide_Long)

print(symbol,'以市价单调多仓到仓位', percent)

# 获取股指期货的保证金比率

ratio = get_history_instruments(symbols=index_futures, start_date=last_day, end_date=last_day)[0]['margin_ratio']

# 更新股指期货的权重

    percent = context.percentage_futures * ratio

# 买入股指期货对冲

    order_target_percent(symbol=index_futures, percent=percent, order_type=OrderType_Market,

                        position_side=PositionSide_Short)

print(index_futures,'以市价单调空仓到仓位', percent)

if__name__ =='__main__':

'''

    strategy_id策略ID,由系统生成

    filename文件名,请与本文件名保持一致

    mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST

    token绑定计算机的ID,可在系统设置-密钥管理中生成

    backtest_start_time回测开始时间

    backtest_end_time回测结束时间

    backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST

    backtest_initial_cash回测初始资金

    backtest_commission_ratio回测佣金比例

    backtest_slippage_ratio回测滑点比例

    '''

run(strategy_id='strategy_id',

filename='main.py',

        mode=MODE_BACKTEST,

token='token_id',

backtest_start_time='2017-07-01 08:00:00',

backtest_end_time='2017-10-01 16:00:00',

        backtest_adjust=ADJUST_PREV,

backtest_initial_cash=10000000,

backtest_commission_ratio=0.0001,

backtest_slippage_ratio=0.0001)

链接下文:经典的股票/期货量化交易策略,拿来即用的Python策略源码(2)


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • pyspark.sql模块 模块上下文 Spark SQL和DataFrames的重要类: pyspark.sql...
    mpro阅读 9,446评论 0 13