{
  "openapi": "3.1.0",
  "info": {
    "title": "The Turbotopia Fund API",
    "version": "1.8.0",
    "description": "Lokalt REST API for fondskonfigurasjon, porteføljesimulering og systemstatus."
  },
  "servers": [
    {
      "url": "/api/v1",
      "description": "Samme vert som dashboardet"
    }
  ],
  "paths": {
    "/health": {
      "get": {
        "summary": "Helsestatus",
        "operationId": "getHealth",
        "responses": {
          "200": {
            "description": "Tjenesten kjører",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Health" }
              }
            }
          }
        }
      }
    },
    "/meta": {
      "get": {
        "summary": "Applikasjons- og API-metadata",
        "operationId": "getMeta",
        "responses": {
          "200": {
            "description": "Metadata",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Meta" }
              }
            }
          }
        }
      }
    },
    "/config": {
      "get": {
        "summary": "Hent lagret fondskonfigurasjon",
        "operationId": "getConfig",
        "responses": {
          "200": {
            "description": "Gjeldende konfigurasjon",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/FundConfig" }
              }
            }
          }
        }
      },
      "put": {
        "summary": "Oppdater fondskonfigurasjonen",
        "description": "Erstatter eller oppdaterer konfigurasjonen i det persistente datavolumet.",
        "operationId": "updateConfig",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/FundConfig" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Konfigurasjonen ble lagret",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": { "type": "boolean" },
                    "config": { "$ref": "#/components/schemas/FundConfig" }
                  }
                }
              }
            }
          },
          "500": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/manual-transactions": {
      "get": {
        "summary": "Hent manuelle handler",
        "operationId": "getManualTransactions",
        "responses": {
          "200": {
            "description": "Liste over registrerte handler",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "transactions": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/ManualTransaction" }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Registrer manuell handel",
        "operationId": "createManualTransaction",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/ManualTransactionInput" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Handelen ble lagret",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": { "type": "boolean" },
                    "transaction": { "$ref": "#/components/schemas/ManualTransaction" },
                    "transactions": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/ManualTransaction" }
                    }
                  }
                }
              }
            }
          },
          "500": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/manual-transactions/{id}": {
      "delete": {
        "summary": "Fjern manuell handel",
        "operationId": "deleteManualTransaction",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Handelen ble fjernet",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": { "type": "boolean" },
                    "transactions": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/ManualTransaction" }
                    }
                  }
                }
              }
            }
          },
          "500": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/rebalance-approvals": {
      "get": {
        "summary": "Hent godkjente rebalanseringer",
        "operationId": "getRebalanceApprovals",
        "responses": {
          "200": {
            "description": "Liste over godkjenninger",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "approvals": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/RebalanceApproval" }
                    }
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Opprett eller oppdater rebalanseringsgodkjenning",
        "operationId": "upsertRebalanceApproval",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/RebalanceApprovalInput" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Godkjenningen ble lagret",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": { "type": "boolean" },
                    "approval": { "$ref": "#/components/schemas/RebalanceApproval" },
                    "approvals": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/RebalanceApproval" }
                    }
                  }
                }
              }
            }
          },
          "500": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/rebalance-approvals/{month}": {
      "delete": {
        "summary": "Fjern godkjenning for en måned",
        "operationId": "deleteRebalanceApproval",
        "parameters": [
          {
            "name": "month",
            "in": "path",
            "required": true,
            "schema": { "type": "string", "pattern": "^\\d{4}-\\d{2}$" }
          }
        ],
        "responses": {
          "200": {
            "description": "Godkjenningen ble fjernet",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": { "type": "boolean" },
                    "approvals": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/RebalanceApproval" }
                    }
                  }
                }
              }
            }
          },
          "500": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/reset": {
      "post": {
        "summary": "Reset fondet og start med blanke ark",
        "description": "Sletter handelsdata og godkjenninger, tømmer aksjeuniverset og nullstiller startkapital. Mandat, inntekt og datakilder beholdes.",
        "operationId": "resetFund",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["confirmation"],
                "properties": {
                  "confirmation": { "type": "string", "const": "RESETT" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Fondet ble resatt",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "ok": { "type": "boolean" },
                    "resetAt": { "type": "string", "format": "date-time" },
                    "config": { "$ref": "#/components/schemas/FundConfig" }
                  }
                }
              }
            }
          },
          "500": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/portfolio": {
      "get": {
        "summary": "Bygg og hent porteføljen",
        "description": "Returnerer historikk, posisjoner, handler, kostbasis og gevinst/tap.",
        "operationId": "getPortfolio",
        "parameters": [
          {
            "name": "refresh",
            "in": "query",
            "description": "Sett til 1 for å forsøke å hente ferske Yahoo Finance-data.",
            "schema": { "type": "string", "enum": ["0", "1"], "default": "0" }
          }
        ],
        "responses": {
          "200": {
            "description": "Beregnet portefølje",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Portfolio" }
              }
            }
          },
          "500": { "$ref": "#/components/responses/Error" }
        }
      }
    },
    "/stocks/search": {
      "get": {
        "summary": "Søk etter aksjer på Oslo Børs",
        "operationId": "searchStocks",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "description": "Selskapsnavn eller ticker, minst to tegn.",
            "schema": { "type": "string", "minLength": 2, "maxLength": 50 }
          }
        ],
        "responses": {
          "200": {
            "description": "Normaliserte søkeresultater fra Yahoo Finance",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "query": { "type": "string" },
                    "results": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/StockSearchResult" }
                    }
                  }
                }
              }
            }
          },
          "500": { "$ref": "#/components/responses/Error" }
        }
      }
    }
  },
  "components": {
    "responses": {
      "Error": {
        "description": "Feil",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      }
    },
    "schemas": {
      "Health": {
        "type": "object",
        "required": ["status", "version", "time", "dataDirectory"],
        "properties": {
          "status": { "type": "string", "examples": ["ok"] },
          "version": { "type": "string", "examples": ["1.8.0"] },
          "time": { "type": "string", "format": "date-time" },
          "dataDirectory": { "type": "string" }
        }
      },
      "Meta": {
        "type": "object",
        "properties": {
          "name": { "type": "string" },
          "version": { "type": "string" },
          "apiVersion": { "type": "string" },
          "documentation": { "type": "string" },
          "openapi": { "type": "string" },
          "changelog": { "type": "string" }
        }
      },
      "Stock": {
        "type": "object",
        "required": ["symbol", "name"],
        "properties": {
          "symbol": { "type": "string", "examples": ["EQNR.OL"] },
          "name": { "type": "string", "examples": ["Equinor"] }
        }
      },
      "Instrument": {
        "type": "object",
        "required": ["symbol", "fxSymbol", "name"],
        "properties": {
          "symbol": { "type": "string" },
          "fxSymbol": { "type": "string" },
          "name": { "type": "string" }
        }
      },
      "FundConfig": {
        "type": "object",
        "required": ["fundName", "startDate", "monthlyIncome", "contributionRate", "stockCap", "satelliteRatioToEquity", "stocks", "silver", "bitcoin"],
        "properties": {
          "fundName": { "type": "string" },
          "startDate": { "type": "string", "format": "date" },
          "universeAsOf": { "type": "string", "format": "date" },
          "monthlyIncome": { "type": "number", "exclusiveMinimum": 0 },
          "startupCash": { "type": "number", "minimum": 0 },
          "contributionRate": { "type": "number", "exclusiveMinimum": 0, "maximum": 1 },
          "stockCap": { "type": "number", "exclusiveMinimum": 0, "maximum": 0.2 },
          "satelliteRatioToEquity": { "type": "number", "minimum": 0, "maximum": 1 },
          "fractionalShares": { "type": "boolean", "const": false },
          "blankSlate": { "type": "boolean" },
          "lastResetAt": { "type": "string", "format": "date-time" },
          "commissionNok": { "type": "number", "minimum": 0 },
          "stocks": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/Stock" }
          },
          "silver": { "$ref": "#/components/schemas/Instrument" },
          "bitcoin": { "$ref": "#/components/schemas/Instrument" },
          "manualTransactions": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ManualTransaction" }
          },
          "rebalanceApprovals": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/RebalanceApproval" }
          }
        }
      },
      "ManualTransactionInput": {
        "type": "object",
        "required": ["date", "type", "symbol", "units", "price"],
        "properties": {
          "id": { "type": "string" },
          "date": { "type": "string", "format": "date" },
          "type": { "type": "string", "enum": ["buy", "sell"] },
          "assetClass": { "type": "string", "enum": ["Aksje", "Sølv", "Bitcoin"] },
          "symbol": { "type": "string" },
          "name": { "type": "string" },
          "units": { "type": "number", "exclusiveMinimum": 0 },
          "price": { "type": "number", "exclusiveMinimum": 0 },
          "commissionNok": { "type": "number", "minimum": 0 },
          "note": { "type": "string" }
        }
      },
      "ManualTransaction": {
        "allOf": [
          { "$ref": "#/components/schemas/ManualTransactionInput" }
        ]
      },
      "RebalanceApprovalInput": {
        "type": "object",
        "required": ["month", "trades"],
        "properties": {
          "month": { "type": "string", "pattern": "^\\d{4}-\\d{2}$" },
          "signalDate": { "type": ["string", "null"], "format": "date" },
          "executionDate": { "type": ["string", "null"], "format": "date" },
          "approvedAt": { "type": "string", "format": "date-time" },
          "note": { "type": "string" },
          "trades": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ManualTransactionInput" }
          }
        }
      },
      "RebalanceApproval": {
        "allOf": [
          { "$ref": "#/components/schemas/RebalanceApprovalInput" }
        ]
      },
      "StockSearchResult": {
        "type": "object",
        "properties": {
          "symbol": { "type": "string", "examples": ["MOWI.OL"] },
          "name": { "type": "string", "examples": ["Mowi ASA"] },
          "exchange": { "type": "string", "examples": ["Oslo"] },
          "sector": { "type": ["string", "null"] }
        }
      },
      "Position": {
        "type": "object",
        "properties": {
          "symbol": { "type": "string" },
          "name": { "type": "string" },
          "assetClass": { "type": "string", "enum": ["Aksje", "Sølv", "Bitcoin"] },
          "units": { "type": "number" },
          "price": { "type": "number" },
          "value": { "type": "number" },
          "costBasis": { "type": "number" },
          "averageCost": { "type": "number" },
          "realizedGain": { "type": "number" },
          "unrealizedGain": { "type": "number" },
          "unrealizedGainPct": { "type": "number" },
          "weight": { "type": "number" },
          "mandateWeight": { "type": ["number", "null"] },
          "effectiveStockCap": { "type": ["number", "null"] },
          "targetWeight": { "type": "number" },
          "drift": { "type": "number" }
        }
      },
      "Portfolio": {
        "type": "object",
        "properties": {
          "fund": { "type": "object", "additionalProperties": true },
          "summary": { "type": "object", "additionalProperties": true },
          "targetAllocation": { "type": "object", "additionalProperties": true },
          "positions": { "type": "array", "items": { "$ref": "#/components/schemas/Position" } },
          "series": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "contributions": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "latestRebalance": { "type": "object", "additionalProperties": true },
          "nextRebalance": { "type": "object", "additionalProperties": true },
          "transactions": { "type": "array", "items": { "type": "object", "additionalProperties": true } },
          "data": { "type": "object", "additionalProperties": true }
        }
      },
      "Error": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": { "type": "string" }
        }
      }
    }
  }
}
