更好的程序员(第 06 部分):9 个导致有效编码的习惯

Omega J Msigwa | 31 一月, 2022

概述

在开始编码之前,我们需要在编码中添加一些习惯,这些习惯可以帮助我们在编码过程中始终保持专注。 大多时候,我们也许会发现自己正在编写代码,但却没有意识到,出于各种原因,例如缺乏专注、没有计划或执行计划不佳,我们的工作效率极其低下。

有效编码

内容:

免责声明:

本文中使用的所有代码片段和编码示例仅用于教育目的。 其中一些并未经过仔细的测试/调试。 它们只是用来阐明我的观点。 如果您决定使用任何代码,您应自担风险。

01: 编码之前规划您的项目


如果您询问 100 名程序员在开始编写代码之前是否规划好了他们的系统,那么恐怕不到 10 名程序员会告诉您,他们开始在键盘上敲代码之前已做好了工作规划。

如果我们想有效地编码,这是我们必须解决的主要问题。

试想您能在不知道要去哪里的情况下跳上公交车吗? 绝对不会吧。

在您开始在键盘上敲代码之前,提前记下您需要的程序类型、工具、您可能想在里面使用的函数库、等等。

自由职业者上有一种叫做工作规范的东西,它与每位开发人员在开始编写任何代码之前必须拥有的工作规划完全相同。

如果您手头没有笔、笔记本或一片纸,您可以利用一些免费程序,譬如微软的 WordWPS 办公套件来规划您的工作。 您可以绘制流程图和插图来描述您需要的一切。

我强烈同意查尔斯·凯特林(Charles Kettering)所说的“问题陈述明白就相当于解决了一半”。

例如,我只想创建一个简单的网格智能交易系统,其中包含 Labouchere 资金管理系统。

以下是简单的工作规划。

注意:以下所有输入变量的初始值都可以优化

初始值: Lotsize = 0.01; xgrid = 100; LotIncrement = 0.01;

我的策略

Lotsize 的公式为按仓位类型

网格条件 (策略,但以更明确的方式)

资金管理

函数库

条件 01:当尚未开仓时,买入和卖出双向开仓(每种类型都有一笔持仓)

X = Lotsize + (LotIncrement * 持仓类型数量)

 例如: BuyLotsize = 0.01 + ( 0.01 * 买入持仓数量);

当买入持仓数量为 0(零)当已经有买入持仓,且当前出价低于最后一笔买入仓位的开仓价减去 xgrid 点数时,买入条件出现  

StopLoss = 0 (默认)

 

标准仓位类库

标准品种信息类库

标准交易类库

条件 02 (卖出条件):如果市场价格比前一笔卖出仓位上涨了 xgrid 点数,则开立一笔卖出持仓,其手数等于前一笔卖出仓位的  Lotsize 加上 LotIncrement 的值。    卖出条件发生在卖出持仓数量为 0(零),已有卖出持仓,且要价高于上笔卖出仓位的开仓价加上  xgrid  点

TakeProfit = 100 (默认)

 
条件 03 (买入条件):如果市场价格比之前的买入仓位下跌 xgrid 点数,则开立买入持仓,Lotsize 等于之前的 Lotsize 加上 LotIncrement 的值。        

仅仅从简单的工作规划中,您就可以看到工作执行起来要容易得多,远非以一种模糊的思维方式开始编写代码可比。 更好的规划可以帮助您辨别您需要学习或提醒自己的地方,以防您似乎忘记了它们。

在开始编写代码之前,确保您有一份详尽的工作规划。 越清晰越好

02: 制作代码颗粒化集合


如果您发现要在多个程序(如智能交易系统、指标或脚本)中多次定义函数或类,那么您可能需要它们的集合,从而可以多次复用它们,而无需反复定义它们。 若要最有效地做到这一点,需要经由面向对象编程 (OOP)。

从工作规划的例子中,我们看到我们需要在 EA 中创建两个函数。 

  1. 一个函数按类型计算仓位
  2. 一个函数按持仓类型提供最后一笔持仓的开仓价

这些函数几乎在每个网格 EA 里都需要,因此我们可以创建一个名为 gridmodule.mqh 的包含文件,并将这两个函数存储在其内,从而可将它们包含在我们主要的 .mq5 文件里。

gridmodule.mqh 内部

//+------------------------------------------------------------------+
//|                                                  gridmodule.mqh |
//|                                     Copyright 2021, Omega Joctan |
//|                        https://www.mql5.com/en/users/omegajoctan |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, Omega Joctan"
#property link      "https://www.mql5.com/en/users/omegajoctan"
//+------------------------------------------------------------------+
//| Libraries                                                        |
//+------------------------------------------------------------------+ 
#include <Trade\PositionInfo.mqh>
#include <Trade\SymbolInfo.mqh>

CSymbolInfo   m_symbol;
CPositionInfo m_position;
//+------------------------------------------------------------------+
//| DLL imports                                                      |
//+------------------------------------------------------------------+
class CGrid
  {
   protected:
     int                   MagicNumber;
  
   public:
                           CGrid(void);
                          ~CGrid(void);
      void                 InitializeModule(int magic) { MagicNumber = magic; }
      double               LastPositionOpenPrice(ENUM_POSITION_TYPE type);
      int                  CountPositions(ENUM_POSITION_TYPE type);
   
  };
//+------------------------------------------------------------------+
//|               Constructor                                        |
//+------------------------------------------------------------------+
CGrid::CGrid(void)
 {
 
 }
//+------------------------------------------------------------------+
//|                Destructor                                        |
//+------------------------------------------------------------------+
CGrid :: ~CGrid(void)
 {
 
 }
//+------------------------------------------------------------------+
//|           Last Position Open Price By Position Type              |
//+------------------------------------------------------------------+
double CGrid::LastPositionOpenPrice(ENUM_POSITION_TYPE type)
 {
  double LastPrice = -1;
  ulong  LastTime = 0; 
   for (int i=PositionsTotal()-1; i>=0; i--)
     if (m_position.SelectByIndex(i))
       if (m_position.Magic() == MagicNumber && m_position.Symbol()==Symbol() && m_position.PositionType()==type)
          {
             ulong positionTime = m_position.TimeMsc();
             if ( positionTime > LastTime ) //FInd the latest position
               {
                  LastPrice = m_position.PriceOpen();
                  LastTime = m_position.TimeMsc();
               }
          }
       return LastPrice;
 }
//+------------------------------------------------------------------+
//|                Count Positions By Type                           |
//+------------------------------------------------------------------+
int CGrid::CountPositions(ENUM_POSITION_TYPE type)
 {
   int counter = 0; //variable to store our positions number
   for (int i=PositionsTotal()-1; i>=0; i--)
     if (m_position.SelectByIndex(i)) // Select position by its index
        if (m_position.Magic() == MagicNumber && m_position.Symbol() == Symbol() && m_position.PositionType() == type)
          {
            counter++; 
          }
      return counter;
 }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+

如此做有很多益处,因为它能令代码调试变得容易:调试只需在一个地方完成,并且在主要的 .mq5文件里需要编写的代码更少。

03: 例行公事

例行公事可令您的思维纳入正确的编码思维框架,这对提高你的工作效率有很大帮助。 它也是帮助您保持承诺,并成为一名每天都始终如一的程序员的最佳方式。

举例来说,您可以例行从每天上午 8:00 到 11:00 进行编码。

若您坚持同样的例行公事,在几周或几个月后,您会注意到,每当您在这些时间里用电脑开始编码时,您的大脑就会出于兴奋状态。

创建一个最适合您的时间表。


04: 深度工作时间表

在现代社会,太多事情让人容易分心。 如日很难集中精力,尤其是当编码遇到难关时。 请注意,只有在一天中的几分钟/几个小时内,我们才会高度专注,从而变得富有创造力。

我们的大脑通常需要很长一段工作时间不受干扰,才能达到这种境界。 为了达到这种状态,我们需要安排数天长的时间不间断地编码。

正是通过长时间的深度工作,我们才能完成日常例行工作中可能面对的大量艰苦工作。 我建议每一位程序员都要计划好某些日子,让自己努力工作,不受干扰。

05: 制作单用途函数并进行测试

避免在一个函数中进行太多操作。 每当您意识到必须为一个主线操作编写代码时,考虑创建另一个函数。

Ontick 应答函数中(或在任何其它主要或次要函数中)塞入太多的循环,将无时不刻地对您造成伤害。 这就像您拔出装在口袋里的手榴弹顶针。

强迫所有逻辑出现在一个地方,迫使简单的算法变得复杂,这是制造错误的最佳方法之一,它会令您付出金钱和生命中最宝贵的资源,时间作为代价。

我相信大多数时候编码都必须是有趣的,这样我们才能始终如一。 复杂的算法毫无乐趣,不管阅读代码的人多么有经验和聪明。 确保每个函数只执行一个操作,且它应该有一个易于阅读和理解的函数名。

查看上一示例中仓位类型的函数 CountPositions,该函数带有一个参数

int CGrid::CountPositions(ENUM_POSITION_TYPE type)
 {
   int counter = 0; //variable to store our positions number
   for (int i=PositionsTotal()-1; i>=0; i--)
     if (m_position.SelectByIndex(i)) // Select position by its index
        if (m_position.Magic() == MagicNumber && m_position.Symbol() == Symbol() && m_position.PositionType() == type)
          {
            counter++; 
          }
      return counter;
 }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+

仅需看一眼函数名,每个人都可以知道函数都是关于按类型计算仓位的,这也是函数内部实际所做的,仅此而已。

06: 为您自己的未来添加评论

在代码中添加注释的简单习惯可被视为一种初级的编码方式。 但对于那些知道如何有效利用它的人来说,它可以改变游戏规则。

input    group        "Money management"
input    double       InitialLots = 0.01; //Initial position volume
input    int          EquityUse = 50;  // Percentage of equity to be used in all trading operations
input    group        "Average True Range";
input    int          AtrBarSignal = 1; //Atr value will be calculation taken from this candle

您现在编写的大多数变量、函数和代码片段将在几周甚至几天后被您遗忘。 若缺乏有意义的注释,您将对之前编写的代码毫无头绪(想想这有多愚蠢!!)。

在 MQL5 中添加注释将允许 IDE MetaEditor 帮助您在每次使用它时更容易回忆它。

Metaeditor 注释提醒

为什么要让 MetaEditor 难于帮助您? 这对我来说毫无意义。

请记住,在这个职业生涯中,您并非单打独斗,所以您总是希望代码库中的其他开发人员能够轻松地传递和使用这些代码,而您要做到这一点的唯一方法就是为代码加上注释,令其易于阅读和理解

07: 养成盲打习惯


我们不要忘记,为了有效地编写代码,我们需要成为键盘上的优秀编剧。 每位程序员都必须努力开拓和提高他们的键盘打字能力。 若要擅长打字,最好的方法就是养成一种盲打的习惯:在不看键盘的情况下在键盘上敲打正确的字母。

大多时候,我们倾向于一边看键盘和屏幕一边编写代码,却没有意识到这是一种不专业的打字方式。

您必须突破打字习惯,通过不看键盘(只看屏幕)来训练自己打字。 这可以通过肌肉记忆打字来实现。

养成这个习惯有点困难,但一旦养成了,打字就变成了一种自动练习。

在一个名为 keybr.com 的免费网站上练习了几周后,我养成了这个习惯,故我要向所有想养成这种习惯的人推荐这个最佳网站。

08: 使用最佳工具 


作为一名程序员,工具对于提高您的整体工作效率是一个非常重要的部分,我如何强调这一点都感到不够,因为您需要更好的工具,即便不是最佳的(硬件和软件)。

始终确保您选用的是最好的函数库(我建议选用 MQL5 标准库),当您陷入困境时,以及当您需要学习和尝试新事物时,您还需要拥有最好的信息源(阅读这篇文章了解更多详细信息),最重要的是,您需要一台性能良好的 PC,以及顺畅的互联网连接。

09: 进行版本控制


当您编写一个程序时,您是否想过,在您做了几次修改之后,出于某种原因,您意识到需要返回到之前编写的代码?

进行代码版本控制始终是一个好习惯,这样您就可以跟踪进度,并可以随时返回到以前版本的代码。

我知道两种方法可完成这件事。 第一种方法是用 Git,第二种方法是手动过程,包括通过文本文件复制和粘贴来存储代码的方法。

b>使用 Git

如果您还不熟悉 Git 和 Github,我建议您阅读 Git 的文档。

b>经由文本文件

为项目创建文件夹,添加该项目所需的所有必要文件。 然后在文件资源管理器中打开该文件夹,查看文件夹中的内容(请参见我上一个示例中的下图)。

经由文本文件


您的 .mq5 文件做出重大改变之后,与添加新函数类似,您必须另行创建一个文本文件,文件名里指定代码的版本和添加的内容。 参看图片

使用文本文件的版本控制

您可以在主要的 .mq5 或 .mqh 文件旁边打开一个文本文件,并在 windows 中单击 CTRL+F7 保存这两个文件,编辑器上会编译所有打开的文件。 编译成功后,您可以忽略来自该文本文件的所有错误,并关闭它。

文本文件旁边的 mq5 文件


结束语

本文到此为止。 我希望您已经获得了一些积极的东西,令您能够进一步成为一名更好的程序员。 如果您发现自己还缺少一些智慧之珠,请在下面的讨论版面与我和其他程序员分享。 本文中使用的所有代码都附在文后。

感谢阅读,致以最崇高的问候。