English Русский Español Deutsch 日本語 Português
preview
同时交易多种工具时平衡风险

同时交易多种工具时平衡风险

MetaTrader 5示例 |
881 8
Aleksandr Seredin
Aleksandr Seredin

本文将讨论在日内同时交易多种工具时如何平衡风险的话题。本文的目的是让用户能够从头开始编写平衡工具的代码,并向有经验的用户介绍其他可能以前未使用过的旧想法的实现方法。为此,我们将探讨风险概念的定义,选择优化的标准,重点关注实现解决方案的技术方面,分析此类实现的标准终端能力集,并探讨将此算法集成到软件基础设施中的其他可能方法。


按风险平衡交易工具的标准

在同时交易几种金融工具时,我们将考虑两个主要因素作为风险平衡标准。

  • 交易品种分时价格
  • 交易品种日平均波动率

分时价格是具有标准交易品种手的交易品种的最小价格变化的货币值。我们将这一标准考虑在内,因为不同工具的分时价格值可能会有很大差异。例如,从 EURGBP 的 1.27042 到 AUDNZD 的 0.61374。

日均波动率是指交易品种价格在一天内的特征变化。与前一个选定的标准相比,该值在不同交易品种上的恒定性较差,而且会随着市场阶段的变化而变化。例如,EURGBP 通常平均波动约 336 点,而 CHFJPY 在同一天可波动 1271 点,几乎是 EURGBP 的四倍。这里提供的数据描述了价格波动的 "通常" 和 "最可能" 值,但没有考虑到在某些时刻,当价格开始向一个方向剧烈波动而不回调时,交易品种的异常高波动率。下面是 USDJPY 走势的一个例子。

图 1.D1 图表中交易品种波动性增加

图1.D1 图表上交易品种波动性增加

这种行为会给存款带来非常严重的风险,"如何降低交易者风险" 一文对此已有足够详细的描述。在本文中,我们将提出这样一个论点,即不可能通过平衡工具来防范这种风险。这是一个完全不同的风险类别。通过平衡,我们可以在平均波动率的框架内保护存款免受市场与我们的仓位背道而驰的风险。如果您想保护您的资金免受异常波动或 "黑天鹅" 的影响,那么请遵循以下原则。 

  • 不要在单一交易品种上承担大比例的风险。 
  • 不要一直持有未平仓位。 
  • 不要将管理的所有资金都放在一个经纪商的单一账户中。 
  • 不要同时在不同的账户和经纪商上进行相同的交易。 

遵循这些原则,如果在未平仓仓位中出现不利于您的交易品种价格,您就可以将损失降到最低。现在,我们再来考虑与标准波动率相关的风险。

同时考虑这两个因素将使您能够在同时交易每种货币对时平衡风险并使预期利润正常化,而不会过多地考虑任何单一工具的风险。随后,这种方法将为进一步分析交易历史提供更均匀的交易统计数据,并减少策略优化器处理这些数据时的误差,从而降低样本与平均数据之间的标准偏差。要更详细地了解标准偏差值如何影响一组数据的分析质量,您可以阅读文章 "交易中的数学:如何估算交易结果"。现在,让我们来选择一个用于存储数据的容器。


选择存储数据的容器 

在选择项目中用于存储数据的容器时,我们将考虑以下因素:

  • 容器性能
  • 初始化容器所需的内存
  • 是否提供用于数据分析的内置功能
  • 是否易于通过用户界面进行初始化

在选择用于存储数据的容器时,最常见的标准是容器的性能和存储它们所需的计算机内存。不同类型的存储通常可以在处理数据时提供更好的性能,或者增加占用的内存量。

为了进行检查,让我们声明一个简单数组和一个 vector 类型的特殊容器,同时用 double 数据类型和相同的值对它们进行初步初始化

   double arr[] = {1.5, 2.3};
   vector<double> vect = {1.5, 2.3};
使用 sizeof 操作,在编译阶段确定与上述类型相对应的内存大小。
Print(sizeof(arr));
Print(sizeof(vect));

结果,我们得到 16 和 128 字节。vector 数据类型内存需求的这种差异是由内置功能决定的,包括额外的冗余内存分配,以确保更好的性能。

在此基础上,考虑到我们的容器任务只需要存储之前选择的数据,我们将使用一种简单的存储类型作为数组。对于我们随后将在算法中处理的同质数据类型,最好使用特殊的 vector 数据类型。使用这种类型还可以节省为标准操作编写自定义函数的开发时间,因为 vector 中已经实现了这些功能。

因此,计算所需的数据存储将如下所示。
   string symbols[];       // symbols used for balancing

   double tick_val[],      // symbol tick price
          atr[],           // symbol volatility
          volume[],        // calculated position volume for symbols taking into account balancing 
          point[];         // value of one price change point 

   vector<double> risk_contract; // risk amount for a standard contract

现在,让我们继续考虑实施平衡交易品种数据输入解决方案的备选方案。


选择交易品种输入方法

在 MetaTrader 5 中选择数据输入方法时有许多解决方案。在全局范围内,它们被分为旨在通过标准终端对话框与用户直接交互的解决方案,或通过保存在磁盘上的文件或远程交互界面与其他应用程序交互的解决方案。

鉴于需要以 string 格式输入存储在终端中的许多交易品种,我们可以突出以下几种在脚本中实现数据输入的可能选项:

  1. 从 *.csv 表格文件读取数据
  2. 从 *.bin 二进制文件中读取数据
  3. 从 .sqlite 数据库文件读取数据
  4. 使用第三方方法实现终端与远程数据库的交互
  5. 使用 web API 解决方案
  6. 终端使用适当类型的变量和 input 类型的内存类修改器的标准

在选择项目中的数据录入方法之前,我们将简要考虑实现这项任务的每种方案的利弊。选择第 1 个所述方法时,可通过处理文件的标准终端函数从 *.csv 类型的表格文件中读取数据。一般来说,代码可能如下所示:

   string file_name = "Inputs.csv";                         // file name

   int handle=FileOpen(file_name,FILE_CSV|FILE_READ,";");   // attempt to find and open the file

   if(handle!=INVALID_HANDLE)                               // if the file is found, then
     {
      while(FileIsEnding(handle)==false)                    // start reading the file
        {
         string str_follow = FileReadString(handle);        // reading
        
        // here we implement filling the container depending on its type
        }
     }

终端文档中详细描述了实现文件处理功能的其他选项。在此选项中,用户只需使用第三方电子表格应用程序(如 MS Excel 或 OpenOffice)准备输入参数文件。即使是标准的 Windows 记事本也可以。

第二个方法,FileLoad() 标准终端函数适用于使用 *.bin 文件。要使用该扩展名,您需要事先知道应用程序在保存该文件时使用的数据结构,以便从带有该扩展名的文件中读取数据。这种想法的实现过程可能是这样的。

   struct InputsData                   // take the structure, according to which the binary file was created 
     {
      int                  symbol_id;  // id of a balanced symbol
      ENUM_POSITION_TYPE   type;       // position type
     };

   InputsData inputsData[];            // storage of inputs

   string  filename="Inputs.bin";      // file name

   ArrayFree(inputsData);              // array released

   long count=FileLoad(filename,inputsData,FILE_COMMON); // load file

这种方法的主要缺点是 FileLoad() 函数无法处理包含对象数据类型的数据结构。因此,如果结构体中包含 string 数据类型,则无法使用该功能。在这种情况下,您必须额外使用自定义容器字典,以便将 int 整数值中的字符 id 转换为相应的 string 数据类型,或者向相应的数据库提出额外请求。一般来说,由于执行相当简单的操作过于复杂,这种方法在我们的实现中并不是最成功的。

第三点特别建议使用终端内置功能处理 .sqlite 文件数据库。这是一个内置的终端选项,用于处理与硬盘上保存的文件交互建立的关系数据库。

   string filename="Inputs.sqlite"; // file name with inputs prepared in advance

   int db=DatabaseOpen(filename, DATABASE_OPEN_READWRITE |
                       DATABASE_OPEN_CREATE | DATABASE_OPEN_COMMON); // open the database

   if(db!=INVALID_HANDLE)                                            // if opened
     {
      // implement queries to the database using the DatabaseExecute() function
      // the query structure will depend on the structure of the database tables
     }

在实现这种方法时,首先必须建立数据库表的结构。获取必要数据的查询结构将取决于此。这种方法的主要优点是可以进行关系型数据存储,工具的 ID 将以整数格式而不是字符串格式存储在表格中,这对优化计算机磁盘空间大有裨益。值得注意的是,在某些条件下,这一版本的数据库可以非常高效。更多详情,请参阅文章 "SQLite:在 MQL5 中本地处理 SQL 数据库"。

第四个方法介绍了应用远程数据库的选项,这需要使用额外的第三方库。由于无法通过标准终端功能完全实现,因此该方案比上段所述方法更耗费人力。关于这一主题的文章很多,其中包括 "如何从 MQL5(MQL4)访问 MySQL 数据库" 一文中描述的实现终端与 MySQL 数据库交互的良好方案。

第五段中介绍的使用 web API 请求获取输入信息的方法,可能是我们的任务中最通用、最跨平台的解决方案。该功能通过预定义函数WebRequest()内置于终端中,如果您已经拥有用于后端和前端应用程序的基础架构,那么它的多功能性将是您的最佳选择。否则,从头开始开发这些应用程序可能会耗费大量的时间和资源,尽管这些解决方案可以用许多现代编程语言和解释器编写。

在目前的实现过程中,我们将使用第六个方法所述的 string 数据类型的 input 类型内存类修改器变量,原因很简单,因为该选项能够提供所有必要的功能,而无需额外开发自定义程序。当然,我们不会为每个交易品种声明很多变量,因为我们无法事先知道要平衡多少交易品种,而有一个更优雅、更灵活的解决方案可以解决这个问题。我们将使用一行数据来包含所有值,然后再进一步分离其中的数据。为此,我们在全局层面以如下形式声明一个变量:

input string input_symbols = "EURCHFz USDJPYz";

确保使用默认值对其进行初始化,以便非开发人员的用户确切了解如何输入交易品种才能使应用程序正常运行。在这种情况下,我们将使用常规空格作为分隔线,以方便用户。

我们将使用预定义的终端函数从该变量中获取数据并填充 symbols[] 数组,该函数用于处理如下形式的 StringSplit() 字符串:

   string symbols[];                         // storage of user-entered symbols
   
   StringSplit(input_symbols,' ',symbols);   // split the string into the symbols we need

   int size = ArraySize(symbols);            // remember the size of the resulting array right away

在执行 StringSplit() 函数时,通过引用传递给该函数的 symbols[] 数组将使用空格形式的分隔符(' ')填充从字符串中提取的数据。

现在,我们已经有了一个填满交易品种名称值的数组,可以进行风险平衡,接下来,我们要请求所选终端交易品种的必要数据,以便进一步计算。


通过预定义的终端函数获取必要的交易品种数据

在计算时,我们需要知道每种金融工具价格的最小变动值,以及这一变动将使我们损失多少存款货币。我们可以通过预定义的 SymbolInfoDouble() 终端函数实现这一功能,并将必要的 ENUM_SYMBOL_INFO_DOUBLE 枚举作为函数参数之一。数据枚举将通过常用的 "for" 循环来实现,具体如下:

for(int i=0; i<size; i++)  // loop through previously entered symbols
     {
      point[i] = SymbolInfoDouble(symbols[i],SYMBOL_POINT);                	// requested the minimum price change size (tick)
      tick_val[i] = SymbolInfoDouble(symbols[i],SYMBOL_TRADE_TICK_VALUE_LOSS);  // request tick price in currency
     }

请注意,ENUM_SYMBOL_INFO_DOUBLE 枚举不仅包含 SYMBOL_TRADE_TICK_VALUE_LOSS 值(用于请求以货币表示的分时价格),还包含 SYMBOL_TRADE_TICK_VALUE 值和与之相等的 SYMBOL_TRADE_TICK_VALUE_PROFIT 值。实际上,我们在计算中可以使用对任何指定值的请求,因为这些值之间的差异并不大。例如,AUDNZD 交叉盘的指定参数值如下表所示:

函数参数 函数返回的分时价格值
SYMBOL_TRADE_TICK_VALUE 0.6062700000000001
SYMBOL_TRADE_TICK_VALUE_LOSS 0.6066200000000002
SYMBOL_TRADE_TICK_VALUE_PROFIT 0.6062700000000001

表 1.AUDNZD 的 SymbolInfoDouble() 函数不同参数返回的分时价格值的差异

尽管在我们的案例中使用 SYMBOL_TRADE_TICK_VALUE_LOSS 参数是最正确的,但我相信我们可以使用这里提出的任何选项。正如 Richard Hamming 在 1962 年指出的那样: 

"计算的目的是洞察力,而不是数字"

让我们继续请求必要的波动数据。


通过标准自定义 CiATR 类读取所需的波动率数据

在前面的章节中,我们已经提到过波动率的概念,它是描述一个交易品种在一定时期内(在我们的例子中是一个交易日)典型价格变化的指标。尽管这一指标显而易见,但其计算方法却大相径庭,主要原因有以下几个方面:

  • 考虑日线图上的未封闭缺口
  • 使用平均值和所应用的周期
  • 在计算中排除 "abnormal"(异常罕见)波动率的柱形
  • 根据每日柱形的最高价/最低价计算,或仅根据开盘价/收盘价计算
  • 或者我们通常交易 Renko 柱形图,平均时间对我们来说根本不重要

在计算波动率时考虑间隙是证券交易所工作中经常使用的方法,这与外汇市场不同, 因为在外汇市场上白天不关闭的市场间隙是非常罕见的,而且最终计算的日均波动率也不会发生变化。这里的计算不同之处仅在于我们从两个值中取最大值。第一个是每个柱形的最高点和最低点之差,第二个是当前柱形的最高点和最低点与前一个柱形的收盘点之差。我们可以使用内置的 MathMax() 函数来实现这一目标。

在对所得数值进行平均时,必须考虑到平均周期越长,该指标对市场波动变化的影响就越小。通常,外汇市场的日平均波动率为 3 至 5 天。此外,在计算波动率指标时,最好排除市场的异常波动。这可以使用样本的中位值自动完成。为此,我们可以使用为 vector 类型类实例调用的内置 Median() 方法。

许多交易者喜欢只通过开盘价和收盘价来考虑波动性,而不考虑烛形引线的情况来做均线。不建议使用这种方法,因为它得出的数值可能比实际市场波动率低得多。使用 Renko 柱形时,逻辑发生了很大变化,这里的波动率汇总不是来自交易周期,而是由波动率决定交易周期。因此,这种方法不适合我们的文章。

在我们的实现过程中,我们将通过 ATR 终端指标使用存储在开放源码格式终端库中的标准自定义 CiATR 类来进行波动率查询。我们将使用快速值 3 来平均指标。一般来说,波动性请求代码如下。在全局层面,通过调用默认构造函数来声明类变量的名称。

CiATR indAtr[];

在这里,我们使用指标数组来同时存储数据,而不是重载一个现有指标,这主要是为了方便和进一步扩展代码的功能。接下来,我们在交易品种迭代循环中添加以下代码,在该循环中,我们已经在请求交易品种数据,以获取指标值。

indAtr[i].Create(symbols[i],PERIOD_D1, atr_period);   // create symbol and period
   
indAtr[i].Refresh();          // be sure to update data

atr[i] = indAtr[i].Main(1);   // request data on closed bars on D1

既然已经获得了计算所需的所有初始数据,就可以直接进行计算了。


不同离场方法的风险计算逻辑有两种选择

在编写同时交易几种工具时平衡风险的逻辑时,仍需考虑以下要点。在我们的交易系统中,如何从平衡仓位中退出,以及如何考虑我们将平衡的交易品种的相关性。即使乍一看,外汇市场交叉盘相关性的明确标准也并不总能保证在所考虑的时间段内的相关性水平,而且往往可以被视为单独的交易品种。例如,如果我们在交易日开始时就决定了今天要交易的一组交易品种和进场方向,那么我们就应该提前了解以下内容。我们是否会在交易日结束时关闭所有仓位?我们是否会考虑在日间分别退出每个仓位,还是在一段时间内简单地关闭所有仓位?不可能有某种适用于所有人的通用秘诀,因为每个交易者都会根据自己的知识和经验,按照相当多的标准来决定进场和出场。

在此实现过程中,我们将进行一种通用计算,只需改变交易工具的输入风险参数,并纳入/剔除交易者认为目前具有相关性的交易工具,即可根据您的交易事实灵活确定仓位的入场交易量。为此,我们对一种工具的输入风险参数进行如下声明。

input double risk_per_instr = 50;

此外,为了方便使用,我们还将同时输出平衡结果,将用户输入的风险分别与交易品种进行回归,并考虑这些交易品种的相关性。这样,交易者就能获得一系列不同的仓位交易量,最重要的是,还能同时交易这些工具的比例。为此,我们首先需要在主计算循环中添加以下条目。

risk_contract[i] = tick_val[i]*atr[i]/point[i]; // calculate the risk for a standard contract taking into account volatility and point price

接下来,在指定的循环之外,我们在集合中找到风险值最大的工具,以便从中建立一个比例来平衡整个集合的交易量。我们容器的内置功能正是为此而设计的。

double max_risk = risk_contract.Max();          // call the built-in container method to find the maximum value

现在,我们知道了我们这一组中的最大风险,我们再进行一个循环,计算样本中每个工具的平衡交易量(前提是它们之间没有相关性),并立即将结果显示在日志中。

for(int i=0; i<size; i++)	// loop through the size of our symbol array
     {
      volume[i] = NormalizeDouble((max_risk / risk_contract[i]) * (risk_per_instr / max_risk),calc_digits); // calculate the balanced volume
     }

Print("Separate");		// display the header in the journal preliminarily

for(int i=0; i<size; i++)	// loop through the array again
     {
      Print(symbols[i]+"\t"+DoubleToString(volume[i],calc_digits));	// display the resulting volume values
     }

这就是我们仓位的最大日内交易量,并与日均最大可能波动率和预期波动率相平衡,同时考虑到我们对交易品种所赋予的风险。

接下来,计算我们的持仓量,前提是交易日交易品种可能开始相关,这实际上可能会大大增加交易品种的预期风险,而不是我们在输入参数中输入的风险。为此,请添加以下代码,我们只需将得出的值除以交易工具的数量即可。

Print("Complex");		// display the header in the journal preliminarily
   
for(int i=0; i<size; i++)	// loop through the array
     {
      Print(symbols[i]+"\t"+DoubleToString(volume[i]/size,calc_digits));	// calculate the minimum volume for entry
     }

现在,我们已经得到了风险平衡仓位交易量的最大和最小限额。交易者能够根据它们独立确定特定范围内的入场交易量。最重要的是遵守计算中标明的比例。还应注意的是,记录结果是最简单的方法,但不是唯一的方法。您还可以使用其他标准终端函数向用户显示信息。在这种情况下,终端提供了非常广泛的功能,包括使用 MessageBox()Alert()SendNotification()SendMail() 等函数。我们接着查看并编译文件中 EA 的完整代码。


在脚本中最终实现解决方案 

就是这样,我们的执行代码将如下所示。

#property strict		

#include <Indicators\Oscilators.mqh>

//---
input string input_symbols = "EURCHFz USDJPYz";
input double risk_per_instr = 50;
input int atr_period = 3;
input int calc_digits = 3;
CiATR indAtr[];


//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  { 
   string symbols[];
   StringSplit(input_symbols,' ',symbols);   

   int size = ArraySize(symbols);
   double tick_val[], atr[], volume[], point[];
   vector<double> risk_contract;

   ArrayResize(tick_val,size);
   ArrayResize(atr,size);
   ArrayResize(volume,size);
   ArrayResize(point,size);
   ArrayResize(indAtr,size);
   risk_contract.Resize(size);

   for(int i=0; i<size; i++)
     {
      indAtr[i].Create(symbols[i],PERIOD_D1, atr_period);
      indAtr[i].Refresh();

      point[i] = SymbolInfoDouble(symbols[i],SYMBOL_POINT);
      tick_val[i] = SymbolInfoDouble(symbols[i],SYMBOL_TRADE_TICK_VALUE);

      atr[i] = indAtr[i].Main(1);
      risk_contract[i] = tick_val[i]*atr[i]/point[i];
     }

   double max_risk = risk_contract.Max();
   Print("Max risk in set\t"+symbols[risk_contract.ArgMax()]+"\t"+DoubleToString(max_risk));

   for(int i=0; i<size; i++)
     {
      volume[i] = NormalizeDouble((max_risk / risk_contract[i]) * (risk_per_instr / max_risk),calc_digits);
     }

   Print("Separate");
   for(int i=0; i<size; i++)
     {
      Print(symbols[i]+"\t"+DoubleToString(volume[i],calc_digits));
     }

   Print("Complex");
   for(int i=0; i<size; i++)
     {
      Print(symbols[i]+"\t"+DoubleToString(volume[i]/size,calc_digits));
     }
  }
//+------------------------------------------------------------------+

编译完成后,会出现输入参数窗口。例如,我们要平衡三个交易品种,最大风险为 500 美元。


图 2.交易者输入参数

图例 2.交易者输入参数

运行该脚本后,在考虑风险平衡的情况下,每个交易品种的交易量将得到以下数据。

图 3.输出数据

图 3.输出数据

下面是一个完整功能代码,用于使用终端提供的最简单但必要程度最低的功能计算多个风险平衡交易品种的同步交易量。如果您愿意,我们可以使用文章中指出的附加功能以及我们自己的开发成果来扩展这种算法。


结论

我们得到了一个功能齐全的脚本,它可以让不通过算法交易进行交易的交易者在日内交易时快速准确地调整仓位交易量。我希望,即使是那些采用算法进行交易的人,也能在这里获得新的想法,从而改进他们的软件,并有可能改善他们当前的交易结果。感谢您的阅读和反馈!

本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/14163

附加的文件 |
RiskBallance.mq5 (2.27 KB)
最近评论 | 前往讨论 (8)
Aleksandr Seredin
Aleksandr Seredin | 9 2月 2024 在 18:59
Maxim Kuznetsov #:

什么都没有...

没有风险,没有多符号。

耻辱

感谢您的评论

Anatoliy Migachyov
Anatoliy Migachyov | 14 2月 2024 在 12:27

许多人不愿意听到风险,这就是平衡,一般来说,这是许多交易者的痛处

Aleksandr Seredin
Aleksandr Seredin | 14 2月 2024 在 17:10
Anatoliy Migachyov #:

许多人不愿意听到风险,这就是平衡,一般来说,这是许多交易者的痛处

我完全同意。一句话,却触及了多少问题:

- 他们不想听,希望 "如果你不打电话给坏人,它就会安静"。但在市场上并不是这样的,我认为这里没有实用主义神秘思想的立足之地

- 通常情况下,在第一笔存款被提走之后,人们才会开始考虑风险问题。)

- 甚至在第一笔存款被提走之后,每个人都会开始 "讨厌""余额 "这个词,原因很简单,因为很明显,余额开始降低盈利能力。

- 因此,对于那些无法理解风险和无法建立稳定交易系统的人来说,这就是 "痛苦的话题",然后这些人就开始写 "没什么 "和 "耻辱 "之类的评论 ))))。

对这篇评论的作者表示敬意,他的发言简明扼要,内容翔实。谢谢

nowenn
nowenn | 12 6月 2024 在 02:40
感谢您提供的代码示例,主要问题是根据您交易的时间框架,我们如何定义正常和异常风险。对吗?
Aleksandr Seredin
Aleksandr Seredin | 12 6月 2024 在 07:19
nowenn #:
感谢您提供的代码示例,主要问题是根据您交易的时间框架,我们如何定义正常和异常风险。对吗?

是的。通常是使用日时间框架来评估风险。

种群优化算法:微人工免疫系统(Micro-AIS) 种群优化算法:微人工免疫系统(Micro-AIS)
本文研究一种基于人体免疫系统原理的优化方法 — 微人工免疫系统(Micro-AIS) - AIS 的修订版。Micro-AIS 使用更简单的免疫系统模型,和更简单的免疫信息处理操作。本文还讨论了 Micro-AIS 与传统 AIS 相比的优缺点。
种群优化算法:细菌觅食优化 — 遗传算法(BFO-GA) 种群优化算法:细菌觅食优化 — 遗传算法(BFO-GA)
本文释义了一种解决优化问题的新方式,即把细菌觅食优化(BFO)算法和遗传算法(GA)中所用的技术结合到混合型 BFO-GA 算法当中。它用细菌群落来全局搜索最优解,并用遗传运算器来优调局部最优值。与原始的 BFO 不同,细菌现在可以突变,并继承基因。
数据科学和机器学习(第 18 部分):掌握市场复杂性博弈,截断型 SVD 对比 NMF 数据科学和机器学习(第 18 部分):掌握市场复杂性博弈,截断型 SVD 对比 NMF
截断型奇异值分解(SVD)和非负矩阵分解(NMF)都是降维技术。它们在制定数据驱动的交易策略方面都发挥着重要作用。探索降维的艺术,揭示洞察和优化定量分析,以明智的方式航行在错综复杂的金融市场。
使用优化算法即时配置 EA 参数 使用优化算法即时配置 EA 参数
文章讨论了使用优化算法即时查找最佳 EA 参数,以及交易操作和 EA 逻辑虚拟化的实际问题。这篇文章可作为在 EA 中实现优化算法的指导。