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.
Files changed (164) hide show
  1. package/CHANGELOG.md +142 -0
  2. package/LICENSE +21 -0
  3. package/README.md +240 -0
  4. package/dist/builders/YAMLBuilder.d.ts +77 -0
  5. package/dist/builders/YAMLBuilder.d.ts.map +1 -0
  6. package/dist/builders/YAMLBuilder.js +251 -0
  7. package/dist/builders/YAMLBuilder.js.map +1 -0
  8. package/dist/economics/constants.d.ts +39 -0
  9. package/dist/economics/constants.d.ts.map +1 -0
  10. package/dist/economics/constants.js +46 -0
  11. package/dist/economics/constants.js.map +1 -0
  12. package/dist/economics/formulas.d.ts +19 -0
  13. package/dist/economics/formulas.d.ts.map +1 -0
  14. package/dist/economics/formulas.js +260 -0
  15. package/dist/economics/formulas.js.map +1 -0
  16. package/dist/economics/index.d.ts +9 -0
  17. package/dist/economics/index.d.ts.map +1 -0
  18. package/dist/economics/index.js +9 -0
  19. package/dist/economics/index.js.map +1 -0
  20. package/dist/economics/validators.d.ts +18 -0
  21. package/dist/economics/validators.d.ts.map +1 -0
  22. package/dist/economics/validators.js +111 -0
  23. package/dist/economics/validators.js.map +1 -0
  24. package/dist/errors/index.d.ts +172 -0
  25. package/dist/errors/index.d.ts.map +1 -0
  26. package/dist/errors/index.js +313 -0
  27. package/dist/errors/index.js.map +1 -0
  28. package/dist/formatters/index.d.ts +8 -0
  29. package/dist/formatters/index.d.ts.map +1 -0
  30. package/dist/formatters/index.js +8 -0
  31. package/dist/formatters/index.js.map +1 -0
  32. package/dist/formatters/redux.d.ts +15 -0
  33. package/dist/formatters/redux.d.ts.map +1 -0
  34. package/dist/formatters/redux.js +35 -0
  35. package/dist/formatters/redux.js.map +1 -0
  36. package/dist/formatters/yaml.d.ts +18 -0
  37. package/dist/formatters/yaml.d.ts.map +1 -0
  38. package/dist/formatters/yaml.js +40 -0
  39. package/dist/formatters/yaml.js.map +1 -0
  40. package/dist/index.d.ts +11 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +19 -0
  43. package/dist/index.js.map +1 -0
  44. package/dist/server.d.ts +14 -0
  45. package/dist/server.d.ts.map +1 -0
  46. package/dist/server.js +86 -0
  47. package/dist/server.js.map +1 -0
  48. package/dist/templates/advancedMicro.d.ts +8 -0
  49. package/dist/templates/advancedMicro.d.ts.map +1 -0
  50. package/dist/templates/advancedMicro.js +834 -0
  51. package/dist/templates/advancedMicro.js.map +1 -0
  52. package/dist/templates/consumer.d.ts +12 -0
  53. package/dist/templates/consumer.d.ts.map +1 -0
  54. package/dist/templates/consumer.js +1978 -0
  55. package/dist/templates/consumer.js.map +1 -0
  56. package/dist/templates/elasticity.d.ts +8 -0
  57. package/dist/templates/elasticity.d.ts.map +1 -0
  58. package/dist/templates/elasticity.js +500 -0
  59. package/dist/templates/elasticity.js.map +1 -0
  60. package/dist/templates/externalities.d.ts +11 -0
  61. package/dist/templates/externalities.d.ts.map +1 -0
  62. package/dist/templates/externalities.js +997 -0
  63. package/dist/templates/externalities.js.map +1 -0
  64. package/dist/templates/financeBehavioral.d.ts +8 -0
  65. package/dist/templates/financeBehavioral.d.ts.map +1 -0
  66. package/dist/templates/financeBehavioral.js +860 -0
  67. package/dist/templates/financeBehavioral.js.map +1 -0
  68. package/dist/templates/growth.d.ts +8 -0
  69. package/dist/templates/growth.d.ts.map +1 -0
  70. package/dist/templates/growth.js +740 -0
  71. package/dist/templates/growth.js.map +1 -0
  72. package/dist/templates/index.d.ts +31 -0
  73. package/dist/templates/index.d.ts.map +1 -0
  74. package/dist/templates/index.js +91 -0
  75. package/dist/templates/index.js.map +1 -0
  76. package/dist/templates/inequality.d.ts +8 -0
  77. package/dist/templates/inequality.d.ts.map +1 -0
  78. package/dist/templates/inequality.js +562 -0
  79. package/dist/templates/inequality.js.map +1 -0
  80. package/dist/templates/intertemporalMacro.d.ts +8 -0
  81. package/dist/templates/intertemporalMacro.d.ts.map +1 -0
  82. package/dist/templates/intertemporalMacro.js +550 -0
  83. package/dist/templates/intertemporalMacro.js.map +1 -0
  84. package/dist/templates/isLM.d.ts +8 -0
  85. package/dist/templates/isLM.d.ts.map +1 -0
  86. package/dist/templates/isLM.js +747 -0
  87. package/dist/templates/isLM.js.map +1 -0
  88. package/dist/templates/macro.d.ts +8 -0
  89. package/dist/templates/macro.d.ts.map +1 -0
  90. package/dist/templates/macro.js +600 -0
  91. package/dist/templates/macro.js.map +1 -0
  92. package/dist/templates/marketStructures.d.ts +11 -0
  93. package/dist/templates/marketStructures.d.ts.map +1 -0
  94. package/dist/templates/marketStructures.js +1135 -0
  95. package/dist/templates/marketStructures.js.map +1 -0
  96. package/dist/templates/newKeynesian.d.ts +8 -0
  97. package/dist/templates/newKeynesian.d.ts.map +1 -0
  98. package/dist/templates/newKeynesian.js +633 -0
  99. package/dist/templates/newKeynesian.js.map +1 -0
  100. package/dist/templates/oligopoly.d.ts +11 -0
  101. package/dist/templates/oligopoly.d.ts.map +1 -0
  102. package/dist/templates/oligopoly.js +1113 -0
  103. package/dist/templates/oligopoly.js.map +1 -0
  104. package/dist/templates/ppf.d.ts +8 -0
  105. package/dist/templates/ppf.d.ts.map +1 -0
  106. package/dist/templates/ppf.js +439 -0
  107. package/dist/templates/ppf.js.map +1 -0
  108. package/dist/templates/producer.d.ts +11 -0
  109. package/dist/templates/producer.d.ts.map +1 -0
  110. package/dist/templates/producer.js +979 -0
  111. package/dist/templates/producer.js.map +1 -0
  112. package/dist/templates/production.d.ts +8 -0
  113. package/dist/templates/production.d.ts.map +1 -0
  114. package/dist/templates/production.js +574 -0
  115. package/dist/templates/production.js.map +1 -0
  116. package/dist/templates/supplyDemand.d.ts +8 -0
  117. package/dist/templates/supplyDemand.d.ts.map +1 -0
  118. package/dist/templates/supplyDemand.js +1282 -0
  119. package/dist/templates/supplyDemand.js.map +1 -0
  120. package/dist/templates/tradeGrowth.d.ts +8 -0
  121. package/dist/templates/tradeGrowth.d.ts.map +1 -0
  122. package/dist/templates/tradeGrowth.js +637 -0
  123. package/dist/templates/tradeGrowth.js.map +1 -0
  124. package/dist/tools/index.d.ts +25 -0
  125. package/dist/tools/index.d.ts.map +1 -0
  126. package/dist/tools/index.js +54 -0
  127. package/dist/tools/index.js.map +1 -0
  128. package/dist/tools/models.d.ts +8 -0
  129. package/dist/tools/models.d.ts.map +1 -0
  130. package/dist/tools/models.js +828 -0
  131. package/dist/tools/models.js.map +1 -0
  132. package/dist/tools/output.d.ts +8 -0
  133. package/dist/tools/output.d.ts.map +1 -0
  134. package/dist/tools/output.js +236 -0
  135. package/dist/tools/output.js.map +1 -0
  136. package/dist/tools/templates.d.ts +8 -0
  137. package/dist/tools/templates.d.ts.map +1 -0
  138. package/dist/tools/templates.js +247 -0
  139. package/dist/tools/templates.js.map +1 -0
  140. package/dist/tools/validation.d.ts +8 -0
  141. package/dist/tools/validation.d.ts.map +1 -0
  142. package/dist/tools/validation.js +181 -0
  143. package/dist/tools/validation.js.map +1 -0
  144. package/dist/types/index.d.ts +187 -0
  145. package/dist/types/index.d.ts.map +1 -0
  146. package/dist/types/index.js +7 -0
  147. package/dist/types/index.js.map +1 -0
  148. package/dist/utils/cache.d.ts +99 -0
  149. package/dist/utils/cache.d.ts.map +1 -0
  150. package/dist/utils/cache.js +192 -0
  151. package/dist/utils/cache.js.map +1 -0
  152. package/dist/utils/index.d.ts +8 -0
  153. package/dist/utils/index.d.ts.map +1 -0
  154. package/dist/utils/index.js +8 -0
  155. package/dist/utils/index.js.map +1 -0
  156. package/dist/utils/logger.d.ts +128 -0
  157. package/dist/utils/logger.d.ts.map +1 -0
  158. package/dist/utils/logger.js +251 -0
  159. package/dist/utils/logger.js.map +1 -0
  160. package/dist/validation/index.d.ts +42 -0
  161. package/dist/validation/index.d.ts.map +1 -0
  162. package/dist/validation/index.js +282 -0
  163. package/dist/validation/index.js.map +1 -0
  164. 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