交易中的神经网络:针对金融市场的多模态、扩增工具型智代(FinAgent)
概述
金融市场在维持经济稳定、参与资本配置、以及风险管理方面扮演着重要角色。现代金融交易系统广泛采用技术分析,强化了这些流程;然而,在市场高度波动、且变化多端的条件下,它们往往面临重大限制。基于规则的交易系统僵化、且难以适应快速变化的市场条件,频繁导致效率下降。基于强化学习(RL)的系统展现出更高的适应性,但也存在其短处:
- 对大量训练数据的需求很高;
- 决策往往难以解释;
- 很难在不同市场条件下普适;
- 对市场噪音敏感;
- 整合多模态市场信息受约束。
近年来,大语言模型(LLMs)在决策制定方面展现出惊人潜力,其应用范围已超越自然语言处理领域。记忆模块与规划模块的集成,令 LLMs 能够适应动态变化的环境。多模态 LLMs 通过处理文本和视觉信息进一步强化了这些能力,而外部工具的加入拓宽了这些模型所能处理任务的范围,包括复杂的金融场景。
尽管在金融数据分析方面取得了成功,LLM 智代仍面临若干局限:
- 有限的多模态数值、文本、和视觉数据处理;
- 需要针对来自多源的数据进行精确整合;
- 对快速变化市场的适应能力低;
- 难以运用专业知识和传统方法;
- 决策制定透明度不足。
研究《金融交易的多模态基础智代:工具增强、多元化、与普适》的作者试图通过引入 FinAgent 框架来解决这些局限 — 这是一种多模态基础智代,即整合文本和视觉信息来分析市场动态和历史数据。FinAgent 的关键组件包括多模态数据处理,识别关键市场趋势、分析短期和长期决策的两层反射模块、把分析噪声最小化的记忆系统,以及融合专家知识和高级交易策略的决策制定模块。
FinAgent 算法
FinAgent 框架是一个分析金融市场数据,并制定明智决策的工具。它为用户提供了一套金融产品,帮助理解市场过程、预测动态、以及优化交易策略。FinAgent 由五个主要模块组成,它们相互作用形成一个用于数据处理和决策制定的生态系统。
市场分析模块负责收集、处理和解读各种数据来源,包括市场新闻、价格变动、和公司报告。通过先进方法,该模块识别隐藏的形态,令智代能够适配当前市场条件调整其动作。
为了达成最大效率,FinAgent 分析当前市场数据,以及积累的历史信息。每日更新,如新闻、价格波动、及其它运营数据,构成短期决策制定的基础。与此并发,分析过去事件有助于揭示长期形态,从而开发未来运营的韧性策略。这种双重方式确保了高度的适应性和灵活性。
FinAgent 的数据采集过程用到大语言模型(LLM),将市场信息转化为文本查询。这些查询随后用来在记忆模块的历史数据库中搜索类似数据。应用向量相似度方法强化搜索准确性,专注于最相关的信息。此外,系统采用专用文本字段,提升数据处理能力,防止关键细节丢失。所得数据经过结构化和汇总,简化分析并将次要信息的影响最小化。
两层反射模块的功能类似于人类学习过程。低层反射模块识别价格变化与市场动态之间的相关性,实现短期市场波动预测,这对于在较小时间尺度操作的交易者尤为重要。与此同时,高层反射模块基于历史数据和以往交易结果分析更复杂、更深层次的关联性。这允许误差检测和纠正策略的开发。该过程包括可视化关键点,如买卖时刻,并评估其有效性。迭代学习方式令智代能够积累经验,并利用这些经验改进未来的动作。
记忆模块在确保 FinAgent 稳定运营中扮演着核心角色。它负责存储数据,并实现高效的提取。向量搜索方法允许快速访问庞大数据集合中的相关信息,降低噪音,并强化分析精度。FinAGent 中的记忆机制为其提供了重要的上下文和认知能力。在金融交易中,记忆对于准确性、适应性、以及从过往经验中学习尤为重要。它允许智代利用当前新闻和报告预测未来市场变化,适应波动性环境,并持续优调策略。
决策制定模块整合并处理关键数据,包括市场摘要、低层反射的价格动态分析、以及以往决策的洞察。它还协同专业投资建议的辅助工具,及经过时间考验的交易策略。该模块的一个关键功能是市场情绪分析,基于当前价格走势预测涨跌趋势,并反射所学教训。此外,它还会参考专家建议,并评估传统指标的有效性。
基于这些分析与当前财务背景的结合,该模块生成最终决策:是否买入、卖出、亦或持有某项资产。应用情境学习原则来创建逻辑决策结构。这确保每一次交易动作都合理,根植于在相关背景内对市场动态的全面理解。这样的方式能够更好地适应市场条件,并促成明智、且具战略意义的决策。
因此,FinAgent 体现为一个综合性工具,集成了数据分析、反射、和流程自动化。它令交易者和分析师能够有效适应市场、风险最小化、强化盈利能力,为策略规划开辟新机遇。
FinAgent 框架的原始可视化如下:

实现 MQL5 版本
在验证了 FinAgent 框架的理论层面后,我们现在转入本文的实践部分,于此我们按照我们对所提议方式的愿景实现 MQL5 版本。
重点要注意,类似于以往工作,我们排除了大语言模型的运用,并致力于利用我们现有工具实现所提议的方式。
我们从创建低层和高层反射模块开始工作。
低层反射模块
在开始开发低层反射模块时,值得留意的是 FinAgent 框架作者提议的记忆模块架构。记忆模块在概念上可划分为三个对象,即分别从市场收集模块分析所需的信息,以及两个不同层次的反射模块。这允许我们在不改变整体信息流的情况下重造模型的模块,并将各个记忆模块整合到相应的模块之中。取该特性优点,我们把一个针对该信息流的记忆模块集成到低层反射模块之中。
修改后的低层反射模块在 CNeuronLowLevelReflection 对象内实现 — 其结构如下所示。
class CNeuronLowLevelReflection : public CNeuronMemory { protected: CNeuronLSTMOCL cChangeLSTM; CNeuronMambaOCL cChangeMamba; CNeuronRelativeCrossAttention cCrossAttention[2]; //--- virtual bool feedForward(CNeuronBaseOCL *NeuronOCL) override; virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL) override; virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) override; public: CNeuronLowLevelReflection(void) {}; ~CNeuronLowLevelReflection(void) {}; //--- virtual bool Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint window, uint window_key, uint units_count, uint heads, ENUM_OPTIMIZATION optimization_type, uint batch) override; //--- virtual int Type(void) override const { return defNeuronLowLevelReflection; } //--- virtual bool Save(int const file_handle) override; virtual bool Load(int const file_handle) override; //--- virtual bool WeightsUpdate(CNeuronBaseOCL *source, float tau) override; virtual void SetOpenCL(COpenCLMy *obj) override; //--- virtual bool Clear(void) override; };
以记忆对象作为父类,能够把市场信息分析流程、与认知记忆功能无缝集成到统一的信息管道当中。这不仅令系统能够高效处理原始数据,还能参考历史信息,为当前分析提供背景信息。
类结构包括各种架构的重现对象,以及交叉注意力模块。这些组件在信息处理和决策制定中扮演着关键角色。在实现对象方法期间,会详解它们的目的和功能,从而更深度理解它们对系统性能的影响。
所有内部对象都声明为静态,允许我们将类构造和析构函数留空。这些声明和继承对象的初始化均在 Init 方法中执行。
bool CNeuronLowLevelReflection::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint window, uint window_key, uint units_count, uint heads, ENUM_OPTIMIZATION optimization_type, uint batch) { if(!CNeuronMemory::Init(numOutputs, myIndex, open_cl, window, window_key, units_count, heads, optimization_type, batch)) return false;
该方法的参数结构完全继承自父类。在方法主体中,我们立即调用父类同名方法,传递所有接收到的参数。
父类方法已实现了控制接收参数、及初始化继承对象的算法。
接下来,我们初始化新声明的对象。我们首先初始化两个重现对象,旨在识别分析参数的动态。针对这些对象采用不同的架构解决方案,确保了更深度的分析,令系统能够有效适应短期和长期变化,检测多个时间尺度上的趋势。
int index = 0; if(!cChangeLSTM.Init(0, index, OpenCL, window, units_count, optimization, iBatch)) return false; index++; if(!cChangeMamba.Init(0, index, OpenCL, window, 2 * window, units_count, optimization, iBatch)) return false;
分析结果经由交叉注意力模块整合成统一解决方案,其有效结合来自不同源头和层级的信息,专注于参数之间的关键关系和依赖关系。交叉注意力有助于发现隐藏的形态,及其相互联系,提升决策品质,并提供更准确、连贯的信息感知。
for(int i = 0; i < 2; i++) { index++; if(!cCrossAttention[i].Init(0, index, OpenCL, window, window_key, units_count, heads, window, units_count, optimization, iBatch)) return false; } //--- return true; }
所有内部对象初始化之后,我们将操作的逻辑结果返回调用程序,并结束方法执行。
下一阶段是构建低层反射模块的前馈算法,已在 feedForward 方法内实现。
bool CNeuronLowLevelReflection::feedForward(CNeuronBaseOCL *NeuronOCL) { if(!cChangeLSTM.FeedForward(NeuronOCL)) return false; if(!cChangeMamba.FeedForward(NeuronOCL)) return false;
该方法接收指向原始数据对象的指针,其中包含环境当前状态的描述。这些数据传递给我们重现对象的相应方法,来识别参数动态,令系统能够追踪环境变化,并相应适配决策。
然后,我们利用交叉注意力模块,据检测到的信息变化,丰富当前环境状态的描述。这允许新数据与现有数据整合,强化上下文,并提升对环境动态的感知。该方式创建一个“轨迹跟踪”,反射所分析状态的变化。
if(!cCrossAttention[0].FeedForward(NeuronOCL, cChangeLSTM.getOutput())) return false; if(!cCrossAttention[1].FeedForward(cCrossAttention[0].AsObject(), cChangeMamba.getOutput())) return false;
分析结果会传递至已由父类实现的记忆模块。该模块识别持久性趋势,形成预测将至价格走势的基础。
return CNeuronMemory::feedForward(cCrossAttention[1].AsObject()); }
如是所见,三个内部对象在前馈通验期间处理描述环境的原产数据。由此,在反向传播通验期间,我们必须从这三个信息流中收集误差梯度。误差梯度分派算法已在 calcInputGradients 方法中实现。
bool CNeuronLowLevelReflection::calcInputGradients(CNeuronBaseOCL *NeuronOCL) { if(!NeuronOCL) return false;
该方法接收指向同一源数据对象的指针。然而,这次我们传递一个误差梯度,反射源数据对模型输出的影响。该方法首先检查所接收指针的有效性,否则数据传送是不可能的。
利用父类,我们将误差梯度经由记忆模块向下传播到交叉注意力模块。
if(!CNeuronMemory::calcInputGradients(cCrossAttention[1].AsObject())) return false; if(!cCrossAttention[0].calcHiddenGradients(cCrossAttention[1].AsObject(), cChangeMamba.getOutput(), cChangeMamba.getGradient(), (ENUM_ACTIVATION)cChangeMamba.Activation())) return false;
然后我们将所得误差分派到重现模块。
接着,我们将误差梯度经由三条信息流传播回原始输入层级。此处我们首先计算交叉注意力模块流水线的梯度。
if(!NeuronOCL.calcHiddenGradients(cCrossAttention[0].AsObject(), cChangeLSTM.getOutput(), cChangeLSTM.getGradient(), (ENUM_ACTIVATION)cChangeLSTM.Activation())) return false;
然后我们替换指向原始输入数据对象梯度缓冲区的指针,将误差传递到重现对象流水线中,并汇总不同信息流的数值。
CBufferFloat *temp = NeuronOCL.getGradient(); if(!NeuronOCL.SetGradient(cChangeMamba.getPrevOutput(), false) || !NeuronOCL.calcHiddenGradients(cChangeMamba.AsObject()) || !SumAndNormilize(NeuronOCL.getGradient(), temp, temp, iWindow, false, 0, 0, 0, 1)) return false;
if(!NeuronOCL.calcHiddenGradients(cChangeLSTM.AsObject()) || !SumAndNormilize(NeuronOCL.getGradient(), temp, temp, iWindow, false, 0, 0, 0, 1)) return false;
最后,数据缓冲区的指针恢复到原始状态,确保为后续使用做好充分准备。
if(!NeuronOCL.SetGradient(temp, false)) return false; //--- return true; }
在方法结束处,返回操作的逻辑结果至调用程序。
构造低层反射模块方法算法的讨论至此完结。您能在附件中找到该类、及其所有方法的完整代码。
高层反射模块
接下来,我们需要创建高层反射模块,专注于相互依存关系的更深度、更全面的分析。不同于低层反射模块专注于短期变化、及当前动态,高层反射模块检验自以往交易操作中识别出的长期趋势和关系。
该模块的主要意向是透彻评估先前决策的有效性,以及分析实际结果。该过程令我们能够评估当前交易策略的应用效果,及其与设定目标的契合度。分析的关键部分是识别策略的优缺点。
此外,高层反射模块预计将提供优化交易策略的具体建议。这些建议旨在提升交易的盈利能力和/或降低风险。
显然,为了全盘实现高层反射模块的功能,显然需要更多的输入数据。首先,我们需要分析智代的实际动作。
进而,我们还需要制定决策时刻的环境描述。这就能评估每个决策在特定市场条件下的合理程度,以及这些条件如何影响结果。
同等重要的是盈亏信息,其有助于评估决策对财务结果的影响。这些数据不仅能评估交易成功,还能辨别策略中或许需要调整的潜在弱点。
甚至,为了高效运行高层反射模块,分析结果必须要加以存储,以备未来使用。这令系统能够参考以往的结论,并基于积累的经验改进决策制定。FinAgent 框架的作者考虑到了这一需求,并将相应的记忆模块整合进架构之中。
因此,我们得到四条输入信息流:
- 智代动作;
- 环境状态;
- 财务结果(账户状态);
- 记忆。
在当前实现的对象间数据交换接口中,只允许两条信息流。
类似于低层反射模块,高层反射模块使用记忆模块作为新对象的父类。这种设计允许记忆流直接在对象内部形成,摒除额外输入源的需求。如是结果,模块变得更加自主,并能够在自身结构内高效处理、并记忆数据。
此外,高层反射模块的输出很可能被解释为智代动作张量的潜在表示。这是因为智代的动作本质上是由我们模块所提供结果的函数。由此,分析成果中的变化直接影响智代动作的调整。该方式能够创建更具适应性的行为模型,令智代动作基于高层反射获得的信息进行动态优化。
故此,利用上述假设和架构决策,我们构造了一个新对象接口的基本模型,配备两个输入数据源来操作。这些信息为分析当前状况和之前动作的结果提供了必要信息。
不过,还有一个更重要的层面。为了分析智代的整体行为政策,而非仅关注单笔交易,FinAgent 框架作者建议包含带有已执行交易标记的账户余额图表分析。该方式为智代的策略结果提供了一个宽泛视野。在我们的实现中,我们通过添加重现对象来处理表示账户状态和智代动作的张量,再现了这一解决方案。这些对象为余额动态与交易行为之间的关系建模,能更深度地分析所应用政策。
上述解决方案已在新对象 CNeuronHighLevelReflection 中实现。该对象的结构如下所示。
class CNeuronHighLevelReflection : public CNeuronMemory { protected: CNeuronBaseOCL cAccount; CNeuronLSTMOCL cHistoryAccount; CNeuronRelativeCrossAttention cActionReason; CNeuronLSTMOCL cHistoryActions; CNeuronRelativeCrossAttention cActionResult; //--- virtual bool feedForward(CNeuronBaseOCL *NeuronOCL) override { return false; } virtual bool feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) override; virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL) override { return false; } virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput, CBufferFloat *SecondGradient, ENUM_ACTIVATION SecondActivation = None) override; virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) override {return false; } virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) override; public: CNeuronHighLevelReflection(void) {}; ~CNeuronHighLevelReflection(void) {}; //--- virtual bool Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint window, uint window_key, uint units_count, uint heads, uint desc_account, uint actions_state, ENUM_OPTIMIZATION optimization_type, uint batch) override; //--- virtual int Type(void) override const { return defNeuronHighLevelReflection; } //--- virtual bool Save(int const file_handle) override; virtual bool Load(int const file_handle) override; //--- virtual bool WeightsUpdate(CNeuronBaseOCL *source, float tau) override; virtual void SetOpenCL(COpenCLMy *obj) override; //--- virtual bool Clear(void) override; };
对象结构包含熟悉的可覆写方法集合,提供灵活性和适应性的功能,且无需破坏整体系统架构。此外,新类包含若干个内部对象,其功能将在方法实现期间详述。
所有内部对象都声明为静态,允许我们将类构造和析构函数留空。所有声明和继承的对象,都在 Init 方法中执行初始化。
bool CNeuronHighLevelReflection::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint window, uint window_key, uint units_count, uint heads, uint desc_account, uint actions_state, ENUM_OPTIMIZATION optimization_type, uint batch) { if(!CNeuronMemory::Init(numOutputs, myIndex, open_cl, 3, window_key, actions_state / 3, heads, optimization_type, batch)) return false;
初始化方法接收一组定义所创建对象架构的常量。其中一个参数指定了智代动作向量的维度。
如上所注,该对象的输出预期会产生智代动作张量的潜在表示。智代的每笔交易操作都由三个参数来描述:交易量、止损水平、和止损水平。买入和卖出交易以分开的张量行表示,摒弃了以额外参数来指示交易方向的需要。
在方法主体中,我们调用父类的同名方法(本例中使用记忆模块)。参数包括智代的动作张量数据,已根据上述假设结构化。该方式维持了功能连续性,并利用父类的能力进行数据处理。所有这些都令您能够将当前对象的分析结果集成到整体模型结构之中,确保后续对象的一致性和可访问性。
父类操作执行成功后,我们初始化嵌套对象。首先,我们初始化一个基本全连接层,用来正确处理第二条信息流。
int index = 0; if(!cAccount.Init(0, index, OpenCL, desc_account, optimization, iBatch)) return false;
接下来,我们初始化重现模块,以便跟踪账户状态变化。
index++; if(!cHistoryAccount.Init(0, index, OpenCL, desc_account, 1, optimization, iBatch)) return false;
为了分析最新交易决策的有效性,我们利用交叉注意力模块来验证智代动作与环境状态张量之间的依赖关系。
index++; if(!cActionReason.Init(0, index, OpenCL, iWindow, iWindowKey, iUnits, iHeads, window, units_count, optimization, iBatch)) return false;
动作动态收集在相应的重现模块之中。
index++; if(!cHistoryActions.Init(0, index, OpenCL, iWindow, iUnits, optimization, iBatch)) return false;
然后,我们通过比较交叉关注区模块内的交易活动动态与账户状态,评估智代政策的有效性。
index++; if(!cActionResult.Init(0, index, OpenCL, iWindow, iWindowKey, iUnits, iHeads, desc_account, 1, optimization, iBatch)) return false;; //--- return true; }
所有内部对象初始化之后,我们将操作的逻辑结果返回调用程序,并结束方法执行。
接下来,我们在 feedForward 方法中构造高层反射模块的前馈通验算法。
bool CNeuronHighLevelReflection::feedForward(CNeuronBaseOCL *NeuronOCL, CBufferFloat *SecondInput) { if(!NeuronOCL || !SecondInput) return false;
该方法接收指向两条信息流输入对象的指针。我们会立即验证指针的有效性。
应当注意的是,第二条信息流由数据缓冲区表示。在后续操作前,我们取专门准备的内部对象的指针替换缓冲区。这令我们能够利用内部对象的基础接口来处理接收到的数据。
if(cAccount.getOutput() != SecondInput) { if(cAccount.Neurons() != SecondInput.Total()) if(!cAccount.Init(0, 0, OpenCL, SecondInput.Total(), optimization, iBatch)) return false; if(!cAccount.SetOutput(SecondInput, true)) return false; }
然后我们利用重现模块评估账户状态的变化。
if(!cHistoryAccount.FeedForward(cAccount.AsObject())) return false;
最近交易决策的验证,则是由交叉注意力模块来检查是否与当前环境状态对立。
if(!cActionReason.FeedForward(this.AsObject(), NeuronOCL.getOutput())) return false;
重点是在描述当前环境状态张量、与决策制定张量之间区分。模型在动作执行完毕,且系统切换到新状态之后,接收其环境状态张量。然而,这一差异预计不会显著影响分析结果。
每增加一根柱线,模型就会触发一次新迭代。所分析历史的深度远超一根柱线,提供了详尽且综合性的分析。每次状态转变时,数据在多模态时间序列中平移一个元素,而最早的柱线则被排除。该柱线对当前动作的影响预计极小,故此将其移除对分析影响不大。
囊括的新柱线,此刻制定的决策未知,为评估交易的正确性提供了额外信息。该机制有助于评估交易后果,以及实际市场变化与先前预测之间的对应关系。
分析结果会被传递给重现模块,监控智代的政策。
if(!cHistoryActions.FeedForward(cActionReason.AsObject())) return false;
然后,我们利用交叉注意力模块依据财务结果背景分析该政策。
if(!cActionResult.FeedForward(cHistoryActions.AsObject(), cHistoryAccount.getOutput())) return false;
接着,更新结果缓冲区,保留智代的最新动作,如此就能正确的反向通验,并调用父类方法执行记忆模块功能。
if(!SwapBuffers(Output, PrevOutput)) return false; //--- return CNeuronMemory::feedForward(cActionResult.AsObject()); }
这些操作的逻辑结果会返回给调用程序,并结束方法。
前馈通验方法的实现完成后,我们继续组织反向传播通验。反向传播操作使用线性算法,这不应带来实现上的困难。故以,我认为没必要再详细讨论。高层反射对象及其所有方法的完整代码可在附件中获取。
我们已至文章末段,但与 FinAgent 框架实现相关的工作尚未完成。我们稍事休息,在下一篇文章中,我们将把这个项目带至逻辑上的结局。
结束语
本文探讨了 FinAgent 框架,这是一种创新解决方案,整合文本和视觉信息,综合分析市场动态和历史数据。FinAgent 用到五个关键组件,在交易决策制定时提供高准确性和适应性。这令它成为开发高效且灵活的交易策略的有力工具,能够在波动的市场环境中运作。
在实践章节,我们实现了两个反射模块的 MQL5 版本。在下一篇文章中,我们将继续这项工作,完成框架,并基于真实历史数据测试已实现解决方案的有效性。
参考
文章中所用程序
| # | 名称 | 类型 | 说明 |
|---|---|---|---|
| 1 | Research.mq5 | 智能系统 | 收集样本的智能系统 |
| 2 | ResearchRealORL.mq5 | 智能系统 | 利用 Real-ORL 方法收集样本的智能系统 |
| 3 | Study.mq5 | 智能系统 | 模型训练 EA |
| 4 | Test.mq5 | 智能系统 | 模型测试智能系统 |
| 5 | Trajectory.mqh | 类库 | 系统状态和模型架构描述结构 |
| 6 | NeuroNet.mqh | 类库 | 创建神经网络的类库 |
| 7 | NeuroNet.cl | 代码库 | OpenCL 程序代码 |
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/16850
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
将人工智能(AI)模型集成到已有的MQL5交易策略中
新手在交易中的10个基本错误
价格行为分析工具包开发(第二十部分):外部资金流(4)——相关性路径探索器
如果您也能用英语撰写文章,那就再好不过了。
您是用哪种论坛语言发帖的?这篇文章也有英文版 ...交易中的神经网络:金融市场的多模态、工具增强型代理(FinAgent)
这是一个多语言讨论主题,正是因为这篇文章被翻译成了多种语言。