寻找股票回测python的最佳参数组合

时间:2014-04-29 14:17:15

标签: python finance

我很好奇如何在一组5-6个参数下做到这一点。在找到最大值增加时评估结果。

由于我拥有的参数数量,组合数量似乎很大。但我的选择只是使用for循环吗?

我一直在构建此分配中的网格搜索策略(仅使用for循环),但还有更多变量。

http://nbviewer.ipython.org/github/cs109/content/blob/master/HW3.ipynb

1 个答案:

答案 0 :(得分:4)

如果您有一个复杂的黑盒功能,并且需要以非线性方式调整输入,那么这听起来似乎是使用遗传算法(GA)的一个很好的应用。

什么是遗传算法?

通常,您可以将一个可能的参数组合编码为一系列位。这将是' DNA'你的解决方案。例如,您可以为每个值分配8位,将参数1分配为位0-7,将参数2分配为位8-15等。

在此之后,您将创建大量完全随机的解决方案(比如10,000),并使用您的函数对每个解决方案进行评估。在此之后,每个可能的解决方案都能获得与其同行的相对适应性。决定此功能的函数称为适应度函数,是我们尝试优化的目标函数。

根据这个相对频率,您可以根据此频率选择5,000对解决方案,以便更频繁地选择最佳解决方案,但即使是最差的也会有时通过。你'繁殖'#39;这些对中的每一对,通过在比特DNA串上挑选两个随机剪切点,并将该部分从一个交换到另一个,以在下一个群体中产生两个新的后代。

在此之后,您会一次又一次地重复整个过程,直到您感到无聊,或者表现最佳的解决方案足够好。

为什么会有效?

一般而言,最佳解决方案会更频繁地合并。组合它们的过程允许将不同的成功解决方案混合在一起,以创建比父母任何一方更好的东西。

GA是否是正确的工具?

使用GA的好处是,它会尝试优化你抛出的任何东西,而不是真正理解它。它也不关心你有多少独立参数。如果你有100个参数,那么嵌套循环真的是不可能的。重要的是你的健身功能评估的速度有多快。它越快,每秒可以计算的迭代次数就越多。

缺点是需要创建大量机器才能使GA首先工作,并且解决方案不能保证最佳,或者在任何特定的时间范围内到达。

然而,在实践中,他们往往在复杂的问题上做得非常好,因为你无法负担搜索整个可能性空间的问题。它是一个很好的工具,可以放在你的工具箱中,因为你几乎可以把它扔到任何东西。

示例:

这是一个简洁,简洁的GA示例。这条线上方的代码是通用的,但是我们在下面解决了找到一条有很多腿的忠诚动物的问题,这不是一种臭味或侵略性。这被编码为4个字节。

import random


class DNA(object):
    score = 0

    def __init__(self, bits):
        self.bits = bits
        self.length = len(bits)

    # https://en.wikipedia.org/wiki/Crossover_%28genetic_algorithm%29#Two-point_crossover
    def two_point_crossover(self, other):
        start = random.randint(0, self.length - 1)
        end = random.randint(start, self.length - 1)

        child_1 = DNA(self.bits[:start] + other.bits[start:end] + self.bits[end:])
        child_2 = DNA(other.bits[:start] + self.bits[start:end] + other.bits[end:])
        return child_1, child_2

    # https://en.wikipedia.org/wiki/Mutation_%28genetic_algorithm%29
    def mutate(self, probability):
        self.bits = [bit if random.random() > probability else not bit for bit in self.bits]

    # Allow us to 'breed' two strings by doing dna_1 * dna_2
    def __mul__(self, other):
        return self.two_point_crossover(other)

    @staticmethod
    def decode_byte(bits):
        out = 0
        for bit in reversed(bits):
            out = out << 1
            out += bit

        return out

    def as_bytes(self):
        return [DNA.decode_byte(self.bits[start:start+8]) for start in xrange(0, self.length, 8)]


class Population(object):
    cumulative_scores = None
    total_score = 0
    best_score = 0
    best_member = None

    def __init__(self, members):
        self.members = members
        self.length = len(members)

    def rate(self, fitness_function):
        self.cumulative_scores = []
        self.total_scores = 0

        for member in self.members:
            score = fitness_function(member)
            member.score = score
            self.total_score += score
            self.cumulative_scores.append((self.total_score, member))

            if score > self.best_score:
                self.best_score = score
                self.best_member = member

    # https://en.wikipedia.org/wiki/Fitness_proportionate_selection
    def roulette_wheel_selection(self):
        pick = random.uniform(0, self.total_score)

        current = 0
        pos = 0

        while current < pick:
            (score, member) = self.cumulative_scores[pos]
            pos += 1
            current = score

        return member

    def mutate(self, mutation_rate):
        for member in self.members:
            member.mutate(mutation_rate)


class GeneticAlgorithm(object):
    def __init__(self, fitness_function, population, mutation_rate=0.01):
        self.population = population
        self.fitness_function = fitness_function
        self.mutation_rate=0.01

    def mutate(self):
        self.population.mutate(self.mutation_rate)

    def rate(self):
        self.population.rate(self.fitness_function)

    def breed(self):
        new_members = []
        while len(new_members) < self.population.length:
            parent_1 = self.population.roulette_wheel_selection()
            parent_2 = self.population.roulette_wheel_selection()

            new_members += parent_1 * parent_2

        self.population = Population(new_members[:self.population.length])


#----------------------------------------#
# My problem here 

def my_fitness(dna):
    angry, legs, smelly, loyalty = dna.as_bytes()

    score = float((510 - angry - smelly) * loyalty * legs) / float(510*255*255)
    return score


pop = Population([DNA([0] * 8 * 4) for _ in range(1000)])
pop.mutate(0.5)     # Totally randomise our starting set
ga = GeneticAlgorithm(my_fitness, pop)

for _ in range(100):
    ga.mutate()
    ga.rate()
    print "Best so far:", ga.population.best_score
    ga.breed()

# Finished!

ga.rate()
angry, legs, smelly, loyalty = ga.population.best_member.as_bytes()

print "Best score: ", ga.population.best_score
print "Angry", angry, "Legs", legs, "Smelly", smelly, "Loyalty", loyalty