(
params: CreateOrderParams,
ctx: CreateOrderContext,
)
| 288 | * @throws {ValidationError} If the outcomeId format is unrecognizable. |
| 289 | */ |
| 290 | export async function createOrder( |
| 291 | params: CreateOrderParams, |
| 292 | ctx: CreateOrderContext, |
| 293 | ): Promise<Order> { |
| 294 | try { |
| 295 | // 1. Validate auth |
| 296 | const headers = ctx.getAuthHeaders(); |
| 297 | if (!headers.Authorization) { |
| 298 | throw new AuthenticationError( |
| 299 | 'Metaculus forecast submission requires authentication. ' |
| 300 | + 'Pass { apiToken: "..." } when constructing MetaculusExchange.', |
| 301 | "Metaculus", |
| 302 | ); |
| 303 | } |
| 304 | |
| 305 | // 2. Parse outcomeId to determine question type |
| 306 | const parsed = parseOutcomeId(params.outcomeId); |
| 307 | |
| 308 | // 3. Continuous questions can't be traded via createOrder |
| 309 | if (parsed.type === "continuous") { |
| 310 | throw new InvalidOrder( |
| 311 | "Continuous/numeric/date questions cannot be traded via createOrder. " |
| 312 | + "These require a 201-point CDF which cannot be expressed as a single price. " |
| 313 | + "Use the Metaculus API directly for continuous forecasts.", |
| 314 | "Metaculus", |
| 315 | ); |
| 316 | } |
| 317 | |
| 318 | // 4. Validate price |
| 319 | const probability = validateProbability(params.price); |
| 320 | |
| 321 | // 5. Log warnings for params that don't apply to Metaculus |
| 322 | if (params.side && params.side !== "buy") { |
| 323 | logger.warn( |
| 324 | `Metaculus: Ignoring side="${params.side}" -- Metaculus forecasts are probability submissions, not buy/sell. ` |
| 325 | + "Set the probability via the `price` parameter instead.", |
| 326 | ); |
| 327 | } |
| 328 | if (params.type && params.type !== "market") { |
| 329 | logger.warn( |
| 330 | `Metaculus: Ignoring type="${params.type}" -- Metaculus forecasts execute instantly (no limit orders).`, |
| 331 | ); |
| 332 | } |
| 333 | |
| 334 | // 6. Build the forecast payload |
| 335 | let payload: any[]; |
| 336 | |
| 337 | if (parsed.type === "binary") { |
| 338 | payload = [{ question: parsed.questionId, probability_yes: probability }]; |
| 339 | } else { |
| 340 | // Multiple-choice: need current probabilities to redistribute |
| 341 | if (!ctx.fetchOutcomes) { |
| 342 | throw new InvalidOrder( |
| 343 | "Multiple-choice forecast requires market outcome data but fetchOutcomes is not available.", |
| 344 | "Metaculus", |
| 345 | ); |
| 346 | } |
| 347 |
no test coverage detected