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?
Order
would inheritsOrders
? for meOrders
is nothing more than a list/array/set/collection/whatever of Order