0

I'm writing a trading framework in MQL and I'm confused how I should organise my class hierarchy.

|-- Terminal (Log)
|   |-- Market
|   |   |-- Chart
|   |   |   |-- Draw
|   |-- Account
|   |   |   |-- Trade (Orders?)
|   |   |   |   |-- Orders?
|   |   |   |   |   |-- Order

In the brackets are the class variables within that class. Question marks are unclear.

Here is my previous attempt, but the compiler basically gave up and was too confused.

Below is brief sample of implementation of these classes to give you an idea what the class is about with key methods to show their purpose (method bodies are omitted if not relevant).

To be clear, the language syntax doesn't allow to extend one class by multiple, also doesn't support abstract classes. However instance of the class can be passed into another in the constructor and assigned to a class variable.


More detailed explanation of the classes and their purpose. I hope this is clear.

  • Terminal

    Defines trading terminal methods and error handling.

    class Terminal {
      Log *logger;
      // Methods.
      static bool IsTradingAllowed(); // Check if terminal is allowed to trade.
      static string CodeToError(int code); // Translate error code into text.
    }
    
    • Market

      Defines class to access the market properties for the given symbol.

      class Market : public Terminal {
        string symbol; // Trading symbol pair.
        // Methods.
        double Ask(); // Get ask price (it's using symbol).
        static bool SymbolExists(string _symbol); // Log an error on fail (use logger).
      }
      
      • Chart

        Chart and timeframe operations. One symbol pair in the market potentially could have multiple timeframes, so it's more specific way to access market data.

        class Chart : public Market {
          ENUM_TIMEFRAMES tf; // Class variable for timeframe (e.g. M1, M30).
          // Methods.
          void Chart(_tf) { tf = _tf; } // Constructor.
          bool IsPeak(); // Uses symbol and tf to check price peak.
        }
        

        There is also another possibility that Market could be a sub-class of Chart. However there can be multiple chart timeframes for the same market. Before I had separate Timeframe class, but having just Chart class is more obvious. To avoid confusion with Market, Chart consist bars on the chart.

        • Draw

          Interacts with objects on the chart (e.g. draw a line on the chart).

          class Draw : public Chart {
            long chart_id;
            // Methods.
            bool DrawVLine(name, time);
            bool ObjectAdd(...);
            bool ObjectDelete(name);
          }
          
    • Account

      Class to access the main account details. User is logged into account via terminal, so it's logical that Account extends Terminal.

      class Account : public Terminal {
        double init_balance, current_balance;
        // Methods.
        double GetBalance(); // Current account balance.
        double GetProfit(); // Account profit.
        double GetMarginFree(); // Use Terminal logger to report any error.
      }
      
      • Trade

        Class to take user account into market action. Trade is extending Account class, because logically its actions affect the user balance. If user has no balance, trades are not possible.

        class Trade : public Account {
          struct trade_params { uint slippage; }
          Orders *orders; // ???
          Chart *chart; // ???
          // Constructor.
          void Trade(Chart *_chart) { chart = _chart; } /// ??? Chart or Market?
          // Methods.
          double CalcLotSize(); // Problem: Lack of access to Market.
          double CalcMaxLotSize(); // Problem: Lack of access to Market.
          bool NewOrder() { // Create a new Order instance.
            if (IsTradingAllowed()) { orders[] = new Order(); };
          }
          double OptimizeLotSize() { chart.market.GetSymbol(); }
          Orders *Orders(); // Getter to return access to Order class.
        }
        

        Concerns:

        • I'm not sure whether it's better for Orders be defined as a class variable, or another sub-class.
        • I need to access to Market as well to Account class variables.
        • Not sure about passing another sub-class from different tree into constructor to access its values (whether it's Market or Chart).
        • Whether passing another class in constructor and calling chart.market.GetSymbol(); is a valid approach.
        • Passing Chart instance in Trade, it's not clear how I should initialize Draw class when I need to call some drawing methods on the current chart. On the other hand, passing the deepest class to access all of its features, doesn't make much sense.
      • Orders

        Class to deal with list of orders/deals on the market (as a whole). The Orders class deal with pool of orders and it extends Trade, because each trade is result of trading action.

        class Orders : public Trade {
          Order *orders[]; // Current orders.
          Order *history[]; // Orders from history.
          // Methods.
          Order *SelectOrder(int _ticket); // Selects Order instance by a ticket.
          Order *SelectMostProfitable(); // Returns Order instance.
          int TotalOrders(); // Returns number of all orders from the main pool.
          bool CloseAll(); // Traverse orders and invoke Close()
        }
        
        • Order

          Class to deal with a single specific order. Once trading action takes place, new order (instance) is being placed.

          class Order : public Orders {
            struct params { int ticket; double price, profit; }
            Order(); // Constructor to open new order and update params.
            // Methods.
            double GetTicket(); // Returns params.ticket.
            double GetProfit(); // Returns order current profit.
            bool Close(); // Closes the order.
          }
          

          It extends Orders, because it's part of the pool, however the Order doesn't benefit much from its parent (no common variables, it's more like other way round). It doesn't make much sense to give Order access to methods to deal with all orders, but on the other hand, which class should be a parent instead?


I would like to understand what is the problem in above class hierarchy, ideally by following some good OOP practices.

Question:

How above hierarchy should look like in the ideal world and what would be your suggestion to address my concerns?


My thoughts are:

  • At first, it seems to be logical that Order is extended by Orders class, but on the other hand having orders[] in Trade class seems illogical to have separate instances of Orders (each time when it's created), so it could be that extending Order to Market would be a better approach, but I'm not sure. Or to not extend Order class at all.
  • I think Orders class doesn't fit right. Ideally dropping it and merging into Trade would be great, but I think having separate class to deal with orders as a whole would be more logical. On the other hand, dropping Orders class could solve a lot of problems.
  • I'm not sure at what point Trade and Chart should interact with account. Should Market/Chart class be used in Trade class as a variable and assigned from the constructor, should the interaction happen from the common parent class or the hierarchy should be completely different.
  • The hierarchy is not fixed, and I'd like to add further more classes on top of it, so the hierarchy should be fairly flexible.

Any thoughts?

3
  • My instinct would be to flatten this hierarchy. I don't see a good reason for all the nesting, especially since the classes are only loosely related. Jan 20, 2017 at 2:05
  • @RobertHarvey At the beginning I had all classes flat (passing only classes which I need in the constructors), then I've sent the code to one guy and told me that he doesn't understand the architecture of the project and cannot imagine what is the tree of classes and their dependencies. So I started to play around to make some logical hierarchy out of it so I end up confused as well, so I'm not sure what would be better.
    – kenorb
    Jan 20, 2017 at 2:08
  • if those classes aren't related, the fact they're flatten is normal. For instance, why an Order would inherits Orders ? for me Orders is nothing more than a list/array/set/collection/whatever of Order
    – Walfrat
    Jan 20, 2017 at 9:01

1 Answer 1

4

Inheritance

When I see a class as a subtype of another, it tells me that is the most important thing to know about the class. So I would expect that the whole reason Market and Account exist are to be different types of terminals. If that's the case, MarketTerminal and AccountTerminal would be much better names for them, and their Terminalness should be obvious in how they're defined and used. That's not the impression I'm getting from your code.

Inheritance is transitive (inherited?), so realize that you are currently saying that a Draw is a specific kind of Chart, and also a specific kind of Market, and Terminal. Does it make sense that any function that you write that uses a Terminal should also work if you give it a Market, a Chart, a Draw, or a Trade?

I'll go into more depth on a couple classes below.

Terminal

It looks to me like Terminal has one purpose: to hold a Log. (Or is it a Logger? Those seem like different things.) That's not what I expect from the word "Terminal", but I don't know your domain well. Certainly it's not clear to me why this class exists. If I needed a Log, why would I ask for a Terminal, rather than just a Log?

You say a Terminal "defines error handling", but it doesn't -- the class just happens to statically contain a couple utility functions. You can't really think of static methods as having much to do with the definition of the class, which is about how instances of that class behave. It just seems like you needed somewhere to put these functions; maybe you're missing an important concept in your domain that can better represent these concepts.

Market

According to your hierarchy, a Market is a type of Terminal. According to your description, it allows access to market properties for a symbol. According to your code, it looks like it's meant to be a pair of (trading symbol, asking price), which sounds like an Order of some type. It sounds like you've found three concepts you want to handle, but they're all kinda jammed together in this nebulous thing right now.

string symbol and bool SymbolExists(string symbol) don't feel like they belong in the same object. Why do I need to give you a symbol if you already have one? How could an instance even exist if the defining symbol doesn't?

If an "ask" is a noun, I wouldn't expect Ask to be a function. You have a comment saying "Get ask price", what about GetAskPrice as the name of the function?

Find the core concepts of your domain

I could go on, but I think there's plenty for you to think about. In general these classes don't really have a single, focused reason to exist; that all seem kind of jumbled. I would recommend you step back from the code for a day or two to research and understand the domain better -- the important concepts should eventually make themselves known. Use inheritance very sparingly: only when the entire reason a class exists is to be a specific kind of another class.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.