- Published on
Function - Sorted Indices
- Authors

- Name
- CoresightQuant
NOTE
本文为公开交易策略的技战术复盘,聚焦方法逻辑、交易场景与风险边界。
在量化交易的研究与实战中,我们经常需要对一组数据(如股票收益率、技术指标值、波动率)进行排序,并根据排序结果来决定买卖对象。然而,很多时候我们并不关心数据本身的大小顺序,而是关心它们在原数组中的位置——也就是“索引”。本文要介绍的公开策略脚本,正是这样一个功能:通过冒泡排序获取数组的排序索引。虽然它看起来只是算法世界的“小工具”,但用好了,完全可以成为多标的轮动、因子选股等策略的重要基石。
策略解读
这段英文描述可以这样理解:假设我们有一个存放了若干数值的数组,例如“昨日涨跌幅”列表,我们希望对列表中的数值进行从大到小(或从小到大)排列。普通的排序函数会告诉你排序后的数值序列,例如 [5, 3, 1] 排序后得到 [1, 3, 5]。但“排序索引”函数告诉你的是,数值 5 原本是数组中的第几个、数值 3 是第几个、数值 1 是第几个,它返回的结果是一串位置编号,例如 [2, 1, 0] 或 [0, 1, 2],具体取决于排序方向和实现方式。
从更深的层面看,在量化策略中,我们通常需要的是“选哪些标的”或者“哪些标的的排名靠前”。如果我们只看排序后的数值,可能会丢失标的的原始身份信息。而索引排序保留了“排列名次”与“原始位置”的映射关系,使我们能够轻松把排名结果映射回具体的股票、期货品种或加密货币。例如,当某个因子值最大的标的需要被纳入组合时,我们只需要找到最大因子值对应的索引,然后去索引对应的标的字典里取名字即可。
值得注意的是,原文提到的“冒泡排序”是一种非常直观的排序算法。它的基本思想是反复比较相邻元素,如果顺序错误就交换它们,就像气泡一样慢慢浮到正确位置。虽然它在大数据量下性能不佳,但在处理几十个交易标的的数组时,完全可行。原作者将这个简单的算法封装成“获取排序索引”的函数,说明在公开交易社区中,这类基础工具具有一定的通用价值。
名词解释
- 排序索引(Sorted Indices):一个由原数组元素在排序后应处位置所对应的原始位置编号组成的数组。例如原数组
[50, 20, 80]按升序排序后,索引序列可能是[1, 0, 2],表示最小元素在原位置1,最大元素在原位置2。它保留了元素的位置信息,便于映射到标的名称。 - 冒泡排序(Bubble Sort):一种简单的排序算法。它重复地遍历待排序数组,依次比较相邻两个元素,如果顺序错误则交换它们,直到整列有序。因为越小的元素会经由交换慢慢“浮”到数组顶端,所以得名。
- 数组(Array):在程序设计中,数组是一组按顺序排列的相同类型数据集合。每个数据可以有一个唯一的编号,称为下标或索引,通常从0开始计数。在量化中,数组常用来存储某一指标在一段时间内的历史值,或某一截面内多个标的的数值。
- 时间复杂度(Time Complexity):用于衡量算法运行时间随输入规模增长而变化的趋势。冒泡排序的时间复杂度为O(n²),意味着当数组元素数量翻倍时,运行时间大约增长到四倍。这个指标对评估排序算法在实时交易中的可行性很重要。
- 横截面数据(Cross-Sectional Data):在某一特定时间点上,对多个对象(如所有股票、所有期货合约)观测到的数据集合。常见的横截面数据包括同一时刻的全部股票市盈率、当日各品种涨幅等。排序索引函数常常用于处理这类数据,以确定各标的的相对位置。
- 多标的轮动策略(Multi-Asset Rotation):一种量化交易策略,定期选择一批表现最强或符合特定规则的标的买入,同时卖出不符合条件的标的。排序索引函数可以直接用于对候选标的的因子值进行排名,进而筛选出排名前几的标的。
- 因子(Factor):影响金融资产价格表现的某种可量化特征,如动量、规模、价值、波动率等。对因子值排序是因子投资的基础操作,排序索引则是实现该操作的高效工具之一。
策略思路讲解
基于“函数获取排序索引”这一能力,我们可以构建一套完整的量化选股(或选品种)策略。核心思路是:在每一个调仓周期中,先对候选池中的所有标的计算一个或多个指标值,然后利用排序索引函数对这些指标值进行排名,最后根据排名结果决定买入哪些标的、卖出哪些标的。
以最常见的动量轮动为例,策略逻辑如下:
-
确定候选池:选定一组可交易的资产,例如沪深300成分股、主流加密货币,或是不同期限的期货合约。候选池数量通常在10到50之间,因为过大的数量会增加冒泡排序的计算负担,但现代计算机仍然可以轻松处理上千个标的。
-
计算指标值:在每周或每月调仓日,对每个标的计算其过去一段时间(如过去20天、60天)的累计收益率,作为动量因子值。这个因子值就是我们要排序的数组元素。
-
调用排序索引函数:把候选池中所有标的的动量因子值放入一个数组,调用公开策略中的“获取排序索引”函数,得到一组指示原位置的索引序列。例如,假设有5个标的,动量值分别为
[-2, 3, 1, -1, 4],按降序排序后,排序索引可能为[4, 1, 2, 3, 0]。这告诉我们动量最强的标的是原数组第5个(索引4),次强的是第2个(索引1),以此类推。 -
映射回标的并调仓:根据索引序列,找出排名前20%(或前N名)的标的,将它们纳入投资组合,并赋予相同的权重或根据排名分配权重。同时卖出不在排名前N的标的。
-
定期重复:每个调仓周期重复上述过程,实现动态轮动。
除了动量因子,排序索引函数还可以用于其他许多场景。例如,在统计套利策略中,我们可以对价格偏离程度进行排序,选择偏离最大的标的进行均值回归交易;在资产配置中,我们可以对风险调整后收益进行排序,选择性价比最高的资产;在风控模块中,我们可以对当前持仓的浮盈浮亏进行排序,优先平掉最差的头寸。
从实现角度看,虽然原始函数名称为“函数”,使用时实际上只要传入一个数组,就能得到排序后的索引数组。在策略脚本中,这个函数可以像积木一样嵌入到更大的逻辑块中。需要特别注意的是,冒泡排序是稳定排序,也就是说,如果两个元素值相同,它们的相对顺序在排序后保持不变。这对于处理并列排名时非常有价值,因为你可以根据原始顺序来进行微调,避免频繁切换持仓。
优点
第一,逻辑简单,易于理解。冒泡排序的机制非常直观,即使是初学者也能一眼看懂。因此,这个“排序索引”函数的实现思路清晰,不容易出错,也便于在公开交易社区中分享和交流。作为一个教学示例,它教会我们如何在交易系统中处理数组排序,而不仅仅是调用现成的库函数。
第二,保留了标的的身份信息。直接排序可能只得到数值序列,而排序索引得到了原始位置编号,这对交易策略至关重要——因为我们需要知道哪只股票排名第一,而不是仅仅知道最大的数值是多少。有了排序索引,我们可以快速映射到标的代码或名称,进而下单。
第三,适用性广泛。无论处理的是股票、期货、外汇还是加密货币,只要是把多个对象的某个属性进行对比,都可以使用排序索引。它不依赖特定的市场或品种,是一种跨领域的通用工具。
第四,可以控制排序方向。多数实现允许选择升序或降序排列。在需要选择最强标的时用降序索引,在选择最弱标的或寻找背离时用升序索引。同一个小函数就能满足不同策略方向的需求。
第五,轻量级,无额外依赖。冒泡排序不需要额外引入复杂的库,代码量极少。在计算资源受限或需要快速部署脚本的环境中,这种简洁性可以减少依赖导致的兼容性问题。
第六,有助于理解算法与交易结合。通过使用这个函数,读者可以深入体会到算法基础(如排序)在量化投资中的具体应用。它从一个侧面展示了计算机科学知识如何直接转化为交易能力。
缺点
第一,排序效率较低。冒泡排序的时间复杂度是O(n²),当候选标的数量增加时,排序所需时间呈平方级增长。尽管现代计算机处理几百个标的毫无压力,但在高频交易、实时 tick 级排序等超低延迟场景中,这种低效算法可能成为瓶颈。
第二,对大数据集不友好。如果候选池包含成千上万只股票,或者需要在一个循环中反复执行排序,冒泡排序将消耗大量计算资源,影响策略回测速度和实时响应能力。这种情形下,快速排序、归并排序等O(n log n)算法会更合适。
第三,缺乏灵活的排序键。该函数只对一维数组进行排序,而在真实策略中,我们往往需要根据多列数据汇总成综合得分后再排序。如果函数只能处理单个数组,则需要额外工作来构建综合因子数组。
第四,未考虑缺失值或异常值。基础排序函数通常不会自动处理数组中的空值、NaN或极端值。在交易数据中,停牌股票、新上市标的常常导致因子值为空,如果直接排序可能导致索引错乱或排名失真。
第五,可能过度优化于简单场景。由于函数设计的初衷是“获取排序索引”,并没有提供更丰富的输出,如排序后数值、差值、排名变化率等。策略开发者可能需要额外写逻辑来获取这些信息,增加了编码负担。
第六,稳定性带来的沉默问题。虽然稳定排序是优点,但有时候也会掩盖数据中的真实变化。例如,两个因子值相同的标的一直保持相同顺序,可能导致策略长期持有某一只标的而忽略另一只,降低组合的多样性。
起源年份考证
需要说明的是,本文所讨论的“函数”本身来自公开交易社区,具体发布年份不详,作者为 RicardoSantos。对于这一类“使用冒泡排序获取排序索引”的方法,我们无法准确考证到具体函数首次出现的年份,但可以从算法历史与交易策略应用两个维度给出时间线考证。
冒泡排序的提出:冒泡排序作为一种计算机排序算法,其思想最早可以追溯到20世纪50年代。一般认为,冒泡排序的雏形出现在1956年的一篇技术报告中,由美国数学家 Edward H. Friend 提出,后来在20世纪60年代被广泛研究并作为基础算法课程内容。因此,算法本身的起源年份可定为1950年代。
交易社区中的排序类工具:在量化交易领域,使用排序方法来选择资产的思想早于电脑算法。20世纪中期,投资大师如格雷厄姆就曾使用市盈率等指标筛选股票,但手工排序效率极低。1980年代至1990年代,随着个人电脑和电子表格的普及,投资者开始使用Excel等工具进行简单排序来选股。基于排序的横截面策略(例如按动量或价值因子排名)在学术研究中也逐渐兴起。
- 1988年,Jegadeesh 和 Titman 等学者开始系统研究动量效应,随后基于收益率排序的投资组合构建方法被广泛讨论。
- 1990年代末至2000年代初,互联网交易平台和在线交易社区的兴起,使得个人开发者开始分享自定义指标和策略函数。公开交易社区中陆续出现各种排序函数,但最初多使用内置的排序函数。
- 2010年前后,随着脚本语言在交易平台中的流行,越来越多的开发者开始编写底层算法教学型函数,包括使用冒泡排序这类经典算法来演示原理。因此,本文所提到的具体函数,其类型(面向交易用户的排序索引函数)大约在2000年代后期开始流行,并在2010年代成为公开交易社区中的常见教学示例。
综上所述,冒泡排序算法起源于1956年前后;而“获取排序索引”这类交易辅助函数,若从公开源码生态角度看,大致成熟于2010年以后。但需要强调的是,这属于“该类方法”的考证,并非针对 RicardoSantos 所写的具体脚本。
改进建议
第一,更换更高效的排序算法。在保留“获取排序索引”功能的前提下,可以将底层的冒泡排序替换为快速排序或归并排序。这样在大规模标的下依然能快速得到索引序列。如果允许,也可以直接使用交易社区或语言库中已有的高效排序函数,再将数值排序结果转换为索引。
第二,增加对缺失值(NaN)的处理。在排序前显式地移除或过滤掉空值,或者把它们排在最后面。这样可以防止停牌股票或未上市品种干扰正常标的的排名。具体实现上,可以先把有效值提取到新数组,排序后再通过原索引映射回原数组。
第三,支持多维排序或权重评分。可以扩展函数,使其接受多个数组,并根据不同权重计算综合得分后再排序。例如,动量因子占60%,波动率因子占40%,加权求和后得到综合得分数组,再返回排序索引。这样能够满足更复杂的多因子选股需求。
第四,增加排序方向参数。让用户可以通过参数指定升序或降序,而不需要修改函数主体。同时,还可以提供输出“相对排名”或“百分位排名”的选项,以便策略直接使用标准化得分,而不必反复处理索引。
第五,加入稳定性和一致性检查。当多个标的的因子值完全相同(并列排名)时,可以根据其他辅助规则(如成交量、市值)打破平局,以避免频繁调仓或随机选择。同时,可以提供一个接口,让用户自定义比较规则,从而更灵活地适应不同策略。
第六,优化内存与性能。冒泡排序虽然代码简洁,但如果在循环中反复调用,每次排序都会创建临时数组,可能增加内存开销。建议在函数内部尽可能重用数组,或者使用原地排序的变体,并在大数组场景下使用分治算法来降低栈空间消耗。
第七,增加排序结果的可视化输出。在调试或回测时,我们往往希望直观地看到原数组、排序后数组、排序索引之间的对应关系。函数可以返回一个结构体或字典,同时包含已排序值、排序索引、原位置等字段,从而便于在图表上标注“排名第一的标的”。
总结
“Function - Sorted Indices”虽然是一个短小的脚本,但它揭示了量化交易中一个非常核心的思维:我们不只需要数值的大小顺序,更需要知道这些顺序对应的对象是谁。通过冒泡排序,这个函数实现了从“数值数组”到“位置索引数组”的转换,为我们打开了一扇轻松处理多标的排名问题的窗户。
从实用角度看,它广泛应用于动量轮动、因子选股、异常检测、风险控制等场景。尽管冒泡排序具有效率低、扩展性差的缺点,但我们可以通过算法替换、参数优化、缺失值过滤等手段将其改造得更加强大。起源考证显示,冒泡排序算法自上世纪50年代诞生以来,已经走过了几十年的历程,而它被移植到交易社区中,正是无数开发者将计算机科学基础应用到金融实践的一个缩影。
最终,这篇文章想传递的不仅仅是某个函数的功能说明,而是希望读者能够从这个小工具出发,深入思考排序在交易中的价值。当你下一次看到一组涨跌幅数据时,不妨问问自己:如果我要选取最强的前20%,该如何利用排序索引来完成?又该如何避免因为一家公司的停牌而导致整个排序混乱?带着这些问题去研究和改良,你会逐渐形成属于自己的高效策略工具箱。这正是公开交易社区分享基础函数的意义所在——它给了我们一块积木,让我们自己搭建出通往盈利之路的城堡。