equilibria-mcp-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +142 -0
- package/LICENSE +21 -0
- package/README.md +240 -0
- package/dist/builders/YAMLBuilder.d.ts +77 -0
- package/dist/builders/YAMLBuilder.d.ts.map +1 -0
- package/dist/builders/YAMLBuilder.js +251 -0
- package/dist/builders/YAMLBuilder.js.map +1 -0
- package/dist/economics/constants.d.ts +39 -0
- package/dist/economics/constants.d.ts.map +1 -0
- package/dist/economics/constants.js +46 -0
- package/dist/economics/constants.js.map +1 -0
- package/dist/economics/formulas.d.ts +19 -0
- package/dist/economics/formulas.d.ts.map +1 -0
- package/dist/economics/formulas.js +260 -0
- package/dist/economics/formulas.js.map +1 -0
- package/dist/economics/index.d.ts +9 -0
- package/dist/economics/index.d.ts.map +1 -0
- package/dist/economics/index.js +9 -0
- package/dist/economics/index.js.map +1 -0
- package/dist/economics/validators.d.ts +18 -0
- package/dist/economics/validators.d.ts.map +1 -0
- package/dist/economics/validators.js +111 -0
- package/dist/economics/validators.js.map +1 -0
- package/dist/errors/index.d.ts +172 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +313 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/formatters/index.d.ts +8 -0
- package/dist/formatters/index.d.ts.map +1 -0
- package/dist/formatters/index.js +8 -0
- package/dist/formatters/index.js.map +1 -0
- package/dist/formatters/redux.d.ts +15 -0
- package/dist/formatters/redux.d.ts.map +1 -0
- package/dist/formatters/redux.js +35 -0
- package/dist/formatters/redux.js.map +1 -0
- package/dist/formatters/yaml.d.ts +18 -0
- package/dist/formatters/yaml.d.ts.map +1 -0
- package/dist/formatters/yaml.js +40 -0
- package/dist/formatters/yaml.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +14 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +86 -0
- package/dist/server.js.map +1 -0
- package/dist/templates/advancedMicro.d.ts +8 -0
- package/dist/templates/advancedMicro.d.ts.map +1 -0
- package/dist/templates/advancedMicro.js +834 -0
- package/dist/templates/advancedMicro.js.map +1 -0
- package/dist/templates/consumer.d.ts +12 -0
- package/dist/templates/consumer.d.ts.map +1 -0
- package/dist/templates/consumer.js +1978 -0
- package/dist/templates/consumer.js.map +1 -0
- package/dist/templates/elasticity.d.ts +8 -0
- package/dist/templates/elasticity.d.ts.map +1 -0
- package/dist/templates/elasticity.js +500 -0
- package/dist/templates/elasticity.js.map +1 -0
- package/dist/templates/externalities.d.ts +11 -0
- package/dist/templates/externalities.d.ts.map +1 -0
- package/dist/templates/externalities.js +997 -0
- package/dist/templates/externalities.js.map +1 -0
- package/dist/templates/financeBehavioral.d.ts +8 -0
- package/dist/templates/financeBehavioral.d.ts.map +1 -0
- package/dist/templates/financeBehavioral.js +860 -0
- package/dist/templates/financeBehavioral.js.map +1 -0
- package/dist/templates/growth.d.ts +8 -0
- package/dist/templates/growth.d.ts.map +1 -0
- package/dist/templates/growth.js +740 -0
- package/dist/templates/growth.js.map +1 -0
- package/dist/templates/index.d.ts +31 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +91 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/templates/inequality.d.ts +8 -0
- package/dist/templates/inequality.d.ts.map +1 -0
- package/dist/templates/inequality.js +562 -0
- package/dist/templates/inequality.js.map +1 -0
- package/dist/templates/intertemporalMacro.d.ts +8 -0
- package/dist/templates/intertemporalMacro.d.ts.map +1 -0
- package/dist/templates/intertemporalMacro.js +550 -0
- package/dist/templates/intertemporalMacro.js.map +1 -0
- package/dist/templates/isLM.d.ts +8 -0
- package/dist/templates/isLM.d.ts.map +1 -0
- package/dist/templates/isLM.js +747 -0
- package/dist/templates/isLM.js.map +1 -0
- package/dist/templates/macro.d.ts +8 -0
- package/dist/templates/macro.d.ts.map +1 -0
- package/dist/templates/macro.js +600 -0
- package/dist/templates/macro.js.map +1 -0
- package/dist/templates/marketStructures.d.ts +11 -0
- package/dist/templates/marketStructures.d.ts.map +1 -0
- package/dist/templates/marketStructures.js +1135 -0
- package/dist/templates/marketStructures.js.map +1 -0
- package/dist/templates/newKeynesian.d.ts +8 -0
- package/dist/templates/newKeynesian.d.ts.map +1 -0
- package/dist/templates/newKeynesian.js +633 -0
- package/dist/templates/newKeynesian.js.map +1 -0
- package/dist/templates/oligopoly.d.ts +11 -0
- package/dist/templates/oligopoly.d.ts.map +1 -0
- package/dist/templates/oligopoly.js +1113 -0
- package/dist/templates/oligopoly.js.map +1 -0
- package/dist/templates/ppf.d.ts +8 -0
- package/dist/templates/ppf.d.ts.map +1 -0
- package/dist/templates/ppf.js +439 -0
- package/dist/templates/ppf.js.map +1 -0
- package/dist/templates/producer.d.ts +11 -0
- package/dist/templates/producer.d.ts.map +1 -0
- package/dist/templates/producer.js +979 -0
- package/dist/templates/producer.js.map +1 -0
- package/dist/templates/production.d.ts +8 -0
- package/dist/templates/production.d.ts.map +1 -0
- package/dist/templates/production.js +574 -0
- package/dist/templates/production.js.map +1 -0
- package/dist/templates/supplyDemand.d.ts +8 -0
- package/dist/templates/supplyDemand.d.ts.map +1 -0
- package/dist/templates/supplyDemand.js +1282 -0
- package/dist/templates/supplyDemand.js.map +1 -0
- package/dist/templates/tradeGrowth.d.ts +8 -0
- package/dist/templates/tradeGrowth.d.ts.map +1 -0
- package/dist/templates/tradeGrowth.js +637 -0
- package/dist/templates/tradeGrowth.js.map +1 -0
- package/dist/tools/index.d.ts +25 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +54 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/models.d.ts +8 -0
- package/dist/tools/models.d.ts.map +1 -0
- package/dist/tools/models.js +828 -0
- package/dist/tools/models.js.map +1 -0
- package/dist/tools/output.d.ts +8 -0
- package/dist/tools/output.d.ts.map +1 -0
- package/dist/tools/output.js +236 -0
- package/dist/tools/output.js.map +1 -0
- package/dist/tools/templates.d.ts +8 -0
- package/dist/tools/templates.d.ts.map +1 -0
- package/dist/tools/templates.js +247 -0
- package/dist/tools/templates.js.map +1 -0
- package/dist/tools/validation.d.ts +8 -0
- package/dist/tools/validation.d.ts.map +1 -0
- package/dist/tools/validation.js +181 -0
- package/dist/tools/validation.js.map +1 -0
- package/dist/types/index.d.ts +187 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/cache.d.ts +99 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +192 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/index.d.ts +8 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +8 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +128 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +251 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/validation/index.d.ts +42 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +282 -0
- package/dist/validation/index.js.map +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,997 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Externalities and Public Goods Templates
|
|
3
|
+
*
|
|
4
|
+
* Templates for market failures including negative and positive externalities,
|
|
5
|
+
* Pigouvian taxes/subsidies, Coase theorem, and public goods.
|
|
6
|
+
*
|
|
7
|
+
* Phase 3.6: Externalities & Public Goods
|
|
8
|
+
*/
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// PHASE 3.6: EXTERNALITIES & PUBLIC GOODS
|
|
11
|
+
// ============================================================================
|
|
12
|
+
/**
|
|
13
|
+
* Negative Externality
|
|
14
|
+
*
|
|
15
|
+
* Market with external costs (pollution). MSC > MPC.
|
|
16
|
+
* Shows market failure: overproduction relative to social optimum.
|
|
17
|
+
*/
|
|
18
|
+
const negativeExternality = {
|
|
19
|
+
id: 'negative_externality',
|
|
20
|
+
name: 'Negative Externality',
|
|
21
|
+
description: 'Market with external costs (e.g., pollution). Marginal Social Cost exceeds Marginal Private Cost. Market overproduces relative to socially optimal quantity.',
|
|
22
|
+
category: 'market_structures',
|
|
23
|
+
level: 'undergraduate',
|
|
24
|
+
tags: ['externality', 'negative externality', 'pollution', 'market failure', 'MSC', 'MPC'],
|
|
25
|
+
parameters: {
|
|
26
|
+
demandIntercept: {
|
|
27
|
+
type: 'number',
|
|
28
|
+
default: 100,
|
|
29
|
+
description: 'Demand intercept',
|
|
30
|
+
},
|
|
31
|
+
demandSlope: {
|
|
32
|
+
type: 'number',
|
|
33
|
+
default: 1,
|
|
34
|
+
description: 'Demand slope',
|
|
35
|
+
},
|
|
36
|
+
mpcIntercept: {
|
|
37
|
+
type: 'number',
|
|
38
|
+
default: 10,
|
|
39
|
+
description: 'Marginal private cost intercept',
|
|
40
|
+
},
|
|
41
|
+
mpcSlope: {
|
|
42
|
+
type: 'number',
|
|
43
|
+
default: 1,
|
|
44
|
+
description: 'Marginal private cost slope',
|
|
45
|
+
},
|
|
46
|
+
externalCostPerUnit: {
|
|
47
|
+
type: 'number',
|
|
48
|
+
default: 20,
|
|
49
|
+
description: 'External cost per unit (constant)',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
generate: (params) => {
|
|
53
|
+
const a = params.demandIntercept ?? 100;
|
|
54
|
+
const b = params.demandSlope ?? 1;
|
|
55
|
+
const c0 = params.mpcIntercept ?? 10;
|
|
56
|
+
const c1 = params.mpcSlope ?? 1;
|
|
57
|
+
const EC = params.externalCostPerUnit ?? 20;
|
|
58
|
+
return {
|
|
59
|
+
metadata: {
|
|
60
|
+
specVersion: '1.3',
|
|
61
|
+
title: 'Negative Externality',
|
|
62
|
+
description: 'MSC = MPC + MEC',
|
|
63
|
+
},
|
|
64
|
+
parameters: {
|
|
65
|
+
a: { value: a, label: 'Demand Intercept', min: 50, max: 150 },
|
|
66
|
+
b: { value: b, label: 'Demand Slope', min: 0.5, max: 2, step: 0.1 },
|
|
67
|
+
c0: { value: c0, label: 'MPC Intercept', min: 0, max: 30 },
|
|
68
|
+
c1: { value: c1, label: 'MPC Slope', min: 0.5, max: 2, step: 0.1 },
|
|
69
|
+
EC: { value: EC, label: 'External Cost', min: 5, max: 50 },
|
|
70
|
+
// Market equilibrium (MPC = D)
|
|
71
|
+
Q_market: {
|
|
72
|
+
expression: '(a - c0) / (b + c1)',
|
|
73
|
+
label: 'Q_market',
|
|
74
|
+
readonly: true,
|
|
75
|
+
},
|
|
76
|
+
P_market: {
|
|
77
|
+
expression: 'a - b * Q_market',
|
|
78
|
+
label: 'P_market',
|
|
79
|
+
readonly: true,
|
|
80
|
+
},
|
|
81
|
+
// Social optimum (MSC = D)
|
|
82
|
+
Q_social: {
|
|
83
|
+
expression: '(a - c0 - EC) / (b + c1)',
|
|
84
|
+
label: 'Q_social',
|
|
85
|
+
readonly: true,
|
|
86
|
+
},
|
|
87
|
+
P_social: {
|
|
88
|
+
expression: 'a - b * Q_social',
|
|
89
|
+
label: 'P_social',
|
|
90
|
+
readonly: true,
|
|
91
|
+
},
|
|
92
|
+
// Overproduction
|
|
93
|
+
overproduction: {
|
|
94
|
+
expression: 'Q_market - Q_social',
|
|
95
|
+
label: 'Overproduction',
|
|
96
|
+
readonly: true,
|
|
97
|
+
},
|
|
98
|
+
// Deadweight loss
|
|
99
|
+
DWL: {
|
|
100
|
+
expression: '0.5 * EC * overproduction',
|
|
101
|
+
label: 'DWL',
|
|
102
|
+
readonly: true,
|
|
103
|
+
},
|
|
104
|
+
// Chart bounds
|
|
105
|
+
chartMaxQ: { expression: 'Q_market * 1.3', hidden: true },
|
|
106
|
+
chartMaxP: { expression: 'a * 1.1', hidden: true },
|
|
107
|
+
},
|
|
108
|
+
charts: [
|
|
109
|
+
{
|
|
110
|
+
id: 'main',
|
|
111
|
+
title: 'Negative Externality: Overproduction',
|
|
112
|
+
xAxis: { label: 'Quantity (Q)', min: 0, max: 'chartMaxQ' },
|
|
113
|
+
yAxis: { label: 'Price, Cost', min: 0, max: 'chartMaxP' },
|
|
114
|
+
elements: [
|
|
115
|
+
// DWL triangle
|
|
116
|
+
{
|
|
117
|
+
id: 'dwl',
|
|
118
|
+
type: 'polygon',
|
|
119
|
+
points: [
|
|
120
|
+
{ x: 'Q_social', y: 'P_social' },
|
|
121
|
+
{ x: 'Q_market', y: 'P_market' },
|
|
122
|
+
{ x: 'Q_market', y: 'c0 + c1 * Q_market + EC' },
|
|
123
|
+
],
|
|
124
|
+
fill: '#EF4444',
|
|
125
|
+
opacity: 0.3,
|
|
126
|
+
label: 'DWL',
|
|
127
|
+
},
|
|
128
|
+
// Demand (MPB = MSB for negative externality in production)
|
|
129
|
+
{
|
|
130
|
+
id: 'demand',
|
|
131
|
+
type: 'line',
|
|
132
|
+
equation: 'a - b * x',
|
|
133
|
+
color: '#2563EB',
|
|
134
|
+
strokeWidth: 3,
|
|
135
|
+
label: 'Demand (MPB = MSB)',
|
|
136
|
+
domain: { min: 0, max: 'a / b' },
|
|
137
|
+
},
|
|
138
|
+
// MPC (supply)
|
|
139
|
+
{
|
|
140
|
+
id: 'mpc',
|
|
141
|
+
type: 'line',
|
|
142
|
+
equation: 'c0 + c1 * x',
|
|
143
|
+
color: '#F59E0B',
|
|
144
|
+
strokeWidth: 3,
|
|
145
|
+
label: 'MPC (Supply)',
|
|
146
|
+
},
|
|
147
|
+
// MSC = MPC + MEC
|
|
148
|
+
{
|
|
149
|
+
id: 'msc',
|
|
150
|
+
type: 'line',
|
|
151
|
+
equation: 'c0 + EC + c1 * x',
|
|
152
|
+
color: '#DC2626',
|
|
153
|
+
strokeWidth: 3,
|
|
154
|
+
label: 'MSC = MPC + MEC',
|
|
155
|
+
},
|
|
156
|
+
// External cost arrow
|
|
157
|
+
{
|
|
158
|
+
id: 'ec-arrow',
|
|
159
|
+
type: 'arrow',
|
|
160
|
+
x1: 'Q_market * 0.7',
|
|
161
|
+
y1: 'c0 + c1 * Q_market * 0.7',
|
|
162
|
+
x2: 'Q_market * 0.7',
|
|
163
|
+
y2: 'c0 + EC + c1 * Q_market * 0.7',
|
|
164
|
+
color: '#DC2626',
|
|
165
|
+
},
|
|
166
|
+
// Market equilibrium
|
|
167
|
+
{
|
|
168
|
+
id: 'market-eq',
|
|
169
|
+
type: 'point',
|
|
170
|
+
x: 'Q_market',
|
|
171
|
+
y: 'P_market',
|
|
172
|
+
color: '#F59E0B',
|
|
173
|
+
size: 10,
|
|
174
|
+
label: 'Market Eq.',
|
|
175
|
+
droplines: { x: true, y: true },
|
|
176
|
+
},
|
|
177
|
+
// Social optimum
|
|
178
|
+
{
|
|
179
|
+
id: 'social-opt',
|
|
180
|
+
type: 'point',
|
|
181
|
+
x: 'Q_social',
|
|
182
|
+
y: 'P_social',
|
|
183
|
+
color: '#16A34A',
|
|
184
|
+
size: 10,
|
|
185
|
+
label: 'Social Opt.',
|
|
186
|
+
droplines: { x: true, y: true },
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
annotations: [
|
|
190
|
+
{
|
|
191
|
+
id: 'ec-label',
|
|
192
|
+
text: 'MEC = $${EC}',
|
|
193
|
+
x: 'Q_market * 0.75',
|
|
194
|
+
y: 'c0 + EC / 2 + c1 * Q_market * 0.7',
|
|
195
|
+
color: '#DC2626',
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
id: 'dwl-label',
|
|
199
|
+
text: 'DWL = $${DWL:.0f}',
|
|
200
|
+
x: '(Q_social + Q_market) / 2',
|
|
201
|
+
y: '(P_market + c0 + c1 * Q_market + EC) / 2',
|
|
202
|
+
color: '#DC2626',
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
id: 'overprod-label',
|
|
206
|
+
text: 'Overproduction = ${overproduction:.1f}',
|
|
207
|
+
x: 'chartMaxQ * 0.6',
|
|
208
|
+
y: 'chartMaxP * 0.15',
|
|
209
|
+
color: '#6B7280',
|
|
210
|
+
},
|
|
211
|
+
],
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
};
|
|
215
|
+
},
|
|
216
|
+
};
|
|
217
|
+
/**
|
|
218
|
+
* Positive Externality
|
|
219
|
+
*
|
|
220
|
+
* Market with external benefits (education, vaccines). MSB > MPB.
|
|
221
|
+
* Shows market failure: underproduction relative to social optimum.
|
|
222
|
+
*/
|
|
223
|
+
const positiveExternality = {
|
|
224
|
+
id: 'positive_externality',
|
|
225
|
+
name: 'Positive Externality',
|
|
226
|
+
description: 'Market with external benefits (e.g., education, vaccines). Marginal Social Benefit exceeds Marginal Private Benefit. Market underproduces.',
|
|
227
|
+
category: 'market_structures',
|
|
228
|
+
level: 'undergraduate',
|
|
229
|
+
tags: ['externality', 'positive externality', 'education', 'market failure', 'MSB', 'MPB'],
|
|
230
|
+
parameters: {
|
|
231
|
+
mpbIntercept: {
|
|
232
|
+
type: 'number',
|
|
233
|
+
default: 100,
|
|
234
|
+
description: 'Marginal private benefit intercept',
|
|
235
|
+
},
|
|
236
|
+
mpbSlope: {
|
|
237
|
+
type: 'number',
|
|
238
|
+
default: 1,
|
|
239
|
+
description: 'MPB slope',
|
|
240
|
+
},
|
|
241
|
+
supplyIntercept: {
|
|
242
|
+
type: 'number',
|
|
243
|
+
default: 10,
|
|
244
|
+
description: 'Supply intercept',
|
|
245
|
+
},
|
|
246
|
+
supplySlope: {
|
|
247
|
+
type: 'number',
|
|
248
|
+
default: 1,
|
|
249
|
+
description: 'Supply slope',
|
|
250
|
+
},
|
|
251
|
+
externalBenefitPerUnit: {
|
|
252
|
+
type: 'number',
|
|
253
|
+
default: 20,
|
|
254
|
+
description: 'External benefit per unit',
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
generate: (params) => {
|
|
258
|
+
const a = params.mpbIntercept ?? 100;
|
|
259
|
+
const b = params.mpbSlope ?? 1;
|
|
260
|
+
const c0 = params.supplyIntercept ?? 10;
|
|
261
|
+
const c1 = params.supplySlope ?? 1;
|
|
262
|
+
const EB = params.externalBenefitPerUnit ?? 20;
|
|
263
|
+
return {
|
|
264
|
+
metadata: {
|
|
265
|
+
specVersion: '1.3',
|
|
266
|
+
title: 'Positive Externality',
|
|
267
|
+
description: 'MSB = MPB + MEB',
|
|
268
|
+
},
|
|
269
|
+
parameters: {
|
|
270
|
+
a: { value: a, label: 'MPB Intercept', min: 50, max: 150 },
|
|
271
|
+
b: { value: b, label: 'MPB Slope', min: 0.5, max: 2, step: 0.1 },
|
|
272
|
+
c0: { value: c0, label: 'Supply Intercept', min: 0, max: 30 },
|
|
273
|
+
c1: { value: c1, label: 'Supply Slope', min: 0.5, max: 2, step: 0.1 },
|
|
274
|
+
EB: { value: EB, label: 'External Benefit', min: 5, max: 50 },
|
|
275
|
+
// Market equilibrium (MPB = Supply)
|
|
276
|
+
Q_market: {
|
|
277
|
+
expression: '(a - c0) / (b + c1)',
|
|
278
|
+
label: 'Q_market',
|
|
279
|
+
readonly: true,
|
|
280
|
+
},
|
|
281
|
+
P_market: {
|
|
282
|
+
expression: 'c0 + c1 * Q_market',
|
|
283
|
+
label: 'P_market',
|
|
284
|
+
readonly: true,
|
|
285
|
+
},
|
|
286
|
+
// Social optimum (MSB = Supply)
|
|
287
|
+
Q_social: {
|
|
288
|
+
expression: '(a + EB - c0) / (b + c1)',
|
|
289
|
+
label: 'Q_social',
|
|
290
|
+
readonly: true,
|
|
291
|
+
},
|
|
292
|
+
P_social: {
|
|
293
|
+
expression: 'c0 + c1 * Q_social',
|
|
294
|
+
label: 'P_social',
|
|
295
|
+
readonly: true,
|
|
296
|
+
},
|
|
297
|
+
// Underproduction
|
|
298
|
+
underproduction: {
|
|
299
|
+
expression: 'Q_social - Q_market',
|
|
300
|
+
label: 'Underproduction',
|
|
301
|
+
readonly: true,
|
|
302
|
+
},
|
|
303
|
+
// Deadweight loss
|
|
304
|
+
DWL: {
|
|
305
|
+
expression: '0.5 * EB * underproduction',
|
|
306
|
+
label: 'DWL',
|
|
307
|
+
readonly: true,
|
|
308
|
+
},
|
|
309
|
+
// Chart bounds
|
|
310
|
+
chartMaxQ: { expression: 'Q_social * 1.3', hidden: true },
|
|
311
|
+
chartMaxP: { expression: '(a + EB) * 1.1', hidden: true },
|
|
312
|
+
},
|
|
313
|
+
charts: [
|
|
314
|
+
{
|
|
315
|
+
id: 'main',
|
|
316
|
+
title: 'Positive Externality: Underproduction',
|
|
317
|
+
xAxis: { label: 'Quantity (Q)', min: 0, max: 'chartMaxQ' },
|
|
318
|
+
yAxis: { label: 'Price, Benefit', min: 0, max: 'chartMaxP' },
|
|
319
|
+
elements: [
|
|
320
|
+
// DWL triangle
|
|
321
|
+
{
|
|
322
|
+
id: 'dwl',
|
|
323
|
+
type: 'polygon',
|
|
324
|
+
points: [
|
|
325
|
+
{ x: 'Q_market', y: 'a - b * Q_market' },
|
|
326
|
+
{ x: 'Q_market', y: 'a + EB - b * Q_market' },
|
|
327
|
+
{ x: 'Q_social', y: 'P_social' },
|
|
328
|
+
],
|
|
329
|
+
fill: '#EF4444',
|
|
330
|
+
opacity: 0.3,
|
|
331
|
+
label: 'DWL',
|
|
332
|
+
},
|
|
333
|
+
// MPB (private demand)
|
|
334
|
+
{
|
|
335
|
+
id: 'mpb',
|
|
336
|
+
type: 'line',
|
|
337
|
+
equation: 'a - b * x',
|
|
338
|
+
color: '#F59E0B',
|
|
339
|
+
strokeWidth: 3,
|
|
340
|
+
label: 'MPB (Private Demand)',
|
|
341
|
+
domain: { min: 0, max: 'a / b' },
|
|
342
|
+
},
|
|
343
|
+
// MSB = MPB + MEB
|
|
344
|
+
{
|
|
345
|
+
id: 'msb',
|
|
346
|
+
type: 'line',
|
|
347
|
+
equation: 'a + EB - b * x',
|
|
348
|
+
color: '#16A34A',
|
|
349
|
+
strokeWidth: 3,
|
|
350
|
+
label: 'MSB = MPB + MEB',
|
|
351
|
+
domain: { min: 0, max: '(a + EB) / b' },
|
|
352
|
+
},
|
|
353
|
+
// Supply (MPC = MSC)
|
|
354
|
+
{
|
|
355
|
+
id: 'supply',
|
|
356
|
+
type: 'line',
|
|
357
|
+
equation: 'c0 + c1 * x',
|
|
358
|
+
color: '#2563EB',
|
|
359
|
+
strokeWidth: 3,
|
|
360
|
+
label: 'Supply (MPC = MSC)',
|
|
361
|
+
},
|
|
362
|
+
// External benefit arrow
|
|
363
|
+
{
|
|
364
|
+
id: 'eb-arrow',
|
|
365
|
+
type: 'arrow',
|
|
366
|
+
x1: 'Q_market * 0.6',
|
|
367
|
+
y1: 'a - b * Q_market * 0.6',
|
|
368
|
+
x2: 'Q_market * 0.6',
|
|
369
|
+
y2: 'a + EB - b * Q_market * 0.6',
|
|
370
|
+
color: '#16A34A',
|
|
371
|
+
},
|
|
372
|
+
// Market equilibrium
|
|
373
|
+
{
|
|
374
|
+
id: 'market-eq',
|
|
375
|
+
type: 'point',
|
|
376
|
+
x: 'Q_market',
|
|
377
|
+
y: 'P_market',
|
|
378
|
+
color: '#F59E0B',
|
|
379
|
+
size: 10,
|
|
380
|
+
label: 'Market Eq.',
|
|
381
|
+
droplines: { x: true, y: true },
|
|
382
|
+
},
|
|
383
|
+
// Social optimum
|
|
384
|
+
{
|
|
385
|
+
id: 'social-opt',
|
|
386
|
+
type: 'point',
|
|
387
|
+
x: 'Q_social',
|
|
388
|
+
y: 'P_social',
|
|
389
|
+
color: '#16A34A',
|
|
390
|
+
size: 10,
|
|
391
|
+
label: 'Social Opt.',
|
|
392
|
+
droplines: { x: true, y: true },
|
|
393
|
+
},
|
|
394
|
+
],
|
|
395
|
+
annotations: [
|
|
396
|
+
{
|
|
397
|
+
id: 'eb-label',
|
|
398
|
+
text: 'MEB = $${EB}',
|
|
399
|
+
x: 'Q_market * 0.65',
|
|
400
|
+
y: 'a + EB / 2 - b * Q_market * 0.6',
|
|
401
|
+
color: '#16A34A',
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
id: 'dwl-label',
|
|
405
|
+
text: 'DWL = $${DWL:.0f}',
|
|
406
|
+
x: '(Q_market + Q_social) / 2',
|
|
407
|
+
y: 'a + EB * 0.5 - b * (Q_market + Q_social) / 2',
|
|
408
|
+
color: '#DC2626',
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
id: 'underprod-label',
|
|
412
|
+
text: 'Underproduction = ${underproduction:.1f}',
|
|
413
|
+
x: 'chartMaxQ * 0.6',
|
|
414
|
+
y: 'chartMaxP * 0.9',
|
|
415
|
+
color: '#6B7280',
|
|
416
|
+
},
|
|
417
|
+
],
|
|
418
|
+
},
|
|
419
|
+
],
|
|
420
|
+
};
|
|
421
|
+
},
|
|
422
|
+
};
|
|
423
|
+
/**
|
|
424
|
+
* Pigouvian Tax
|
|
425
|
+
*
|
|
426
|
+
* Corrective tax on negative externality that internalizes external cost.
|
|
427
|
+
*/
|
|
428
|
+
const pigouvianTax = {
|
|
429
|
+
id: 'pigouvian_tax',
|
|
430
|
+
name: 'Pigouvian Tax',
|
|
431
|
+
description: 'Corrective tax equal to marginal external cost. Shifts private supply to align with social cost, achieving efficient outcome.',
|
|
432
|
+
category: 'market_structures',
|
|
433
|
+
level: 'undergraduate',
|
|
434
|
+
tags: ['Pigouvian tax', 'corrective tax', 'externality', 'policy', 'efficiency'],
|
|
435
|
+
parameters: {
|
|
436
|
+
demandIntercept: {
|
|
437
|
+
type: 'number',
|
|
438
|
+
default: 100,
|
|
439
|
+
description: 'Demand intercept',
|
|
440
|
+
},
|
|
441
|
+
demandSlope: {
|
|
442
|
+
type: 'number',
|
|
443
|
+
default: 1,
|
|
444
|
+
description: 'Demand slope',
|
|
445
|
+
},
|
|
446
|
+
mpcIntercept: {
|
|
447
|
+
type: 'number',
|
|
448
|
+
default: 10,
|
|
449
|
+
description: 'MPC intercept',
|
|
450
|
+
},
|
|
451
|
+
mpcSlope: {
|
|
452
|
+
type: 'number',
|
|
453
|
+
default: 1,
|
|
454
|
+
description: 'MPC slope',
|
|
455
|
+
},
|
|
456
|
+
externalCost: {
|
|
457
|
+
type: 'number',
|
|
458
|
+
default: 20,
|
|
459
|
+
description: 'Marginal external cost (= optimal tax)',
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
generate: (params) => {
|
|
463
|
+
const a = params.demandIntercept ?? 100;
|
|
464
|
+
const b = params.demandSlope ?? 1;
|
|
465
|
+
const c0 = params.mpcIntercept ?? 10;
|
|
466
|
+
const c1 = params.mpcSlope ?? 1;
|
|
467
|
+
const t = params.externalCost ?? 20;
|
|
468
|
+
return {
|
|
469
|
+
metadata: {
|
|
470
|
+
specVersion: '1.3',
|
|
471
|
+
title: 'Pigouvian Tax',
|
|
472
|
+
description: 't* = MEC',
|
|
473
|
+
},
|
|
474
|
+
parameters: {
|
|
475
|
+
a: { value: a, label: 'Demand Intercept', min: 50, max: 150 },
|
|
476
|
+
b: { value: b, label: 'Demand Slope', min: 0.5, max: 2, step: 0.1 },
|
|
477
|
+
c0: { value: c0, label: 'MPC Intercept', min: 0, max: 30 },
|
|
478
|
+
c1: { value: c1, label: 'MPC Slope', min: 0.5, max: 2, step: 0.1 },
|
|
479
|
+
t: { value: t, label: 'Tax = MEC', min: 5, max: 50 },
|
|
480
|
+
// Before tax (market equilibrium)
|
|
481
|
+
Q_before: { expression: '(a - c0) / (b + c1)', label: 'Q (before)', readonly: true },
|
|
482
|
+
P_before: { expression: 'a - b * Q_before', label: 'P (before)', readonly: true },
|
|
483
|
+
// After tax (efficient equilibrium)
|
|
484
|
+
Q_after: {
|
|
485
|
+
expression: '(a - c0 - t) / (b + c1)',
|
|
486
|
+
label: 'Q (after)',
|
|
487
|
+
readonly: true,
|
|
488
|
+
},
|
|
489
|
+
P_consumer: {
|
|
490
|
+
expression: 'a - b * Q_after',
|
|
491
|
+
label: 'P (consumer)',
|
|
492
|
+
readonly: true,
|
|
493
|
+
},
|
|
494
|
+
P_producer: {
|
|
495
|
+
expression: 'P_consumer - t',
|
|
496
|
+
label: 'P (producer)',
|
|
497
|
+
readonly: true,
|
|
498
|
+
},
|
|
499
|
+
// Tax revenue
|
|
500
|
+
taxRevenue: {
|
|
501
|
+
expression: 't * Q_after',
|
|
502
|
+
label: 'Tax Revenue',
|
|
503
|
+
readonly: true,
|
|
504
|
+
},
|
|
505
|
+
// Reduction in externality
|
|
506
|
+
reduction: {
|
|
507
|
+
expression: 'Q_before - Q_after',
|
|
508
|
+
label: 'Q Reduction',
|
|
509
|
+
readonly: true,
|
|
510
|
+
},
|
|
511
|
+
// Chart bounds
|
|
512
|
+
chartMaxQ: { expression: 'Q_before * 1.3', hidden: true },
|
|
513
|
+
chartMaxP: { expression: 'a * 1.1', hidden: true },
|
|
514
|
+
},
|
|
515
|
+
charts: [
|
|
516
|
+
{
|
|
517
|
+
id: 'main',
|
|
518
|
+
title: 'Pigouvian Tax: t* = MEC',
|
|
519
|
+
xAxis: { label: 'Quantity (Q)', min: 0, max: 'chartMaxQ' },
|
|
520
|
+
yAxis: { label: 'Price, Cost', min: 0, max: 'chartMaxP' },
|
|
521
|
+
elements: [
|
|
522
|
+
// Tax revenue rectangle
|
|
523
|
+
{
|
|
524
|
+
id: 'tax-revenue',
|
|
525
|
+
type: 'rectangle',
|
|
526
|
+
x1: 0,
|
|
527
|
+
y1: 'P_producer',
|
|
528
|
+
x2: 'Q_after',
|
|
529
|
+
y2: 'P_consumer',
|
|
530
|
+
fill: '#7C3AED',
|
|
531
|
+
opacity: 0.3,
|
|
532
|
+
label: 'Tax Revenue',
|
|
533
|
+
},
|
|
534
|
+
// Demand
|
|
535
|
+
{
|
|
536
|
+
id: 'demand',
|
|
537
|
+
type: 'line',
|
|
538
|
+
equation: 'a - b * x',
|
|
539
|
+
color: '#2563EB',
|
|
540
|
+
strokeWidth: 3,
|
|
541
|
+
label: 'Demand',
|
|
542
|
+
domain: { min: 0, max: 'a / b' },
|
|
543
|
+
},
|
|
544
|
+
// MPC (original supply)
|
|
545
|
+
{
|
|
546
|
+
id: 'mpc',
|
|
547
|
+
type: 'line',
|
|
548
|
+
equation: 'c0 + c1 * x',
|
|
549
|
+
color: '#F59E0B',
|
|
550
|
+
strokeWidth: 2,
|
|
551
|
+
lineStyle: 'dashed',
|
|
552
|
+
label: 'MPC',
|
|
553
|
+
},
|
|
554
|
+
// MPC + tax (new supply)
|
|
555
|
+
{
|
|
556
|
+
id: 'mpc-tax',
|
|
557
|
+
type: 'line',
|
|
558
|
+
equation: 'c0 + t + c1 * x',
|
|
559
|
+
color: '#DC2626',
|
|
560
|
+
strokeWidth: 3,
|
|
561
|
+
label: 'MPC + Tax = MSC',
|
|
562
|
+
},
|
|
563
|
+
// Before tax equilibrium
|
|
564
|
+
{
|
|
565
|
+
id: 'before-eq',
|
|
566
|
+
type: 'point',
|
|
567
|
+
x: 'Q_before',
|
|
568
|
+
y: 'P_before',
|
|
569
|
+
color: '#F59E0B',
|
|
570
|
+
size: 8,
|
|
571
|
+
label: 'Before Tax',
|
|
572
|
+
},
|
|
573
|
+
// After tax equilibrium
|
|
574
|
+
{
|
|
575
|
+
id: 'after-eq',
|
|
576
|
+
type: 'point',
|
|
577
|
+
x: 'Q_after',
|
|
578
|
+
y: 'P_consumer',
|
|
579
|
+
color: '#16A34A',
|
|
580
|
+
size: 10,
|
|
581
|
+
label: 'Efficient (After Tax)',
|
|
582
|
+
droplines: { x: true, y: true },
|
|
583
|
+
},
|
|
584
|
+
// Tax wedge
|
|
585
|
+
{
|
|
586
|
+
id: 'tax-wedge',
|
|
587
|
+
type: 'arrow',
|
|
588
|
+
x1: 'Q_after',
|
|
589
|
+
y1: 'P_producer',
|
|
590
|
+
x2: 'Q_after',
|
|
591
|
+
y2: 'P_consumer',
|
|
592
|
+
color: '#7C3AED',
|
|
593
|
+
},
|
|
594
|
+
],
|
|
595
|
+
annotations: [
|
|
596
|
+
{
|
|
597
|
+
id: 'tax-label',
|
|
598
|
+
text: 'Tax = $${t}',
|
|
599
|
+
x: 'Q_after + chartMaxQ * 0.05',
|
|
600
|
+
y: '(P_consumer + P_producer) / 2',
|
|
601
|
+
color: '#7C3AED',
|
|
602
|
+
},
|
|
603
|
+
{
|
|
604
|
+
id: 'revenue-label',
|
|
605
|
+
text: 'Revenue = $${taxRevenue:.0f}',
|
|
606
|
+
x: 'Q_after / 2',
|
|
607
|
+
y: '(P_consumer + P_producer) / 2',
|
|
608
|
+
color: '#7C3AED',
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
id: 'efficiency',
|
|
612
|
+
text: 'Tax internalizes externality → Efficient',
|
|
613
|
+
x: 'chartMaxQ * 0.5',
|
|
614
|
+
y: 'chartMaxP * 0.15',
|
|
615
|
+
color: '#16A34A',
|
|
616
|
+
},
|
|
617
|
+
],
|
|
618
|
+
},
|
|
619
|
+
],
|
|
620
|
+
};
|
|
621
|
+
},
|
|
622
|
+
};
|
|
623
|
+
/**
|
|
624
|
+
* Coase Theorem
|
|
625
|
+
*
|
|
626
|
+
* Shows how private bargaining can achieve efficiency
|
|
627
|
+
* regardless of initial property rights allocation.
|
|
628
|
+
*/
|
|
629
|
+
const coaseTheorem = {
|
|
630
|
+
id: 'coase_theorem',
|
|
631
|
+
name: 'Coase Theorem',
|
|
632
|
+
description: 'When transaction costs are zero and property rights are well-defined, private bargaining achieves efficient outcome regardless of initial rights allocation.',
|
|
633
|
+
category: 'market_structures',
|
|
634
|
+
level: 'undergraduate',
|
|
635
|
+
tags: ['Coase theorem', 'property rights', 'bargaining', 'externality', 'efficiency'],
|
|
636
|
+
parameters: {
|
|
637
|
+
pollutionLevels: {
|
|
638
|
+
type: 'number',
|
|
639
|
+
default: 10,
|
|
640
|
+
description: 'Maximum pollution units',
|
|
641
|
+
},
|
|
642
|
+
marginalBenefitPolluter: {
|
|
643
|
+
type: 'number',
|
|
644
|
+
default: 100,
|
|
645
|
+
description: 'Polluter MB intercept',
|
|
646
|
+
},
|
|
647
|
+
marginalDamageVictim: {
|
|
648
|
+
type: 'number',
|
|
649
|
+
default: 10,
|
|
650
|
+
description: 'Victim MD per unit',
|
|
651
|
+
},
|
|
652
|
+
},
|
|
653
|
+
generate: (params) => {
|
|
654
|
+
const maxQ = params.pollutionLevels ?? 10;
|
|
655
|
+
const MB_intercept = params.marginalBenefitPolluter ?? 100;
|
|
656
|
+
const MD = params.marginalDamageVictim ?? 10;
|
|
657
|
+
return {
|
|
658
|
+
metadata: {
|
|
659
|
+
specVersion: '1.3',
|
|
660
|
+
title: 'Coase Theorem',
|
|
661
|
+
description: 'Private bargaining solution',
|
|
662
|
+
},
|
|
663
|
+
parameters: {
|
|
664
|
+
maxQ: { value: maxQ, label: 'Max Pollution', min: 5, max: 20 },
|
|
665
|
+
MB_int: { value: MB_intercept, label: 'MB Intercept', min: 50, max: 150 },
|
|
666
|
+
MD: { value: MD, label: 'MD per unit', min: 5, max: 30 },
|
|
667
|
+
// MB slope (linear decline)
|
|
668
|
+
MB_slope: { expression: 'MB_int / maxQ', hidden: true },
|
|
669
|
+
// Efficient pollution level: MB = MD
|
|
670
|
+
Q_efficient: {
|
|
671
|
+
expression: '(MB_int - MD) / MB_slope',
|
|
672
|
+
label: 'Q* (efficient)',
|
|
673
|
+
readonly: true,
|
|
674
|
+
},
|
|
675
|
+
// Total benefit at efficient level
|
|
676
|
+
totalBenefitPolluter: {
|
|
677
|
+
expression: '0.5 * (MB_int + MD) * Q_efficient',
|
|
678
|
+
label: 'Polluter Benefit',
|
|
679
|
+
readonly: true,
|
|
680
|
+
},
|
|
681
|
+
// Total damage at efficient level
|
|
682
|
+
totalDamageVictim: {
|
|
683
|
+
expression: 'MD * Q_efficient',
|
|
684
|
+
label: 'Victim Damage',
|
|
685
|
+
readonly: true,
|
|
686
|
+
},
|
|
687
|
+
// Net social gain
|
|
688
|
+
netGain: {
|
|
689
|
+
expression: 'totalBenefitPolluter - totalDamageVictim',
|
|
690
|
+
label: 'Net Social Gain',
|
|
691
|
+
readonly: true,
|
|
692
|
+
},
|
|
693
|
+
// Chart bounds
|
|
694
|
+
chartMaxQ: { expression: 'maxQ * 1.2', hidden: true },
|
|
695
|
+
chartMaxP: { expression: 'MB_int * 1.1', hidden: true },
|
|
696
|
+
},
|
|
697
|
+
charts: [
|
|
698
|
+
{
|
|
699
|
+
id: 'main',
|
|
700
|
+
title: 'Coase Theorem: Bargaining to Efficiency',
|
|
701
|
+
xAxis: { label: 'Pollution Level (Q)', min: 0, max: 'chartMaxQ' },
|
|
702
|
+
yAxis: { label: 'MB, MD ($)', min: 0, max: 'chartMaxP' },
|
|
703
|
+
elements: [
|
|
704
|
+
// Net gain from pollution (up to efficient level)
|
|
705
|
+
{
|
|
706
|
+
id: 'net-benefit',
|
|
707
|
+
type: 'area',
|
|
708
|
+
topBoundary: 'mb-curve',
|
|
709
|
+
bottomBoundary: 'MD',
|
|
710
|
+
leftBoundary: '0',
|
|
711
|
+
rightBoundary: 'Q_efficient',
|
|
712
|
+
color: '#22C55E',
|
|
713
|
+
opacity: 0.3,
|
|
714
|
+
label: 'Net Social Benefit',
|
|
715
|
+
},
|
|
716
|
+
// Marginal Benefit to polluter (declining)
|
|
717
|
+
{
|
|
718
|
+
id: 'mb-curve',
|
|
719
|
+
type: 'line',
|
|
720
|
+
equation: 'MB_int - MB_slope * x',
|
|
721
|
+
color: '#2563EB',
|
|
722
|
+
strokeWidth: 3,
|
|
723
|
+
label: 'MB (Polluter)',
|
|
724
|
+
domain: { min: 0, max: 'maxQ' },
|
|
725
|
+
},
|
|
726
|
+
// Marginal Damage to victim (constant)
|
|
727
|
+
{
|
|
728
|
+
id: 'md-line',
|
|
729
|
+
type: 'horizontalLine',
|
|
730
|
+
y: 'MD',
|
|
731
|
+
color: '#DC2626',
|
|
732
|
+
strokeWidth: 3,
|
|
733
|
+
label: 'MD (Victim)',
|
|
734
|
+
},
|
|
735
|
+
// Efficient point
|
|
736
|
+
{
|
|
737
|
+
id: 'efficient',
|
|
738
|
+
type: 'point',
|
|
739
|
+
x: 'Q_efficient',
|
|
740
|
+
y: 'MD',
|
|
741
|
+
color: '#16A34A',
|
|
742
|
+
size: 12,
|
|
743
|
+
label: 'MB = MD',
|
|
744
|
+
droplines: { x: true, y: true },
|
|
745
|
+
},
|
|
746
|
+
// Scenario markers
|
|
747
|
+
{
|
|
748
|
+
id: 'victim-rights',
|
|
749
|
+
type: 'point',
|
|
750
|
+
x: 0,
|
|
751
|
+
y: 0,
|
|
752
|
+
color: '#7C3AED',
|
|
753
|
+
label: 'Victim has rights',
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
id: 'polluter-rights',
|
|
757
|
+
type: 'point',
|
|
758
|
+
x: 'maxQ',
|
|
759
|
+
y: 0,
|
|
760
|
+
color: '#F59E0B',
|
|
761
|
+
label: 'Polluter has rights',
|
|
762
|
+
},
|
|
763
|
+
],
|
|
764
|
+
annotations: [
|
|
765
|
+
{
|
|
766
|
+
id: 'efficient-label',
|
|
767
|
+
text: 'Q* = ${Q_efficient:.1f}',
|
|
768
|
+
x: 'Q_efficient + chartMaxQ * 0.05',
|
|
769
|
+
y: 'MD + chartMaxP * 0.1',
|
|
770
|
+
color: '#16A34A',
|
|
771
|
+
},
|
|
772
|
+
{
|
|
773
|
+
id: 'scenario1',
|
|
774
|
+
text: 'If victim has rights: Polluter pays to pollute',
|
|
775
|
+
x: 'chartMaxQ * 0.5',
|
|
776
|
+
y: 'chartMaxP * 0.9',
|
|
777
|
+
color: '#7C3AED',
|
|
778
|
+
},
|
|
779
|
+
{
|
|
780
|
+
id: 'scenario2',
|
|
781
|
+
text: 'If polluter has rights: Victim pays to reduce',
|
|
782
|
+
x: 'chartMaxQ * 0.5',
|
|
783
|
+
y: 'chartMaxP * 0.82',
|
|
784
|
+
color: '#F59E0B',
|
|
785
|
+
},
|
|
786
|
+
{
|
|
787
|
+
id: 'result',
|
|
788
|
+
text: 'Same efficient Q* either way!',
|
|
789
|
+
x: 'chartMaxQ * 0.5',
|
|
790
|
+
y: 'chartMaxP * 0.74',
|
|
791
|
+
color: '#16A34A',
|
|
792
|
+
},
|
|
793
|
+
],
|
|
794
|
+
},
|
|
795
|
+
],
|
|
796
|
+
};
|
|
797
|
+
},
|
|
798
|
+
};
|
|
799
|
+
/**
|
|
800
|
+
* Public Goods
|
|
801
|
+
*
|
|
802
|
+
* Non-rivalrous and non-excludable goods. Shows vertical summation of
|
|
803
|
+
* demand curves and the free-rider problem.
|
|
804
|
+
*/
|
|
805
|
+
const publicGoods = {
|
|
806
|
+
id: 'public_goods',
|
|
807
|
+
name: 'Public Goods',
|
|
808
|
+
description: 'Non-rivalrous, non-excludable goods. Social demand is vertical sum of individual demands. Free-rider problem leads to underprovision.',
|
|
809
|
+
category: 'market_structures',
|
|
810
|
+
level: 'undergraduate',
|
|
811
|
+
tags: ['public goods', 'non-rivalry', 'non-excludability', 'free rider', 'vertical summation'],
|
|
812
|
+
parameters: {
|
|
813
|
+
person1Intercept: {
|
|
814
|
+
type: 'number',
|
|
815
|
+
default: 50,
|
|
816
|
+
description: 'Person 1 WTP intercept',
|
|
817
|
+
},
|
|
818
|
+
person2Intercept: {
|
|
819
|
+
type: 'number',
|
|
820
|
+
default: 40,
|
|
821
|
+
description: 'Person 2 WTP intercept',
|
|
822
|
+
},
|
|
823
|
+
demandSlope: {
|
|
824
|
+
type: 'number',
|
|
825
|
+
default: 1,
|
|
826
|
+
description: 'Common demand slope',
|
|
827
|
+
},
|
|
828
|
+
marginalCost: {
|
|
829
|
+
type: 'number',
|
|
830
|
+
default: 30,
|
|
831
|
+
description: 'Marginal cost of provision',
|
|
832
|
+
},
|
|
833
|
+
},
|
|
834
|
+
generate: (params) => {
|
|
835
|
+
const a1 = params.person1Intercept ?? 50;
|
|
836
|
+
const a2 = params.person2Intercept ?? 40;
|
|
837
|
+
const b = params.demandSlope ?? 1;
|
|
838
|
+
const MC = params.marginalCost ?? 30;
|
|
839
|
+
return {
|
|
840
|
+
metadata: {
|
|
841
|
+
specVersion: '1.3',
|
|
842
|
+
title: 'Public Goods',
|
|
843
|
+
description: 'Vertical summation of demand',
|
|
844
|
+
},
|
|
845
|
+
parameters: {
|
|
846
|
+
a1: { value: a1, label: 'Person 1 WTP', min: 20, max: 80 },
|
|
847
|
+
a2: { value: a2, label: 'Person 2 WTP', min: 20, max: 80 },
|
|
848
|
+
b: { value: b, label: 'Demand Slope', min: 0.5, max: 2, step: 0.1 },
|
|
849
|
+
MC: { value: MC, label: 'Marginal Cost', min: 10, max: 60 },
|
|
850
|
+
// Social demand (vertical sum): P = (a1 + a2) - 2bQ
|
|
851
|
+
socialIntercept: { expression: 'a1 + a2', hidden: true },
|
|
852
|
+
socialSlope: { expression: '2 * b', hidden: true },
|
|
853
|
+
// Optimal provision: Social MB = MC
|
|
854
|
+
Q_optimal: {
|
|
855
|
+
expression: '(a1 + a2 - MC) / (2 * b)',
|
|
856
|
+
label: 'Q* (optimal)',
|
|
857
|
+
readonly: true,
|
|
858
|
+
},
|
|
859
|
+
// Individual WTP at optimal Q
|
|
860
|
+
WTP1_opt: { expression: 'a1 - b * Q_optimal', label: 'WTP₁ at Q*', readonly: true },
|
|
861
|
+
WTP2_opt: { expression: 'a2 - b * Q_optimal', label: 'WTP₂ at Q*', readonly: true },
|
|
862
|
+
// Market provision (if private good)
|
|
863
|
+
Q_private: { expression: '(a1 - MC) / b', hidden: true },
|
|
864
|
+
// Chart bounds
|
|
865
|
+
chartMaxQ: { expression: 'max(a1, a2) / b * 1.2', hidden: true },
|
|
866
|
+
chartMaxP: { expression: 'socialIntercept * 1.1', hidden: true },
|
|
867
|
+
},
|
|
868
|
+
charts: [
|
|
869
|
+
{
|
|
870
|
+
id: 'main',
|
|
871
|
+
title: 'Public Good: Vertical Summation',
|
|
872
|
+
xAxis: { label: 'Quantity of Public Good', min: 0, max: 'chartMaxQ' },
|
|
873
|
+
yAxis: { label: 'WTP, Cost', min: 0, max: 'chartMaxP' },
|
|
874
|
+
elements: [
|
|
875
|
+
// Person 1 demand
|
|
876
|
+
{
|
|
877
|
+
id: 'd1',
|
|
878
|
+
type: 'line',
|
|
879
|
+
equation: 'a1 - b * x',
|
|
880
|
+
color: '#3B82F6',
|
|
881
|
+
strokeWidth: 2,
|
|
882
|
+
label: 'D₁ (Person 1)',
|
|
883
|
+
domain: { min: 0, max: 'a1 / b' },
|
|
884
|
+
},
|
|
885
|
+
// Person 2 demand
|
|
886
|
+
{
|
|
887
|
+
id: 'd2',
|
|
888
|
+
type: 'line',
|
|
889
|
+
equation: 'a2 - b * x',
|
|
890
|
+
color: '#F59E0B',
|
|
891
|
+
strokeWidth: 2,
|
|
892
|
+
label: 'D₂ (Person 2)',
|
|
893
|
+
domain: { min: 0, max: 'a2 / b' },
|
|
894
|
+
},
|
|
895
|
+
// Social demand (vertical sum)
|
|
896
|
+
{
|
|
897
|
+
id: 'social-demand',
|
|
898
|
+
type: 'line',
|
|
899
|
+
equation: 'socialIntercept - socialSlope * x',
|
|
900
|
+
color: '#16A34A',
|
|
901
|
+
strokeWidth: 3,
|
|
902
|
+
label: 'D_Social (D₁ + D₂)',
|
|
903
|
+
domain: { min: 0, max: 'socialIntercept / socialSlope' },
|
|
904
|
+
},
|
|
905
|
+
// MC
|
|
906
|
+
{
|
|
907
|
+
id: 'mc',
|
|
908
|
+
type: 'horizontalLine',
|
|
909
|
+
y: 'MC',
|
|
910
|
+
color: '#DC2626',
|
|
911
|
+
strokeWidth: 3,
|
|
912
|
+
label: 'MC',
|
|
913
|
+
},
|
|
914
|
+
// Optimal point
|
|
915
|
+
{
|
|
916
|
+
id: 'optimal',
|
|
917
|
+
type: 'point',
|
|
918
|
+
x: 'Q_optimal',
|
|
919
|
+
y: 'MC',
|
|
920
|
+
color: '#16A34A',
|
|
921
|
+
size: 12,
|
|
922
|
+
label: 'Optimal',
|
|
923
|
+
droplines: { x: true },
|
|
924
|
+
},
|
|
925
|
+
// Individual WTP contributions
|
|
926
|
+
{
|
|
927
|
+
id: 'wtp1-point',
|
|
928
|
+
type: 'point',
|
|
929
|
+
x: 'Q_optimal',
|
|
930
|
+
y: 'WTP1_opt',
|
|
931
|
+
color: '#3B82F6',
|
|
932
|
+
size: 6,
|
|
933
|
+
},
|
|
934
|
+
{
|
|
935
|
+
id: 'wtp2-point',
|
|
936
|
+
type: 'point',
|
|
937
|
+
x: 'Q_optimal',
|
|
938
|
+
y: 'WTP2_opt',
|
|
939
|
+
color: '#F59E0B',
|
|
940
|
+
size: 6,
|
|
941
|
+
},
|
|
942
|
+
// Vertical stacking at Q*
|
|
943
|
+
{
|
|
944
|
+
id: 'stack-line',
|
|
945
|
+
type: 'verticalLine',
|
|
946
|
+
x: 'Q_optimal',
|
|
947
|
+
color: '#6B7280',
|
|
948
|
+
lineStyle: 'dashed',
|
|
949
|
+
domain: { min: 0, max: 'MC' },
|
|
950
|
+
},
|
|
951
|
+
],
|
|
952
|
+
annotations: [
|
|
953
|
+
{
|
|
954
|
+
id: 'sum-label',
|
|
955
|
+
text: 'WTP₁ + WTP₂ = MC',
|
|
956
|
+
x: 'Q_optimal + chartMaxQ * 0.1',
|
|
957
|
+
y: 'MC',
|
|
958
|
+
color: '#16A34A',
|
|
959
|
+
},
|
|
960
|
+
{
|
|
961
|
+
id: 'wtp1-label',
|
|
962
|
+
text: 'WTP₁ = $${WTP1_opt:.1f}',
|
|
963
|
+
x: 'Q_optimal + chartMaxQ * 0.1',
|
|
964
|
+
y: 'WTP1_opt',
|
|
965
|
+
color: '#3B82F6',
|
|
966
|
+
},
|
|
967
|
+
{
|
|
968
|
+
id: 'wtp2-label',
|
|
969
|
+
text: 'WTP₂ = $${WTP2_opt:.1f}',
|
|
970
|
+
x: 'Q_optimal + chartMaxQ * 0.1',
|
|
971
|
+
y: 'WTP2_opt',
|
|
972
|
+
color: '#F59E0B',
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
id: 'free-rider',
|
|
976
|
+
text: 'Free-rider problem: Each hopes other pays',
|
|
977
|
+
x: 'chartMaxQ * 0.5',
|
|
978
|
+
y: 'chartMaxP * 0.15',
|
|
979
|
+
color: '#DC2626',
|
|
980
|
+
},
|
|
981
|
+
],
|
|
982
|
+
},
|
|
983
|
+
],
|
|
984
|
+
};
|
|
985
|
+
},
|
|
986
|
+
};
|
|
987
|
+
// ============================================================================
|
|
988
|
+
// EXPORTS
|
|
989
|
+
// ============================================================================
|
|
990
|
+
export const externalitiesTemplates = [
|
|
991
|
+
negativeExternality,
|
|
992
|
+
positiveExternality,
|
|
993
|
+
pigouvianTax,
|
|
994
|
+
coaseTheorem,
|
|
995
|
+
publicGoods,
|
|
996
|
+
];
|
|
997
|
+
//# sourceMappingURL=externalities.js.map
|