226 lines
5.6 KiB
ReasonML
226 lines
5.6 KiB
ReasonML
|
module Data = {
|
||
|
type fund =
|
||
|
| ANIMAL_WELFARE
|
||
|
| GLOBAL_HEALTH
|
||
|
| LONG_TERM_FUTURE
|
||
|
| META;
|
||
|
|
||
|
type group =
|
||
|
| Fund(fund)
|
||
|
| All;
|
||
|
|
||
|
type output =
|
||
|
| DONATIONS
|
||
|
| PAYOUTS;
|
||
|
|
||
|
type fundWithInfo = {
|
||
|
group,
|
||
|
name: string,
|
||
|
existingDonations: option(float),
|
||
|
existingPayouts: option(float),
|
||
|
};
|
||
|
|
||
|
let makeFundWithInfo = (name, group, existingDonations, existingPayouts) => {
|
||
|
group,
|
||
|
name,
|
||
|
existingDonations,
|
||
|
existingPayouts,
|
||
|
};
|
||
|
|
||
|
let funds = [|
|
||
|
makeFundWithInfo(
|
||
|
"Animal Welfare Fund",
|
||
|
Fund(ANIMAL_WELFARE),
|
||
|
Some(4000.0),
|
||
|
Some(10.0),
|
||
|
),
|
||
|
makeFundWithInfo(
|
||
|
"Global Health Fund",
|
||
|
Fund(GLOBAL_HEALTH),
|
||
|
Some(4000.0),
|
||
|
Some(10.0),
|
||
|
),
|
||
|
makeFundWithInfo(
|
||
|
"Long Term Future Fund",
|
||
|
Fund(LONG_TERM_FUTURE),
|
||
|
Some(4000.0),
|
||
|
Some(10.0),
|
||
|
),
|
||
|
makeFundWithInfo(
|
||
|
"Meta Fund",
|
||
|
Fund(ANIMAL_WELFARE),
|
||
|
Some(4000.0),
|
||
|
Some(10.0),
|
||
|
),
|
||
|
makeFundWithInfo("All", All, None, None),
|
||
|
|];
|
||
|
};
|
||
|
|
||
|
module Model = {
|
||
|
open Data;
|
||
|
let currentYear = 2020.;
|
||
|
let firstYearStdDev = 0.2;
|
||
|
type yearlyNumericDiff = {
|
||
|
meanDiff: float,
|
||
|
stdDiff: float,
|
||
|
};
|
||
|
|
||
|
let yearlyMeanGrowthRateIfNotClosed = (group: group): yearlyNumericDiff => {
|
||
|
{meanDiff: 1.1, stdDiff: 1.1};
|
||
|
};
|
||
|
|
||
|
let calculateDifference =
|
||
|
(currentValue, dateTime, currentDateTime, y: yearlyNumericDiff) => {
|
||
|
let yearDiff = MomentRe.diff(dateTime, currentDateTime, `days) /. 365.;
|
||
|
let meanDiff = Js.Math.pow_float(~base=y.meanDiff, ~exp=yearDiff);
|
||
|
let stdDevDiff = Js.Math.pow_float(~base=y.meanDiff, ~exp=yearDiff);
|
||
|
FloatCdf.normal(currentValue *. meanDiff, firstYearStdDev *. stdDevDiff);
|
||
|
};
|
||
|
|
||
|
let rec currentValue = (group: group, output) => {
|
||
|
let sum = (): float =>
|
||
|
currentValue(Fund(ANIMAL_WELFARE), output)
|
||
|
+. currentValue(Fund(GLOBAL_HEALTH), output)
|
||
|
+. currentValue(Fund(LONG_TERM_FUTURE), output)
|
||
|
+. currentValue(Fund(META), output);
|
||
|
switch (group, output) {
|
||
|
| (Fund(ANIMAL_WELFARE), DONATIONS) => 300000.0
|
||
|
| (Fund(ANIMAL_WELFARE), PAYOUTS) => 2300000.0
|
||
|
| (Fund(GLOBAL_HEALTH), DONATIONS) => 1000000.0
|
||
|
| (Fund(GLOBAL_HEALTH), PAYOUTS) => 500000.0
|
||
|
| (Fund(LONG_TERM_FUTURE), DONATIONS) => 600000.0
|
||
|
| (Fund(LONG_TERM_FUTURE), PAYOUTS) => 120000.0
|
||
|
| (Fund(META), DONATIONS) => 9300000.0
|
||
|
| (Fund(META), PAYOUTS) => 830000.0
|
||
|
| (All, _) => sum()
|
||
|
};
|
||
|
};
|
||
|
let make =
|
||
|
(
|
||
|
group: group,
|
||
|
dateTime: MomentRe.Moment.t,
|
||
|
currentDateTime: MomentRe.Moment.t,
|
||
|
output: output,
|
||
|
) => {
|
||
|
Prop.Value.FloatCdf(
|
||
|
calculateDifference(
|
||
|
currentValue(group, output),
|
||
|
dateTime,
|
||
|
currentDateTime,
|
||
|
yearlyMeanGrowthRateIfNotClosed(group),
|
||
|
),
|
||
|
);
|
||
|
};
|
||
|
};
|
||
|
|
||
|
module Interface = {
|
||
|
open Data;
|
||
|
let convertChoice = (s: string) =>
|
||
|
switch (s) {
|
||
|
| "animal" => Fund(ANIMAL_WELFARE)
|
||
|
| "globalHealth" => Fund(GLOBAL_HEALTH)
|
||
|
| "longTerm" => Fund(LONG_TERM_FUTURE)
|
||
|
| "meta" => Fund(META)
|
||
|
| _ => All
|
||
|
};
|
||
|
|
||
|
let convertOutput = (s: string) =>
|
||
|
switch (s) {
|
||
|
| "donations" => DONATIONS
|
||
|
| _ => PAYOUTS
|
||
|
};
|
||
|
|
||
|
let run = (p: Prop.Combo.t) => {
|
||
|
let get = Prop.ValueMap.get(p.inputValues);
|
||
|
switch (
|
||
|
get("Fund"),
|
||
|
get("Day"),
|
||
|
get(Prop.TypeWithMetadata.currentYear.id),
|
||
|
get("Output"),
|
||
|
) {
|
||
|
| (
|
||
|
Some(SelectSingle(fund)),
|
||
|
Some(DateTime(intendedYear)),
|
||
|
Some(DateTime(currentYear)),
|
||
|
Some(SelectSingle(output)),
|
||
|
) =>
|
||
|
Some(
|
||
|
Model.make(
|
||
|
convertChoice(fund),
|
||
|
intendedYear,
|
||
|
currentYear,
|
||
|
convertOutput(output),
|
||
|
),
|
||
|
)
|
||
|
| _ => None
|
||
|
};
|
||
|
};
|
||
|
|
||
|
let model: Prop.Model.t =
|
||
|
Prop.{
|
||
|
name: "EA Funds: Donations & Payouts",
|
||
|
description: "Calculate the payments and payouts of EA Funds based on existing data.",
|
||
|
version: "1.0.0",
|
||
|
author: "Ozzie Gooen",
|
||
|
inputTypes: [|
|
||
|
TypeWithMetadata.make(
|
||
|
~name="Fund",
|
||
|
~type_=
|
||
|
SelectSingle({
|
||
|
default: Some("total"),
|
||
|
options: [
|
||
|
{name: "Animal Welfare Fund", id: "animal"},
|
||
|
{name: "Global Health Fund", id: "globalHealth"},
|
||
|
{name: "Long Term Future Fund", id: "longTerm"},
|
||
|
{name: "Meta Fund", id: "longterm"},
|
||
|
{name: "All", id: "all"},
|
||
|
],
|
||
|
}),
|
||
|
(),
|
||
|
),
|
||
|
TypeWithMetadata.make(
|
||
|
~name="Day",
|
||
|
~type_=
|
||
|
DateTime({
|
||
|
default:
|
||
|
Some(
|
||
|
MomentRe.Moment.add(
|
||
|
~duration=MomentRe.duration(5., `years),
|
||
|
MomentRe.momentNow(),
|
||
|
),
|
||
|
),
|
||
|
min:
|
||
|
Some(
|
||
|
MomentRe.Moment.subtract(
|
||
|
~duration=MomentRe.duration(20., `years),
|
||
|
MomentRe.momentNow(),
|
||
|
),
|
||
|
),
|
||
|
max:
|
||
|
Some(
|
||
|
MomentRe.Moment.add(
|
||
|
~duration=MomentRe.duration(20., `years),
|
||
|
MomentRe.momentNow(),
|
||
|
),
|
||
|
),
|
||
|
}),
|
||
|
(),
|
||
|
),
|
||
|
TypeWithMetadata.currentYear,
|
||
|
TypeWithMetadata.make(
|
||
|
~name="Output",
|
||
|
~type_=
|
||
|
SelectSingle({
|
||
|
default: Some("Output"),
|
||
|
options: [
|
||
|
{name: "Donations", id: "donations"},
|
||
|
{name: "Funding", id: "funding"},
|
||
|
],
|
||
|
}),
|
||
|
(),
|
||
|
),
|
||
|
|],
|
||
|
outputTypes: [||],
|
||
|
run,
|
||
|
};
|
||
|
};
|