File size: 3,836 Bytes
f453a26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/**
 * Econometric Model Executor
 * Runs the built-in TypeScript simulation models (debt Monte Carlo, VAR impulse responses)
 * with validated parameters. No user-supplied code is executed.
 */

import { SandboxResult } from "../contracts/tool_outputs.js";
import { runVarImpulseSimulation, VarInputPayload } from "./templates/var_shock_model.js";
import { MonteCarloDebtInputPayload, simulateDebtTrajectory } from "./templates/monte_carlo_debt.js";

export type EconometricModel = SandboxResult["model"];

export const ECONOMETRIC_MODELS: EconometricModel[] = ["debt_monte_carlo", "var_irf"];

export function executeEconometricModel(
  executionId: string,
  model: "debt_monte_carlo",
  params: MonteCarloDebtInputPayload
): SandboxResult;
export function executeEconometricModel(executionId: string, model: "var_irf", params: VarInputPayload): SandboxResult;
export function executeEconometricModel(
  executionId: string,
  model: EconometricModel,
  params: MonteCarloDebtInputPayload | VarInputPayload
): SandboxResult;
export function executeEconometricModel(
  executionId: string,
  model: EconometricModel,
  params: MonteCarloDebtInputPayload | VarInputPayload
): SandboxResult {
  const startTime = Date.now();

  try {
    if (model === "debt_monte_carlo") {
      const mc = simulateDebtTrajectory(params as MonteCarloDebtInputPayload);
      return {
        execution_id: executionId,
        success: true,
        model,
        computed_metrics: mc.summary_metrics,
        stderr_log: "",
        execution_time_ms: Date.now() - startTime,
        trajectory_curves: mc.trajectory_curves,
      };
    }

    const varResult = runVarImpulseSimulation(params as VarInputPayload);
    return {
      execution_id: executionId,
      success: true,
      model,
      computed_metrics: varResult.summary_metrics,
      stderr_log: "",
      execution_time_ms: Date.now() - startTime,
      impulse_responses: varResult.impulse_projections,
    };
  } catch (err: unknown) {
    const errorMsg = err instanceof Error ? err.message : String(err);
    return {
      execution_id: executionId,
      success: false,
      model,
      computed_metrics: {},
      stderr_log: `Model execution error: ${errorMsg}`,
      execution_time_ms: Date.now() - startTime,
    };
  }
}

export function executeEconometricScript(
  executionId: string,
  _pythonCode: string,
  datasetJson: string
): SandboxResult {
  let parsed: any = {};
  try {
    parsed = typeof datasetJson === "string" ? JSON.parse(datasetJson) : datasetJson || {};
  } catch (_e) {
    parsed = {};
  }

  // gdp_growth_rate is a percent (World Bank units); real_growth_mean is already a fraction.
  const pct = Number(parsed.gdp_growth_rate);
  const frac = Number(parsed.real_growth_mean);
  const growthFraction = Number.isFinite(pct) && parsed.gdp_growth_rate !== null && parsed.gdp_growth_rate !== ""
    ? pct / 100
    : Number.isFinite(frac) && parsed.real_growth_mean !== null && parsed.real_growth_mean !== ""
      ? frac
      : 0.042;

  return executeEconometricModel(
    executionId,
    "debt_monte_carlo",
    {
      initial_debt_to_gdp: numberOr(parsed.initial_debt_to_gdp, 28.1),
      primary_balance_pct_gdp: numberOr(parsed.primary_balance_pct_gdp, -2.3),
      real_growth_mean: growthFraction,
      real_growth_std: 0.015,
      effective_interest_rate_mean: 0.038,
      effective_interest_rate_std: 0.005,
      fiscal_multiplier: numberOr(parsed.fiscal_multiplier, 1.35),
      capex_shock_pct_gdp: numberOr(parsed.capex_shock_pct_gdp, 1.5),
      horizon_years: numberOr(parsed.horizon_years, 15)
    }
  );
}


function numberOr(value: unknown, fallback: number): number {
  if (value === null || value === undefined || value === "") return fallback;
  const n = Number(value);
  return Number.isFinite(n) ? n : fallback;
}