{
 "openapi": "3.1.0",
 "info": {
  "title": "聰明投 SmartInvest 對外回測與行情 API（受邀測試）",
  "version": "1.2.0",
  "description": "聰明投網頁回測功能與市場歷史資料的對外門面（docs/38）。只讀＋運算：不下單、不建立規則、不寫任何資料。\n第一步請呼叫 GET /catalog——策略 JSON（DSL）完整格式、16 個情境指標、商品、限制、回測口徑都在裡面。\n\n約定：\n- 金額與價格一律十進位字串（例 \"3000\"）；百分比為數字（149.5 代表 149.5%）。\n- 成功回應一律 { data, meta }；任何介面都必須顯示 meta.disclaimer 與 meta.attribution。\n- 錯誤回應的 details[].fix 說明怎麼改，照改再送。\n- 比較策略看 summary.returnGapPct（策略報酬率 − 傻傻定扣報酬率，百分點）。\n- 歷史模擬，非投資建議；受邀非商業用途，不得散布原始資料。\n- v1.1（2026-11-12）：一句話結論 plainText、固定基準 benchmark、多期間 periods、壓力期間 covid-2020／bear-2022、PATCH 部分更新、錯誤 userMessage。\n",
  "contact": {
   "name": "聰明投專案"
  }
 },
 "servers": [
  {
   "url": "https://{host}/api/public/v1",
   "variables": {
    "host": {
     "default": "example.invalid",
     "description": "業主提供的固定網域（docs/38 §16）"
    }
   }
  }
 ],
 "security": [
  {
   "bearerAuth": []
  }
 ],
 "tags": [
  {
   "name": "discovery",
   "description": "說明與目錄（先看這裡）"
  },
  {
   "name": "backtest",
   "description": "回測、驗證、批次、沙盒"
  },
  {
   "name": "market-data",
   "description": "市場指標與收盤序列"
  },
  {
   "name": "workspace",
   "description": "我的策略與我的投組（需 workspace 權限；每把金鑰獨立）"
  },
  {
   "name": "system"
  }
 ],
 "paths": {
  "/catalog": {
   "get": {
    "tags": [
     "discovery"
    ],
    "operationId": "getCatalog",
    "summary": "一次取得 DSL 格式、積木、16 指標、商品、限制、回測口徑與範例",
    "description": "內容由伺服器程式常數產生，永遠與引擎一致。AI 應先呼叫此端點再組策略。",
    "responses": {
     "200": {
      "description": "目錄",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Catalog"
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/templates": {
   "get": {
    "tags": [
     "discovery"
    ],
    "operationId": "listTemplates",
    "summary": "情境模板（8 張＋自訂），每張附可直接回測的 exampleDsl",
    "responses": {
     "200": {
      "description": "模板清單",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "items": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/Template"
             }
            }
           }
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/strategies": {
   "get": {
    "tags": [
     "discovery"
    ],
    "operationId": "listStrategies",
    "summary": "策略館精選策略與近 1／3／5 年回測報告",
    "responses": {
     "200": {
      "description": "策略清單",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "items": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/Strategy"
             }
            }
           }
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/strategies/{strategyId}": {
   "get": {
    "tags": [
     "discovery"
    ],
    "operationId": "getStrategy",
    "summary": "單一精選策略",
    "parameters": [
     {
      "name": "strategyId",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "examples": [
        "bottom-scorecard"
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "策略",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Strategy"
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/backtest/validate": {
   "post": {
    "tags": [
     "backtest"
    ],
    "operationId": "validateStrategy",
    "summary": "只驗證策略、不回測；錯誤附欄位路徑與修法",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "dsl"
        ],
        "properties": {
         "dsl": {
          "$ref": "#/components/schemas/EngineDsl"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "驗證結果（無效時 valid=false，仍回 200）",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/ValidateResult"
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/backtest/run": {
   "post": {
    "tags": [
     "backtest"
    ],
    "operationId": "runBacktest",
    "summary": "回測一個策略（與網頁 /simulate 同一引擎、同一口徑）",
    "description": "dsl 與 strategyId 二擇一；period 與 from/to 二擇一（都不給＝近 3 年）。\ndetail=summary 只回摘要與事件（省 token），monthly 加逐月資料，full 再加每月各部位股數×價格。\n",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/BacktestRunRequest"
       },
       "examples": {
        "featured": {
         "summary": "跑策略館策略",
         "value": {
          "strategyId": "bottom-scorecard",
          "period": "5y",
          "detail": "summary"
         }
        },
        "custom": {
         "summary": "自訂定期定額、自訂期間",
         "value": {
          "dsl": {
           "kind": "basic",
           "schedule": {
            "type": "multi_day",
            "daysOfMonth": [
             6
            ]
           },
           "primary": "0050",
           "amount": "3000"
          },
          "from": "2020-01",
          "to": "2025-12",
          "detail": "monthly"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "回測結果",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/BacktestResult"
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/backtest/batch": {
   "post": {
    "tags": [
     "backtest"
    ],
    "operationId": "runBacktestBatch",
    "summary": "一次回測最多 20 組策略變體（參數掃描），回比較表",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/BatchRequest"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "比較表（無效的變體在該列回 error，其餘照跑）",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "window": {
             "oneOf": [
              {
               "$ref": "#/components/schemas/Window"
              },
              {
               "type": "null"
              }
             ],
             "description": "各組期間相同時的共同期間；各組期間不同時為 null（看各列 window）"
            },
            "mixedWindows": {
             "type": "boolean",
             "description": "v1.2：各組期間不同時為 true"
            },
            "rows": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/BatchRow"
             }
            },
            "sortBy": {
             "type": "string"
            },
            "benchmark": {
             "$ref": "#/components/schemas/Benchmark"
            }
           }
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/backtest/portfolio": {
   "post": {
    "tags": [
     "backtest"
    ],
    "operationId": "previewPortfolioBacktest",
    "summary": "投組即時回測（不存檔）——拉滑桿調參時用；回應與已存投組回測同形",
    "description": "只需 backtest:run。策略可用 dsl 或 strategyId；有工作區權限時也可用 savedStrategyId。建議前端停手 400 毫秒再送。",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "$ref": "#/components/schemas/PortfolioInput"
         },
         {
          "type": "object",
          "properties": {
           "period": {
            "type": "string",
            "enum": [
             "1y",
             "3y",
             "5y",
             "covid-2020",
             "bear-2022"
            ]
           },
           "from": {
            "$ref": "#/components/schemas/Month"
           },
           "to": {
            "$ref": "#/components/schemas/Month"
           },
           "adjustSplits": {
            "type": "boolean",
            "default": true
           },
           "adjustDividends": {
            "type": "boolean",
            "default": false
           },
           "detail": {
            "type": "string",
            "enum": [
             "summary",
             "monthly",
             "full"
            ],
            "default": "monthly"
           },
           "fullMonthsOnly": {
            "type": "boolean"
           },
           "benchmark": {
            "type": "string"
           },
           "periods": {
            "type": "array",
            "items": {
             "type": "string"
            }
           }
          }
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "投組回測結果",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PortfolioBacktest"
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/sandbox/scripts": {
   "get": {
    "tags": [
     "backtest"
    ],
    "operationId": "listSandboxScripts",
    "summary": "劇本沙盒清單（真實歷史劇本、合成劇本、隨機走勢）",
    "responses": {
     "200": {
      "description": "劇本清單",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "items": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/SandboxScript"
             }
            }
           }
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/sandbox/run": {
   "post": {
    "tags": [
     "backtest"
    ],
    "operationId": "runSandbox",
    "summary": "用劇本壓力測試一個策略（沙盒深拷貝，不影響任何資料）",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "scriptKey"
        ],
        "properties": {
         "scriptKey": {
          "type": "string",
          "enum": [
           "covid-2020",
           "bear-2022",
           "flash-crash",
           "bear-year",
           "slow-bull",
           "box-range",
           "random-3y",
           "random-1y-day"
          ]
         },
         "dsl": {
          "$ref": "#/components/schemas/EngineDsl"
         },
         "strategyId": {
          "type": "string",
          "description": "策略館 id；與 dsl、savedStrategyId 三擇一"
         },
         "savedStrategyId": {
          "type": "string",
          "description": "自己工作區的策略 id（需 workspace:read）"
         },
         "strategyName": {
          "type": "string",
          "maxLength": 30
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "沙盒結果",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/SandboxResult"
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/market-data/indicators": {
   "get": {
    "tags": [
     "market-data"
    ],
    "operationId": "getMarketIndicators",
    "summary": "逐月市場指標歷史（16 個情境指標＋落底分數＋大盤回檔），與回測判斷條件同一份計算",
    "parameters": [
     {
      "name": "from",
      "in": "query",
      "required": true,
      "schema": {
       "$ref": "#/components/schemas/Month"
      }
     },
     {
      "name": "to",
      "in": "query",
      "required": false,
      "schema": {
       "$ref": "#/components/schemas/Month"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "逐月指標",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "rows": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/IndicatorRow"
             }
            }
           }
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/market-data/scorecard": {
   "get": {
    "tags": [
     "market-data"
    ],
    "operationId": "getScorecard",
    "summary": "今日落底記分板（分數、區間、逐條命中、權重全表）",
    "responses": {
     "200": {
      "description": "記分板",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Scorecard"
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/market-data/series": {
   "get": {
    "tags": [
     "market-data"
    ],
    "operationId": "getPriceSeries",
    "summary": "收盤序列（月底收盤或逐日收盤；只讀本機資料庫）",
    "description": "interval=day 需要 market:daily 權限。可含 TAIEX（加權指數）與 FX-USD（美元兌台幣）。",
    "parameters": [
     {
      "name": "symbols",
      "in": "query",
      "required": true,
      "description": "逗號分隔，最多 5 個",
      "schema": {
       "type": "string",
       "examples": [
        "0050,TAIEX"
       ]
      }
     },
     {
      "name": "interval",
      "in": "query",
      "schema": {
       "type": "string",
       "enum": [
        "month",
        "day"
       ],
       "default": "month"
      }
     },
     {
      "name": "from",
      "in": "query",
      "required": true,
      "description": "interval=day 用 YYYY-MM-DD（最早 2015-01-01）；month 用 YYYY-MM",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "to",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "adjust",
      "in": "query",
      "schema": {
       "type": "string",
       "enum": [
        "none",
        "splits",
        "splits_dividends"
       ],
       "default": "splits"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "序列",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "series": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/Series"
             }
            }
           }
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/market-data/freshness": {
   "get": {
    "tags": [
     "market-data"
    ],
    "operationId": "getFreshness",
    "summary": "各商品本機資料的最新日期與來源",
    "responses": {
     "200": {
      "description": "新鮮度",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "items": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/Freshness"
             }
            },
            "datasets": {
             "type": "array",
             "items": {
              "type": "object",
              "properties": {
               "dataset": {
                "type": "string"
               },
               "name": {
                "type": "string"
               },
               "source": {
                "type": "string"
               },
               "lastMonth": {
                "type": [
                 "string",
                 "null"
                ]
               },
               "expectedMonth": {
                "type": "string"
               },
               "stale": {
                "type": "boolean"
               }
              }
             },
             "description": "政府統計與籌碼資料的新鮮度"
            },
            "expectedLatest": {
             "type": "string"
            },
            "lastRefresh": {
             "type": "object",
             "additionalProperties": true
            },
            "schedule": {
             "type": "string"
            }
           }
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/products": {
   "get": {
    "tags": [
     "discovery"
    ],
    "operationId": "listProducts",
    "summary": "可回測商品主檔與現價",
    "parameters": [
     {
      "name": "query",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "type",
      "in": "query",
      "schema": {
       "type": "string",
       "enum": [
        "stock",
        "etf",
        "letf"
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "商品",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "items": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/Product"
             }
            }
           }
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/status": {
   "get": {
    "tags": [
     "system"
    ],
    "operationId": "getStatus",
    "summary": "服務狀態、資料新鮮度摘要、這把金鑰的權限與剩餘額度",
    "responses": {
     "200": {
      "description": "狀態",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Status"
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/workspace/strategies": {
   "get": {
    "tags": [
     "workspace"
    ],
    "operationId": "listSavedStrategies",
    "summary": "列出自己工作區的策略",
    "parameters": [
     {
      "name": "tag",
      "in": "query",
      "schema": {
       "type": "string"
      }
     },
     {
      "name": "q",
      "in": "query",
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "策略清單",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "items": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/SavedStrategy"
             }
            }
           }
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   },
   "post": {
    "tags": [
     "workspace"
    ],
    "operationId": "createSavedStrategy",
    "summary": "新增策略（存檔前會驗證；可從策略館或模板複製）",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/SavedStrategyInput"
       }
      }
     }
    },
    "responses": {
     "201": {
      "description": "已建立",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/SavedStrategy"
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/workspace/strategies/{id}": {
   "parameters": [
    {
     "name": "id",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     }
    }
   ],
   "get": {
    "tags": [
     "workspace"
    ],
    "operationId": "getSavedStrategy",
    "summary": "取得單一策略",
    "responses": {
     "200": {
      "description": "策略",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/SavedStrategy"
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   },
   "put": {
    "tags": [
     "workspace"
    ],
    "operationId": "updateSavedStrategy",
    "summary": "整筆取代策略（需帶目前 version 或 If-Match；沒給的欄位會被清空，部分更新請用 PATCH）",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "$ref": "#/components/schemas/SavedStrategyInput"
         },
         {
          "type": "object",
          "required": [
           "version"
          ],
          "properties": {
           "version": {
            "type": "integer"
           }
          }
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "已更新",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/SavedStrategy"
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    },
    "parameters": [
     {
      "name": "If-Match",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      },
      "description": "可代替 body.version"
     }
    ]
   },
   "delete": {
    "tags": [
     "workspace"
    ],
    "operationId": "deleteSavedStrategy",
    "summary": "刪除策略（不可復原；被投組引用時 409 STRATEGY_IN_USE）",
    "responses": {
     "204": {
      "description": "已刪除"
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   },
   "patch": {
    "tags": [
     "workspace"
    ],
    "operationId": "patchSavedStrategy",
    "summary": "部分更新：只送要改的欄位，其餘保留（需 version 或 If-Match）",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "version": {
          "type": "integer"
         }
        },
        "additionalProperties": true,
        "description": "SavedStrategyInput 的任意子集＋version"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "已更新",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/SavedStrategy"
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    },
    "parameters": [
     {
      "name": "If-Match",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      },
      "description": "可代替 body.version"
     }
    ]
   }
  },
  "/workspace/portfolios": {
   "get": {
    "tags": [
     "workspace"
    ],
    "operationId": "listPortfolios",
    "summary": "列出自己工作區的投組（含伺服器推導欄位）",
    "responses": {
     "200": {
      "description": "投組清單",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "items": {
             "type": "array",
             "items": {
              "$ref": "#/components/schemas/Portfolio"
             }
            }
           }
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    },
    "parameters": [
     {
      "name": "tag",
      "in": "query",
      "schema": {
       "type": "string"
      }
     }
    ]
   },
   "post": {
    "tags": [
     "workspace"
    ],
    "operationId": "createPortfolio",
    "summary": "新增投組（月承諾合計不得超過月預算）",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/PortfolioInput"
       }
      }
     }
    },
    "responses": {
     "201": {
      "description": "已建立",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Portfolio"
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/workspace/portfolios/{id}": {
   "parameters": [
    {
     "name": "id",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     }
    }
   ],
   "get": {
    "tags": [
     "workspace"
    ],
    "operationId": "getPortfolio",
    "summary": "取得單一投組",
    "responses": {
     "200": {
      "description": "投組",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Portfolio"
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   },
   "put": {
    "tags": [
     "workspace"
    ],
    "operationId": "updatePortfolio",
    "summary": "整筆取代投組（需帶目前 version 或 If-Match；沒給的欄位會被清空，部分更新請用 PATCH）",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "$ref": "#/components/schemas/PortfolioInput"
         },
         {
          "type": "object",
          "required": [
           "version"
          ],
          "properties": {
           "version": {
            "type": "integer"
           }
          }
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "已更新",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Portfolio"
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    },
    "parameters": [
     {
      "name": "If-Match",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      },
      "description": "可代替 body.version"
     }
    ]
   },
   "delete": {
    "tags": [
     "workspace"
    ],
    "operationId": "deletePortfolio",
    "summary": "刪除投組（不可復原）",
    "responses": {
     "204": {
      "description": "已刪除"
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   },
   "patch": {
    "tags": [
     "workspace"
    ],
    "operationId": "patchPortfolio",
    "summary": "部分更新：只送要改的欄位，其餘保留（需 version 或 If-Match）",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "version": {
          "type": "integer"
         }
        },
        "additionalProperties": true,
        "description": "PortfolioInput 的任意子集＋version"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "已更新",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/Portfolio"
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    },
    "parameters": [
     {
      "name": "If-Match",
      "in": "header",
      "required": false,
      "schema": {
       "type": "string"
      },
      "description": "可代替 body.version"
     }
    ]
   }
  },
  "/workspace/portfolios/{id}/backtest": {
   "parameters": [
    {
     "name": "id",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     }
    }
   ],
   "post": {
    "tags": [
     "workspace"
    ],
    "operationId": "backtestPortfolio",
    "summary": "投組回測——各啟用策略同期回測後逐月加總，附目標檢核（保守口徑，不計報酬）",
    "requestBody": {
     "required": false,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "period": {
          "type": "string",
          "enum": [
           "1y",
           "3y",
           "5y",
           "covid-2020",
           "bear-2022"
          ]
         },
         "from": {
          "$ref": "#/components/schemas/Month"
         },
         "to": {
          "$ref": "#/components/schemas/Month"
         },
         "adjustSplits": {
          "type": "boolean",
          "default": true
         },
         "adjustDividends": {
          "type": "boolean",
          "default": false
         },
         "detail": {
          "type": "string",
          "enum": [
           "summary",
           "monthly",
           "full"
          ],
          "default": "monthly"
         },
         "fullMonthsOnly": {
          "type": "boolean"
         },
         "benchmark": {
          "type": "string"
         },
         "periods": {
          "type": "array",
          "items": {
           "type": "string"
          }
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "投組回測結果",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/PortfolioBacktest"
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/workspace/portfolios/{id}/rebalance": {
   "parameters": [
    {
     "name": "id",
     "in": "path",
     "required": true,
     "schema": {
      "type": "string"
     }
    }
   ],
   "get": {
    "tags": [
     "workspace"
    ],
    "operationId": "getPortfolioRebalance",
    "summary": "依自填持倉、目標配置與最新收盤試算偏離與調整買賣腿（只試算，不執行、不構成建議）",
    "responses": {
     "200": {
      "description": "偏離計算",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/RebalancePlan"
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    },
    "parameters": [
     {
      "name": "mode",
      "in": "query",
      "schema": {
       "type": "string",
       "enum": [
        "sell",
        "buyOnly"
       ],
       "default": "sell"
      },
      "description": "buyOnly＝不賣出、只用新資金補低配"
     },
     {
      "name": "newMoney",
      "in": "query",
      "schema": {
       "type": "string"
      },
      "description": "buyOnly 時這次要投入的新資金"
     }
    ]
   }
  },
  "/workspace/export": {
   "get": {
    "tags": [
     "workspace"
    ],
    "operationId": "exportWorkspace",
    "summary": "匯出整個工作區",
    "responses": {
     "200": {
      "description": "工作區檔",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "$ref": "#/components/schemas/WorkspaceExport"
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  },
  "/workspace/import": {
   "post": {
    "tags": [
     "workspace"
    ],
    "operationId": "importWorkspace",
    "summary": "合併匯入（一律建新 id，不覆蓋既有）；也接受平台「匯出策略」檔",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "oneOf": [
         {
          "$ref": "#/components/schemas/WorkspaceExport"
         },
         {
          "type": "object",
          "description": "平台匯出策略檔",
          "required": [
           "app",
           "regimes"
          ],
          "properties": {
           "app": {
            "type": "string",
            "const": "smartinvest-strategy"
           },
           "version": {
            "type": "integer"
           },
           "name": {
            "type": "string"
           },
           "regimes": {
            "type": "array",
            "items": {
             "$ref": "#/components/schemas/Regime"
            }
           }
          }
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "匯入結果",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "required": [
          "data",
          "meta"
         ],
         "properties": {
          "data": {
           "type": "object",
           "properties": {
            "strategiesAdded": {
             "type": "integer"
            },
            "portfoliosAdded": {
             "type": "integer"
            },
            "idMap": {
             "type": "object",
             "additionalProperties": {
              "type": "string"
             },
             "description": "匯入檔 id → 新 id"
            }
           }
          },
          "meta": {
           "$ref": "#/components/schemas/Meta"
          }
         }
        }
       }
      }
     },
     "default": {
      "$ref": "#/components/responses/Error"
     }
    }
   }
  }
 },
 "components": {
  "securitySchemes": {
   "bearerAuth": {
    "type": "http",
    "scheme": "bearer",
    "description": "個人 API 金鑰，格式 sik_<8 碼代號>_<32 碼密鑰>，由業主發放；工作區以 8 碼代號識別身分"
   }
  },
  "responses": {
   "Error": {
    "description": "錯誤（400/401/403/404/413/422/429/503）",
    "headers": {
     "Retry-After": {
      "description": "429 時，幾秒後可再試",
      "schema": {
       "type": "integer"
      }
     }
    },
    "content": {
     "application/json": {
      "schema": {
       "$ref": "#/components/schemas/Error"
      }
     }
    }
   }
  },
  "schemas": {
   "Money": {
    "type": "string",
    "pattern": "^-?\\d+(\\.\\d+)?$",
    "description": "十進位字串（台幣元或價格）"
   },
   "Month": {
    "type": "string",
    "pattern": "^\\d{4}-\\d{2}$",
    "examples": [
     "2020-01"
    ]
   },
   "Window": {
    "type": "object",
    "properties": {
     "from": {
      "$ref": "#/components/schemas/Month"
     },
     "to": {
      "$ref": "#/components/schemas/Month"
     },
     "months": {
      "type": "integer"
     }
    }
   },
   "Meta": {
    "type": "object",
    "required": [
     "apiVersion",
     "engine",
     "methodologyVersion",
     "dataSource",
     "disclaimer",
     "attribution",
     "usage",
     "warnings",
     "traceId"
    ],
    "properties": {
     "apiVersion": {
      "type": "string",
      "const": "public-v1"
     },
     "engine": {
      "type": "string",
      "description": "回測引擎版本；prototype-monthly-v3＝月頻原型（同月收盤成交）",
      "examples": [
       "prototype-monthly-v3"
      ]
     },
     "methodologyVersion": {
      "type": "string",
      "description": "回測口徑版本（最後一次口徑變更日）；不同版本的數字不可直接比較",
      "examples": [
       "2026-07-19"
      ]
     },
     "dataSource": {
      "type": "string",
      "enum": [
       "finmind",
       "twse",
       "mock-fallback",
       "stub",
       "none"
      ],
      "description": "混合來源取最保守值；mock-fallback＝模擬資料"
     },
     "dataAsOf": {
      "type": [
       "string",
       "null"
      ],
      "description": "資料最新日期 YYYY-MM-DD"
     },
     "attribution": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "必須在介面上顯示的資料來源顯名"
     },
     "usage": {
      "type": "string",
      "description": "使用限制（受邀非商業、不得散布）"
     },
     "disclaimer": {
      "type": "string",
      "description": "必須在介面上顯示的免責聲明"
     },
     "warnings": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "解讀提醒（零觸發、投入不同、資料降級、資料過期、商品晚上市、指標暖身不足）；給開發者看的原文，含參數名"
     },
     "notices": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Notice"
      },
      "description": "v1.2：與 warnings 一一對應的結構化提醒；介面請顯示 userMessage、用 code 分類"
     },
     "traceId": {
      "type": "string"
     }
    }
   },
   "Notice": {
    "type": "object",
    "required": [
     "code",
     "userMessage",
     "devHint"
    ],
    "properties": {
     "code": {
      "type": "string",
      "description": "穩定代碼，例 ZERO_TRIGGER、IRR_TIMING、INVESTED_DIFFERS、PARTIAL_MONTH、LEVERAGED、NO_DAILY_LIMIT、DRAWDOWN_IS_TAIEX、DIVIDENDS_EXCLUDED、BELOW_ONE_SHARE、STALE_MACRO、STALE_PRICES、MOCK_DATA、MONTHLY_SPLIT；未分類為 INFO",
      "examples": [
       "IRR_TIMING"
      ]
     },
     "userMessage": {
      "type": "string",
      "description": "可直接給終端使用者看的白話句（不含參數名）"
     },
     "devHint": {
      "type": "string",
      "description": "原始提醒（同 warnings 內容，含參數名與修法）"
     },
     "scope": {
      "type": "string",
      "description": "投組回測中只屬於某一條策略的提醒，填該策略名稱（warnings 原文以【策略名】開頭）；整體提醒沒有這欄"
     }
    }
   },
   "Error": {
    "type": "object",
    "required": [
     "code",
     "message",
     "traceId"
    ],
    "properties": {
     "code": {
      "type": "string",
      "enum": [
       "REQUEST_INVALID",
       "AUTH_MISSING",
       "AUTH_INVALID",
       "SCOPE_DENIED",
       "NOT_FOUND",
       "PAYLOAD_TOO_LARGE",
       "ENGINE_VALIDATION_FAILED",
       "ENGINE_SYMBOL_NOT_FOUND",
       "ENGINE_SYMBOL_DISABLED",
       "DATA_COVERAGE_INSUFFICIENT",
       "VERSION_CONFLICT",
       "STRATEGY_IN_USE",
       "WORKSPACE_QUOTA_EXCEEDED",
       "PORTFOLIO_BUDGET_EXCEEDED",
       "RATE_LIMITED",
       "PUBLIC_API_DISABLED",
       "ENGINE_INTERNAL"
      ]
     },
     "message": {
      "type": "string"
     },
     "hint": {
      "type": "string",
      "description": "一句話說明還差幾步、怎麼修"
     },
     "details": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/ErrorDetail"
      }
     },
     "docs": {
      "type": "string"
     },
     "traceId": {
      "type": "string"
     },
     "userMessage": {
      "type": "string",
      "description": "可直接顯示給終端使用者的白話訊息"
     },
     "retryAfterSec": {
      "type": "integer",
      "description": "429 時幾秒後可再試"
     },
     "meta": {
      "type": "object",
      "properties": {
       "apiVersion": {
        "type": "string"
       },
       "disclaimer": {
        "type": "string"
       },
       "attribution": {
        "type": "array",
        "items": {
         "type": "string"
        }
       },
       "usage": {
        "type": "string"
       },
       "traceId": {
        "type": "string"
       }
      }
     }
    }
   },
   "ErrorDetail": {
    "type": "object",
    "required": [
     "field",
     "problem",
     "fix"
    ],
    "properties": {
     "field": {
      "type": "string",
      "description": "JSON Pointer，例 /dsl/regimes/0/allocations"
     },
     "problem": {
      "type": "string"
     },
     "fix": {
      "type": "string"
     },
     "suggestions": {
      "type": "array",
      "description": "v1.2 一鍵修復：把 patch 合併進同一層物件即可通過驗證（例：月存除不盡時給 down／up／改扣款日）",
      "items": {
       "type": "object",
       "required": [
        "label",
        "patch"
       ],
       "properties": {
        "label": {
         "type": "string"
        },
        "patch": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     }
    }
   },
   "Allocation": {
    "type": "object",
    "required": [
     "symbol",
     "weightPct"
    ],
    "properties": {
     "symbol": {
      "type": "string",
      "description": "商品代號（見 /products）"
     },
     "weightPct": {
      "type": "number",
      "minimum": 0,
      "maximum": 100
     }
    }
   },
   "Schedule": {
    "type": "object",
    "required": [
     "type",
     "daysOfMonth"
    ],
    "properties": {
     "type": {
      "type": "string",
      "enum": [
       "multi_day",
       "daily"
      ],
      "description": "multi_day＝每月指定日扣款（遇假日順延）；daily＝每個交易日扣款（每月約 21 次）"
     },
     "daysOfMonth": {
      "type": "array",
      "items": {
       "type": "integer",
       "minimum": 1,
       "maximum": 31
      },
      "description": "multi_day 時必填；daily 時給空陣列"
     }
    }
   },
   "Tier": {
    "type": "object",
    "required": [
     "drop",
     "factor"
    ],
    "properties": {
     "drop": {
      "type": "number",
      "description": "大盤自前波高點回檔 ≥ 此 %時"
     },
     "factor": {
      "type": "number",
      "minimum": 0,
      "maximum": 10,
      "description": "當期扣款倍率"
     }
    }
   },
   "RegimeIndicator": {
    "type": "string",
    "enum": [
     "index_sd_band",
     "biz_light",
     "ma_month",
     "ma_quarter",
     "ma_year",
     "index_bias",
     "leading_trend",
     "biz_light_trend",
     "volatility",
     "rate_cycle",
     "cpi",
     "margin_heat",
     "foreign_flow",
     "volume_energy",
     "bottom_score",
     "bottom_zone"
    ],
    "description": "各指標可用值見 GET /catalog 的 indicators[]"
   },
   "RegimeCondition": {
    "type": "object",
    "required": [
     "indicator",
     "op",
     "value"
    ],
    "properties": {
     "indicator": {
      "$ref": "#/components/schemas/RegimeIndicator"
     },
     "op": {
      "type": "string",
      "enum": [
       "eq",
       "neq",
       "gte",
       "lte"
      ],
      "description": "分檔指標用 eq/neq；數值指標（bottom_score）用 gte/lte"
     },
     "value": {
      "type": "string",
      "description": "分檔值（如 below、blue）或分數字串（如 \"4\"）"
     }
    }
   },
   "Regime": {
    "type": "object",
    "required": [
     "label",
     "logic",
     "conditions",
     "allocations",
     "cashPct"
    ],
    "description": "情境——條件成立時本月的扣款與再平衡行為。情境依序比對，第一個成立者生效；都不成立本月不扣款",
    "properties": {
     "label": {
      "type": "string"
     },
     "logic": {
      "type": "string",
      "enum": [
       "and",
       "or"
      ]
     },
     "conditions": {
      "type": "array",
      "minItems": 1,
      "maxItems": 10,
      "items": {
       "$ref": "#/components/schemas/RegimeCondition"
      }
     },
     "contribution": {
      "type": "string",
      "enum": [
       "none",
       "scale"
      ],
      "description": "none＝本月不扣款；scale＝依 factor 倍率扣款"
     },
     "factor": {
      "type": "number",
      "minimum": 0,
      "maximum": 10,
      "description": "contribution=scale 時的倍率，1＝基準月額"
     },
     "allocations": {
      "type": "array",
      "maxItems": 10,
      "items": {
       "$ref": "#/components/schemas/Allocation"
      },
      "description": "本月新扣款怎麼拆"
     },
     "cashPct": {
      "type": "number",
      "minimum": 0,
      "maximum": 100,
      "description": "本月扣款保留現金 %；與 allocations 合計 100"
     },
     "rebalanceMode": {
      "type": "string",
      "enum": [
       "off",
       "always",
       "threshold"
      ]
     },
     "rebalanceThresholdPct": {
      "type": "number",
      "minimum": 1,
      "maximum": 50
     },
     "rebalanceAllocations": {
      "type": "array",
      "maxItems": 10,
      "items": {
       "$ref": "#/components/schemas/Allocation"
      },
      "description": "持倉要調回的目標；未給＝沿用 allocations"
     },
     "rebalanceCashPct": {
      "type": "number",
      "minimum": 0,
      "maximum": 100
     },
     "mode": {
      "type": "string",
      "enum": [
       "keep",
       "boost",
       "rebalance",
       "rebalance_only"
      ],
      "deprecated": true,
      "description": "舊格式，仍接受，伺服器自動轉成 contribution/rebalanceMode"
     }
    }
   },
   "EngineDsl": {
    "type": "object",
    "required": [
     "schedule",
     "primary"
    ],
    "description": "長效智慧單規則。能力欄位可任意交叉組合；kind 只是分類顯示",
    "properties": {
     "kind": {
      "type": "string",
      "enum": [
       "basic",
       "switch",
       "tiers",
       "allocate",
       "rebalance",
       "regime"
      ]
     },
     "schedule": {
      "$ref": "#/components/schemas/Schedule"
     },
     "primary": {
      "type": "string",
      "description": "主標的；也是傻傻定扣基準的標的"
     },
     "amount": {
      "oneOf": [
       {
        "$ref": "#/components/schemas/Money"
       },
       {
        "type": "integer"
       }
      ],
      "description": "單次扣款金額（台幣，100 的倍數；純再平衡規則可為 0）"
     },
     "alt": {
      "type": "string",
      "description": "條件切換的替代標的（與 allocations 互斥）"
     },
     "thresholdPct": {
      "type": "number",
      "description": "大盤回檔 ≥ 此 % 時改買 alt"
     },
     "convertOnSwitch": {
      "type": "boolean",
      "description": "切換時把既有持倉一併轉換（需搭配 alt+thresholdPct）"
     },
     "tiers": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Tier"
      },
      "description": "跌深階梯加碼"
     },
     "allocations": {
      "type": "array",
      "maxItems": 10,
      "items": {
       "$ref": "#/components/schemas/Allocation"
      },
      "description": "多標的比例（合計 100）"
     },
     "takeProfitPct": {
      "type": "number",
      "minimum": 1,
      "maximum": 95
     },
     "stopLossPct": {
      "type": "number",
      "minimum": 1,
      "maximum": 95
     },
     "tpSlScope": {
      "type": "string",
      "enum": [
       "rule",
       "pool"
      ]
     },
     "rebalance": {
      "type": "object",
      "required": [
       "thresholdPct"
      ],
      "properties": {
       "thresholdPct": {
        "type": "number",
        "minimum": 1,
        "maximum": 50
       }
      }
     },
     "regimes": {
      "type": "array",
      "maxItems": 10,
      "items": {
       "$ref": "#/components/schemas/Regime"
      },
      "description": "情境積木；設定後覆蓋 alt/allocations"
     },
     "monthlyAmount": {
      "oneOf": [
       {
        "$ref": "#/components/schemas/Money"
       },
       {
        "type": "integer"
       }
      ],
      "description": "便利欄位：只給月合計時，伺服器依扣款次數拆成 100 的倍數（amount 優先）；v1.2 起除不盡預設回 422 並附 suggestions"
     },
     "monthlyAmountRounding": {
      "type": "string",
      "enum": [
       "strict",
       "down",
       "up"
      ],
      "default": "strict",
      "description": "monthlyAmount 除不盡時：strict＝報錯（預設）；down／up＝每次金額往下／往上取 100 的倍數並在 notices 說明"
     }
    }
   },
   "BacktestRunRequest": {
    "type": "object",
    "properties": {
     "dsl": {
      "$ref": "#/components/schemas/EngineDsl"
     },
     "strategyId": {
      "type": "string",
      "description": "策略館 id；與 dsl、savedStrategyId 三擇一"
     },
     "savedStrategyId": {
      "type": "string",
      "description": "自己工作區的策略 id（需 workspace:read）"
     },
     "period": {
      "type": "string",
      "enum": [
       "1y",
       "3y",
       "5y",
       "covid-2020",
       "bear-2022"
      ],
      "description": "近 N 年或壓力期間；與 from/to 二擇一；都不給＝3y"
     },
     "from": {
      "$ref": "#/components/schemas/Month",
      "description": "自訂起始月（最早 2016-01）"
     },
     "to": {
      "$ref": "#/components/schemas/Month",
      "description": "自訂結束月（省略＝本月）"
     },
     "adjustSplits": {
      "type": "boolean",
      "default": true,
      "description": "分割還原（只套台股；美股源頭已還原）"
     },
     "adjustDividends": {
      "type": "boolean",
      "default": false,
      "description": "權息還原＝含息總報酬；配息型商品建議開"
     },
     "detail": {
      "type": "string",
      "enum": [
       "summary",
       "monthly",
       "full"
      ],
      "default": "monthly",
      "description": "summary 只回摘要（省 token）；monthly 加逐月；full 再加每月各部位股數×價格"
     },
     "fullMonthsOnly": {
      "type": "boolean",
      "default": false,
      "description": "只算完整月份（不含本月至今）"
     },
     "benchmark": {
      "type": "string",
      "description": "固定比較標的（例 0050）；回應另給 benchmark"
     },
     "periods": {
      "type": "array",
      "items": {
       "type": "string",
       "enum": [
        "1y",
        "3y",
        "5y",
        "covid-2020",
        "bear-2022"
       ]
      },
      "description": "一次回多個期間的摘要（byPeriod）"
     }
    }
   },
   "PortfolioMetrics": {
    "type": "object",
    "description": "以時間加權月報酬計算",
    "properties": {
     "periods": {
      "type": "integer"
     },
     "annualizedReturnPct": {
      "type": "number"
     },
     "volatilityPct": {
      "type": "number"
     },
     "sharpe": {
      "type": "number"
     },
     "sortino": {
      "type": "number"
     },
     "maxDrawdownPct": {
      "type": "number"
     },
     "maxDrawdownMonths": {
      "type": "integer",
      "deprecated": true,
      "description": "舊名，數值同 longestUnderwaterMonths（最長套牢月數，不是最大回檔那一段的長度）；新程式請用 longestUnderwaterMonths"
     },
     "calmar": {
      "type": "number"
     },
     "winRatePct": {
      "type": "number"
     },
     "bestMonthPct": {
      "type": "number"
     },
     "worstMonthPct": {
      "type": "number"
     },
     "longestUnderwaterMonths": {
      "type": "integer",
      "description": "自高點跌下到再創新高的最長月數（含進行中）；同 maxDrawdownMonths"
     }
    }
   },
   "BacktestSummary": {
    "type": "object",
    "properties": {
     "months": {
      "type": "integer"
     },
     "totalInvested": {
      "$ref": "#/components/schemas/Money"
     },
     "baselineInvested": {
      "$ref": "#/components/schemas/Money"
     },
     "strategyFinal": {
      "$ref": "#/components/schemas/Money"
     },
     "baselineFinal": {
      "$ref": "#/components/schemas/Money"
     },
     "difference": {
      "$ref": "#/components/schemas/Money"
     },
     "strategyReturnPct": {
      "type": "number"
     },
     "baselineReturnPct": {
      "type": "number"
     },
     "returnGapPct": {
      "type": "number",
      "description": "策略 − 傻傻定扣（百分點）；比較策略用這欄"
     },
     "totalSellTax": {
      "$ref": "#/components/schemas/Money"
     },
     "eventCounts": {
      "type": "object",
      "additionalProperties": {
       "type": "integer"
      },
      "description": "boost/reduce/switch/convert/rebalance/take_profit/stop_loss/skip/cash_reserve/reentry 各幾次"
     },
     "totalEvents": {
      "type": "integer",
      "description": "0＝行為與傻傻定扣相同"
     },
     "riskFreeAnnualPct": {
      "type": "number"
     },
     "irrAnnualPct": {
      "type": [
       "number",
       "null"
      ],
      "description": "資金加權年化報酬（IRR）：把投入時機算進去；逢低加碼的時機價值看這欄"
     },
     "baselineIrrAnnualPct": {
      "type": [
       "number",
       "null"
      ],
      "description": "傻傻定扣的資金加權年化報酬"
     },
     "irrGapPct": {
      "type": [
       "number",
       "null"
      ],
      "description": "時機價值：策略 − 定扣的資金加權年化差（百分點）"
     }
    }
   },
   "ReplayEvent": {
    "type": "object",
    "properties": {
     "month": {
      "$ref": "#/components/schemas/Month"
     },
     "type": {
      "type": "string",
      "enum": [
       "boost",
       "reduce",
       "switch",
       "convert",
       "rebalance",
       "take_profit",
       "stop_loss",
       "skip",
       "cash_reserve",
       "reentry"
      ]
     },
     "label": {
      "type": "string"
     },
     "detail": {
      "type": "string"
     },
     "amount": {
      "$ref": "#/components/schemas/Money"
     }
    }
   },
   "ReplayPosition": {
    "type": "object",
    "properties": {
     "symbol": {
      "type": "string"
     },
     "units": {
      "type": "number"
     },
     "price": {
      "$ref": "#/components/schemas/Money"
     },
     "value": {
      "$ref": "#/components/schemas/Money"
     },
     "weightPct": {
      "type": "number"
     }
    }
   },
   "ReplayPoint": {
    "type": "object",
    "properties": {
     "month": {
      "$ref": "#/components/schemas/Month"
     },
     "drawdownPct": {
      "type": "number",
      "description": "依扣款排程評估的大盤回檔 %"
     },
     "action": {
      "type": "string"
     },
     "events": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/ReplayEvent"
      }
     },
     "invested": {
      "$ref": "#/components/schemas/Money"
     },
     "strategyValue": {
      "$ref": "#/components/schemas/Money"
     },
     "baselineValue": {
      "$ref": "#/components/schemas/Money"
     },
     "holdingsValue": {
      "$ref": "#/components/schemas/Money"
     },
     "cash": {
      "$ref": "#/components/schemas/Money"
     },
     "cumulativeInvested": {
      "$ref": "#/components/schemas/Money"
     },
     "pnl": {
      "$ref": "#/components/schemas/Money"
     },
     "pnlPct": {
      "type": "number"
     },
     "matchedRegime": {
      "type": [
       "object",
       "null"
      ],
      "description": "該月命中的情境（無情境或都沒命中為 null）；最後一期＝本月訊號",
      "properties": {
       "index": {
        "type": "integer"
       },
       "label": {
        "type": "string"
       }
      }
     },
     "positions": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/ReplayPosition"
      },
      "description": "僅 detail=full"
     },
     "strategyDrawdownPct": {
      "type": [
       "number",
       "null"
      ],
      "description": "策略時間加權淨值自前高的回檔 %（水下曲線）"
     },
     "baselineDrawdownPct": {
      "type": [
       "number",
       "null"
      ],
      "description": "定扣的同口徑回檔 %"
     }
    }
   },
   "Coverage": {
    "type": "object",
    "properties": {
     "symbol": {
      "type": "string"
     },
     "firstDate": {
      "type": "string"
     },
     "lastDate": {
      "type": "string"
     },
     "source": {
      "type": "string"
     }
    }
   },
   "BacktestResult": {
    "type": "object",
    "properties": {
     "window": {
      "$ref": "#/components/schemas/Window"
     },
     "summary": {
      "$ref": "#/components/schemas/BacktestSummary"
     },
     "metrics": {
      "$ref": "#/components/schemas/PortfolioMetrics"
     },
     "baselineMetrics": {
      "$ref": "#/components/schemas/PortfolioMetrics"
     },
     "events": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/ReplayEvent"
      }
     },
     "points": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/ReplayPoint"
      },
      "description": "detail=monthly 或 full 才有"
     },
     "adjustments": {
      "type": "object",
      "properties": {
       "splits": {
        "type": "boolean"
       },
       "dividends": {
        "type": "boolean"
       }
      }
     },
     "coverage": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Coverage"
      }
     },
     "costBasis": {
      "type": "string",
      "description": "交易成本口徑全文"
     },
     "label": {
      "type": "string"
     },
     "plainText": {
      "type": "string",
      "description": "平台審定的一句話結論（只描述歷史、不含建議）"
     },
     "currentSignal": {
      "$ref": "#/components/schemas/Signal"
     },
     "benchmark": {
      "$ref": "#/components/schemas/Benchmark"
     },
     "byPeriod": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/PeriodSummary"
      }
     }
    }
   },
   "ValidateResult": {
    "type": "object",
    "properties": {
     "valid": {
      "type": "boolean"
     },
     "errors": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/ErrorDetail"
      }
     },
     "warnings": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "normalized": {
      "$ref": "#/components/schemas/EngineDsl"
     },
     "derived": {
      "type": "object",
      "properties": {
       "symbols": {
        "type": "array",
        "items": {
         "type": "string"
        }
       },
       "debitsPerMonth": {
        "type": "integer"
       },
       "monthlyBase": {
        "$ref": "#/components/schemas/Money"
       },
       "requiresRiskDisclosure": {
        "type": "boolean"
       }
      }
     }
    }
   },
   "BatchRequest": {
    "type": "object",
    "required": [
     "variants"
    ],
    "properties": {
     "variants": {
      "type": "array",
      "minItems": 1,
      "maxItems": 20,
      "items": {
       "type": "object",
       "required": [
        "label"
       ],
       "properties": {
        "label": {
         "type": "string",
         "maxLength": 40
        },
        "dsl": {
         "$ref": "#/components/schemas/EngineDsl"
        },
        "savedStrategyId": {
         "type": "string",
         "description": "與 dsl 二擇一"
        },
        "strategyId": {
         "type": "string"
        },
        "period": {
         "type": "string",
         "description": "這組自己的期間（可省略＝用共用期間）"
        },
        "from": {
         "$ref": "#/components/schemas/Month"
        },
        "to": {
         "$ref": "#/components/schemas/Month"
        }
       }
      }
     },
     "period": {
      "type": "string",
      "enum": [
       "1y",
       "3y",
       "5y",
       "covid-2020",
       "bear-2022"
      ],
      "description": "近 N 年或壓力期間；與 from/to 二擇一；都不給＝3y"
     },
     "from": {
      "$ref": "#/components/schemas/Month",
      "description": "自訂起始月（最早 2016-01）"
     },
     "to": {
      "$ref": "#/components/schemas/Month",
      "description": "自訂結束月（省略＝本月）"
     },
     "adjustSplits": {
      "type": "boolean",
      "default": true,
      "description": "分割還原（只套台股；美股源頭已還原）"
     },
     "adjustDividends": {
      "type": "boolean",
      "default": false,
      "description": "權息還原＝含息總報酬；配息型商品建議開"
     },
     "sortBy": {
      "type": "string",
      "enum": [
       "returnGapPct",
       "irrGapPct",
       "strategyReturnPct",
       "sharpe",
       "calmar",
       "maxDrawdownPct"
      ],
      "default": "returnGapPct"
     },
     "fullMonthsOnly": {
      "type": "boolean",
      "default": false,
      "description": "只算完整月份（不含本月至今）"
     },
     "benchmark": {
      "type": "string",
      "description": "固定比較標的（例 0050）；回應另給 benchmark"
     },
     "periods": {
      "type": "array",
      "items": {
       "type": "string",
       "enum": [
        "1y",
        "3y",
        "5y",
        "covid-2020",
        "bear-2022"
       ]
      },
      "description": "一次回多個期間的摘要（byPeriod）"
     },
     "detail": {
      "type": "string",
      "enum": [
       "summary",
       "monthly"
      ],
      "default": "summary",
      "description": "monthly＝每列附逐月曲線"
     }
    }
   },
   "BatchRow": {
    "type": "object",
    "properties": {
     "index": {
      "type": "integer",
      "description": "v1.2：對應 variants 陣列的第幾組（0 起算；結果依 sortBy 排序後仍可對回）"
     },
     "label": {
      "type": "string"
     },
     "valid": {
      "type": "boolean"
     },
     "error": {
      "$ref": "#/components/schemas/Error"
     },
     "summary": {
      "$ref": "#/components/schemas/BacktestSummary"
     },
     "metrics": {
      "$ref": "#/components/schemas/PortfolioMetrics"
     },
     "totalEvents": {
      "type": "integer"
     },
     "window": {
      "$ref": "#/components/schemas/Window"
     },
     "plainText": {
      "type": "string"
     },
     "baselineMetrics": {
      "$ref": "#/components/schemas/PortfolioMetrics"
     },
     "benchmark": {
      "$ref": "#/components/schemas/Benchmark"
     },
     "warnings": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "notices": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Notice"
      }
     },
     "byPeriod": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/PeriodSummary"
      }
     },
     "points": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/ReplayPoint"
      },
      "description": "detail=monthly／full 才有；v1.2 起與 /backtest/run 同形（含回檔、事件）"
     }
    }
   },
   "SandboxScript": {
    "type": "object",
    "properties": {
     "key": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "granularity": {
      "type": "string",
      "enum": [
       "day",
       "month"
      ]
     },
     "desc": {
      "type": "string"
     },
     "periods": {
      "type": "integer"
     },
     "real": {
      "type": "boolean",
      "description": "true＝真實歷史路徑"
     }
    }
   },
   "SandboxResult": {
    "type": "object",
    "description": "劇本資訊、逐期資產軌跡、期末持倉、觸發明細（同網頁 /demo）",
    "properties": {
     "script": {
      "type": "object",
      "additionalProperties": true
     },
     "series": {
      "type": "array",
      "items": {
       "type": "object",
       "additionalProperties": true
      }
     },
     "finalHoldings": {
      "type": "array",
      "items": {
       "type": "object",
       "additionalProperties": true
      }
     },
     "triggers": {
      "type": "array",
      "items": {
       "type": "object",
       "additionalProperties": true
      }
     },
     "limitations": {
      "type": "array",
      "items": {
       "type": "string"
      }
     }
    },
    "additionalProperties": true
   },
   "IndicatorRow": {
    "type": "object",
    "properties": {
     "month": {
      "$ref": "#/components/schemas/Month"
     },
     "taiexClose": {
      "$ref": "#/components/schemas/Money"
     },
     "drawdownPct": {
      "type": "number",
      "description": "月內最深回檔 %"
     },
     "indicators": {
      "type": "object",
      "description": "16 個指標當月值；無資料為 null",
      "additionalProperties": {
       "type": [
        "string",
        "number",
        "null"
       ]
      }
     },
     "publishedMonth": {
      "type": "object",
      "description": "各統計指標實際使用的資料月份；expected＝該月應可取得的發布月",
      "additionalProperties": {
       "type": "string"
      }
     },
     "text": {
      "type": "object",
      "additionalProperties": {
       "type": "string"
      },
      "description": "各指標當月值的白話"
     },
     "raw": {
      "type": "object",
      "properties": {
       "bizLightScore": {
        "type": [
         "number",
         "null"
        ]
       },
       "leadingIndex": {
        "type": [
         "number",
         "null"
        ]
       },
       "cpiYoYPct": {
        "type": [
         "number",
         "null"
        ]
       }
      }
     },
     "derivedKeys": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "當月沒有真實資料、由引擎推導代入的指標"
     },
     "staleKeys": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "史料未更新、沿用最後一筆已發布值的指標"
     }
    }
   },
   "Scorecard": {
    "type": "object",
    "additionalProperties": true,
    "properties": {
     "score": {
      "type": "number"
     },
     "zone": {
      "type": "string",
      "enum": [
       "deep_bottom",
       "bottom",
       "neutral",
       "overheat"
      ]
     },
     "zoneText": {
      "type": "string"
     },
     "coverage": {
      "type": "object",
      "properties": {
       "available": {
        "type": "integer"
       },
       "total": {
        "type": "integer"
       }
      }
     },
     "range": {
      "type": "object",
      "properties": {
       "min": {
        "type": "integer"
       },
       "max": {
        "type": "integer"
       }
      }
     },
     "weights": {
      "type": "array",
      "items": {
       "type": "object",
       "additionalProperties": true
      }
     },
     "asOf": {
      "type": [
       "string",
       "null"
      ]
     },
     "breadthAsOf": {
      "type": [
       "string",
       "null"
      ]
     },
     "bizLightMonth": {
      "type": [
       "string",
       "null"
      ]
     },
     "staleKeys": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "v1.2：政府統計史料未更新、沿用最後一筆已發布值的指標（biz_light、biz_light_trend、leading_trend、cpi）"
     },
     "publishedMonth": {
      "type": "object",
      "additionalProperties": true,
      "description": "各政府統計資料集最後一筆月份與應有月份（expected）"
     }
    }
   },
   "Series": {
    "type": "object",
    "properties": {
     "symbol": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "currency": {
      "type": "string",
      "enum": [
       "TWD",
       "USD",
       "POINT"
      ],
      "description": "POINT＝指數點數"
     },
     "interval": {
      "type": "string",
      "enum": [
       "day",
       "month"
      ]
     },
     "firstDate": {
      "type": "string"
     },
     "lastDate": {
      "type": "string"
     },
     "source": {
      "type": "string"
     },
     "bars": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "date": {
         "type": "string",
         "description": "day＝YYYY-MM-DD；month＝YYYY-MM"
        },
        "close": {
         "$ref": "#/components/schemas/Money"
        },
        "adjClose": {
         "$ref": "#/components/schemas/Money"
        }
       }
      }
     },
     "adjust": {
      "type": "string"
     }
    }
   },
   "Freshness": {
    "type": "object",
    "properties": {
     "symbol": {
      "type": "string"
     },
     "lastDate": {
      "type": "string"
     },
     "source": {
      "type": "string"
     },
     "fetchedOn": {
      "type": "string"
     },
     "stale": {
      "type": "boolean"
     },
     "firstDate": {
      "type": "string"
     }
    }
   },
   "Product": {
    "type": "object",
    "properties": {
     "symbol": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "market": {
      "type": "string",
      "enum": [
       "twse",
       "tpex",
       "us"
      ]
     },
     "type": {
      "type": "string",
      "enum": [
       "stock",
       "etf",
       "letf"
      ]
     },
     "price": {
      "$ref": "#/components/schemas/Money"
     },
     "priceUsd": {
      "oneOf": [
       {
        "$ref": "#/components/schemas/Money"
       },
       {
        "type": "null"
       }
      ]
     },
     "priceDate": {
      "type": [
       "string",
       "null"
      ]
     },
     "firstDate": {
      "type": [
       "string",
       "null"
      ],
      "description": "本機資料最早日（能回測多久）"
     },
     "requiresRiskDisclosure": {
      "type": "boolean"
     },
     "noDailyLimit": {
      "type": "boolean"
     },
     "currency": {
      "type": "string",
      "enum": [
       "TWD",
       "USD"
      ]
     },
     "category": {
      "type": "string",
      "description": "例：台股市值型 ETF、美股個股"
     },
     "categoryCode": {
      "type": [
       "string",
       "null"
      ],
      "enum": [
       "tw_equity_etf",
       "tw_dividend_etf",
       "us_treasury_etf",
       "ig_corp_bond_etf",
       "tw_leveraged_etf",
       "tw_stock",
       "us_equity_etf",
       "global_equity_etf",
       "us_stock",
       null
      ],
      "description": "v1.2 穩定分類代碼（分組／篩選用，不要比對中文名稱）"
     },
     "plain": {
      "type": "string",
      "description": "一句話白話介紹（只描述商品，不評價）"
     },
     "fxRate": {
      "oneOf": [
       {
        "$ref": "#/components/schemas/Money"
       },
       {
        "type": "null"
       }
      ]
     },
     "dataSource": {
      "type": "string"
     }
    }
   },
   "Catalog": {
    "type": "object",
    "properties": {
     "dslVersion": {
      "type": "string"
     },
     "dslJsonSchema": {
      "type": "object",
      "additionalProperties": true
     },
     "blocks": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "key": {
         "type": "string"
        },
        "name": {
         "type": "string"
        },
        "fields": {
         "type": "array",
         "items": {
          "type": "string"
         }
        },
        "rules": {
         "type": "array",
         "items": {
          "type": "string"
         }
        },
        "example": {
         "type": "object",
         "additionalProperties": true
        }
       }
      }
     },
     "indicators": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "key": {
         "$ref": "#/components/schemas/RegimeIndicator"
        },
        "category": {
         "type": "string"
        },
        "valueType": {
         "type": "string",
         "enum": [
          "enum",
          "number"
         ]
        },
        "ops": {
         "type": "array",
         "items": {
          "type": "string"
         }
        },
        "values": {
         "type": "array",
         "items": {
          "type": "object",
          "properties": {
           "value": {
            "type": "string"
           },
           "text": {
            "type": "string"
           }
          }
         }
        },
        "range": {
         "type": "object",
         "properties": {
          "min": {
           "type": "number"
          },
          "max": {
           "type": "number"
          }
         }
        },
        "source": {
         "type": "string"
        },
        "publishLagMonths": {
         "type": "integer"
        },
        "note": {
         "type": "string"
        }
       }
      }
     },
     "drawdown": {
      "type": "object",
      "additionalProperties": true
     },
     "scorecard": {
      "type": "object",
      "additionalProperties": true
     },
     "products": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Product"
      }
     },
     "limits": {
      "type": "object",
      "additionalProperties": true
     },
     "methodology": {
      "type": "object",
      "additionalProperties": true
     },
     "examples": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "name": {
         "type": "string"
        },
        "dsl": {
         "$ref": "#/components/schemas/EngineDsl"
        }
       }
      }
     },
     "firstSteps": {
      "type": "array",
      "items": {
       "type": "string"
      }
     }
    }
   },
   "Template": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "title": {
      "type": "string"
     },
     "audience": {
      "type": "string"
     },
     "tagline": {
      "type": "string"
     },
     "bullets": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "involvesLeveraged": {
      "type": "boolean"
     },
     "defaults": {
      "type": "object",
      "additionalProperties": true
     },
     "exampleDsl": {
      "$ref": "#/components/schemas/EngineDsl"
     },
     "implementationNotes": {
      "type": "array",
      "items": {
       "type": "string"
      },
      "description": "模板說明與目前實作的差異（誠實揭露）"
     }
    }
   },
   "Strategy": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "tagline": {
      "type": "string"
     },
     "audience": {
      "type": "string"
     },
     "risk": {
      "type": "string",
      "enum": [
       "low",
       "mid",
       "high"
      ]
     },
     "tags": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "dsl": {
      "$ref": "#/components/schemas/EngineDsl"
     },
     "report": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "period": {
         "type": "string",
         "enum": [
          "1y",
          "3y",
          "5y"
         ]
        },
        "months": {
         "type": "integer"
        },
        "strategyReturnPct": {
         "type": "number"
        },
        "baselineReturnPct": {
         "type": "number"
        },
        "returnGapPct": {
         "type": "number"
        },
        "triggers": {
         "type": "integer"
        },
        "irrAnnualPct": {
         "type": [
          "number",
          "null"
         ],
         "description": "v1.2 資金加權年化（%）"
        },
        "baselineIrrAnnualPct": {
         "type": [
          "number",
          "null"
         ]
        },
        "irrGapPct": {
         "type": [
          "number",
          "null"
         ],
         "description": "v1.2 策略 − 定扣的資金加權年化差（百分點）"
        },
        "dataSource": {
         "type": "string"
        },
        "metrics": {
         "$ref": "#/components/schemas/PortfolioMetrics"
        },
        "baselineMetrics": {
         "$ref": "#/components/schemas/PortfolioMetrics"
        }
       }
      }
     },
     "monthlyAmount": {
      "$ref": "#/components/schemas/Money"
     }
    }
   },
   "SavedStrategyInput": {
    "type": "object",
    "required": [
     "name"
    ],
    "description": "dsl、fromStrategyId、fromTemplateId 三擇一",
    "properties": {
     "name": {
      "type": "string",
      "maxLength": 40
     },
     "note": {
      "type": "string",
      "maxLength": 500
     },
     "tags": {
      "type": "array",
      "maxItems": 10,
      "items": {
       "type": "string",
       "maxLength": 20
      }
     },
     "dsl": {
      "$ref": "#/components/schemas/EngineDsl"
     },
     "fromStrategyId": {
      "type": "string",
      "description": "從策略館複製"
     },
     "fromTemplateId": {
      "type": "string",
      "description": "從情境模板 exampleDsl 複製"
     },
     "snapshot": {
      "type": "string",
      "enum": [
       "1y",
       "3y",
       "5y"
      ],
      "description": "存檔時順便回測並記下結果"
     }
    }
   },
   "SavedStrategy": {
    "type": "object",
    "properties": {
     "id": {
      "type": "string",
      "examples": [
       "s_8f2k1q9x0a"
      ]
     },
     "name": {
      "type": "string"
     },
     "note": {
      "type": "string"
     },
     "tags": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "source": {
      "type": "string",
      "description": "api／import／fork:<策略館 id>／template:<模板 id>"
     },
     "dsl": {
      "$ref": "#/components/schemas/EngineDsl"
     },
     "backtestSnapshot": {
      "type": [
       "object",
       "null"
      ],
      "properties": {
       "period": {
        "type": "string"
       },
       "strategyReturnPct": {
        "type": "number"
       },
       "baselineReturnPct": {
        "type": "number"
       },
       "months": {
        "type": "integer"
       },
       "dataSource": {
        "type": "string"
       },
       "at": {
        "type": "string",
        "format": "date-time"
       }
      }
     },
     "version": {
      "type": "integer"
     },
     "createdAt": {
      "type": "string",
      "format": "date-time"
     },
     "updatedAt": {
      "type": "string",
      "format": "date-time"
     }
    }
   },
   "PortfolioStrategyRef": {
    "type": "object",
    "required": [
     "label"
    ],
    "description": "savedStrategyId、strategyId、dsl 三擇一",
    "properties": {
     "label": {
      "type": "string",
      "maxLength": 40
     },
     "savedStrategyId": {
      "type": "string"
     },
     "strategyId": {
      "type": "string"
     },
     "dsl": {
      "$ref": "#/components/schemas/EngineDsl"
     },
     "enabled": {
      "type": "boolean",
      "default": true
     }
    }
   },
   "PortfolioInput": {
    "type": "object",
    "required": [
     "name",
     "monthlyBudget"
    ],
    "properties": {
     "name": {
      "type": "string",
      "maxLength": 40
     },
     "note": {
      "type": "string",
      "maxLength": 500
     },
     "monthlyBudget": {
      "$ref": "#/components/schemas/Money"
     },
     "goal": {
      "type": "object",
      "properties": {
       "type": {
        "type": "string",
        "enum": [
         "house",
         "car",
         "retire",
         "edu",
         "dream",
         "custom"
        ]
       },
       "label": {
        "type": "string",
        "maxLength": 40
       },
       "targetAmount": {
        "$ref": "#/components/schemas/Money"
       },
       "targetDate": {
        "$ref": "#/components/schemas/Month"
       }
      }
     },
     "targetWeights": {
      "type": "array",
      "maxItems": 10,
      "items": {
       "$ref": "#/components/schemas/Allocation"
      },
      "description": "目標配置（合計 100；可含 CASH）"
     },
     "holdings": {
      "type": "array",
      "maxItems": 20,
      "description": "自填的模擬持倉（非真實帳戶）",
      "items": {
       "type": "object",
       "required": [
        "symbol",
        "units"
       ],
       "properties": {
        "symbol": {
         "type": "string"
        },
        "units": {
         "type": "number",
         "minimum": 0
        },
        "cost": {
         "$ref": "#/components/schemas/Money"
        }
       }
      }
     },
     "cash": {
      "$ref": "#/components/schemas/Money"
     },
     "strategies": {
      "type": "array",
      "maxItems": 10,
      "items": {
       "$ref": "#/components/schemas/PortfolioStrategyRef"
      }
     },
     "tags": {
      "type": "array",
      "maxItems": 10,
      "items": {
       "type": "string",
       "maxLength": 20
      },
      "description": "例：客戶代號、方案 A／B"
     }
    }
   },
   "GoalCheck": {
    "type": "object",
    "properties": {
     "targetAmount": {
      "$ref": "#/components/schemas/Money"
     },
     "targetDate": {
      "$ref": "#/components/schemas/Month"
     },
     "monthsToGoal": {
      "type": [
       "integer",
       "null"
      ]
     },
     "currentValue": {
      "$ref": "#/components/schemas/Money"
     },
     "requiredMonthly": {
      "oneOf": [
       {
        "$ref": "#/components/schemas/Money"
       },
       {
        "type": "null"
       }
      ]
     },
     "committedMonthly": {
      "$ref": "#/components/schemas/Money"
     },
     "gapMonthly": {
      "oneOf": [
       {
        "$ref": "#/components/schemas/Money"
       },
       {
        "type": "null"
       }
      ]
     },
     "status": {
      "type": "string",
      "enum": [
       "on_track",
       "behind",
       "achieved",
       "no_deadline"
      ]
     },
     "basis": {
      "type": "string",
      "description": "保守口徑說明：不計投資報酬"
     }
    }
   },
   "Portfolio": {
    "allOf": [
     {
      "$ref": "#/components/schemas/PortfolioInput"
     },
     {
      "type": "object",
      "properties": {
       "id": {
        "type": "string",
        "examples": [
         "p_3m9x7c2b1d"
        ]
       },
       "version": {
        "type": "integer"
       },
       "createdAt": {
        "type": "string",
        "format": "date-time"
       },
       "updatedAt": {
        "type": "string",
        "format": "date-time"
       },
       "derived": {
        "type": "object",
        "description": "伺服器推導，不採信呼叫端自報",
        "properties": {
         "monthlyCommitted": {
          "$ref": "#/components/schemas/Money"
         },
         "budgetRemaining": {
          "$ref": "#/components/schemas/Money"
         },
         "requiresRiskDisclosure": {
          "type": "boolean"
         },
         "currentValue": {
          "$ref": "#/components/schemas/Money"
         },
         "goalCheck": {
          "oneOf": [
           {
            "$ref": "#/components/schemas/GoalCheck"
           },
           {
            "type": "null"
           }
          ]
         },
         "monthlyCommittedMax": {
          "$ref": "#/components/schemas/Money"
         },
         "maxDeviationPct": {
          "type": [
           "number",
           "null"
          ]
         }
        }
       },
       "warnings": {
        "type": "array",
        "items": {
         "type": "string"
        }
       }
      }
     }
    ]
   },
   "PortfolioBacktest": {
    "type": "object",
    "properties": {
     "window": {
      "$ref": "#/components/schemas/Window"
     },
     "portfolio": {
      "type": "object",
      "description": "各策略逐月加總後的投組結果",
      "properties": {
       "summary": {
        "$ref": "#/components/schemas/BacktestSummary"
       },
       "metrics": {
        "$ref": "#/components/schemas/PortfolioMetrics"
       },
       "baselineMetrics": {
        "$ref": "#/components/schemas/PortfolioMetrics"
       },
       "points": {
        "type": "array",
        "items": {
         "type": "object",
         "properties": {
          "month": {
           "$ref": "#/components/schemas/Month"
          },
          "invested": {
           "$ref": "#/components/schemas/Money"
          },
          "cumulativeInvested": {
           "$ref": "#/components/schemas/Money"
          },
          "value": {
           "$ref": "#/components/schemas/Money"
          },
          "baselineValue": {
           "$ref": "#/components/schemas/Money"
          },
          "pnlPct": {
           "type": "number"
          }
         }
        }
       },
       "plainText": {
        "type": "string"
       },
       "finalAllocation": {
        "type": "array",
        "items": {
         "type": "object",
         "properties": {
          "symbol": {
           "type": "string"
          },
          "name": {
           "type": "string"
          },
          "value": {
           "$ref": "#/components/schemas/Money"
          },
          "weightPct": {
           "type": "number"
          }
         }
        }
       }
      }
     },
     "byStrategy": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "label": {
         "type": "string"
        },
        "summary": {
         "$ref": "#/components/schemas/BacktestSummary"
        },
        "metrics": {
         "$ref": "#/components/schemas/PortfolioMetrics"
        },
        "events": {
         "type": "array",
         "items": {
          "$ref": "#/components/schemas/ReplayEvent"
         }
        },
        "plainText": {
         "type": "string"
        },
        "points": {
         "type": "array",
         "items": {
          "type": "object",
          "properties": {
           "month": {
            "$ref": "#/components/schemas/Month"
           },
           "value": {
            "$ref": "#/components/schemas/Money"
           },
           "baselineValue": {
            "$ref": "#/components/schemas/Money"
           },
           "matchedRegime": {
            "type": [
             "object",
             "null"
            ]
           }
          }
         }
        }
       }
      }
     },
     "goalCheck": {
      "oneOf": [
       {
        "$ref": "#/components/schemas/GoalCheck"
       },
       {
        "type": "null"
       }
      ]
     },
     "currentSignal": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Signal"
      }
     },
     "derived": {
      "type": "object",
      "additionalProperties": true
     },
     "coverage": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Coverage"
      }
     },
     "adjustments": {
      "type": "object",
      "additionalProperties": true
     },
     "benchmark": {
      "$ref": "#/components/schemas/Benchmark"
     },
     "byPeriod": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/PeriodSummary"
      }
     }
    }
   },
   "RebalancePlan": {
    "type": "object",
    "properties": {
     "asOf": {
      "type": "string"
     },
     "totalValue": {
      "$ref": "#/components/schemas/Money"
     },
     "maxDeviationPct": {
      "type": "number"
     },
     "rows": {
      "type": "array",
      "items": {
       "type": "object",
       "properties": {
        "symbol": {
         "type": "string"
        },
        "currentPct": {
         "type": "number"
        },
        "targetPct": {
         "type": "number"
        },
        "deviationPct": {
         "type": "number"
        },
        "price": {
         "$ref": "#/components/schemas/Money"
        },
        "name": {
         "type": "string"
        }
       }
      }
     },
     "legs": {
      "type": "array",
      "description": "試算的調整買賣腿（整股、賣先買後）；只試算，不執行、不構成建議",
      "items": {
       "type": "object",
       "properties": {
        "side": {
         "type": "string",
         "enum": [
          "sell",
          "buy"
         ]
        },
        "symbol": {
         "type": "string"
        },
        "units": {
         "type": "number"
        },
        "amount": {
         "$ref": "#/components/schemas/Money"
        },
        "estimatedSellTax": {
         "$ref": "#/components/schemas/Money",
         "description": "v1.2 賣出腿的證交稅估算（股票 0.3%、ETF 0.1%、債券 ETF 免徵、美股無）"
        }
       }
      }
     },
     "mode": {
      "type": "string",
      "enum": [
       "sell",
       "buyOnly"
      ]
     },
     "newMoney": {
      "$ref": "#/components/schemas/Money"
     },
     "fullCatchUpMoney": {
      "$ref": "#/components/schemas/Money",
      "description": "v1.2 mode=buyOnly：不賣出、讓每一檔都回到目標所需的最小新資金（＝max(各檔市值 ÷ 目標比例) − 目前總值；與 newMoney 無關）"
     },
     "note": {
      "type": "string"
     }
    }
   },
   "WorkspaceExport": {
    "type": "object",
    "required": [
     "app",
     "version"
    ],
    "properties": {
     "app": {
      "type": "string",
      "const": "smartinvest-workspace"
     },
     "version": {
      "type": "integer",
      "const": 1
     },
     "strategies": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/SavedStrategy"
      }
     },
     "portfolios": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/Portfolio"
      }
     }
    }
   },
   "Status": {
    "type": "object",
    "properties": {
     "ok": {
      "type": "boolean"
     },
     "apiVersion": {
      "type": "string"
     },
     "engine": {
      "type": "string"
     },
     "dataAsOf": {
      "type": [
       "string",
       "null"
      ]
     },
     "staleSymbols": {
      "type": "array",
      "items": {
       "type": "string"
      }
     },
     "key": {
      "type": "object",
      "properties": {
       "id": {
        "type": "string"
       },
       "last4": {
        "type": "string"
       },
       "scopes": {
        "type": "array",
        "items": {
         "type": "string"
        }
       },
       "usedToday": {
        "type": "integer"
       },
       "dailyLimit": {
        "type": "integer"
       },
       "name": {
        "type": "string"
       },
       "perMinute": {
        "type": "object",
        "additionalProperties": {
         "type": "integer"
        }
       }
      }
     },
     "workspace": {
      "type": [
       "object",
       "null"
      ],
      "description": "有 workspace 權限時才有",
      "properties": {
       "strategies": {
        "type": "integer"
       },
       "strategiesMax": {
        "type": "integer"
       },
       "portfolios": {
        "type": "integer"
       },
       "portfoliosMax": {
        "type": "integer"
       },
       "bytes": {
        "type": "integer"
       },
       "bytesMax": {
        "type": "integer"
       }
      }
     },
     "staleDatasets": {
      "type": "array",
      "items": {
       "type": "object",
       "additionalProperties": true
      }
     },
     "lastRefresh": {
      "type": [
       "string",
       "null"
      ]
     }
    }
   },
   "Benchmark": {
    "type": "object",
    "properties": {
     "symbol": {
      "type": "string"
     },
     "name": {
      "type": "string"
     },
     "label": {
      "type": "string",
      "description": "v1.2 表頭用短標籤（單一標的＝代號；組合＝「組合基準」）"
     },
     "components": {
      "type": "array",
      "description": "v1.2 基準成分（單一標的為一筆 100%）",
      "items": {
       "$ref": "#/components/schemas/Allocation"
      }
     },
     "invested": {
      "$ref": "#/components/schemas/Money"
     },
     "final": {
      "$ref": "#/components/schemas/Money"
     },
     "returnPct": {
      "type": "number"
     },
     "returnGapPct": {
      "type": "number",
      "description": "策略 − 基準標的定扣（百分點）"
     },
     "irrAnnualPct": {
      "type": [
       "number",
       "null"
      ],
      "description": "v1.2：基準的資金加權年化（%）"
     },
     "irrGapPct": {
      "type": [
       "number",
       "null"
      ],
      "description": "v1.2：策略（或投組）− 基準的資金加權年化差（百分點）"
     },
     "metrics": {
      "$ref": "#/components/schemas/PortfolioMetrics"
     },
     "note": {
      "type": "string"
     }
    }
   },
   "PeriodSummary": {
    "type": "object",
    "properties": {
     "period": {
      "type": "string"
     },
     "months": {
      "type": "integer"
     },
     "window": {
      "$ref": "#/components/schemas/Window"
     },
     "plainText": {
      "type": "string"
     },
     "summary": {
      "type": "object",
      "additionalProperties": true
     },
     "strategyReturnPct": {
      "type": "number"
     },
     "baselineReturnPct": {
      "type": "number"
     },
     "returnGapPct": {
      "type": "number"
     },
     "totalEvents": {
      "type": "integer"
     },
     "metrics": {
      "$ref": "#/components/schemas/PortfolioMetrics"
     },
     "baselineMetrics": {
      "$ref": "#/components/schemas/PortfolioMetrics"
     },
     "benchmark": {
      "$ref": "#/components/schemas/Benchmark"
     },
     "error": {
      "type": "object",
      "additionalProperties": true
     },
     "irrAnnualPct": {
      "type": [
       "number",
       "null"
      ]
     },
     "baselineIrrAnnualPct": {
      "type": [
       "number",
       "null"
      ]
     },
     "irrGapPct": {
      "type": [
       "number",
       "null"
      ]
     }
    }
   },
   "Signal": {
    "type": "object",
    "description": "本月訊號＝最後一期",
    "properties": {
     "label": {
      "type": "string"
     },
     "month": {
      "$ref": "#/components/schemas/Month"
     },
     "matchedRegime": {
      "type": [
       "object",
       "null"
      ],
      "properties": {
       "index": {
        "type": "integer"
       },
       "label": {
        "type": "string"
       }
      }
     },
     "action": {
      "type": "string"
     },
     "events": {
      "type": "array",
      "items": {
       "$ref": "#/components/schemas/ReplayEvent"
      }
     }
    }
   }
  }
 }
}