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

0 投票
1 回答
1934 浏览
提问于 2025-04-18 04:45

我很好奇,如何在有5到6个参数的情况下做到这一点。最终的结果是通过找到最大的值的增加来评估的。

由于我有很多参数,组合的数量看起来非常庞大。但是,我的选择就是用一个for循环吗?

在这个作业中,我一直在使用网格搜索策略(就是用for循环),但我有更多的变量。

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

1 个回答

6

如果你有一个复杂的黑箱函数,需要以非线性的方式调整输入,那么使用遗传算法(GA)可能是个不错的选择。

什么是遗传算法?

简单来说,你可以把参数的一个可能组合用一串二进制位来表示。这就像是你解决方案的“DNA”。比如,你可以给每个值分配8位,参数1用0到7位,参数2用8到15位,依此类推。

接下来,你会创建一个完全随机的解决方案的大群体(比如10,000个),然后用你的函数来评估每一个。之后,每个可能的解决方案会根据它与其他方案的比较得到一个相对的适应度。决定这个适应度的函数叫做适应度函数,它是我们想要优化的目标函数。

根据这个相对适应度,你会选择5,000对解决方案,这样表现好的方案会被选得更多,但表现差的方案有时也能被选中。你会“繁殖”这些对方案,随机选择两个切割点,在二进制DNA字符串上交换部分内容,从而产生两个新的后代,进入下一代。

之后,你会不断重复这个过程,直到你觉得无聊,或者最好的解决方案已经足够好了。

为什么它有效?

一般来说,表现最好的解决方案会被更频繁地组合。通过组合这些方案,可以将不同的成功方案融合在一起,创造出比任何一个父方案都可能更好的新方案。

遗传算法是合适的工具吗?

使用遗传算法的好处是,它会尝试优化你给它的任何东西,而不需要真正理解它。它也不关心你有多少个独立参数。如果你有100个参数,那么嵌套循环就不太适用了。关键在于你的适应度函数评估速度有多快。越快,你每秒能计算的迭代次数就越多。

缺点是,最开始要建立很多机制才能让遗传算法工作,而且得到的解决方案不一定是最优的,也不一定能在特定的时间内得到。

不过在实际应用中,遗传算法在复杂问题上往往表现得相当不错,尤其是当你无法遍历所有可能性时。它是你工具箱里一个不错的工具,几乎可以用在任何事情上。

一个例子:

这里有一个简单的遗传算法示例。上面那段代码是通用的,下面我们解决的问题是找到一种忠诚的动物,它有很多腿,而且不臭也不具攻击性。这用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

撰写回答