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,1135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Market Structures Templates
|
|
3
|
+
*
|
|
4
|
+
* Templates for market structure models including perfect competition,
|
|
5
|
+
* monopoly, and price discrimination.
|
|
6
|
+
*
|
|
7
|
+
* Phase 3.4: Market Structures - Perfect Competition & Monopoly
|
|
8
|
+
*/
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// PHASE 3.4: MARKET STRUCTURES
|
|
11
|
+
// ============================================================================
|
|
12
|
+
/**
|
|
13
|
+
* Perfect Competition - Short Run
|
|
14
|
+
*
|
|
15
|
+
* Shows individual firm facing horizontal demand at market price,
|
|
16
|
+
* profit maximization where P = MC, and short-run profit/loss.
|
|
17
|
+
*/
|
|
18
|
+
const perfectCompetitionShortRun = {
|
|
19
|
+
id: 'perfect_competition_short_run',
|
|
20
|
+
name: 'Perfect Competition - Short Run',
|
|
21
|
+
description: 'Price-taking firm in perfect competition. Firm faces horizontal demand at market price and produces where P = MC. Shows profit/loss as shaded area.',
|
|
22
|
+
category: 'market_structures',
|
|
23
|
+
level: 'undergraduate',
|
|
24
|
+
tags: ['perfect competition', 'price taker', 'profit maximization', 'short run'],
|
|
25
|
+
parameters: {
|
|
26
|
+
marketPrice: {
|
|
27
|
+
type: 'number',
|
|
28
|
+
default: 12,
|
|
29
|
+
min: 5,
|
|
30
|
+
max: 25,
|
|
31
|
+
description: 'Market price (exogenous to firm)',
|
|
32
|
+
},
|
|
33
|
+
fixedCost: {
|
|
34
|
+
type: 'number',
|
|
35
|
+
default: 20,
|
|
36
|
+
min: 0,
|
|
37
|
+
max: 100,
|
|
38
|
+
description: 'Total fixed cost (TFC)',
|
|
39
|
+
},
|
|
40
|
+
mcIntercept: {
|
|
41
|
+
type: 'number',
|
|
42
|
+
default: 2,
|
|
43
|
+
min: 0,
|
|
44
|
+
max: 10,
|
|
45
|
+
description: 'MC curve intercept',
|
|
46
|
+
},
|
|
47
|
+
mcSlope: {
|
|
48
|
+
type: 'number',
|
|
49
|
+
default: 0.5,
|
|
50
|
+
min: 0.1,
|
|
51
|
+
max: 2,
|
|
52
|
+
description: 'MC curve slope',
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
generate: (params) => {
|
|
56
|
+
const P = params.marketPrice ?? 12;
|
|
57
|
+
const TFC = params.fixedCost ?? 20;
|
|
58
|
+
const mc0 = params.mcIntercept ?? 2;
|
|
59
|
+
const mcSlope = params.mcSlope ?? 0.5;
|
|
60
|
+
return {
|
|
61
|
+
metadata: {
|
|
62
|
+
specVersion: '1.3',
|
|
63
|
+
title: 'Perfect Competition - Short Run',
|
|
64
|
+
description: 'Profit maximization: P = MC',
|
|
65
|
+
},
|
|
66
|
+
parameters: {
|
|
67
|
+
P: { value: P, label: 'Market Price', min: 5, max: 25 },
|
|
68
|
+
TFC: { value: TFC, label: 'Fixed Cost', min: 0, max: 100 },
|
|
69
|
+
mc0: { value: mc0, label: 'MC intercept', min: 0, max: 10 },
|
|
70
|
+
mcSlope: { value: mcSlope, label: 'MC slope', min: 0.1, max: 2, step: 0.1 },
|
|
71
|
+
// Optimal quantity where P = MC: P = mc0 + mcSlope * Q => Q = (P - mc0) / mcSlope
|
|
72
|
+
Q_star: {
|
|
73
|
+
expression: 'max(0, (P - mc0) / mcSlope)',
|
|
74
|
+
label: 'Q*',
|
|
75
|
+
readonly: true,
|
|
76
|
+
},
|
|
77
|
+
// AVC = mc0 + (mcSlope/2) * Q (integral of MC)
|
|
78
|
+
AVC_star: {
|
|
79
|
+
expression: 'mc0 + (mcSlope / 2) * Q_star',
|
|
80
|
+
label: 'AVC*',
|
|
81
|
+
readonly: true,
|
|
82
|
+
},
|
|
83
|
+
// ATC = AVC + AFC = AVC + TFC/Q
|
|
84
|
+
ATC_star: {
|
|
85
|
+
expression: 'AVC_star + TFC / max(1, Q_star)',
|
|
86
|
+
label: 'ATC*',
|
|
87
|
+
readonly: true,
|
|
88
|
+
},
|
|
89
|
+
// Total Revenue
|
|
90
|
+
TR: { expression: 'P * Q_star', label: 'Total Revenue', readonly: true },
|
|
91
|
+
// Total Cost = TFC + TVC where TVC = integral of MC
|
|
92
|
+
TC: {
|
|
93
|
+
expression: 'TFC + mc0 * Q_star + (mcSlope / 2) * Q_star * Q_star',
|
|
94
|
+
label: 'Total Cost',
|
|
95
|
+
readonly: true,
|
|
96
|
+
},
|
|
97
|
+
// Profit
|
|
98
|
+
profit: { expression: 'TR - TC', label: 'Profit', readonly: true },
|
|
99
|
+
// Chart bounds
|
|
100
|
+
chartMaxQ: { expression: 'Q_star * 2', hidden: true },
|
|
101
|
+
chartMaxP: { expression: 'max(P, ATC_star) * 1.5', hidden: true },
|
|
102
|
+
// Shutdown point (P = min AVC)
|
|
103
|
+
shutdownP: { expression: 'mc0', label: 'Shutdown Price', readonly: true },
|
|
104
|
+
},
|
|
105
|
+
charts: [
|
|
106
|
+
{
|
|
107
|
+
id: 'firm',
|
|
108
|
+
title: 'Individual Firm',
|
|
109
|
+
xAxis: { label: 'Quantity (q)', min: 0, max: 'chartMaxQ' },
|
|
110
|
+
yAxis: { label: 'Price, Cost', min: 0, max: 'chartMaxP' },
|
|
111
|
+
elements: [
|
|
112
|
+
// Profit/Loss area
|
|
113
|
+
{
|
|
114
|
+
id: 'profit-area',
|
|
115
|
+
type: 'rectangle',
|
|
116
|
+
x1: 0,
|
|
117
|
+
y1: 'ATC_star',
|
|
118
|
+
x2: 'Q_star',
|
|
119
|
+
y2: 'P',
|
|
120
|
+
fill: 'profit > 0 ? "#22C55E" : "#EF4444"',
|
|
121
|
+
opacity: 0.3,
|
|
122
|
+
label: 'profit > 0 ? "Profit" : "Loss"',
|
|
123
|
+
},
|
|
124
|
+
// Demand = MR = AR = P (horizontal line)
|
|
125
|
+
{
|
|
126
|
+
id: 'demand',
|
|
127
|
+
type: 'horizontalLine',
|
|
128
|
+
y: 'P',
|
|
129
|
+
color: '#2563EB',
|
|
130
|
+
strokeWidth: 3,
|
|
131
|
+
label: 'D = MR = AR = P',
|
|
132
|
+
},
|
|
133
|
+
// MC curve
|
|
134
|
+
{
|
|
135
|
+
id: 'mc',
|
|
136
|
+
type: 'line',
|
|
137
|
+
equation: 'mc0 + mcSlope * x',
|
|
138
|
+
color: '#DC2626',
|
|
139
|
+
strokeWidth: 3,
|
|
140
|
+
label: 'MC',
|
|
141
|
+
},
|
|
142
|
+
// AVC curve: AVC = mc0 + (mcSlope/2) * Q
|
|
143
|
+
{
|
|
144
|
+
id: 'avc',
|
|
145
|
+
type: 'line',
|
|
146
|
+
equation: 'mc0 + (mcSlope / 2) * x',
|
|
147
|
+
color: '#F59E0B',
|
|
148
|
+
strokeWidth: 2,
|
|
149
|
+
label: 'AVC',
|
|
150
|
+
domain: { min: 0.5, max: 'chartMaxQ' },
|
|
151
|
+
},
|
|
152
|
+
// ATC curve: ATC = TFC/Q + AVC
|
|
153
|
+
{
|
|
154
|
+
id: 'atc',
|
|
155
|
+
type: 'line',
|
|
156
|
+
equation: 'TFC / x + mc0 + (mcSlope / 2) * x',
|
|
157
|
+
color: '#16A34A',
|
|
158
|
+
strokeWidth: 2,
|
|
159
|
+
label: 'ATC',
|
|
160
|
+
domain: { min: 1, max: 'chartMaxQ' },
|
|
161
|
+
},
|
|
162
|
+
// Optimal point
|
|
163
|
+
{
|
|
164
|
+
id: 'optimum',
|
|
165
|
+
type: 'point',
|
|
166
|
+
x: 'Q_star',
|
|
167
|
+
y: 'P',
|
|
168
|
+
color: '#2563EB',
|
|
169
|
+
size: 8,
|
|
170
|
+
label: 'P = MC',
|
|
171
|
+
droplines: { x: true, y: false },
|
|
172
|
+
},
|
|
173
|
+
// ATC at optimum
|
|
174
|
+
{
|
|
175
|
+
id: 'atc-point',
|
|
176
|
+
type: 'point',
|
|
177
|
+
x: 'Q_star',
|
|
178
|
+
y: 'ATC_star',
|
|
179
|
+
color: '#16A34A',
|
|
180
|
+
droplines: { y: true },
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
annotations: [
|
|
184
|
+
{
|
|
185
|
+
id: 'q-label',
|
|
186
|
+
text: 'Q* = ${Q_star:.1f}',
|
|
187
|
+
x: 'Q_star',
|
|
188
|
+
y: 'chartMaxP * 0.1',
|
|
189
|
+
color: '#2563EB',
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
id: 'profit-label',
|
|
193
|
+
text: 'π = $${profit:.2f}',
|
|
194
|
+
x: 'Q_star / 2',
|
|
195
|
+
y: '(P + ATC_star) / 2',
|
|
196
|
+
color: 'profit > 0 ? "#16A34A" : "#DC2626"',
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
};
|
|
202
|
+
},
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* Perfect Competition - Long Run
|
|
206
|
+
*
|
|
207
|
+
* Shows long-run equilibrium with zero economic profit,
|
|
208
|
+
* where P = minimum ATC and firms produce at efficient scale.
|
|
209
|
+
*/
|
|
210
|
+
const perfectCompetitionLongRun = {
|
|
211
|
+
id: 'perfect_competition_long_run',
|
|
212
|
+
name: 'Perfect Competition - Long Run',
|
|
213
|
+
description: 'Long-run equilibrium with free entry/exit. Price equals minimum ATC, economic profit is zero, and firms produce at efficient scale.',
|
|
214
|
+
category: 'market_structures',
|
|
215
|
+
level: 'undergraduate',
|
|
216
|
+
tags: ['perfect competition', 'long run', 'zero profit', 'efficient scale'],
|
|
217
|
+
parameters: {
|
|
218
|
+
minATC: {
|
|
219
|
+
type: 'number',
|
|
220
|
+
default: 10,
|
|
221
|
+
min: 5,
|
|
222
|
+
max: 20,
|
|
223
|
+
description: 'Minimum ATC (long-run equilibrium price)',
|
|
224
|
+
},
|
|
225
|
+
efficientScale: {
|
|
226
|
+
type: 'number',
|
|
227
|
+
default: 10,
|
|
228
|
+
min: 5,
|
|
229
|
+
max: 20,
|
|
230
|
+
description: 'Efficient scale (output at min ATC)',
|
|
231
|
+
},
|
|
232
|
+
atcCurvature: {
|
|
233
|
+
type: 'number',
|
|
234
|
+
default: 0.1,
|
|
235
|
+
min: 0.05,
|
|
236
|
+
max: 0.3,
|
|
237
|
+
description: 'ATC curve curvature parameter',
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
generate: (params) => {
|
|
241
|
+
const minATC = params.minATC ?? 10;
|
|
242
|
+
const Q_eff = params.efficientScale ?? 10;
|
|
243
|
+
const a = params.atcCurvature ?? 0.1;
|
|
244
|
+
return {
|
|
245
|
+
metadata: {
|
|
246
|
+
specVersion: '1.3',
|
|
247
|
+
title: 'Perfect Competition - Long Run',
|
|
248
|
+
description: 'Zero profit equilibrium',
|
|
249
|
+
},
|
|
250
|
+
parameters: {
|
|
251
|
+
minATC: { value: minATC, label: 'Min ATC (P*)', min: 5, max: 20 },
|
|
252
|
+
Q_eff: { value: Q_eff, label: 'Efficient Scale', min: 5, max: 20 },
|
|
253
|
+
a: { value: a, label: 'ATC Curvature', min: 0.05, max: 0.3, step: 0.01 },
|
|
254
|
+
// Long-run price = min ATC
|
|
255
|
+
P_lr: { expression: 'minATC', label: 'LR Price', readonly: true },
|
|
256
|
+
// ATC at efficient scale
|
|
257
|
+
ATC_eff: { expression: 'minATC', label: 'ATC at Q*', readonly: true },
|
|
258
|
+
// Chart bounds
|
|
259
|
+
chartMaxQ: { expression: 'Q_eff * 2.5', hidden: true },
|
|
260
|
+
chartMaxP: { expression: 'minATC * 2', hidden: true },
|
|
261
|
+
// Profit at LR equilibrium
|
|
262
|
+
profit: { value: 0, label: 'Economic Profit', readonly: true },
|
|
263
|
+
},
|
|
264
|
+
charts: [
|
|
265
|
+
{
|
|
266
|
+
id: 'firm',
|
|
267
|
+
title: 'Representative Firm - Long Run Equilibrium',
|
|
268
|
+
xAxis: { label: 'Quantity (q)', min: 0, max: 'chartMaxQ' },
|
|
269
|
+
yAxis: { label: 'Price, Cost', min: 0, max: 'chartMaxP' },
|
|
270
|
+
elements: [
|
|
271
|
+
// Demand = P (horizontal at min ATC)
|
|
272
|
+
{
|
|
273
|
+
id: 'demand',
|
|
274
|
+
type: 'horizontalLine',
|
|
275
|
+
y: 'P_lr',
|
|
276
|
+
color: '#2563EB',
|
|
277
|
+
strokeWidth: 3,
|
|
278
|
+
label: 'D = MR = P*',
|
|
279
|
+
},
|
|
280
|
+
// U-shaped ATC: ATC = minATC + a(Q - Q_eff)²
|
|
281
|
+
{
|
|
282
|
+
id: 'atc',
|
|
283
|
+
type: 'line',
|
|
284
|
+
equation: 'minATC + a * ((x - Q_eff) ^ 2)',
|
|
285
|
+
color: '#16A34A',
|
|
286
|
+
strokeWidth: 3,
|
|
287
|
+
label: 'LRAC',
|
|
288
|
+
domain: { min: 1, max: 'chartMaxQ' },
|
|
289
|
+
},
|
|
290
|
+
// MC curve (derivative of TC): MC = minATC + 2a(Q - Q_eff) + 2a*Q_eff
|
|
291
|
+
// Simplified: passes through min ATC at Q_eff
|
|
292
|
+
{
|
|
293
|
+
id: 'mc',
|
|
294
|
+
type: 'line',
|
|
295
|
+
equation: 'minATC + 2 * a * (x - Q_eff)',
|
|
296
|
+
color: '#DC2626',
|
|
297
|
+
strokeWidth: 3,
|
|
298
|
+
label: 'MC',
|
|
299
|
+
domain: { min: 1, max: 'chartMaxQ' },
|
|
300
|
+
},
|
|
301
|
+
// Efficient scale point
|
|
302
|
+
{
|
|
303
|
+
id: 'efficient-point',
|
|
304
|
+
type: 'point',
|
|
305
|
+
x: 'Q_eff',
|
|
306
|
+
y: 'minATC',
|
|
307
|
+
color: '#16A34A',
|
|
308
|
+
size: 10,
|
|
309
|
+
label: 'Efficient Scale',
|
|
310
|
+
droplines: { x: true, y: true },
|
|
311
|
+
},
|
|
312
|
+
// MC = ATC intersection
|
|
313
|
+
{
|
|
314
|
+
id: 'mc-atc-intersect',
|
|
315
|
+
type: 'point',
|
|
316
|
+
x: 'Q_eff',
|
|
317
|
+
y: 'minATC',
|
|
318
|
+
color: '#DC2626',
|
|
319
|
+
size: 6,
|
|
320
|
+
},
|
|
321
|
+
],
|
|
322
|
+
annotations: [
|
|
323
|
+
{
|
|
324
|
+
id: 'zero-profit',
|
|
325
|
+
text: 'Zero Economic Profit',
|
|
326
|
+
x: 'chartMaxQ * 0.6',
|
|
327
|
+
y: 'chartMaxP * 0.85',
|
|
328
|
+
color: '#6B7280',
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
id: 'equilibrium-label',
|
|
332
|
+
text: 'P* = min ATC = $${minATC}',
|
|
333
|
+
x: 'chartMaxQ * 0.6',
|
|
334
|
+
y: 'chartMaxP * 0.75',
|
|
335
|
+
color: '#2563EB',
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
id: 'scale-label',
|
|
339
|
+
text: 'Q* = ${Q_eff} (efficient scale)',
|
|
340
|
+
x: 'Q_eff + 1',
|
|
341
|
+
y: 'minATC * 0.7',
|
|
342
|
+
color: '#16A34A',
|
|
343
|
+
},
|
|
344
|
+
],
|
|
345
|
+
},
|
|
346
|
+
],
|
|
347
|
+
};
|
|
348
|
+
},
|
|
349
|
+
};
|
|
350
|
+
/**
|
|
351
|
+
* Monopoly - Basic
|
|
352
|
+
*
|
|
353
|
+
* Shows monopolist facing downward-sloping demand,
|
|
354
|
+
* MR curve with twice the slope, and profit maximization where MR = MC.
|
|
355
|
+
*/
|
|
356
|
+
const monopolyBasic = {
|
|
357
|
+
id: 'monopoly_basic',
|
|
358
|
+
name: 'Monopoly - Profit Maximization',
|
|
359
|
+
description: 'Monopolist profit maximization where MR = MC. Shows downward-sloping demand, MR with twice the slope, optimal quantity and price, and monopoly profit.',
|
|
360
|
+
category: 'market_structures',
|
|
361
|
+
level: 'undergraduate',
|
|
362
|
+
tags: ['monopoly', 'MR=MC', 'market power', 'profit maximization'],
|
|
363
|
+
parameters: {
|
|
364
|
+
demandIntercept: {
|
|
365
|
+
type: 'number',
|
|
366
|
+
default: 100,
|
|
367
|
+
min: 50,
|
|
368
|
+
max: 200,
|
|
369
|
+
description: 'Demand curve intercept (choke price)',
|
|
370
|
+
},
|
|
371
|
+
demandSlope: {
|
|
372
|
+
type: 'number',
|
|
373
|
+
default: 2,
|
|
374
|
+
min: 0.5,
|
|
375
|
+
max: 5,
|
|
376
|
+
description: 'Demand curve slope',
|
|
377
|
+
},
|
|
378
|
+
mcConstant: {
|
|
379
|
+
type: 'number',
|
|
380
|
+
default: 20,
|
|
381
|
+
min: 5,
|
|
382
|
+
max: 50,
|
|
383
|
+
description: 'Marginal cost (constant)',
|
|
384
|
+
},
|
|
385
|
+
},
|
|
386
|
+
generate: (params) => {
|
|
387
|
+
const a = params.demandIntercept ?? 100;
|
|
388
|
+
const b = params.demandSlope ?? 2;
|
|
389
|
+
const MC = params.mcConstant ?? 20;
|
|
390
|
+
return {
|
|
391
|
+
metadata: {
|
|
392
|
+
specVersion: '1.3',
|
|
393
|
+
title: 'Monopoly Profit Maximization',
|
|
394
|
+
description: 'MR = MC optimization',
|
|
395
|
+
},
|
|
396
|
+
parameters: {
|
|
397
|
+
a: { value: a, label: 'Demand Intercept', min: 50, max: 200 },
|
|
398
|
+
b: { value: b, label: 'Demand Slope', min: 0.5, max: 5, step: 0.1 },
|
|
399
|
+
MC: { value: MC, label: 'Marginal Cost', min: 5, max: 50 },
|
|
400
|
+
// Demand: P = a - bQ, MR = a - 2bQ
|
|
401
|
+
// Optimal Q where MR = MC: a - 2bQ = MC => Q* = (a - MC) / (2b)
|
|
402
|
+
Q_m: { expression: '(a - MC) / (2 * b)', label: 'Q_m', readonly: true },
|
|
403
|
+
// Monopoly price: P = a - bQ*
|
|
404
|
+
P_m: { expression: 'a - b * Q_m', label: 'P_m', readonly: true },
|
|
405
|
+
// MR at optimal quantity
|
|
406
|
+
MR_star: { expression: 'a - 2 * b * Q_m', label: 'MR*', readonly: true },
|
|
407
|
+
// Revenue and cost
|
|
408
|
+
TR: { expression: 'P_m * Q_m', label: 'Total Revenue', readonly: true },
|
|
409
|
+
TC: { expression: 'MC * Q_m', label: 'Total Cost', readonly: true },
|
|
410
|
+
profit: { expression: 'TR - TC', label: 'Profit', readonly: true },
|
|
411
|
+
// Competitive outcome for comparison
|
|
412
|
+
Q_c: { expression: '(a - MC) / b', hidden: true },
|
|
413
|
+
P_c: { expression: 'MC', hidden: true },
|
|
414
|
+
// Markup
|
|
415
|
+
markup: { expression: '(P_m - MC) / P_m', label: 'Lerner Index', readonly: true },
|
|
416
|
+
// Chart bounds
|
|
417
|
+
chartMaxQ: { expression: 'a / b * 1.1', hidden: true },
|
|
418
|
+
chartMaxP: { expression: 'a * 1.1', hidden: true },
|
|
419
|
+
},
|
|
420
|
+
charts: [
|
|
421
|
+
{
|
|
422
|
+
id: 'main',
|
|
423
|
+
title: 'Monopoly: MR = MC',
|
|
424
|
+
xAxis: { label: 'Quantity (Q)', min: 0, max: 'chartMaxQ' },
|
|
425
|
+
yAxis: { label: 'Price, Cost', min: 0, max: 'chartMaxP' },
|
|
426
|
+
elements: [
|
|
427
|
+
// Profit rectangle
|
|
428
|
+
{
|
|
429
|
+
id: 'profit-area',
|
|
430
|
+
type: 'rectangle',
|
|
431
|
+
x1: 0,
|
|
432
|
+
y1: 'MC',
|
|
433
|
+
x2: 'Q_m',
|
|
434
|
+
y2: 'P_m',
|
|
435
|
+
fill: '#22C55E',
|
|
436
|
+
opacity: 0.3,
|
|
437
|
+
label: 'Monopoly Profit',
|
|
438
|
+
},
|
|
439
|
+
// Demand curve
|
|
440
|
+
{
|
|
441
|
+
id: 'demand',
|
|
442
|
+
type: 'line',
|
|
443
|
+
equation: 'a - b * x',
|
|
444
|
+
color: '#2563EB',
|
|
445
|
+
strokeWidth: 3,
|
|
446
|
+
label: 'Demand (AR)',
|
|
447
|
+
domain: { min: 0, max: 'a / b' },
|
|
448
|
+
},
|
|
449
|
+
// MR curve (twice the slope)
|
|
450
|
+
{
|
|
451
|
+
id: 'mr',
|
|
452
|
+
type: 'line',
|
|
453
|
+
equation: 'a - 2 * b * x',
|
|
454
|
+
color: '#7C3AED',
|
|
455
|
+
strokeWidth: 3,
|
|
456
|
+
label: 'MR',
|
|
457
|
+
domain: { min: 0, max: 'a / (2 * b)' },
|
|
458
|
+
},
|
|
459
|
+
// MC curve (horizontal)
|
|
460
|
+
{
|
|
461
|
+
id: 'mc',
|
|
462
|
+
type: 'horizontalLine',
|
|
463
|
+
y: 'MC',
|
|
464
|
+
color: '#DC2626',
|
|
465
|
+
strokeWidth: 3,
|
|
466
|
+
label: 'MC',
|
|
467
|
+
},
|
|
468
|
+
// Optimal quantity point on MR
|
|
469
|
+
{
|
|
470
|
+
id: 'mr-mc-intersect',
|
|
471
|
+
type: 'point',
|
|
472
|
+
x: 'Q_m',
|
|
473
|
+
y: 'MC',
|
|
474
|
+
color: '#7C3AED',
|
|
475
|
+
size: 8,
|
|
476
|
+
label: 'MR = MC',
|
|
477
|
+
},
|
|
478
|
+
// Price point on demand
|
|
479
|
+
{
|
|
480
|
+
id: 'price-point',
|
|
481
|
+
type: 'point',
|
|
482
|
+
x: 'Q_m',
|
|
483
|
+
y: 'P_m',
|
|
484
|
+
color: '#2563EB',
|
|
485
|
+
size: 8,
|
|
486
|
+
label: 'P_m',
|
|
487
|
+
droplines: { x: true, y: true },
|
|
488
|
+
},
|
|
489
|
+
// Vertical line from Q_m to demand
|
|
490
|
+
{
|
|
491
|
+
id: 'q-line',
|
|
492
|
+
type: 'verticalLine',
|
|
493
|
+
x: 'Q_m',
|
|
494
|
+
color: '#6B7280',
|
|
495
|
+
lineStyle: 'dashed',
|
|
496
|
+
domain: { min: 0, max: 'P_m' },
|
|
497
|
+
},
|
|
498
|
+
],
|
|
499
|
+
annotations: [
|
|
500
|
+
{
|
|
501
|
+
id: 'q-label',
|
|
502
|
+
text: 'Q_m = ${Q_m:.1f}',
|
|
503
|
+
x: 'Q_m + chartMaxQ * 0.05',
|
|
504
|
+
y: 'chartMaxP * 0.15',
|
|
505
|
+
color: '#7C3AED',
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
id: 'p-label',
|
|
509
|
+
text: 'P_m = $${P_m:.1f}',
|
|
510
|
+
x: 'chartMaxQ * 0.05',
|
|
511
|
+
y: 'P_m + chartMaxP * 0.03',
|
|
512
|
+
color: '#2563EB',
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
id: 'profit-label',
|
|
516
|
+
text: 'π = $${profit:.0f}',
|
|
517
|
+
x: 'Q_m / 2',
|
|
518
|
+
y: '(P_m + MC) / 2',
|
|
519
|
+
color: '#16A34A',
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
id: 'markup-label',
|
|
523
|
+
text: 'Lerner Index = ${markup:.2f}',
|
|
524
|
+
x: 'chartMaxQ * 0.6',
|
|
525
|
+
y: 'chartMaxP * 0.9',
|
|
526
|
+
color: '#6B7280',
|
|
527
|
+
},
|
|
528
|
+
],
|
|
529
|
+
},
|
|
530
|
+
],
|
|
531
|
+
};
|
|
532
|
+
},
|
|
533
|
+
};
|
|
534
|
+
/**
|
|
535
|
+
* Monopoly - Deadweight Loss
|
|
536
|
+
*
|
|
537
|
+
* Compares monopoly outcome with competitive outcome,
|
|
538
|
+
* showing the deadweight loss from monopoly pricing.
|
|
539
|
+
*/
|
|
540
|
+
const monopolyDeadweightLoss = {
|
|
541
|
+
id: 'monopoly_deadweight_loss',
|
|
542
|
+
name: 'Monopoly - Deadweight Loss',
|
|
543
|
+
description: 'Welfare analysis comparing monopoly to perfect competition. Shows deadweight loss triangle, consumer surplus, and producer surplus under both market structures.',
|
|
544
|
+
category: 'market_structures',
|
|
545
|
+
level: 'undergraduate',
|
|
546
|
+
tags: ['monopoly', 'deadweight loss', 'welfare', 'efficiency', 'consumer surplus'],
|
|
547
|
+
parameters: {
|
|
548
|
+
demandIntercept: {
|
|
549
|
+
type: 'number',
|
|
550
|
+
default: 100,
|
|
551
|
+
description: 'Demand intercept',
|
|
552
|
+
},
|
|
553
|
+
demandSlope: {
|
|
554
|
+
type: 'number',
|
|
555
|
+
default: 2,
|
|
556
|
+
description: 'Demand slope',
|
|
557
|
+
},
|
|
558
|
+
marginalCost: {
|
|
559
|
+
type: 'number',
|
|
560
|
+
default: 20,
|
|
561
|
+
description: 'Constant marginal cost',
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
generate: (params) => {
|
|
565
|
+
const a = params.demandIntercept ?? 100;
|
|
566
|
+
const b = params.demandSlope ?? 2;
|
|
567
|
+
const MC = params.marginalCost ?? 20;
|
|
568
|
+
return {
|
|
569
|
+
metadata: {
|
|
570
|
+
specVersion: '1.3',
|
|
571
|
+
title: 'Monopoly Deadweight Loss',
|
|
572
|
+
description: 'Welfare comparison with perfect competition',
|
|
573
|
+
},
|
|
574
|
+
parameters: {
|
|
575
|
+
a: { value: a, label: 'Demand Intercept', min: 50, max: 200 },
|
|
576
|
+
b: { value: b, label: 'Demand Slope', min: 0.5, max: 5, step: 0.1 },
|
|
577
|
+
MC: { value: MC, label: 'Marginal Cost', min: 5, max: 50 },
|
|
578
|
+
// Monopoly outcome
|
|
579
|
+
Q_m: { expression: '(a - MC) / (2 * b)', label: 'Q_m', readonly: true },
|
|
580
|
+
P_m: { expression: 'a - b * Q_m', label: 'P_m', readonly: true },
|
|
581
|
+
// Competitive outcome
|
|
582
|
+
Q_c: { expression: '(a - MC) / b', label: 'Q_c', readonly: true },
|
|
583
|
+
P_c: { expression: 'MC', label: 'P_c', readonly: true },
|
|
584
|
+
// Consumer Surplus under monopoly: (1/2)(a - P_m)(Q_m)
|
|
585
|
+
CS_m: { expression: '0.5 * (a - P_m) * Q_m', label: 'CS (monopoly)', readonly: true },
|
|
586
|
+
// Producer Surplus under monopoly: (P_m - MC) * Q_m
|
|
587
|
+
PS_m: { expression: '(P_m - MC) * Q_m', label: 'PS (monopoly)', readonly: true },
|
|
588
|
+
// Consumer Surplus under competition: (1/2)(a - MC)(Q_c)
|
|
589
|
+
CS_c: {
|
|
590
|
+
expression: '0.5 * (a - MC) * Q_c',
|
|
591
|
+
label: 'CS (competitive)',
|
|
592
|
+
readonly: true,
|
|
593
|
+
},
|
|
594
|
+
// Total Surplus comparison
|
|
595
|
+
TS_m: { expression: 'CS_m + PS_m', label: 'TS (monopoly)', readonly: true },
|
|
596
|
+
TS_c: { expression: 'CS_c', label: 'TS (competitive)', readonly: true },
|
|
597
|
+
// Deadweight Loss: (1/2)(P_m - MC)(Q_c - Q_m)
|
|
598
|
+
DWL: {
|
|
599
|
+
expression: '0.5 * (P_m - MC) * (Q_c - Q_m)',
|
|
600
|
+
label: 'Deadweight Loss',
|
|
601
|
+
readonly: true,
|
|
602
|
+
},
|
|
603
|
+
// Chart bounds
|
|
604
|
+
chartMaxQ: { expression: 'Q_c * 1.2', hidden: true },
|
|
605
|
+
chartMaxP: { expression: 'a * 1.1', hidden: true },
|
|
606
|
+
},
|
|
607
|
+
charts: [
|
|
608
|
+
{
|
|
609
|
+
id: 'main',
|
|
610
|
+
title: 'Monopoly vs Perfect Competition',
|
|
611
|
+
xAxis: { label: 'Quantity (Q)', min: 0, max: 'chartMaxQ' },
|
|
612
|
+
yAxis: { label: 'Price', min: 0, max: 'chartMaxP' },
|
|
613
|
+
elements: [
|
|
614
|
+
// Consumer Surplus under monopoly
|
|
615
|
+
{
|
|
616
|
+
id: 'cs-monopoly',
|
|
617
|
+
type: 'area',
|
|
618
|
+
topBoundary: 'a',
|
|
619
|
+
bottomBoundary: 'demand',
|
|
620
|
+
leftBoundary: '0',
|
|
621
|
+
rightBoundary: 'Q_m',
|
|
622
|
+
color: '#3B82F6',
|
|
623
|
+
opacity: 0.3,
|
|
624
|
+
label: 'CS',
|
|
625
|
+
},
|
|
626
|
+
// Producer Surplus under monopoly
|
|
627
|
+
{
|
|
628
|
+
id: 'ps-monopoly',
|
|
629
|
+
type: 'rectangle',
|
|
630
|
+
x1: 0,
|
|
631
|
+
y1: 'MC',
|
|
632
|
+
x2: 'Q_m',
|
|
633
|
+
y2: 'P_m',
|
|
634
|
+
fill: '#22C55E',
|
|
635
|
+
opacity: 0.3,
|
|
636
|
+
label: 'PS',
|
|
637
|
+
},
|
|
638
|
+
// Deadweight Loss triangle
|
|
639
|
+
{
|
|
640
|
+
id: 'dwl',
|
|
641
|
+
type: 'polygon',
|
|
642
|
+
points: [
|
|
643
|
+
{ x: 'Q_m', y: 'P_m' },
|
|
644
|
+
{ x: 'Q_m', y: 'MC' },
|
|
645
|
+
{ x: 'Q_c', y: 'MC' },
|
|
646
|
+
],
|
|
647
|
+
fill: '#EF4444',
|
|
648
|
+
opacity: 0.4,
|
|
649
|
+
label: 'DWL',
|
|
650
|
+
},
|
|
651
|
+
// Demand curve
|
|
652
|
+
{
|
|
653
|
+
id: 'demand',
|
|
654
|
+
type: 'line',
|
|
655
|
+
equation: 'a - b * x',
|
|
656
|
+
color: '#2563EB',
|
|
657
|
+
strokeWidth: 3,
|
|
658
|
+
label: 'Demand',
|
|
659
|
+
domain: { min: 0, max: 'a / b' },
|
|
660
|
+
},
|
|
661
|
+
// MR curve
|
|
662
|
+
{
|
|
663
|
+
id: 'mr',
|
|
664
|
+
type: 'line',
|
|
665
|
+
equation: 'a - 2 * b * x',
|
|
666
|
+
color: '#7C3AED',
|
|
667
|
+
strokeWidth: 2,
|
|
668
|
+
lineStyle: 'dashed',
|
|
669
|
+
label: 'MR',
|
|
670
|
+
domain: { min: 0, max: 'a / (2 * b)' },
|
|
671
|
+
},
|
|
672
|
+
// MC curve
|
|
673
|
+
{
|
|
674
|
+
id: 'mc',
|
|
675
|
+
type: 'horizontalLine',
|
|
676
|
+
y: 'MC',
|
|
677
|
+
color: '#DC2626',
|
|
678
|
+
strokeWidth: 3,
|
|
679
|
+
label: 'MC = P_c',
|
|
680
|
+
},
|
|
681
|
+
// Monopoly point
|
|
682
|
+
{
|
|
683
|
+
id: 'monopoly-point',
|
|
684
|
+
type: 'point',
|
|
685
|
+
x: 'Q_m',
|
|
686
|
+
y: 'P_m',
|
|
687
|
+
color: '#7C3AED',
|
|
688
|
+
size: 8,
|
|
689
|
+
label: 'Monopoly',
|
|
690
|
+
droplines: { x: true, y: true },
|
|
691
|
+
},
|
|
692
|
+
// Competitive point
|
|
693
|
+
{
|
|
694
|
+
id: 'competitive-point',
|
|
695
|
+
type: 'point',
|
|
696
|
+
x: 'Q_c',
|
|
697
|
+
y: 'P_c',
|
|
698
|
+
color: '#16A34A',
|
|
699
|
+
size: 8,
|
|
700
|
+
label: 'Competition',
|
|
701
|
+
droplines: { x: true, y: true },
|
|
702
|
+
},
|
|
703
|
+
],
|
|
704
|
+
annotations: [
|
|
705
|
+
{
|
|
706
|
+
id: 'dwl-label',
|
|
707
|
+
text: 'DWL = $${DWL:.0f}',
|
|
708
|
+
x: '(Q_m + Q_c) / 2',
|
|
709
|
+
y: '(P_m + MC) / 2',
|
|
710
|
+
color: '#DC2626',
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
id: 'comparison',
|
|
714
|
+
text: 'Monopoly: Q=${Q_m:.0f}, P=$${P_m:.0f}',
|
|
715
|
+
x: 'chartMaxQ * 0.6',
|
|
716
|
+
y: 'chartMaxP * 0.9',
|
|
717
|
+
color: '#7C3AED',
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
id: 'competition',
|
|
721
|
+
text: 'Competition: Q=${Q_c:.0f}, P=$${P_c:.0f}',
|
|
722
|
+
x: 'chartMaxQ * 0.6',
|
|
723
|
+
y: 'chartMaxP * 0.82',
|
|
724
|
+
color: '#16A34A',
|
|
725
|
+
},
|
|
726
|
+
],
|
|
727
|
+
},
|
|
728
|
+
],
|
|
729
|
+
};
|
|
730
|
+
},
|
|
731
|
+
};
|
|
732
|
+
/**
|
|
733
|
+
* First-Degree Price Discrimination
|
|
734
|
+
*
|
|
735
|
+
* Perfect price discrimination where monopolist captures all consumer surplus.
|
|
736
|
+
*/
|
|
737
|
+
const priceDiscriminationFirst = {
|
|
738
|
+
id: 'price_discrimination_first_degree',
|
|
739
|
+
name: 'First-Degree Price Discrimination',
|
|
740
|
+
description: 'Perfect price discrimination where each unit is sold at consumer\'s maximum willingness to pay. Monopolist captures all consumer surplus; no deadweight loss.',
|
|
741
|
+
category: 'market_structures',
|
|
742
|
+
level: 'undergraduate',
|
|
743
|
+
tags: ['price discrimination', 'perfect discrimination', 'consumer surplus', 'monopoly'],
|
|
744
|
+
parameters: {
|
|
745
|
+
demandIntercept: {
|
|
746
|
+
type: 'number',
|
|
747
|
+
default: 100,
|
|
748
|
+
description: 'Demand intercept',
|
|
749
|
+
},
|
|
750
|
+
demandSlope: {
|
|
751
|
+
type: 'number',
|
|
752
|
+
default: 2,
|
|
753
|
+
description: 'Demand slope',
|
|
754
|
+
},
|
|
755
|
+
marginalCost: {
|
|
756
|
+
type: 'number',
|
|
757
|
+
default: 20,
|
|
758
|
+
description: 'Marginal cost',
|
|
759
|
+
},
|
|
760
|
+
},
|
|
761
|
+
generate: (params) => {
|
|
762
|
+
const a = params.demandIntercept ?? 100;
|
|
763
|
+
const b = params.demandSlope ?? 2;
|
|
764
|
+
const MC = params.marginalCost ?? 20;
|
|
765
|
+
return {
|
|
766
|
+
metadata: {
|
|
767
|
+
specVersion: '1.3',
|
|
768
|
+
title: 'First-Degree Price Discrimination',
|
|
769
|
+
description: 'Perfect price discrimination',
|
|
770
|
+
},
|
|
771
|
+
parameters: {
|
|
772
|
+
a: { value: a, label: 'Demand Intercept', min: 50, max: 200 },
|
|
773
|
+
b: { value: b, label: 'Demand Slope', min: 0.5, max: 5, step: 0.1 },
|
|
774
|
+
MC: { value: MC, label: 'Marginal Cost', min: 5, max: 50 },
|
|
775
|
+
// Output equals competitive quantity (efficient)
|
|
776
|
+
Q_pd: { expression: '(a - MC) / b', label: 'Q (PD)', readonly: true },
|
|
777
|
+
// Under perfect PD, firm extracts all surplus
|
|
778
|
+
profit: {
|
|
779
|
+
expression: '0.5 * (a - MC) * Q_pd',
|
|
780
|
+
label: 'Profit (all surplus)',
|
|
781
|
+
readonly: true,
|
|
782
|
+
},
|
|
783
|
+
// No CS under perfect PD
|
|
784
|
+
CS: { value: 0, label: 'Consumer Surplus', readonly: true },
|
|
785
|
+
// No DWL under perfect PD
|
|
786
|
+
DWL: { value: 0, label: 'Deadweight Loss', readonly: true },
|
|
787
|
+
// Compare to simple monopoly
|
|
788
|
+
Q_m: { expression: '(a - MC) / (2 * b)', hidden: true },
|
|
789
|
+
P_m: { expression: 'a - b * Q_m', hidden: true },
|
|
790
|
+
profit_m: { expression: '(P_m - MC) * Q_m', label: 'Simple Monopoly π', readonly: true },
|
|
791
|
+
// Chart bounds
|
|
792
|
+
chartMaxQ: { expression: 'Q_pd * 1.2', hidden: true },
|
|
793
|
+
chartMaxP: { expression: 'a * 1.1', hidden: true },
|
|
794
|
+
},
|
|
795
|
+
charts: [
|
|
796
|
+
{
|
|
797
|
+
id: 'main',
|
|
798
|
+
title: 'First-Degree Price Discrimination',
|
|
799
|
+
xAxis: { label: 'Quantity (Q)', min: 0, max: 'chartMaxQ' },
|
|
800
|
+
yAxis: { label: 'Price', min: 0, max: 'chartMaxP' },
|
|
801
|
+
elements: [
|
|
802
|
+
// Producer captures all surplus
|
|
803
|
+
{
|
|
804
|
+
id: 'total-surplus',
|
|
805
|
+
type: 'area',
|
|
806
|
+
topBoundary: 'demand',
|
|
807
|
+
bottomBoundary: 'MC',
|
|
808
|
+
leftBoundary: '0',
|
|
809
|
+
rightBoundary: 'Q_pd',
|
|
810
|
+
color: '#22C55E',
|
|
811
|
+
opacity: 0.4,
|
|
812
|
+
label: 'Producer Surplus (all)',
|
|
813
|
+
},
|
|
814
|
+
// Demand curve
|
|
815
|
+
{
|
|
816
|
+
id: 'demand',
|
|
817
|
+
type: 'line',
|
|
818
|
+
equation: 'a - b * x',
|
|
819
|
+
color: '#2563EB',
|
|
820
|
+
strokeWidth: 3,
|
|
821
|
+
label: 'Demand = MR',
|
|
822
|
+
domain: { min: 0, max: 'a / b' },
|
|
823
|
+
},
|
|
824
|
+
// MC curve
|
|
825
|
+
{
|
|
826
|
+
id: 'mc',
|
|
827
|
+
type: 'horizontalLine',
|
|
828
|
+
y: 'MC',
|
|
829
|
+
color: '#DC2626',
|
|
830
|
+
strokeWidth: 3,
|
|
831
|
+
label: 'MC',
|
|
832
|
+
},
|
|
833
|
+
// Simple monopoly MR for comparison
|
|
834
|
+
{
|
|
835
|
+
id: 'mr-simple',
|
|
836
|
+
type: 'line',
|
|
837
|
+
equation: 'a - 2 * b * x',
|
|
838
|
+
color: '#9CA3AF',
|
|
839
|
+
strokeWidth: 1,
|
|
840
|
+
lineStyle: 'dashed',
|
|
841
|
+
label: 'MR (simple monopoly)',
|
|
842
|
+
domain: { min: 0, max: 'a / (2 * b)' },
|
|
843
|
+
},
|
|
844
|
+
// Efficient quantity
|
|
845
|
+
{
|
|
846
|
+
id: 'pd-point',
|
|
847
|
+
type: 'point',
|
|
848
|
+
x: 'Q_pd',
|
|
849
|
+
y: 'MC',
|
|
850
|
+
color: '#16A34A',
|
|
851
|
+
size: 10,
|
|
852
|
+
label: 'Efficient Output',
|
|
853
|
+
droplines: { x: true },
|
|
854
|
+
},
|
|
855
|
+
// Simple monopoly point for comparison
|
|
856
|
+
{
|
|
857
|
+
id: 'monopoly-point',
|
|
858
|
+
type: 'point',
|
|
859
|
+
x: 'Q_m',
|
|
860
|
+
y: 'P_m',
|
|
861
|
+
color: '#9CA3AF',
|
|
862
|
+
size: 6,
|
|
863
|
+
label: 'Simple Monopoly',
|
|
864
|
+
},
|
|
865
|
+
],
|
|
866
|
+
annotations: [
|
|
867
|
+
{
|
|
868
|
+
id: 'key-result',
|
|
869
|
+
text: 'MR = Demand (each unit priced individually)',
|
|
870
|
+
x: 'chartMaxQ * 0.5',
|
|
871
|
+
y: 'chartMaxP * 0.9',
|
|
872
|
+
color: '#2563EB',
|
|
873
|
+
},
|
|
874
|
+
{
|
|
875
|
+
id: 'profit-label',
|
|
876
|
+
text: 'π = $${profit:.0f} (all surplus)',
|
|
877
|
+
x: 'Q_pd / 2',
|
|
878
|
+
y: '(a + MC) / 2',
|
|
879
|
+
color: '#16A34A',
|
|
880
|
+
},
|
|
881
|
+
{
|
|
882
|
+
id: 'efficiency',
|
|
883
|
+
text: 'Efficient (no DWL)',
|
|
884
|
+
x: 'Q_pd + 2',
|
|
885
|
+
y: 'MC + (a - MC) * 0.3',
|
|
886
|
+
color: '#16A34A',
|
|
887
|
+
},
|
|
888
|
+
],
|
|
889
|
+
},
|
|
890
|
+
],
|
|
891
|
+
};
|
|
892
|
+
},
|
|
893
|
+
};
|
|
894
|
+
/**
|
|
895
|
+
* Third-Degree Price Discrimination
|
|
896
|
+
*
|
|
897
|
+
* Market segmentation with different prices for different groups.
|
|
898
|
+
*/
|
|
899
|
+
const priceDiscriminationThird = {
|
|
900
|
+
id: 'price_discrimination_third_degree',
|
|
901
|
+
name: 'Third-Degree Price Discrimination',
|
|
902
|
+
description: 'Market segmentation price discrimination. Different prices for different consumer groups based on demand elasticity. Higher price in less elastic market.',
|
|
903
|
+
category: 'market_structures',
|
|
904
|
+
level: 'undergraduate',
|
|
905
|
+
tags: ['price discrimination', 'market segmentation', 'elasticity', 'monopoly'],
|
|
906
|
+
parameters: {
|
|
907
|
+
demand1Intercept: {
|
|
908
|
+
type: 'number',
|
|
909
|
+
default: 100,
|
|
910
|
+
description: 'Market 1 demand intercept',
|
|
911
|
+
},
|
|
912
|
+
demand1Slope: {
|
|
913
|
+
type: 'number',
|
|
914
|
+
default: 1,
|
|
915
|
+
description: 'Market 1 demand slope (less elastic)',
|
|
916
|
+
},
|
|
917
|
+
demand2Intercept: {
|
|
918
|
+
type: 'number',
|
|
919
|
+
default: 80,
|
|
920
|
+
description: 'Market 2 demand intercept',
|
|
921
|
+
},
|
|
922
|
+
demand2Slope: {
|
|
923
|
+
type: 'number',
|
|
924
|
+
default: 2,
|
|
925
|
+
description: 'Market 2 demand slope (more elastic)',
|
|
926
|
+
},
|
|
927
|
+
marginalCost: {
|
|
928
|
+
type: 'number',
|
|
929
|
+
default: 20,
|
|
930
|
+
description: 'Common marginal cost',
|
|
931
|
+
},
|
|
932
|
+
},
|
|
933
|
+
generate: (params) => {
|
|
934
|
+
const a1 = params.demand1Intercept ?? 100;
|
|
935
|
+
const b1 = params.demand1Slope ?? 1;
|
|
936
|
+
const a2 = params.demand2Intercept ?? 80;
|
|
937
|
+
const b2 = params.demand2Slope ?? 2;
|
|
938
|
+
const MC = params.marginalCost ?? 20;
|
|
939
|
+
return {
|
|
940
|
+
metadata: {
|
|
941
|
+
specVersion: '1.3',
|
|
942
|
+
title: 'Third-Degree Price Discrimination',
|
|
943
|
+
description: 'Market segmentation',
|
|
944
|
+
},
|
|
945
|
+
parameters: {
|
|
946
|
+
a1: { value: a1, label: 'Market 1 Intercept', min: 50, max: 150 },
|
|
947
|
+
b1: { value: b1, label: 'Market 1 Slope', min: 0.5, max: 3, step: 0.1 },
|
|
948
|
+
a2: { value: a2, label: 'Market 2 Intercept', min: 40, max: 120 },
|
|
949
|
+
b2: { value: b2, label: 'Market 2 Slope', min: 0.5, max: 4, step: 0.1 },
|
|
950
|
+
MC: { value: MC, label: 'Marginal Cost', min: 5, max: 40 },
|
|
951
|
+
// Market 1: MR1 = a1 - 2*b1*Q1 = MC => Q1 = (a1 - MC)/(2*b1)
|
|
952
|
+
Q1: { expression: '(a1 - MC) / (2 * b1)', label: 'Q₁', readonly: true },
|
|
953
|
+
P1: { expression: 'a1 - b1 * Q1', label: 'P₁', readonly: true },
|
|
954
|
+
// Market 2: MR2 = a2 - 2*b2*Q2 = MC => Q2 = (a2 - MC)/(2*b2)
|
|
955
|
+
Q2: { expression: '(a2 - MC) / (2 * b2)', label: 'Q₂', readonly: true },
|
|
956
|
+
P2: { expression: 'a2 - b2 * Q2', label: 'P₂', readonly: true },
|
|
957
|
+
// Profits
|
|
958
|
+
profit1: { expression: '(P1 - MC) * Q1', label: 'π₁', readonly: true },
|
|
959
|
+
profit2: { expression: '(P2 - MC) * Q2', label: 'π₂', readonly: true },
|
|
960
|
+
totalProfit: { expression: 'profit1 + profit2', label: 'Total π', readonly: true },
|
|
961
|
+
// Elasticities at optimal points
|
|
962
|
+
Ed1: { expression: '-1 * (1 / b1) * (P1 / Q1)', label: 'Ed₁', readonly: true },
|
|
963
|
+
Ed2: { expression: '-1 * (1 / b2) * (P2 / Q2)', label: 'Ed₂', readonly: true },
|
|
964
|
+
// Chart bounds
|
|
965
|
+
chartMaxQ1: { expression: 'a1 / b1 * 0.7', hidden: true },
|
|
966
|
+
chartMaxQ2: { expression: 'a2 / b2 * 0.7', hidden: true },
|
|
967
|
+
chartMaxP: { expression: 'max(a1, a2) * 1.1', hidden: true },
|
|
968
|
+
},
|
|
969
|
+
charts: [
|
|
970
|
+
{
|
|
971
|
+
id: 'market1',
|
|
972
|
+
title: 'Market 1 (Less Elastic)',
|
|
973
|
+
xAxis: { label: 'Q₁', min: 0, max: 'chartMaxQ1' },
|
|
974
|
+
yAxis: { label: 'Price', min: 0, max: 'chartMaxP' },
|
|
975
|
+
elements: [
|
|
976
|
+
// Profit area
|
|
977
|
+
{
|
|
978
|
+
id: 'profit1',
|
|
979
|
+
type: 'rectangle',
|
|
980
|
+
x1: 0,
|
|
981
|
+
y1: 'MC',
|
|
982
|
+
x2: 'Q1',
|
|
983
|
+
y2: 'P1',
|
|
984
|
+
fill: '#22C55E',
|
|
985
|
+
opacity: 0.3,
|
|
986
|
+
label: 'Profit',
|
|
987
|
+
},
|
|
988
|
+
// Demand 1
|
|
989
|
+
{
|
|
990
|
+
id: 'demand1',
|
|
991
|
+
type: 'line',
|
|
992
|
+
equation: 'a1 - b1 * x',
|
|
993
|
+
color: '#2563EB',
|
|
994
|
+
strokeWidth: 3,
|
|
995
|
+
label: 'D₁',
|
|
996
|
+
domain: { min: 0, max: 'a1 / b1' },
|
|
997
|
+
},
|
|
998
|
+
// MR1
|
|
999
|
+
{
|
|
1000
|
+
id: 'mr1',
|
|
1001
|
+
type: 'line',
|
|
1002
|
+
equation: 'a1 - 2 * b1 * x',
|
|
1003
|
+
color: '#7C3AED',
|
|
1004
|
+
strokeWidth: 2,
|
|
1005
|
+
label: 'MR₁',
|
|
1006
|
+
domain: { min: 0, max: 'a1 / (2 * b1)' },
|
|
1007
|
+
},
|
|
1008
|
+
// MC
|
|
1009
|
+
{
|
|
1010
|
+
id: 'mc1',
|
|
1011
|
+
type: 'horizontalLine',
|
|
1012
|
+
y: 'MC',
|
|
1013
|
+
color: '#DC2626',
|
|
1014
|
+
strokeWidth: 2,
|
|
1015
|
+
label: 'MC',
|
|
1016
|
+
},
|
|
1017
|
+
// Optimal point
|
|
1018
|
+
{
|
|
1019
|
+
id: 'opt1',
|
|
1020
|
+
type: 'point',
|
|
1021
|
+
x: 'Q1',
|
|
1022
|
+
y: 'P1',
|
|
1023
|
+
color: '#2563EB',
|
|
1024
|
+
size: 8,
|
|
1025
|
+
droplines: { x: true, y: true },
|
|
1026
|
+
},
|
|
1027
|
+
],
|
|
1028
|
+
annotations: [
|
|
1029
|
+
{
|
|
1030
|
+
id: 'price1-label',
|
|
1031
|
+
text: 'P₁ = $${P1:.1f}',
|
|
1032
|
+
x: 'Q1 + chartMaxQ1 * 0.1',
|
|
1033
|
+
y: 'P1',
|
|
1034
|
+
color: '#2563EB',
|
|
1035
|
+
},
|
|
1036
|
+
{
|
|
1037
|
+
id: 'ed1-label',
|
|
1038
|
+
text: 'Ed₁ = ${Ed1:.2f}',
|
|
1039
|
+
x: 'chartMaxQ1 * 0.6',
|
|
1040
|
+
y: 'chartMaxP * 0.85',
|
|
1041
|
+
color: '#6B7280',
|
|
1042
|
+
},
|
|
1043
|
+
],
|
|
1044
|
+
},
|
|
1045
|
+
{
|
|
1046
|
+
id: 'market2',
|
|
1047
|
+
title: 'Market 2 (More Elastic)',
|
|
1048
|
+
xAxis: { label: 'Q₂', min: 0, max: 'chartMaxQ2' },
|
|
1049
|
+
yAxis: { label: 'Price', min: 0, max: 'chartMaxP' },
|
|
1050
|
+
elements: [
|
|
1051
|
+
// Profit area
|
|
1052
|
+
{
|
|
1053
|
+
id: 'profit2',
|
|
1054
|
+
type: 'rectangle',
|
|
1055
|
+
x1: 0,
|
|
1056
|
+
y1: 'MC',
|
|
1057
|
+
x2: 'Q2',
|
|
1058
|
+
y2: 'P2',
|
|
1059
|
+
fill: '#22C55E',
|
|
1060
|
+
opacity: 0.3,
|
|
1061
|
+
label: 'Profit',
|
|
1062
|
+
},
|
|
1063
|
+
// Demand 2
|
|
1064
|
+
{
|
|
1065
|
+
id: 'demand2',
|
|
1066
|
+
type: 'line',
|
|
1067
|
+
equation: 'a2 - b2 * x',
|
|
1068
|
+
color: '#F59E0B',
|
|
1069
|
+
strokeWidth: 3,
|
|
1070
|
+
label: 'D₂',
|
|
1071
|
+
domain: { min: 0, max: 'a2 / b2' },
|
|
1072
|
+
},
|
|
1073
|
+
// MR2
|
|
1074
|
+
{
|
|
1075
|
+
id: 'mr2',
|
|
1076
|
+
type: 'line',
|
|
1077
|
+
equation: 'a2 - 2 * b2 * x',
|
|
1078
|
+
color: '#7C3AED',
|
|
1079
|
+
strokeWidth: 2,
|
|
1080
|
+
label: 'MR₂',
|
|
1081
|
+
domain: { min: 0, max: 'a2 / (2 * b2)' },
|
|
1082
|
+
},
|
|
1083
|
+
// MC
|
|
1084
|
+
{
|
|
1085
|
+
id: 'mc2',
|
|
1086
|
+
type: 'horizontalLine',
|
|
1087
|
+
y: 'MC',
|
|
1088
|
+
color: '#DC2626',
|
|
1089
|
+
strokeWidth: 2,
|
|
1090
|
+
label: 'MC',
|
|
1091
|
+
},
|
|
1092
|
+
// Optimal point
|
|
1093
|
+
{
|
|
1094
|
+
id: 'opt2',
|
|
1095
|
+
type: 'point',
|
|
1096
|
+
x: 'Q2',
|
|
1097
|
+
y: 'P2',
|
|
1098
|
+
color: '#F59E0B',
|
|
1099
|
+
size: 8,
|
|
1100
|
+
droplines: { x: true, y: true },
|
|
1101
|
+
},
|
|
1102
|
+
],
|
|
1103
|
+
annotations: [
|
|
1104
|
+
{
|
|
1105
|
+
id: 'price2-label',
|
|
1106
|
+
text: 'P₂ = $${P2:.1f}',
|
|
1107
|
+
x: 'Q2 + chartMaxQ2 * 0.1',
|
|
1108
|
+
y: 'P2',
|
|
1109
|
+
color: '#F59E0B',
|
|
1110
|
+
},
|
|
1111
|
+
{
|
|
1112
|
+
id: 'ed2-label',
|
|
1113
|
+
text: 'Ed₂ = ${Ed2:.2f}',
|
|
1114
|
+
x: 'chartMaxQ2 * 0.6',
|
|
1115
|
+
y: 'chartMaxP * 0.85',
|
|
1116
|
+
color: '#6B7280',
|
|
1117
|
+
},
|
|
1118
|
+
],
|
|
1119
|
+
},
|
|
1120
|
+
],
|
|
1121
|
+
};
|
|
1122
|
+
},
|
|
1123
|
+
};
|
|
1124
|
+
// ============================================================================
|
|
1125
|
+
// EXPORTS
|
|
1126
|
+
// ============================================================================
|
|
1127
|
+
export const marketStructuresTemplates = [
|
|
1128
|
+
perfectCompetitionShortRun,
|
|
1129
|
+
perfectCompetitionLongRun,
|
|
1130
|
+
monopolyBasic,
|
|
1131
|
+
monopolyDeadweightLoss,
|
|
1132
|
+
priceDiscriminationFirst,
|
|
1133
|
+
priceDiscriminationThird,
|
|
1134
|
+
];
|
|
1135
|
+
//# sourceMappingURL=marketStructures.js.map
|