English Русский Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
preview
从头开始开发智能交易系统(第 19 部分):新订单系统 (II)

从头开始开发智能交易系统(第 19 部分):新订单系统 (II)

MetaTrader 5交易 | 13 九月 2022, 14:24
1 145 1
Daniel Jose
Daniel Jose

概述

在上一篇文章中,从头开始开发交易专家顾问(第 18 部分),我们在订单系统中实现了一些修复、更改和调整,旨在创建一个系统,能够在净持和对冲不同结算类型的账户里执行交易,因为账户操作存在差异。 对于净持结算类型,系统生成一个平均价格,且每种资产只能有一笔持仓。 在对冲账户中,您可以有多笔持仓,每笔持仓都有各自的限定。 您可以同时买卖相同的资产。 这样的操作只能在对冲账户上进行。 这是了解期权交易的基础。

但现在是时候令订单系统完全可视化了,这样我们就可以剔除消息框,且在不借助它的情况下分析每笔持仓的数值。 我们可以通过新的订单系统来做到这一点。 这将允许我们一次调整若干件事情。 此外,由于 EA 将实时显示相关信息,不需要任何额外计算,我们就能够轻松了解 OCO 订单或挂单的盈亏限额。

虽然这只是第一部分的实现,但我们并不是从头开始:我们将修改现有系统,往正在交易的资产图表里添加更多对象和事件。


1.0. 计划

我们在这里所用的系统规划并不特别困难:我们将修改现有系统,只更改图表上表示订单的系统。 这是主要思路,看起来十分简单。 但实际上,这需要很强的创造力,因为我们要操纵和数据建模,从而 MetaTrader 5 平台能替我们完成所有的艰苦工作。

有若干种方法可对数据建模,每种方法都有其优缺点。

  • 第一种方法是使用列表。 它可以是单循环、双循环,甚至是哈希系统。 使用任何这些方式的优点是系统易于实现。 然而,缺点则是要防止数据操纵或限制订单数量。 甚而,在这种情况下,我们必须创建保存列表的所有额外逻辑。
  • 第二种方式是创建类数组,而类将包含并维护所有新创建的对象。 在这种情况下,数组将像列表一样工作,但我们必须编写少量代码,因为 MQL5 已经支持在使用列表的情况下原来必须编写代码的一些东西。 然而,我们会有其它问题,例如事件处理,在这种状况下,事情会变得非常困难。
  • 第三种方式是我们将要采取的方式。 我们将强制以 MQL5 创建代码来支持动态对象。 这似乎看起来有些不现实,但如果我们针对所用数据进行正确建模,那么 MQL5 语言将令我们能够创建一个系统,且其对屏幕上的对象数量没有限制。 甚至,所有对象都将能够生成和接收事件。 尽管它们有各自的特点,但平台将看到它们都链接起来,就像它们在列表或数组索引中一样。

如果您认为这不太容易实现,请查看 C_HLineTrade 类的以下代码部分:

inline void SetLineOrder(ulong ticket, double price, eHLineTrade hl, bool select)
{
        string sz0 = def_NameHLineTrade + (string)hl + (string)ticket, sz1;
                                
        ObjectCreate(Terminal.Get_ID(), sz0, OBJ_HLINE, 0, 0, 0);

//... The rest of the code.... 

高亮显示的部分准确地向我们展示了可以创建任意数量的水平线,它们将以完全独立的方式接收事件。 我们所有需要做的就是根据每一行的名称实现事件,因为名称是唯一的。 MetaTrader 5 平台将负责剩下的工作。 结果大约如下所示:


虽然这看起来已经很理想了,但这样建模不足以实现我们真正需要的结果。 这个思路已经可以实现。 但 EA 中当前可用的数据建模并不理想,因为我们不能基于一个名称拥有无限数量的对象。 我们需要进行一些修改,而这需要相当深入地修改代码。

我们现在将开始实现这种新的数据建模方法,但我们只是简单地修改必要的内容,同时保持整体代码的稳定,因为它应该尽可能稳定地继续工作。 所有操作将由 MetaTrader 5 平台执行,我们仅指示平台应如何理解我们的建模。


2.0. 实现

第一处修改是将 C_HLineTrade 更改为新的 C_ObjectsTrade 类。 这个新类将能够支持我们所需要的 — 能够链接无限数量对象的一种方法。

我们先看看下面代码中的原始定义。

class C_ObjectsTrade
{
//+------------------------------------------------------------------+
#define def_NameObjectsTrade 	"SMD_OT"
#define def_SeparatorInfo       '*'
#define def_IndicatorTicket0    1
//+------------------------------------------------------------------+
        protected:
                enum eIndicatorTrade {IT_NULL, IT_STOP= 65, IT_TAKE, IT_PRICE};
//+------------------------------------------------------------------+

// ... The rest of the class code

此处我们有了我们将要实现的初始基础。 它在未来将得到扩展,但目前我希望系统保持稳定,尽管它正在被修改,并具有新的数据建模。

即使在受保护部分声明中,我们也拥有以下函数:

inline double GetLimitsTake(void) const { return m_Limits.TakeProfit; }
//+------------------------------------------------------------------+
inline double GetLimitsStop(void) const { return m_Limits.StopLoss; }
//+------------------------------------------------------------------+
inline bool GetLimitsIsBuy(void) const { return m_Limits.IsBuy; }
//+------------------------------------------------------------------+
inline void SetLimits(double take, double stop, bool isbuy)
{
        m_Limits.IsBuy = isbuy;
        m_Limits.TakeProfit = (m_Limits.TakeProfit < 0 ? take : (isbuy ? (m_Limits.TakeProfit > take ? m_Limits.TakeProfit : take) : (take > m_Limits.TakeProfit ? m_Limits.TakeProfit : take)));
        m_Limits.StopLoss = (m_Limits.StopLoss < 0 ? stop : (isbuy ? (m_Limits.StopLoss < stop ? m_Limits.StopLoss : stop) : (stop < m_Limits.StopLoss ? m_Limits.StopLoss : stop)));
}
//+------------------------------------------------------------------+
inline int GetBaseFinanceLeveRange(void) const { return m_BaseFinance.Leverange; }
//+------------------------------------------------------------------+
inline int GetBaseFinanceIsDayTrade(void) const { return m_BaseFinance.IsDayTrade; }
//+------------------------------------------------------------------+
inline int GetBaseFinanceTakeProfit(void) const { return m_BaseFinance.FinanceTake; }
//+------------------------------------------------------------------+
inline int GetBaseFinanceStopLoss(void) const { return m_BaseFinance.FinanceStop; }

目前,这些函数只是作为未来我们将要实现的另一个方案的保险措施。 尽管我们可以在另一个位置实现数据和解析,但最好尽可能在继承链底层保留一些东西。 即使返回值仅由派生类取用,我也不愿允许这样直接:我不想让派生类访问 C_ObjectsTrade 对象类中的值,因为这将打破对象类封装的思想,如果派生类在不经由过程调用的情况下就能变更相关基类的值,这令未来的修改或错误修复变得困难。

为了尽可能减少调用重叠,所有函数都声明为内联:这稍微增加了可执行文件的大小,但会导致更安全的系统。

现在我们来到私密声明。

//+------------------------------------------------------------------+
        private :
                string  m_SelectObj;
                struct st00
                {
                        double  TakeProfit,
                                StopLoss;
                        bool    IsBuy;
                }m_Limits;
                struct st01
                {
                        int     FinanceTake,
                                FinanceStop,
                                Leverange;
                        bool    IsDayTrade;
                }m_BaseFinance;
//+------------------------------------------------------------------+
                string MountName(ulong ticket, eIndicatorTrade it)
                {
                        return StringFormat("%s%c%c%c%d", def_NameObjectsTrade, def_SeparatorInfo, (char)it, def_SeparatorInfo, ticket);
                }
//+------------------------------------------------------------------+

最重要的部分是高亮显示的片段,它将建模对象的名称。 我保留了一些基础,它们在系统中仍然可用。 这是因为我们首先创建和修改建模,保持系统稳定。 然后我们将添加新对象,而这将非常容易、快速地完成。 甚至,我们将保持已经实现的稳定。

虽然代码经历了比这里所示更多的变化,但我只关注新函数,以及与以前代码相比变化相当大的部分。

第一个函数如下所示:

inline string CreateIndicatorTrade(ulong ticket, eIndicatorTrade it, bool select)
{
        string sz0 = MountName(ticket, it);
                                
        ObjectCreate(Terminal.Get_ID(), sz0, OBJ_HLINE, 0, 0, 0);
        ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_COLOR, (it == IT_PRICE ? clrBlue : (it == IT_STOP ? clrFireBrick : clrForestGreen)));
        ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_WIDTH, 1);
        ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_STYLE, STYLE_DASHDOT);
        ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_SELECTABLE, select);
        ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_SELECTED, false);
        ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_BACK, true);
        ObjectSetString(Terminal.Get_ID(), sz0, OBJPROP_TOOLTIP, (string)ticket + " "+StringSubstr(EnumToString(it), 3, 10));
                                
        return sz0;
}

目前,它只会创建一条水平线。 注意名称生成代码;还要注意,颜色现在将由代码内部定义,而不是由用户定义。

然后我们重载相同的函数,如下所示。

inline string CreateIndicatorTrade(ulong ticket, double price, eIndicatorTrade it, bool select)
{
        if (price <= 0)
        {
                RemoveIndicatorTrade(ticket, it);
                return NULL;
        }
        string sz0 = CreateIndicatorTrade(ticket, it, select);
        ObjectMove(Terminal.Get_ID(), sz0, 0, 0, price);
                                
        return sz0;
}

不要把这两个函数混淆,因为尽管它们看起来一样,但实际上不同。 重载非常常见:我们创建一个简单的函数,然后往其中添加新参数,以便累积某种类型的建模。 如果我们没有通过重载实现它,我们有时将不得不重复相同的代码。 这很危险,因为我们会忘记声明一些东西。 此外,这也不太实用,因此我们重载的函数只需调用一个,取代调用若干个。

于此应提到的一件事是第二个版本中高亮显示的部分。 不需要在此处创建它,我们可以在其它地方做这件事。 但是,正如所见,当我们尝试以零价格创建一些对象时,实际上它必须被销毁。

为了实际查看发生这种情况的时刻,请查看下面的代码:

class C_Router : public C_ObjectsTrade
{

// ... Internal class code ....

                void UpdatePosition(int iAdjust = -1)
                        {

// ... Internal function code ...

                                for(int i0 = p; i0 >= 0; i0--) if(PositionGetSymbol(i0) == Terminal.GetSymbol())
                                {
                                        ul = PositionGetInteger(POSITION_TICKET);
                                        m_bContainsPosition = true;
                                        CreateIndicatorTrade(ul, PositionGetDouble(POSITION_PRICE_OPEN), IT_PRICE, false);
                                        CreateIndicatorTrade(ul, take = PositionGetDouble(POSITION_TP), IT_TAKE, true);
                                        CreateIndicatorTrade(ul, stop = PositionGetDouble(POSITION_SL), IT_STOP, true);

// ... The rest of the code...

每次 EA 收到 OnTrade 事件时,它将执行上述函数,并尝试在选定点上创建一个指标,但如果用户删除限制,则该指标将变为零。 因此,当调用时,它实际上会从图表中删除指标,内存中已无用的对象也可为我们节省下来。 由此,我们在某些方面有所收获,因为检查将在创建时刻正确完成。

但我们仍然有重载的问题,因为有些人可能不完全理解在实际中如何使用代码。 为了理解这一点,请查看以下两段代码:

class C_OrderView : public C_Router
{
        private  :
//+------------------------------------------------------------------+
        public   :
//+------------------------------------------------------------------+
                void InitBaseFinance(int nContracts, int FinanceTake, int FinanceStop, bool b1)
                        {                       
                                SetBaseFinance(nContracts, FinanceTake, FinanceStop, b1);
                                CreateIndicatorTrade(def_IndicatorTicket0, IT_PRICE, false);
                                CreateIndicatorTrade(def_IndicatorTicket0, IT_TAKE, false);
                                CreateIndicatorTrade(def_IndicatorTicket0, IT_STOP, false);
                        }
//+------------------------------------------------------------------+

// ... Rest of the code...
class C_Router : public C_ObjectsTrade
{

// ... Class code ...

                void UpdatePosition(int iAdjust = -1)
                        {
// ... Function code ....
                                for(int i0 = p; i0 >= 0; i0--) if(PositionGetSymbol(i0) == Terminal.GetSymbol())
                                {
                                        ul = PositionGetInteger(POSITION_TICKET);
                                        m_bContainsPosition = true;
                                        CreateIndicatorTrade(ul, PositionGetDouble(POSITION_PRICE_OPEN), IT_PRICE, false);

// ... The rest of the code...

请注意,在这两种情况下,我们所用的函数名称相同。 此外,它们都是同一个 C_ObjectsTrade 类的一部分。 然而,即使在这种情况下,编译器也可能区分它们,这是由于参数的数量。 如果您仔细观察,就会发现唯一的区别是一个额外的 “price” 参数,但也许还有其它一些参数。 正如您所看到的,调用一个 N 合一的重载版本要比复制所有代码要容易得多,如此最终我们得到了更干净的代码,更易于维护。

现在我们回到 C_ObjectsTrade 类。 下一个我们需要理解的函数如下:

bool GetInfosOrder(const string &sparam, ulong &ticket, double &price, eIndicatorTrade &it)
{
        string szRet[];
        char szInfo[];
                                
        if (StringSplit(sparam, def_SeparatorInfo, szRet) < 2) return false;
        if (szRet[0] != def_NameObjectsTrade) return false;
        StringToCharArray(szRet[1], szInfo);
        it = (eIndicatorTrade)szInfo[0];
        ticket = (ulong) StringToInteger(szRet[2]);
        price = ObjectGetDouble(Terminal.Get_ID(), sparam, OBJPROP_PRICE);
                                
        return true;
}

事实上,它是整个新系统的心脏、大脑和身体。 虽然它看起来很简单,但它所做的工作对于整个 EA 来说是至关重要的,因为我们的新建模系统需要它。

密切注意高亮显示的代码,特别是 StringSplit 函数。 如果它在 MQL5 中不存,我们就必须为其进行编码。 幸运的是,MQL5 已拥有它,因此我们就可充分利用这个函数。 它所做的就是将对象的名称分解为所需的数据。 当创建对象名称时,它以非常特定的方式建模,因此我们可以撤消此编码模型,因此 StringSplit 将撤消 StringFormat 函数所做的工作。

函数的其余部分捕获对象名称中存在的数据,以便我们稍后测试和使用。 也就是说,MetaTrader 5 为我们生成数据,我们对数据进行分解,以便探知发生了什么,然后告诉 MetaTrader 5 应该采取哪些步骤。 我们的目的是让 MetaTrader 5 为我们工作。 我不会从头开始创建模型;取而代之,我会从零开始为界面和 EA 建模。 因此,我们应该从 MetaTrader 5 提供的支持当中受益,而不必寻找外部解决方案。

在下面的代码中,我们将完成与上面所做的非常类似的操作:

inline void RemoveAllsIndicatorTrade(bool bFull)
{
        string sz0, szRet[];
        int i0 = StringLen(def_NameObjectsTrade);
                                
        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false);
        for (int c0 = ObjectsTotal(Terminal.Get_ID(), -1, -1); c0 >= 0; c0--)
        {
                sz0 = ObjectName(Terminal.Get_ID(), c0, -1, -1);
                if (StringSubstr(sz0, 0, i0) == def_NameObjectsTrade)
                {
                        if (!bFull)
                        {
                                StringSplit(sz0, def_SeparatorInfo, szRet);
                                if (StringToInteger(szRet[2]) == def_IndicatorTicket0) continue;
                        }
                }else continue;                                         
                ObjectDelete(Terminal.Get_ID(), sz0);
        }
        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);
}

每次我们从图表中删除一条线,无论是要平仓,亦或是要删除的限价等级,都必须删除相应的对象,就像从图表中移除 EA 一样。 我们需要删除这些对象,但我们也要有一组标线,除非绝对必要,否则不应删除:这是 Ticket0,除非非常必要,否则不得删除。 为了避免将其删除,我们要把代码高亮显示。 如果没有这个,我们每次都需要重新创建这个 Ticket0,因为这个 ticket 在另一个代码部分中非常重要,我们将在后面讨论。

在所有其它时间,我们需要删除一些特定的内容。 为此,我们将调用另一个移除函数,如下所示。

inline void RemoveIndicatorTrade(ulong ticket, eIndicatorTrade it = IT_NULL)
{
        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false);
        if ((it != NULL) && (it != IT_PRICE))
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, it));
        else
        {
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, IT_PRICE));
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, IT_TAKE));
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, IT_STOP));
        }
        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);
}

下一个新例程如下所示:

inline void PositionAxlePrice(double price, ulong ticket, eIndicatorTrade it, int FinanceTake, int FinanceStop, int Leverange, bool isBuy)
{
        double ad = Terminal.GetAdjustToTrade() / (Leverange * Terminal.GetVolumeMinimal());
        ObjectMove(Terminal.Get_ID(), MountName(ticket, it), 0, 0, price);
        if (it == IT_PRICE)
        {
                ObjectMove(Terminal.Get_ID(), MountName(ticket, IT_TAKE), 0, 0, price + Terminal.AdjustPrice(FinanceTake * (isBuy ? ad : (-ad))));
                ObjectMove(Terminal.Get_ID(), MountName(ticket, IT_STOP), 0, 0, price + Terminal.AdjustPrice(FinanceStop * (isBuy ? (-ad) : ad)));
        }
}

它将在价格轴上放置对象。 但不要太依附于它,因为它很快会因为各种原因而不复存在。 它们当中之一是我们在本系列的另一篇文章中谈到的:一个图表上的多个指标(第 05 部分):将 MetaTrader 5 转换为 RAD(I)系统。 本文带有一个表格,显示了可以使用笛卡尔坐标进行定位的对象,这些坐标是 X 和 Y。价格和时间坐标尽管在某些情况下很有用,但并非始终方便:当我们想要在屏幕上的某些点上定位元素时,尽管使用价格和时间座标开发东西会更快,它们比 X 和 Y 系统更难精确定位。

我们将在下一次进行修改,而现在我们的目的是创建一个替代目前所用的系统。

接下来,我们在 C_ObjectsTrade 类中有最后一个重要函数。 代码如下所示:

inline double GetDisplacement(const bool IsBuy, const double Vol, eIndicatorTrade it) const
{
        int i0 = (it == IT_TAKE ? m_BaseFinance.FinanceTake : m_BaseFinance.FinanceStop),
            i1 = (it == IT_TAKE ? (IsBuy ? 1 : -1) : (IsBuy ? -1 : 1));
        return (Terminal.AdjustPrice(i0 * (Vol / m_BaseFinance.Leverange) * Terminal.GetAdjustToTrade() / Vol) * i1);
}

此函数会把在图表交易者中指定的挂单或市价开仓的数值进行转换。

所有这些修改都是为了实现将 C_HLineTrade 函数转换为 C_ObjectsTrade。 然而,这些变更还需要一些其它修改。 例如,有些类也发生了显著变化,如 C_ViewOrder。 这个类的某些部分不复存在,因为它们的存在没有意义,而其余的函数已经改变。 某些值得特别注意的函数如下高亮所示。

第一个是初始化来自图表交易者的数据的函数。

void InitBaseFinance(int nContracts, int FinanceTake, int FinanceStop, bool b1)
{                       
        SetBaseFinance(nContracts, FinanceTake, FinanceStop, b1);
        CreateIndicatorTrade(def_IndicatorTicket0, IT_PRICE, false);
        CreateIndicatorTrade(def_IndicatorTicket0, IT_TAKE, false);
        CreateIndicatorTrade(def_IndicatorTicket0, IT_STOP, false);
}

高亮显示的部分是实际创建 Ticket0 的地方。 此 ticket 可用来根据鼠标和键盘放置挂单:(SHIFT)买入,(CTRL)卖出。 以前,在这一点上创建了等级线,这些等级线用于指示订单的位置。 现在事情简单得多:当我们看到一笔订单时,我们也会看到一笔挂单或一笔持仓。 这意味着我们将始终检查系统。 这就像您要组装一辆汽车,您所有的时间都在检查它的制动器,如此当您真的需要用到它们时,您就会知道它的行为。

冗长代码的最大问题是,当我们创建函数时,我们只能在实际使用时才知道它能否工作。 但现在系统总是在检查 — 即使我们并未调用所有的函数,但因代码在不同的地方重用,故它们也会不断被检查。

我在本文中要提到的最后一个例程如下所示。 它将放置一笔挂单。 请注意,与前几篇文章中的相同函数相比,它变得极致紧凑。

inline void MoveTo(uint Key)
{
        static double local = 0;
        datetime dt;
        bool    bEClick, bKeyBuy, bKeySell, bCheck;
        double  take = 0, stop = 0, price;
                                
        bEClick  = (Key & 0x01) == 0x01;    //Let mouse button click
        bKeyBuy  = (Key & 0x04) == 0x04;    //Pressed SHIFT
        bKeySell = (Key & 0x08) == 0x08;    //Pressed CTRL  
        Mouse.GetPositionDP(dt, price);
        if (bKeyBuy != bKeySell)
        {
                Mouse.Hide();
                bCheck = CheckLimits(price);
        } else Mouse.Show();
        PositionAxlePrice((bKeyBuy != bKeySell ? price : 0), def_IndicatorTicket0, IT_PRICE, (bCheck ? 0 : GetBaseFinanceTakeProfit()), (bCheck ? 0 : GetBaseFinanceStopLoss()), GetBaseFinanceLeveRange(), bKeyBuy);
        if((bEClick) && (bKeyBuy != bKeySell) && (local == 0)) CreateOrderPendent(bKeyBuy, local = price);
        local = (local != price ? 0 : local);
}

原因是,现在系统中会有一个新规则,因此函数“减轻了一些负重”,变得更加紧凑。


结束语

我已在这里介绍了一些将在下一篇文章中用到的变更。 所有这些的目的是令它们更简单,并在不同时间展现出可能不同的事物。 我的想法是,每个人都学习并遵循如何编写 EA,从而帮助您进行操作,这就是为什么我不单只是介绍一个完整并可立即使用的系统。 我想表明,有一些问题需要解决,并介绍我在解决开发过程中出现的问题,和解决问题时所采取的途径。 我希望您能理解这一点。 因为如果打算创建一个系统,并以完备待用的形式呈现,那我最好这样做,并推销这个想法,但这并非我的意图...

本文由MetaQuotes Ltd译自葡萄牙语
原文地址: https://www.mql5.com/pt/articles/10474

附加的文件 |
EA.zip (12023.87 KB)
最近评论 | 前往讨论 (1)
QQ1171513819 微信AT5050
QQ1171513819 微信AT5050 | 14 9月 2022 在 03:43

       ATFX黄金外汇平台诚招代理商
       ATFX免费开设个人/公司代理,专属代理后台,佣金平仓即返。英国FCA全牌照外汇经纪商诚招代理
     【优势】全球11国12个对外办事处,横跨欧洲、美洲、非洲、亚洲
     【优势】平台稳定,顶级流动性报价。STP模式,多家知名流动商LP报价,快速成交,不卡盘不掉线,不滑点,对冲,拨头皮,秒单,EA等无任何交易限制。
     【优势】英国FCA顶级外汇牌照,塞浦路斯stp牌照。毛里求斯金融牌照,阿布扎比外汇牌照,圣文森特外汇金融牌照等多重监管。资金安全,正规平台支持长期大资金盈利出金。
     【优势】快速安全的多种对公出入金方式:电汇,银联,对公,支付宝,微信等
     【优势】品种全:外汇,黄金,原油,指数,美股,港股等200多种交易品种
     【优势】超低点差黄金0.35,欧美0.18。基础返佣黄金/指数/原油/每手14/15/16美金每手;外汇8/9/10美金每手。MT4操作
        ATFX代理咨询:85292029084(微信)  QQ:1171513819      微信:AT5050
机器学习和交易中的元模型:交易订单的原始时序 机器学习和交易中的元模型:交易订单的原始时序
机器学习中的元模型:很少或无人为干预的情况下自动创建交易系统 — 模型自行决定何时以及如何进行交易。
学习如何基于交易量设计交易系统 学习如何基于交易量设计交易系统
这是我们系列文集中的新篇章,介绍如何基于最流行的技术指标设计交易系统。 本文将专门讨论交易量指标。 作为一个概念,交易量是金融市场交易中非常重要的因素之一,我们必须予以关注。 贯穿本文,我们将学习如何基于交易量指标设计一款简单的系统。
您应该知道的 MQL5 向导技术(第 01 部分):回归分析 您应该知道的 MQL5 向导技术(第 01 部分):回归分析
今天的交易者都是一位哲学家,他几乎总是(有意识地或无意识地)寻找新的思路,尝试它们,选择修改或抛弃它们;这是一个需要付出相当勤奋程度的探索过程。 这显然会花费交易者高昂的时间,且需要避免错误。 本系列文章将提出,MQL5 向导应该是交易者的支柱。 为什么呢? 因为交易者不仅经由 MQL5 向导组装他的新想法来节省时间,而且大大减少了重复编码的错误;他最终会把精力集中在交易哲学的几个关键领域。
神经网络实验(第 1 部分):重温几何学 神经网络实验(第 1 部分):重温几何学
在本文中,我将利用实验和非标准方法开发一个可盈利的交易系统,并验证神经网络是否对交易者有任何帮助。