Interessante Sicht auf die PLO - Seite 11

 
Georgiy Merts:

"Apotheose" halte ich für einen Ausdruck von fxsaber, zu dem er selbst nicht sagen konnte, wie er funktioniert, sondern nur sagte, dass "der Code wiederholt getestet wurde und er funktioniert". Das sollte meiner Meinung nach nicht der Fall sein:

Dieser Code prüft, obein otfFilingType-Auftrag ausgeführt werden kann, und gibt ihn zurück, wenn er auf strSymbol verfügbar ist, andernfalls ist er korrekt.


Ich habe absolut keine Ahnung, wie das funktioniert. Und verlassen Sie sich nur auf die Autorität von fxsaber.

Vielleicht kann das jemand erklären?

Um das zu verstehen, zerlegen Sie diesen sperrigen Rückgabewert in seine Bestandteile.

 
Igor Makanu:

Ihr Beispiel von "der Zweck heiligt die Mittel"

Übrigens ist dieser Code wie Spaghetti, aber wenn man bedenkt, dass er wahrscheinlich der nützlichste Autor in kodobase im Moment ist und ich selbst seine Codes so verwende, wie sie sind, sollte jemand anderes Kritik üben

 
Andrei Trukhanovich:

Übrigens ist dieser Code wie Spaghetti, aber angesichts der Tatsache, dass er wahrscheinlich der nützlichste Autor in kodobase im Moment ist und ich selbst seine Codes so benutze, wie sie sind, lassen Sie jemand anderen Kritik üben

es geht nicht um Kritik, ich versuche herauszufinden, was sie tun kann.... Aber wie das Interview zeigt, und@fxsaber selbst zugab - nichts als Kopfschmerzen


SZZ: es ist höchstwahrscheinlich, dass die erste Variante des kleinen Monsters deutlicher aussah ;)

 
Igor Makanu:

ZS: die Wahrscheinlichkeit ist groß, dass die Originalversion des kleinen Monsters offensichtlicher aussah ;)

Entnommen aus ZIP (enthält die allererste Version).

  static bool VirtualOrderSelect( const TICKET_TYPE Index, const int Select, const int Pool = MODE_TRADES )
  {
    return(VIRTUAL::SelectOrders ? VIRTUAL::SelectOrders.OrderSelect(Index, Select, Pool) :
           #ifdef __MQL5__
             #ifdef __MT4ORDERS__
               ::OrderSelect(Index, Select, Pool)
             #else // __MT4ORDERS__
               false
             #endif // __MT4ORDERS__
           #else // __MQL5__
             ::OrderSelect(Index, Select, Pool)
           #endif // __MQL5__
           );
  }

Danach wurden jedoch immer mehr Funktionen benötigt, und der Code wurde langsam erweitert. Anders war es bei der Funktion Füllen. Dort wurde nichts ausgearbeitet, es wurde sofort geschrieben.

 
Vasiliy Sokolov:

Nur der Compiler weiß genau, was er tun wird. Moderne Compiler haben eine erstaunliche Euristik. Sie stellen sich auf den durchschnittlichen Codierer ein und wissen bereits besser, was er oder sie braucht. Das Beste, was ein Compiler tun kann, ist, einfachen, überschaubaren Code mit kurzen Funktionen zu schreiben. Für den Compiler ist es einfacher und effizienter, den aus vielen Funktionsknoten bestehenden Quellcodegraphen zu analysieren, um das resultierende Programm zu erstellen. Dies wird sich nur positiv auf die Produktivität auswirken, da die benötigten Funktionen an den richtigen Stellen untergebracht werden.

Sie haben völlig Recht.

Wenn wir über MQL5 sprechen, können Sie die "Optimierungs"-Methoden von vor 10-20-30 Jahren vergessen. Sie müssen einen möglichst lesbaren Code schreiben. Das ist es, und nicht das Hacker-Zeug und reine Klugscheißerei.

Warum?

Da der Compiler 5-10 Zyklen der Code-Neuordnung durchläuft, ist er erstaunlich klar und prägnant, ganz zu schweigen von der Verwendung von Dutzenden von Optimierungsmustern.

Der MQL5-Compiler ist amüsiert über menschliche Versuche, +2% Geschwindigkeit zu erreichen.


Wenn es Sie interessiert, sehen Sie sich an, wie mathematische Berechnungen erfolgen ohne Verzweigung und ein Befehl (128 Bit Daten) berechnet zwei Wurzeln auf einmal

Dieser Code wird zu folgendem Assembler-SSE-Code:

         D1=sqrt((X1-X)*(X1-X)+(Y1-Y)*(Y1-Y));
         D2=sqrt((X2-X)*(X2-X)+(Y2-Y)*(Y2-Y));
         D3=sqrt((X3-X)*(X3-X)+(Y3-Y)*(Y3-Y));
         D4=sqrt((X4-X)*(X4-X)+(Y4-Y)*(Y4-Y));
         D5=sqrt((X5-X)*(X5-X)+(Y5-Y)*(Y5-Y));
         D6=sqrt((X6-X)*(X6-X)+(Y6-Y)*(Y6-Y));
         D7=sqrt((X7-X)*(X7-X)+(Y7-Y)*(Y7-Y));
         D8=sqrt((X8-X)*(X8-X)+(Y8-Y)*(Y8-Y));
        ...
        sqrtsd  xmm1, xmm1
        unpcklpd        xmm4, xmm4
        movapd  xmm3, xmmword ptr [rsp + 432]
        unpcklpd        xmm3, xmmword ptr [rsp + 384]
        subpd   xmm3, xmm4
        mulpd   xmm3, xmm3
        unpcklpd        xmm0, xmm0
        movapd  xmm5, xmmword ptr [rsp + 416]
        unpcklpd        xmm5, xmmword ptr [rsp + 400]
        subpd   xmm5, xmm0
        mulpd   xmm5, xmm5
        addpd   xmm5, xmm3
        sqrtpd  xmm8, xmm5
        movapd  xmm5, xmmword ptr [rsp + 464]
        subpd   xmm5, xmm4
        mulpd   xmm5, xmm5
        movapd  xmm7, xmm9
        subpd   xmm7, xmm0
        mulpd   xmm7, xmm7
        addpd   xmm7, xmm5
        movapd  xmm6, xmm10
        unpcklpd        xmm6, xmm11
        subpd   xmm6, xmm4
        movapd  xmm3, xmmword ptr [rsp + 368]
        unpcklpd        xmm3, xmmword ptr [rsp + 352]
        subpd   xmm3, xmm0
        movapd  xmm4, xmm8
        shufpd  xmm4, xmm4, 1
        sqrtpd  xmm5, xmm7
        mulpd   xmm6, xmm6
        mulpd   xmm3, xmm3
        addpd   xmm3, xmm6
        sqrtpd  xmm15, xmm3
        movapd  xmm0, xmm14
        unpcklpd        xmm0, xmmword ptr [rsp + 336]
        subpd   xmm0, xmm2
        mulpd   xmm0, xmm0
        movapd  xmm2, xmm0
        shufpd  xmm2, xmm2, 1
        addsd   xmm2, xmm0
        movapd  xmm0, xmm15
        shufpd  xmm0, xmm0, 1
        sqrtsd  xmm12, xmm2
Das ist wirklich ein Kunstwerk. 8 Wurzeln wurden in 4 Aufrufen des Assembler-Befehls berechnet. Es wurden zwei Doppelnummern in einem Aufruf berechnet.

  • Beim Durcharbeiten eines Arrays ist alles normal, mit Prüfungen, Verzweigungen und Verlusten bei der Umwandlung von Double- in Integer-Indizes

  • Bei der Arbeit mit Arrays in diesem Beispiel kommt es zu einer ständigen Vermischung von FPU und ALU, was sehr schlecht für die Leistung ist

  • Die Optimierung des dynamischen Array-Zugriffs ist großartig - mehr als lobenswert. Aber das Mischen von FPU/ALU-Operationen + Double -> Integer + Verzweigung verschwendet Zeit

  • Die allgemeine Schlussfolgerung: Die Mathematik hat in MQL5 dank der perfekten Optimierung gewonnen. Nicht die Arrays verlieren hier, sondern die Mathematik gewinnt.


     
    Georgiy Merts:

    Warum ist das so?

    Im Gegenteil, mit zwei "Wenns" ist es viel einfacher als mit dem "Oder"-Operator.

    Es ist einfacher, zuerst eine Bedingung zu prüfen und die Funktion zu verlassen, wenn sie wahr ist, und dann die andere Bedingung zu prüfen und ebenfalls zu verlassen, wenn sie wahr ist, als das Ergebnis einer komplexen Bedingung mit logischem "oder" (das leicht mit "und" verwechselt werden kann) zu erraten und beide Rückgabeoptionen zu prüfen.

    Es ist ziemlich lustig, unten zu lesen, dass "die Rechtfertigung für solche ist Debugging", weil es bedeutet, dass solche Code ist viel verständlicher (sonst warum ist es in Debugging?).

    "Apotheose" halte ich für einen Ausdruck von fxsaber, zu dem er selbst nicht sagen konnte, wie er funktioniert, sondern nur sagte, dass "der Code wiederholt getestet wurde und er funktioniert". Das sollte meiner Meinung nach nicht der Fall sein:

    Dieser Code prüft, obein otfFilingType-Auftrag ausgeführt werden kann, und gibt ihn zurück, wenn er auf strSymbol verfügbar ist, ansonsten ist er korrekt.


    Ich habe absolut keine Ahnung, wie das funktioniert. Und verlassen Sie sich nur auf die Autorität von fxsaber.

    Vielleichtkann das jemand erklären?

    Ich habe mich einmal hingesetzt und es Schritt für Schritt auseinandergenommen, anscheinend brauchte ich einen Stift und Papier)

    Warum dieses Parsing nützlich war - ich habe verstanden, dass bei einer Änderung der Enum-Struktur alles kaputt geht), da ganzzahlige Beziehungen von Enum-Werten verwendet werden (die gemäß der Idee von Enumerationen selbst gekapselt sein sollten). Ich selbst versuche, dies zu vermeiden, höchstens das Verhältnis mehr - weniger. Denjenigen, die sich mit WinAPI beschäftigen, ist dies wahrscheinlich bekannt.

     
    Andrey Khatimlianskii:

    Um das zu verstehen, muss man nur diesen sperrigen Ausdruck "Rückkehrer" auseinandernehmen.

    Ja, genau das habe ich versucht. Aber ich war nicht motiviert genug, es auseinander zu nehmen...

     
    Renat Fatkhullin:

    Völlig richtig.

    Wenn wir über MQL5 sprechen, können Sie die "Optimierungs"-Methoden von vor 10-20-30 Jahren vergessen. Sie müssen einen möglichst lesbaren Code schreiben. Das ist es, und nicht das Hacker-Zeug und reine Klugscheißerei.

    Ganz genau. Zu diesem Schluss bin ich schon vor langer Zeit gekommen. Aber nicht, weil ich glaube, dass der Compiler es besser machen wird. Aber da die Hauptquelle für Probleme im Code der Mensch selbst ist, sollte man den Code so schreiben, dass er so einfach und transparent wie möglich ist.

    Wenn es nicht "transparent" sein kann, müssen Sie detaillierte Kommentare schreiben, warum es so und nicht anders ist.

    Und der Compiler... Selbst wenn es nicht effizient genug arbeitet, ist das weniger ein Problem als ein potenzieller Fehler, der darauf zurückzuführen ist, dass Sie einige Aspekte des Programms nicht berücksichtigen.

     
    fxsaber:

    Der Code ist sehr einfach und kurz(Beschreibung). Wenn Sie es auf FP schreiben, wäre es interessant zu vergleichen.

    Ich bitte Sie. C# im FP-Stil:


    using System;
    using System.Linq;
    using System.Collections.Generic;
    using static FeesCalculator.FeesCalcs;
    using System.Text;
    using static System.Math;
    
    namespace FeesCalculator
    {
        public static class EnumerableExt
        {
            /// <summary>
            /// Call of function 'action' for each element if sequence (mapping).
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="seq">Enumerable sequence</param>
            /// <param name="action">Need action</param>
            public static void Map<T>(this IEnumerable<T> seq, Action<T> action)
            {
                foreach (var item in seq)
                    action(item);
            }
        }
        /// <summary>
        /// Инвестиционный результат
        /// </summary>
        public record AccountRecord
        {
            public double Investor { get; init; }
            public double Manager { get; init; }
            public double Summary { get; init; }
    
            public static AccountRecord operator *(AccountRecord r, double v)
            {
                return new AccountRecord
                {
                    Investor = r.Investor * v,
                    Manager = r.Manager * v,
                    Summary = r.Summary * v
                };
            }
            public override string ToString()
            {
                StringBuilder sb = new StringBuilder();
                sb.Append(nameof(Investor)).Append(" = ").Append(Investor.ToString("F4")).Append(' ');
                sb.Append(nameof(Manager)).Append(" = ").Append(Manager.ToString("F4")).Append(' ');
                sb.Append(nameof(Summary)).Append(" = ").Append(Summary.ToString("F4"));
                return sb.ToString();
            }
        }
        /// <summary>
        /// Параметры оферты
        /// </summary>
        public record PammSet
        {
            /// <summary>
            /// Доходность за расчетный период
            /// </summary>
            public double Perfomance { get; init; }
            /// <summary>
            /// Вознаграждение управляющего
            /// </summary>
            public double Gift { get; init; }
            /// <summary>
            /// Количество расчетных периодов
            /// </summary>
            public int N { get; init; }
            /// <inheritdoc/>
            public override string ToString()
            {
                StringBuilder str = new StringBuilder();
                str.Append("Доходность за расчетный период: ").Append((Perfomance * 100.0).ToString("F1")).Append("%\t\n");
                str.Append("Вознаграждение управляющего: ").Append((Gift * 100.0).ToString("F1")).Append("%\t\n");
                str.Append("Количество расчетных периодов: ").Append(N);
                return str.ToString();
            }
    
        }
        /// <summary>
        /// Формулы расчета
        /// </summary>
        public class FeesCalcs
        {
            /// <summary>
            /// Если управляющий снимает деньги в конце каждого расчетного периода
            /// </summary>
            /// <param name="Performance"></param>
            /// <param name="Gift"></param>
            /// <param name="N"></param>
            /// <returns></returns>
            public static AccountRecord Set1(PammSet set)
            {
                Func<double> investor = () => Pow(1 + (set.Perfomance) * (1 - set.Gift), set.N);
                Func<double> manager = () => (investor() - 1) * set.Gift / (1 - set.Gift);
                AccountRecord ac = new AccountRecord
                {
                    Investor = investor(),
                    Manager = manager(),
                    Summary = investor() + manager()
                };
                return ac;
            }
            /// <summary>
            /// Если ничего не делалось в расчетные периоды
            /// </summary>
            /// <param name="Performance"></param>
            /// <param name="Gift"></param>
            /// <param name="N"></param>
            /// <returns></returns>
            public static AccountRecord Set2(PammSet set)
            {
                Func<double> summary = () => Pow(set.Perfomance + 1, set.N);
                Func<double> manager = () => (summary() - 1) * set.Gift;
                double s = summary();
                double d = manager();
                AccountRecord ac = new AccountRecord
                {
                    Summary = summary(),
                    Manager = manager(),
                    Investor = summary() - manager()
                };
                return ac;
            }
            /// <summary>
            /// Если управляющий снимает деньги в конце каждого расчетного периода и реинвестирует их
            /// </summary>
            /// <param name="Performance"></param>
            /// <param name="Gift"></param>
            /// <param name="N"></param>
            /// <returns></returns>
            public static AccountRecord Set3(PammSet set)
            {
                Func<double> summary = () => Pow(set.Perfomance + 1, set.N);
                Func<double> investor = () => Pow(1 + (set.Perfomance) * (1 - set.Gift), set.N);
                return new AccountRecord
                {
                    Summary = summary(),
                    Investor = investor(),
                    Manager = summary() - investor()
                };
            }
            /// <summary>
            /// Сопостовление: описание - функция расчета
            /// </summary>
            public static readonly Dictionary<string, Func<PammSet, AccountRecord>> DescrToAccountRecord = new Dictionary<string, Func<PammSet, AccountRecord>>
            {
                {"Если управляющий снимает деньги в конце каждого расчетного периода (1)", Set1 },
                {"Если ничего не делалось в расчетные периоды (2)", Set2 },
                {"Если управляющий снимает деньги в конце каждого расчетного периода и реинвестирует их (3)", Set3 },
            };
        }
        class Program
        {
            static void Main(string[] args)
            {
                var _ = args.Select((a) => double.Parse(a)).Take(3).ToList();
                PammSet pamm = new PammSet
                {
                    Perfomance = _[0]/100.0,
                    Gift = _[1]/100.0,
                    N = (int)_[2]
                };
                Console.WriteLine($"Данные для инвестиционного счета со следующими характеристиками:\n\n{pamm}\n");
                Console.WriteLine("Конечный результат для Инвестора, Управляющего и Суммарно:\n");
                Func<KeyValuePair<string, Func<PammSet, AccountRecord>>, string> f = (kvp) => kvp.Key + ".\n" + kvp.Value(pamm).ToString() + "\n";
                Enumerable.Select(DescrToAccountRecord, f).Map((s) => Console.WriteLine(s));
                Console.WriteLine(pamm);
                Func<int, PammSet> toPamm = (n) => new PammSet { Perfomance = pamm.Perfomance, Gift = pamm.Gift, N = n };
                Func<PammSet, IEnumerable<AccountRecord>> toAccs = (n) => DescrToAccountRecord.Select((s) => s.Value(n));
                var accounts = Enumerable.Repeat(1, pamm.N).Select((a, b) => a + b).Select(toPamm).Select(toAccs);
                Func<AccountRecord, string> toString = (ar) => ar.Investor.ToString("F2") + "\t" +
                                                               ar.Manager.ToString("F2") + "\t" +
                                                               ar.Summary.ToString("F2") + "\t";
                int period = default;
                accounts.Map
                (
                    (en) => 
                    {
                        Console.Write($"{++period}:\t");
                        en.Map((ar) =>
                            Console.Write(toString(ar)));
                        Console.WriteLine();
                    }
                );
            }
        }
    }
    

    FP ist natürlich ziemlich krumm (da es von einem krummen FP-Programmierer in der unvollständigen FP-Sprache C# geschrieben wurde), aber das Ziel ist zu zeigen, dass man mit modernem OOP-Jargon auch in FP arbeiten kann. Natürlich ist es begrenzt, es ist nicht F# oder Haskell, aber niemand verbietet es, im FP-Stil zu schreiben. Verwenden Sie Unveränderlichkeit, Funktionen höherer Ordnung, Abschlüsse, Zuordnungen usw. - Sie können gerne alles machen. Aber das macht den Code aus irgendeinem Grund nicht perfekt.

    s.w. Die Gesamtstruktur des Codes ist absichtlich dem Original nachempfunden. Obwohl es in FP eigentlich ganz anders sein sollte, ohne komplexe Objekte und ohne jede nachahmende Karte, hat der Künstler doch gemalt, was er konnte.

     
    Vasiliy Sokolov:

    Ich bitte Sie. C# im FP-Stil:

    Ich verstehe, dass es eine Frage der Gewohnheit und der Syntaxkenntnisse ist, aber es fällt mir sehr schwer, den Code zu verstehen, obwohl ich der ursprüngliche Autor bin.

    Forum zum Thema Handel, automatisierte Handelssysteme und Strategietests

    Interessante Meinung über OOP

    fxsaber, 2021.01.29 13:39

    Eine Lappalie.

    #property strict
    #property script_show_inputs
    
    input double inPerformance = 100; // Сколько процентов дает система за период
    input double inGift = 50;         // Награда управляющего в процентах (< 100)
    input int inN = 12;               // Сколько расчетных периодов
    
    struct BASE
    {
      double Investor;
      double Manager;
      double Summary;
    
      // Если управляющий снимает деньги в конце каждого расчетного периода.
      void Set1( const double Performance, const double Gift, const int N )
      {
        this.Investor = ::MathPow(1 + (Performance - 1)* (1 - Gift), N);
        this.Manager = (this.Investor - 1) * Gift / (1 - Gift);
        this.Summary = this.Investor + this.Manager;
    
        return;
      }
    
      // Если ничего не делалось в расчетные периоды.
      void Set2( const double Performance, const double Gift, const int N )
      {
        this.Summary = ::MathPow(Performance, N);
        this.Manager = (this.Summary - 1) * Gift;
        this.Investor = this.Summary - this.Manager;
    
        return;
      }
    
      // Если управляющий снимает деньги в конце каждого расчетного периода и реинвестирует их.
      void Set3( const double Performance, const double Gift, const int N )
      {
        this.Summary = ::MathPow(Performance, N);
        this.Investor = ::MathPow(1 + (Performance - 1)* (1 - Gift), N);
        this.Manager = this.Summary - this.Investor;
    
        return;
      }
    
      void operator *=( const double Deposit = 1 )
      {
        this.Investor *= Deposit;
        this.Manager *= Deposit;
        this.Summary *= Deposit;
    
        return;
      }
    #define  TOSTRING(A) #A + " = " + ::DoubleToString(A, 4) + " "
      string ToString( const bool FlagName = true ) const
      {
        return(FlagName ? TOSTRING(Investor) + TOSTRING(Manager) + TOSTRING(Summary)
                        : ::StringFormat("||%-12.4f||%-12.4f||%-12.4f||", this.Investor, this.Manager, this.Summary));
      }
    #undef  TOSTRING
    
    };
    
    struct PAMM
    {
      double Performance; // Доходность за расчетный период
      double Gift;        // Вознагражение управляющего.
      int N;              // Сколько расчетных периодов.
    
      BASE Base1; // Если управляющий снимает деньги в конце каждого расчетного периода.
      BASE Base2; // Если ничего не делалось в расчетные периоды.
      BASE Base3; // Если управляющий снимает деньги в конце каждого расчетного периода и реинвестирует их.
    
      void Set( const double dPerformance, const double dGift, const int iN )
      {
        this.Performance = dPerformance;
        this.Gift = dGift;
        this.N = iN;
    
        this.Base1.Set1(1 + this.Performance / 100, this.Gift / 100, this.N);
        this.Base2.Set2(1 + this.Performance / 100, this.Gift / 100, this.N);
        this.Base3.Set3(1 + this.Performance / 100, this.Gift / 100, this.N);
      }
    
      void operator *=( const double Deposit = 1 )
      {
        this.Base1 *= Deposit;
        this.Base2 *= Deposit;
        this.Base3 *= Deposit;
    
        return;
      }
    
      string GetDescription( void ) const
      {
        return("Доходность за расчетный период " + ::DoubleToString(this.Performance, 1) + "%\n" +
               "Вознагражение управляющего " + ::DoubleToString(this.Gift, 1) + "%\n" +
               "Количество расчетных периодов. " + (string)this.N);
      }
    
      string ToString( const bool FlagName = true, const bool FlagDescription = true ) const
      {
        return(FlagDescription ? "Данные для инвестиционного счета со следующими характеристиками:\n\n" + this.GetDescription() +
                                 "\n\nКонечный результат для Инвестора, Управляющего и Суммарно:"
                                 "\n\nЕсли управляющий снимает деньги в конце каждого расчетного периода (1).\n" + this.Base1.ToString(FlagName) +
                                 "\n\nЕсли ничего не делалось в расчетные периоды (2).\n" + this.Base2.ToString(FlagName) +
                                 "\n\nЕсли управляющий снимает деньги в конце каждого расчетного периода и реинвестирует их (3).\n" + this.Base3.ToString(FlagName)
                               : this.Base1.ToString(FlagName) + this.Base2.ToString(FlagName) + this.Base3.ToString(FlagName));
      }
    };
    
    void OnStart()
    {
      PAMM Pamm;
    
      Pamm.Set(inPerformance, inGift, inN);
    
      Print(Pamm.ToString());
    
      string Str = Pamm.GetDescription() + "\n   ";
    
      for (int i = 1; i <= 3; i++ )
        Str += ::StringFormat("||%-12s||%-12s||%-12s||", "Investor" + (string)i, "Manager" + (string)i, "Summary" + (string)i);
    
      for (int i = 1; i <= inN; i++ )
      {
        Pamm.Set(inPerformance, inGift, i);
    
        Str += StringFormat("\n%-2d:", i) + Pamm.ToString(false, false);
      }
    
      Print(Str);
    }

    Technisch gesehen ist es wahrscheinlich ein OOP. Aber der primitivste Teil davon. Ich könnte es aber nicht ohne sie machen. Möglicherweise, dumme Gehirnumstellung und Nieten das.