金融中的深度学习(二)
原文:
annas-archive.org/md5/1d10c05a97f5e0702eda83b77eec256b译者:飞龙
第四章:深度学习的线性代数与微积分
代数和微积分是数据科学的重要组成部分。机器学习和深度学习算法大部分基于代数和微积分技术。本章以易懂的方式介绍了一些关键主题。
代数是研究运算和关系规则,以及由此产生的构造和思想的学科。代数涵盖了诸如线性方程和矩阵等主题。你可以将代数视为通向微积分的第一步。
微积分是研究曲线斜率和变化率的学科。微积分涵盖了诸如导数和积分之类的主题。它在许多领域如经济学和工程学中被广泛应用。许多学习算法依赖微积分的概念来执行复杂的操作。
两者之间的区别在于,微积分处理变化、运动和累积的概念,而代数处理数学符号以及操纵这些符号的规则。微积分关注于变化函数的特性和行为,而代数则提供了解方程和理解函数的基础。
线性代数
代数包含了各种数学结构,包括数字、变量以及加法、减法、乘法和除法等运算。线性代数是代数的一个基本分支,处理向量空间和线性变换。它在机器学习和深度学习中被广泛用于数据预处理、降维以及解线性方程组等任务。矩阵和向量是线性代数中的核心数据结构,矩阵乘法等操作在各种算法中很常见。
向量和矩阵
一个向量是一个具有大小(长度)和方向(箭头)的对象。向量的基本表示是带有坐标的箭头。但首先,让我们看看什么是轴。
x轴和y轴是垂直线,用于指定平面的边界以及其中不同点的位置,在二维笛卡尔坐标系中。x轴是水平的,y轴是垂直的。
这些轴可以表示向量,其中x轴代表向量的水平分量,y轴代表其垂直分量。
注意
在时间序列分析中,x轴通常是时间步长(小时、天等),而y轴是在相应时间步长(价格、收益等)处的值。
图 4-1 展示了一个简单的二维笛卡尔坐标系,包括两个轴。
二维笛卡尔坐标系使用简单的括号来显示不同点的位置,遵循这个顺序:
-
点的坐标 = (
x,y) -
变量
x代表水平位置 -
变量
y代表水平位置
图 4-1. 二维笛卡尔坐标系
因此,如果你想要画出坐标为 (2, 3) 的点 A,你可能会从零点看一个图表,向右移动两个点,然后向上移动三个点。点的结果应该看起来像图 4-2。
图 4-2. 点 A 在坐标系中的位置
现在让我们添加另一个点,并在它们之间画一个向量。假设你有坐标为 (4, 5) 的点 B。由于点 B 的坐标高于点 A 的坐标,你会预期向量 AB 呈现向上斜坡。图 4-3 显示了新点 B 和向量 AB。
图 4-3. 向量 AB 在大小和方向上连接点 A 和点 B
然而,通过使用两点的坐标画出向量后,你如何引用这个向量呢?简单地说,向量 AB 有其自己的坐标来表示它。请记住,向量是从点 A 到点 B 的移动表示。这意味着沿 x 轴和 y 轴的双点移动就是向量。在数学上,为了找到向量,你应该从彼此的两个坐标点中减去它们,并尊重方向。以下是如何做到这一点的:
-
向量 AB 意味着你从点 A 到点 B;因此,你需要从点 B 的坐标中减去点 A 的坐标:
AB → = 〈 4 - 2 , 5 - 3 〉 AB → = 〈 2 , 2 〉
-
向量 BA 意味着你从点 B 到点 A;因此,你需要从点 A 的坐标中减去点 B 的坐标:
BA → = 〈 2 - 4 , 3 - 5 〉 BA → = 〈 - 2 , - 2 〉
要解释 AB 和 BA 向量,你需要从移动的角度思考。向量 AB 表示从点 A 到点 B,水平和垂直分别移动两个正向点(向右和向上)。向量 BA 表示从点 B 到点 A,水平和垂直分别移动两个负向点(向左和向下)。
注意
尽管 AB 向量和 BA 向量具有相同的斜率,但它们并不是同一物体。那么斜率到底是什么呢?
斜率 是直线上两点之间的垂直变化与水平变化之比。你可以使用以下数学公式计算斜率:
S l o p e = (ΔY) (ΔX) S l o p e o f AB → = 2 2 = 1 S l o p e o f BA → = -2 -2 = 1
如果这两个向量仅仅是线段(没有方向),那么它们将是相同的对象。然而,加上方向分量使它们成为两个可以区分的数学对象。
图 4-4 更详细地解释了斜率的概念,x 向右移动了两个点,y 向左移动了两个点。
图 4-4. 向量 AB 的 x 变化和 y 变化
注意
具有大小为 1 的向量被称为单位向量。
图 4-5 显示了向量 BA 的 x 变化和 y 变化情况。
图 4-5. 向量 BA 的 x 变化和 y 变化
研究人员通常将向量用作速度的表示,尤其是在工程领域。导航是一个严重依赖向量的领域。它允许航海员确定其位置并规划其目的地。自然地,大小表示速度,方向表示目的地。
您可以相互添加和减去向量,也可以与标量相加和减去向量。这允许在方向和大小上进行移动。您应该从前面的讨论中了解到,向量指示轴上不同点之间的方向。
注意
标量是具有大小但没有方向的值。与向量相反,标量用于表示元素,如温度和价格。基本上,标量就是数字。
矩阵是一个包含数字并以行和列组织的矩形数组。[¹] 矩阵在计算机图形学和其他领域中都很有用,用来定义和操作线性方程组。矩阵与向量的区别是什么?最简单的答案是,向量是具有单列或单行的矩阵。这里是一个 3 × 3 矩阵的基本示例:
5 2 9 - 8 10 13 1 5 12
矩阵的大小是它包含的行数和列数。行是水平线,列是垂直线。以下是一个 2 × 4 矩阵的表示(即两行四列):
5 2 1 3 - 8 10 9 4
以下是一个 4 × 2 矩阵的表示(即四行两列):
5 2 - 8 10 8 22 7 3
注意
矩阵在机器学习中被广泛使用。行通常表示时间,列表示特征。
不同矩阵的加法很简单,但只能在矩阵大小相同时使用(即它们具有相同的列数和行数)。例如,让我们来加下面这两个矩阵:
1 2 5 8 + 3 9 1 5 = 4 11 6 13
你可以看到,要将两个矩阵相加,只需将相同位置的数字相加即可。现在,如果你试图添加下一对矩阵,你将无法做到,因为要添加的内容不匹配:
8 3 3 2 + 3 9 1 5 5 4
矩阵的减法也很简单,遵循与矩阵加法相同的规则。让我们来看一个例子:
5 2 - 8 10 - 3 9 - 1 - 5 = 2 - 7 - 9 15
显然,矩阵的减法也是矩阵加法,只是其中一个矩阵中的数字变为相反数。
标量乘以矩阵非常简单。让我们看一个例子:
3 × 5 2 8 22 = 15 6 24 66
因此,基本上是将矩阵中的每个单元格乘以标量。将一个矩阵乘以另一个矩阵稍微复杂一些,因为它使用点积方法。首先,要将两个矩阵相乘,它们必须满足以下条件:
Matrix xy × Matrix yz = Matrix xz
这意味着第一个矩阵的列数必须等于第二个矩阵的行数,并且点积的结果矩阵是第一个矩阵的行数和第二个矩阵的列数。点积在以下示例中表示为 1 × 3 和 3 × 1 矩阵乘法的例子(注意列数和行数相等):
1 2 3 × 3 2 1 = ( 1 × 3 ) + ( 2 × 2 ) + ( 3 × 1 ) = 10
现在让我们来看一个 2 × 2 矩阵乘法的例子:
1 2 0 1 × 3 0 2 1 = 7 2 2 1
有一种特殊类型的矩阵称为单位矩阵,它基本上是矩阵的数字 1。对于 2 × 2 维度,它定义如下:
I = 1 0 0 1
对于 3 × 3 维度如下:
I = 1 0 0 0 1 0 0 0 1
将任何矩阵乘以单位矩阵会产生相同的原始矩阵。这就是为什么它可以被称为矩阵的单位元(将任何数字乘以 1 会得到相同的数字)。值得注意的是矩阵乘法不是可交换的,这意味着乘法顺序改变结果:
A B ≠ B A
矩阵转置是一个过程,涉及将行变为列,反之亦然。矩阵的转置是通过沿其主对角线反射矩阵而获得的:
461142 T = 4 1 6 4 1 2
在一些机器学习算法中使用了转置操作,处理这些模型时不是一个罕见的操作。如果你想了解矩阵在数据科学和机器学习中的角色,可以参考这个非详尽的列表:
数据表示
矩阵通常用来表示数据,其中行代表样本,列代表特征。例如,矩阵中的一行可以表示一个时间步中的 OHLC 数据。
线性代数
矩阵和线性代数是紧密相关的,许多学习算法在其操作中使用了矩阵的概念。对这些数学概念有基本的理解有助于在处理机器学习算法时平滑学习曲线。
数据关系矩阵
协方差和相关性度量通常表示为矩阵。这些关系计算是时间序列分析中的重要概念。
注意
本节的关键要点如下:
-
向量是一个具有大小(长度)和方向(箭头)的对象。多个向量组合在一起形成一个矩阵。
-
矩阵可以用来存储数据。它有其特殊的执行操作方式。
-
矩阵乘法使用点积方法。
-
转置矩阵意味着交换其行和列。
线性方程介绍
你在“回归分析和统计推断”中看到了线性方程的一个例子。线性方程基本上是介绍不同变量和常数之间平等关系的公式。在机器学习中,它通常是依赖变量(输出)和自变量(输入)之间的关系。理解线性方程的最佳方法是通过示例。
注意
线性方程的目标是找到一个未知变量,通常用字母x表示。
我们将从一个非常基础的例子开始,你可以将其视为以后将要看到的更高级概念的第一个构建块。 下面的例子要求找到满足方程的x的值:
10 x = 20
你应该理解方程为“10 乘以哪个数等于 20?” 当一个常数直接附加到变量(比如x)上时,它指的是一个乘法操作。现在,要解出x(即找到使方程相等的x的值),你有一个明显的解决方案,就是去掉 10,这样你就在方程的一边得到x,另一边得到剩下的部分。
自然地,要摆脱 10,你需要除以 10,这样剩下的就是 1,如果与变量x相乘则无效。 但请记住两件重要的事情:
-
如果你在方程的一边进行数学运算,那么你必须在另一边也这样做。 这就是它们被称为方程的原因。
-
为了简单起见,不要除以常数以摆脱它,你应该乘以它的倒数。
一个数的倒数是 1 除以该数。 这是它的数学表示:
R e c i p r o c a l ( x ) = 1 x
现在,回到例子中,要找到x,你可以这样做:
( 1 10 ) 10 x = 20 ( 1 10 )
进行乘法并简化后得到以下结果:
x = 2
这意味着方程的解为 2。 要验证这一点,你只需将 2 插入到原始方程中如下所示:
10 × 2 = 20
因此,需要两个 10 才能得到 20。
注意
将数字除以它本身等同于乘以它的倒数。
让我们来看一个如何通过线性技术解决x的例子。 考虑以下问题:
8 6 x = 24
进行乘法并简化后得到以下结果:
( 6 8 ) 8 6 x = 24 ( 6 8 )
x = 18
这意味着方程的解为 18。 要验证这一点,你只需将 18 插入到原始方程中如下所示:
8 6 × 18 = 24
通常,线性方程不会这么简单。 有时它们包含更多的变量和常数,需要更详细的解决方案,但让我们一步一步地进行。 考虑以下例子:
3 x - 6 = 12
解出x需要稍微重新排列方程。 记住,目标是将x留在一边,其余部分留在另一边。 在这里,你必须在处理 3 之前去掉常数 6。 解决方案的第一部分如下:
3 x - 6 ( + 6 ) = 12 ( + 6 )
注意如何在方程的两边都加上 6。左边的部分将自行抵消,而右边的部分将加起来得到 18:
3 x = 18
最后,你需要将变量x附加的常数的倒数相乘:
( 1 3 ) 3 x = 18 ( 1 3 )
简化并解出x得到以下解决方案:
x = 6
这意味着方程的解为 6。 要验证这一点,只需将 6 插入到原始方程中如下所示:
( 3 × 6 ) - 6 = 12
到目前为止,你应该已经注意到,线性代数主要是关于使用快捷和快速的技巧简化方程并找到未知变量。下一个例子显示有时变量x可能出现在多个地方:
6 x + x = 27 - 2 x
记住,主要重点是让x在方程的一边,其余部分在另一边:
6 x + x + 2 x = 27
在x的常数上加给你以下结果:
9 x = 27
最后一步是除以 9,这样你就只剩下x:
x = 3
现在,你可以通过在原方程中将 3 代入x的位置来验证这一点。你会注意到方程两边是相等的。
注意
尽管本节内容很简单,但它包含了你在代数和微积分中开始进阶所需的基础。本节的关键要点如下:
-
线性方程是一种表示,其中任何变量的最高指数为一。这意味着没有变量被提升到二次及以上的幂次。
-
在图表上绘制时,线性方程的直线是直的。
-
在建模各种真实世界事件中应用线性方程的重要性使其在许多数学和研究领域至关重要。它们在机器学习中也被广泛应用。
-
解决x的过程是找到一个值,使得方程两边相等。
-
当对方程的一侧进行操作(例如加上常数或乘以常数)时,你也必须对另一侧进行相同的操作。
方程组
方程组是指有两个或更多个方程共同工作来解决一个或多个变量的情况。因此,不再是通常的单一方程:
x + 10 = 20
方程组类似于以下形式:
x + 10 = 20
y + 2 x = 10
方程组在机器学习中非常有用,广泛应用于其许多方面。
让我们从本节开头的方程组出发,通过图形化方法来解决它。绘制这两个函数实际上可以直接给出解决方案。交点就是解决方案。因此,交点的坐标(x, y)分别指的是x和y的解决方案。
从图 4-6 可以看出,x = 10 而y = –10。将这些值代入各自的变量后得到正确答案:
10 + 10 = 20
(–10) + (2 × 10) = 10
![###### 图 4-6. 显示两个函数及其交点(解决方案)由于函数是线性的,解决它们可能导致三种结果之一:1. 每个变量只有一个解决方案。1. 不存在解决方案。这发生在函数平行时(这意味着它们从未相交)。1. 存在无数个解。当通过简化时,两个函数相同时(因为所有点都落在直线上)。在通过代数解方程组之前,让我们通过视觉方式看看如何没有解和如何有无限多个解。考虑以下系统:2 x = 104 x = 20图 4-7 将它们一起绘制。由于它们正好是同一方程,它们落在同一条线上。实际上,图 4-7 中有两条线,但由于它们相同,它们是无法区分的。对于线上的每个x,都有一个对应的y。
图 4-7. 显示两个函数及其无限交点的图表
现在考虑以下系统:
3 x = 10
6 x = 10
图 4-8 显示它们永不相交,这是直观的,因为你不能用不同的数字(由变量x表示)乘以同一个数字而期望得到相同的结果。
图 4-8. 显示两个函数及其不可能的交点的图表
代数方法用于解决超过两个变量的情况,因为它们无法通过图表求解。主要包括两种方法:替换法和消元法。
替换法 用于可以在一个方程中替换变量值并将其插入第二个方程的情况。考虑以下例子:
x + y = 2
10 x + y = 10
最简单的方法是重新排列第一个方程,使得你可以用x来表示y:
y = 2 - x
10 x + ( 2 - x ) = 10
在第二个方程中解x变得很简单:
10 x + ( 2 - x ) = 10 10 x + 2 - x = 10 10 x - x = 10 - 2 9 x = 8 x = 8 9 x = 0 . 8889
现在你已经找到x的值,通过将x的值代入第一个方程中,你可以轻松找到y的值:
0 . 8889 + y = 2 y = 2 - 0 . 8889 y = 1 . 111
要检查你的解是否正确,可以在两个公式中分别代入x和y的值:
0 . 8889 + 1 . 111 = 2 ( 10 × 0 . 8889 ) + 1 . 111 = 10
从图形上看,这意味着两个方程在(0.8889, 1.111)处相交。这种技术可以用于多于两个变量。按照相同的过程进行,直到方程简化到足以给出答案。替换法的问题在于处理超过两个变量时可能需要一些时间。
消元法 是一个更快的替代方法。它涉及消除变量,直到只剩下一个。考虑以下例子:
2 x + 4 y = 20 3 x + 2 y = 10
注意到有 4y和 2y,可以将第二个方程乘以 2,这样你可以将两个方程相减(从而消除y变量):
2 x + 4 y = 20 6 x + 4 y = 20
将两个方程相减得到以下结果:
- 4 x = 0 x = 0
因此,x = 0. 从图形上看,这意味着它们在x = 0 时相交(正好在垂直的y线上)。将x的值代入第一个公式中得到y = 5:
( 2 × 0 ) + 4 y = 20 4 y = 20 y = 5
类似地,消元法也可以解决三个变量的方程。选择替换法或消元法取决于要解决的方程类型。
注意
本节的主要收获如下:
-
方程组同时解决变量。它们在机器学习中非常有用,被一些算法使用。
-
对于简单的方程组,图形解法是首选。
-
通过代数解方程组涉及使用代入和消元方法。
-
当系统比较简单时,推荐使用代入法,但当系统稍微复杂时,则使用消元法。
三角学
三角学探索被称为三角函数的行为,这些函数将三角形的角度与其边长关联起来。最常用的三角形是直角三角形,其中一个角为 90°。图 4-9 展示了一个直角三角形的例子。
图 4-9. 直角三角形
让我们定义直角三角形的主要特征:
-
三角形的最长边称为斜边。
-
斜边对面的角是直角(90°的那个角)。
-
根据选择的另一个角(θ)(剩下的两个角之一),连接这个角和斜边的线称为邻边,另一条线称为对边。
注意
三角函数是数学函数,用于将直角三角形的角度与其边长的比率关联起来。它们在几何学、物理学、工程学等领域有各种应用。它们帮助分析和解决与角度、距离、振荡和波形等相关的问题。
三角函数简单来说就是三角形的一条线除以另一条线的比值。记住三角形有三条边(斜边、对边和邻边)。三角函数如下找到:
s i n ( θ ) = Opposite Hypotenuse
c o s ( θ ) = Adjacent Hypotenuse
t a n ( θ ) = Opposite Adjacent
从前面的三个三角函数中,可以通过基本的线性代数得到一个三角恒等式,通过sin和cos可以得到tan:
t a n ( θ ) = sin(θ) cos(θ)
双曲函数类似于三角函数,但是使用指数函数来定义。在理解双曲函数之前,必须了解欧拉数。
注意
这部分关于双曲函数的内容很有趣,因为它构成了所谓的激活函数的基础,这是神经网络中的一个关键概念,深度学习模型的主角。您将在第八章中详细了解它们。
欧拉数(记为e)是数学中最重要的数之一。它是一个无理数,即不能用分数表示的实数。无理一词来自于无法用比率来表示它的事实;这与其性质无关。欧拉数也是自然对数ln的底数,其前几位数字为 2.71828。一个最佳的近似值公式如下:
e = (1+1 n) n
通过在前述公式中增加n,您将接近e的值。欧拉数有许多有趣的性质,最显著的是其斜率等于其自身的值。考虑以下函数(也称为自然指数函数):
f ( x ) = e x
在任何点,函数的斜率都是相同的值。看看图 4-10。
图 4-10. 自然指数函数的图表
注意
你可能会想知道为什么我在这本书中解释指数和对数。主要有两个原因:
-
指数和更重要的是欧拉数在双曲函数中被使用,其中 tanh(x) 是神经网络中的主要激活函数,一种机器学习和深度学习模型。
-
对数在损失函数中非常有用,这是你在后面章节中会看到的概念。
双曲函数使用自然指数函数,并定义如下:
s i n h ( x ) = e x -e -x 2
c o s h ( x ) = e x +e -x 2
t a n h ( x ) = e x -e -x e x +e -x
tanh(x) 的主要特征包括非线性、限制在 [–1, 1] 之间,并且其以零为中心。图 4-11 显示了 tanh(x) 的图表。
图 4-11. 显示了 tanh(x) 的图表,显示它在 –1 和 1 之间的限制。
注意
本节的主要要点如下:
-
三角学是一个研究三角函数行为的领域,它将三角形的角度与其边长联系起来。
-
三角恒等式是将三角函数相互关联的快捷方式。
-
欧拉数 e 是无理数,是自然对数的底数。它在指数增长和双曲函数中有许多应用。
-
双曲正切函数在神经网络中,一种深度学习算法中被使用。
微积分
正如前面提到的,微积分是数学的一个分支,专注于速率变化和数量积累的研究。它包括两个主要分支:微分学(处理导数)和积分学(处理积分)。本节简要介绍了这两种微积分类型,同时讨论了诸如极限和优化等主题。
极限与连续性
微积分通过使微不足道的小量显现出来而起作用。
—Keith Devlin
极限并不一定是噩梦。我一直发现人们对它们有所误解。实际上,它们非常容易理解。但首先,你需要动力,这源于了解学习极限的附加价值。
理解极限对于机器学习模型来说是非常重要的,原因有很多:
优化
在像梯度下降这样的优化方法中,极限可以用来调节步长并保证收敛到局部最小值。
特征选择
极限可以用来排列各种模型特征的重要性并进行特征选择,这可以使模型更简单且性能更好。
敏感性分析
机器学习模型对输入数据变化的敏感性以及其泛化到新数据的能力可以用来检验模型的行为。
同样,极限被用于你即将学习的更高级的微积分概念中。
极限的主要目的是在函数未定义时知道函数的值。但是什么是未定义函数?当您有一个给出不可能的解决方案的函数(例如除以零)时,极限帮助您绕过此问题以了解该点处函数的值。因此,极限的目的是即使在函数未定义时也能解决函数。
请记住,将 x 作为输入的函数的解是 y 轴上的一个值。 图 4-12 展示了以下函数的线性图形:
f ( x ) = x + 2
图 4-12. 函数 f(x) = x + 2 的图形
图形中函数的解是每次考虑 x 的值时位于线性线上的解。
当 x = 4 时,函数(y 的值)的解将是什么?显然,答案是 6,因为将 x 的值替换为 4 给出 6:
f ( 4 ) = 4 + 2 = 6
将此解释视为极限的解决方案就像询问函数在 x 从两侧(负/递减侧和正/递增侧)接近 4 时的解决方案一样。 表 4-1 简化了这个困境。
表 4-1. 找到接近 4 的 x
| f(x) | x |
|---|---|
| 5.998 | 3.998 |
| 5.999 | 3.999 |
| 6.000 | 4.000 |
| 6.001 | 4.001 |
| 6.002 | 4.002 |
从负侧接近相当于在 4 以下添加一个数字的分数并每次分析结果。类似地,从正侧接近相当于在 4 以上减去一个数字的分数并每次分析结果。随着 x 接近 4,解似乎收敛到 6。这是极限的解。
一般形式的极限按照以下约定编写:
lim x→a f ( x ) = L
极限的一般形式读作:当您沿着 x 轴接近 a(无论是从正侧还是从负侧),函数 f(x) 都会趋于值 L。
注意
极限的概念表明,当您从任一侧(负或正)锁定并接近一个数字时,方程的解会接近某个数字,而极限的解就是那个数字。
如前所述,当使用传统的代入方法无法确定解的确切位置时,极限就变得很有用。
单侧极限与一般极限不同。对于左侧极限,您从负侧向正侧寻找极限,而对于右侧极限,您从正侧向负侧寻找极限。当两个单侧极限存在且相等时,一般极限存在。因此,前述陈述可以总结如下:
-
左侧极限存在。
-
右侧极限存在。
-
左侧极限等于右侧极限。
左侧极限定义如下:
lim x→a - f ( x ) = L
右侧极限定义如下:
lim x→a + f ( x ) = L
考虑以下方程:
f ( x ) = x 3 -27 x-3
当 x = 3 时,函数的解决方案是什么?代入得出以下问题:
f ( 3 ) = 3 3 -27 3-3 = 27-27 3-3 = 0 0 = Undefined
然而,从 表 4-2 显示的极限的角度来看,当从左侧或右侧逼近 x = 3 时,解决方案趋近于 27。
表 4-2. 寻找 x 逼近 3 的值
| f(x) | x |
|---|---|
| 2.9998 | 26.9982 |
| 2.9999 | 26.9991 |
| 3.0000 | 未定义 |
| 3.0001 | 27.0009 |
| 3.0002 | 27.0018 |
从图形上看,这可以看作是图表在两个轴上的不连续性。这种不连续性存在于坐标 (3, 27) 附近的线上。一些函数没有极限。例如,当 x 接近 5 时,以下函数的极限是多少?
lim x→5 1 x-5
查看 表 4-3,当 x 接近 5 时,从两边逼近结果显著分离。例如,从负侧逼近,4.9999 的极限是 –10,000,从正侧逼近,5.0001 的极限是 10,000。
表 4-3. 寻找 x 逼近 5 的值
| f(x) | x |
|---|---|
| 4.9998 | –5000 |
| 4.9999 | –10000 |
| 5.0000 | 未定义 |
| 5.0001 | 10000 |
| 5.0002 | 5000 |
要记住,一般极限存在的条件是单侧极限必须存在且相等,但在这里并非如此。通过绘图可以看出,这为 图 4-13 说明了为何极限不存在。
图 4-13. 证明极限不存在的函数图表
但如果你想分析的函数看起来像这样:
lim x→5 1 |x-5|
查看 表 4-3,当 x 接近 5 时,结果在逼近时快速加速到被称为无穷大 (∞) 的非常大的数值:
f ( x ) = 1 |x-5|
查看 表 4-4:
表 4-4. 另一种寻找 x 逼近 5 的尝试
| f(x) | x |
|---|---|
| 4.99997 | 334333.33 |
| 4.99998 | 50000 |
| 4.99999 | 100000 |
| 4.9999999 | 10000000 |
| 5.00000 | 未定义 |
| 5.0000001 | 10000000 |
| 5.00001 | 100000 |
| 5.00002 | 50000 |
| 5.00003 | 334333.33 |
在每一个微小的步骤中,x 接近 5,y 也逐渐接近正无穷大。因此极限的答案是正无穷大(+∞)。 图 4-14 显示了函数的图表。请注意,当 x 接近 5 时,两边的值都在上升。
图 4-14. 证明极限存在的函数图表
连续 函数是在图表中没有间隙或空洞的函数,而 不连续 函数则包含这些间隙和空洞。这通常意味着后者包含函数未定义的点,并可能需要通过极限进行近似。因此,连续性和极限是两个相关的概念。
让我们继续解决极限问题;毕竟,你不会每次都创建表格并主观分析结果以找到极限。解决极限有三种方法:
-
替换:这是最简单的规则,通常首先使用。
-
因式分解:这是替换失败后的下一步。
-
共轭方法:这个解决方案是在前两个方法不起作用之后的选择。
替换 简单地将x接近的值代入。基本上,这些是使用极限的函数解。看下面的例子:
lim x→5 x + 10 - 2 x
使用替换,函数的极限如下找到:
lim x→5 x + 10 - 2 x = 5 + 10 - ( 2 × 5 ) = 5
因此,极限的答案为 5。
因式分解 是替换不起作用时的下一步选择(例如,将x的值代入函数后,极限未定义)。 因式分解 是通过使用因子以改变方程的形式的方法,使得在使用替换时方程不再是未定义的。看下面的例子:
lim x→-6 (x+6)(x 2 -x+1) x+6
如果尝试替换,将得到一个未定义的值如下:
lim x→-6 (x+6)(x 2 -x+1) x+6 = (-6+6)((-6) 2 -(-6)+1) -6+6 = 0 0 = Undefined
在这种情况下,因式分解可能有所帮助。例如,分子乘以(x + 6),然后除以(x + 6)。通过取消两个项来简化这个过程可能会得到一个解:
lim x→-6 (x+6)(x 2 -x+1) x+6 = lim x→-6 x 2 - x + 1
现在因式分解已完成,您可以再次尝试替换:
lim x→-6 x 2 - x + 1 = (-6) 2 - ( - 6 ) + 1 = 43
函数在x趋向于-6 时的极限因此为 43。
当替换和因式分解都不起作用时,形成共轭是下一步的选择。 共轭 是通过简单地改变两个变量之间的符号来形成的。例如,x + y 的共轭是 x - y。在分数的情况下,通过用其中一个的共轭来乘分子和分母(最好使用具有平方根的项的共轭,因为它将被取消)。考虑以下例子:
lim x→9 x-9 x-3
通过将分母的共轭乘以两个项,您将开始使用共轭方法来解决问题:
lim x→9 x-9 x-3 ( x+3 x+3 )
考虑乘法然后简化得到如下结果:
lim x→9 (x-9)(x+3) (x-3)(x+3)
您将得到以下熟悉的情况:
lim x→9 (x-9)(x+3) x-9
lim x→9 x + 3
现在函数已准备好进行替换:
lim x→9 9 + 3 = 3 + 3 = 6
因此,函数的解为 6。如您所见,有时需要对方程进行处理,然后才能进行替换。
注意
本节的关键要点如下:
-
极限帮助找到在某些点可能未定义的函数的解。
-
对于一般的极限存在,两个单侧极限必须存在且必须相等。
-
有多种方法可以找到函数的极限,尤其是替换、因式分解和形成共轭。
导数
导数 衡量给定一个或多个输入变化时函数的变化。换句话说,它是给定点上函数变化率。
在构建机器学习模型中,对导数有深刻的理解是重要的,有多个原因:
优化
要使损失函数最小化,优化方法利用导数确定最陡下降的方向并修改模型的参数。
反向传播
在深度学习中执行梯度下降时,反向传播技术利用导数计算损失函数对模型参数的梯度。
超参数调优
为了提升模型性能,导数用于敏感性分析和超参数调优。
不要忘记前一节关于极限的知识,因为在本节中你同样会需要这些知识。微积分主要涉及导数和积分。本节讨论了导数及其用途。
你可以将导数视为表示(或建模)另一函数在某点斜率的函数。斜率是一条线相对于水平线的位置的度量。正斜率表示线上升,而负斜率表示线下降。
导数和斜率是相关的概念,但它们并不完全相同。以下是两者的主要区别:
斜率
斜率衡量了一条线的陡峭程度。它是y轴变化与x轴变化的比率。
导数
导数描述了给定函数的变化率。当函数上两点之间的距离趋近于零时,该函数在该点的导数就是切线斜率的极限。
在解释导数的通俗术语及展示一些例子之前,让我们先看看它们的形式定义:
f ' ( x ) = lim h→0 f(x+h)-f(x) h
这个方程构成了求解导数的基础,尽管你将会学到许多简化的捷径。让我们尝试使用形式化定义找到函数的导数。考虑以下方程:
f ( x ) = x 2 + 4 x - 2
要找到导数,将*f(x)*代入形式化定义,然后解决极限:
f ' ( x ) = lim h→0 f(x+h)-f(x) h
为了简化问题,让我们找到f(x + h),这样插入形式化定义会更容易:
f ( x + h ) = (x+h) 2 + 4 ( x + h ) - 2
f ( x + h ) = x 2 + 2 x h + h 2 + 4 x + 4 h - 2
现在让我们将*f(x + h)*代入定义中:
f ' ( x ) = lim h→0 x 2 +2xh+h 2 +4x+4h-2-x 2 -4x+2 h
注意看有多少术语可以简化,这样公式会更清晰。记住,你现在是在寻找极限,而求导则是在解决了极限之后进行的:
f ' ( x ) = lim h→0 2xh+h 2 +4h h
通过除以h,进一步简化的潜力得以释放,因为你可以将分子中的所有项除以分母h:
f ' ( x ) = lim h→0 2 x + h + 4
现在是时候解决极限了。由于方程简单,第一次尝试是通过代入求解,正如你所猜测的那样,这是可行的。通过代入变量h并使其趋向于零(根据极限),你得到如下结果:
f ' ( x ) = 2 x + 4
这就是原始函数f(x)的导数。如果你想找到函数在x = 2 时的导数,只需将 2 代入导数函数即可:
f ' ( 2 ) = 2 ( 2 ) + 4 = 8
图 4-15 展示了原始函数的图像及其导数(直线)。注意f'(2)恰好在 8 处。当x = 2 时,*f(x)*的斜率为 8。
图 4-15. 原始函数f(x)及其导数f'(x)
注意
注意当*f(x)*触底并开始上升时,*f'(x)*会穿过零线。
每次你想要找到导数时,可能不会使用形式化定义。有导数规则可以通过快捷方式节省大量时间。第一个规则称为幂规则,是找到具有指数的函数的导数的一种方法。
使用这种符号引用导数也很常见(这与*f'(x)*是一样的):
dy dx
寻找导数的幂规则如下:
dy dx ( a x n ) = ( a . n ) x n-1
基本上,这意味着导数是通过将常数乘以指数然后从指数中减去 1 来找到的。这里有一个例子:
f ( x ) = x 4
f ' ( x ) = ( 1 × 4 ) x (4-1) = 4 x 3
请记住,如果变量没有附加常数,这意味着该常数等于 1。这里有一个具有相同原则的更复杂的例子:
f ( x ) = 2 x 2 + 3 x 7 - 2 x 3
f ' ( x ) = 4 x + 21 x 6 - 6 x 2
值得注意的是,即使常数不符合幂规则的一般形式,该规则也适用于常数。常数的导数为零。虽然知道为什么有所帮助,但首先你必须了解以下数学概念:
x 0 = 1
话虽如此,你可以想象常数总是乘以x的零次幂(因为这样做不会改变它们的值)。现在,如果你想要找到 17 的导数,就像这样:
17 = 17 x 0 = ( 0 × 17 ) x 0-1 = 0 x -1 = 0
正如你所知,任何乘以零的东西返回零作为结果。这为常数导数规则提供了如下定义:
dy dx ( a ) = 0
遇到分数或负数指数时,你要遵循相同的逻辑。
导数的乘积规则在两个函数相乘时非常有用。乘积规则如下:
dy dx [ f ( x ) g ( x ) ] = f ' ( x ) g ( x ) + f ( x ) g ' ( x )
让我们举个例子,并使用乘积法则找到导数:
h ( x ) = ( x 2 + 2 ) ( x 3 + 1 )
方程式可以明显地分成两个项,f(x)和g(x),如下所示:
f ( x ) = ( x 2 + 2 )
g ( x ) = ( x 3 + 1 )
在应用乘积法则之前,我们来找出这两个术语的导数。请注意,一旦理解了幂规则,找到*f(x)和g(x)*的导数就很容易:
f ' ( x ) = 2 x
g ' ( x ) = 3 x 2
在应用乘积规则时,你应该得到以下结果:
h ' ( x ) = ( x 2 + 2 ) ( 3 x 2 ) + ( 2 x ) ( x 3 + 1 )
h ' ( x ) = 3 x 4 + 6 x 2 + 2 x 4 + 2 x
h ' ( x ) = 5 x 4 + 6 x 2 + 2 x
图 4-16 显示了*h(x)和h'(x)*的图形。
图 4-16. 原始h(x)及其导数h'(x)
现在让我们将注意力转向商规则,它处理两个函数的除法。形式定义如下:
dy dx [ f(x) g(x) ] = f ' (x)g(x)-f(x)g ' (x) [g(x)] 2
让我们将其应用到以下函数中:
f ( x ) = x 2 -x+1 x 2 +1
通常,最好先找到*f(x)和g(x)*的导数,在这种情况下,它们明显是分开的,*f(x)*是分子,*g(x)*是分母。在应用商规则时,你应该得到以下结果:
f ' ( x ) = (2x-1)(x 2 +1)-(x 2 -x+1)(2x) (x 2 +1) 2
f ' ( x ) = 2x 3 +2x-x 2 -1-2x 3 +2x 2 -2x (x 2 +1) 2
f ' ( x ) = x 2 -1 (x 2 +1) 2
指数导数处理应用于常数的幂规则。看看下面的方程式。你会如何找到它的导数?
f ( x ) = a x
不同于通常的变量-基数-常量-指数,它是常量-基数-变量-指数。在尝试计算导数时,这种处理方式有所不同。形式化定义如下:
dy dx a x = a x ( ln a )
下面的示例显示了如何做到这一点:
dy dx 4 x = 4 x ( ln 4 )
之前提到的欧拉数有一个特殊的导数。当涉及到找到e的导数时,答案很有趣:
dy dx e x = e x ( ln e ) = e x
这是因为自然对数函数和指数函数是彼此的反函数,所以术语ln e等于 1。因此,指数函数e的导数就是它本身。
与此同时,让我们讨论一下对数导数。到现在为止,你应该知道指数和对数是什么。两种对数的一般定义如下:
dy dx log a x = 1 xlna
dy dx ln x = log e x = 1 xlne = 1 x
注意在自然对数的二阶导函数中,再次遇到ln e项,因此使得简化变得非常容易,因为它等于 1。
以以下示例为例:
f ( x ) = 7 l o g 2 ( x )
使用正式定义,这个对数函数的导数如下:
f ' ( x ) = 7 ( 1 xln2 ) = 7 xln2
注意
对数log的基数是 10,但自然对数ln的基数是e(~2.7182)。
自然对数和对数函数实际上是通过简单的乘法线性相关的。如果你知道常数a的对数,你可以通过将a的对数乘以 2.4303 来找到它的自然对数ln。
导数中的一个主要概念是链式法则。让我们回到幂规则,它处理变量的指数。记住以下公式找到导数:
dy dx ( a x n ) = ( a . n ) x n-1
这是一个简化的版本,因为只有x,但现实是你必须乘以幂指数下面的项的导数。到目前为止,你只看到x作为幂指数下的变量。x的导数是 1,这就是为什么它被简化和隐藏的原因。然而,对于更复杂的函数,比如这个:
f ( x ) = (4x+1) 2
通过以下两个步骤找到函数的导数:
-
找到外部函数的导数而不触及内部函数。
-
找到内部函数的导数并将其乘以函数的其余部分。
因此解决方案如下(知道4x + 1 的导数只是 4):
f ' ( x ) = 2 ( 4 x + 1 ) . 4
f ' ( x ) = 8 ( 4 x + 1 )
f ' ( x ) = 32 x + 8
指数函数也是如此。接下来以以下示例为例:
f ( x ) = e x
f ' ( x ) = e x ( 1 ) = e x
链式法则实际上可以被认为是一条总规则,因为它适用于任何地方,即使是在乘积法则和商法则中也是如此。
在求导数中有更多的概念需要掌握,但是由于本书不打算成为完整的微积分大师课程,你至少应该了解导数的含义,如何找到它,它代表什么以及在机器学习和深度学习中如何使用它。
注意
本节的关键点如下:
-
导数衡量给定一个或多个输入变化的函数的变化。
-
幂规则用于找到函数的幂的导数。
-
乘积法则用于找到两个相乘的函数的导数。
-
商法则用于找到两个相除的函数的导数。
-
链式法则是不同 iating 中使用的主要规则(这意味着找到导数的过程)。由于简单性,它经常被忽视。
-
导数在机器学习中发挥着关键作用,比如启用优化技术,帮助模型训练,并增强模型的可解释性。
积分与微积分基本定理
积分 是一种操作,表示在给定区间内函数曲线下的面积。它是导数的反操作,这也是为什么它被称为反导数。
找到积分的过程称为积分。积分可以用来找到曲线下的面积,在金融世界中广泛应用于风险管理、投资组合管理、概率方法,甚至期权定价等领域。
理解积分的最简单方法是考虑计算函数曲线下面积。可以通过手动计算x轴上的不同变化来完成这一过程,但添加这些片段以找到面积是一个繁琐的过程。这就是积分发挥作用的地方。
请记住,积分是导数的反操作。这很重要,因为它意味着两者之间存在直接关系。积分的基本定义如下:
∫ f ( x ) d x = F ( X ) + C
The ∫ symbol represents the integration process
f ( x ) is the derivative of the general function F ( x )
C represents the lost constant in the differentiation process
d x represents slicing along x as it approaches zero
前面的方程意味着f(x)的积分是一般函数F(x)加上一个常数C,这个常数在初始微分过程中丢失了。以下是一个例子,更好地解释放入常数的必要性。
考虑以下函数:
f ( x ) = x 2 + 5
计算其导数,得到以下结果:
f ' ( x ) = 2 x
现在,如果你想要积分它,以便回到原始函数(在这种情况下用大写字母F(x)代表而不是f(x))?
∫ 2 x d x
通常,看到差异化过程(这意味着取导数),你会返回 2 作为指数,这给出以下答案:
∫ 2 x d x = x 2
这看起来不像原始函数。它缺少常数 5. 但你无法知道这一点,即使你知道有一个常数,你也无法知道它是多少:1?2?677?这就是为什么在积分过程中添加常数C来表示丢失的常数的原因。因此,积分问题的答案如下:
∫ 2 x d x = x 2 + C
注意
到目前为止,讨论一直局限于不定积分,其中积分符号是裸露的(这意味着它没有边界)。我们将在定义完成积分所需的规则之后立即看到这意味着什么。
对于幂函数(就像前面的函数一样),积分的一般规则如下:
∫ x a d x = x a+1 a+1 + C
这比看上去要简单得多。你只是在反转你之前看到的幂规则。考虑以下例子:
∫ 2 x 6 d x
∫ 2 x 6 d x = 2x 7 7 + C
∫ 2 x 6 d x = 2 7 x 7 + C
要验证你的答案,你可以找到结果的导数(使用幂规则):
F ( x ) = 2 7 x 7 + C
f ' ( x ) = ( 7 ) 2 7 x 7-1 + 0
f ' ( x ) = 2 x 6
让我们看另一个例子。考虑以下积分问题:
∫ 2 d x
根据规则,您应该找到以下结果:
∫ 2 d x = 2 x + C
让我们继续讨论定积分,这些是在函数曲线下方用指定的上下限表示的积分。因此,不定积分在曲线下方找到的区域的任何地方,而定积分则在由点a和点b给定的区间内被限制。 不定积分的一般定义如下:
∫ a b f ( x ) d x = F ( B ) - F ( A )
这是最简单的方法。您将解决积分,然后插入两个数字并从彼此减去两个函数。考虑以下积分评估(积分求解通常称为评估积分):
∫ 0 6 3 x 2 - 10 x + 4 d x
第一步是理解所询问的内容。从积分的定义来看,在x轴上[0, 2]之间的区域似乎是使用给定函数计算的:
F ( x ) = ( [ x 3 - 5 x 2 + 4 x + C ] ) | 0 6
要在给定点评估积分,只需按以下方式插入值:
F ( x ) = ( [ 6 3 - 5 (6) 2 + 4 ( 6 ) + C ] ) - ( [ 0 3 - 5 (0) 2 + 4 ( 0 ) + C ] )
F ( x ) = ( [ 216 - 180 + 24 + C ] ) - ( [ 0 - 0 + 0 + C ] )
F ( x ) = ( [ 60 + C ] ) - ( [ 0 + C ] )
F ( x ) = ( 60 - 0 )
F ( x ) = 60
注意
常数C将始终取消掉不定积分,因此在这种问题中可以忽略它。
因此,图形f(x)下方和x轴上方的区域,以及在x轴上[0, 6]之间的区域,都等于 60 平方单位。以下显示了积分的一些经验法则(毕竟,本章旨在更新您的知识或使您对一些关键数学概念有基本理解):
-
要找到常数的积分:
∫ a d x = a x + C
-
要找到变量的积分:
∫ x d x = 1 2 x 2 + C
-
要找到倒数的积分:
∫ 1 x d x = ln | x | + C
-
要找到指数的积分:
∫ a x d x = a x ln(a) + C
∫ e x d x = e x + C
微积分基本定理将导数与积分联系起来。这意味着它用积分来定义导数,反之亦然。 微积分的基本定理实际上由两部分组成:
第一部分
微积分基本定理的第一部分指出,如果您有一个连续的函数f(x),那么原始函数F(x),定义为f(x)的反导数,从固定的起点a到x是一个在a到x处处可微的函数,并且其导数简单地是在x处评估的f(x)。
第二部分
微积分基本定理的第二部分指出,如果您有一个在某个区间[a, b]上连续的函数f(x),并定义一个新函数F(x)为f(x)从a到x的积分,则该函数f(x)在相同区间[a, b]上的定积分可以计算为F(b) – F(a)。
定理在许多领域都很有用,包括物理学和工程学,但优化和其他数学模型也受益于它。在不同学习算法中使用积分的一些例子可以总结如下:
密度估计
积分在密度估计中使用,这是许多机器学习算法的一部分,用于计算概率密度函数。
强化学习
积分在强化学习中用于计算奖励函数的期望值。强化学习在第十章中有详细介绍。
注意
本节的要点如下:
-
积分也称为反导数,是导数的反向操作。
-
不定积分在曲线下的整体面积,而定积分在由点a和点b界定的区间内。
-
微积分基本定理是导数和积分之间的桥梁。
-
在机器学习中,积分用于建模不确定性、进行预测和估计期望值。
优化
几种机器学习和深度学习算法依赖于优化技术来减少误差函数。
优化是在所有可能的解决方案中找到最佳解决方案的过程。优化就是找到函数的最高点和最低点。图 4-17 展示了以下公式的图形:
f ( x ) = x 4 - 2 x 2 + x
图 4-17. 函数图:f ( x ) = x 4 - 2 x 2 + x
局部最小值是指在x轴右侧的数值递减,直到达到一个开始递增的点。该点不一定是函数中的最低点,因此称为局部。在图 4-17 中,函数在点 A 处有局部最小值。
局部最大值是指在x轴右侧的数值递增,直到达到一个开始递减的点。该点不一定是函数中的最高点。在图 4-17 中,函数在点 B 处有局部最大值。
全局最小值是指在x轴右侧的数值递减,直到达到一个开始递增的点。该点必须是函数中的最低点,因此称为全局。在图 4-17 中,函数在点 C 处有全局最小值。
全局最大值是指在x轴右侧的数值递增,直到达到一个开始递减的点。该点必须是函数中的最高点。在图 4-17 中,没有全局最大值,因为函数将无限地继续而没有顶点。您可以清楚地看到函数如何向上加速。
当处理机器和深度学习模型时,目标是找到能使所谓的损失函数(给出预测误差的函数)最小化的模型参数(或输入)。如果损失函数是凸的,优化技术应当找到能使损失函数最小化的参数,趋向于全局最小值。
如果损失函数是非凸的,则不能保证收敛,优化可能只会导致接近局部最小值,这是目标的一部分,但这会忽略全局最小值,这才是最终的目标。
那么这些最小值和最大值是如何找到的呢?让我们一步一步来看:
-
第一步是进行第一阶导数测试(即计算函数的导数)。然后,将函数设为零并解出x将给出所谓的临界点。临界点是函数变换方向的点(数值停止向一个方向移动并开始向另一个方向移动)。因此,这些点是极大值和极小值。
-
第二步是进行二阶导数测试(即计算导数的导数)。然后,将函数设为零并解出x将给出所谓的拐点。拐点显示了函数凹向上和凹向下的地方。
换句话说,临界点是函数变换方向的地方,拐点是函数改变凹凸性的地方。图 4-18 显示了凹函数和凸函数之间的区别。
Concave up function = x 2
Concave down function = - x 2
图 4-18. 一个凹向上的函数和一个凹向下的函数
找到极值的步骤如下:
-
找到第一阶导数并将其设为零。
-
解第一阶导数以找到x。这些值称为临界点,它们代表函数变换方向的点。
-
将值插入公式中,这些值要么在临界点下要么在临界点上。如果第一阶导数的结果为正,则意味着在该点周围增长,如果为负,则意味着在该点周围减少。
-
找到第二阶导数并将其设为零。
-
解第二阶导数以找到x。这些值称为拐点,代表了凹凸性从向上变为向下或反之的点。
-
将值插入公式中,这些值要么在拐点下要么在拐点上。如果第二阶导数的结果为正,则意味着该点有一个最小值,如果为负,则意味着该点有一个最大值。
重要的是要理解,第一阶导数测试与临界点相关,而第二阶导数测试与拐点相关。以下示例找到函数的极值:
f ( x ) = x 2 + x + 4
第一步是取第一阶导数,将其设为零并解出x:
f ' ( x ) = 2 x + 1
2 x + 1 = 0
x = - 1 2
结果表明在该数值处存在临界点。现在找出二阶导数:
f '' ( x ) = 2
接下来,必须将临界点代入二阶导数公式中:
f '' ( - 1 2 ) = 2
在临界点,二阶导数为正。这意味着在该点有一个局部最小值。
在接下来的章节中,您将看到更复杂的优化技术,如梯度下降和随机梯度下降,这些在机器学习算法中非常常见。请注意,您不必完全理解优化和解决未知变量的细节,因为算法将自行处理。
注意
本节的关键要点如下:
-
优化是找到函数的极值点的过程。
-
临界点是函数改变方向的点。
-
拐点表明函数凹向上和凹向下的位置。
-
损失函数是衡量预测机器学习中预测误差的函数。
总结
第二章、第三章和第四章介绍了主要的数值概念,帮助您开始理解基本的机器学习和深度学习模型。我已尽最大努力尝试尽可能简化技术细节。然而,我建议您至少阅读这三章两次,这样您学到的所有东西就会变得非常熟悉。我还鼓励您在其他材料中深入研究这些概念。
当然,深度学习需要更深入的数学知识,但我相信通过本章的概念,您可以开始涉足算法的创建。毕竟,它们是从包和库中预构建的,本章的目的是帮助您了解您正在使用的内容。使用过时的工具从头构建模型的可能性不大。
到现在为止,您应该已经对数据科学和数学要求有了一定的理解,可以舒适地开始。在您能够开始构建第一个机器学习模型之前,我们还有两个主题需要涵盖:技术分析和 Python 数据科学。
¹ 矩阵也可以包含符号和表达式,但出于简化的目的,让我们坚持使用数字。
第五章:介绍技术分析
技术分析提供许多类型的输入(解释变量),您可以在深度学习模型中使用它们。本章介绍了这一广泛领域,以便您在接下来的章节中掌握创建基于技术的学习模型所需的必要知识。
金融技术分析依赖于对价格行动历史的视觉解释,以确定市场可能的整体方向。它依赖于过去是未来最好的预测者的理念。技术分析这一广泛领域内有几种技术,特别是以下几种:
图表分析
这是将主观的视觉解释技巧应用于图表上。通常使用绘制支撑和阻力线以及回调等方法来寻找能够确定下一步走势的反转水平。
指标分析
这是使用数学公式创建客观指标的地方,这些指标可以是趋势跟随或反向的。已知的指标包括移动平均线和相对强度指数(RSI),这两者在本章节中将有更详细的讨论。
模式识别
这是您监控某些重复配置并对其采取行动的地方。模式通常是偶尔出现的事件,呈现某种理论或实证结果。在金融领域,这更为复杂,但某些模式已被证明随着时间的推移增加了价值,部分原因可能是由于称为自我实现预言的现象(一种过程,初始预期导致其确认)。
让我们快速回顾一下技术分析的历史,这样您就可以更好地了解预期。技术分析依赖于三个原则:
历史会重演
在趋势和区间中,您可能会看到集群。此外,某些配置大多数情况下可能会产生类似的结果。
市场包含一切
假设当前价格已包含所有基本、技术和量化信息。
市场波动呈波浪形
由于不同的时间框架和需求,交易者以不同的频率买入和卖出,从而形成趋势和波浪,而不是直线。
不幸的是,技术分析在零售交易社区中被过度炒作和误用,这使得它在专业行业中的声誉略显不佳。每种分析方法都有其优势和劣势,有成功的基本分析、技术分析和量化投资者,但也有三个领域的失败投资者。
基本分析依赖经济和金融数据对特定证券或货币进行长期投资视角的判断,而量化分析则更为灵活,并且更常用于短期数据。它使用数学和统计概念进行预测或风险管理。
在其他假设中,技术分析表明市场并不有效,但这意味着什么呢?市场效率表明信息已经融入当前价格,而价格和价值是相同的。当你购买资产时,你希望它在基本分析术语中是被低估,或在技术分析术语中是超卖,这就是为什么你相信价格会上涨以达到价值。因此,你假设价值大于价格。
市场效率驳斥了价格不等于价值的任何主张,因此建议任何阿尔法交易都不应产生超过平均水平的回报(阿尔法交易是进行超过基准(通常是指数)的投机操作)。
市场效率假设是技术分析师的最大敌人之一,因为它的原则之一在弱式有效市场中,你无法通过技术分析获得超额收益。因此,技术分析在一开始就遭到质疑,随后基本分析也受到了打击。
公平地假设,在未来的某个时候,由于参与者数量和信息获取的便利性,市场将不得不变得有效。然而,正如政治和异常事件所示,市场往往远非有效。
注意
一个导致市场恐慌和非理性的政治事件例子是 2022 年爆发的俄乌战争。一个异常经济事件的例子是央行意外加息。
图表分析
在你理解什么是图表分析之前,你需要知道打开图表——更具体地说,是蜡烛图。
假设某只股票的市场开盘价为102)和最低价(101)。请记住,这四个数据分别称为开盘价、最高价、最低价和收盘价(OHLC),它们代表了创建蜡烛图所需的四个基本价格。
蜡烛图非常简单直观。它们是沿时间线排列的盒状元素,包含 OHLC 数据。图 5-1 展示了关于蜡烛图如何运作的一切。
图 5-1. 左侧为看涨蜡烛图;右侧为看跌蜡烛图
看涨蜡烛图的收盘价高于其开盘价,而看跌蜡烛图的收盘价低于其开盘价。
蜡烛图是分析金融时间序列的常见方法之一。它比简单的折线图包含更多信息,并且比条形图具有更高的视觉可解释性。
注意
折线图是按时间顺序连接收盘价而创建的。这是绘制资产图表的最简单方式。它包含了三种图表类型中最少的信息,因为它只显示收盘价。
图表分析是通过主观绘图找到支撑和阻力线的任务。线,无论是水平还是斜线,是预测市场反应以下级别的核心:
支撑位
市场应该反弹的水平,因为暗示其周围的需求应该高于供给
阻力位
市场应该回落的水平,因为暗示其周围的供给应该高于需求
资产在时间轴上的方向可以分为三种:上升趋势,即价格创造更高的高点;下降趋势,即价格创造更低的低点;以及横盘(或波动),即价格在长时间内围绕同一水平波动。
图 5-2 显示欧元兑美元的水平支撑位接近 1.0840。通常,交易者在价格接近支撑时开始考虑买入。这是因为预期会出现向上的反应,因为力量平衡应更向需求(正面)方面转移,交易者接受支付更高价格,因为他们预计将来价格会更高(请记住前面讨论过的价格对价值的论点)。这里的含义是大多数交易者看到一个低于其价值的价格。
图 5-2. 欧元兑美元的蜡烛图显示支撑位于 1.0840
图 5-3 显示美元兑瑞士法郎的阻力水平接近 0.9030。通常,交易者在接近阻力时开始考虑做空市场。这是因为预期会出现向下的反应,因为力量平衡应更向供给方面转移。这里的含义是大多数交易者看到一个高于其价值的价格。
图 5-3. 美元兑瑞士法郎的蜡烛图显示阻力位于 0.9030
横盘(波动)市场更有把握水平支撑和阻力线会发挥作用。这是因为已经暗示了供需之间的一般平衡。因此,如果有过剩供应,市场会迅速调整,因为需求应该足以稳定价格。
图 5-4 展示了一个范围市场被困在两个水平水平之间;这是美元兑加元的情况(一个美元价格对加拿大元)。在范围市场中,每当市场接近阻力线时,你应该更有信心地认为将会出现下跌,而在接近支撑时,你应该更有信心地认为将会出现反弹,而不像在上升市场中那样,当市场接近阻力线时,你应该更有信心地认为将会出现下跌,而在接近支撑时,你应该更有信心地认为将会出现反弹。
图表分析同样适用于趋势市场。这体现在上升和下降通道的形式上。它们与水平水平具有相同的倾向,但有所偏见(稍后讨论)。
图 5-4. 美元兑加元蜡烛图显示支撑位在 1.4540,阻力位在 1.4620
图 5-5 展示了一个上升通道,在这里,支撑和阻力点随着时间的推移上升,反映出稳定增长的需求压力带来的看涨压力。
看到这种情况的交易者会预期市场接近上升通道的下部时会有看涨反应,而市场接近通道上部时则会有看跌反应。
这没有坚实的科学依据,因为没有规定市场必须平行移动,但可能是自我实现预言的原因,使得这种通道被认为具有预测性质。
图 5-5. 欧元兑美元蜡烛图显示一个上升通道
图 5-6 展示了一个下降通道,其中支撑和阻力点随时间下降,反映出稳定增长的供应压力带来的看跌压力。一般而言,看跌通道往往更为激进,因为恐惧主导了贪婪,卖方比买方更加恐慌。
图 5-6. 欧元兑美元蜡烛图显示一个下降通道
当处理上升和下降通道时,我提到了一种偏见。我将这种偏见称为隐形之手。原因如下:
“趋势是你的朋友。”这句话是股票投资者、投资顾问和金融分析师马丁·兹威格创造的,意味着在上升通道中,你需要更多地关注在市场回归支撑区域时的买入。这是因为你希望看涨压力的隐形之手增加你获胜交易的概率。类似地,在下降通道的情况下,你应该更多地关注市场达到上限时的卖空。兹威格格的格言的完整版本如下:“趋势是你的朋友,直到趋势结束时才会弯曲。”这意味着在任何时刻,市场都可能改变其制度,任何与趋势的友谊最终都会终止。最终,图表分析是主观的,更多地依赖于交易者或分析师的经验。
值得一提的是,除了通过视觉估计来绘制支撑和阻力水平外,还有许多其他找到支撑和阻力水平的方法:
斐波那契回撤
在这里,你可以使用斐波那契比率来确定反应水平。斐波那契回撤通常在市场向上或向下的趋势中计算,这样你就可以知道市场如果触及这些水平将会反转。这种方法的问题在于它非常主观,并且像任何其他技术一样并不完美。优点在于它提供了许多有趣的水平。
枢轴点
使用枢轴点,你使用简单的数学公式找到水平。基于昨天的交易活动,你使用公式预测今天未来的支撑和阻力水平。然后,每当市场接近这些水平,你都尝试通过朝相反方向交易来淡化移动。
移动平均线
这些将在下一节讨论。它们具有动态特性并跟随价格。你也可以用它们来检测当前的市场趋势。
提示
找到支撑和阻力水平的最佳方法是尽可能结合多种技术,这样你就可以有一定的方法融合,进而增加对初始想法的信心。交易是一个数字游戏,尽可能地增加胜算,应该最终提高你的系统表现。
指标分析
指标分析 是第二常用的技术分析工具。通常与绘图一同使用以确认你的初始想法。你可以把指标 看作助手。它们可以分为两类:
趋势跟随指标
用于检测和交易预期继续的趋势市场。因此,它们与移动的持续性相关。
逆向指标
用于淡化移动¹,最好在横盘市场中使用²,因为它们通常标志着初始移动的结束。因此,它们与预期的移动逆转相关(因此与移动的反持续性相关)。
下一节将介绍技术分析的两大支柱:移动平均线(趋势跟随)和相对强弱指数(逆向)。
注意
指标很重要,因为在后续章节中你会将它们作为不同学习算法的输入。
移动平均线
最著名的趋势跟随叠加指标是移动平均线。它的简单性使它无疑成为最受欢迎的工具之一。移动平均线有助于确认并跟随趋势。你还可以用它们来找到支撑和阻力水平、止损点和目标,以及理解潜在的趋势。
有许多类型的移动平均线,但最常见的是简单移动平均线,其中你取收盘价的滚动平均,如下公式所示:
Moving average i = Price i +Price i-1 +...+Price i-n n
图 5-7 显示了应用于 USDCAD 的 30 小时简单移动平均线。30 小时表示我计算最近 30 个小时的移动平均线,针对每小时的条形图。
图 5-7. 在 USDCAD 上显示的 K 线图,带有 30 小时简单移动平均线
移动平均线的经验法则包括以下内容:
-
每当市场高于其移动平均线时,就会有牛市动量正在进行,你最好寻找买入机会。
-
每当市场低于其移动平均线时,就会有熊市动量正在进行,你最好寻找卖空机会。
-
每当市场穿过其移动平均线时,可以说动量已经改变,市场可能正在进入新的状态(趋势)。
你也可以结合移动平均线以获取信号。例如,每当短期移动平均线穿过长期移动平均线时,就会出现一个牛市交叉,市场可能会继续上涨。这也被称为金叉。
相反,每当短期移动平均线穿越长期移动平均线下方时,就会出现一个熊市交叉,市场可能会继续下跌。这也被称为死亡交叉。
图 5-8 显示了 USDCAD 与 10 小时(接近市场价格)和 30 小时移动平均线(远离市场价格)。
图 5-8. 在 USDCAD 上显示的 K 线图,带有 30 小时和 10 小时简单移动平均线
相对强弱指数
现在让我们看看反向指标。由 J. Welles Wilder Jr.首次引入³,相对强弱指数(RSI)是最流行和多功能的有界指标之一。它主要用作反向指标,极端值表明可以利用反应。使用以下步骤计算默认的 14 周期 RSI:
-
计算前一次收盘价格的变化。
-
将正净变化与负净变化分开。
-
计算正净变化和负净变化的平滑移动平均线。
-
将平滑的正变化除以平滑的绝对负变化。将此计算称为相对强度(RS)。
-
对每个时间步骤应用这个归一化公式来得到 RSI:
R S I i = 100 - 100 1+RS i
注意
平滑移动平均线是 RSI 的创始者开发的一种特殊类型的移动平均线。它比简单移动平均线更平稳、更稳定。
通常,默认情况下,RSI 使用 14 个周期的回溯期,尽管每位交易者可能对此有自己的偏好。以下是如何使用这个指标:
-
每当相对强弱指标(RSI)显示 30 或更低的读数时,市场被认为是超卖状态,可能会出现向上的修正。
-
每当 RSI 显示 70 或更高的读数时,市场被认为是超买的,可能会出现向下的修正。
-
每当 RSI 超过或跌破 50 水平时,可能会出现新的趋势,但这通常是一个薄弱的假设,更多是理论性的而不是实际性的。
图 5-9 显示了 EURUSD 相对于其 14 周期 RSI 在第二面板中。指标应用于确认多头或空头偏见,并且在时机和分析当前市场状态方面非常有帮助。
图 5-9。EURUSD 小时线的值在顶部面板,14 周期 RSI 在底部面板
总结一下,指标可以用多种方式计算。两种最常用的是移动平均线和 RSI。
图案识别
模式是一种特定预测随后移动的重复配置。模式可以分为以下类型:
经典价格模式
这些被称为技术性反转价格模式,它们非常主观,并且由于在不考虑主观条件的情况下很难进行回测而被认为是不可靠的。然而,它们仍然被许多交易者和分析师使用。
时间模式
基于时间和价格的综合因素,这些模式不太为人所知,但如果正确使用,它们可以具有强大的预测能力。
蜡烛图形态⁴
这是使用 OHLC 数据来预测市场未来反应的地方。蜡烛图是可视化图表的最佳方式之一,因为它们包含许多可能标志反转或确认移动的模式。
经典价格模式指的是理论配置,如双顶和矩形。它们通常是反转或继续模式:
连续价格模式
这些是确认总体持续移动的配置。例如,矩形和三角形。
反转价格模式
这些是消退总体持续移动的配置。例如,头肩顶和双底。
传统图表分析师熟悉双顶和双底,它们标志着趋势反转并提供了这种反转的潜力。尽管它们很简单,但它们是主观的,有些并不像其他一些那样明显。
这阻碍了知道它们是否增加了价值。图 5-10 显示了双顶的插图,在验证图案后通常会给出一个空头偏见,这通常是打破连接两个顶部之间的低点线。这条线称为颈线。
图 5-10。双顶插图
注意双顶中的这三个重要元素:
颈线
这是连接两个高峰之间的最低低点和图案的开始/结束的线。它用于确定回撤水平。
回撤
打破颈线后,市场应该朝着颈线做出绝望的尝试,但由于卖方利用该水平重新进场继续做空,未能进一步上涨。因此,回撤水平是在验证双顶后的理论上最佳卖出点。
潜力
这是双顶的目标。它被测量为模式顶部和向下投影的颈线之间的中点,从相同的颈线点开始。
双顶或双底可以有任何大小,但最好是对大多数市场参与者可见,以增加其影响力。从心理学角度来看,该模式的解释是在第二个顶部或底部时,市场未能将价格推高超过第一个峰值,因此显示出弱点,卖方可能会利用这一点。
这里有其他更为客观的模式;也就是说,它们具有明确的检测和启动规则。这些都基于明确的客观条件,不受分析师个人判断的影响。这有助于它们的回测和评估。
总结
技术分析提供了大量工具来数学、图形或甚至心理学(通过模式)分析市场。本章的学习目标是理解技术分析及其技术指标,以便在其作为解释变量时熟悉它们(正如在第十一章中所述)。
¹ “对走势的衰退” 是一种交易技术,其中您与正在进行的趋势相反地交易,希望能够把握其结束的时机。
² 横向市场通常处于均衡状态,没有特定的趋势描述它们。它们倾向于在接近彼此的顶部和底部之间摆动。
³ 请参阅 J. Welles Wilder Jr.(趋势研究)的*《技术交易系统中的新概念》*。
⁴ 请参阅我的书籍*《掌握金融模式识别》*(O’Reilly),详细讨论蜡烛图模式。
第六章:为数据科学介绍 Python
这是我们深入进入机器学习和深度学习领域之前的最后一章。对于有经验的 Python 开发人员来说,本章是可选的,但对于没有扎实编程背景的人来说却很重要。了解算法背后的直觉是一个很大的优势,但如果你不能正确实现这些算法,你的知识将无法发挥作用。毕竟,这些算法需要编写代码才能工作,而不能手动操作,因此你需要理解基本的语法以及如何操作和转换数据。
由于本书不旨在成为 Python 编程的 A-Z 指南,本章仅侧重于一些基础知识和一些额外的技术,这些将有助于你顺利地浏览后续章节。
下载 Python
代码被定义为一组指令,旨在由计算机执行。通常需要特定的语法,以便计算机无误地应用一组指令。有许多编程语言,它们分为两大类:
低级编程语言
这些是通常用于编写操作系统和固件的机器语言。它们非常难以阅读。这些语言对硬件有相当高的控制级别。汇编语言是低级语言的一个例子。
高级编程语言
这些是用户友好的高级语言(抽象级别高)。它们通常用于编写程序和软件。高级语言的例子包括 Python 和 Julia。
本书中使用的编程语言是 Python,这是一种流行且多才多艺的语言,在研究和专业交易社区中广泛采用。正如你从本章标题中可能已经了解到的那样,你将会对 Python 以及开始构建自己脚本所需的工具有所了解。但在此之前,你需要下载 Python。
Python 解释器是用于编写和执行使用 Python 语法编写的代码的软件。我使用 Spyder。有些人可能更熟悉其他解释器,如 Jupyter 和 PyCharm,但处理过程是一样的。你可以从官方网站下载 Spyder,或者更好地作为一个更大的包 Anaconda 的一部分下载它,Anaconda 便于安装并提供更多工具。请注意,Spyder 是开源且免费使用的。
图 6-1 显示了 Python 控制台,代码输出显示在其中。
图 6-1 Spyder 的控制台
Python 文件的扩展名为*.py*,它们允许你保存代码并在以后引用它们。你还可以打开多个代码文件并在它们之间导航。
本章的大纲如下:
-
理解 Python 语言以及如何编写无错误的代码
-
了解如何使用控制流以及在时间序列分析中的重要性
-
理解库和函数及其在简化编码中的作用
-
理解不同类型的错误及其处理方法
-
理解如何使用数据操作库如numpy和pandas
-
最后,了解如何将历史财务时间序列数据导入 Python,以便使用适当的工具进行分析(这些工具我们已经讨论过,也将在接下来的章节中讨论)。
基本操作和语法
语法是定义编写可运行代码所需语句结构的一组规则。当您与计算机进行通信时,必须确保它能理解您,因此对语法有扎实的理解非常重要。
注释是非可执行代码,用于解释随后的可执行代码。注释用于让其他程序员理解代码。在 Python 中,注释前面带有井号(#):
# This is a comment. Comments are ignored by the interpreter
# Comments explain the code or give more details about its use
# Comments are written on one line; otherwise, you have to rewrite '#'
注意
请确保理解注释是非可执行的。这意味着当您运行(执行)代码时,解释器会忽略它们,不会返回错误。
有时候,您需要为您的代码编写文档,这可能需要多行代码(在某些情况下甚至是段落)。在每一行写上井号可能会很乏味且凌乱。这就是为什么有一种方法可以编写长注释。为此,您可以使用三个单引号将您的注释写在其中,如下所示:
'''
Python was created in the late 1980s by Guido van Rossum
The name "Python" was inspired by the comedy group Monty Python
'''
值得注意的是,三重引号称为文档字符串,实际上并不是注释(根据官方 Python 文档)。
让我们讨论变量和常量。常量是一个不变的值,而变量在给定事件时可以取不同的值。常量可以是数字 6,而变量可以是字母x,在一定条件或状态下可以取任何数字。变量使用=运算符进行定义:
# Defining a variable
x = 10
# Writing a constant
6
运行(执行)前面的代码将会在变量资源管理器中存储变量x及其相应值。同时,代码的输出将是6。变量是区分大小写的。因此:
# Declaring my_variable
my_variable = 1
# Declaring My_variable
My_variable = 2
# The variable my_variable is different from My_variable
变量声明不能以数字开头,但数字可以包含在变量声明的中间或末尾:
# Returns a SyntaxError
1x = 5
# Valid declaration
x1 = 5
# Valid declaration
x1x = 5
变量还可以包含下划线,但不能包含其他字符:
# Returns a SyntaxError
x–y = 5
# Valid declaration
x_y = 5
强烈建议变量使用简短而直接的名称。例如,考虑创建一个变量来保存某个移动平均线的回溯期(这是在第五章中介绍的技术指标):
# Recommended name
ma_lookback = 10
# Not recommended name
the_lookback_on_that_moving_average = 10
有几种不同的数据类型,具有不同的特性:
数值数据类型
这是最简单的数据类型,仅由数字组成。数值数据类型分为整数、浮点数和复数。整数是简单的整数(正数或负数),如 6 和-19。浮点数比整数更精确,因为它们包含逗号后的值,例如 2.7 和-8.09。复数包括虚数。¹
字符串
正如之前通过注释和文档字符串看到的那样,可以在代码旁边写入文本而不影响执行过程。字符串是表示字符序列的文本结构。字符串可以是函数的输入和参数,而不仅仅是注释。
布尔值
布尔值是用于评估给定表达式或条件的真值的二进制(真或假)数据类型。例如,可以使用布尔值来评估市场价格是否高于或低于 100 期移动平均线。
数据集合
这些是包含多个数据集的序列,每个数据集具有不同且独特的用途。数组是相同类型元素的序列(主要是数值)。在本书中经常会使用数组(使用一个名为numpy的 Python 库,在本章中进行讨论)。数据框是二维结构化数据表,也经常在本书中使用(使用一个名为pandas的 Python 库,在本章中也进行讨论)。集合是无序元素的序列。列表是有序元素的集合,可以是不同的数据类型。元组是有序的不可变元素集合,可以是不同的数据类型。用于存储固定值序列。字典表示键-值对的集合。
下面的代码片段显示了数值数据类型的几个示例:
# Creating a variable that holds an integer
my_integer = 1
# Creating a variable that holds a float number
my_float_number = 1.2
# Using the built-in Python function type() to verify the variables
type(my_integer)
type(my_float_number)
输出应如下所示(请注意,创建的两个变量将出现在变量资源管理器中):
int # The output of type(my_integer)
float # The output of type(my_float_number)
字符串只是文本。用来解释字符串的最常用示例是短语“Hello World”:
# Outputting the phrase Hello World
print('Hello World')
输出应如下所示:
Hello World
字符串也可以作为函数的参数,后面你会在本章看到。
布尔值是真或假值。下面的代码片段展示了它们的使用示例:
# Make a statement that the type of my_integer is integer
type(my_integer) is int
# Make a statement that the type of my_float_number is float
type(my_float_number) is float
# Make a statement that the type of my_integer is float
type(my_integer) is float
'''
Intuitively, the two first statements will return True as they are
indeed true. The third statement is False as the variable my_integer
is an integer and not a float number
'''
上述代码的输出如下:
True
True
False
让我们讨论操作符的工作原理。你已经看到了一个操作符的示例:赋值操作符=用于定义变量。操作符在变量、常量甚至数据结构之间执行特殊的数学和其他任务。有不同类型的操作符。让我们从算术运算符开始,如下面的代码片段所示:
# Arithmetic operator - Addition
1 + 1 # The line outputs 2
# Arithmetic operator - Subtraction
1 – 1 # The line outputs 0
# Arithmetic operator - Multiplication
2 * 2 # The line outputs 4
# Arithmetic operator - Division
4 / 2 # The line outputs 2.0 as a float number
# Arithmetic operator - Exponents
2 ** 4 # The line outputs 16
下一个类型的运算符是比较运算符。这些用于比较不同的元素。它们在控制流事件中经常使用,如本章的下一节所述。以下代码片段显示了一些比较运算符:
# Comparison operator - Equality
2 == 2 # The line outputs True
# Comparison operator - Non equality
2 != 3 # The line outputs True
# Comparison operator - Greater than
2 > 3 # The line outputs False
# Comparison operator - Greater than or equal to
2 >= 2 # The line outputs True
# Comparison operator - Less than
2 < 3 # The line outputs True
# Comparison operator - Less than or equal to
2 <= 2 # The line outputs True
逻辑运算符结合两个或多个条件进行后续评估。有三个逻辑运算符:and、or和not。以下代码块展示了逻辑运算符的一个例子:
# Logical operator - and
2 and 1 < 4 # The line outputs True
2 and 5 < 4 # The line outputs False
# Logical operator - or
2 or 5 < 4 # The line outputs 2, which is the integer less than 4
数据收集结构(数组和数据帧)将在后续章节中讨论,因为它们由于复杂性和独特工具的原因需要深入介绍。让我们以结合到目前为止所讨论内容的代码结束这一部分:
# Declaring two variables x and y and assigning them values
x = 10
y = 2.5
# Checking the types of the variables
type(x) # Returns int
type(y) # Returns float
# Taking x to the power of y and storing it in a variable z
z = x ** y # Returns 316.22
# Checking if the result is greater than or equal to 100
z >= 100 # Returns True as 316.22 >= 100
控制流
条件语句构成了所谓控制流的第一部分(第二部分是稍后讨论的循环)。条件语句作为今天人工智能的祖先,只有在满足特定条件时才执行代码。
使用if、elif和else来管理条件语句。以下代码片段是一个例子:
# Declaring the variables
a = 9
b = 2
# First condition (specific)
if a > b:
print('a is greater than b')
# Second condition (specific)
elif a < b:
print('a is less than b')
# Third condition (general)
else:
print('a is equal to b')
因此,条件语句以if开始。然后,针对每个新的唯一和特定条件,使用elif(它是else if的融合),直到使用其余的概率宇宙作为独立的条件是有意义的,这是由else语句使用的。请注意,else语句不需要条件,因为它存在的目的是覆盖未覆盖的其余部分。
循环用于重复执行代码块,直到满足预定义条件。循环在时间序列中广泛用于计算指标、验证状态和回测交易策略。
使用for(用于迭代有限和定义的序列或一系列元素)和while(用于在满足条件之前继续迭代)语句管理循环。例如,以下代码使用循环打印值{1, 2, 3, 4}:
# Using a for loop
for i in range(1, 5):
print(i)
# Using a while loop
i = 1
while i < 5:
print(i)
i = i + 1
翻译后的for循环简单地表示,对于范围从 1 开始到 5(不包括)的每个元素i(或任何其他字母,取决于编码器),在每次循环中打印i的值(因此,在第一次循环中,i的值为 1,在第二次循环中,其值为 2)。
while循环表示从i = 1开始,循环时打印其值,然后在完成第一次循环前将其加 1。当i变大于 4 时结束循环。
注意
理论上,while循环是无限的,直到另有指示。
值得注意的是,i = i + 1也可以表达为i += 1。算法的目标是以客观的方式递归地应用许多操作,这使得循环结合条件语句尤其有用。让我们看一个金融时间序列的例子:
-
创建一系列数值以模拟假设价格。
-
在循环数据范围内循环,创建条件:如果价格自上一时期以来上涨,则打印 1。类似地,如果价格自上一时期以来下跌,则打印-1。最后,如果价格与上一时期相同,则打印 0。
可以通过以下代码块完成:
# Creating the time series
time_series = [1, 3, 5, 2, 4, 1, 6, 4, 2, 4, 4, 4]
for i in range(len(time_series)):
# The condition where the current price rose
if time_series[i] > time_series[i – 1]:
print(1)
# The condition where the current price fell
elif time_series[i] < time_series[i – 1]:
print(–1)
# The condition where the current price hasn't changed
else:
print(0)
该代码定义了一个值列表(在本例中是一个称为time_series的时间序列),然后使用len()函数循环其长度以应用条件。请注意,在每个循环中,当前时间步被称为i,因此使得前一个时间步为i – 1。
库和函数
在 Python 中,库是一组预写代码,提供功能以便于创建应用程序。模块是独立的 Python 文件,包含可重复使用的代码和数据,可以被导入和在其他 Python 代码中使用,这在库中很常见。因此,模块是一个包含函数和其他类型代码的单个 Python 文件,可以被其他 Python 程序使用和导入。通过使用模块将相似的代码分成不同的文件,通常可以更轻松和有效地管理和维护大型代码库。
编程就是简化任务并使其更清晰的过程。函数在这方面至关重要。函数是一段可重复使用的代码块,在调用时执行特定任务。它只需要定义一次。例如,当您有一个重复的任务,比如计算时间序列的移动平均值时,可以使用函数,这样您就不必每次想使用它时都重新编写移动平均代码。相反,您定义函数与原始代码,然后在需要计算移动平均时调用它。
多个函数形成一个模块,多个模块形成一个库。一个库通常是主题导向的。例如,在本书中,sklearn库将与机器学习模型一起使用。类似地,数据处理和导入使用两个后面章节中讨论的库numpy和pandas。绘图和制图使用matplotlib库完成。
在使用之前必须将库导入 Python 解释器(这相当于承认它们的存在)。这样做的语法如下:
# The import statement must be followed by the name of the library
import numpy
# Optionally, you can give the library a shortcut for easier reference
import numpy as np
有时您只需要从库中导入一个函数或模块。为此,您不需要导入整个库:
# Importing one function from a library
from math import sqrt
上述代码说明了math是一个 Python 库,包含许多数学函数,特别是用于找出给定数字的平方根的sqrt函数。
让我们看看如何定义一个函数。使用def后跟函数名和任何可选参数来定义函数。以下示例创建了一个函数,用于对任意两个给定变量求和:
# Defining the function sum_operation and giving it two arguments
def sum_operation(first_variable, second_variable):
# Outputting the sum of the two variables
print(first_variable + second_variable)
# Calling the function with 1 and 3 as arguments
sum_operation(1, 3) # The output of this line is 4
注意
调用函数意味着执行其预期功能。换句话说,调用函数就是简单地使用它。函数的时间轴是定义和调用。
让我们看看如何从库中导入一个函数并使用其功能:
# Importing the library
import math
# Using the natural logarithm function
math.log(10)
# Using the exponential function (e)
math.exp(3)
# Using the factorial function
math.factorial(50)
顺便提一下,阶乘操作是一种数学操作,用于计算从 1 到某个数字(这是math.factorial()中请求的参数)的所有正整数的乘积。
库可能不像 1 加 1 那么容易。有时,需要先安装外部库,然后才能将其导入 Python 解释器。安装可以通过命令提示符使用以下语法完成:
pip install library_name
回顾第三章,讨论了最大信息系数(MIC)。要计算 MIC,可以使用以下代码(在定义正弦和余弦波之后)。
# Importing the library
from minepy import MINE
# Calculating the MIC
mine = MINE(alpha = 0.6, c = 15)
mine.compute_score(sine, cosine)
MIC = mine.mic()
print('Correlation | MIC: ', round(MIC, 3))
直接导入库可能会导致错误,因为它尚未通过pip安装。因此,您必须首先使用提示符安装库,语法如下(不在 Python 解释器中):
pip install minepy
注意
您可能需要更新 Microsoft Visual C++(至少到版本 14.0)以避免在尝试运行minepy库时出现任何错误。
还需要仔细阅读库随附的文档,以便正确使用它们。库的文档将解释函数的目的,以及每个函数可以接受的参数类型(例如,字符串或数字)。
现在让我们回到函数的主题。函数可以有一个return语句,允许将结果存储在变量中,以便在代码的其他部分中使用。
让我们举两个简单的例子,然后逐步讨论它们:
# Defining a function to sum two variables and return the result
def sum_operation(first_variable, second_variable):
# The summing operation is stored in a variable called final_sum
final_sum = first_variable + second_variable
# The result is returned
return final_sum
# Create a new variable that holds the result of the function
summed_value = sum_operation(1, 2)
# Use the new variable in a new mathematical operation
double_summed_value = summed_value * 2
先前的代码定义了一个名为sum_operation的函数,该函数接受两个参数,然后将操作存储在一个名为final_sum的变量中,然后将其返回,以便可以在外部存储它。接下来,定义了一个名为summed_value的新变量,它作为函数的输出。最后,创建了另一个变量double_summed_value,它是summed_value乘以 2 的结果。这是如何在外部操作中使用函数结果作为变量的示例。现在让我们考虑一个嵌套函数的例子(请记住之前定义的sum_operation函数):
# Defining a function to square the result gotten from sum_operation()
def square_summed_value(first_variable, second_variable):
# Calling the nested sum_operation function and storing its result
final_sum = sum_operation(first_variable, second_variable)
# Creating a variable that stores the square of final_sum
squared_sum = final_sum ** 2
# The result is returned
return squared_sum
# Create a new variable that holds the result of the function
squared_summed_value = square_summed_value(1, 2)
上述代码片段定义了一个名为square_summed_value的函数,它接受两个参数。此外,它使用了一个嵌套函数,在本例中是sum_operation。嵌套函数的结果再次存储在一个名为final_sum的变量中,该变量作为在找到squared_sum变量时的输入。该变量以final_sum的平方的形式找到。
让我们结束 Python 和机器学习中常见库的部分(除了numpy和pandas):
-
matplotlib:用于绘制和可视化数据
-
sklearn:用于机器学习模型
-
scipy:用于科学计算和优化
-
keras:用于神经网络
-
math:用于使用数学工具,如平方根
-
random:用于生成随机变量
-
requests:用于进行网页抓取的 HTTP 请求
异常处理和错误
很多时候,当代码执行时出现错误,并且解释器遇到阻止其进一步执行的障碍时会发生错误。最基本的错误是SyntaxError,当单词拼写错误或缺少使代码无法理解的元素时会发生:
# Will not output a SyntaxError if executed
my_range = range(1, 10)
# Will output a SyntaxError if executed
my_range = range(1, 10
从前面的代码可以看出,第二行代码末尾缺少括号,解释器无法理解。这种类型的错误可能是您最常见的错误之一。另一个常见错误是NameError,当在执行包含它的代码之前未定义变量时会发生。考虑以下示例:
x + y
由于解释器不知道x和y的值,因为它们没有定义,上述代码将给您一个NameError。
当解释器无法找到您尝试导入的库或模块时,将发生ModuleNotFoundError。这通常发生在库或模块安装在错误的目录或未正确安装时。解决此问题的常见方法包括:
-
验证模块的名称是否正确写入
-
验证模块是否正确
pip安装 -
验证模块是否安装在正确的位置
另一种常见错误类型是TypeError,当您在不兼容的元素上应用某个操作时会发生,例如将整数与字符串相加。以下操作会引发TypeError:
# Defining variable x
x = 1
# Defining variable y
y = 'Hello
# Summing the two variables, which will raise a TypeError
x + y
在时间序列分析中,您可能会遇到以下四种错误:
IndexError
当引用当前数组或数据框超出范围的索引时,将引发此错误。想象一下,有一个包含 300 个值(行)的数组。如果您想循环遍历它们,并在每个循环中在下一个单元格(时间步长+1)中输入数字 1,那么解释器将引发IndexError,因为在最后一个循环中没有下一个单元格。
ValueError
当尝试用无效参数调用函数时,将引发此错误。例如,试图在调用函数时将整数元素作为字符串传递。
KeyError
当尝试访问不存在的数据框中的元素时会发生这种情况。例如,如果数据框中有三列,并且您引用一个不存在的列(可能由于语法问题),那么您可能会遇到KeyError。
ZeroDivisionError
这个错误很直观,在试图除以零时发生。
您可能会遇到其他类型的错误。重要的是要理解它们指的是什么,以便能够修复它们并使代码再次运行。
异常 是可能不会致命到代码的错误,因为它们只是显示一个警告,但不一定终止代码。因此,异常发生在代码执行期间(与错误相反,错误是因为解释器无法执行代码而发生的)。要忽略某些异常(和错误),使用 try 和 except 关键字。当你确信处理异常不会改变代码的输出时,这是很有用的。
让我们以创建一个将时间序列的第一列除以第二列的下一个值为例。第一步是将时间序列定义为数据框或数组(或任何其他数据收集结构):
# Importing the required library to create an array
import numpy as np
# Creating a two-column list with 8 rows
my_time_series = [(1, 3),
(1, 4),
(1, 4),
(1, 6),
(1, 4),
(0, 2),
(1, 1),
(0, 6)]
# Transforming the list into an array
my_time_series = np.array(my_time_series)
现在让我们编写一个除法函数,它将获取第一列中的任何值,并将其除以第二列中的下一个值:
# Defining the function
def division(first_column, second_column):
# Looping through the length of the created array
for i in range(len(my_time_series)):
# Division operation and storing it in the variable x
x = my_time_series[i, first_column] /
my_time_series[i + 1, second_column]
# Outputting the result
print(x)
# Calling the function
division(0, 1)
运行前两个代码块将会产生一个 IndexError,因为在最后一个循环中,函数找不到第二列的下一个值,因为它不存在:
IndexError: index 8 is out of bounds for axis 0 with size 8
通过 try 和 except 来修复这个问题将忽略导致问题的最后一次计算,并返回期望的结果:
# Defining the function
def division(first_column, second_column):
# Looping through the length of the created array
for i in range(len(my_time_series)):
# First part of the exception handling
try:
# Division operation and storing it in the variable x
x = my_time_series[i, first_column] /
my_time_series[i + 1, second_column]
# Outputting the result
print(x)
# Exception handling of a specific error
except IndexError:
# Ignoring (passing) the error
pass
# Calling the function
division(0, 1)
输出如下:
0.25
0.25
0.16
0.25
0.50
0.00
0.16
numpy 和 pandas 中的数据结构
您现在了解了什么是库,并且知道 numpy 和 pandas 是在 Python 中操作、处理和导入数据的首选库。本节讨论了两者之间的区别,以及绝对是您数据分析工具箱中的重要功能。但首先,让我们定义这两个库:
numpy
numpy 是 Numerical Python 的缩写,是一个允许使用多维数组和矩阵的 Python 库。它提供了一个强大的接口,用于对数组和矩阵执行各种操作。
pandas
pandas 是 Panel Data 的缩写,是一个允许使用数据框(一种类型的表格数据)的 Python 库。它提供了两种主要的数据结构:series 和 dataframes。series 是一个类似数组的一维对象,可以保存任何数据类型。dataframe 是一个二维表格状结构,由行和列组成(类似于电子表格)。
这两个库在分析时间序列数据方面非常有用。数组仅保存数值类型数据,因此实际上不保存日期类型数据。这可能是使用 pandas 而不是 numpy 的优点之一,但两者都有各自的优势和相对的弱点。最终,这是一个选择问题。本书将优先使用 numpy,因为它简单,并且下一章中的机器学习模型使用 sklearn 库,该库应用于数组。
注意
在 numpy 和 pandas 之间切换需要转换时间序列类型。这是一个相对简单的任务,但有时可能会导致某些类型数据的丢失(例如,日期数据)。
在讨论它们的潜力之前,让我们导入这两个库:
import numpy as np
import pandas as pd
以下代码创建了两个具有两列和三行的时间序列。第一个时间序列称为my_data_frame,使用pandas库函数pd.DataFrame创建。第二个时间序列称为my_array,使用numpy库函数np.array创建:
# Creating a dataframe
my_data_frame = pd.DataFrame({'first_column' : [1, 2, 3],
'second_column' : [4, 5, 6]})
# Creating an array
my_array = np.array([[1, 4], [2, 5], [3, 6]])
如图 6-2 所示,数据框具有真实的索引,可以有列名。数组只能容纳一种数据类型。
图 6-2。pandas数据框(左)和numpy数组(右)
要在这两种类型的数据之间切换,您将使用前面代码块中使用的相同两个函数:
# To transform my_data_frame into my_new_array
my_new_array = np.array(my_data_frame)
# To transform my_array into my_new_data_frame
my_new_data_frame = pd.DataFrame(my_array)
现在让我们看看在处理模型时会非常有用的一些函数。切片和连接是您必须掌握的过程之一,以便在数据分析中顺利导航。考虑以下数组:
first_array = np.array([ 1, 2, 3, 5, 8, 13])
second_array = np.array([21, 34, 55, 89, 144, 233])
连接是将两个数据集合并在一起的行为,可以通过行(axis = 0)或列(axis = 1)来进行。让我们都来做一下:
# Reshaping the arrays so they become dimensionally compatible
first_array = np.reshape(first_array, (–1, 1))
second_array = np.reshape(second_array, (–1, 1))
# Concatenating both arrays by columns
combined_array = np.concatenate((first_array, second_array), axis = 1)
# Concatenating both arrays by rows
combined_array = np.concatenate((first_array, second_array), axis = 0)
现在让我们对数据框做同样的事情。考虑以下数据框:
first_data_frame = pd.DataFrame({'first_column' : [ 1, 2, 3],
'second_column' : [ 4, 5, 6]})
second_data_frame = pd.DataFrame({'first_column' : [ 7, 8, 9],
'second_column' : [10, 11, 12]})
当您希望将数据合并为一个结构时,连接非常有用。这是如何在数据框中完成的(请注意,这只是语法和函数源的简单更改):
# Concatenating both dataframes by columns
combined_data_frame = pd.concat([first_data_frame, second_data_frame],
axis = 1)
# Concatenating both dataframes by rows
combined_data_frame = pd.concat([first_data_frame, second_data_frame],
axis = 0)
记住,对于时间序列,行(水平单元格)代表一个时间步(例如,每小时),其中包含所有的数据,而列则代表不同类型的数据(例如,金融工具的开盘价和收盘价)。现在让我们看看数组的切片技术:
# Defining a one-dimensional array
my_array = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
# Referring to the first value of the array
my_array[0] # Outputs 1
# Referring to the last value of the array
my_array[–1] # Outputs 10
# Referring to the sixth value of the array
my_array[6] # Outputs 7
# Referring to the first three values of the array
my_array[0:3] # Outputs array([1, 2, 3])
my_array[:3] # Outputs array([1, 2, 3])
# Referring to the last three values of the array
my_array[–3:] # Outputs array([8, 9, 10])
# Referring to all the values as of the second value
my_array[1:] # Outputs array([2, 3, 4, 5, 6, 7, 8, 9, 10])
# Defining a multidimensional array
my_array = np.array([[ 1, 2, 3, 4, 5],
[ 6, 7, 8, 9, 10],
[11, 12, 13, 14, 15]])
# Referring to the first value and second column of the array
my_array[0, 1] # Outputs 2
# Referring to the last value and last column of the array
my_array[–1, –1] # Outputs 15
# Referring to the third value and second-to-last column of the array
my_array[2, –2] # Outputs 14
# Referring to the first three values and fourth column of the array
my_array[:, 2:4] # Outputs array([[3, 4], [8, 9], [13, 14]])
# Referring to the last two values and fifth column of the array
my_array[–2:, 4] # Outputs array([10, 15])
# Referring to all the values and all the columns up until the second row
my_array[:2, ] # Outputs array([[ 1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
# Referring to the last row with all the columns
my_array[–1:, :] # Outputs array([[11, 12, 13, 14, 15]])
注意
重要的是要知道,Python 的索引从零开始。这意味着要引用数据结构中的第一个元素,您需要将其索引为 index = 0。值得注意的是,在范围内,最后一个元素是被排除的,这意味着数据结构中的前三个元素被称为 [0, 3],这将给出索引为 0、1 和 2 的元素。
现在让我们看看数据框的同样的事情,这样当您想要操作数据结构时,这节就会成为一种迷你参考:
# Defining a one-dimensional dataframe
my_df= pd.DataFrame({'first_column': [1, 2, 3, 4, 5,
6, 7, 8, 9, 10]})
# Referring to the first value of the dataframe
my_df.iloc[0]['first_column'] # Outputs 1
# Referring to the last value of the dataframe
my_df.iloc[–1]['first_column'] # Outputs 10
# Referring to the sixth value of the dataframe
my_df.iloc[6]['first_column'] # Outputs 7
# Referring to the first three values of the dataframe
my_df.iloc[0:3]['first_column'] # Outputs ([1, 2, 3])
# Referring to the last three values of the dataframe
my_df.iloc[–3:]['first_column'] # Outputs ([8, 9, 10])
# Referring to all the values as of the second value
my_df.iloc[1:]['first_column'] # Outputs ([2, 3, 4, 5, 6, 7, 8, 9, 10])
# Defining a multidimensional dataframe
my_df = pd.DataFrame({'first_column' : [ 1, 6, 11],
'second_column' : [ 2, 7, 12],
'third_column' : [ 3, 8, 13],
'fourth_column' : [ 4, 9, 14],
'fifth_column' : [ 5, 10, 15]})
# Referring to the first value and second column of the dataframe
my_df.iloc[0]['second_column'] # Outputs 2
# Referring to the last value and last column of the dataframe
my_df.iloc[–1]['fifth_column'] # Outputs 15
# Referring to the third value and second-to-last column of the dataframe
my_df.iloc[2]['fourth_column'] # Outputs 14
# Referring to the first three values and fourth column of the dataframe
my_df.iloc[:][['third_column', 'fourth_column']]
# Referring to the last two values and fifth column of the dataframe
my_df.iloc[–2:]['fifth_column'] # Outputs ([10, 15])
# Referring to all the values and all the columns up until the second row
my_df.iloc[:2,] # Outputs ([[ 1, 2, 3, 4, 5], [6, 7, 8, 9, 10]])
# Referring to the last row with all the columns
my_df.iloc[–1:,] # Outputs ([[11, 12, 13, 14, 15]])
注意
尝试回到前面的章节执行那里提供的代码。现在你应该有更加牢固的理解了。
在 Python 中导入金融时间序列
本节讨论了部署机器和深度学习算法的关键方面。它涉及到运行模型和评估其性能所需的历史 OHLC 数据。
第一步是准备算法成功所需的环境和其他一切。为此,您需要两个程序:
-
您用来编写和执行代码的 Python 解释器。您已经完成了这一步骤。
-
您用作数据库的制图和金融软件。本节涵盖了这部分内容。
用于制定基准图表的是 MetaTrader 5,这是全球许多交易员使用的程序。MetaTrader 5 与 Spyder 一起工作,因此您应该先下载 Spyder 并熟悉其工作方式。
从 官方网站 下载并安装 MetaTrader 5. 你需要创建一个演示账户,这只是一个带有虚拟货币的虚拟账户。单词 demo 并不是指使用的时间有限,而是指它不使用真实货币。
要开设账户,请选择 文件 > 开设账户,选择 MetaQuotes Software Corp,然后点击下一步。然后选择第一个选项开设演示账户;这将允许你交易虚拟货币。最后,输入一些基本信息,如你的姓名、电子邮件和账户类型。你将不会收到验证请求或任何类型的确认,因为演示应直接启动,让你查看图表。
图 6-3 展示了平台的界面。默认情况下,MetaTrader 5 并不显示其覆盖的所有市场,因此,如果需要导入和可视化,你需要使其可访问。点击 View,点击 Market Watch,然后右键点击任何新标签中显示的符号,并选择 Show All。这样,你可以看到更多市场的扩展列表。
在你开始编码之前,你需要安装 MetaTrader 5 Python 集成库,以便稍后在 Spyder 中使用它。这很简单,只需一步。打开 Anaconda 提示符,输入:
pip install MetaTrader5
安装是让你在解释器中使用为 MetaTrader 5 设计的 Python 模块和函数的桥梁。
图 6-3. MetaTrader 5 界面
以下代码块使用了内置语句 import,该语句调用了内部(自创建)或外部(第三方创建)的库。你会记得库是函数的存储库,因此,你需要导入与你想要做的事情相关的库。为了演示目的,导入以下模块、包和库:
import datetime # Gives tools for manipulating dates and time
import pytz # Offers cross-platform time zone calculations
import MetaTrader5 as mt5 # Importing the software's library
import pandas as pd
import numpy as np
下一步是创建你可以导入的时间框架的宇宙。尽管我将向你展示如何分析和回测小时数据,你可以定义一个更广泛的宇宙,如下面的代码片段所示:
frame_M15 = mt5.TIMEFRAME_M15 # 15-minute time frame
frameframe_M30 = mt5.TIMEFRAME_M30 # 30-minute time frame
frame_H1 = mt5.TIMEFRAME_H1 # Hourly time frame
frame_H4 = mt5.TIMEFRAME_H4 # 4-hour time frame
frame_D1 = mt5.TIMEFRAME_D1 # Daily time frame
frame_W1 = mt5.TIMEFRAME_W1 # Weekly time frame
frame_M1 = mt5.TIMEFRAME_MN1 # Monthly time frame
注意
完整代码可在本书的 GitHub 仓库 中找到,名称为 master_function.py。
时间框架 是你记录价格频率的方式。使用小时数据时,你将每小时记录最后一个价格。这意味着在一天内,你可以有多达 24 个小时价格。这允许你查看价格的日内演变。目标是在特定期间内记录全部的 OHLC 数据。
以下代码定义了当前时间,这样算法在导入数据时就有了一个参考点。基本上,你正在创建一个存储当前时间和日期的变量:
now = datetime.datetime.now()
现在我们继续定义您想要进行回测的金融工具的全集。在本书中,回测将专注于外汇市场(FX)。因此,让我们创建一个变量来存储一些关键的货币对:
assets = ['EURUSD', 'USDCHF', 'GBPUSD', 'USDCAD']
现在,您已准备好时间和资产变量,只需创建导入算法的结构。函数get_quotes()完成此任务:
def get_quotes(time_frame, year = 2005, month = 1, day = 1,
asset = "EURUSD"):
if not mt5.initialize():
print("initialize() failed, error code =", mt5.last_error())
quit()
timezone = pytz.timezone("Europe/Paris")
time_from = datetime.datetime(year, month, day, tzinfo = timezone)
time_to = datetime.datetime.now(timezone) + datetime.timedelta(days=1)
rates = mt5.copy_rates_range(asset, time_frame, time_from, time_to)
rates_frame = pd.DataFrame(rates)
return rates_frame
注意,在get_quotes()函数中,您使用了pytz和pandas库。该函数首先定义了 Olson 时区,²您可以自行设置。以下是一个简要的、不详尽的时间区域列表,根据您的时区,您可以输入以下内容:
America/New_York
Europe/London
Europe/Paris
Asia/Tokyo
Australia/Sydney
接下来定义两个名为time_from和time_to的变量:
-
变量
time_from包含指向导入日期开始的datetime(例如,2020 年 01 月 01 日)。 -
变量
time_to包含指向导入日期结束的datetime,它使用变量now表示当前时间和日期。
下一步是创建一个变量,使用您指定的时间段导入金融数据。通过rates变量使用mt5.copy_rates_range()函数完成。最后,使用pandas将数据转换为数据框。导入过程所需的最终函数是mass_import()函数。它允许您使用变量选择时间范围,然后使用get_quotes()函数导入和格式化数据到数组。以下代码片段定义了mass_import()函数:
def mass_import(asset, time_frame):
if time_frame == 'H1':
data = get_quotes(frame_H1, 2013, 1, 1, asset = assets[asset])
data = data.iloc[:, 1:5].values
data = data.round(decimals = 5)
return data
函数mass_import()自动将数据框转换为数组,因此在使用自动导入时无需担心转换问题。
注意
如果使用mass_import()函数时出现空数组,可能需要将年份参数调整得更高,以获取数据。例如,如果在mass_import()函数中得到空数组,尝试在get_quotes()函数中使用更新一些的年份(“2014”而不是“2013”)。
要导入从 2014 年初至今的历史小时级 EURUSD 数据,可以键入以下内容(假设已经定义了get_quotes()、now、数据框和相关库):
# Defining the universe of currency pairs
assets = ['EURUSD', 'USDCHF', 'GBPUSD', 'USDCAD']
# Redefining the mass_import function to switch to a default 2014
def mass_import(asset, time_frame):
if time_frame == 'H1':
data = get_quotes(frame_H1, 2014, 1, 1, asset = assets[asset])
data = data.iloc[:, 1:5].values
data = data.round(decimals = 5)
# Calling the mass_import function and storing it in a variable
eurusd_data = mass_import(0, 'H1')
注意
注意在mass_import函数中如何使用return语句将历史数据存储在选择的变量中。
尽管 MetaTrader 5 有 macOS 版本,但 Python 库只在 Windows 上运行。在 macOS 或 Linux 上,您可能需要尝试手动导入方法(或者在第七章中提出的使用pandas-datareader库的替代方法)。
自动导入是一个巨大的时间节省器,但即使是 Windows 用户也可能遇到令人沮丧的错误。因此,我将向你展示手动导入的方法,这可以作为解决方法。在本书的Github 页面上,你会找到一个名为Historical Data的文件夹。文件夹内包含一系列以 Excel 格式存储的历史金融时间序列,可以下载。
手动导入需要一个包含从第三方下载的 OHLC 数据的 Excel 文件(例如本书 GitHub 存储库中提供的 Excel 文件)。在这种情况下,你可以使用pandas库来导入并将其转换为数组。
让我们以Daily_GBPUSD_Historical_Data.xlsx**.为例。从存储库(在Historical Data中找到)下载文件并存储在桌面上。Spyder 目录必须与文件处于同一位置。通俗地讲,这意味着 Spyder 必须在你的桌面上搜索 Excel 文件。要选择正确的目录,点击箭头旁边的文件夹按钮。目录选项卡应该看起来像图 6-4。
图 6-4. 选择正确文件夹后的目录选项卡
你应该会得到一个单独的窗口,你可以在其中选择桌面位置,然后验证选择。完成这些步骤后,选项卡应该看起来像图 6-5。
图 6-5. 选择正确桌面位置后的目录选项卡
你可以使用内置于pandas中的read_excel()函数来获取 Excel 文件中的值。按照以下语法操作:
# Importing the excel file into the Python interpreter
my_data = pd.read_excel('Daily_GBPUSD_Historical_Data.xlsx.xlsx')
你应该有一个名为Daily_GBPUSD_Historical_Data.xlsx的数据框,其中包含五列,分别代表开盘价、最高价、最低价和收盘价。通常在使用属于它的函数之前,你必须输入库的名称;这就是为什么read_excel()之前要加上pd的原因。
注意
由于兼容性问题,我建议 Windows 用户使用自动导入,而 macOS 用户使用手动导入。
概要
Python,作为编程语言中的主要明星,得到了开发者社区的广泛采纳。掌握它对于开启数据科学领域的巨大潜力至关重要。
下一章将讨论机器学习和不同的预测算法。主要目标是能够编写这些算法并在金融数据上运行回测。你会发现,一旦开始理解这个过程,只需移除一个算法并插入另一个(如果它们具有相同的假设),就能轻松应对。热身章节结束了,现在是时候开始编程了。
¹ 虚数是一种表示负数平方根的复数类型。
² 这个 Olson 时区命名自其创建者阿瑟·大卫·奥尔森,处理与时区数据和夏令时规则相关的问题。对于开发人员和计算机系统来说,它是处理时间相关功能和转换的重要资源。