遗传算法在交易策略中的应用与强化学习概述
遗传算法状态与求解器
首先,我们定义遗传算法的状态为
GAState
超类型的
case class
:
sealed abstract class GAState(val description: String)
case class GA_FAILED(val _description: String) extends GAState(_description)
object GA_RUNNING extends GAState("Running")
case class GA_NO_CONVERGENCE(val _desc: String) extends GAState(_desc)
GASolver
类负责管理繁殖周期,并评估退出条件或收敛标准:
class GASolver[T <: Gene](config: GAConfig, score: Chromosome[T] => Unit) extends PipeOperator[Population[T], Population[T]] {
var state: GAState = GA_NOT_RUNNING
def |> : PartialFunction[Population[T], Population[T]] = {
case population: Population[T] if(population.size > 1) => {
val reproduction = Reproduction[T](score)
state = GA_RUNNING
Range(0, config.maxCycles).find(n => {
reproduction.mate(population, config, n) match {
case true => converge(population, n) != GA_RUNNING
case false => { …. }
}
}) match {
case Some(n) => population
}
}
}
}
这个繁殖周期由
find
函数控制,它会在应用收敛标准之前,测试繁殖过程中是否出现错误。
交易策略问题
问题是在给定一组交易信号的情况下,找到预测证券价格涨跌的最佳交易策略。交易策略定义为一组交易信号
tsj
,当从金融指标(如证券价格、每日或每周交易量)派生的变量
x = {xj}
超过、等于或低于预定义目标值
αj
时触发。金融专业人士面临两个挑战:
- 选择与给定数据集相关的最小交易信号集(最小化成本或不适应度函数)
- 利用个人经验和专业知识调整这些交易信号
交易类与遗传世界的对应关系
| 通用类 | 对应的证券交易类 |
|---|---|
| Operator | SOperator |
| Gene | Signal |
| Chromosome | Strategy |
| Population | StrategiesFactory |
交易策略的定义
染色体是交易策略的遗传编码,
StrategyFactory
类负责组装交易策略的组件,包括运算符、不适应度函数和信号。
交易运算符
我们通过
SOperator
类扩展
Operator
特质来定义触发信号所需的操作:
class SOperator(val _id: Int) extends Operator {
override def id: Int = _id
override def apply(idx: Int): SOperator = new SOperator(idx)
}
object LESS_THAN extends SOperator(1)
object GREATER_THAN extends SOperator(2)
每个运算符通过
operatorFuncMap
关联一个评分函数,用于计算信号相对于实际值或时间序列的不适应度:
val operatorFuncMap = Map[Operator, (Double, Double) => Double](
LESS_THAN -> ((x: Double, target: Double) => target - x),
GREATER_THAN -> ((x: Double, target: Double) => x - target),
…
)
成本/不适应度函数
考虑一个由两个信号组成的交易策略,用于预测证券价格的突然相对下降
Δp
:
- 相对成交量
vm
,条件为
vm < α
- 相对波动率
vl
,条件为
vl > β
对于
n
个交易时段,成本或不适应度函数
C
如下:
[
C(p, v_m, v_l) = \sum_{t=0}^{n - 1} w_t \left( \left| v_{m,t} - \alpha \right| + \left| v_{l,t} - \beta \right| \right)
]
其中
w = -Δp
。
交易信号
我们通过子类化
Gene
类来定义交易信号:
class Signal(_id: String, _target: Double, _op: Operator, xt: DblVector, weights: DblVector)(implicit discr: Discretization) extends Gene(_id, _target, _op) {
def score: Double = sumScore(operatorFuncMap.get(op).get)
def sumScore(f: (Double, Double) => Double): Double = xt.foldLeft(0.0)((s, x) => s + f(x, target))
}
交易策略
交易策略是交易信号的无序列表,
StrategyFactory
类用于生成交易策略:
class StrategyFactory(nSignals: Int)(implicit discr: Discretization) {
val signals = new ListBuffer[Signal]
lazy val strategies: Pool[Signal] = {
implicit val ordered = Signal.orderedSignals
val xss = new Pool[Signal]
val treeSet = new TreeSet[Signal] ++= signals.toList
val subsetsIterator = treeSet.subsets(nSignals)
while (subsetsIterator.hasNext) {
val subset = subsetsIterator.next
val signalList: List[Signal] = subset.toList
xss.append(Chromosome[Signal](signalList))
}
xss
}
def += (id: String, target: Double, op: Operator, xt: XTSeries[Double], weights: DblVector): Unit = {
signals.append(Signal(id, target, op, xt.toArray, weights))
}
}
orderedSignals
用于对信号进行排序:
val orderedSignals = Ordering.by((signal: Signal) => signal.id)
信号编码
交易谓词的编码是遗传算法中最关键的元素。在我们的示例中,我们将谓词编码为元组(目标值,运算符)。例如,对于
volatility > 0.62
,离散化将
0.62
转换为 32 位,运算符转换为 2 位表示。阈值值通过以下方式转换为整数:
encoding e: (x: Double) => (x * 100000).toInt
decoding d: (x: Int) => x * 1e-5
测试用例
目标是评估在 2008 年秋季股市暴跌期间最相关(最适合)的交易策略。我们以高盛的股票价格作为市场突然下跌的代理。模型使用以下参数:
-
deltaPrice
:两个连续交易时段之间的股票价格变化
-
deltaVolume
:两个连续交易时段之间的成交量相对变化
-
deltaVolatility
:两个连续交易时段之间的波动率相对变化
-
relVolatility
:交易时段内的相对波动率
-
relCloseOpen
:股票开盘价和收盘价的相对差异
执行遗传算法需要以下步骤:
1. 提取模型参数或变量。
2. 生成交易策略的初始种群。
3. 设置遗传算法的配置参数,包括允许的最大繁殖周期数、交叉和变异比率以及种群增长的软限制函数。
4. 使用评分/不适应度函数实例化遗传算法。
5. 提取最能解释高盛股票价格急剧下跌的最适合交易策略。
数据提取
以股票价格变化为例,数据提取代码如下:
val path = "resources/data/chap10/GS.csv"
val src = DataSource(path, false, true, 1)
val price = src |> YahooFinancials.adjClose
val deltaPrice = price.drop(1).zip(price.dropRight(1)).map(p => (1.0 – p._2/p._1))
相对成交量和波动率的提取与股票价格变化的提取类似。
初始种群
val NUM_SIGNALS_PER_STRATEGY = 3
val factory = new StrategyFactory(NUM_SIGNALS_PER_STRATEGY)
factory += ("Delta_volume", 1.1, GREATER_THAN, deltaVolume, deltaPrice)
factory += ("Rel_volatility", 1.3, GREATER_THAN, relVolatility.drop(1), deltaPrice)
val limit = factory.strategies.size
val population = Population[Signal](limit, factory.strategies)
val R = 1024.0
implicit val digitize = new Discretization(R)
配置
val XOVER = 0.2; val MU = 0.6; val MAX_CYCLES = 250
val CUTOFF_SLOPE = -0.003; val CUTOFF_INTERCEPT = 1.003
val softLimit = (n: Int) => CUTOFF_SLOPE * n + CUTOFF_INTERCEPT
val config = GAConfig(XOVER, MUTATE, MAX_NUM_ITERS, softLimit)
GA 实例化
val scoring = (chr: Chromosome[Signal]) => {
val signals: List[Gene] = chr.code
chr.unfitness = signals.foldLeft(0.0)((s, x) => s + x.score)
}
val gaSolver = GASolver[Signal](config, scoring)
GA 执行
val NFITS = 2
val best = gaSolver |> population
best.fittest(NFITS).getOrElse(ArrayBuffer.empty).foreach(ch => Display.show(s"Best: ${ch.toString(" ")}", logger))
测试
我们进行两个测试:
- 评估使用未加权评分函数的遗传算法
- 评估使用加权评分的遗传算法配置
未加权评分
测试使用三组不同的交叉和变异比率:(0.6, 0.2)、(0.3, 0.1) 和 (0.2, 0.6)。最佳交易策略如下:
- 0.6 - 0.2:
Delta_volume > 1.10
,
Rel_close - Open > 0.75
,
Rel_volatility > 0.97
,平均染色体不适应度 = 0.025
- 0.3 - 0.1:
Delta_volatility > 0.9
,
Rel_close - Open < 0.8
,
Rel_volatility > 1.77
,不适应度 = 0.100
- 0.2 - 0.6:
Delta_volatility > 0.9
,
Delta_volume > 33.09
,
Rel_volatility > 1.09
,不适应度 = 0.099
这些最适合的交易策略与初始种群差异不大,可能的原因包括:
- 交易信号的初始猜测较好
- 初始种群规模太小,无法产生遗传多样性
- 测试未考虑股票价格的下跌速度
遗传算法在执行过程中收敛较快,然后趋于稳定。种群规模通过交叉和变异操作增加,直到达到最大的 256 个交易策略。软限制在 23 个交易周期后开始生效。不同交叉和变异比率对遗传算法的执行影响不大,但高交叉比率(0.6)下染色体不适应度得分会波动,有时会导致算法循环使用相同的少数交易策略。
加权评分
使用加权不适应度评分公式的测试产生了有趣的结果。种群规模的变化与未加权不适应度测试类似,但平均染色体不适应度在优化过程中不会稳定,直到种群规模被软限制函数减小。加权函数增加了股票价格下跌速度到不适应度评分中,由于成本/不适应度计算公式的复杂性,增加了遗传算法无法正确收敛的可能性。可能的解决方案包括:
- 使加权函数更简单(加法形式)
- 增加初始种群的规模和多样性
遗传算法的优缺点
遗传算法为科学家提供了一个强大的工具包,可用于优化以下类型的问题:
- 理解不充分的问题
- 可能有多个足够好的解决方案的问题
- 具有离散、不连续和不可微函数的问题
- 可以轻松与规则引擎和知识库集成的问题
- 不需要深入领域知识的问题
- 不需要牛顿 - 拉夫森、共轭梯度或 BFGS 等数值方法的问题
然而,进化计算不适用于以下问题:
- 无法明确定义适应度函数的问题
- 找到全局最小值或最大值至关重要的问题
- 执行时间必须可预测的问题
- 需要实时或准实时提供解决方案的问题
强化学习概述
强化学习广泛应用于游戏和机器人领域。学习分类器系统将强化学习技术与进化计算相结合,是一种有趣的算法,但在机器学习文献中并不常见。在强化学习部分,我们将学习以下内容:
- 强化学习的基本概念
- Q 学习算法的详细实现
- 使用强化学习管理和平衡投资组合的简单方法
- 学习分类器系统的介绍
- 扩展学习分类器的简单实现
学习分类器系统部分主要是信息性的,不包含测试用例。随着第一个自主系统的设计,对传统学习技术的替代需求应运而生。
以下是执行遗传算法的流程图:
graph TD;
A[提取模型参数] --> B[生成初始种群];
B --> C[设置配置参数];
C --> D[实例化遗传算法];
D --> E[执行遗传算法];
E --> F[提取最适合策略];
通过以上内容,我们详细介绍了遗传算法在交易策略中的应用以及强化学习的相关概念,希望能为相关领域的研究和实践提供有价值的参考。
遗传算法在交易策略中的应用与强化学习概述
强化学习基础概念
强化学习是一种通过智能体(agent)与环境进行交互来学习最优行为策略的机器学习方法。在强化学习中,智能体在环境中执行动作,环境会根据智能体的动作返回奖励信号和新的状态。智能体的目标是通过不断尝试和学习,最大化长期累积奖励。
其核心组成部分包括:
-
智能体(Agent)
:执行动作并与环境交互的实体。
-
环境(Environment)
:智能体所处的外部世界,它根据智能体的动作产生新的状态和奖励。
-
动作(Action)
:智能体在环境中可以执行的操作。
-
状态(State)
:描述环境当前情况的信息。
-
奖励(Reward)
:环境根据智能体的动作给予的即时反馈,用于指导智能体学习。
Q - 学习算法实现
Q - 学习是一种无模型的强化学习算法,它通过学习动作价值函数(Q - 函数)来找到最优策略。Q - 函数 $Q(s, a)$ 表示在状态 $s$ 下执行动作 $a$ 的预期累积奖励。
Q - 学习算法的更新公式为:
[
Q(s_t, a_t) \leftarrow Q(s_t, a_t) + \alpha \left[ r_{t + 1} + \gamma \max_{a} Q(s_{t + 1}, a) - Q(s_t, a_t) \right]
]
其中:
- $s_t$ 是当前状态。
- $a_t$ 是当前执行的动作。
- $r_{t + 1}$ 是执行动作 $a_t$ 后获得的奖励。
- $s_{t + 1}$ 是执行动作 $a_t$ 后转移到的新状态。
- $\alpha$ 是学习率,控制每次更新的步长。
- $\gamma$ 是折扣因子,用于权衡即时奖励和未来奖励。
以下是 Q - 学习算法的伪代码:
初始化 Q - 表 Q(s, a) 为 0
对于每个训练周期:
初始化状态 s
当未达到终止状态:
根据 Q - 表选择动作 a(可以使用 $\epsilon$-贪心策略)
执行动作 a,观察奖励 r 和新状态 s'
更新 Q - 表:
Q(s, a) = Q(s, a) + α * [r + γ * max(Q(s', a')) - Q(s, a)]
s = s'
强化学习在投资组合管理中的应用
使用强化学习管理和平衡投资组合的基本思路是将投资组合的状态(如资产价值、持仓比例等)作为状态空间,将买卖资产的操作作为动作空间,将投资组合的收益作为奖励信号。智能体通过与市场环境交互,学习如何调整投资组合以最大化长期收益。
具体步骤如下:
1.
定义状态空间
:包括资产价格、持仓比例、市场指标等信息。
2.
定义动作空间
:如买入、卖出或持有某种资产。
3.
定义奖励函数
:可以是投资组合的收益率、夏普比率等。
4.
选择强化学习算法
:如 Q - 学习、深度 Q 网络(DQN)等。
5.
训练智能体
:通过不断与市场环境交互,更新策略以最大化奖励。
学习分类器系统介绍
学习分类器系统(Learning Classifier Systems,LCS)结合了强化学习技术与进化计算。它由一组分类器(规则)组成,每个分类器包含一个条件部分和一个动作部分。当环境状态满足某个分类器的条件时,该分类器被激活并执行相应的动作。
LCS 的工作流程如下:
1.
匹配
:将当前环境状态与分类器的条件进行匹配,找出所有匹配的分类器。
2.
选择
:从匹配的分类器中选择一个或多个分类器执行动作。
3.
奖励分配
:根据环境返回的奖励,更新分类器的强度和适应度。
4.
发现
:通过遗传算法等进化机制生成新的分类器,以探索更好的策略。
扩展学习分类器的简单实现
扩展学习分类器可以通过引入更多的特征和规则来提高系统的性能。以下是一个简单的扩展学习分类器的实现示例:
// 定义分类器类
class Classifier(condition: String, action: String, strength: Double) {
def matchCondition(state: String): Boolean = {
// 实现条件匹配逻辑
state.contains(condition)
}
def executeAction(): String = action
def updateStrength(reward: Double): Unit = {
// 更新分类器强度
strength += reward
}
}
// 定义学习分类器系统类
class LearningClassifierSystem {
private val classifiers = new ListBuffer[Classifier]()
def addClassifier(classifier: Classifier): Unit = {
classifiers.append(classifier)
}
def run(state: String, reward: Double): String = {
// 匹配分类器
val matchedClassifiers = classifiers.filter(_.matchCondition(state))
// 选择分类器
val selectedClassifier = if (matchedClassifiers.nonEmpty) {
matchedClassifiers.maxBy(_.strength)
} else {
// 如果没有匹配的分类器,随机选择一个动作
new Classifier("", "default_action", 0)
}
// 执行动作
val action = selectedClassifier.executeAction()
// 更新分类器强度
selectedClassifier.updateStrength(reward)
action
}
}
总结
通过本文,我们深入探讨了遗传算法在交易策略中的应用以及强化学习的相关内容。在交易策略中,遗传算法可以帮助我们优化交易信号的选择和调整,提高交易策略的适应性。然而,遗传算法也存在一些局限性,如收敛问题和对初始种群的依赖。
强化学习为我们提供了一种新的解决问题的思路,特别是在游戏、机器人和投资组合管理等领域。学习分类器系统将强化学习与进化计算相结合,为解决复杂问题提供了更强大的工具。
以下是学习分类器系统的工作流程图:
graph TD;
A[环境状态] --> B[匹配分类器];
B --> C{是否有匹配分类器};
C -- 是 --> D[选择分类器];
C -- 否 --> E[随机选择动作];
D --> F[执行动作];
E --> F;
F --> G[获取奖励];
G --> H[更新分类器强度];
H --> I[生成新分类器(可选)];
I --> B;
希望本文能为读者在遗传算法和强化学习领域的学习和实践提供有益的参考。
超级会员免费看
1075

被折叠的 条评论
为什么被折叠?



