Interesante visión de la OLP - página 11

 
Georgiy Merts:

"Apoteosis" considero una expresión de fxsaber, sobre la que él mismo no pudo decir cómo funciona, afirmando simplemente que "el código ha sido probado repetidamente, y funciona". Eso, en mi opinión, no debería ser así:

Este código comprueba si se puedeejecutar una orden otfFilingType, y la devuelve si está disponible en strSymbol, en caso contrario es correcta.


No tengo ni idea de cómo funciona. Y sólo confiar en la autoridad de fxsaber.

¿Quizás alguien pueda explicarlo?

Para entenderlo, basta con desmontar esta engorrosa expresión de retorno en sus componentes.

 
Igor Makanu:

Tu ejemplo de "el fin justifica los medios"

por cierto este código es como un espagueti, pero teniendo en cuenta que es probablemente el autor más útil en kodobase en este momento y yo mismo uso sus códigos como es, que alguien más critique

 
Andrei Trukhanovich:

por cierto este código es como un espagueti, pero dado que es probablemente el autor más útil en kodobase ahora mismo y yo mismo uso sus códigos tal cual, que alguien más critique

no se trata de criticar, estoy tratando de averiguar lo que puede hacer.... Pero como se muestra en la entrevista, y el propio@fxsaber admitió - nada más que dolores de cabeza


SZZ: es muy probable, que la variante inicial del pequeño monstruo se viera más claramente ;)

 
Igor Makanu:

ZS: alta probabilidad de que la versión original del monstruito pareciera más evidente ;)

Lo tomé del ZIP (contiene la primera versión).

  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__
           );
  }

Pero después se necesitó más y más funcionalidad, y el código se fue desarrollando poco a poco. Con la función de llenado fue diferente. No se ha desarrollado nada allí, se ha escrito de golpe.

 
Vasiliy Sokolov:

Sólo el compilador sabe exactamente lo que hará. Los compiladores modernos tienen una eurística asombrosa. Se adaptan al codificador medio y ya saben mejor lo que necesita. Lo mejor que puede hacer un compilador es escribir un código sencillo y directo con funciones cortas. Es más fácil y eficiente para el compilador analizar el gráfico del código fuente que consta de muchos nodos de función para construir el programa resultante. Sólo tendrá un efecto positivo en la productividad, ya que las funciones necesarias estarán bloqueadas en todos los lugares adecuados.

Tienes toda la razón.

Si hablamos de MQL5, puedes olvidarte de los métodos de "optimización" de hace 10-20-30 años. Tienes que escribir el código más legible. Es eso, y no el rollo de los hackers y las puras linduras.

¿Por qué?

Como el compilador pasará por 5-10 ciclos de reordenación del código, es sorprendentemente claro y conciso, por no mencionar el uso de docenas de patrones de optimización.

El compilador MQL5 se divierte con los intentos humanos de hacer +2% de velocidad.


Si te interesa, comprueba cómo se han reordenado y calculado cálculos matemáticos van sin bifurcarse y un comando (128 bits de datos) calcula dos raíces a la vez

Este código se convierte en el siguiente código SSE en ensamblador:

         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
Esto es una obra de arte en realidad. Se calcularon 8 raíces en 4 llamadas del comando ensamblador. Se han calculado dos números dobles en una sola llamada.

  • Cuando se trabaja a través de un array, todo es normal, con comprobaciones, bifurcaciones y pérdidas en la conversión de índices dobles -> enteros

  • Al trabajar con arrays en este ejemplo hay una mezcla constante de FPU/ALU que es muy mala para el rendimiento

  • La optimización del acceso a las matrices dinámicas es genial, más allá de los elogios. Pero la mezcla de operaciones FPU/ALU + doble -> entero + bifurcación hace perder tiempo

  • La conclusión general: las matemáticas han ganado en MQL5 debido a la perfecta optimización. Aquí no pierden las matrices, sino que ganan las matemáticas.


     
    Georgiy Merts:

    ¿Por qué?

    Por el contrario, es mucho más fácil con dos "if" que con el operador "or".

    Es más fácil trazar una condición primero, y salir de la función si es verdadera, y luego comprobar la otra condición, y también salir si es verdadera, que intentar averiguar el resultado de una condición compleja con "o" lógica (que puede confundirse con "y"), y trazar ambas formas de retorno.

    Es bastante gracioso leer más abajo que "la justificación de tal es la depuración", porque significa que tal código es mucho más comprensible (si no, ¿por qué está en depuración?).

    "Apoteosis" considero una expresión de fxsaber, sobre la que él mismo no pudo decir cómo funciona, afirmando simplemente que "el código ha sido probado repetidamente, y funciona". Eso, en mi opinión, no debería ser así:

    Este código comprueba si se puedeejecutar una orden otfFilingType, y la devuelve si está disponible en strSymbol, en caso contrario es correcta.


    No tengo ni idea de cómo funciona. Y sólo confiar en la autoridad de fxsaber.

    ¿Quizásalguien pueda explicarlo?

    Una vez me senté y lo desmonté paso a paso, parece que necesité un bolígrafo y un papel)

    Por qué este análisis fue útil - entendí que en caso de cambio de la estructura de la enumeración todo se romperá) ya que se utilizan relaciones de enteros de los valores de la enumeración (que de acuerdo con la idea de las enumeraciones en sí debe ser encapsulado). Yo mismo intento evitarlo, como mucho la proporción más-menos. Para los que tratan con WinAPI, probablemente les resulte familiar.

     
    Andrey Khatimlianskii:

    Para entenderlo, sólo hay que desmontar esa expresión tan poco manejable de los retornados.

    Sí, eso es lo que he intentado. Pero no estaba lo suficientemente motivado para desmontarlo...

     
    Renat Fatkhullin:

    Absolutamente correcto.

    Si hablamos de MQL5, puedes olvidarte de los métodos de "optimización" de hace 10-20-30 años. Tienes que escribir el código más legible. Es eso, y no el rollo de los hackers y las puras linduras.

    Exactamente. Hace tiempo que llegué a esta conclusión. Pero no porque crea que el compilador lo hará mejor. Pero porque la principal fuente de problemas en el código es el propio ser humano, lo que significa que hay que escribir el código de forma que sea lo más sencillo y transparente posible.

    Si no puedes hacerlo "transparente", tienes que escribir comentarios detallados de por qué es así y no así.

    Y el compilador... Incluso si no lo hace con la suficiente eficacia, es menos un problema que un posible error debido a que no se tienen en cuenta algunos aspectos del programa.

     
    fxsaber:

    El código es muy simple y corto(descripción). Si lo escribes en FP, sería interesante comparar.

    Por favor. C# en estilo FP:


    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 está bastante torcido, por supuesto (ya que fue escrito por un codificador de FP torcido, en el lenguaje incompleto de FP C#).Pero el objetivo es mostrar que en la jerga moderna de OOP también se puede hacer en FP. Por supuesto, es limitado, no es F# o Haskell, pero nadie prohíbe escribir en estilo FP. Utilice la inmutabilidad, las funciones de orden superior, los cierres, los mapeos, etc. - eres bienvenido a hacerlo todo. Pero esto no hace que el código sea perfecto por alguna razón.

    s.w. La estructura general del código imita deliberadamente el original. Aunque en el buen sentido debe ser bastante diferente en FP, sin objetos complejos y foreach imitando Mapa, pero el artista pintó como pudo.

     
    Vasiliy Sokolov:

    Por favor. C# en estilo FP:

    Entiendo que es una cuestión de costumbre y de conocimiento de la sintaxis, pero me cuesta mucho entrar en el código a pesar de ser el autor original.

    Foro sobre comercio, sistemas de comercio automatizados y pruebas de estrategias

    Opinión interesante sobre OOP

    fxsaber, 2021.01.29 13:39

    Una nimiedad.

    #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);
    }

    Técnicamente, es probable que sea un OOP. Pero la parte más primitiva. Sin embargo, no podría hacerlo sin él. Posiblemente, la reorganización del cerebro tonto y remachar esto.

    Razón de la queja: