"Apotheosis" I consider one expression by fxsaber, about which he himself could not say how it works, stating simply that "the code has been repeatedly tested, and it works". That, in my opinion, should not be the case:
This code checks ifan otfFilingTypeorder can beexecuted, and returns it if it is available on strSymbol, otherwise it is correct.
I have absolutely no idea how it works. And only rely on fxsaber's authority.
Maybe someone can explain ?
To understand it, just take apart this cumbersome return expression into its components.
by the way this code is just like spaghetti, but considering it's probably the most useful author in kodobase right now and I myself use his codes as is, let someone else critique
by the way this code is just like spaghetti, but given that it's probably the most useful author in kodobase right now and I myself use his codes as is, let someone else critique
it's not about criticism, i'm trying to figure out what it can do.... But as shown by the interview, and@fxsaber himself admitted - nothing but headaches
SZZ: it is highly probable, that initial variant of the small monster looked more clearly ;)
But after that more and more functionality was needed, and the code was slowly getting fleshed out. It was different with the Filling function. Nothing was fleshed out there, it was written at once.
Only the compiler knows exactly what it will do. Modern compilers have staggering euristics. They adapt to the average coder and already know better what he or she needs. The best thing a compiler can do is write simple, straightforward code with short functions. It is easier and more efficient for the compiler to analyse the source code graph consisting of many function nodes to build the resulting program. It will only have a positive effect on productivity since the needed functions will be locked into all the right places.
You're absolutely right.
If we're talking about MQL5, you can forget about the "optimization" methods of 10-20-30 years ago. You need to write the most readable code. It is it, and not the hacker stuff and pure smartassery.
Why?
Because the compiler will go through 5-10 cycles of code reordering, it's amazingly clear and concise, not to mention the use of dozens of optimization patterns.
The MQL5 compiler is amused by human attempts to make +2% speed.
If you're interested, check out how math calculations go without branching and one command (128 bits of data) calculates two roots at once
This code turns into the following assembler SSE code:
On the contrary, it's much easier with two "ifs" than with the "or" operator.
It's easier to trace one condition first, and leave the function if it's true, and then check the other condition, and also leave if it's true, than to try to figure out the result of a complex condition with logical "or" (which can be confused with "and"), and trace both ways of return.
It's pretty funny to read below that "the justification for such is debugging", because it means that such code is much more understandable (otherwise why is it in debugging ?).
"Apotheosis" I consider one expression by fxsaber, about which he himself could not say how it works, stating simply that "the code has been repeatedly tested, and it works". That, in my opinion, should not be the case:
This code checks ifan otfFilingTypeorder can beexecuted, and returns it if it is available on strSymbol, otherwise it is correct.
I have absolutely no idea how it works. And only rely on fxsaber's authority.
Maybesomeone can explain ?
I once sat down and took it apart step by step, seems to have needed a pen and paper)
Why this parsing was useful - I understood that in case of change of enum structure everything will be broken) since integer relations of enum values are used (which according to the idea of enumerations itself should be encapsulated). I try to avoid this myself, at most the more-less ratio. For those who deal with WinAPI, it is probably familiar.
If we're talking about MQL5, you can forget about "optimization" methods of 10-20-30 years ago. You need to write the most readable code. It is it, and not the hacker stuff and pure smartassery.
Exactly. I came to this conclusion long ago. But not because I think the compiler will make it better. But because the main source of problems in code is a human being himself, which means that one should write code in a way that makes it as simple and transparent as possible.
If you cannot make it "transparent", you have to write detailed comments why it is this way and not that way.
And the compiler... Even if it doesn't do it efficiently enough, it's less of a problem than a potential bug due to the fact that you don't take into account some aspects of the program.
The code is very simple and short(description). If you write it on FP, it would be interesting to compare.
Please. C# in FP style:
using System;
using System.Linq;
using System.Collections.Generic;
using static FeesCalculator.FeesCalcs;
using System.Text;
using static System.Math;
namespace FeesCalculator
{
publicstaticclass 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>publicstaticvoid Map<T>(this IEnumerable<T> seq, Action<T> action)
{
foreach (var item in seq)
action(item);
}
}
/// <summary>/// Инвестиционный результат/// </summary>public record AccountRecord
{
publicdouble Investor { get; init; }
publicdouble Manager { get; init; }
publicdouble Summary { get; init; }
publicstatic AccountRecord operator *(AccountRecord r, double v)
{
returnnew AccountRecord
{
Investor = r.Investor * v,
Manager = r.Manager * v,
Summary = r.Summary * v
};
}
publicoverridestring 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>publicdouble Perfomance { get; init; }
/// <summary>/// Вознаграждение управляющего/// </summary>publicdouble Gift { get; init; }
/// <summary>/// Количество расчетных периодов/// </summary>publicint N { get; init; }
/// <inheritdoc/>publicoverridestring 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>publicclass FeesCalcs
{
/// <summary>/// Если управляющий снимает деньги в конце каждого расчетного периода/// </summary>/// <param name="Performance"></param>/// <param name="Gift"></param>/// <param name="N"></param>/// <returns></returns>publicstatic 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>publicstatic 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>publicstatic 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);
returnnew AccountRecord
{
Summary = summary(),
Investor = investor(),
Manager = summary() - investor()
};
}
/// <summary>/// Сопостовление: описание - функция расчета/// </summary>publicstatic readonly Dictionary<string, Func<PammSet, AccountRecord>> DescrToAccountRecord = new Dictionary<string, Func<PammSet, AccountRecord>>
{
{"Если управляющий снимает деньги в конце каждого расчетного периода (1)", Set1 },
{"Если ничего не делалось в расчетные периоды (2)", Set2 },
{"Если управляющий снимает деньги в конце каждого расчетного периода и реинвестирует их (3)", Set3 },
};
}
class Program
{
staticvoid 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 is quite crooked of course (as it was written by a crooked FP coder, in the incomplete FP language C#).But the goal is to show that on modern OOP jargon you can do it in FP too. Of course, it is limited, it is not F# or Haskell, but nobody forbids to write in FP style. Use immutability, higher-order functions, closures, mappings, etc. - you're welcome to do it all. But this doesn't make the code perfect for some reason.
s.w. The overall code structure deliberately mimics the original. Although in a good way it should be quite different in FP, without complex objects and foreach mimicking Map, but the artist painted as he could.
I understand that it's a matter of habit and syntax knowledge, but I'm having a very hard time getting into the code even though I'm the original author.
#property strict#property script_show_inputsinputdouble inPerformance = 100; // Сколько процентов дает система за периодinputdouble inGift = 50; // Награда управляющего в процентах (< 100)inputint inN = 12; // Сколько расчетных периодовstruct BASE
{
double Investor;
double Manager;
double Summary;
// Если управляющий снимает деньги в конце каждого расчетного периода.void Set1( constdouble Performance, constdouble Gift, constint 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( constdouble Performance, constdouble Gift, constint N )
{
this.Summary = ::MathPow(Performance, N);
this.Manager = (this.Summary - 1) * Gift;
this.Investor = this.Summary - this.Manager;
return;
}
// Если управляющий снимает деньги в конце каждого расчетного периода и реинвестирует их.void Set3( constdouble Performance, constdouble Gift, constint N )
{
this.Summary = ::MathPow(Performance, N);
this.Investor = ::MathPow(1 + (Performance - 1)* (1 - Gift), N);
this.Manager = this.Summary - this.Investor;
return;
}
voidoperator *=( constdouble Deposit = 1 )
{
this.Investor *= Deposit;
this.Manager *= Deposit;
this.Summary *= Deposit;
return;
}
#define TOSTRING(A) #A + " = " + ::DoubleToString(A, 4) + " "string ToString( constbool 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( constdouble dPerformance, constdouble dGift, constint 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);
}
voidoperator *=( constdouble 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( constbool FlagName = true, constbool 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));
}
};
voidOnStart()
{
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);
}
Technically, it's probably an OOP. But the most primitive part of it. Couldn't do it without it, though. Possibly, dumb brain rearrangement and riveting this.
"Apotheosis" I consider one expression by fxsaber, about which he himself could not say how it works, stating simply that "the code has been repeatedly tested, and it works". That, in my opinion, should not be the case:
This code checks ifan otfFilingTypeorder can beexecuted, and returns it if it is available on strSymbol, otherwise it is correct.
I have absolutely no idea how it works. And only rely on fxsaber's authority.
Maybe someone can explain ?
To understand it, just take apart this cumbersome return expression into its components.
Your example from "the end justifies the means"
by the way this code is just like spaghetti, but considering it's probably the most useful author in kodobase right now and I myself use his codes as is, let someone else critique
by the way this code is just like spaghetti, but given that it's probably the most useful author in kodobase right now and I myself use his codes as is, let someone else critique
it's not about criticism, i'm trying to figure out what it can do.... But as shown by the interview, and@fxsaber himself admitted - nothing but headaches
SZZ: it is highly probable, that initial variant of the small monster looked more clearly ;)
ZS: high probability that the original version of the little monster looked more obvious ;)
Took it from ZIP (contains the very first release).
But after that more and more functionality was needed, and the code was slowly getting fleshed out. It was different with the Filling function. Nothing was fleshed out there, it was written at once.
Only the compiler knows exactly what it will do. Modern compilers have staggering euristics. They adapt to the average coder and already know better what he or she needs. The best thing a compiler can do is write simple, straightforward code with short functions. It is easier and more efficient for the compiler to analyse the source code graph consisting of many function nodes to build the resulting program. It will only have a positive effect on productivity since the needed functions will be locked into all the right places.
You're absolutely right.
If we're talking about MQL5, you can forget about the "optimization" methods of 10-20-30 years ago. You need to write the most readable code. It is it, and not the hacker stuff and pure smartassery.
Why?
Because the compiler will go through 5-10 cycles of code reordering, it's amazingly clear and concise, not to mention the use of dozens of optimization patterns.
The MQL5 compiler is amused by human attempts to make +2% speed.
This is a work of art actually. 8 roots were calculated in 4 calls of the assembler command. Two double numbers were calculated in one call.If you're interested, check out how math calculations go without branching and one command (128 bits of data) calculates two roots at once
This code turns into the following assembler SSE code:
The general conclusion: mathematics has won in MQL5 due to perfect optimization. It's not arrays that lose here, but mathematics wins.
Why is that?
On the contrary, it's much easier with two "ifs" than with the "or" operator.
It's easier to trace one condition first, and leave the function if it's true, and then check the other condition, and also leave if it's true, than to try to figure out the result of a complex condition with logical "or" (which can be confused with "and"), and trace both ways of return.
It's pretty funny to read below that "the justification for such is debugging", because it means that such code is much more understandable (otherwise why is it in debugging ?).
"Apotheosis" I consider one expression by fxsaber, about which he himself could not say how it works, stating simply that "the code has been repeatedly tested, and it works". That, in my opinion, should not be the case:
This code checks ifan otfFilingTypeorder can beexecuted, and returns it if it is available on strSymbol, otherwise it is correct.
I have absolutely no idea how it works. And only rely on fxsaber's authority.
Maybesomeone can explain ?
I once sat down and took it apart step by step, seems to have needed a pen and paper)
Why this parsing was useful - I understood that in case of change of enum structure everything will be broken) since integer relations of enum values are used (which according to the idea of enumerations itself should be encapsulated). I try to avoid this myself, at most the more-less ratio. For those who deal with WinAPI, it is probably familiar.
To understand, all you have to do is take that unwieldy retournee expression apart.
Yes, that's what I tried to do. But, there wasn't enough motivation to take it apart...
Absolutely right.
If we're talking about MQL5, you can forget about "optimization" methods of 10-20-30 years ago. You need to write the most readable code. It is it, and not the hacker stuff and pure smartassery.
Exactly. I came to this conclusion long ago. But not because I think the compiler will make it better. But because the main source of problems in code is a human being himself, which means that one should write code in a way that makes it as simple and transparent as possible.
If you cannot make it "transparent", you have to write detailed comments why it is this way and not that way.
And the compiler... Even if it doesn't do it efficiently enough, it's less of a problem than a potential bug due to the fact that you don't take into account some aspects of the program.
The code is very simple and short(description). If you write it on FP, it would be interesting to compare.
Please. C# in FP style:
FP is quite crooked of course (as it was written by a crooked FP coder, in the incomplete FP language C#).But the goal is to show that on modern OOP jargon you can do it in FP too. Of course, it is limited, it is not F# or Haskell, but nobody forbids to write in FP style. Use immutability, higher-order functions, closures, mappings, etc. - you're welcome to do it all. But this doesn't make the code perfect for some reason.
s.w. The overall code structure deliberately mimics the original. Although in a good way it should be quite different in FP, without complex objects and foreach mimicking Map, but the artist painted as he could.
Please. C# in FP style:
I understand that it's a matter of habit and syntax knowledge, but I'm having a very hard time getting into the code even though I'm the original author.
Forum on trading, automated trading systems and strategy testing
Interesting opinion about OOP
fxsaber, 2021.01.29 13:39
A trifle.
Technically, it's probably an OOP. But the most primitive part of it. Couldn't do it without it, though. Possibly, dumb brain rearrangement and riveting this.