话不多说,直接进入正题
股票池:沪深300
买入策略:选择过去收盘价标准差小于0.05,且量比大于3的股票,当第二天高开的时候进行买入;
卖出策略:卖出条件为收益30%或者亏损7%;
首先我们可以先新建一个文件用于保存放量因子这个技术函数,我这里命名为volume_strategy.py,函数代码如下:
import numpy as np
import pandas as pd
from gm.api import *
import datetime
import statsmodels.api as sm
from dateutil.relativedelta import relativedelta
from sklearn.preprocessing import MinMaxScaler
"""
放量策略:
股票池:以XXX股票池为基础,过去收盘价标准差小于0.05的股票列入关注股票
买入策略:当第N日的成交量相比与第N-1日的成交量大于5的时候买入;
卖出策略:卖出条件为收益30%或者亏损7%;
"""
# 输入值:index:指数代码 now:现在时间 threshold:标准差大小
#返回值:股票清单
def volume_strategy_stock_pool (index,now,threshold):
# 建立股票池
last_day = get_previous_trading_date("SHSE",now) # 获取前一交易日的时间
symbol_array = stk_get_index_constituents(index).sort_values(["symbol"],ascending=False) #获取成分股,按照股票代码从大到小排序
symbol_list = symbol_array["symbol"].values # 提取股票代码
symbol_list_final = []
for symbol in symbol_list:
data = history_n(symbol=symbol, frequency="1d", count=14, end_time=last_day, fields="close", adjust=ADJUST_PREV, df=True)
if data.empty == True: #判断数据是否为空
pass
#print("数据无效",symbol)
else:
close = data["close"].values
close_std = np.std(close)
if close_std < threshold:
symbol_list_final.append(symbol)
return symbol_list_final #返回股票清单
# 输入值 index:股票清单 now:目前时间 symbol_count:总持仓股票数
# 返回值 symbol_buy_price_list,买入股票清单,dataframe类型 (股票,价格)
def volume_strategy_buy (index,now,symbol_count):
# 买入股票
last_day = get_previous_trading_date("SHSE", now) # 获取前一交易日的时间
symbol_buy_price = {}
symbol_buy_price_list = []
for symbol in index:
data = history_n(symbol=symbol, frequency="1d", count=2, end_time=last_day, fields="volume", adjust=ADJUST_PREV, df=True)
if data.empty == True or len(data["volume"].values) != 2: #判断数据是否为空
pass # print("数据无效",symbol)
else:
volume = data["volume"].values
if volume[1]/volume[0] >= 3:
current_price = current(symbol)[0]["price"]
last_price = history_n(symbol=symbol, frequency="1d", count=1, end_time=last_day, fields="close", adjust=ADJUST_PREV, df=True)["close"].values
if np.log(current_price/last_price) > 0: # 目前价格高于昨日收盘价,高开
order_target_percent(symbol=symbol,percent=1/symbol_count,order_type=OrderType_Market,position_side=PositionSide_Long)
symbol_buy_price[symbol] = current_price
print("buy",symbol,current_price)
symbol_buy_price_list = pd.DataFrame((list(symbol_buy_price.items())), columns=["symbol", "price"])
return symbol_buy_price_list
# 输入值 symbol_buy_price,买入股票清单,dataframe类型 (股票,价格)
# 返回值 sell_symbol_list,卖出股票清单,list类型
def volume_strategy_sell(symbol_buy_price):
#卖出股票
sell_symbol_list = []
buy_price_list = list(symbol_buy_price["price"].values)
symbol_list = list(symbol_buy_price["symbol"].values)
for num in range(0,len(symbol_list)-1):
current_price = current(symbol_list[num])[0]["price"]
buy_price = buy_price_list[num]
if current_price > buy_price*1.3 or current_price < buy_price*0.93 :
order_target_percent(symbol=symbol_list[num],percent=0,order_type=OrderType_Market,position_side=PositionSide_Long)
print("sell",symbol_list[num],"卖出价:",current_price,"买入价:",buy_price,"收益:",(current_price-buy_price)/current_price)
sell_symbol_list.append(symbol_list[num])
return sell_symbol_list
该文件中一共包含3个函数:
第一个函数是volume_strategy_stock_pool (index,now,threshold),这个函数的作用从现有股票池中选择收盘价连续14天标准差小于threshold的股票,形成一个备选股清单。因为有些股票有过停牌,所以我们剔除了那些无效数据。否则做标准差的时候会报错。
第二个函数是volume_strategy_buy (index,now,symbol_count),是购买股票的函数,我们根据备选股清单进行逐个排查,选择量比大于3的股票,如果第二天开盘价格大于前一天的收盘价,则买入;
第三个函数是volume_strategy_sell(symbol_buy_price),是卖出股票的函数,如果盈利超过30%,或者亏损超过7%,则卖出,否则仍然持有。
之后我们通过主函数调用,新建py文件volume_strategy_test.py,代码如下:
# coding=utf-8
from __future__ import print_function, absolute_import
import pandas as pd
from gm.api import *
import volume_strategy as vs
def init(context):
# 每天14:50 定时执行algo任务,
# algo执行定时任务函数,只能传context参数
# date_rule执行频率,目前暂时支持1d、1w、1m,其中1w、1m仅用于回测,实时模式1d以上的频率,需要在algo判断日期
# time_rule执行时间, 注意多个定时任务设置同一个时间点,前面的定时任务会被后面的覆盖
context.symbol = "SHSE.000922" # 沪深300
context.num = 5 # 持仓做多股票种类
context.threshold = 0.05 #选股标准差
context.symbol_list = pd.DataFrame() #买入股票清单(股票,买入价格)
schedule(schedule_func=algo, date_rule='1d', time_rule='09:31:00')
def algo(context):
# 以沪深300指数为基础,选择14天标准差小于0.05的股票建立股票池
symbol_list_final = vs.volume_strategy_stock_pool(context.symbol,context.now,context.threshold)
# 判断买入股票的数量是否大于能持仓的最大数量
if len(context.symbol_list.index) <= context.num:
symbol_price_list = vs.volume_strategy_buy(symbol_list_final,context.now,context.num) # 从股票池中获取量比大于3的股票作为标的股票,进行买入
if len(symbol_price_list) != 0: # 如果选择出新的标的股票,则把标的股票加入到买入股票清单中
context.symbol_list = pd.concat([context.symbol_list,symbol_price_list],axis=0).drop_duplicates(subset=['symbol'],keep='first') # 删除symbol重复项,只保留第一项
context.symbol_list = context.symbol_list.reset_index(drop=True) # 重置行索引,不保留原索引作为列
if len(context.symbol_list.index) != 0:
sell_symbol_list = vs.volume_strategy_sell(context.symbol_list) #卖出股票,获得卖出股票清单
if len(sell_symbol_list) != 0:
for symbol in sell_symbol_list: # 从股票清单中删除已经卖出的股票
rows_to_delete = context.symbol_list[context.symbol_list['symbol'] == symbol].index
context.symbol_list = context.symbol_list.drop(rows_to_delete)
print(context.symbol_list)
# 查看最终的回测结果
def on_backtest_finished(context, indicator):
print(indicator)
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回测滑点比例
backtest_match_mode市价撮合模式,以下一tick/bar开盘价撮合:0,以当前tick/bar收盘价撮合:1
'''
run(strategy_id='自己的策略',
filename='volume_strategy_test.py',
mode=MODE_BACKTEST,
token='自己的token码',
backtest_start_time='2020-01-01 09:30:00',
backtest_end_time='2024-6-20 15:00:00',
backtest_adjust=ADJUST_PREV,
backtest_initial_cash=10000,
backtest_commission_ratio=0.0001,
backtest_slippage_ratio=0.0001,
backtest_match_mode=1)
在主函数def algo(context)中,我们先调用了volume_strategy_stock_pool()函数,得到了备选股票的清单,之后我们通过volume_strategy_buy()函数对股票进行买入操作,但因为我们的资金是有效的,所以我们只能持仓一定数量的股票,在这里我假设是5只,所以当持仓数量超过5只的时候,我们便不用再买入股票了,只需要看是否需要卖出。当有新的股票进行买卖资源池中,我们需要进行增删操作。要不每次都需要判断已经卖出的股票是否还需要卖出,浪费时间。
回测结果如下:
从回测结果看,该策略的收益还是可以的,4年半收益为97.81%,但在个股的操作上,看起来并不是十分明智,以交通银行为例,买入和卖出的时机都不算好,我们可以适当调节买入策略或者卖出策略,比如量比大于5买入,或者收益大于40%卖出,或者其他卖出方式,看是否可以得到更好的结果。