神经网络在交易中的应用:用于智能体自适应行为的分层技能发现(HiSSD)
引言
近年来,协作式多智能体强化学习(MARL)的应用价值愈发凸显。该技术已广泛应用于诸多领域,包括游戏、自动驾驶、物流、社会行为分析,其中金融市场是尤为重要的应用场景。在需要多套策略或多个智能体协同运作的场景中,传统方法往往效果不佳。而多智能体强化学习(MARL)在这类场景下表现出色。
尽管如此,该领域仍存在诸多难题。搭建高精度仿真环境、或是与真实环境持续在线交互,都需要耗费大量资源。在实际应用中,智能体需要在动态变化的环境中运行:参与主体数量、任务目标、环境参数都会随时间发生改变。因此,业内愈发倾向于训练可高效自适应并尽可能高效地在任务间迁移知识,且不产生额外成本。
传统做法是先针对单一任务训练智能体,再针对新任务做微调。但该方式存在不少缺陷。首先,智能体需要重新与新环境交互,成本高昂。其次,针对固定数量智能体训练的模型,扩展性较差。一旦智能体组成结构或任务参数发生变动,模型性能就会下降。
为解决上述问题,研究人员开始采用基于 Transformer 的网络架构。这类架构灵活性更强,模型不再受限于固定数量的智能体,能够适配新环境。这为通用协作行为模式的研究奠定了基础,这类行为技能可跨任务迁移,并在不同场景中复用。
目前业界已提出多种方案来实现这类通用技能。部分方案采用两阶段训练:先提取通用行为模式,再基于模式学习决策策略。还有方案融合离线学习与在线学习,以此加快模型对新环境的适配速度。
上述方法取得了明显成效,尤其有效降低了模型向同类任务迁移的成本。但这些方法同样存在短板。通用技能固然实用,却往往忽略了特定任务目标所需的专属特征。而任务能否达成,往往取决于这些细节。此外,多数方案忽略了交互行为的时序特征。协作行为并非一蹴而就,而是随时间逐步形成的。行为的先后顺序、协作的稳定性都至关重要。
为解决上述问题,论文《基于离线多任务数据学习可泛化技能以实现多智能体协作》提出了 HiSSD 框架,即分层可分离技能挖掘框架。该全新架构可同步学习通用技能与任务专属技能,无需对两类技能进行人为分离,也不受僵化约束限制。在该分层结构中,两类知识在层次结构中并行演化。
通用技能用于提炼通用协作模式。即便身处陌生环境,智能体也能依靠通用技能保持一致且协调的行动方式。通用技能是智能体行为的基础,适用于各类场景。与之相对,任务专属技能为特定任务量身打造。它能让智能体根据任务目标与环境状态,精准调整行为策略。
HiSSD 的核心思想是同步分层学习。该方法能够更深入地挖掘交互行为的时序特征与具体任务的场景信息。最终既能提升智能体的行为表现,也能保障训练得到的策略稳定迁移至新环境。
框架作者在主流测试平台 SMAC 与 MuJoCo 上开展实验,验证了该方法的有效性。即便是面对从未接触过的任务,经过 HiSSD 训练的智能体依旧展现出优秀的协作能力。其决策策略灵活、精准且稳定性强。
HiSSD 算法
HiSSD(分层可分离技能挖掘)是一种面向多智能体场景的新型训练算法,旨在实现技能跨任务迁移,同时保证智能体在去中心化环境中稳定运行。该算法的核心思路是将单个智能体的行为做分层拆解,分为两大模块:跨智能体、跨任务共享的通用技能,以及用于适配特定角色与目标的任务专属技能。
HiSSD 与现有多数方法的一大核心区别,是支持模型所有组件同步训练。它摒弃了分阶段、分步训练的模式,规划器、控制器、编码器、状态价值模型等所有模块统一联合优化。这种同步训练方式能够减少分层模块间的逻辑偏差,让智能体的行为更加连贯、高效。
模型基于离线数据集开展训练 DT = {Di},数据集中的每个子集 Di 对应一项独立任务。 在每个时间步,第 k 个智能体获取观测数据 ot,k。规划器依据观测数据生成通用技能表征ct,k:
![]()
该表征代表智能体的高层行为意图与整体策略规划。随后,任务专属技能编码器gω基于相同观测数据,生成任务专属技能表征zt,k。
![]()
通用技能、任务专属技能与当前观测数据一同输入控制器,由控制器输出智能体的执行动作。
![]()
环境价值模型采用适配多智能体场景的改进型隐式 Q 学习 (IQL)进行训练。模型根据所有智能体获得的综合奖励来估算状态价值:

损失函数采用截断均方误差:
![]()
规划器依据模型输出的价值估算结果进行训练。规划器的核心目标是选择能够导向最优未来状态的高层行为技能。通过最小化损失函数实现该目标,损失函数同时考量下一状态的价值与预测状态的似然:

也可采用指数形式的损失函数:

控制器与任务专属技能编码器,利用变分自编码器进行行为表征编码。其核心思路是训练控制器复现示范动作,同时挖掘任务的隐式特征结构。损失函数包含动作对数似然项,以及基于 KL 散度的正则项。
![]()
为保证智能体的技能能够区分不同任务,模型额外引入对比学习。编码器被训练为:对每个任务,表征离gω(q) 与同任务正样本 k+ 更接近,而与其他任务负样本k-更远。

其中 σ 为温度系数,采用指数移动平均(EMA)更新的编码器版本 gω† 用于稳定训练过程。
控制器的最终损失函数融合了行为学习损失与对比学习损失。
最终,HiSSD 构建出一套完整系统:从高层策略规划到底层执行动作,所有行为模块协同训练,可在去中心化模式下稳定高效运行。
下文为原论文给出的 HiSSD 框架结构图。

在MQL5中的实现
了解 HiSSD 框架的理论基础后,接下来进入实践部分,我们将基于 MQL5 实现这套算法。
模型设计核心原则
在讲解具体实现细节前,我们先梳理本次方案遵循的几项核心设计原则。
首先需要说明,HiSSD 框架原生面向多智能体学习场景。这与我们的应用场景有所区别:本次目标是针对单一金融品种训练交易策略。但该架构对于多任务特性显著的真实金融市场而言,具备很高的应用价值。我们对原有分层结构进行拓展,训练多个独立智能体,每个智能体负责分析、预测一类特定市场数据。为让最终决策更加连贯、高效,我们增设一个高层管理模型,汇总各智能体的分析结果并选出最优交易策略。这类多任务系统在人工智能领域已发展成熟,将其应用于金融市场,可有效应对各类影响因素与交易风险。
第二点关键内容:在原生 HiSSD 框架中,每个智能体仅依据本地观测数据输出动作。但金融市场中的价格、成交量及各类指标存在强关联性,因此我们需要对这一设定进行优化。我们从多维度原始市场时序数据中拆分出单变量序列,分发给不同智能体。与此同时,各类市场指标之间普遍存在相关性。例如,某一标的的价格波动会对其他标的产生影响。针对该问题,我们引入数据增强机制,让每个智能体都能获取反映整体市场状态的共享信息,以此提升决策质量。
最后着重强调模块化设计,这也是 HiSSD 框架的核心特性。该设计模式非常适配复杂的多任务系统。整套架构可拆分为多个独立模块,每个模块各司其职。模块化不仅便于单独优化各个组件,也能让系统灵活适配变化的市场环境。这一点在金融场景中尤为重要:市场行情瞬息万变,快速调整、迭代模型局部模块的能力,是实现稳定交易的关键。
技能编码器
我们首先实现技能编码器模块。这里先介绍 HiSSD 框架的一项重要架构特点:通用技能与任务专属技能均源自同一类输入信息。该输入信息即为智能体的本地观测数据。在本方案中,输入数据就是从描述市场状态的多维度时序数据中拆分得到的单变量序列。
基于这一特性,我们对两类技能提取任务采用统一的网络结构。该方案具备多项优势。第一,简化模型设计与问题排查工作。第二,可使用统一标准评估特征提取效果。最后,复用网络模块能够降低开发成本,也便于将模型拓展至新任务、新交易品种。
技能编码器由 CNeuronSkillsEncoder 类实现,其结构如下所示。
class CNeuronSkillsEncoder : public CNeuronSoftMaxOCL { protected: CNeuronCATCH cCrossObservAttention; CNeuronTransposeOCL cTranspose; CNeuronConvOCL cSkillsProjection[2]; CNeuronBaseOCL cPrevSkillsConcat; //--- virtual bool feedForward(CNeuronBaseOCL *NeuronOCL) override; virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) override; virtual bool calcInputGradients(CNeuronBaseOCL *prevLayer) override; public: CNeuronSkillsEncoder(void) {}; ~CNeuronSkillsEncoder(void) {}; //--- virtual bool Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint time_step, uint variables, uint skills, uint window, uint step, uint window_key, uint heads, ENUM_OPTIMIZATION optimization_type, uint batch); //--- virtual int Type(void) override const { return defNeuronSkillsEncoder; } //--- 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; };
该结构包含多个内部组件,各自承担不同功能。在实现类方法的过程中,我们会详细讲解各组件的作用。目前需要重点说明的是,所有对象均采用静态声明。因此类的构造函数与析构函数无需编写额外逻辑。所有内部对象的初始化工作都在 Init 方法中完成。
bool CNeuronSkillsEncoder::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint time_step, uint variables, uint skills, uint window, uint step, uint window_key, uint heads, ENUM_OPTIMIZATION optimization_type, uint batch) { if(!CNeuronSoftMaxOCL::Init(numOutputs, myIndex, open_cl, variables * skills, optimization_type, batch)) return false; SetHeads(variables);
Init 方法接收一组配置参数,用于确定当前实例的整体架构。大部分参数在之前的内容中已有介绍,此处不再赘述。
方法内部首先调用父类的对应方法,该方法已完成基础控制逻辑,以及继承组件和接口的初始化。
本次选择 SoftMax 函数层作为父类。借助该层可以对各个智能体技能编码器的输出做归一化处理,将数值统一到可比区间。单个智能体的技能数量由 skills 参数指定。对应地,该模块为每个智能体输出等长向量,向量中每个数值代表一项技能的权重。
在本方案中,智能体数量与多维度时序数据内的单变量序列数量保持一致。因此该数值也被设为 SoftMax 归一化分支的数量。
完成父类初始化后,开始初始化新增的内部组件。首先初始化单变量序列之间的信息交互模块。本实验中,该功能由此前开发的 CATCH 框架组件实现。
int index = 0; if(!cCrossObservAttention.Init(0, index, OpenCL, time_step, variables, window, step, window_key, heads, optimization, iBatch)) return false;
补充说明:该模块会在频域下对各单变量序列做对齐处理。频谱被拆分为多个片段,并在各个独立频带内开展分析。该方式能够更灵活地刻画市场信号的变化特征。我们不再对完整频谱进行整体分析,而是聚焦于特定频域区间。这一点在金融市场中尤为关键:高频波动与低频波动承载的信息截然不同。高频分量通常代表短期震荡与噪声,低频分量则反映市场趋势与长期规律。
借此可以挖掘出同步性、相位偏移、数据相关性等共性特征,并将其作为辅助信息用于技能特征的构建。
关键在于,组件间并非直接传递信息,而是通过掩码注意力机制完成交互。该设计可以避免智能体之间相互过度干扰,同时保留去中心化的特性。最终,每个智能体既独立处理自身对应的单变量序列,又能融合整个市场的全局特征。
CATCH 模块的输出仍为原始多维度时序数据,但数据中已融入各通道之间的关联特征。
随后,各个智能体分别获取自身对应的单变量序列。为转换数据格式以适配后续计算,这里使用转置层进行处理。
index++; if(!cTranspose.Init(0, index, OpenCL, time_step, variables, optimization, iBatch)) return false;
之后通过独立分支生成每个智能体的技能向量。该环节采用两层串行卷积层实现。
需要注意的是,待分析序列的长度可能存在较大差异。如果一次性处理完整序列,会产生较高的计算开销。针对该问题,我们先将序列分段,再通过卷积层完成维度调整。
uint count = (time_step - window + step - 1) / step; if(count <= 1) { window = time_step; count = 1; } //--- index++; if(!cSkillsProjection[0].Init(0, index, OpenCL, window, step, window_key, count, variables, optimization, iBatch)) return false; cSkillsProjection[0].SetActivationFunction(SoftPlus);
第二层卷积层会聚合同一条单变量序列的所有分段数据,为每个智能体生成唯一的技能向量。序列长度维度设为 1,也正体现了这一点。
index++; if(!cSkillsProjection[1].Init(0, index, OpenCL, window_key * count, window_key * count, skills, 1, variables, optimization, iBatch)) return false; cSkillsProjection[1].SetActivationFunction(None);
需要着重说明的是,该卷积层的参数明确限定了模型可处理的独立单变量序列数量。这使得每个智能体都拥有专属的卷积运算单元与独立权重参数。同时,各个智能体仅处理多维度时序数据中属于自己的局部片段。
换言之,每个智能体并非单纯读取独立数据,而是只获取限定范围内的信息。这一设计更贴合真实金融市场的场景:每位交易者(即本文中的智能体)依托不同信息源做出决策。
最终,这套系统不再是多个并行执行同一任务的单元,而是一组分工协作的专业化智能体,每个智能体在各自的信息环境中学习,构建独有的市场认知,并形成专属交易策略。该架构提升了系统稳定性,丰富了行为策略的多样性;同时可以融合各个局部策略,最终实现核心目标:在充满不确定性的市场中,高效且自适应地完成交易。
接下来初始化一个辅助对象,其张量尺寸为输出张量的两倍。该对象的作用,会在后续讲解误差梯度分发逻辑时展开说明。
index++; if(!cPrevSkillsConcat.Init(0, index, OpenCL, 2 * Neurons(), optimization, iBatch)) return false; cPrevSkillsConcat.SetActivationFunction((ENUM_ACTIVATION)cSkillsProjection[1].Activation()); //--- return true; }
完成所有内部对象的初始化后,初始化方法执行完毕,并向上层调用方返回执行成功的状态。
下一阶段,我们实现 feedForward 方法中的前向传播逻辑。不难看出,整个执行流程逻辑清晰、按顺序逐级推进。
bool CNeuronSkillsEncoder::feedForward(CNeuronBaseOCL *NeuronOCL) { if(!cCrossObservAttention.FeedForward(NeuronOCL)) return false;
该方法接收指向多维度输入数据对象的指针,并将其直接传入内部的跨通道关联分析模块。
if(!cTranspose.FeedForward(cCrossObservAttention.AsObject())) return false;
模块输出结果经过转置处理后,送入技能张量生成模块。
if(!cSkillsProjection[0].FeedForward(cTranspose.AsObject())) return false;
这里有一处关键细节需要说明。在调用第二层卷积层的前向传播函数前,我们会交换存储中间结果的缓冲区指针。通过这一简单操作,即可保留上一轮的运算输出。该设计的重要性,将在反向传播部分进行解释。
if(!cSkillsProjection[1].SwapOutputs() || !cSkillsProjection[1].FeedForward(cSkillsProjection[0].AsObject())) return false; //--- return CNeuronSoftMaxOCL::feedForward(cSkillsProjection[1].AsObject()); }
最终生成的技能张量经由父类映射为概率分布,随后方法结束并向调用方返回成功标识。
接下来实现反向传播相关算法。整个过程分为两个环节:
- 一是误差梯度分发,对应 calcInputGradients 方法,负责将误差梯度传递至所有组件;
- 二是参数优化,对应 updateInputWeights 方法,通过更新参数以最小化损失值。
我们先讲解 calcInputGradients 方法。该方法会依据各模块对最终输出的贡献度,逐层反向传播梯度。前文已大致介绍过它的结构。现在进行完整详解,并说明该方法对模型训练的重要意义。
该方法接收前向传播阶段使用的输入数据对象指针。此时需要将误差梯度沿着计算链路反向回传至输入数据。
bool CNeuronSkillsEncoder::calcInputGradients(CNeuronBaseOCL *prevLayer) { if(!prevLayer) return false;
显然,只能传入合法有效的对象。因此方法开头会立刻校验传入指针的有效性。若为空或非法,将直接终止方法执行,后续运算也就失去了意义。这类校验是基础的安全防护手段,能够保障算法正常运行,避免训练过程中出现未知异常。
指针校验通过后,进入梯度分发阶段。梯度会沿着整个运算链路,依次传递至每一个参与计算的组件。每个组件负责一部分模型逻辑,我们需要逐级、精准地将误差梯度回传给对应模块。
这套机制至关重要:它为模型构建起内部反馈回路,让每一个模块都能根据最终误差调整自身参数。模型正是依靠这套机制不断学习,迭代出能够最小化损失函数的最优参数。
第一阶段,依托父类提供的功能,将网络后端传来的误差梯度回传至当前对象的外部接口,一直传递到智能体技能张量的最终生成层。
if(!CNeuronSoftMaxOCL::calcInputGradients(cSkillsProjection[1].AsObject())) return false;
该过程是模型训练的核心环节,能够让智能体生成真正贴合任务目标的技能特征。梯度的逐级反向传递,会让模型清晰获知此前行为与决策的偏差大小。
但我们的目标不仅是训练智能体生成有效技能,更要让它们形成协同运作的整体,引导各个智能体各司其职、方向互补,从而提升系统整体表现。这并非对单个智能体的独立训练,而是打造智能体之间的协同效应。
为此我们引入对比学习算法,让不同智能体的技能张量具备差异化特征。值得注意的是,这种差异化不仅体现在同一时间步的不同智能体之间,也体现在不同时间步生成的技能特征之间。这会进一步强化状态表征的区分度。
最终,每个智能体不仅需要区分自身技能的独有属性,还需要结合历史行为形成的上下文信息综合判断。这会促使智能体学习更复杂、具备时序关联的行为策略,更好地适配不断变化的市场环境。
金融市场行情瞬息万变,因此智能体结合历史行为、区分不同状态的技能尤为关键。技能差异化设计搭配历史上下文信息,能够构建出稳定性与适应性更强的交易策略,在高波动、高不确定性的市场环境中稳定运行。
对比学习算法是实现上述差异化设计的核心,它能提升智能体区分、解读各类市场场景的能力,帮助智能体在每一个决策节点做出更合理的判断。
在前向传播阶段,生成新的技能张量前,我们会保存上一轮的运算结果。现在将新旧两个环境状态对应的输出张量拼接为一个整体张量。
if(!Concat(cSkillsProjection[1].getOutput(), cSkillsProjection[1].getPrevOutput(), cPrevSkillsConcat.getOutput(), cSkillsProjection[1].GetFilters(), cSkillsProjection[1].GetFilters(), iHeads)) return false;
随后调用特征差异化计算函数,该函数的作用是在技能特征空间中,尽可能拉开各个向量之间的距离。
if(!DiversityLoss(cPrevSkillsConcat.AsObject(), 2 * iHeads, cSkillsProjection[1].GetFilters(), false)) return false;
计算得到的误差梯度,再按对应序列进行拆分。
if(!DeConcat(cPrevSkillsConcat.getOutput(), cPrevSkillsConcat.getPrevOutput(), cPrevSkillsConcat.getGradient(), cSkillsProjection[1].GetFilters(), cSkillsProjection[1].GetFilters(), iHeads)) return false;
接着将当前时间步产生的技能相关梯度,累加到历史累积梯度中。
if(!SumAndNormilize(cSkillsProjection[1].getGradient(), cPrevSkillsConcat.getOutput(), cSkillsProjection[1].getGradient(), cSkillsProjection[1].GetFilters(), false, 0, 0, 0, 1)) return false;
方法的后续流程按顺序执行。误差梯度逐层回传至技能生成模块。
if(!cSkillsProjection[0].calcHiddenGradients(cSkillsProjection[1].AsObject())) return false;
梯度结果经过转置后,传入跨通道关联分析模块。
if(!cTranspose.calcHiddenGradients(cSkillsProjection[0].AsObject())) return false; if(!cCrossObservAttention.calcHiddenGradients(cTranspose.AsObject())) return false;
最后,将误差梯度沿计算链路反向回传至原始输入数据对象。
return prevLayer.calcHiddenGradients(cCrossObservAttention.AsObject());
}
该方法执行结束后,向调用方返回执行结果。
参数更新方法 updateInputWeights 逻辑十分简单,仅需依次调用内部组件对应的更新方法即可。需要注意的是,并非所有内部模块都包含可训练参数,因此只有部分模块会参与本次参数更新。
bool CNeuronSkillsEncoder::updateInputWeights(CNeuronBaseOCL *NeuronOCL) { if(!cCrossObservAttention.UpdateInputWeights(NeuronOCL)) return false; if(!cSkillsProjection[0].UpdateInputWeights(cTranspose.AsObject())) return false; if(!cSkillsProjection[1].UpdateInputWeights(cSkillsProjection[0].AsObject())) return false; //--- return true; }
这里再补充说明一下模型的保存与加载机制。想必大家已经注意到,用于拼接两次连续前向传播结果的组件并不包含可训练参数。该组件仅作为辅助模块,负责合并不同处理链路的输出数据。即便如此,这个拼接组件仍配有数据缓冲区,其容量为编码器输出张量的两倍。但这些缓冲区仅用于临时存放中间运算数据,并不会对模型运行造成关键影响。因此在保存训练好的模型时,可以不保存这类缓冲区数据,从而节省磁盘空间。
bool CNeuronSkillsEncoder::Save(const int file_handle) { if(!CNeuronSoftMaxOCL::Save(file_handle)) return false; if(!cCrossObservAttention.Save(file_handle)) return false; if(!cTranspose.Save(file_handle)) return false; for(uint i = 0; i < cSkillsProjection.Size(); i++) if(!cSkillsProjection[i].Save(file_handle)) return false; //--- return true; }
加载模型时,可根据技能编码器的已知输出张量维度,快速重新初始化拼接组件。
bool CNeuronSkillsEncoder::Load(const int file_handle) { if(!CNeuronSoftMaxOCL::Load(file_handle)) return false; if(!LoadInsideLayer(file_handle, cCrossObservAttention.AsObject())) return false; if(!LoadInsideLayer(file_handle, cTranspose.AsObject())) return false; for(uint i = 0; i < cSkillsProjection.Size(); i++) if(!LoadInsideLayer(file_handle, cSkillsProjection[i].AsObject())) return false; //--- if(!cPrevSkillsConcat.Init(0, 4, OpenCL, 2 * Neurons(), optimization, iBatch)) return false; cPrevSkillsConcat.SetActivationFunction((ENUM_ACTIVATION)cSkillsProjection[1].Activation()); //--- return true; }
至此,关于技能编码器的实现讲解全部结束。该模块及其所有方法的完整源码详见附件。
本次内容我们完成了大量工作,受篇幅所限,我们暂且告一段落,相关内容将在下一篇文章中继续讲解。
结论
本文梳理了 HiSSD 框架的理论基础,该框架是一套功能强大、灵活性高的多智能体训练系统。各个智能体在与系统内其他主体密切交互的同时,学习专属的行为策略。结合通用技能与任务专属技能的学习模式,能够训练出适应性更强、稳定性更高的策略,可在不确定且动态变化的环境中稳定运行。
HiSSD 采用模块化设计,同时融合了对比学习、变分行为编码等多种训练机制,是解决多智能体系统问题极具潜力的优秀框架。
在实践部分,我们基于 MQL5 着手实现这套算法方案。重点完成了通用技能编码器的代码开发。下一篇文章我们将继续推进开发工作,并在真实历史行情数据上,验证这套实现方案的实际效果。
参考
本文用到的程序
| # | 名称 | 类型 | 说明 |
|---|---|---|---|
| 1 | Research.mq5 | EA | 采集样本数据的EA |
| 2 | ResearchRealORL.mq5 | EA | 采用 Real-ORL 方法采集样本的 EA |
| 3 | Study.mq5 | EA | 模型训练EA |
| 4 | Test.mq5 | EA | 模型测试EA |
| 5 | Trajectory.mqh | 类库 | 系统状态与模型架构描述结构 |
| 6 | NeuroNet.mqh | 类库 | 用于构建神经网络的类库 |
| 7 | NeuroNet.cl | 代码库 | OpenCL 程序代码 |
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/17729
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
外汇套利交易:带风险控制的公允价值回归矩阵交易系统
新手在交易中的10个基本错误
市场模拟(第 18 部分):SQL 入门(一)