ЧИСТВНДОХ на М – а почему бы и нет?
#АнатомияФункций – CustomFunctions
Всем привет!
В чат закинули задачку – можно ли посчитать экселевскую ЧИСТВНДОХ через PQ?
Ну было бы нельзя, не было бы поста )))
На вход нам подают список дат и список потоков, вычислить нужно внутреннюю ставку доходности, что в переводе на русский означает численно решить уравнение.
Получился такой код:
Где
sum – функция, вычисляющая сумму, которую мы должны обнулить
min – стартовая дата
zip, lst – получаем список для sum – по каждой дате получаем период в годах и сам поток
base – знак исходной суммы (нам это знание нужно, чтобы ловить переход через ноль)
gen – ну и запускаем генератор – обычный метод половинного деления (краевые значения и точность задал вручную, кому интересно может сделать параметрами или вообще вычислить)
to – результат последней итерации и есть искомое.
Как-то так – тут мало М, в основном численные методы.
Кому код покажется громоздким – можно переписать на рекурсию:
Так немножко лаконичнее, суть та же, как в общем и скорость.
Ну и пример использования:
Вроде работает и даже не вешает комп, пользуйтесь!
Надеюсь, было полезно.
Всех благ!
@buchlotnik
#АнатомияФункций – CustomFunctions
Всем привет!
В чат закинули задачку – можно ли посчитать экселевскую ЧИСТВНДОХ через PQ?
Ну было бы нельзя, не было бы поста )))
На вход нам подают список дат и список потоков, вычислить нужно внутреннюю ставку доходности, что в переводе на русский означает численно решить уравнение.
Получился такой код:
ЧИСТВНДОХ =(даты as list,потоки as list) as number =>
[ sum=(x)=>List.Sum(List.Transform(lst,(y)=>y{1}/Number.Power(1+x,y{0}))),
min = List.Min(даты),
zip= List.Zip({даты,потоки}),
lst = List.Buffer(List.Transform(zip,(x)=>{Duration.TotalDays(x{0}-min)/365,x{1}})),
base = Number.Sign(sum(0)),
gen=List.Generate( ()=>[i=0,a=0,b=100,p=(a+b)/2,s=sum(p),t=Number.Sign(s), f=t=base],
(x)=>x[i]<100 and x[b]-x[a]>0.0000001,
(x)=>[ i=x[i]+1,
a=if x[f] then x[p] else x[a],
b=if x[f] then x[b] else x[p],
p=(a+b)/2,
s=sum(p),
t=Number.Sign(s),
f=t=base],
(x)=>x[p]),
to = List.Last(gen)][to]
Где
sum – функция, вычисляющая сумму, которую мы должны обнулить
min – стартовая дата
zip, lst – получаем список для sum – по каждой дате получаем период в годах и сам поток
base – знак исходной суммы (нам это знание нужно, чтобы ловить переход через ноль)
gen – ну и запускаем генератор – обычный метод половинного деления (краевые значения и точность задал вручную, кому интересно может сделать параметрами или вообще вычислить)
to – результат последней итерации и есть искомое.
Как-то так – тут мало М, в основном численные методы.
Кому код покажется громоздким – можно переписать на рекурсию:
ЧИСТВНДОХ =(даты as list,потоки as list) as number =>
[ sum=(x)=>List.Sum(List.Transform(lst,(y)=>y{1}/Number.Power(1+x,y{0}))),
min = List.Min(даты),
zip= List.Zip({даты,потоки}),
lst = List.Buffer(List.Transform(zip,(x)=>{Duration.TotalDays(x{0}-min)/365,x{1}})),
base = Number.Sign(sum(0)),
func=(i,a,b)=> [ p=(a+b)/2, s=sum(p), t=Number.Sign(s), f=t=base,
out =if i<100 and (b-a)>0.0000001
then if f
then @func(i+1,p,b)
else @func(i+1,a,p) else p ][out],
to = func(0,0,100)][to]
Так немножко лаконичнее, суть та же, как в общем и скорость.
Ну и пример использования:
let
from = #table({"Даты","Потоки"},{{#date(2019,1,1),-1090},{#date(2019,12,31),123},{#date(2020,12,31),123},{#date(2021,12,31),123},{#date(2022,12,31),123},{#date(2023,12,31),1353}}),
to = ЧИСТВНДОХ(from[Даты],from[Потоки])
in
to //0.13257
Вроде работает и даже не вешает комп, пользуйтесь!
Надеюсь, было полезно.
Всех благ!
@buchlotnik