您的位置:

gplearn全方位解析

一、gplearn因子

gplearn是一个基于Python开发的遗传编程算法库,可以通过遗传编程的方式,自动寻找最符合数据集特征的函数。其中,gplearn中的因子概念是非常重要的,因为所有函数都是由因子组合而成的。gplearn中的因子包括常数、变量、自定义函数和操作符。

先看一个简单的例子,通过gplearn来拟合一条直线方程y = 2x + 1。那么,我们需要定义的因子就是x变量和常数1和2,其中操作符‘+’和‘×’都是gplearn库提供的。

from gplearn.genetic import SymbolicRegressor
import numpy as np

x = np.arange(-1,1,0.1)
y = 2*x + 1

est_gp = SymbolicRegressor(population_size=5000, generations=50, stopping_criteria=0.01,
                           const_range=(-1,1), 
                           p_crossover=0.7, p_subtree_mutation=0.1, p_hoist_mutation=0.05, 
                           p_point_mutation=0.1, max_samples=0.9, verbose=1,
                           parsimony_coefficient=0.01, random_state=0)

est_gp.fit(x.reshape(-1,1), y)

print(est_gp._program)

代码输出为:add(mul(2, X0), 0.993),其中X0表示x变量,0.993表示常数,add代表操作符“+”,mul代表操作符“×”。

二、gplearn和deap因子

gplearn除了包含其自身的因子之外,也可以与其他遗传编程算法库进行结合,以扩充其自身的因子库。其中,deap是Python中另一个常用的遗传编程算法库,两者结合可以构造更为丰富的因子库。

以deap中的因子为例,其中的因子可以分为三类:终止符、中间符和算子。终止符是描述数据输入的因子,而中间符用于描述计算过程中的结果,算子则是种群进化过程中用于结合因子的组合运算符。

gplearn中可以通过定义deap中的因子来扩充其自身的因子库。以deap中的基本因子库介绍:

from deap import gp
from gplearn.genetic import SymbolicRegressor
import numpy as np

def protectedDiv(x1, x2):
    if x2 == 0:
        return 1
    else:
        return x1/x2

pset = gp.PrimitiveSet("MAIN", 2)
pset.addPrimitive(np.add, 2)
pset.addPrimitive(np.subtract, 2)
pset.addPrimitive(np.multiply, 2)
pset.addPrimitive(protectedDiv, 2)
pset.addPrimitive(np.sin, 1)
pset.addPrimitive(np.cos, 1)
pset.addPrimitive(np.tan, 1)
pset.addPrimitive(np.square, 1)
pset.addPrimitive(np.sqrt, 1)

creator.create("FitnessMin", base.Fitness, weights=(-1.0,))
creator.create("Individual", gp.PrimitiveTree, fitness=creator.FitnessMin)

toolbox = base.Toolbox()
toolbox.register("expr", gp.genHalfAndHalf, pset=pset, min_=1, max_=2)
toolbox.register("individual", tools.initIterate, creator.Individual, toolbox.expr)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)

est_gp = SymbolicRegressor(population_size=5000, generations=50, stopping_criteria=0.01,
                           const_range=(-1, 1), 
                           p_crossover=0.7, p_subtree_mutation=0.1, p_hoist_mutation=0.05, 
                           p_point_mutation=0.1, max_samples=0.9, verbose=1,
                           parsimony_coefficient=0.01, random_state=0, 
                           function_set=toolbox)

est_gp.fit(x.reshape(-1,1), y)

print(est_gp._program)

上述代码中的内容与之前的例子类似,只是在gplearn中的SymbolicRegressor中,引入了deap库中定义的基本因子库,其中包含了加、减、乘、除、正弦、余弦、正切、平方和平方根等基本的数学函数运算。

三、gplearn聚宽

gplearn可以与聚宽结合,进行量化选股策略的研发。聚宽是一个国内领先的量化平台,提供了较为全面的量化交易解决方案,在量化选股、策略回测、实盘交易、风险管理等方面具备强大的技术支撑。

下面的例子演示了如何将gplearn与聚宽平台结合,进行快速策略开发。以判断空头市场的趋势为例,通过gplearn的SymbolicClassifier算法,进行模型的训练和预测。

以月线数据为例,定义了若干个市场趋势相关的特征因子,由gplearn自动优化其组合方式。
##########定义函数因子##########
pset = gp.PrimitiveSetTyped("MAIN", [float,float,float], bool)
pset.renameArguments(ARG0='open',ARG1='high',ARG2='low')
def if_then_else(input, output1, output2):
    return output1 if input else output2
pset.addPrimitive(np.greater, [float,float], bool)
pset.addPrimitive(np.subtract, [float,float], float)
pset.addPrimitive(np.add, [float,float], float)
pset.addPrimitive(np.multiply, [float,float], float)
pset.addPrimitive(if_then_else, [bool,float,float], float)
pset.addTerminal(0.6)
pset.addTerminal(0.4)
pset.addPrimitive(operator.and_,[bool,bool],bool)
pset.addPrimitive(operator.or_,[bool,bool],bool)
pset.addPrimitive(operator.not_,[bool],bool)
def sqrt(x1):
    return np.sqrt(abs(x1))
def square(x1):
    return x1*x1
pset.addPrimitive(sqrt,[float,float],float)
pset.addPrimitive(square,[float,float],float)

##########定义SymbolicClassifier##########
from gplearn.genetic import SymbolicClassifier
clf = SymbolicClassifier(population_size=1000,
                          generations=20,
                          tournament_size=20,
                          stopping_criteria=0.01,
                          function_set=pset,
                          random_state=0,
                          verbose=1)

 ##########选择股票池##########
def initialize(context):
    set_benchmark('000300.XSHG')
    g.stocks = get_index_stocks('000300.XSHG')

##########定义交易逻辑##########
def handle_bar(context, bar_dict):
    if context.stock_buyed:
        orders = context.portfolio.positions.keys()
        if len(orders)>0:
            for stock in orders:
                order_target_value(stock, 0)

    X = get_bars(context.stocks,
                 count = 300,
                 unit = '1d',
                 fields=[ 'open','close', 'high', 'low', 'volume','money'],
                 include_now=True)
    X['diff']=(X['close']-X['open'])/X['open']
    X['avg_price'] = X['money']/X['volume']
    X['vol_ave_3'] = X['volume'].rolling(window=3).mean()
    X['vol_ave_5'] = X['volume'].rolling(window=5).mean()
    X['vol_ave_10'] = X['volume'].rolling(window=10).mean()
    X = X.dropna()
    y = np.where(X['close']-X['open']>0, True, False)

    clf.fit(X[['open','high','low']].tail(len(y)), y)
    predict = clf.predict(X[['open','high','low']].iloc[-1].reshape(1, -1))[0]

    if predict and context.stock_buyed is False:
        market_cap_list = []
        adv20_list = []
        for stock in context.stocks:
            q = query(valuation.market_cap, valuation.circulating_market_cap).filter(valuation.code == stock)
            df = get_fundamentals(q)
            market_cap = df['market_cap'][0]
            adv20_value = average_volume(stock,20)
            market_cap_list.append(market_cap)
            adv20_list.append(adv20_value)
        context.stocks = [x for _,x in sorted(zip(market_cap_list,context.stocks))]
        context.stocks = context.stocks[:int(len(context.stocks)/2)]
        for stock in context.stocks:
            if not is_st_stock(stock):
                if not is_paused(stock):
                    if not is_high_limit(stock):
                        order_target_percent(stock, 1.0/len(context.stocks))

实际运行效果取决于所选取的连接功能的股票池数量和所选择的因子,可以在聚宽平台上自由调整。

四、gplearn因子挖掘

gplearn还可以基于遗传编程的思想,对数据集中的因子进行挖掘。以Iris为例,进行特征提取。

##########引入数据##########
from sklearn.datasets import load_iris
from gplearn.genetic import SymbolicTransformer
from sklearn.pipeline import make_pipeline
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split

data = load_iris()

##########定义gplearn对象##########
est_gp = SymbolicTransformer(population_size=5000, 
                               hall_of_fame=100,
                               n_components=10,
                               generations=20,
                               tournament_size=20,
                               stopping_criteria=0.01,
                               p_crossover=0.7,
                               p_subtree_mutation=0.1,
                               p_hoist_mutation=0.05,
                               p_point_mutation=0.1,
                               max_samples=0.9,
                               verbose=1,
                               random_state=0,
                               function_set=('add', 'sub', 'mul', 'div',
                                             'sin', 'cos', 'tan', 'sqrt', 'log', 'abs',
                                             max, min))

X_train, X_test, y_train, y_test = train_test_split(
        data.data, data.target, test_size=0.2, random_state=0)

pipeline = make_pipeline(est_gp, LogisticRegression())

pipeline.fit(X_train, y_train)
print(pipeline.score(X_test, y_test))

上述代码中,通过load_iris函数引入了iris数据集,其中包含样本为150个,每个样本有四个特征。

定义了gplearn.SymbolicTransformer,使用遗传编程的思想构建出最表达能力最强的因子组合。10的n_components参数限制了运行的最长时间。这里10个因子表达式产生在20个世代中,每个世代由5000个个体组成。

五、gplearn自定义函数

gplearn中的函数库可以通过自定义函数进行扩充,使用gplearn基于遗传编程的方式,寻找最优的函数拟合模型。自定义函数与已有函数一样具有可重复性以及可扩展性,同样可以快速构建符合实际需求的数学模型。

以x的三次方作为自定义函数,进行样本拟合为例:
from gplearn.genetic import SymbolicRegressor
import numpy as np

def my_func(x):
    return x**3

est_gp = SymbolicRegressor(population_size=5000, generations=75, stopping_criteria=0.01,
                           elitism=3, tournament_size=20, const_range=(-1, 1), 
                           p_crossover=0.7, p_subtree_mutation=0.1, p_hoist_mutation=0.05,
                           p_point_mutation=0.1, max_samples=0.9, verbose=1,
                           function_set=['add', 'sub', 'mul', 'div', 'sqrt', 'log', my_func],
                           parsimony_coefficient=0.01, random_state=0)

x = np.arange(-1,1,0.1)
y = x**3 - 0.5

est_gp.fit(x.reshape(-1,1), y)

print(est_gp._program)

定义了一个三次方的自定义函数my_func,然后将其加入到function_set中。在算法执行过程中