有趣的问题(货币套利)

Posted

技术标签:

【中文标题】有趣的问题(货币套利)【英文标题】:Interesting Problem (Currency arbitrage) 【发布时间】:2010-02-17 16:30:30 【问题描述】:

套利是利用货币兑换价值的差异来赚取利润的过程。 考虑一个人,他从一定数量的货币 X 开始,经过一系列交换,最后得到更多的 X(比他最初拥有的)。 给定 n 种货币和一张 (nxn) 汇率表,设计一个算法,假设一个人不会多次进行一次兑换,他应该使用该算法来获得最大利润。

我想到了这样的解决方案:

    使用修改后的 Dijkstra 算法查找单源最长产品路径。 这给出了从源货币到其他货币的最长产品路径。 现在,对其他货币进行迭代并乘以迄今为止的最大乘积,w(curr,source)(边到源的权重)。 选择所有此类路径中的最大值。

虽然这看起来不错,但我仍然怀疑这个算法的正确性和问题的完整性。(即问题是 NP-Complete 吗?)因为它有点类似于旅行商问题。

为这个问题寻找您的 cmets 和更好的解决方案(如果有的话)。

谢谢。

编辑: 谷歌搜索这个话题把我带到了这个here,其中已经解决了套利检测问题,但没有解决最大套利的交换。这可以作为参考。

【问题讨论】:

这是作业吗?我知道这是我几年前学习的算法课程教科书中的一个问题。 是的,它来自 Dasgupta 的算法(并不是说这是唯一出现的地方)。而且我看不出与旅行推销员有任何联系。您并没有尝试访问每种货币。 绝对是功课。主题也没有帮助。 不确定您所说的“产品路径”是什么意思 乘积路径,他的意思是路径的权重是它的边权重的乘积,而不是(通常的)总和。 【参考方案1】:

此处不能使用 Dijkstra,因为无法修改 Dijkstra 以返回最长路径,而不是最短路径。一般来说,longest path problem 实际上正如您所怀疑的那样是 NP 完全的,并且与您所建议的旅行商问题有关。

您正在寻找的(如您所知)是一个边权重的乘积大于 1 的循环,即 w1 * w2 * w3 * ... > 1. 如果我们取双方的对数,我们可以重新设想这个问题,把它变成一个总和而不是一个乘积:

log (w1 * w2 * w3 ... ) > log(1)

=> log(w1) + log(w2) + log(w3) ... > 0

如果我们取负数...

=> -log(w1) - log(w2) - log(w3) ...

所以我们现在只是在图中寻找负循环,这可以使用 Bellman-Ford 算法(或者,如果您不需要知道路径,则使用 Floyd-Warshall 算法)来解决

首先,我们变换图形:

for (int i = 0; i < N; ++i)
  for (int j = 0; j < N; ++j)
    w[i][j] = -log(w[i][j]);

然后我们执行标准的 Bellman-Ford

double dis[N], pre[N];

for (int i = 0; i < N; ++i)
   dis[i] = INF, pre[i] = -1;

dis[source] = 0;

for (int k = 0; k < N; ++k)
  for (int i = 0; i < N; ++i)
    for (int j = 0; j < N; ++j)
      if (dis[i] + w[i][j] < dis[j])
        dis[j] = dis[i] + w[i][j], pre[j] = i;

现在我们检查负循环:

for (int i = 0; i < N; ++i)
  for (int j = 0; j < N; ++j)
    if (dis[i] + w[i][j] < dis[j])
      // Node j is part of a negative cycle

然后您可以使用pre 数组来查找负循环。从pre[source] 开始,然后继续工作。

【讨论】:

抱歉,我误读了这个问题。啊,所以你在图中寻找机会周期。这需要 Bellman-Ford 算法将边权重转换为 w' = -ln(w)。这种转换的原因是它减少了在图中找到负循环的问题。我将使用正确的代码更新我的解决方案。 @Neeraj:但众所周知log (A*B) = log A + log B... @Neeraj,我知道日志转换,因为这是一个众所周知的问题,有一个众所周知的解决方案。正如 Kenny 所说,众所周知,您可以使用对数将乘积转换为和。 @Neeraj,我可以向您保证这是正确的解决方案,并且评估周期不是 NP Complete。您说的是正确的,如果存在负周期,则没有最短路径,但是如果没有负周期,那么您的问题的答案是简单地不执行交换,因为您需要一个周期(回到原始货币)除了负循环之外的任何事情都是无利可图的。如果您不相信我,只需谷歌“套利问题”并亲自查看。正如我所说,这是一个众所周知的问题,这就是解决方案。 cs.ucdavis.edu/~amenta/f05/hw5.pdf 不同之处在于您不是在评估每个周期,只需选择一个。【参考方案2】:

当只有大约 150 个currencies currently in existence 时,这是一个 NP 难题这一事实并不重要,而且我怀疑您的外汇经纪商无论如何最多只会让您交易 20 对。因此,我对n 货币的算法是:

    制作深度树n 和分支因子n。树的节点是货币,树的根是你的起始货币X。两个节点(货币)之间的每个链接都有权重w,其中w 是两种货币之间的汇率。 您还应该在每个节点上存储累积外汇汇率(通过将树中高于它的所有外汇汇率相乘计算得出)。这是根(货币X)和该节点的货币之间的汇率。 遍历树中代表货币X 的所有节点(也许您应该保留指向这些节点的指针列表以加快算法的这一阶段)。其中只有n^n(在大O 表示法方面效率非常低,但请记住您的n 大约是20)。累积外汇汇率最高的是您的最佳外汇汇率,并且(如果为正)这些节点之间通过树的路径表示以货币 X 开始和结束的套利周期。 请注意,您可以在步骤 1 中生成树时遵循以下规则来修剪树(从而将复杂性从 O(n^n) 降低到 O(n)
      如果您到达货币节点X,请不要生成任何子节点。 为了将分支因子从n 减少到1,在每个节点生成所有n 子节点,并且只添加具有最大累积外汇汇率的子节点(当转换回货币X 时)。

【讨论】:

分支因子应该是n-1【参考方案3】:

恕我直言,这个问题有一个简单的数学结构,它适用于一个非常简单的 O(N^3) 算法。给定一个 NxN 货币对表,如果不可能套利,表的 reduced row echelon form 应该只产生 1 个线性独立行(即所有其他行都是第一行的倍数/线性组合)。

我们可以perform gaussian elimination 并检查我们是否只得到 1 个线性独立的行。如果没有,额外的线性独立行将提供有关可用于套利的货币对数量的信息。

【讨论】:

这可能是某种其他形式的套利算法,但几乎可以肯定不是这里讨论的跨货币套利。还是我在这里遗漏了什么?【参考方案4】:

记录转化率。然后,您试图在具有正、负或零加权边的图中找到从 X 开始且总和最大的循环。这是一个 NP-hard 问题,因为在未加权图中找到最大循环的简单问题是 NP-hard。

【讨论】:

您不太可能找到 NP-hard problem 的多项式时间解。请注意,Amit 说的是 NP-hard problem (不是算法,不管你认为这意味着什么)。另请注意,他说的是 NP-hard 问题(不完整)。 @rgrig.. 是的,如果以这种方式建模,问题将是 NP 完全的,但可能有其他方法来建模可能不是 NP 完全的问题。 @Neeraj 澄清一下:找到一种盈利方式,即找到一个正循环是在 P 中(Poita_ 有一个解决方案)。但是找到最高的利润路径是 NP 难的。【参考方案5】:

除非我完全搞砸了,否则我相信我的实现可以使用 Bellman-Ford 算法:

#include <algorithm>
#include <cmath>
#include <iostream>
#include <vector>


std::vector<std::vector<double>> transform_matrix(std::vector<std::vector<double>>& matrix)

    int n = matrix.size();
    int m = matrix[0].size();
    for (int i = 0; i < n; ++i)
    
        for (int j = 0; j < m; ++j)
        
            matrix[i][j] = log(matrix[i][j]);
        
    
    return matrix;


bool is_arbitrage(std::vector<std::vector<double>>& currencies)


    std::vector<std::vector<double>> tm = transform_matrix(currencies);

    // Bellman-ford algorithm
    int src = 0;
    int n = tm.size(); 
    std::vector<double> min_dist(n, INFINITY);

    min_dist[src] = 0.0;

    for (int i = 0; i < n - 1; ++i)
    
        for (int j = 0; j < n; ++j)
        
            for (int k = 0; k < n; ++k)
            
                if (min_dist[k] > min_dist[j] + tm[j][k])
                    min_dist[k] = min_dist[j] + tm[j][k];
            
        
    

    for (int j = 0; j < n; ++j)
    
        for (int k = 0; k < n; ++k)
        
            if (min_dist[k] > min_dist[j] + tm[j][k])
                return true;
        
    
    return false;



int main()

    std::vector<std::vector<double>> currencies =  1, 1.30, 1.6, .68, 1, 1.1, .6, .9, 1 ;
    if (is_arbitrage(currencies))
        std::cout << "There exists an arbitrage!" << "\n";
    else
        std::cout << "There does not exist an arbitrage!" << "\n";



    std::cin.get();

【讨论】:

以上是关于有趣的问题(货币套利)的主要内容,如果未能解决你的问题,请参考以下文章

金融量化1 永续合约资金费率资金费率套利持仓爆仓平仓移仓穿仓盘口盘口流动性等加密货币金融量化相关概念

HDOJ 1217 Arbitrage (最短路)

影子银行

R语言无套利区间模型:正向套利和反向套利次数收益率分析华泰柏瑞300ETF可视化

蝶式套利(butterfly spread)

金融英语常用词汇70个-按字母排列