Libraries: Virtual - page 8

 

After language changes over the last year, my version of Virtual stopped working. I switched back to yours.
But I really missed a commission that would take into account price changes. Especially for crypto, where the price changes many times during testing.
In my old version was accurate, but slow calculation with string operations and querying prices by instrument. Probably from this slowdown you didn't want to add it to your own.

This time I made a simple and fast calculation for instruments like EURUSD, AUDUSD ... with USD deposit currency and for all crypto, there all to USDT. I.e. if quote currency == account currency, the calculation will be correct.

#define COMMISSION_TO_PRICE 2 // умножить полученную комиссию на цену, например для EURUSD *=1.12345; 1 - комиссия берется только при входе, 2 - комиссия берется 2 раза и при входе и при выходе

The commission is simply multiplied by OpenPrice or ClosePrice. For instruments like USDJPY or if the deposit currency is not USD(T), the option can be disabled, as it will not be accurate (like your current option).

#ifdef  ORDER_COMMISSION
 this.Commission = this.Lots * (ORDER_COMMISSION);
 #ifdef  COMMISSION_TO_PRICE
   this.Commission *= this.OpenPrice + (COMMISSION_TO_PRICE==2 ? this.ClosePrice : 0.0);
 #endif // COMMISSION_TO_PRICE
#endif// ORDER_COMMISSION

Here is what I got on EURUSD:

DC:
Virtual c * per price
Virtual without * on price

As you can see, with the added option the commission differs only by 18 cents for 140 trades. And without it by 42 dollars. Of course, you can select in your variant the average multiplier not 4, but 4.305 for example (average commission for the test), but it will have to be done manually for each instrument and recompile.

The edits are not big, I hope you will want to add this option to the library code. Or maybe you will think of something more universal.


Files:
Order.mqh  36 kb
 
Forester deposit currency USD and for all crypto, there all to USDT. I.e. if the quote currency == account currency, the calculation will be correct.

In fact, this formula always works.

Commission = 0.002%; // per side.

OrderCommission = (OrderOpenPrice + OrderClosePrice) * Commission * OrderLots * TickValue / TickSize;

Of course, you can choose in your variant the average multiplier not 4, but 4.305 for example (average commission for the test), but it will have to be done manually for each instrument and recompile.

Another variant.

input double inCommission = 0;
// .....
#define  ORDER_COMMISSION inCommission // OrderCommission() = OrderLots() * (ORDER_COMMISSION)), including the dynamic option.

Edits are not big, I hope you will want to add this variant to the library code. Or maybe you will think of something more universal.

Totally agree that the edit is useful and should be made. It should be checked for partial close (OrderClose not for the whole lot) and CloseBy. To check it, it is enough to write a script where two differently directed positions are opened on the same tick and a partial and CloseBy-close are made at once. I am not ready to do it myself yet. If you provide such a script with correct results, I will make your corrections faster.

 
fxsaber #:

In fact, this formula works all the time.

It's easier for you, you're always on top of things. I don't do it all the time. For the first time since July, I decided to test something again....

About
OrderClosePrice
- there are probably some brokerage centres that take commission only at opening - for them it is better not to take it into account. For pipsing the difference will be small, but for big targets it will be noticeable.
 
fxsaber #:
Totally agree that the edit is useful and should be made. It is necessary to check for partial closing (OrderClose not for the whole lot) and CloseBy. To check it, it is enough to write a script where two differently directed positions are opened on the same tick and a partial and CloseBy-close are made at once. I am not ready to do it myself yet. If you send me such a script with a correct result, I will make your corrections faster.
I tested what is commented out there and I left it as a working version. But now I've forgotten everything. CloseBy - I don't use it, I usually have simpler ideas. I don't have time for it either. As long as there is enthusiasm, I will do strategy.
 

Virtual adds a commission to non-performing limits:


Apparently we need to do a type check
 
Forester #:

Virtual adds a commission to unexecuted limits:

Apparently we need to do a type check
#define ORDER_COMMISSION -5 // OrderCommission() = OrderLots() * (ORDER_COMMISSION)), including the dynamic option.
#include <fxsaber\Virtual\Virtual.mqh> // https://www.mql5.com/en/code/22577

void OnStart()
{
  if (VIRTUAL::SelectByHandle(VIRTUAL::Create()))
  {
    VIRTUAL::NewTick();
    
    OrderDelete(OrderSend(_Symbol, OP_BUYLIMIT, 1, 0.01, 0, 0, 0));
    
    Print(VIRTUAL::ToString(1));
  }
}


Result.

#2 2025.11.25 19:23:11.105 buy limit 1.00 EURGBP.pro 0.01000 0.00000 0.00000 2025.11.25 19:23:11.105 0.87715 0.00 0.00 0.00 0 - 00:00:00


It works correctly. Apparently, these edits require additional checking.

 
fxsaber #:

It works correctly. Apparently, these edits require additional verification.

You are right.

In ToClose() I added calculation of commissions outside if (this.IsPosition()){

Fixed it, attached the file

Files:
Order.mqh  37 kb
 

I read equity and balance from Virtual on each tick and create a chart. It was not noticeable until today, but today they put huge swaps of -66.

The chart looks like this.



It should be like the record from MT5.

The final profit, as you can see, differs by a few cents due to errors in rounding commissions (I applied real commissions, not in points) and swaps (I still don't understand how MT rounds them - not via round()).

The sum of swaps today for this calculation is -5208.97. Just the size of the jump on the chart. I think that swaps are not added to equity and balance during the calculation, but are applied only at the end, that's why it looks like this.

I call VIRTUAL::CalcSwaps(...) in OnTester() Can this be done in virtual trading? It would be too expensive on each tick. Perhaps it should be done at the beginning of each day. Maybe it is already programmed and should be enabled by some defyne? I remember that in my version I made swaps for each day, as you have something different. If you haven't done it in the past, I can send you my code for an example.

 
Aleksei Kuznetsov #:

Maybe it's already programmed and should be enabled by some defyne? I remember that in my version I made swaps for each day, as you have it differently. If you haven't done it in the past, I can send you my code for an example.

I haven't. But for the Balance/Equity-Curve plotting problem, I would do CalcSwaps once in OnTester and then the corresponding one-pass adjustment of Balance[]/Equity[] after that. It's super-cheap and accurate.

The whole swaps thing in MT5 seems flawed, as there is no swap history. And on a large history the results are horribly crooked when swaps are taken into account. It may happen that you optimised on the weekend and then the best runs from opt-files show completely different values, if they are done on weekdays.

Still, MT5-tester is not a benchmark.