接触量化交易之前要先了解量化交易策略,请问大家有没有比较简单又容易上手的量化交易策略呢?
通道策略
所谓通道策略是指利用收盘价或者最高/最低价构造出交易上轨道和下轨道,
很多,可以从经典策略开始:
传统量化策略:小市值策略、海龟策略、金叉死叉策略、行业轮动策略、多因子选股策略等
AI量化策略:Stockranker策略、随机森林策略、CNN策略、DNN策略、Transformer策略、DQN策略等
以上都有策略源码,举个小市值策略的代码案例,其余BigQuant来克隆源码。
小市值策略源码
# Python 代码入口函数,input_1/2/3 对应三个输入端,data_1/2/3 对应三个输出端
def m2_run_bigquant_run(input_1, input_2, input_3):
# 示例代码如下。在这里编写您的代码
# 计算行业行业收益
industry_df = DataSource("basic_info_index_CN_STOCK_A").read()
SW_industry_list = industry_df[industry_df.instrument.str.startswith('SW')].instrument.tolist()
SW_industry_df = DataSource("bar1d_index_CN_STOCK_A").read(SW_industry_list)
SW_industry_ret = SW_industry_df.groupby('instrument').apply(calcu_ret)
SW_industry_ret.reset_index(inplace=True, drop=True)
SW_industry_ret['date'] = SW_industry_ret['date'].apply(lambda x:x.strftime('%Y-%m-%d'))
daily_buy_industry_dict = {dt:seek_head_industry(SW_industry_ret.set_index('date').loc[dt]) for dt in list(set(SW_industry_ret.date))}
daily_stock = input_1.read()
daily_stock['industry_sw_level1_0'] = daily_stock['industry_sw_level1_0'].apply(lambda x:'SW'+str(x)+'.HIX')
daily_stock = daily_stock.groupby(['date', 'industry_sw_level1_0']).apply(seek_head_stock).reset_index()
buy_df = pd.DataFrame()
for k,v in daily_buy_industry_dict.items():
buy_df = buy_df.append(daily_stock[(daily_stock.date==k)&(daily_stock.industry_sw_level1_0.apply(lambda x: x in v))])
buy_df = buy_df.groupby('date').apply(lambda x:x[0].values.tolist()).reset_index()
buy_df[0] = buy_df[0].apply(lambda x:x[0]+x[1]+x[2])
data_1 = DataSource.write_df(buy_df)
return Outputs(data_1=data_1, data_2=None, data_3=None)
# 计算不同周期的动量
def calcu_ret(df):
df = df.sort_values('date')
for i in [42, 84, 126]: # 分别代表2月、4月、半年的动量
df['ret_%s'%i] = df['close']/df['close'].shift(i)-1
return df
# 计算出得分
def seek_head_industry(df):
for j in ['ret_42','ret_84','ret_126']:
df['%s'%j] = df['%s'%j].rank(ascending=True)
df['score'] = 0.4*df['ret_42']+0.3*df['ret_84']+0.3*df['ret_126'] # 得分的权重分别为0.4、0.3、0.3
result = df.sort_values('score', ascending=False)
return list(result.instrument)[:3] # 前3个行业
# 选出特定行业优质股票
def seek_head_stock(df):
result = df.sort_values(['myrank'], ascending=False)
return list(result.instrument[:10]) # 每个行业选10只股票
# 后处理函数,可选。输入是主函数的输出,可以在这里对数据做处理,或者返回更友好的outputs数据格式。此函数输出不会被缓存。
def m2_post_run_bigquant_run(outputs):
return outputs
# 回测引擎:初始化函数,只执行一次
def m4_initialize_bigquant_run(context):
# 手续费设置
context.set_commission(PerOrder(buy_cost=0.0003, sell_cost=0.0013, min_cost=5))
context.mydata = context.options["data"].read()
# 回测引擎:每日数据处理函数,每天执行一次
def m4_handle_data_bigquant_run(context, data):
# 按月调仓
if context.trading_day_index % 20 != 0:
return
date = data.current_dt.strftime('%Y-%m-%d') # 日期
# 整理出当天要买入的股票
stock_to_buy = context.mydata[context.mydata.date==date][0].tolist()[0]
# 通过positions对象,使用列表生成式的方法获取目前持仓的股票列表
stock_hold_now = [equity.symbol for equity in context.portfolio.positions]
# 继续持有的股票:调仓时,如果买入的股票已经存在于目前的持仓里,那么应继续持有
no_need_to_sell = [i for i in stock_hold_now if i in stock_to_buy]
# 需要卖出的股票
stock_to_sell = [i for i in stock_hold_now if i not in no_need_to_sell]
# 卖出
for stock in stock_to_sell:
# 如果该股票停牌,则没法成交。因此需要用can_trade方法检查下该股票的状态
# 如果返回真值,则可以正常下单,否则会出错
# 因为stock是字符串格式,我们用symbol方法将其转化成平台可以接受的形式:Equity格式
if data.can_trade(context.symbol(stock)):
# order_target_percent是平台的一个下单接口,表明下单使得该股票的权重为0,
# 即卖出全部股票,可参考回测文档
context.order_target_percent(context.symbol(stock), 0)
# 如果当天没有买入的股票,就返回
if len(stock_to_buy) == 0:
return
# 等权重买入
weight = 1 / len(stock_to_buy)
# 买入
for stock in stock_to_buy:
if data.can_trade(context.symbol(stock)):
# 下单使得某只股票的持仓权重达到weight,因为weight大于0,因此是等权重买入
context.order_target_percent(context.symbol(stock), weight)
# 回测引擎:准备数据,只执行一次
def m4_prepare_bigquant_run(context):
pass
# 回测引擎:每个单位时间开始前调用一次,即每日开盘前调用一次。
def m4_before_trading_start_bigquant_run(context, data):
pass
m7 = M.input_features.v1(
features="""industry_sw_level1_0
myrank=rank_fs_roe_ttm_0+rank_fs_net_profit_qoq_0-rank_pb_lf_0"""
)
m5 = M.instruments.v2(
start_date='2020-01-01',
end_date='2021-11-26',
market='CN_STOCK_A',
instrument_list='',
max_count=0
)
m6 = M.general_feature_extractor.v7(
instruments=m5.data,
features=m7.data,
start_date='',
end_date='',
before_start_days=90
)
m8 = M.derived_feature_extractor.v3(
input_data=m6.data,
features=m7.data,
date_col='date',
instrument_col='instrument',
drop_na=False,
remove_extra_columns=False,
user_functions={}
)
m9 = M.dropnan.v2(
input_data=m8.data
)
m2 = M.cached.v3(
input_1=m9.data,
run=m2_run_bigquant_run,
post_run=m2_post_run_bigquant_run,
input_ports='',
params='{}',
output_ports='data_1'
)
m4 = M.trade.v4(
instruments=m5.data,
options_data=m2.data_1,
start_date='',
end_date='',
initialize=m4_initialize_bigquant_run,
handle_data=m4_handle_data_bigquant_run,
prepare=m4_prepare_bigquant_run,
before_trading_start=m4_before_trading_start_bigquant_run,
volume_limit=0.025,
order_price_field_buy='open',
order_price_field_sell='close',
capital_base=10000000,
auto_cancel_non_tradable_orders=True,
data_frequency='daily',
price_type='真实价格',
product_type='股票',
plot_charts=True,
backtest_only=False,
benchmark=''
)