Interessante assumir a OLP - página 11

 
Georgiy Merts:

"Apoteose" considero uma expressão do fxsaber, sobre a qual ele mesmo não poderia dizer como funciona, afirmando simplesmente que "o código foi testado repetidamente, e funciona". Isso, em minha opinião, não deveria ser o caso:

Este código verifica seo pedido do tipo FilingType pode serexecutado e o devolve se estiver disponível no strSymbol, caso contrário está correto.


Não tenho a menor idéia de como funciona. E contar apenas com a autoridade do fxsaber.

Talvez alguém possa explicar ?

Para compreendê-la, basta desmontar esta expressão de retorno incômoda em seus componentes.

 
Igor Makanu:

Seu exemplo de "os fins justificam os meios".

a propósito, este código é como o spaghetti, mas considerando que este é provavelmente o autor mais útil em kodobase neste momento e eu mesmo utilizo seus códigos como está, deixe alguém criticar

 
Andrei Trukhanovich:

a propósito, este código é como o spaghetti, mas dado que é provavelmente o autor mais útil em kodobase neste momento e eu mesmo utilizo seus códigos como está, deixe outra pessoa criticar

não se trata de crítica, eu estou tentando descobrir o que pode fazer.... Mas como mostra a entrevista, e o próprio@fxsaber admitiu - nada além de dores de cabeça


SZZ: é altamente provável, aquela variante inicial do pequeno monstro parecia mais clara ;)

 
Igor Makanu:

ZS: alta probabilidade de que a versão original do pequeno monstro parecesse mais óbvia ;)

Foi tirado do ZIP (contém o primeiro lançamento).

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

Mas depois disso, foi sendo necessário cada vez mais funcionalidade, e o código foi lentamente sendo enxaguado para fora. Foi diferente com a função Preenchimento. Nada foi lavado lá fora, foi escrito de uma só vez.

 
Vasiliy Sokolov:

Somente o compilador sabe exatamente o que vai fazer. Os compiladores modernos têm uma eurística espantosa. Eles se adaptam ao codificador médio e já sabem melhor o que ele ou ela precisa. A melhor coisa que um compilador pode fazer é escrever um código simples, direto e com funções curtas. É mais fácil e mais eficiente para o compilador analisar o gráfico do código fonte que consiste de muitos nós de função para construir o programa resultante. Ele só terá um efeito positivo na produtividade, uma vez que as funções necessárias serão bloqueadas em todos os lugares certos.

Você está absolutamente certo.

Se estamos falando da MQL5, você pode esquecer os métodos de "otimização" de 10-20-30 anos atrás. Você precisa escrever o código mais legível. É isso, e não as coisas de hacker e de puro espertalhão.

Por quê?

Como o compilador passará por 5-10 ciclos de reordenamento de código, ele é incrivelmente claro e conciso, sem mencionar o uso de dezenas de padrões de otimização.

O compilador MQL5 é divertido com as tentativas humanas de fazer +2% de velocidade.


Se você estiver interessado, verifique como cálculos matemáticos vão sem ramificação e um comando (128 bits de dados) calcula duas raízes ao mesmo tempo

Este código transforma-se no seguinte código SSE do assembler:

         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
Esta é uma obra de arte, de fato. 8 raízes foram calculadas em 4 chamadas do comando assembler. Dois números duplos foram calculados em uma chamada.

  • Quando se trabalha com uma matriz, tudo é normal, com verificações, ramificações e perdas na conversão de índice duplo -> inteiro

  • Quando se trabalha com matrizes neste exemplo, há uma mistura constante FPU/ALU que é muito ruim para o desempenho

  • A otimização do acesso à matriz dinâmica é grande - além dos elogios. Mas a mistura de operações de FPU/ALU + duplo -> inteiro + ramificação desperdiça tempo

  • A conclusão geral: a matemática ganhou na MQL5 devido à otimização perfeita. Não são as arrays que perdem aqui, mas a matemática ganha.


     
    Georgiy Merts:

    Por que isso acontece?

    Pelo contrário, é muito mais fácil com dois "se" do que com o "ou" operador.

    É mais fácil rastrear uma condição primeiro, e deixar a função se for verdadeira, e então verificar a outra condição, e também deixar se for verdadeira, do que adivinhar o resultado de uma condição complexa com "ou" lógico (que pode ser facilmente confundido com "e"), e rastrear ambas as opções de retorno.

    É muito engraçado ler abaixo que "a justificação para tal é a depuração", porque significa que tal código é muito mais compreensível (caso contrário, por que está na depuração?).

    "Apoteose" considero uma expressão do fxsaber, sobre a qual ele mesmo não poderia dizer como funciona, afirmando simplesmente que "o código tem sido testado repetidamente, e funciona". Isso, em minha opinião, não deveria ser o caso:

    Este código verifica seum pedido do tipo FilingType pode serexecutado, e o devolve se estiver disponível no strSymbol, caso contrário está correto.


    Não tenho a menor idéia de como funciona. E contar apenas com a autoridade do fxsaber.

    Talvezalguém possa explicar ?

    Uma vez sentei-me e desmontei-o passo a passo, parece ter precisado de uma caneta e papel)

    Por que esta análise foi útil - entendi que em caso de mudança de estrutura de enumeração tudo será quebrado) já que são usadas relações inteiras de valores de enumeração (que de acordo com a própria idéia de enumeração deve ser encapsulada). Eu mesmo tento evitar isso, no máximo a proporção mais inútil. Para aqueles que lidam com o WinAPI, provavelmente é familiar.

     
    Andrey Khatimlianskii:

    Para entender, tudo o que você tem que fazer é desmontar essa expressão de retournee desajeitado.

    Sim, foi isso que eu tentei. Mas, não havia motivação suficiente para desmontá-lo...

     
    Renat Fatkhullin:

    Absolutamente certo.

    Se estamos falando da MQL5, você pode esquecer os métodos de "otimização" de 10-20-30 anos atrás. Você precisa escrever o código mais legível. É isso, e não as coisas de hacker e de puro espertalhão.

    Exatamente. Cheguei a esta conclusão há muito tempo. Mas não porque eu acho que o compilador vai melhorar as coisas. Mas porque a principal fonte de problemas em código é o próprio ser humano, o que significa que se deve escrever código de uma maneira que o torne o mais simples e transparente possível.

    Se você não pode torná-lo "transparente", você tem que escrever comentários detalhados porque é desta maneira e não daquela maneira.

    E o compilador... Mesmo que não o faça com eficiência suficiente, é menos um problema do que um bug em potencial devido ao fato de não levar em conta alguns aspectos do programa.

     
    fxsaber:

    O código é muito simples e curto(descrição). Se você escrevê-lo na FP, seria interessante comparar.

    Por favor. C# em 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();
                    }
                );
            }
        }
    }
    

    O FP é bastante torto, é claro (como foi escrito por um codificador FP torto, na linguagem C# incompleta do FP), mas o objetivo é mostrar que no jargão moderno do OOP você pode fazer isso também no FP. Claro que é limitado, não é F# ou Haskell, mas ninguém proíbe de escrever em estilo FP. Usar imutabilidade, funções de ordem superior, fechamentos, mapeamentos, etc. - você é bem-vindo a fazer tudo isso. Mas isto não torna o código perfeito por alguma razão.

    s.w. A estrutura geral do código imita deliberadamente o original. Embora de uma boa maneira deveria ser bem diferente em FP, sem objetos complexos e sem imitar o Mapa, mas o artista pintou como pôde.

     
    Vasiliy Sokolov:

    Por favor. C# em estilo FP:

    Entendo que é uma questão de hábito e conhecimento de sintaxe, mas estou tendo muita dificuldade para entrar no código, apesar de ser o autor original.

    Fórum sobre comércio, sistemas automatizados de comércio e testes estratégicos

    Opinião interessante sobre o OOP

    fxsaber, 2021.01.29 13:39

    Uma bagatela.

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

    Tecnicamente, é provavelmente um OOP. Mas a parte mais primitiva é a parte mais primitiva. No entanto, não seria possível sem ele. Possivelmente, uma reordenação cerebral burra e rebitadora.