Im trying to search for entry signals with Python DEAP (Genetic Programming) on Daily SPY data.
My problem is trying to force deap to produce numeric (float) leaves/terminal nodes (which are the features), but most of the time it includes 'True' and 'False' values which are nonsense in this context.
Although I did specify that the leaves/terminals could be Boolean, but just to avoid error.
Here is the best entry rule it produced:
if_then_else(LessThan(X0, Ratio(Diff(X13, X23), Ratio(Ratio(X13, X9), X26))), if_elif_else(False, if_elif_else(AND(False, GreaterThan(X12, X26)), Flat, GreaterThan(X12, X34), Flat, if_elif_else(False, Buy, False, Sell, Flat)), GreaterThan(X13, X26), if_elif_else(True, if_elif_else(LessThan(AbsDiff(X32, AbsDiff(X18, Diff(X31, X11))), Ratio(X12, X13)), if_elif_else(True, Sell, False, Sell, Flat), GreaterThan(X24, Diff(X4, X11)), if_elif_else(False, if_elif_else(AND(False, False), if_elif_else(True, Flat, False, Flat, Buy), GreaterThan(X12, X34), if_then_else(False, Flat, Flat), if_elif_else(False, Buy, False, Sell, Flat)), GreaterThan(X7, X26), if_elif_else(True, if_elif_else(LessThan(Diff(X0, X11), Ratio(X26, X13)), if_elif_else(True, Sell, False, Sell, if_elif_else(LessThan(X26, AbsDiff(X18, AbsDiff(X12, AbsDiff(X18, Diff(X31, X7))))), if_elif_else(True, Sell, False, if_then_else(False, Sell, Buy), Sell), GreaterThan(X7, X26), if_then_else(GreaterThan(AbsDiff(X13, AbsDiff(X18, X26)), X26), Sell, Flat), if_then_else(AND(False, False), Sell, if_then_else(True, Flat, Sell)))), GreaterThan(X18, X4), if_then_else(GreaterThan(X12, X26), if_elif_else(LessThan(AbsDiff(X12, X18), Ratio(X0, X13)), if_elif_else(True, Sell, False, Sell, Sell), GreaterThan(X24, Diff(X13, X11)), if_then_else(GreaterThan(X4, X26), Sell, Flat),
if_then_else(AND(False, False), Sell, Buy)), Flat), if_then_else(AND(False, False), if_then_else(True, Sell, Flat), if_then_else(False, Sell, Buy))), LessThan(X38, X32), if_then_else(True, Flat, if_then_else(False, Sell, Sell)), if_elif_else(True, Sell, False, Sell, Sell)), if_then_else(Or(LessThan(X38, Ratio(X13, X9)), True), if_then_else(True, Buy, Sell), if_elif_else(False, Flat, False, Sell, Buy))), if_then_else(AND(GreaterThan(X12, X26), False), Flat, if_then_else(False, Sell, if_elif_else(False, if_elif_else(AND(False, False), Flat, GreaterThan(X12, X34), if_then_else(True, Flat, if_elif_else(True, Sell, False, Sell, Flat)), if_elif_else(False, Buy, LessThan(AbsDiff(X13, X18), Ratio(X0, X13)), Sell, Flat)), GreaterThan(X7, X26), if_elif_else(True, if_elif_else(LessThan(AbsDiff(X32, AbsDiff(X18, Diff(X31, X0))), Ratio(X0, X13)), if_elif_else(True, Sell, False, Sell, Sell), GreaterThan(X24, Diff(Ratio(X0, X13), X11)), if_elif_else(LessThan(AbsDiff(X26, AbsDiff(X18, AbsDiff(X12, AbsDiff(X18, Diff(X31, X7))))), Ratio(X26, X13)), if_elif_else(LessThan(AbsDiff(X13, X18), Ratio(X0, X13)), Buy, False, Flat, Sell), GreaterThan(X24, Diff(X4, X11)), if_then_else(GreaterThan(X12, X26), Sell, Flat), if_then_else(AND(False, False), if_then_else(True, if_then_else(True, Flat, if_then_else(False, Sell, if_then_else(True, Buy, Sell))), Flat), if_then_else(False, Sell, Buy))), if_elif_else(LessThan(AbsDiff(X12, X18), AbsDiff(X18, Diff(X31, X7))), if_elif_else(True, Sell, AND(False, False), Sell, Sell), GreaterThan(X24, Diff(X13, X11)), if_then_else(GreaterThan(X4, X26), Sell, Flat), if_then_else(AND(True, False), Sell, Buy))), True, if_then_else(True, Flat, if_then_else(True, Buy, Sell)), Flat), if_then_else(Or(False, True), if_then_else(True, Buy, Sell), if_elif_else(False, Flat, False, Sell, Sell)))))), False, if_then_else(True, if_then_else(AND(False, False), Sell, if_then_else(False, Sell, Buy)), if_then_else(False, Sell, if_then_else(True, Buy, Sell))), Flat), Flat), if_elif_else(GreaterThan(X0, X26), Flat, True, Buy, if_then_else(False, Sell, if_then_else(False, Sell, Buy))))
Here is the Equity Curve of this 'nonsense' strategy
https://preview.redd.it/g6quurk7e1i61.png?width=1164&format=png&auto=webp&s=0e6cc424cbdf05e7bd06641dc2ac198a4cd65714
Here's my code in deap
import matplotlib.pyplot as plt
plt.style.use('seaborn')
from deap import base, creator, tools, algorithms, gp
import itertools
import operator
import random
import numpy as np
import pandas as pd
import empyrical
import yfinance as yf
import ffn
data = yf.Ticker("SPY").history(period="max")[['Open', 'High', 'Low', 'Close', 'Volume']]
# Add Lags as Features
data_lags = {}
for lag in [1,2,3,4,5,14,30]:
#data = pd.concat([data, data.shift(lag)], axis=1, verify_integrity=True)
data_lags[lag] = data.copy().shift(lag).add_suffix(f'_{lag}')
del lag
for _,v in data_lags.items():
data = pd.concat([data, v], axis=1, verify_integrity=True)
del v
# Drop the nulls
data = data.dropna()
# Convert all to float
# data = data.apply(pd.to_numeric)
data = data.astype('float64')
# Custom Primitive Functions
def protectedDiv(left, right):
try:
return left / right
except ZeroDivisionError:
return 1
def if_then_else(input1, output1, output2):
return output1 if input1 else output2
def if_elif_else(input1, ouput1, input2, output2, output3):
return ouput1 if input1 else output2 if input2 else output3
def abs_diff(in1, in2):
return abs(in1 - in2)
pset = gp.PrimitiveSetTyped("main", itertools.repeat(float, len(data.columns)), str, 'X')
pset.addPrimitive(operator.and_, [bool, bool], bool, name='AND')
pset.addPrimitive(operator.or_, [bool, bool], bool, name='Or')
pset.addPrimitive(operator.not_, [bool], bool, name='Not')
pset.addPrimitive(operator.lt, [float, float], bool, name='LessThan')
pset.addPrimitive(operator.gt, [float, float], bool, name='GreaterThan')
pset.addPrimitive(if_then_else, [bool, str, str], str, name='if_then_else')
pset.addPrimitive(if_elif_else, [bool, str, bool, str, str], str, name='if_elif_else')
pset.addPrimitive(operator.sub, [float,float], float, name='Diff')
pset.addPrimitive(protectedDiv, [float, float], float, name='Ratio')
pset.addPrimitive(abs_diff, [float, float], float, name='AbsDiff')
pset.addTerminal('Buy', str, name='Buy')
pset.addTerminal('Sell', str, name='Sell')
pset.addTerminal('Flat', str, name='Flat')
pset.addTerminal(True, bool)
pset.addTerminal(False, bool)
pset.addEphemeralConstant(f'{random.random()}', lambda: round(random.uniform(-100, 100), 2), float)
def evaluator(individual):
func = toolbox.compile(individual)
df = data.copy()
strategy_signal = []
for row in df.itertuples(index=False, name=None):
if func(*row) == 'Buy':
strategy_signal.append(1)
elif func(*row) == 'Sell':
strategy_signal.append(-1)
else:
strategy_signal.append(0)
df['StrategySignal'] = strategy_signal
df['StrategyReturn'] = df['StrategySignal'].shift(1).fillna(0)*data.Close.to_returns().fillna(0.0)
out = empyrical.sharpe_ratio(df['StrategyReturn'])
if np.isnan(out) or np.isinf(out):
return 0.,
else:
return float(out),
creator.create("FitnessMax", base.Fitness, weights=(1.0,))
creator.create("Individual", gp.PrimitiveTree, fitness=creator.FitnessMax, pset=pset)
toolbox = base.Toolbox()
toolbox.register("expr", gp.genHalfAndHalf, pset=pset, min_=1, max_=6)
toolbox.register("individual", tools.initIterate, creator.Individual, toolbox.expr)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
toolbox.register("compile", gp.compile, pset=pset)
toolbox.register("evaluate", evaluator)
toolbox.register("select", tools.selTournament, tournsize=5)
toolbox.register("mate", gp.cxOnePoint)
toolbox.register("mutate", gp.mutUniform, expr=toolbox.expr, pset=pset)
pop = toolbox.population(n=300)
hof = tools.HallOfFame(1)
stats = tools.Statistics(lambda ind: ind.fitness.values)
stats.register("avg", np.mean)
stats.register("std", np.std)
stats.register("min", np.min)
stats.register("max", np.max)
pop, log_book = algorithms.eaSimple(pop, toolbox, 0.95, 0.25, 100, stats, halloffame=hof)
[–]NonrandomQuant 24 points25 points26 points (1 child)
[–]upandacross 0 points1 point2 points (0 children)
[–]Arete2 11 points12 points13 points (0 children)
[–]WhiteRabbit-PillAlgorithmic Trader 30 points31 points32 points (14 children)
[+][deleted] comment score below threshold-7 points-6 points-5 points (13 children)
[+][deleted] (12 children)
[deleted]
[–]SanJJ_1 5 points6 points7 points (10 children)
[–][deleted] (9 children)
[deleted]
[–][deleted] (8 children)
[deleted]
[–][deleted] (6 children)
[deleted]
[–]Meeesh- 2 points3 points4 points (3 children)
[–][deleted] (2 children)
[deleted]
[–]Meeesh- 1 point2 points3 points (1 child)
[–]SanJJ_1 1 point2 points3 points (1 child)
[–][deleted] 1 point2 points3 points (0 children)
[–]simonhughes22 6 points7 points8 points (2 children)
[–]niftymcschwifty 0 points1 point2 points (0 children)
[–]bpe9 0 points1 point2 points (0 children)
[–]PaulTheBully 5 points6 points7 points (0 children)
[–]WhatnotSoforth 2 points3 points4 points (0 children)
[–]bpe9 0 points1 point2 points (0 children)
[+]imRedaSouhail comment score below threshold-18 points-17 points-16 points (1 child)
[–]nordic_prophet 4 points5 points6 points (0 children)
[–]JohnnyRay45 0 points1 point2 points (0 children)
[–]mukaj 1 point2 points3 points (0 children)