{"version":3,"sources":["../src/errors.ts","../src/utils/serialize.ts","../src/client.ts","../src/types.ts","../src/utils/fund-type-validation.ts","../src/utils/date-validation.ts","../src/utils/response-mapping.ts","../src/utils/search-response-parser.ts","../src/utils/allocation-response-mapping.ts","../src/constants.ts","../src/cache-interface.ts","../src/cache.ts","../src/adapters/memory-cache-adapter.ts","../src/tefas-client.ts","../src/adapters/cloudflare-kv-cache-adapter.ts","../src/utils/performance-calculator.ts"],"names":["ApiError","_ApiError","message","cause","TefasError","code","details","InvalidFundTypeError","fundType","supportedTypes","InvalidDateError","date","reason","FundNotFoundError","fundCode","DateRangeError","startDate","endDate","TefasApiError","NetworkError","ResponseValidationError","ConfigurationError","isTefasError","error","isInvalidFundTypeError","isInvalidDateError","isFundNotFoundError","isDateRangeError","isTefasApiError","isNetworkError","isResponseValidationError","isConfigurationError","isHttpError","HttpError","isTimeoutError","TimeoutError","isValidationError","ValidationError","_HttpError","status","statusText","responseData","_TimeoutError","_ValidationError","validationDetails","serializeFormData","data","params","key","value","buildQueryString","searchParams","queryString","ApiClient","_ApiClient","config","url","options","method","lastError","attempt","delay","resolve","fullUrl","contentType","headers","body","controller","timeoutMs","timeoutId","response","errorData","path","base","cleanPath","fullPath","customHeaders","FundType","SUPPORTED_FUND_TYPES","FUND_TYPE_DESCRIPTIONS","isValidFundType","validateFundType","getSupportedFundTypes","getFundTypeDescription","getAllFundTypeDescriptions","toTefasApiFundType","fromTefasApiFundType","apiFundType","isSupportedFundType","getFundTypeValidationError","normalizeFundType","validateAndNormalizeFundType","normalized","getFundTypeDisplayName","getFundTypeCategory","ISO_DATE_REGEX","isValidDateFormat","parsedDate","validateDate","fieldName","parseDate","formatDate","getCurrentDate","addDays","days","newDate","getDaysBetween","start","end","isPastDate","today","isFutureDate","validateDateRange","maxDays","validateTefasDateLimit","fiveYearsAgo","limitDate","convertToTefasDateFormat","year","month","day","parseFuzzyDate","dateInput","referenceDate","trimmedInput","parsedResult","me","normalizeFuzzyDateRange","normalizedStartDate","normalizedEndDate","mapTefasFundData","apiData","timestamp","dateString","volume","investorCount","mapTefasResponse","apiResponse","fundData","index","mappedData","validateFundHistoryResponse","validateFundHistoryResponses","responses","filterByFundType","filterByFundCode","groupByFundCode","groups","sortByDate","a","b","sortByDateDescending","getUniqueFundCodes","fundCodes","getLatestByFundCode","sorted","latest","calculateTotalAssets","total","calculateAveragePrice","parseSearchResponse","results","i","item","parsed","fullName","errorMessage","isValidSearchResponse","convertTimestampToDate","parseCommaFormattedNumber","cleanValue","mapAllocationResponse","TEFAS_ALLOCATION_ENDPOINT","isCacheAdapterWithStats","adapter","TtlCache","entry","now","removed","oldestKey","oldestTime","MemoryCacheAdapter","ttl","FUNDFY_BASE_URL","FUNDFY_AUTH_TOKEN","FUNDFY_ORIGIN","TefasClient","configOverrides","query","limit","searchRequest","trimmedQuery","perTypeLimit","searchPromises","allResults","resultsMap","fundTypeResults","result","normalizedCode","skipFundDetails","resolvedFundType","searchResults","matchedFund","r","cacheKey","cachedResult","historyResponse","contentResponse","historyData","contentData","uniqueFundCodes","history","content","fundDetailsMap","totalFundCodes","detailsFetched","historyMap","contentMap","allKeys","mergedData","fundName","merged","requiredHeaders","header","throwOnNotFound","totalDays","MAX_DAYS","fundHistory","chunks","chunkStart","tentativeEnd","chunkEnd","chunkName","chunkHistory","chunkRequest","chunkResponse","tefasRequest","allFundTypes","fundContent","chunkContent","allocationRequest","request","fund","benchmark","segments","parts","operation","operationName","maxRetries","httpError","CloudflareKVCacheAdapter","seconds","expirationTtl","jsonValue","cursor","keysToDelete","k","deletePromises","PerformanceCalculator","riskFreeRate","frequency","minObservations","fundGroups","periodsPerYear","sortedData","prices","d","returns","cumulativeReturn","annualizedReturn","annualizedVolatility","sharpeRatio","existing","currentPrice","previousPrice","sum","acc","ret","years","product","standardDeviation","volatility","downsideReturns","downsideVolatility","maxDrawdown","peak","drawdown","fundReturns","benchmarkReturns","fundMean","mean","benchmarkMean","covariance","benchmarkVariance","activeReturns","fundRet","activeReturnMean","trackingError","beta","sample"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * TEFAS API Client Error Classes\n * \n * This module defines business-focused error classes for TEFAS operations.\n * All errors are designed to be user-friendly and actionable.\n */\n\n/**\n * Base error class for all API-related errors\n */\nexport class ApiError extends Error {\n  public readonly cause?: unknown;\n\n  constructor(message: string, cause?: unknown) {\n    super(message);\n    this.name = 'ApiError';\n    this.cause = cause;\n    \n    // Maintain proper stack trace for where our error was thrown\n    if (Error.captureStackTrace) {\n      Error.captureStackTrace(this, ApiError);\n    }\n  }\n}\n\n/**\n * Base error class for all TEFAS-related errors\n */\nexport class TefasError extends ApiError {\n  public readonly code: string;\n  public readonly details?: unknown;\n\n  constructor(message: string, code: string, details?: unknown) {\n    super(message);\n    this.name = 'TefasError';\n    this.code = code;\n    this.details = details;\n  }\n}\n\n/**\n * Error thrown when an invalid fund type is provided\n */\nexport class InvalidFundTypeError extends TefasError {\n  constructor(fundType: string) {\n    const supportedTypes = ['EMK', 'YAT', 'BYF', 'GYF', 'GSYF'];\n    super(\n      `Invalid fund type '${fundType}'. Supported types are: ${supportedTypes.join(', ')}`,\n      'INVALID_FUND_TYPE',\n      { providedType: fundType, supportedTypes }\n    );\n    this.name = 'InvalidFundTypeError';\n  }\n}\n\n/**\n * Error thrown when an invalid date format is provided\n */\nexport class InvalidDateError extends TefasError {\n  constructor(date: string, reason: string) {\n    super(\n      `Invalid date '${date}': ${reason}. Please use YYYY-MM-DD format (e.g., 2024-01-01)`,\n      'INVALID_DATE',\n      { providedDate: date, reason }\n    );\n    this.name = 'InvalidDateError';\n  }\n}\n\n/**\n * Error thrown when a specific fund code is not found\n */\nexport class FundNotFoundError extends TefasError {\n  constructor(fundCode: string) {\n    super(\n      `Fund '${fundCode}' not found. Please check the fund code and try again`,\n      'FUND_NOT_FOUND',\n      { fundCode }\n    );\n    this.name = 'FundNotFoundError';\n  }\n}\n\n/**\n * Error thrown when date range is invalid\n */\nexport class DateRangeError extends TefasError {\n  constructor(startDate: string, endDate: string, reason: string) {\n    super(\n      `Invalid date range from '${startDate}' to '${endDate}': ${reason}`,\n      'INVALID_DATE_RANGE',\n      { startDate, endDate, reason }\n    );\n    this.name = 'DateRangeError';\n  }\n}\n\n/**\n * Error thrown when TEFAS API returns an error\n */\nexport class TefasApiError extends TefasError {\n  constructor(message: string, details?: unknown) {\n    super(\n      `TEFAS API error: ${message}`,\n      'TEFAS_API_ERROR',\n      details\n    );\n    this.name = 'TefasApiError';\n  }\n}\n\n/**\n * Error thrown when network connectivity issues occur\n */\nexport class NetworkError extends ApiError {\n  constructor(message: string, cause?: unknown) {\n    super(message, cause);\n    this.name = 'NetworkError';\n  }\n}\n\n/**\n * Error thrown when response validation fails\n */\nexport class ResponseValidationError extends TefasError {\n  constructor(message: string, details?: unknown) {\n    super(\n      `Response validation failed: ${message}`,\n      'RESPONSE_VALIDATION_ERROR',\n      details\n    );\n    this.name = 'ResponseValidationError';\n  }\n}\n\n/**\n * Error thrown when configuration is invalid\n */\nexport class ConfigurationError extends TefasError {\n  constructor(message: string, details?: unknown) {\n    super(\n      `Configuration error: ${message}`,\n      'CONFIGURATION_ERROR',\n      details\n    );\n    this.name = 'ConfigurationError';\n  }\n}\n\n/**\n * Type guard to check if an error is a TefasError\n */\nexport function isTefasError(error: unknown): error is TefasError {\n  return error instanceof TefasError;\n}\n\n/**\n * Type guard to check if an error is an InvalidFundTypeError\n */\nexport function isInvalidFundTypeError(error: unknown): error is InvalidFundTypeError {\n  return error instanceof InvalidFundTypeError;\n}\n\n/**\n * Type guard to check if an error is an InvalidDateError\n */\nexport function isInvalidDateError(error: unknown): error is InvalidDateError {\n  return error instanceof InvalidDateError;\n}\n\n/**\n * Type guard to check if an error is a FundNotFoundError\n */\nexport function isFundNotFoundError(error: unknown): error is FundNotFoundError {\n  return error instanceof FundNotFoundError;\n}\n\n/**\n * Type guard to check if an error is a DateRangeError\n */\nexport function isDateRangeError(error: unknown): error is DateRangeError {\n  return error instanceof DateRangeError;\n}\n\n/**\n * Type guard to check if an error is a TefasApiError\n */\nexport function isTefasApiError(error: unknown): error is TefasApiError {\n  return error instanceof TefasApiError;\n}\n\n/**\n * Type guard to check if an error is a NetworkError\n */\nexport function isNetworkError(error: unknown): error is NetworkError {\n  return error instanceof NetworkError;\n}\n\n/**\n * Type guard to check if an error is a ResponseValidationError\n */\nexport function isResponseValidationError(error: unknown): error is ResponseValidationError {\n  return error instanceof ResponseValidationError;\n}\n\n/**\n * Type guard to check if an error is a ConfigurationError\n */\nexport function isConfigurationError(error: unknown): error is ConfigurationError {\n  return error instanceof ConfigurationError;\n}\n\n/**\n * Type guard to check if an error is an HttpError\n */\nexport function isHttpError(error: unknown): error is HttpError {\n  return error instanceof HttpError;\n}\n\n/**\n * Type guard to check if an error is a TimeoutError\n */\nexport function isTimeoutError(error: unknown): error is TimeoutError {\n  return error instanceof TimeoutError;\n}\n\n/**\n * Type guard to check if an error is a ValidationError\n */\nexport function isValidationError(error: unknown): error is ValidationError {\n  return error instanceof ValidationError;\n}\n\n/**\n * HTTP error class for REST API client\n */\nexport class HttpError extends ApiError {\n  public readonly status: number;\n  public readonly statusText: string;\n  public readonly responseData?: unknown;\n\n  constructor(message: string, status: number, statusText: string, responseData?: unknown) {\n    super(message);\n    this.name = 'HttpError';\n    this.status = status;\n    this.statusText = statusText;\n    this.responseData = responseData;\n    \n    if (Error.captureStackTrace) {\n      Error.captureStackTrace(this, HttpError);\n    }\n  }\n}\n\n/**\n * Timeout error class for REST API client\n */\nexport class TimeoutError extends ApiError {\n  constructor(message: string) {\n    super(message);\n    this.name = 'TimeoutError';\n    \n    if (Error.captureStackTrace) {\n      Error.captureStackTrace(this, TimeoutError);\n    }\n  }\n}\n\n/**\n * Validation error class for REST API client\n */\nexport class ValidationError extends ApiError {\n  public readonly validationDetails?: unknown;\n\n  constructor(message: string, validationDetails?: unknown) {\n    super(message);\n    this.name = 'ValidationError';\n    this.validationDetails = validationDetails;\n    \n    if (Error.captureStackTrace) {\n      Error.captureStackTrace(this, ValidationError);\n    }\n  }\n}","/**\n * Serialize data to application/x-www-form-urlencoded format\n *\n * Converts an object to a URL-encoded string suitable for form POST requests.\n * Undefined and null values are omitted from the output.\n * All values are converted to strings and properly URL-encoded.\n *\n * @param data - Object to serialize\n * @returns URL-encoded string\n *\n * @example\n * ```ts\n * serializeFormData({ name: 'John Doe', age: 30, active: true })\n * // Returns: \"name=John+Doe&age=30&active=true\"\n *\n * serializeFormData({ fontip: 'YAT', bastarih: '01.12.2024' })\n * // Returns: \"fontip=YAT&bastarih=01.12.2024\"\n * ```\n *\n * @since 1.0.0\n */\nexport function serializeFormData(\n  data: Record<string, any>\n): string {\n  const params = new URLSearchParams();\n\n  for (const [key, value] of Object.entries(data)) {\n    if (value !== undefined && value !== null) {\n      params.append(key, String(value));\n    }\n  }\n\n  return params.toString();\n}\n\n/**\n * Build query string from params object\n *\n * Converts an object to a query string with a leading '?'.\n * Undefined and null values are omitted from the output.\n * Returns an empty string if no valid parameters are provided.\n *\n * @param params - Object with query parameters\n * @returns Query string with leading '?' or empty string\n *\n * @example\n * ```ts\n * buildQueryString({ page: 1, limit: 10, active: true })\n * // Returns: \"?page=1&limit=10&active=true\"\n *\n * buildQueryString({})\n * // Returns: \"\"\n *\n * buildQueryString({ search: 'test query', page: 1 })\n * // Returns: \"?search=test+query&page=1\"\n * ```\n *\n * @since 1.0.0\n */\nexport function buildQueryString(\n  params: Record<string, string | number | boolean>\n): string {\n  const searchParams = new URLSearchParams();\n\n  for (const [key, value] of Object.entries(params)) {\n    if (value !== undefined && value !== null) {\n      searchParams.append(key, String(value));\n    }\n  }\n\n  const queryString = searchParams.toString();\n  return queryString ? `?${queryString}` : '';\n}\n\n","import { ApiClientConfig, RequestOptions, HttpMethod } from './types';\nimport { HttpError, NetworkError, TimeoutError, ValidationError } from './errors';\nimport { serializeFormData, buildQueryString } from './utils/serialize';\n\n/**\n * Type-safe REST API client for browser and Node.js environments\n * \n * @example\n * ```ts\n * const client = new ApiClient({\n *   baseUrl: 'https://api.example.com',\n *   timeout: 5000\n * });\n * \n * const users = await client.get<User[]>('/users');\n * ```\n */\nexport class ApiClient {\n  private config: ApiClientConfig;\n\n  /**\n   * Creates a new API client instance\n   * @param config - Client configuration options\n   * @throws {TypeError} If baseUrl is invalid or timeout is not a positive number\n   */\n  constructor(config: ApiClientConfig) {\n    // Validate baseUrl\n    if (!config.baseUrl || typeof config.baseUrl !== 'string') {\n      throw new TypeError('baseUrl is required and must be a string');\n    }\n\n    try {\n      new URL(config.baseUrl);\n    } catch {\n      throw new TypeError('baseUrl must be a valid URL');\n    }\n\n    // Validate timeout if provided\n    if (config.timeout !== undefined) {\n      if (typeof config.timeout !== 'number' || config.timeout <= 0) {\n        throw new TypeError('timeout must be a positive number');\n      }\n    }\n\n    // Set defaults\n    this.config = {\n      timeout: 30000, // 30 seconds default\n      headers: {},\n      ...config\n    };\n  }\n\n  /**\n   * Create a new client with updated configuration\n   * @param config - Partial configuration to merge\n   * @returns New ApiClient instance with merged configuration\n   * @example\n   * ```ts\n   * const client1 = new ApiClient({ baseUrl: 'https://api.example.com' });\n   * const client2 = client1.withConfig({ timeout: 5000 });\n   * // client1 unchanged, client2 has new timeout\n   * ```\n   */\n  withConfig(config: Partial<ApiClientConfig>): ApiClient {\n    return new ApiClient({ ...this.config, ...config });\n  }\n\n  /**\n   * Make a GET request\n   * @param url - Request URL (relative to baseUrl)\n   * @param options - Optional request configuration\n   * @returns Promise resolving to typed response data\n   * @example\n   * ```ts\n   * const users = await client.get<User[]>('/users', {\n   *   params: { page: 1, limit: 10 }\n   * });\n   * ```\n   */\n  async get<T>(\n    url: string, \n    options?: RequestOptions\n  ): Promise<T> {\n    return this.request<T>('GET', url, undefined, options);\n  }\n\n  /**\n   * Make a POST request\n   * @param url - Request URL (relative to baseUrl)\n   * @param data - Request body data\n   * @param options - Optional request configuration\n   * @returns Promise resolving to typed response data\n   * @example\n   * ```ts\n   * const user = await client.post<User>('/users', {\n   *   name: 'John Doe',\n   *   email: 'john@example.com'\n   * });\n   * ```\n   */\n  async post<T>(\n    url: string, \n    data?: unknown, \n    options?: RequestOptions\n  ): Promise<T> {\n    return this.request<T>('POST', url, data, options);\n  }\n\n  /**\n   * Core request method with retry logic\n   */\n  private async request<T>(\n    method: HttpMethod,\n    url: string,\n    data?: unknown,\n    options?: RequestOptions\n  ): Promise<T> {\n    const maxRetries = 3;\n    let lastError: Error;\n\n    for (let attempt = 0; attempt <= maxRetries; attempt++) {\n      try {\n        return await this.executeRequest<T>(method, url, data, options);\n      } catch (error) {\n        lastError = error as Error;\n        \n        // Only retry network errors\n        if (error instanceof NetworkError && attempt < maxRetries) {\n          const delay = Math.pow(2, attempt) * 1000; // Exponential backoff: 1s, 2s, 4s\n          await new Promise(resolve => setTimeout(resolve, delay));\n          continue;\n        }\n        \n        throw error;\n      }\n    }\n\n    throw lastError!;\n  }\n\n  /**\n   * Execute a single request\n   */\n  private async executeRequest<T>(\n    method: HttpMethod,\n    url: string,\n    data?: unknown,\n    options?: RequestOptions\n  ): Promise<T> {\n    // Build full URL\n    const fullUrl = this.buildUrl(url, options?.params);\n    \n    // Determine content type\n    const contentType = options?.contentType ?? 'json';\n    \n    // Build headers\n    const headers = this.buildHeaders(contentType, options?.headers);\n    \n    // Serialize body based on content type\n    const body = this.serializeBody(data, contentType);\n\n    // Setup timeout\n    const controller = new AbortController();\n    const timeoutMs = options?.timeout ?? this.config.timeout ?? 30000;\n    const timeoutId = setTimeout(() => controller.abort(), timeoutMs);\n\n    try {\n      const response = await fetch(fullUrl, {\n        method,\n        headers,\n        body,\n        signal: options?.signal ?? controller.signal\n      });\n\n      clearTimeout(timeoutId);\n\n      // Handle HTTP errors\n      if (!response.ok) {\n        const errorData = await this.parseResponse(response);\n        throw new HttpError(\n          `HTTP ${response.status}: ${response.statusText}`,\n          response.status,\n          response.statusText,\n          errorData\n        );\n      }\n\n      // Parse and return response\n      return await this.parseResponse(response);\n    } catch (error) {\n      clearTimeout(timeoutId);\n\n      // Re-throw known errors\n      if (error instanceof HttpError) {\n        throw error;\n      }\n\n      if (error instanceof ValidationError) {\n        throw error;\n      }\n\n      // Handle timeout\n      if (error instanceof DOMException && error.name === 'AbortError') {\n        throw new TimeoutError(`Request timeout after ${timeoutMs}ms`);\n      }\n\n      // Handle network errors\n      throw new NetworkError('Network request failed', error);\n    }\n  }\n\n  /**\n   * Build full URL with query parameters\n   */\n  private buildUrl(path: string, params?: Record<string, any>): string {\n    const base = this.config.baseUrl.replace(/\\/$/, '');\n    const cleanPath = path.startsWith('/') ? path : `/${path}`;\n    const fullPath = `${base}${cleanPath}`;\n    \n    if (params) {\n      return fullPath + buildQueryString(params);\n    }\n    \n    return fullPath;\n  }\n\n  /**\n   * Build request headers\n   */\n  private buildHeaders(\n    contentType: 'json' | 'form',\n    customHeaders?: Record<string, string>\n  ): Record<string, string> {\n    const headers: Record<string, string> = {\n      ...this.config.headers,\n      ...customHeaders\n    };\n\n    // Set Content-Type based on request type\n    if (contentType === 'json') {\n      headers['Content-Type'] = 'application/json';\n    } else if (contentType === 'form') {\n      headers['Content-Type'] = 'application/x-www-form-urlencoded';\n    }\n\n    return headers;\n  }\n\n  /**\n   * Serialize request body\n   */\n  private serializeBody(\n    data: unknown, \n    contentType: 'json' | 'form'\n  ): string | undefined {\n    if (!data) return undefined;\n\n    if (contentType === 'form') {\n      return serializeFormData(data as Record<string, any>);\n    }\n\n    return JSON.stringify(data);\n  }\n\n  /**\n   * Parse response body\n   */\n  private async parseResponse(response: Response): Promise<any> {\n    const contentType = response.headers.get('content-type');\n    \n    // Handle empty responses (204 No Content)\n    if (response.status === 204 || !contentType) {\n      return undefined;\n    }\n\n    // Parse JSON\n    if (contentType?.includes('application/json')) {\n      try {\n        return await response.json();\n      } catch (error) {\n        throw new ValidationError('Invalid JSON response', error);\n      }\n    }\n\n    // Default to text\n    return await response.text();\n  }\n}\n\n","/**\n * TEFAS API Client Types\n *\n * This module defines all TypeScript interfaces and types for the TEFAS client.\n * It provides strong typing for fund data, requests, and responses.\n */\n\n/**\n * Generic API response wrapper\n */\nexport interface ApiResponse<T> {\n  results: T[];\n}\n\n/**\n * Supported TEFAS fund types\n */\nexport enum FundType {\n  /** Emeklilik (Pension) funds */\n  EMK = 'EMK',\n  /** Yatırım (Investment) funds */\n  YAT = 'YAT',\n  /** Bireysel Yatırım (Individual Investment) funds */\n  BYF = 'BYF',\n  /** Gayrimenkul Yatırım (Real Estate Investment) funds */\n  GYF = 'GYF',\n  /** Girişim Sermayesi Yatırım (Venture Capital Investment) funds */\n  GSYF = 'GSYF'\n}\n\n/**\n * Typed response data containing fund history information\n */\nexport interface FundHistoryResponse {\n  /** Fund identifier */\n  fundCode: string;\n  /** Human-readable fund name */\n  fundName: string;\n  /** Type of fund */\n  fundType: FundType;\n  /** Date of the data point (YYYY-MM-DD) */\n  date: string;\n  /** Fund price/NAV */\n  price: number;\n  /** Trading volume */\n  volume: number;\n  /** Total assets under management */\n  assets: number;\n  /** Number of investors */\n  investorCount: number;\n}\n\n/**\n * Raw TEFAS API response structure\n * This represents the actual response format from the TEFAS API\n */\nexport interface TefasApiResponse {\n  /** Response data array */\n  data: TefasApiFundData[];\n  /** Response metadata */\n  meta?: {\n    total?: number;\n    page?: number;\n    limit?: number;\n  };\n}\n\n/**\n * Raw fund data from TEFAS API\n */\nexport interface TefasApiFundData {\n  /** Date string (timestamp) */\n  TARIH: string;\n  /** Fund code */\n  FONKODU: string;\n  /** Fund name */\n  FONUNVAN: string;\n  /** Price/NAV */\n  FIYAT: number;\n  /** Number of shares */\n  TEDPAYSAYISI: number;\n  /** Number of investors */\n  KISISAYISI: number;\n  /** Portfolio size */\n  PORTFOYBUYUKLUK: number;\n  /** Exchange bulletin price */\n  BORSABULTENFIYAT: string;\n}\n\n/**\n * TEFAS API request parameters\n */\nexport interface TefasApiRequest {\n  /** Fund type */\n  fontip: string;\n  /** Start date */\n  bastarih: string;\n  /** End date */\n  bittarih: string;\n  /** Fund code (optional) */\n  fonkod?: string;\n}\n\n/**\n * Configuration for TEFAS API client\n */\nexport interface TefasClientConfig {\n  /** TEFAS API base URL */\n  baseUrl: string;\n  /** Default timeout in milliseconds */\n  timeout: number;\n  /** Default headers */\n  headers: Record<string, string>;\n  /** Maximum retry attempts */\n  maxRetries: number;\n  /** Retry delay in milliseconds */\n  retryDelay: number;\n\n  /**\n   * Cache configuration (used when cacheAdapter is not provided)\n   *\n   * This configures the default in-memory cache adapter.\n   * If you want to use a custom cache (Redis, Cloudflare KV, etc.),\n   * provide a cacheAdapter instead.\n   */\n  cache?: {\n    /** Whether caching is enabled (default: true) */\n    enabled?: boolean;\n    /** Time to live in milliseconds (default: 900000 = 15 minutes) */\n    ttl?: number;\n    /** Maximum number of cache entries (default: 1000) */\n    maxSize?: number;\n  };\n\n  /**\n   * Custom cache adapter for pluggable caching strategies\n   *\n   * Provide your own cache implementation (Redis, Cloudflare KV, localStorage, etc.)\n   * If not provided, uses the default in-memory cache with settings from `cache` config.\n   *\n   * @example\n   * ```typescript\n   * import { RedisCacheAdapter } from '@tefas-api/cache-redis';\n   *\n   * const client = new TefasClient({\n   *   cacheAdapter: new RedisCacheAdapter({ url: 'redis://localhost:6379' })\n   * });\n   * ```\n   */\n  cacheAdapter?: any; // CacheAdapter<ApiResponse<FundResponse>> - avoiding circular dep\n}\n\n/**\n * Date range validation result\n */\nexport interface DateRangeValidation {\n  /** Whether date range is valid */\n  isValid: boolean;\n  /** Error message if invalid */\n  error?: string;\n  /** Parsed start date */\n  startDate?: Date;\n  /** Parsed end date */\n  endDate?: Date;\n  /** Number of days in range */\n  daysInRange?: number;\n}\n\n/**\n * Result from fund search operation\n *\n * Contains basic fund identification information suitable for\n * autocomplete and selection interfaces.\n */\nexport interface SearchResult {\n  /**\n   * Fund code identifier (e.g., \"IIH\", \"TGE\")\n   *\n   * This is the 3-character code used for subsequent API calls\n   * like getFundHistory().\n   */\n  fundCode: string;\n\n  /**\n   * Full fund display name including code prefix\n   *\n   * Format: \"{CODE} - {FULL_NAME}\"\n   * Example: \"IIH - İSTANBUL PORTFÖY ÜÇÜNCÜ HİSSE SENEDİ FONU (HİSSE SENEDİ YOĞUN FON)\"\n   */\n  fundName: string;\n\n  /**\n   * Type of fund (e.g., FundType.EMK, FundType.YAT)\n   *\n   * This indicates the fund category and is always included in search results.\n   */\n  fundType: FundType;\n}\n\n/**\n * Optional configuration for fund search\n *\n * All properties are optional to provide flexible search capabilities.\n */\nexport interface SearchOptions {\n  /**\n   * Filter search results by fund type\n   *\n   * If omitted, searches across all fund types.\n   *\n   * @example\n   * { fundType: FundType.YAT } // Only search investment funds\n   */\n  fundType?: FundType;\n\n  /**\n   * Maximum number of results to return\n   *\n   * @default 10\n   * @example\n   * { limit: 5 } // Return at most 5 results\n   */\n  limit?: number;\n}\n\n/**\n * Fund category information from Fundfy\n */\nexport interface FundCategory {\n  abbreviation: string;\n  fullName: string;\n}\n\n/**\n * Fund benchmark information from Fundfy\n */\nexport interface FundBenchmark {\n  benchmark: string;\n  percentage: number;\n}\n\n/**\n * Detailed fund information from Fundfy\n */\nexport interface FundDetail {\n  code: string;\n  title: string;\n  auditFirm: string | null;\n  emailAddress: string | null;\n  fundDuration: string | null;\n  contactAddress: string | null;\n  founder: string | null;\n  founderLogo: string | null;\n  umbrellaFundType: string | null;\n  umbrellaFundName: string | null;\n  fundCategory: FundCategory | null;\n  risk: number | null;\n  yearlyManagementFee: number | null;\n  buyingValor: number | null;\n  sellingValor: number | null;\n  strategyStatement: string | null;\n  fundBenchmarks: FundBenchmark[];\n}\n\n/**\n * HTTP method types\n */\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n\n/**\n * API client configuration\n */\nexport interface ApiClientConfig {\n  /** Base URL for API requests */\n  baseUrl: string;\n  /** Request timeout in milliseconds */\n  timeout?: number;\n  /** Default headers for all requests */\n  headers?: Record<string, string>;\n}\n\n/**\n * Request options for API calls\n */\nexport interface RequestOptions {\n  /** Query parameters */\n  params?: Record<string, any>;\n  /** Custom headers for this request */\n  headers?: Record<string, string>;\n  /** Request timeout in milliseconds */\n  timeout?: number;\n  /** Content type for request body */\n  contentType?: 'json' | 'form';\n  /** Abort signal for request cancellation */\n  signal?: AbortSignal;\n}\n\n/**\n * Performance metrics for a fund\n */\nexport interface PerformanceMetrics {\n  /** Fund identifier */\n  fundCode: string;\n  /** Human-readable fund name */\n  fundName: string;\n  /** Type of fund */\n  fundType: FundType;\n  /** Number of data points used in calculations */\n  observations: number;\n  /** Total return over the entire period */\n  cumulativeReturn: number;\n  /** Annualized return (yearly return) */\n  annualizedReturn: number;\n  /** Annualized volatility (risk measure) */\n  annualizedVolatility: number;\n  /** Sharpe ratio (risk-adjusted return) */\n  sharpeRatio: number;\n}\n\n/**\n * Configuration options for performance metrics calculation\n */\nexport interface MetricsOptions {\n  /** Risk-free rate for Sharpe ratio calculation (default: 0) */\n  riskFreeRate?: number;\n  /** Data frequency for calculations */\n  frequency?: 'daily' | 'weekly' | 'monthly' | 'quarterly' | 'annual';\n  /** Return calculation method */\n  method?: 'simple' | 'logarithmic';\n  /** Minimum number of observations required (default: 30) */\n  minObservations?: number;\n}\n\n/**\n * Typed response data containing fund allocation/portfolio composition information\n */\nexport interface FundContentResponse {\n  /** Fund identifier */\n  fundCode: string;\n  /** Human-readable fund name */\n  fundName: string;\n  /** Type of fund */\n  fundType: FundType;\n  /** Date of the data point (YYYY-MM-DD) */\n  date: string;\n  /** Total fund value */\n  totalValue: number;\n\n  // Stock allocations\n  /** Percentage of stocks (HS) */\n  stocks?: number;\n  /** Percentage of foreign securities (YYF) */\n  foreignSecurities?: number;\n  /** Percentage of stock-based funds (HB) */\n  stockBasedFunds?: number;\n  /** Percentage of stock-based investment funds (YHS) */\n  stockBasedInvestmentFunds?: number;\n\n  // Bond allocations\n  /** Percentage of bonds (BB) */\n  bonds?: number;\n  /** Percentage of government bonds (DB) */\n  governmentBonds?: number;\n  /** Percentage of private sector bonds (ÖSDB) */\n  privateSectorBonds?: number;\n  /** Percentage of government securities (GSYKB) */\n  governmentSecurities?: number;\n  /** Percentage of government securities yield (GSYY) */\n  governmentSecuritiesYield?: number;\n  /** Percentage of government yield bonds (GYKB) */\n  governmentYieldBonds?: number;\n  /** Percentage of government yield (GYY) */\n  governmentYield?: number;\n\n  // Deposit and cash allocations\n  /** Percentage of deposits (D) */\n  deposits?: number;\n  /** Percentage of reverse purchase agreements (T) */\n  reversePurchaseAgreements?: number;\n  /** Percentage of precious metals (KM) */\n  preciousMetals?: number;\n  /** Percentage of cash and cash equivalents (OSKS) */\n  cashAndCashEquivalents?: number;\n  /** Percentage of other securities (OSKSYD) */\n  otherSecurities?: number;\n  /** Percentage of other securities yield (ÖKSYD) */\n  otherSecuritiesYield?: number;\n\n  // Treasury and government instruments\n  /** Percentage of treasury bills (BPP) */\n  treasuryBills?: number;\n  /** Percentage of individual investment funds (BYF) */\n  individualInvestmentFunds?: number;\n  /** Percentage of government debt instruments (DT) */\n  governmentDebtInstruments?: number;\n  /** Percentage of government debt instruments foreign (DÖT) */\n  governmentDebtInstrumentsForeign?: number;\n  /** Percentage of eurobonds (EUT) */\n  eurobonds?: number;\n  /** Percentage of foreign bonds (FB) */\n  foreignBonds?: number;\n  /** Percentage of foreign currency bonds (FKB) */\n  foreignCurrencyBonds?: number;\n  /** Percentage of gold and silver (GAS) */\n  goldAndSilver?: number;\n\n  // Participation accounts and certificates\n  /** Percentage of participation accounts (KBA) */\n  participationAccounts?: number;\n  /** Percentage of participation accounts gold (KH) */\n  participationAccountsGold?: number;\n  /** Percentage of participation accounts gold foreign (KHAU) */\n  participationAccountsGoldForeign?: number;\n  /** Percentage of participation accounts foreign currency (KHD) */\n  participationAccountsForeignCurrency?: number;\n  /** Percentage of participation accounts Turkish Lira (KHTL) */\n  participationAccountsTurkishLira?: number;\n  /** Percentage of public lease certificates (KKS) */\n  publicLeaseCertificates?: number;\n  /** Percentage of public lease certificates foreign currency (KKSD) */\n  publicLeaseCertificatesForeignCurrency?: number;\n  /** Percentage of public lease certificates Turkish Lira (KKSTL) */\n  publicLeaseCertificatesTurkishLira?: number;\n  /** Percentage of public lease certificates foreign (KKSYD) */\n  publicLeaseCertificatesForeign?: number;\n  /** Percentage of participation accounts foreign currency (KİBD) */\n  participationAccountsForeignCurrencyBonds?: number;\n\n  // Other instruments\n  /** Percentage of other securities Turkish Lira (OST) */\n  otherSecuritiesTurkishLira?: number;\n  /** Percentage of repo transactions (R) */\n  repoTransactions?: number;\n  /** Percentage of term deposits (TPP) */\n  termDeposits?: number;\n  /** Percentage of treasury bills (TR) */\n  treasuryBillsShort?: number;\n  /** Percentage of variable rate deposits (VDM) */\n  variableRateDeposits?: number;\n  /** Percentage of money market instruments (VM) */\n  moneyMarketInstruments?: number;\n  /** Percentage of money market instruments gold (VMAU) */\n  moneyMarketInstrumentsGold?: number;\n  /** Percentage of money market instruments foreign currency (VMD) */\n  moneyMarketInstrumentsForeignCurrency?: number;\n  /** Percentage of money market instruments Turkish Lira (VMTL) */\n  moneyMarketInstrumentsTurkishLira?: number;\n  /** Percentage of variable income instruments (VİNT) */\n  variableIncomeInstruments?: number;\n\n  // Investment fund participations\n  /** Percentage of investment fund participations (YBA) */\n  investmentFundParticipations?: number;\n  /** Percentage of investment fund participations bonds (YBKB) */\n  investmentFundParticipationsBonds?: number;\n  /** Percentage of investment fund participations other securities (YBOSB) */\n  investmentFundParticipationsOtherSecurities?: number;\n  /** Percentage of investment fund participations foreign currency (YBYF) */\n  investmentFundParticipationsForeignCurrency?: number;\n  /** Percentage of investment fund participations metals (YMK) */\n  investmentFundParticipationsMetals?: number;\n\n  // Precious metals and derivatives\n  /** Percentage of precious metals investment funds (KMBYF) */\n  preciousMetalsInvestmentFunds?: number;\n  /** Percentage of precious metals government debt instruments (KMKBA) */\n  preciousMetalsGovernmentDebtInstruments?: number;\n  /** Percentage of precious metals public lease certificates (KMKKS) */\n  preciousMetalsPublicLeaseCertificates?: number;\n}\n\n/**\n * Merged fund data combining history and content information\n *\n * This interface combines all fields from both FundHistoryResponse and FundContentResponse\n * to provide a unified view of fund data. Fields from either source may be null if\n * the corresponding data is not available for that fund/date combination.\n */\nexport interface FundResponse {\n  /** Fund identifier */\n  fundCode: string;\n  /** Human-readable fund name */\n  fundName: string;\n  /** Type of fund */\n  fundType: FundType;\n  /** Date of the data point (YYYY-MM-DD) */\n  date: string;\n\n  // History data fields (from FundHistoryResponse)\n  /** Fund price/NAV (null if not available) */\n  price: number | null;\n  /** Trading volume (null if not available) */\n  volume: number | null;\n  /** Total assets under management (null if not available) */\n  assets: number | null;\n  /** Number of investors (null if not available) */\n  investorCount: number | null;\n\n  // Content data fields (from FundContentResponse)\n  /** Total fund value (null if not available) */\n  totalValue: number | null;\n\n  // Stock allocations\n  /** Percentage of stocks (HS) (null if not available) */\n  stocks?: number | null;\n  /** Percentage of foreign securities (YYF) (null if not available) */\n  foreignSecurities?: number | null;\n  /** Percentage of stock-based funds (HB) (null if not available) */\n  stockBasedFunds?: number | null;\n  /** Percentage of stock-based investment funds (YHS) (null if not available) */\n  stockBasedInvestmentFunds?: number | null;\n\n  // Bond allocations\n  /** Percentage of bonds (BB) (null if not available) */\n  bonds?: number | null;\n  /** Percentage of government bonds (DB) (null if not available) */\n  governmentBonds?: number | null;\n  /** Percentage of private sector bonds (ÖSDB) (null if not available) */\n  privateSectorBonds?: number | null;\n  /** Percentage of government securities (GSYKB) (null if not available) */\n  governmentSecurities?: number | null;\n  /** Percentage of government securities yield (GSYY) (null if not available) */\n  governmentSecuritiesYield?: number | null;\n  /** Percentage of government yield bonds (GYKB) (null if not available) */\n  governmentYieldBonds?: number | null;\n  /** Percentage of government yield (GYY) (null if not available) */\n  governmentYield?: number | null;\n\n  // Deposit and cash allocations\n  /** Percentage of deposits (D) (null if not available) */\n  deposits?: number | null;\n  /** Percentage of reverse purchase agreements (T) (null if not available) */\n  reversePurchaseAgreements?: number | null;\n  /** Percentage of precious metals (KM) (null if not available) */\n  preciousMetals?: number | null;\n  /** Percentage of cash and cash equivalents (OSKS) (null if not available) */\n  cashAndCashEquivalents?: number | null;\n  /** Percentage of other securities (OSKSYD) (null if not available) */\n  otherSecurities?: number | null;\n  /** Percentage of other securities yield (ÖKSYD) (null if not available) */\n  otherSecuritiesYield?: number | null;\n\n  // Treasury and government instruments\n  /** Percentage of treasury bills (BPP) (null if not available) */\n  treasuryBills?: number | null;\n  /** Percentage of individual investment funds (BYF) (null if not available) */\n  individualInvestmentFunds?: number | null;\n  /** Percentage of government debt instruments (DT) (null if not available) */\n  governmentDebtInstruments?: number | null;\n  /** Percentage of government debt instruments foreign (DÖT) (null if not available) */\n  governmentDebtInstrumentsForeign?: number | null;\n  /** Percentage of eurobonds (EUT) (null if not available) */\n  eurobonds?: number | null;\n  /** Percentage of foreign bonds (FB) (null if not available) */\n  foreignBonds?: number | null;\n  /** Percentage of foreign currency bonds (FKB) (null if not available) */\n  foreignCurrencyBonds?: number | null;\n  /** Percentage of gold and silver (GAS) (null if not available) */\n  goldAndSilver?: number | null;\n\n  // Participation accounts and certificates\n  /** Percentage of participation accounts (KBA) (null if not available) */\n  participationAccounts?: number | null;\n  /** Percentage of participation accounts gold (KH) (null if not available) */\n  participationAccountsGold?: number | null;\n  /** Percentage of participation accounts gold foreign (KHAU) (null if not available) */\n  participationAccountsGoldForeign?: number | null;\n  /** Percentage of participation accounts foreign currency (KHD) (null if not available) */\n  participationAccountsForeignCurrency?: number | null;\n  /** Percentage of participation accounts Turkish Lira (KHTL) (null if not available) */\n  participationAccountsTurkishLira?: number | null;\n  /** Percentage of public lease certificates (KKS) (null if not available) */\n  publicLeaseCertificates?: number | null;\n  /** Percentage of public lease certificates foreign currency (KKSD) (null if not available) */\n  publicLeaseCertificatesForeignCurrency?: number | null;\n  /** Percentage of public lease certificates Turkish Lira (KKSTL) (null if not available) */\n  publicLeaseCertificatesTurkishLira?: number | null;\n  /** Percentage of public lease certificates foreign (KKSYD) (null if not available) */\n  publicLeaseCertificatesForeign?: number | null;\n  /** Percentage of participation accounts foreign currency (KİBD) (null if not available) */\n  participationAccountsForeignCurrencyBonds?: number | null;\n\n  // Other instruments\n  /** Percentage of other securities Turkish Lira (OST) (null if not available) */\n  otherSecuritiesTurkishLira?: number | null;\n  /** Percentage of repo transactions (R) (null if not available) */\n  repoTransactions?: number | null;\n  /** Percentage of term deposits (TPP) (null if not available) */\n  termDeposits?: number | null;\n  /** Percentage of treasury bills (TR) (null if not available) */\n  treasuryBillsShort?: number | null;\n  /** Percentage of variable rate deposits (VDM) (null if not available) */\n  variableRateDeposits?: number | null;\n  /** Percentage of money market instruments (VM) (null if not available) */\n  moneyMarketInstruments?: number | null;\n  /** Percentage of money market instruments gold (VMAU) (null if not available) */\n  moneyMarketInstrumentsGold?: number | null;\n  /** Percentage of money market instruments foreign currency (VMD) (null if not available) */\n  moneyMarketInstrumentsForeignCurrency?: number | null;\n  /** Percentage of money market instruments Turkish Lira (VMTL) (null if not available) */\n  moneyMarketInstrumentsTurkishLira?: number | null;\n  /** Percentage of variable income instruments (VİNT) (null if not available) */\n  variableIncomeInstruments?: number | null;\n\n  // Investment fund participations\n  /** Percentage of investment fund participations (YBA) (null if not available) */\n  investmentFundParticipations?: number | null;\n  /** Percentage of investment fund participations bonds (YBKB) (null if not available) */\n  investmentFundParticipationsBonds?: number | null;\n  /** Percentage of investment fund participations other securities (YBOSB) (null if not available) */\n  investmentFundParticipationsOtherSecurities?: number | null;\n  /** Percentage of investment fund participations foreign currency (YBYF) (null if not available) */\n  investmentFundParticipationsForeignCurrency?: number | null;\n  /** Percentage of investment fund participations metals (YMK) (null if not available) */\n  investmentFundParticipationsMetals?: number | null;\n\n  // Precious metals and derivatives\n  /** Percentage of precious metals investment funds (KMBYF) (null if not available) */\n  preciousMetalsInvestmentFunds?: number | null;\n  /** Percentage of precious metals government debt instruments (KMKBA) (null if not available) */\n  preciousMetalsGovernmentDebtInstruments?: number | null;\n  /** Percentage of precious metals public lease certificates (KMKKS) (null if not available) */\n  preciousMetalsPublicLeaseCertificates?: number | null;\n\n  // Fund detail fields (from FundDetail)\n  /** Audit firm name (null if not available) */\n  auditFirm: string | null;\n  /** Email address (null if not available) */\n  emailAddress: string | null;\n  /** Fund duration (null if not available) */\n  fundDuration: string | null;\n  /** Contact address (null if not available) */\n  contactAddress: string | null;\n  /** Founder name (null if not available) */\n  founder: string | null;\n  /** Founder logo filename (null if not available) */\n  founderLogo: string | null;\n  /** Umbrella fund type (null if not available) */\n  umbrellaFundType: string | null;\n  /** Umbrella fund name (null if not available) */\n  umbrellaFundName: string | null;\n  /** Fund category information (null if not available) */\n  fundCategory: FundCategory | null;\n  /** Risk level (null if not available) */\n  risk: number | null;\n  /** Yearly management fee (null if not available) */\n  yearlyManagementFee: number | null;\n  /** Buying valor (null if not available) */\n  buyingValor: number | null;\n  /** Selling valor (null if not available) */\n  sellingValor: number | null;\n  /** Strategy statement (null if not available) */\n  strategyStatement: string | null;\n  /** Fund benchmarks array */\n  fundBenchmarks: FundBenchmark[];\n}\n\n/**\n * Raw TEFAS allocation API response structure\n * This represents the actual response format from the TEFAS allocation API\n */\nexport interface TefasAllocationApiResponse {\n  /** Response data array */\n  data: TefasAllocationData[];\n  /** Response metadata */\n  draw?: number;\n  recordsTotal?: number;\n  recordsFiltered?: number;\n}\n\n/**\n * Raw allocation data from TEFAS API\n */\nexport interface TefasAllocationData {\n  /** Date string (timestamp in milliseconds) */\n  TARIH: string;\n  /** Fund code */\n  FONKODU: string;\n  /** Fund name */\n  FONUNVAN: string;\n  /** Total fund value (comma-formatted string) */\n  BilFiyat: string;\n\n  // Allocation percentages (all nullable)\n  BB?: number;\n  BPP?: number;\n  BYF?: number;\n  D?: number;\n  DB?: number;\n  DT?: number;\n  DÖT?: number;\n  EUT?: number;\n  FB?: number;\n  FKB?: number;\n  GAS?: number;\n  GSYKB?: number;\n  GSYY?: number;\n  GYKB?: number;\n  GYY?: number;\n  HB?: number;\n  HS?: number;\n  KBA?: number;\n  KH?: number;\n  KHAU?: number;\n  KHD?: number;\n  KHTL?: number;\n  KKS?: number;\n  KKSD?: number;\n  KKSTL?: number;\n  KKSYD?: number;\n  KM?: number;\n  KMBYF?: number;\n  KMKBA?: number;\n  KMKKS?: number;\n  KİBD?: number;\n  OSKS?: number;\n  OSKSYD?: number;\n  OST?: number;\n  R?: number;\n  T?: number;\n  TPP?: number;\n  TR?: number;\n  VDM?: number;\n  VM?: number;\n  VMAU?: number;\n  VMD?: number;\n  VMTL?: number;\n  VİNT?: number;\n  YBA?: number;\n  YBKB?: number;\n  YBOSB?: number;\n  YBYF?: number;\n  YHS?: number;\n  YMK?: number;\n  YYF?: number;\n  ÖKSYD?: number;\n  ÖSDB?: number;\n}\n","/**\n * Fund Type Validation Utilities\n * \n * This module provides utilities for fund type validation and management\n * for TEFAS API requests.\n */\n\nimport { FundType } from '../types';\nimport { InvalidFundTypeError } from '../errors';\n\n/**\n * Array of all supported fund types\n */\nexport const SUPPORTED_FUND_TYPES: readonly FundType[] = [\n  FundType.EMK,\n  FundType.YAT,\n  FundType.BYF,\n  FundType.GYF,\n  FundType.GSYF\n] as const;\n\n/**\n * Fund type descriptions for user-friendly error messages\n */\nexport const FUND_TYPE_DESCRIPTIONS: Record<FundType, string> = {\n  [FundType.EMK]: 'Emeklilik (Pension) funds',\n  [FundType.YAT]: 'Yatırım (Investment) funds',\n  [FundType.BYF]: 'Bireysel Yatırım (Individual Investment) funds',\n  [FundType.GYF]: 'Gayrimenkul Yatırım (Real Estate Investment) funds',\n  [FundType.GSYF]: 'Girişim Sermayesi Yatırım (Venture Capital Investment) funds'\n};\n\n/**\n * Validates if a value is a valid fund type\n * \n * @param value - Value to validate\n * @returns True if valid fund type, false otherwise\n */\nexport function isValidFundType(value: unknown): value is FundType {\n  return typeof value === 'string' && SUPPORTED_FUND_TYPES.includes(value as FundType);\n}\n\n/**\n * Validates a fund type and throws an error if invalid\n * \n * @param fundType - Fund type to validate\n * @throws InvalidFundTypeError if fund type is invalid\n */\nexport function validateFundType(fundType: unknown): asserts fundType is FundType {\n  if (!fundType || typeof fundType !== 'string') {\n    throw new InvalidFundTypeError(String(fundType));\n  }\n\n  if (!isValidFundType(fundType)) {\n    throw new InvalidFundTypeError(fundType);\n  }\n}\n\n/**\n * Gets all supported fund types\n * \n * @returns Array of supported fund types\n */\nexport function getSupportedFundTypes(): readonly FundType[] {\n  return SUPPORTED_FUND_TYPES;\n}\n\n/**\n * Gets fund type description\n * \n * @param fundType - Fund type to get description for\n * @returns Human-readable description of the fund type\n */\nexport function getFundTypeDescription(fundType: FundType): string {\n  return FUND_TYPE_DESCRIPTIONS[fundType];\n}\n\n/**\n * Gets all fund type descriptions\n * \n * @returns Object mapping fund types to their descriptions\n */\nexport function getAllFundTypeDescriptions(): Record<FundType, string> {\n  return { ...FUND_TYPE_DESCRIPTIONS };\n}\n\n/**\n * Converts a fund type to its TEFAS API representation\n * \n * @param fundType - Fund type to convert\n * @returns TEFAS API fund type string\n */\nexport function toTefasApiFundType(fundType: FundType): string {\n  // TEFAS API uses the same fund type codes\n  return fundType;\n}\n\n/**\n * Converts a TEFAS API fund type to our FundType enum\n * \n * @param apiFundType - TEFAS API fund type string\n * @returns FundType enum value\n * @throws InvalidFundTypeError if API fund type is not supported\n */\nexport function fromTefasApiFundType(apiFundType: string): FundType {\n  if (!isValidFundType(apiFundType)) {\n    throw new InvalidFundTypeError(apiFundType);\n  }\n  return apiFundType as FundType;\n}\n\n/**\n * Checks if a fund type is supported\n * \n * @param fundType - Fund type to check\n * @returns True if supported, false otherwise\n */\nexport function isSupportedFundType(fundType: string): boolean {\n  return isValidFundType(fundType);\n}\n\n/**\n * Gets fund type validation error message\n * \n * @param fundType - Invalid fund type\n * @returns User-friendly error message\n */\nexport function getFundTypeValidationError(fundType: string): string {\n  const supportedTypes = SUPPORTED_FUND_TYPES.join(', ');\n  return `Invalid fund type '${fundType}'. Supported types are: ${supportedTypes}`;\n}\n\n/**\n * Normalizes fund type string (uppercase, trimmed)\n * \n * @param fundType - Fund type string to normalize\n * @returns Normalized fund type string\n */\nexport function normalizeFundType(fundType: string): string {\n  return fundType.trim().toUpperCase();\n}\n\n/**\n * Validates and normalizes a fund type\n * \n * @param fundType - Fund type to validate and normalize\n * @returns Normalized fund type\n * @throws InvalidFundTypeError if fund type is invalid\n */\nexport function validateAndNormalizeFundType(fundType: string): FundType {\n  const normalized = normalizeFundType(fundType);\n  validateFundType(normalized);\n  return normalized as FundType;\n}\n\n/**\n * Gets fund type display name for UI purposes\n * \n * @param fundType - Fund type to get display name for\n * @returns Display name for the fund type\n */\nexport function getFundTypeDisplayName(fundType: FundType): string {\n  const descriptions = {\n    [FundType.EMK]: 'Pension Funds',\n    [FundType.YAT]: 'Investment Funds',\n    [FundType.BYF]: 'Individual Investment Funds',\n    [FundType.GYF]: 'Real Estate Investment Funds',\n    [FundType.GSYF]: 'Venture Capital Investment Funds'\n  };\n  \n  return descriptions[fundType];\n}\n\n/**\n * Gets fund type category for grouping purposes\n * \n * @param fundType - Fund type to get category for\n * @returns Category of the fund type\n */\nexport function getFundTypeCategory(fundType: FundType): string {\n  const categories = {\n    [FundType.EMK]: 'Retirement',\n    [FundType.YAT]: 'Investment',\n    [FundType.BYF]: 'Individual',\n    [FundType.GYF]: 'Real Estate',\n    [FundType.GSYF]: 'Venture Capital'\n  };\n  \n  return categories[fundType];\n}\n","/**\n * Date Validation Utilities\n * \n * This module provides utilities for date format validation and range checking\n * for TEFAS API requests.\n */\n\nimport { InvalidDateError, DateRangeError } from '../errors';\nimport { type DateRangeValidation } from '../types';\nimport * as chrono from 'chrono-node';\n\n/**\n * ISO 8601 date format regex (YYYY-MM-DD)\n */\nconst ISO_DATE_REGEX = /^\\d{4}-\\d{2}-\\d{2}$/;\n\n/**\n * Validates if a date string is in ISO 8601 format (YYYY-MM-DD)\n * \n * @param date - Date string to validate\n * @returns True if valid, false otherwise\n */\nexport function isValidDateFormat(date: string): boolean {\n  if (!ISO_DATE_REGEX.test(date)) {\n    return false;\n  }\n\n  // Check if the date is actually valid\n  const parsedDate = new Date(date);\n  return !isNaN(parsedDate.getTime()) && parsedDate.toISOString().split('T')[0] === date;\n}\n\n/**\n * Validates a date string and throws an error if invalid\n * \n * @param date - Date string to validate\n * @param fieldName - Name of the field for error messages\n * @throws InvalidDateError if date is invalid\n */\nexport function validateDate(date: string, fieldName: string = 'date'): void {\n  if (!date || typeof date !== 'string') {\n    throw new InvalidDateError(date, `${fieldName} must be a non-empty string`);\n  }\n\n  if (!ISO_DATE_REGEX.test(date)) {\n    throw new InvalidDateError(date, `${fieldName} must be in YYYY-MM-DD format`);\n  }\n\n  const parsedDate = new Date(date);\n  if (isNaN(parsedDate.getTime())) {\n    throw new InvalidDateError(date, `${fieldName} is not a valid date`);\n  }\n\n  // Check if the parsed date matches the input string (prevents dates like 2024-02-30)\n  if (parsedDate.toISOString().split('T')[0] !== date) {\n    throw new InvalidDateError(date, `${fieldName} is not a valid date`);\n  }\n}\n\n/**\n * Parses a date string to a Date object\n * \n * @param date - Date string in YYYY-MM-DD format\n * @returns Parsed Date object\n * @throws InvalidDateError if date is invalid\n */\nexport function parseDate(date: string): Date {\n  validateDate(date);\n  return new Date(date);\n}\n\n/**\n * Formats a date to YYYY-MM-DD string\n * \n * @param date - Date object to format\n * @returns Formatted date string\n */\nexport function formatDate(date: Date): string {\n  return date.toISOString().split('T')[0];\n}\n\n/**\n * Gets the current date in YYYY-MM-DD format\n * \n * @returns Current date string\n */\nexport function getCurrentDate(): string {\n  return formatDate(new Date());\n}\n\n/**\n * Adds days to a date\n * \n * @param date - Date string in YYYY-MM-DD format\n * @param days - Number of days to add (can be negative)\n * @returns New date string in YYYY-MM-DD format\n */\nexport function addDays(date: string, days: number): string {\n  const parsedDate = parseDate(date);\n  const newDate = new Date(parsedDate);\n  newDate.setDate(newDate.getDate() + days);\n  return formatDate(newDate);\n}\n\n/**\n * Calculates the number of days between two dates\n * \n * @param startDate - Start date string\n * @param endDate - End date string\n * @returns Number of days between dates\n */\nexport function getDaysBetween(startDate: string, endDate: string): number {\n  const start = parseDate(startDate);\n  const end = parseDate(endDate);\n  return Math.ceil((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24));\n}\n\n/**\n * Checks if a date is in the past\n * \n * @param date - Date string to check\n * @returns True if date is in the past\n */\nexport function isPastDate(date: string): boolean {\n  const parsedDate = parseDate(date);\n  const today = new Date();\n  today.setHours(0, 0, 0, 0); // Reset time to start of day\n  return parsedDate < today;\n}\n\n/**\n * Checks if a date is in the future\n * \n * @param date - Date string to check\n * @returns True if date is in the future\n */\nexport function isFutureDate(date: string): boolean {\n  const parsedDate = parseDate(date);\n  const today = new Date();\n  today.setHours(23, 59, 59, 999); // Set time to end of day\n  return parsedDate > today;\n}\n\n/**\n * Validates a date range and throws an error if invalid\n * \n * @param startDate - Start date string in YYYY-MM-DD format\n * @param endDate - End date string in YYYY-MM-DD format\n * @param maxDays - Maximum allowed days in range (optional, no limit if not provided)\n * @throws InvalidDateError if either date format is invalid\n * @throws DateRangeError if date range is invalid\n */\nexport function validateDateRange(startDate: string, endDate: string, maxDays?: number): void {\n  // First validate individual dates\n  validateDate(startDate, 'start date');\n  validateDate(endDate, 'end date');\n\n  // Parse dates for comparison\n  const start = parseDate(startDate);\n  const end = parseDate(endDate);\n\n  // Check if start date is after end date\n  if (start > end) {\n    throw new DateRangeError(startDate, endDate, 'Start date must be before end date');\n  }\n\n  // Check if date range exceeds maximum allowed days (only if maxDays is provided)\n  if (maxDays !== undefined) {\n    const daysBetween = getDaysBetween(startDate, endDate);\n    if (daysBetween > maxDays) {\n      throw new DateRangeError(startDate, endDate, `Date range cannot exceed ${maxDays} days`);\n    }\n  }\n}\n\n/**\n * Validates that start date is not more than 5 years ago (TEFAS API constraint)\n *\n * TEFAS API has a Web Application Firewall (WAF) that rejects requests with\n * startDate earlier than 5 years ago. We add a 1-day buffer for safety.\n *\n * @param startDate - Start date string in YYYY-MM-DD format\n * @throws InvalidDateError if date format is invalid\n * @throws DateRangeError if startDate is earlier than 5 years ago + 1 day buffer\n *\n * @example\n * ```typescript\n * // Today is 2025-11-22\n * validateTefasDateLimit('2020-11-23'); // Valid (exactly 5 years ago + 1 day)\n * validateTefasDateLimit('2020-11-22'); // Throws DateRangeError (too far back)\n * validateTefasDateLimit('2020-11-20'); // Throws DateRangeError (too far back)\n * ```\n */\nexport function validateTefasDateLimit(startDate: string): void {\n  // First validate date format\n  validateDate(startDate, 'start date');\n\n  // Calculate 5 years ago + 1 day buffer\n  const fiveYearsAgo = new Date();\n  fiveYearsAgo.setFullYear(fiveYearsAgo.getFullYear() - 5);\n  fiveYearsAgo.setDate(fiveYearsAgo.getDate() + 1); // +1 day buffer\n\n  const start = parseDate(startDate);\n\n  if (start < fiveYearsAgo) {\n    const limitDate = formatDate(fiveYearsAgo);\n    throw new DateRangeError(\n      startDate,\n      limitDate,\n      `TEFAS API does not support dates earlier than 5 years ago. Start date \"${startDate}\" is too far back. Earliest allowed date: ${limitDate}`\n    );\n  }\n}\n\n/**\n * Converts a date from YYYY-MM-DD format to DD.MM.YYYY format for TEFAS API\n *\n * @param date - Date string in YYYY-MM-DD format\n * @returns Date string in DD.MM.YYYY format\n * @throws InvalidDateError if date is invalid\n */\nexport function convertToTefasDateFormat(date: string): string {\n  validateDate(date);\n  const [year, month, day] = date.split('-');\n  return `${day}.${month}.${year}`;\n}\n\n/**\n * Parses a fuzzy date string (e.g., \"today\", \"yesterday\", \"2024-01-01\") to a normalized YYYY-MM-DD format\n * \n * @param dateInput - Fuzzy date string (e.g., \"today\", \"yesterday\", \"2024-01-01\", \"last week\")\n * @param referenceDate - Optional reference date for relative date parsing (defaults to current date)\n * @returns Normalized date string in YYYY-MM-DD format\n * @throws InvalidDateError if date cannot be parsed\n * \n * @example\n * ```typescript\n * parseFuzzyDate('today'); // Returns current date in YYYY-MM-DD format\n * parseFuzzyDate('yesterday'); // Returns yesterday's date in YYYY-MM-DD format\n * parseFuzzyDate('2024-01-01'); // Returns '2024-01-01'\n * parseFuzzyDate('last week'); // Returns date from last week\n * ```\n */\nexport function parseFuzzyDate(dateInput: string, referenceDate?: Date): string {\n  if (!dateInput || typeof dateInput !== 'string') {\n    throw new InvalidDateError(dateInput, 'Date input must be a non-empty string');\n  }\n\n  const trimmedInput = dateInput.trim();\n  \n  // If it's already in YYYY-MM-DD format, validate and return as-is\n  if (ISO_DATE_REGEX.test(trimmedInput)) {\n    validateDate(trimmedInput);\n    return trimmedInput;\n  }\n\n  // Use chrono to parse fuzzy dates\n  const refDate = referenceDate || new Date();\n  const parsedResult = chrono.parseDate(trimmedInput, refDate);\n\n  if (!parsedResult) {\n    throw new InvalidDateError(dateInput, `Unable to parse date: \"${dateInput}\". Supported formats include: \"today\", \"yesterday\", \"2024-01-01\", \"last week\", etc.`);\n  }\n\n  // Convert to YYYY-MM-DD format\n  return formatDate(parsedResult);\n}\n\n/**\n * Validates and normalizes fuzzy date inputs for TEFAS API\n * \n * @param startDate - Start date (fuzzy or YYYY-MM-DD format)\n * @param endDate - End date (fuzzy or YYYY-MM-DD format)\n * @param maxDays - Maximum allowed days in range (optional, no limit if not provided)\n * @returns Object containing normalized start and end dates\n * @throws InvalidDateError if either date cannot be parsed\n * @throws DateRangeError if date range is invalid\n * \n * @example\n * ```typescript\n * const { startDate, endDate } = normalizeFuzzyDateRange('today', 'yesterday');\n * // Throws DateRangeError because start > end\n * \n * const { startDate, endDate } = normalizeFuzzyDateRange('yesterday', 'today');\n * // Returns normalized dates\n * ```\n */\nexport function normalizeFuzzyDateRange(\n  startDate: string, \n  endDate: string, \n  maxDays?: number\n): { startDate: string; endDate: string } {\n  // Parse fuzzy dates to normalized format\n  const normalizedStartDate = parseFuzzyDate(startDate);\n  const normalizedEndDate = parseFuzzyDate(endDate);\n\n  // Validate the date range\n  validateDateRange(normalizedStartDate, normalizedEndDate, maxDays);\n\n  return {\n    startDate: normalizedStartDate,\n    endDate: normalizedEndDate\n  };\n}\n","/**\n * Response Mapping Utilities\n * \n * This module provides utilities for mapping TEFAS API responses to typed responses\n * and validating response data.\n */\n\nimport { \n  FundHistoryResponse, \n  TefasApiResponse, \n  TefasApiFundData, \n  FundType\n} from '../types';\nimport { ResponseValidationError } from '../errors';\n\n/**\n * Maps a single TEFAS API fund data item to our typed response\n * \n * @param apiData - Raw fund data from TEFAS API\n * @returns Mapped fund history response\n * @throws ResponseValidationError if data is invalid\n */\nexport function mapTefasFundData(apiData: TefasApiFundData): FundHistoryResponse {\n  try {\n    // Validate required fields\n    if (!apiData.FONKODU || typeof apiData.FONKODU !== 'string') {\n      throw new ResponseValidationError('Missing or invalid fund code');\n    }\n\n    if (!apiData.FONUNVAN || typeof apiData.FONUNVAN !== 'string') {\n      throw new ResponseValidationError('Missing or invalid fund name');\n    }\n\n    if (!apiData.TARIH || typeof apiData.TARIH !== 'string') {\n      throw new ResponseValidationError('Missing or invalid date');\n    }\n\n    // Validate numeric fields\n    if (typeof apiData.FIYAT !== 'number' || apiData.FIYAT < 0) {\n      throw new ResponseValidationError('Invalid fund price');\n    }\n\n    if (typeof apiData.PORTFOYBUYUKLUK !== 'number' || apiData.PORTFOYBUYUKLUK < 0) {\n      throw new ResponseValidationError('Invalid total assets');\n    }\n\n    if (typeof apiData.KISISAYISI !== 'number' || apiData.KISISAYISI < 0) {\n      throw new ResponseValidationError('Invalid investor count');\n    }\n\n    // Convert timestamp to date string (YYYY-MM-DD format)\n    const timestamp = parseInt(apiData.TARIH);\n    const date = new Date(timestamp);\n    const dateString = date.toISOString().split('T')[0]; // YYYY-MM-DD format\n\n    const volume = apiData.TEDPAYSAYISI || 0; // Use number of shares as volume\n    const investorCount = apiData.KISISAYISI ?? 0;\n\n    // Determine fund type from the API response or use a default\n    // This will be overridden by the calling function\n    const fundType = FundType.YAT; // Default, will be overridden by caller\n\n    return {\n      fundCode: apiData.FONKODU,\n      fundName: apiData.FONUNVAN,\n      fundType,\n      date: dateString,\n      price: apiData.FIYAT,\n      volume,\n      assets: apiData.PORTFOYBUYUKLUK,\n      investorCount\n    };\n  } catch (error) {\n    if (error instanceof ResponseValidationError) {\n      throw error;\n    }\n    throw new ResponseValidationError(`Failed to map fund data: ${error instanceof Error ? error.message : 'Unknown error'}`);\n  }\n}\n\n/**\n * Maps TEFAS API response to our typed response array\n * \n * @param apiResponse - Raw response from TEFAS API\n * @param fundType - Fund type from the request (used to set fund type in response)\n * @returns Array of mapped fund history responses\n * @throws ResponseValidationError if response is invalid\n */\nexport function mapTefasResponse(apiResponse: TefasApiResponse, fundType: FundType): FundHistoryResponse[] {\n  try {\n    // Validate response structure\n    if (!apiResponse || typeof apiResponse !== 'object') {\n      throw new ResponseValidationError('Invalid response structure');\n    }\n\n    if (!Array.isArray(apiResponse.data)) {\n      throw new ResponseValidationError('Response data is not an array');\n    }\n\n    // Map each fund data item\n    return apiResponse.data.map((fundData, index) => {\n      try {\n        const mappedData = mapTefasFundData(fundData);\n        // Override the fund type with the one from the request\n        mappedData.fundType = fundType;\n        return mappedData;\n      } catch (error) {\n        throw new ResponseValidationError(`Failed to map fund data at index ${index}: ${error instanceof Error ? error.message : 'Unknown error'}`);\n      }\n    });\n  } catch (error) {\n    if (error instanceof ResponseValidationError) {\n      throw error;\n    }\n    throw new ResponseValidationError(`Failed to map TEFAS response: ${error instanceof Error ? error.message : 'Unknown error'}`);\n  }\n}\n\n/**\n * Validates a single fund history response\n * \n * @param response - Fund history response to validate\n * @returns True if valid, false otherwise\n */\nexport function validateFundHistoryResponse(response: FundHistoryResponse): boolean {\n  try {\n    // Check required string fields\n    if (!response.fundCode || typeof response.fundCode !== 'string') return false;\n    if (!response.fundName || typeof response.fundName !== 'string') return false;\n    if (!response.date || typeof response.date !== 'string') return false;\n\n    // Check fund type\n    if (!Object.values(FundType).includes(response.fundType)) return false;\n\n    // Check numeric fields\n    if (typeof response.price !== 'number' || response.price < 0) return false;\n    if (typeof response.volume !== 'number' || response.volume < 0) return false;\n    if (typeof response.assets !== 'number' || response.assets < 0) return false;\n    if (typeof response.investorCount !== 'number' || response.investorCount < 0) return false;\n\n    // Check date format (basic validation)\n    if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(response.date)) return false;\n\n    return true;\n  } catch {\n    return false;\n  }\n}\n\n/**\n * Validates an array of fund history responses\n * \n * @param responses - Array of fund history responses to validate\n * @returns True if all responses are valid, false otherwise\n */\nexport function validateFundHistoryResponses(responses: FundHistoryResponse[]): boolean {\n  if (!Array.isArray(responses)) return false;\n  \n  return responses.every(validateFundHistoryResponse);\n}\n\n/**\n * Filters fund history responses by fund type\n * \n * @param responses - Array of fund history responses\n * @param fundType - Fund type to filter by\n * @returns Filtered array of responses\n */\nexport function filterByFundType(responses: FundHistoryResponse[], fundType: FundType): FundHistoryResponse[] {\n  return responses.filter(response => response.fundType === fundType);\n}\n\n/**\n * Filters fund history responses by fund code\n * \n * @param responses - Array of fund history responses\n * @param fundCode - Fund code to filter by\n * @returns Filtered array of responses\n */\nexport function filterByFundCode(responses: FundHistoryResponse[], fundCode: string): FundHistoryResponse[] {\n  return responses.filter(response => response.fundCode === fundCode);\n}\n\n/**\n * Groups fund history responses by fund code\n * \n * @param responses - Array of fund history responses\n * @returns Object with fund codes as keys and arrays of responses as values\n */\nexport function groupByFundCode(responses: FundHistoryResponse[]): Record<string, FundHistoryResponse[]> {\n  return responses.reduce((groups, response) => {\n    const fundCode = response.fundCode;\n    if (!groups[fundCode]) {\n      groups[fundCode] = [];\n    }\n    groups[fundCode].push(response);\n    return groups;\n  }, {} as Record<string, FundHistoryResponse[]>);\n}\n\n/**\n * Sorts fund history responses by date (ascending)\n * \n * @param responses - Array of fund history responses\n * @returns Sorted array of responses\n */\nexport function sortByDate(responses: FundHistoryResponse[]): FundHistoryResponse[] {\n  return [...responses].sort((a, b) => a.date.localeCompare(b.date));\n}\n\n/**\n * Sorts fund history responses by date (descending)\n * \n * @param responses - Array of fund history responses\n * @returns Sorted array of responses\n */\nexport function sortByDateDescending(responses: FundHistoryResponse[]): FundHistoryResponse[] {\n  return [...responses].sort((a, b) => b.date.localeCompare(a.date));\n}\n\n/**\n * Gets unique fund codes from responses\n * \n * @param responses - Array of fund history responses\n * @returns Array of unique fund codes\n */\nexport function getUniqueFundCodes(responses: FundHistoryResponse[]): string[] {\n  const fundCodes = new Set(responses.map(response => response.fundCode));\n  return Array.from(fundCodes);\n}\n\n/**\n * Gets the latest response for each fund\n * \n * @param responses - Array of fund history responses\n * @returns Object with fund codes as keys and latest responses as values\n */\nexport function getLatestByFundCode(responses: FundHistoryResponse[]): Record<string, FundHistoryResponse> {\n  const sorted = sortByDateDescending(responses);\n  const latest: Record<string, FundHistoryResponse> = {};\n  \n  for (const response of sorted) {\n    if (!latest[response.fundCode]) {\n      latest[response.fundCode] = response;\n    }\n  }\n  \n  return latest;\n}\n\n/**\n * Calculates total assets across all funds\n * \n * @param responses - Array of fund history responses\n * @returns Total assets value\n */\nexport function calculateTotalAssets(responses: FundHistoryResponse[]): number {\n  return responses.reduce((total, response) => total + response.assets, 0);\n}\n\n/**\n * Calculates average price across all funds\n * \n * @param responses - Array of fund history responses\n * @returns Average price\n */\nexport function calculateAveragePrice(responses: FundHistoryResponse[]): number {\n  if (responses.length === 0) return 0;\n  const totalPrice = responses.reduce((total, response) => total + response.price, 0);\n  return totalPrice / responses.length;\n}\n","/**\n * Search Response Parser Utility\n *\n * Handles parsing of the nested JSON response structure from TEFAS GetAllFunds API.\n * The API returns a double-encoded JSON structure that requires special handling.\n */\n\nimport { SearchResult, FundType } from '../types';\nimport { ValidationError } from '../errors';\n\n/**\n * Raw search API response structure from TEFAS GetAllFunds endpoint\n * @internal\n */\ninterface SearchApiResponse {\n  /** Array of JSON-encoded strings */\n  d: string[];\n}\n\n/**\n * Parsed fund entry from nested JSON string\n * @internal\n */\ninterface ParsedFundEntry {\n  /** Full display name: \"{CODE} - {FULL_NAME}\" (code will be extracted and removed) */\n  First: string;\n  /** Fund code (e.g., \"IIH\") */\n  Second: string;\n}\n\n/**\n * Parse TEFAS search API response into SearchResult array\n *\n * The TEFAS GetAllFunds API returns a nested JSON structure:\n * 1. Top level has a \"d\" array property\n * 2. Each array element is a JSON-encoded string (double-encoded)\n * 3. Each string contains an object with \"First\" (display name) and \"Second\" (fund code)\n *\n * @param response - Raw API response object\n * @param fundType - Fund type to include in each result\n * @returns Array of SearchResult objects\n * @throws ValidationError if response structure is invalid\n *\n * @example\n * ```typescript\n * const apiResponse = {\n *   d: [\n *     '{\"First\":\"IIH - İSTANBUL PORTFÖY ÜÇÜNCÜ HİSSE SENEDİ FONU\",\"Second\":\"IIH\"}'\n *   ]\n * };\n * const results = parseSearchResponse(apiResponse, FundType.YAT);\n * // results = [{ fundCode: \"IIH\", fundName: \"İSTANBUL PORTFÖY ÜÇÜNCÜ HİSSE SENEDİ FONU\", fundType: FundType.YAT }]\n * ```\n */\nexport function parseSearchResponse(response: unknown, fundType: FundType): SearchResult[] {\n  // Validate response structure\n  if (!response || typeof response !== 'object') {\n    throw new ValidationError('Invalid search response: response must be an object');\n  }\n\n  const apiResponse = response as SearchApiResponse;\n\n  if (!apiResponse.d || !Array.isArray(apiResponse.d)) {\n    throw new ValidationError('Invalid search response: missing or invalid \"d\" array');\n  }\n\n  // Handle empty results\n  if (apiResponse.d.length === 0) {\n    return [];\n  }\n\n  // Parse each JSON string in the array\n  const results: SearchResult[] = [];\n\n  for (let i = 0; i < apiResponse.d.length; i++) {\n    const item = apiResponse.d[i];\n\n    // Validate item is a string\n    if (typeof item !== 'string') {\n      throw new ValidationError(\n        `Invalid search response: item at index ${i} is not a string`\n      );\n    }\n\n    // Skip empty strings\n    if (item.trim().length === 0) {\n      continue;\n    }\n\n    try {\n      // Parse the nested JSON string\n      const parsed = JSON.parse(item) as ParsedFundEntry;\n\n      // Validate parsed structure\n      if (!parsed || typeof parsed !== 'object') {\n        throw new Error('Parsed item is not an object');\n      }\n\n      if (typeof parsed.First !== 'string' || typeof parsed.Second !== 'string') {\n        throw new Error('Missing or invalid First/Second properties');\n      }\n\n      // Extract full name from display format \"{CODE} - {FULL_NAME}\"\n      const fullName = parsed.First.includes(' - ') \n        ? parsed.First.split(' - ').slice(1).join(' - ') \n        : parsed.First;\n\n      // Map to SearchResult\n      results.push({\n        fundCode: parsed.Second,\n        fundName: fullName,\n        fundType: fundType\n      });\n    } catch (error) {\n      // Provide clear error message for malformed JSON\n      const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n      throw new ValidationError(\n        `Invalid search response: failed to parse item at index ${i}: ${errorMessage}`,\n        error\n      );\n    }\n  }\n\n  return results;\n}\n\n/**\n * Validate that a search response has the expected structure\n *\n * @param response - Response to validate\n * @returns True if response has valid structure, false otherwise\n *\n * @example\n * ```typescript\n * if (isValidSearchResponse(apiResponse)) {\n *   const results = parseSearchResponse(apiResponse, FundType.YAT);\n * }\n * ```\n */\nexport function isValidSearchResponse(response: unknown): boolean {\n  try {\n    if (!response || typeof response !== 'object') {\n      return false;\n    }\n\n    const apiResponse = response as SearchApiResponse;\n\n    if (!apiResponse.d || !Array.isArray(apiResponse.d)) {\n      return false;\n    }\n\n    return true;\n  } catch {\n    return false;\n  }\n}\n","/**\n * Allocation Response Mapping Utilities\n * \n * This module provides utilities for mapping TEFAS allocation API responses\n * to clean, typed response objects with English property names.\n */\n\nimport { FundType, FundContentResponse, TefasAllocationApiResponse } from '../types';\n\n/**\n * Converts a timestamp string (milliseconds) to YYYY-MM-DD format\n * \n * @private\n * @param timestamp - Timestamp string in milliseconds\n * @returns Date string in YYYY-MM-DD format\n */\nfunction convertTimestampToDate(timestamp: string): string {\n  const date = new Date(parseInt(timestamp, 10));\n  // Handle invalid timestamps\n  if (isNaN(date.getTime())) {\n    return '1970-01-01';\n  }\n  return date.toISOString().split('T')[0];\n}\n\n/**\n * Parses a comma-formatted number string to a number\n * \n * @private\n * @param value - Comma-formatted number string (e.g., \"1,234,567.89\")\n * @returns Parsed number\n */\nfunction parseCommaFormattedNumber(value: string): number {\n  if (!value || value.trim() === '') {\n    return 0;\n  }\n  \n  // Remove commas and parse as float\n  const cleanValue = value.replace(/,/g, '');\n  return parseFloat(cleanValue) || 0;\n}\n\n/**\n * Maps TEFAS allocation API response to clean response objects\n * \n * This function converts the raw TEFAS API response format to our clean,\n * typed response format with English property names and proper data types.\n * \n * @param response - Raw TEFAS allocation API response\n * @param fundType - Fund type from the request\n * @returns Array of mapped fund content responses\n */\nexport function mapAllocationResponse(\n  response: TefasAllocationApiResponse,\n  fundType: FundType\n): FundContentResponse[] {\n  if (!response.data || !Array.isArray(response.data)) {\n    return [];\n  }\n\n  return response.data.map((item) => ({\n    fundCode: item.FONKODU,\n    fundName: item.FONUNVAN,\n    fundType,\n    date: convertTimestampToDate(item.TARIH),\n    totalValue: parseCommaFormattedNumber(item.BilFiyat),\n    \n    // Stock allocations\n    stocks: item.HS ?? undefined,\n    foreignSecurities: item.YYF ?? undefined,\n    stockBasedFunds: item.HB ?? undefined,\n    stockBasedInvestmentFunds: item.YHS ?? undefined,\n    \n    // Bond allocations\n    bonds: item.BB ?? undefined,\n    governmentBonds: item.DB ?? undefined,\n    privateSectorBonds: item.ÖSDB ?? undefined,\n    governmentSecurities: item.GSYKB ?? undefined,\n    governmentSecuritiesYield: item.GSYY ?? undefined,\n    governmentYieldBonds: item.GYKB ?? undefined,\n    governmentYield: item.GYY ?? undefined,\n    \n    // Deposit and cash allocations\n    deposits: item.D ?? undefined,\n    reversePurchaseAgreements: item.T ?? undefined,\n    preciousMetals: item.KM ?? undefined,\n    cashAndCashEquivalents: item.OSKS ?? undefined,\n    otherSecurities: item.OSKSYD ?? undefined,\n    otherSecuritiesYield: item.ÖKSYD ?? undefined,\n    \n    // Treasury and government instruments\n    treasuryBills: item.BPP ?? undefined,\n    individualInvestmentFunds: item.BYF ?? undefined,\n    governmentDebtInstruments: item.DT ?? undefined,\n    governmentDebtInstrumentsForeign: item.DÖT ?? undefined,\n    eurobonds: item.EUT ?? undefined,\n    foreignBonds: item.FB ?? undefined,\n    foreignCurrencyBonds: item.FKB ?? undefined,\n    goldAndSilver: item.GAS ?? undefined,\n    \n    // Participation accounts and certificates\n    participationAccounts: item.KBA ?? undefined,\n    participationAccountsGold: item.KH ?? undefined,\n    participationAccountsGoldForeign: item.KHAU ?? undefined,\n    participationAccountsForeignCurrency: item.KHD ?? undefined,\n    participationAccountsTurkishLira: item.KHTL ?? undefined,\n    publicLeaseCertificates: item.KKS ?? undefined,\n    publicLeaseCertificatesForeignCurrency: item.KKSD ?? undefined,\n    publicLeaseCertificatesTurkishLira: item.KKSTL ?? undefined,\n    publicLeaseCertificatesForeign: item.KKSYD ?? undefined,\n    participationAccountsForeignCurrencyBonds: item.KİBD ?? undefined,\n    \n    // Other instruments\n    otherSecuritiesTurkishLira: item.OST ?? undefined,\n    repoTransactions: item.R ?? undefined,\n    termDeposits: item.TPP ?? undefined,\n    treasuryBillsShort: item.TR ?? undefined,\n    variableRateDeposits: item.VDM ?? undefined,\n    moneyMarketInstruments: item.VM ?? undefined,\n    moneyMarketInstrumentsGold: item.VMAU ?? undefined,\n    moneyMarketInstrumentsForeignCurrency: item.VMD ?? undefined,\n    moneyMarketInstrumentsTurkishLira: item.VMTL ?? undefined,\n    variableIncomeInstruments: item.VİNT ?? undefined,\n    \n    // Investment fund participations\n    investmentFundParticipations: item.YBA ?? undefined,\n    investmentFundParticipationsBonds: item.YBKB ?? undefined,\n    investmentFundParticipationsOtherSecurities: item.YBOSB ?? undefined,\n    investmentFundParticipationsForeignCurrency: item.YBYF ?? undefined,\n    investmentFundParticipationsMetals: item.YMK ?? undefined,\n    \n    // Precious metals and derivatives\n    preciousMetalsInvestmentFunds: item.KMBYF ?? undefined,\n    preciousMetalsGovernmentDebtInstruments: item.KMKBA ?? undefined,\n    preciousMetalsPublicLeaseCertificates: item.KMKKS ?? undefined,\n  }));\n}\n","/**\n * Shared constants for the TEFAS API client\n */\n\n/**\n * Default maximum number of days per request window when splitting history calls\n */\nexport const DEFAULT_MAX_DAYS_PER_REQUEST = 90;\n\n/**\n * TEFAS API endpoint for fund allocation/portfolio composition data\n */\nexport const TEFAS_ALLOCATION_ENDPOINT = '/api/DB/BindHistoryAllocation';\n\n\n","/**\n * Cache adapter interfaces for pluggable caching strategies\n *\n * This module defines the contract for cache implementations, allowing users\n * to provide custom caching backends (Redis, Cloudflare KV, localStorage, etc.)\n * while maintaining a consistent API.\n */\n\n/**\n * Core cache adapter interface with essential operations\n *\n * All methods are async to support both local (Map, localStorage) and remote\n * (Redis, Cloudflare KV) cache implementations.\n *\n * @template T - Type of values stored in the cache\n *\n * @example\n * ```typescript\n * // Implement custom cache adapter\n * class RedisCacheAdapter implements CacheAdapter<MyData> {\n *   async get(key: string): Promise<MyData | undefined> {\n *     const value = await redis.get(key);\n *     return value ? JSON.parse(value) : undefined;\n *   }\n *\n *   async set(key: string, value: MyData, ttl?: number): Promise<void> {\n *     await redis.setEx(key, ttl ?? 900, JSON.stringify(value));\n *   }\n *\n *   // ... implement other methods\n * }\n * ```\n */\nexport interface CacheAdapter<T> {\n  /**\n   * Retrieves a value from the cache\n   *\n   * @param key - Cache key\n   * @returns Cached value or undefined if not found or expired\n   */\n  get(key: string): Promise<T | undefined>;\n\n  /**\n   * Stores a value in the cache\n   *\n   * @param key - Cache key\n   * @param value - Value to cache\n   * @param ttl - Optional time-to-live in seconds (overrides default TTL)\n   */\n  set(key: string, value: T, ttl?: number): Promise<void>;\n\n  /**\n   * Removes a specific entry from the cache\n   *\n   * @param key - Cache key to delete\n   * @returns true if key was deleted, false if it didn't exist\n   */\n  delete(key: string): Promise<boolean>;\n\n  /**\n   * Clears all entries from the cache\n   */\n  clear(): Promise<void>;\n\n  /**\n   * Checks if a key exists in the cache and is not expired\n   *\n   * @param key - Cache key\n   * @returns true if key exists and is valid\n   */\n  has(key: string): Promise<boolean>;\n}\n\n/**\n * Cache statistics information\n *\n * Not all cache implementations can provide all statistics.\n * Optional fields allow adapters to return only what they can track.\n */\nexport interface CacheStats {\n  /** Current number of cache entries (if trackable) */\n  size?: number;\n\n  /** Whether caching is enabled */\n  enabled: boolean;\n\n  /** Time-to-live in milliseconds (if applicable) */\n  ttl?: number;\n\n  /** Maximum cache size before eviction (if applicable) */\n  maxSize?: number;\n}\n\n/**\n * Extended cache adapter interface with statistics and maintenance operations\n *\n * This interface is optional - not all cache implementations can provide\n * statistics or manual cleanup operations. Implementations should only\n * implement this if they can meaningfully support these operations.\n *\n * @template T - Type of values stored in the cache\n *\n * @example\n * ```typescript\n * class MemoryCacheAdapter implements CacheAdapterWithStats<MyData> {\n *   async getStats(): Promise<CacheStats> {\n *     return {\n *       size: this.cache.size,\n *       enabled: true,\n *       ttl: this.ttl,\n *       maxSize: this.maxSize\n *     };\n *   }\n *\n *   async prune(): Promise<number> {\n *     return this.cache.removeExpired();\n *   }\n * }\n * ```\n */\nexport interface CacheAdapterWithStats<T> extends CacheAdapter<T> {\n  /**\n   * Gets cache statistics\n   *\n   * @returns Cache statistics object\n   */\n  getStats(): Promise<CacheStats>;\n\n  /**\n   * Removes expired entries from the cache (manual cleanup)\n   *\n   * This is optional because some caches (Redis, Cloudflare KV) handle\n   * expiration automatically and don't need manual pruning.\n   *\n   * @returns Number of entries removed\n   */\n  prune?(): Promise<number>;\n}\n\n/**\n * Type guard to check if a cache adapter supports statistics\n *\n * @param adapter - Cache adapter to check\n * @returns true if adapter implements CacheAdapterWithStats\n *\n * @example\n * ```typescript\n * if (isCacheAdapterWithStats(cache)) {\n *   const stats = await cache.getStats();\n *   console.log(`Cache size: ${stats.size}`);\n * }\n * ```\n */\nexport function isCacheAdapterWithStats<T>(\n  adapter: CacheAdapter<T>\n): adapter is CacheAdapterWithStats<T> {\n  return 'getStats' in adapter && typeof adapter.getStats === 'function';\n}\n","/**\n * In-memory cache with TTL (Time To Live) support for TEFAS API responses.\n *\n * This cache helps reduce redundant API calls by storing responses temporarily.\n * Each cache entry expires after the configured TTL period.\n */\n\n/**\n * Configuration options for the cache\n *\n * @public\n */\nexport interface CacheOptions {\n  /**\n   * Time to live in milliseconds. Default: 15 minutes (900000ms)\n   */\n  ttl?: number;\n\n  /**\n   * Whether caching is enabled. Default: true\n   */\n  enabled?: boolean;\n\n  /**\n   * Maximum number of cache entries. Default: 1000\n   * When exceeded, oldest entries are removed (LRU-style)\n   */\n  maxSize?: number;\n}\n\n/**\n * Cache entry with expiration timestamp\n */\ninterface CacheEntry<T> {\n  value: T;\n  expiresAt: number;\n  createdAt: number;\n}\n\n/**\n * In-memory TTL-based cache implementation\n */\nexport class TtlCache<T> {\n  private cache = new Map<string, CacheEntry<T>>();\n  private readonly ttl: number;\n  private readonly enabled: boolean;\n  private readonly maxSize: number;\n\n  /**\n   * Creates a new TTL cache instance\n   *\n   * @param options - Cache configuration options\n   */\n  constructor(options: CacheOptions = {}) {\n    this.ttl = options.ttl ?? 15 * 60 * 1000; // Default: 15 minutes\n    this.enabled = options.enabled ?? true;\n    this.maxSize = options.maxSize ?? 1000;\n  }\n\n  /**\n   * Gets a value from the cache\n   *\n   * @param key - Cache key\n   * @returns Cached value or undefined if not found or expired\n   */\n  get(key: string): T | undefined {\n    if (!this.enabled) {\n      return undefined;\n    }\n\n    const entry = this.cache.get(key);\n\n    if (!entry) {\n      return undefined;\n    }\n\n    const now = Date.now();\n\n    // Check if entry has expired\n    if (now > entry.expiresAt) {\n      this.cache.delete(key);\n      return undefined;\n    }\n\n    return entry.value;\n  }\n\n  /**\n   * Sets a value in the cache\n   *\n   * @param key - Cache key\n   * @param value - Value to cache\n   */\n  set(key: string, value: T): void {\n    if (!this.enabled) {\n      return;\n    }\n\n    // Enforce max size by removing oldest entries (LRU-style)\n    if (this.cache.size >= this.maxSize) {\n      this.evictOldest();\n    }\n\n    const now = Date.now();\n    const entry: CacheEntry<T> = {\n      value,\n      expiresAt: now + this.ttl,\n      createdAt: now,\n    };\n\n    this.cache.set(key, entry);\n  }\n\n  /**\n   * Checks if a key exists in the cache and is not expired\n   *\n   * @param key - Cache key\n   * @returns true if key exists and is valid\n   */\n  has(key: string): boolean {\n    if (!this.enabled) {\n      return false;\n    }\n\n    const entry = this.cache.get(key);\n\n    if (!entry) {\n      return false;\n    }\n\n    const now = Date.now();\n\n    if (now > entry.expiresAt) {\n      this.cache.delete(key);\n      return false;\n    }\n\n    return true;\n  }\n\n  /**\n   * Deletes a specific key from the cache\n   *\n   * @param key - Cache key to delete\n   * @returns true if key was deleted, false if it didn't exist\n   */\n  delete(key: string): boolean {\n    return this.cache.delete(key);\n  }\n\n  /**\n   * Clears all entries from the cache\n   */\n  clear(): void {\n    this.cache.clear();\n  }\n\n  /**\n   * Gets the current number of entries in the cache\n   *\n   * @returns Number of cache entries\n   */\n  get size(): number {\n    return this.cache.size;\n  }\n\n  /**\n   * Removes all expired entries from the cache\n   *\n   * @returns Number of entries removed\n   */\n  prune(): number {\n    if (!this.enabled) {\n      return 0;\n    }\n\n    const now = Date.now();\n    let removed = 0;\n\n    for (const [key, entry] of this.cache.entries()) {\n      if (now > entry.expiresAt) {\n        this.cache.delete(key);\n        removed++;\n      }\n    }\n\n    return removed;\n  }\n\n  /**\n   * Evicts the oldest entry from the cache (by creation time)\n   * Used to enforce max size limit\n   */\n  private evictOldest(): void {\n    let oldestKey: string | null = null;\n    let oldestTime = Infinity;\n\n    for (const [key, entry] of this.cache.entries()) {\n      if (entry.createdAt < oldestTime) {\n        oldestTime = entry.createdAt;\n        oldestKey = key;\n      }\n    }\n\n    if (oldestKey) {\n      this.cache.delete(oldestKey);\n    }\n  }\n\n  /**\n   * Gets cache statistics\n   *\n   * @returns Cache statistics object\n   */\n  getStats(): { size: number; enabled: boolean; ttl: number; maxSize: number } {\n    return {\n      size: this.size,\n      enabled: this.enabled,\n      ttl: this.ttl,\n      maxSize: this.maxSize,\n    };\n  }\n}\n","/**\n * Memory-based cache adapter using Map\n *\n * This adapter wraps the TtlCache implementation and provides the CacheAdapter\n * interface. It is the default cache used when no custom adapter is provided.\n */\n\nimport { TtlCache, CacheOptions } from '../cache';\nimport { CacheAdapter, CacheAdapterWithStats, CacheStats } from '../cache-interface';\n\n/**\n * In-memory cache adapter using Map with TTL support\n *\n * This is the default cache implementation that wraps the existing TtlCache.\n * All methods return Promises for consistency with the async CacheAdapter interface,\n * even though operations are synchronous.\n *\n * Features:\n * - TTL-based expiration\n * - LRU-style eviction when max size is reached\n * - Automatic expiration checking on access\n * - Manual cache management (clear, prune, stats)\n *\n * @template T - Type of values stored in the cache\n *\n * @example\n * ```typescript\n * const cache = new MemoryCacheAdapter<MyData>({\n *   ttl: 15 * 60 * 1000,  // 15 minutes\n *   maxSize: 1000,\n *   enabled: true\n * });\n *\n * await cache.set('key1', myData);\n * const value = await cache.get('key1');\n * ```\n */\nexport class MemoryCacheAdapter<T> implements CacheAdapterWithStats<T> {\n  private readonly cache: TtlCache<T>;\n  private readonly enabled: boolean;\n\n  /**\n   * Creates a new memory cache adapter\n   *\n   * @param options - Cache configuration options\n   */\n  constructor(options: CacheOptions = {}) {\n    this.cache = new TtlCache<T>(options);\n    this.enabled = options.enabled ?? true;\n  }\n\n  /**\n   * Retrieves a value from the cache\n   *\n   * @param key - Cache key\n   * @returns Cached value or undefined if not found or expired\n   */\n  async get(key: string): Promise<T | undefined> {\n    if (!this.enabled) {\n      return undefined;\n    }\n    return this.cache.get(key);\n  }\n\n  /**\n   * Stores a value in the cache\n   *\n   * Note: The current TtlCache implementation doesn't support per-key TTL.\n   * The ttl parameter is accepted for interface compatibility but uses\n   * the cache-wide TTL configured in the constructor.\n   *\n   * @param key - Cache key\n   * @param value - Value to cache\n   * @param ttl - Time-to-live in seconds (currently ignored, uses cache-wide TTL)\n   */\n  async set(key: string, value: T, ttl?: number): Promise<void> {\n    if (!this.enabled) {\n      return;\n    }\n    // Note: Current TtlCache doesn't support per-key TTL\n    // Future enhancement: extend TtlCache to support per-key TTL\n    this.cache.set(key, value);\n  }\n\n  /**\n   * Removes a specific entry from the cache\n   *\n   * @param key - Cache key to delete\n   * @returns true if key was deleted, false if it didn't exist\n   */\n  async delete(key: string): Promise<boolean> {\n    return this.cache.delete(key);\n  }\n\n  /**\n   * Clears all entries from the cache\n   */\n  async clear(): Promise<void> {\n    this.cache.clear();\n  }\n\n  /**\n   * Checks if a key exists in the cache and is not expired\n   *\n   * @param key - Cache key\n   * @returns true if key exists and is valid\n   */\n  async has(key: string): Promise<boolean> {\n    if (!this.enabled) {\n      return false;\n    }\n    return this.cache.has(key);\n  }\n\n  /**\n   * Gets cache statistics\n   *\n   * @returns Cache statistics including size, TTL, and configuration\n   */\n  async getStats(): Promise<CacheStats> {\n    return this.cache.getStats();\n  }\n\n  /**\n   * Removes expired entries from the cache\n   *\n   * This method is useful for freeing up memory by removing stale entries.\n   * The cache automatically checks expiration on access, but this method\n   * allows for proactive cleanup.\n   *\n   * @returns Number of entries removed\n   */\n  async prune(): Promise<number> {\n    return this.cache.prune();\n  }\n}\n","/**\n * TEFAS API Client\n *\n * This module provides a simplified interface for accessing TEFAS API data.\n * It abstracts away HTTP implementation details and provides a clean, business-focused API.\n */\n\nimport { ApiClient } from './client';\nimport {\n  ApiResponse,\n  FundType,\n  FundHistoryResponse,\n  FundContentResponse,\n  FundResponse,\n  FundDetail,\n  TefasApiRequest,\n  TefasApiResponse,\n  TefasAllocationApiResponse,\n  TefasClientConfig,\n  SearchResult,\n  SearchOptions\n} from './types';\nimport {\n  InvalidFundTypeError,\n  InvalidDateError,\n  DateRangeError,\n  FundNotFoundError,\n  TefasApiError,\n  NetworkError,\n  ConfigurationError,\n  ValidationError\n} from './errors';\nimport { validateFundType, SUPPORTED_FUND_TYPES } from './utils/fund-type-validation';\nimport { convertToTefasDateFormat, addDays, getDaysBetween, validateDateRange, normalizeFuzzyDateRange } from './utils/date-validation';\nimport { mapTefasResponse } from './utils/response-mapping';\nimport { parseSearchResponse } from './utils/search-response-parser';\nimport { mapAllocationResponse } from './utils/allocation-response-mapping';\nimport { DEFAULT_MAX_DAYS_PER_REQUEST, TEFAS_ALLOCATION_ENDPOINT } from './constants';\nimport { CacheAdapter, CacheStats, isCacheAdapterWithStats } from './cache-interface';\nimport { MemoryCacheAdapter } from './adapters/memory-cache-adapter';\n\nconst FUNDFY_BASE_URL = 'https://api.fundfy.net';\nconst FUNDFY_AUTH_TOKEN = 'Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJFcm9sS09DQU1BTiIsImlhdCI6MTczMjYyNTQ2OH0.q2otvxUSxXdIO7zx0NLjN5oRhreR_GReFKRRYPsOXcs';\nconst FUNDFY_ORIGIN = 'https://app.fundfy.net';\n\n// Ambient declaration for build-time injected constant\ndeclare const __VERSION__: string;\n\n/**\n * Simplified TEFAS API client that provides a clean interface for fund data access.\n *\n * This client wraps the base REST API client with TEFAS-specific configuration\n * and methods, hiding HTTP implementation details from users.\n *\n * @example\n * ```typescript\n * import { TefasClient, FundType } from 'tefas-client';\n *\n * const client = new TefasClient();\n *\n * // Get all pension funds for a date range\n * const funds = await client.getFundHistory(\n *   '2024-01-01',\n *   '2024-12-31',\n *   undefined,\n *   FundType.EMK\n * );\n *\n * // Get all funds across all types\n * const allFunds = await client.getFundHistory(\n *   '2024-01-01',\n *   '2024-12-31'\n * );\n *\n * // Get specific fund history\n * const specificFund = await client.getFundHistory(\n *   '2024-01-01',\n *   '2024-12-31',\n *   'TGE',\n *   FundType.EMK\n * );\n * ```\n */\nexport class TefasClient {\n  private readonly baseClient: ApiClient;\n  private readonly config: TefasClientConfig;\n  private maxDaysPerRequest: number = DEFAULT_MAX_DAYS_PER_REQUEST;\n  private readonly cache: CacheAdapter<ApiResponse<FundResponse>>;\n\n  /**\n   * Creates a new TEFAS client instance.\n   *\n   * No configuration is required - the client uses hardcoded TEFAS API settings\n   * for zero-configuration setup. Optionally accepts a partial configuration\n   * to override default settings like cache behavior.\n   *\n   * @param configOverrides - Optional configuration overrides\n   * @throws ConfigurationError if TEFAS API configuration is invalid\n   */\n  constructor(configOverrides?: Partial<TefasClientConfig>) {\n    // Hardcoded TEFAS API configuration with optional overrides\n    this.config = {\n      baseUrl: 'https://www.tefas.gov.tr',\n      timeout: 30000, // 30 seconds\n      headers: {\n        'Content-Type': 'application/x-www-form-urlencoded',\n        'Accept': 'application/json',\n        'User-Agent': `TefasClient/${this.getVersion()}`\n      },\n      maxRetries: 3,\n      retryDelay: 1000,\n      cache: {\n        enabled: true,\n        ttl: 15 * 60 * 1000, // 15 minutes\n        maxSize: 1000\n      },\n      ...configOverrides\n    };\n\n    // Initialize cache with configuration\n    // If user provides a custom cache adapter, use it; otherwise use default memory cache\n    this.cache = configOverrides?.cacheAdapter ||\n      new MemoryCacheAdapter<ApiResponse<FundResponse>>({\n        enabled: this.config.cache?.enabled ?? true,\n        ttl: this.config.cache?.ttl ?? 15 * 60 * 1000,\n        maxSize: this.config.cache?.maxSize ?? 1000\n      });\n\n    // Create base API client with TEFAS configuration\n    this.baseClient = new ApiClient({\n      baseUrl: this.config.baseUrl,\n      timeout: this.config.timeout,\n      headers: this.config.headers\n    });\n\n    // Validate configuration\n    this.validateConfiguration();\n  }\n\n  /**\n   * Returns library version string for User-Agent; falls back to 'dev' in tests.\n   */\n  private getVersion(): string {\n    try {\n      // __VERSION__ is injected at build; guard for test/runtime\n      return typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'dev';\n    } catch {\n      return 'dev';\n    }\n  }\n\n  /**\n   * Sets the maximum number of days per request window when fetching history.\n   * Defaults to 90; values must be integers between 1 and 365.\n   */\n  public setMaxDaysPerRequest(maxDays: number): void {\n    if (typeof maxDays !== 'number' || !Number.isInteger(maxDays)) {\n      throw new ValidationError('maxDaysPerRequest must be an integer');\n    }\n    if (maxDays <= 0 || maxDays > 365) {\n      throw new ValidationError('maxDaysPerRequest must be between 1 and 365');\n    }\n    this.maxDaysPerRequest = maxDays;\n  }\n\n  /**\n   * Get fund history data for funds of a specific type or all types.\n   *\n   * @private\n   * Internal method used by getFund. Use getFund() for public API access.\n   *\n   * This method retrieves historical fund data from TEFAS API. It supports both\n   * standard date formats (YYYY-MM-DD) and fuzzy date parsing for natural language\n   * expressions. The method automatically handles large date ranges by splitting\n   * them into smaller chunks to comply with TEFAS API limitations.\n   *\n   * @param startDate - Start date in YYYY-MM-DD format or fuzzy date (e.g., \"today\", \"yesterday\", \"2024-01-01\")\n   * @param endDate - End date in YYYY-MM-DD format or fuzzy date (e.g., \"today\", \"yesterday\", \"2024-01-01\")\n   * @param fundCode - Optional specific fund code to retrieve (3-character code like \"TGE\", \"IIH\")\n   * @param fundType - Optional fund type to filter by. If omitted, fetches all fund types\n   * @returns Promise resolving to array of fund history data\n   *\n   * @throws InvalidFundTypeError if fund type is not supported\n   * @throws InvalidDateError if date format is invalid\n   * @throws FundNotFoundError if fund code is not found\n   * @throws TefasApiError if TEFAS API returns an error\n   * @throws NetworkError if network request fails\n   *\n   * @see {@link getFund} for public API access\n   * @see {@link FundType} for available fund types\n   * @see {@link FundHistoryResponse} for response structure\n   */\n  private async getFundHistory(\n    startDate: string,\n    endDate: string,\n    fundCode?: string,\n    fundType?: FundType\n  ): Promise<ApiResponse<FundHistoryResponse>> {\n    try {\n      // Normalize fuzzy dates to YYYY-MM-DD format\n      const { startDate: normalizedStartDate, endDate: normalizedEndDate } =\n        normalizeFuzzyDateRange(startDate, endDate);\n\n      // Validate input parameters with normalized dates\n      this.validateFundHistoryRequest(fundType, normalizedStartDate, normalizedEndDate, fundCode);\n\n      // If fundType is provided, fetch only that type\n      if (fundType) {\n        return this.fetchFundHistoryForType(fundType, normalizedStartDate, normalizedEndDate, fundCode);\n      }\n\n      // If no fundType provided, fetch all fund types and merge results\n      return this.fetchAllFundTypes(normalizedStartDate, normalizedEndDate, fundCode);\n    } catch (error) {\n      // Translate errors to business-focused messages\n      throw this.translateError(error);\n    }\n  }\n\n  /**\n   * Search for funds by name or code prefix\n   *\n   * Enables autocomplete and fund selection by searching for funds matching\n   * the provided query string. The search is performed against both fund codes\n   * and fund names. Optionally filter by fund type and limit the number of results.\n   *\n   * This method is perfect for implementing autocomplete functionality, fund\n   * selection dropdowns, or finding funds by partial name matches. The search\n   * is case-insensitive and supports Turkish characters.\n   *\n   * @param query - Search query (fund code or name prefix)\n   * @param options - Optional search configuration (fundType filter, result limit)\n   * @returns Promise resolving to array of matching funds\n   *\n   * @throws ValidationError if query is empty or limit is invalid\n   * @throws InvalidFundTypeError if fundType is invalid\n   * @throws TefasApiError if TEFAS API returns an error\n   * @throws NetworkError if network request fails\n   *\n   * @example\n   * ```typescript\n   * import { TefasClient, FundType } from '@firstthumb/tefas-api';\n   *\n   * const client = new TefasClient();\n   *\n   * // Basic search by code prefix\n   * const results = await client.searchFund('IIH');\n   * console.log(results);\n   * // [{ fundCode: \"IIH\", fundName: \"IIH - İSTANBUL PORTFÖY...\" }]\n   *\n   * // Search with fund type filter\n   * const yatFunds = await client.searchFund('IS', { fundType: FundType.YAT });\n   * console.log(yatFunds); // Only investment funds (YAT type)\n   *\n   * // Search with custom result limit (great for autocomplete)\n   * const limitedResults = await client.searchFund('A', { limit: 5 });\n   * console.log(limitedResults.length); // At most 5 results\n   *\n   * // Combine filters and limits\n   * const filtered = await client.searchFund('IS', {\n   *   fundType: FundType.EMK,\n   *   limit: 3\n   * });\n   *\n   * // Use fund code for detailed history\n   * if (results.length > 0) {\n   *   const history = await client.getFundHistory(\n   *     '2024-01-01',\n   *     '2024-12-31',\n   *     results[0].fundCode,\n   *     FundType.YAT\n   *   );\n   * }\n   *\n   * // Autocomplete implementation example\n   * async function autocompleteFunds(query: string) {\n   *   if (query.length < 2) return [];\n   *\n   *   try {\n   *     const results = await client.searchFund(query, { limit: 10 });\n   *     return results.map(fund => ({\n   *       value: fund.fundCode,\n   *       label: fund.fundName,\n   *       code: fund.fundCode\n   *     }));\n   *   } catch (error) {\n   *     console.error('Search failed:', error);\n   *     return [];\n   *   }\n   * }\n   * ```\n   *\n   * @since 1.0.0\n   * @see {@link SearchResult} for result structure\n   * @see {@link SearchOptions} for configuration options\n   */\n  /**\n   * Search funds for a specific fund type\n   *\n   * @private\n   * @param query - Search query string\n   * @param fundType - Fund type to search\n   * @param limit - Optional limit for number of results\n   * @returns Array of SearchResult objects for the specified fund type\n   */\n  private async searchFundByType(\n    query: string,\n    fundType: FundType,\n    limit?: number\n  ): Promise<SearchResult[]> {\n    // Build search API request\n    const searchRequest = {\n      prefixText: query.trim(),\n      count: limit ?? 10,\n      contextKey: fundType\n    };\n\n    // Make API call to GetAllFunds endpoint\n    const response = await this.baseClient.post(\n      '/Service.asmx/GetAllFunds',\n      searchRequest,\n      { contentType: 'json' }\n    );\n\n    // Parse nested JSON response with fundType\n    return parseSearchResponse(response, fundType);\n  }\n\n  async searchFund(query: string, options?: SearchOptions): Promise<ApiResponse<SearchResult>> {\n    try {\n      // Validate input\n      this.validateSearchQuery(query);\n\n      // Validate options if provided\n      if (options?.fundType) {\n        validateFundType(options.fundType);\n      }\n\n      if (options?.limit !== undefined) {\n        this.validateLimit(options.limit);\n      }\n\n      const trimmedQuery = query.trim();\n      const limit = options?.limit;\n\n      // If fundType is provided, search only that type\n      if (options?.fundType) {\n        const results = await this.searchFundByType(trimmedQuery, options.fundType, limit);\n        return { results };\n      }\n\n      // If no fundType filter, search all fund types in parallel\n      // Use a higher limit per type to ensure we have enough results after merging\n      // If limit is provided, use at least that amount per type, otherwise use default 10\n      const perTypeLimit = limit ? Math.max(limit, 10) : undefined;\n\n      const searchPromises = SUPPORTED_FUND_TYPES.map(fundType =>\n        this.searchFundByType(trimmedQuery, fundType, perTypeLimit).catch((error) => {\n          // Log error but continue with other searches\n          // Return empty array for failed searches\n          console.warn(`Search failed for fund type ${fundType}:`, error);\n          return [] as SearchResult[];\n        })\n      );\n\n      // Wait for all searches to complete\n      const allResults = await Promise.all(searchPromises);\n\n      // Merge and deduplicate results by fundCode\n      const resultsMap = new Map<string, SearchResult>();\n      for (const fundTypeResults of allResults) {\n        for (const result of fundTypeResults) {\n          // Only add if we haven't seen this fundCode before\n          if (!resultsMap.has(result.fundCode)) {\n            resultsMap.set(result.fundCode, result);\n          }\n        }\n      }\n\n      // Convert to array and apply limit if provided\n      let results = Array.from(resultsMap.values());\n      if (limit !== undefined && limit > 0) {\n        results = results.slice(0, limit);\n      }\n\n      return { results };\n    } catch (error) {\n      // Translate errors to business-focused messages\n      throw this.translateError(error);\n    }\n  }\n\n  /**\n   * Fetch fund detail data from Fundfy and return selected fields.\n   *\n   * @private\n   * @param fundCode - Fund code identifier\n   * @returns Fund detail information\n   */\n  private async getFundDetails(fundCode: string): Promise<FundDetail> {\n    try {\n      if (!fundCode || typeof fundCode !== 'string' || fundCode.trim().length === 0) {\n        throw new ValidationError('fundCode must be a non-empty string');\n      }\n\n      const normalizedCode = fundCode.trim().toUpperCase();\n      const fundfyClient = this.baseClient.withConfig({\n        baseUrl: FUNDFY_BASE_URL,\n        headers: {\n          ...this.config.headers,\n          Origin: FUNDFY_ORIGIN,\n          Referer: `${FUNDFY_ORIGIN}/`,\n          Authorization: FUNDFY_AUTH_TOKEN,\n          Accept: 'application/json'\n        }\n      });\n\n      const response = await fundfyClient.get<FundfyDetailResponse>(\n        `/api/v1/fund/detail/${encodeURIComponent(normalizedCode)}`\n      );\n\n      if (!response?.fund) {\n        throw new FundNotFoundError(normalizedCode);\n      }\n\n      return this.mapFundfyResponse(response.fund);\n    } catch (error) {\n      throw this.translateError(error);\n    }\n  }\n\n  /**\n   * Validates limit parameter\n   *\n   * @private\n   * @param limit - Limit value to validate\n   * @throws ValidationError if limit is invalid\n   */\n  private validateLimit(limit: number): void {\n    if (typeof limit !== 'number') {\n      throw new ValidationError('limit must be a number');\n    }\n\n    if (!Number.isInteger(limit)) {\n      throw new ValidationError('limit must be an integer');\n    }\n\n    if (limit <= 0) {\n      throw new ValidationError('limit must be a positive integer');\n    }\n  }\n\n  /**\n   * Validates search query parameter\n   *\n   * @private\n   * @param query - Search query to validate\n   * @throws ValidationError if query is invalid\n   */\n  private validateSearchQuery(query: string): void {\n    // Check if query is provided\n    if (!query || typeof query !== 'string') {\n      throw new ValidationError('query must be a non-empty string');\n    }\n\n    // Check if query is not just whitespace\n    if (query.trim().length === 0) {\n      throw new ValidationError('query must be a non-empty string');\n    }\n  }\n\n  /**\n   * Get fund allocation/portfolio composition data for funds of a specific type or all types.\n   *\n   * @private\n   * Internal method used by getFund. Use getFund() for public API access.\n   *\n   * This method retrieves historical fund allocation data from TEFAS API. It supports both\n   * standard date formats (YYYY-MM-DD) and fuzzy date parsing for natural language\n   * expressions. The method automatically handles large date ranges by splitting\n   * them into smaller chunks to comply with TEFAS API limitations.\n   *\n   * @param startDate - Start date in YYYY-MM-DD format or fuzzy date (e.g., \"today\", \"yesterday\", \"2024-01-01\")\n   * @param endDate - End date in YYYY-MM-DD format or fuzzy date (e.g., \"today\", \"yesterday\", \"2024-01-01\")\n   * @param fundCode - Optional specific fund code to retrieve (3-character code like \"TGE\", \"IIH\")\n   * @param fundType - Optional fund type to filter by. If omitted, fetches all fund types\n   * @returns Promise resolving to array of fund allocation data\n   *\n   * @throws InvalidFundTypeError if fund type is not supported\n   * @throws InvalidDateError if date format is invalid\n   * @throws FundNotFoundError if fund code is not found\n   * @throws TefasApiError if TEFAS API returns an error\n   * @throws NetworkError if network request fails\n   *\n   * @see {@link getFund} for public API access\n   * @see {@link FundType} for available fund types\n   * @see {@link FundContentResponse} for response structure\n   */\n  private async getFundContent(\n    startDate: string,\n    endDate: string,\n    fundCode?: string,\n    fundType?: FundType\n  ): Promise<ApiResponse<FundContentResponse>> {\n    try {\n      // Normalize fuzzy dates to YYYY-MM-DD format\n      const { startDate: normalizedStartDate, endDate: normalizedEndDate } =\n        normalizeFuzzyDateRange(startDate, endDate);\n\n      // Validate input parameters with normalized dates\n      this.validateFundContentRequest(fundType, normalizedStartDate, normalizedEndDate, fundCode);\n\n      // If fundType is provided, fetch only that type\n      if (fundType) {\n        return this.fetchFundContentForType(fundType, normalizedStartDate, normalizedEndDate, fundCode);\n      }\n\n      // If no fundType provided, fetch all fund types and merge results\n      return this.fetchAllFundTypesContent(normalizedStartDate, normalizedEndDate, fundCode);\n    } catch (error) {\n      // Translate errors to business-focused messages\n      throw this.translateError(error);\n    }\n  }\n\n  /**\n   * Get merged fund data combining history and content information.\n   *\n   * This method retrieves both fund history and allocation data from TEFAS API\n   * and merges them based on fund code and date. It provides a unified view of\n   * fund data where fields from either source may be null if the corresponding\n   * data is not available for that fund/date combination.\n   *\n   * **Filtering Behavior:**\n   * - When `fundType` is provided: Searches only that specific fund type\n   * - When `fundType` is omitted: Searches across ALL fund types (EMK, YAT, BYF, GYF, GSYF)\n   * - When both `fundCode` and `fundType` are provided: Filters to that specific fund in that type\n   *\n   * This is the primary public API for fetching fund data. For searching funds by name/code, use {@link searchFund}.\n   *\n   * The method supports both standard date formats (YYYY-MM-DD) and fuzzy date\n   * parsing for natural language expressions. It automatically handles large\n   * date ranges by splitting them into smaller chunks to comply with TEFAS API\n   * limitations.\n   *\n   * @param startDate - Start date in YYYY-MM-DD format or fuzzy date (e.g., \"today\", \"yesterday\", \"2024-01-01\")\n   * @param endDate - End date in YYYY-MM-DD format or fuzzy date (e.g., \"today\", \"yesterday\", \"2024-01-01\")\n   * @param fundCode - Optional specific fund code to retrieve (3-character code like \"TGE\", \"IIH\")\n   * @param fundType - Optional fund type to filter by (EMK, YAT, BYF, GYF, GSYF). If omitted, searches all types.\n   * @returns Promise resolving to array of merged fund data\n   *\n   * @throws InvalidFundTypeError if fund type is invalid\n   * @throws InvalidDateError if date format is invalid\n   * @throws FundNotFoundError if fund code is not found\n   * @throws TefasApiError if TEFAS API returns an error\n   * @throws NetworkError if network request fails\n   *\n   * @example\n   * ```typescript\n   * import { TefasClient, FundType } from '@firstthumb/tefas-api';\n   *\n   * const client = new TefasClient();\n   *\n   * // Get merged data for a specific fund using fuzzy dates\n   * const fundData = await client.getFund(\n   *   'yesterday',\n   *   'today',\n   *   'TGE'\n   * );\n   * console.log(fundData[0].price); // Fund price\n   * console.log(fundData[0].stocks); // Stock allocation percentage\n   *\n   * // Get merged data for all funds across all types\n   * const allFunds = await client.getFund(\n   *   '2024-01-01',\n   *   '2024-12-31'\n   * );\n   * console.log(allFunds.length); // Total number of fund/date combinations\n   *\n   * // Filter by fund type - get only pension funds (EMK)\n   * const pensionFunds = await client.getFund(\n   *   '2024-01-01',\n   *   '2024-12-31',\n   *   undefined,\n   *   FundType.EMK\n   * );\n   *\n   * // Get specific fund in specific type\n   * const specificPensionFund = await client.getFund(\n   *   'last week',\n   *   'today',\n   *   'TGE',\n   *   FundType.EMK\n   * );\n   *\n   * // Handle missing data gracefully\n   * fundData.forEach(fund => {\n   *   if (fund.price !== null) {\n   *     console.log(`Price: ${fund.price}`);\n   *   }\n   *   if (fund.stocks !== null && fund.stocks !== undefined) {\n   *     console.log(`Stock allocation: ${fund.stocks}%`);\n   *   }\n   * });\n   *\n   * // Handle errors gracefully\n   * try {\n   *   const data = await client.getFund('2024-01-01', '2024-01-31', 'INVALID');\n   * } catch (error) {\n   *   if (error instanceof FundNotFoundError) {\n   *     console.log('Fund not found');\n   *   }\n   * }\n   * ```\n   *\n   * @since 1.0.0\n   * @see {@link FundResponse} for response structure\n   * @see {@link getFundHistory} for history-only data\n   * @see {@link getFundContent} for content-only data\n   */\n  async getFund(\n    startDate: string,\n    endDate: string,\n    fundCode?: string,\n    fundType?: FundType,\n    skipFundDetails: boolean = false\n  ): Promise<ApiResponse<FundResponse>> {\n    try {\n      // Validate fund type if provided\n      if (fundType) {\n        validateFundType(fundType);\n      }\n\n      // Normalize fuzzy dates to YYYY-MM-DD format\n      const { startDate: normalizedStartDate, endDate: normalizedEndDate } =\n        normalizeFuzzyDateRange(startDate, endDate);\n\n      // Auto-detect fundType if fundCode is provided but fundType is not\n      let resolvedFundType = fundType;\n      if (fundCode && !resolvedFundType) {\n        const searchResults = await this.searchFund(fundCode, { limit: 10 });\n        if (searchResults.results.length === 0) {\n          throw new FundNotFoundError(fundCode);\n        }\n        // Find exact match by fundCode (case-insensitive)\n        const matchedFund = searchResults.results.find(\n          r => r.fundCode.toUpperCase() === fundCode.trim().toUpperCase()\n        );\n        if (!matchedFund) {\n          throw new FundNotFoundError(fundCode);\n        }\n        resolvedFundType = matchedFund.fundType;\n      }\n\n      // Generate cache key using resolved fundType\n      const cacheKey = this.generateCacheKey(\n        normalizedStartDate,\n        normalizedEndDate,\n        fundCode,\n        resolvedFundType\n      );\n\n      // Check cache first\n      const cachedResult = await this.cache.get(cacheKey);\n      if (cachedResult) {\n        return cachedResult;\n      }\n\n      // Fetch history and content data sequentially to avoid rate limiting\n      const historyResponse = await this.getFundHistory(normalizedStartDate, normalizedEndDate, fundCode, resolvedFundType);\n      const contentResponse = await this.getFundContent(normalizedStartDate, normalizedEndDate, fundCode, resolvedFundType);\n\n      // Extract data from wrapped responses\n      const historyData = historyResponse.results;\n      const contentData = contentResponse.results;\n\n      // Extract unique fund codes from history and content data\n      const uniqueFundCodes = new Set<string>();\n      for (const history of historyData) {\n        uniqueFundCodes.add(history.fundCode);\n      }\n      for (const content of contentData) {\n        uniqueFundCodes.add(content.fundCode);\n      }\n\n      // Fetch fund details for each unique fund code (unless skipped for performance)\n      const fundDetailsMap = new Map<string, FundDetail | null>();\n      if (!skipFundDetails) {\n        const totalFundCodes = uniqueFundCodes.size;\n        if (totalFundCodes > 0) {\n          console.log(`Fetching fund details for ${totalFundCodes} unique fund codes...`);\n        }\n        let detailsFetched = 0;\n        for (const code of uniqueFundCodes) {\n          try {\n            const details = await this.getFundDetails(code);\n            fundDetailsMap.set(code, details);\n            detailsFetched++;\n            if (detailsFetched % 50 === 0) {\n              console.log(`Fetched fund details: ${detailsFetched}/${totalFundCodes}...`);\n            }\n          } catch {\n            // If getFundDetails fails, set null - details unavailable for this fund\n            fundDetailsMap.set(code, null);\n          }\n        }\n        if (totalFundCodes > 0) {\n          console.log(`Completed fetching fund details: ${detailsFetched}/${totalFundCodes} successful`);\n        }\n      } else {\n        // Skip fund details - set all to null for faster bulk operations\n        for (const code of uniqueFundCodes) {\n          fundDetailsMap.set(code, null);\n        }\n        console.log(`Skipping fund details fetch for ${uniqueFundCodes.size} funds (skipFundDetails=true)`);\n      }\n\n      // Create maps for efficient lookup by fundCode + date\n      const historyMap = new Map<string, FundHistoryResponse>();\n      const contentMap = new Map<string, FundContentResponse>();\n\n      // Populate history map\n      for (const history of historyData) {\n        const key = `${history.fundCode}_${history.date}`;\n        historyMap.set(key, history);\n      }\n\n      // Populate content map\n      for (const content of contentData) {\n        const key = `${content.fundCode}_${content.date}`;\n        contentMap.set(key, content);\n      }\n\n      // Collect all unique fundCode + date combinations\n      const allKeys = new Set<string>();\n      for (const key of historyMap.keys()) {\n        allKeys.add(key);\n      }\n      for (const key of contentMap.keys()) {\n        allKeys.add(key);\n      }\n\n      // Merge data for each unique combination\n      const mergedData: FundResponse[] = [];\n      for (const key of allKeys) {\n        const [fundCode, date] = key.split('_');\n        const history = historyMap.get(key);\n        const content = contentMap.get(key);\n\n        // Determine fund name and type from available data\n        const fundName = history?.fundName || content?.fundName || '';\n        const fundType = history?.fundType || content?.fundType || FundType.EMK;\n\n        // Get fund details for this fund code\n        const details = fundDetailsMap.get(fundCode);\n\n        // Create merged response\n        const merged: FundResponse = {\n          fundCode,\n          fundName,\n          fundType,\n          date,\n\n          // History fields\n          price: history?.price ?? null,\n          volume: history?.volume ?? null,\n          assets: history?.assets ?? null,\n          investorCount: history?.investorCount ?? null,\n\n          // Content fields\n          totalValue: content?.totalValue ?? null,\n          stocks: content?.stocks ?? null,\n          foreignSecurities: content?.foreignSecurities ?? null,\n          stockBasedFunds: content?.stockBasedFunds ?? null,\n          stockBasedInvestmentFunds: content?.stockBasedInvestmentFunds ?? null,\n          bonds: content?.bonds ?? null,\n          governmentBonds: content?.governmentBonds ?? null,\n          privateSectorBonds: content?.privateSectorBonds ?? null,\n          governmentSecurities: content?.governmentSecurities ?? null,\n          governmentSecuritiesYield: content?.governmentSecuritiesYield ?? null,\n          governmentYieldBonds: content?.governmentYieldBonds ?? null,\n          governmentYield: content?.governmentYield ?? null,\n          deposits: content?.deposits ?? null,\n          reversePurchaseAgreements: content?.reversePurchaseAgreements ?? null,\n          preciousMetals: content?.preciousMetals ?? null,\n          cashAndCashEquivalents: content?.cashAndCashEquivalents ?? null,\n          otherSecurities: content?.otherSecurities ?? null,\n          otherSecuritiesYield: content?.otherSecuritiesYield ?? null,\n          treasuryBills: content?.treasuryBills ?? null,\n          individualInvestmentFunds: content?.individualInvestmentFunds ?? null,\n          governmentDebtInstruments: content?.governmentDebtInstruments ?? null,\n          governmentDebtInstrumentsForeign: content?.governmentDebtInstrumentsForeign ?? null,\n          eurobonds: content?.eurobonds ?? null,\n          foreignBonds: content?.foreignBonds ?? null,\n          foreignCurrencyBonds: content?.foreignCurrencyBonds ?? null,\n          goldAndSilver: content?.goldAndSilver ?? null,\n          participationAccounts: content?.participationAccounts ?? null,\n          participationAccountsGold: content?.participationAccountsGold ?? null,\n          participationAccountsGoldForeign: content?.participationAccountsGoldForeign ?? null,\n          participationAccountsForeignCurrency: content?.participationAccountsForeignCurrency ?? null,\n          participationAccountsTurkishLira: content?.participationAccountsTurkishLira ?? null,\n          publicLeaseCertificates: content?.publicLeaseCertificates ?? null,\n          publicLeaseCertificatesForeignCurrency: content?.publicLeaseCertificatesForeignCurrency ?? null,\n          publicLeaseCertificatesTurkishLira: content?.publicLeaseCertificatesTurkishLira ?? null,\n          publicLeaseCertificatesForeign: content?.publicLeaseCertificatesForeign ?? null,\n          participationAccountsForeignCurrencyBonds: content?.participationAccountsForeignCurrencyBonds ?? null,\n          otherSecuritiesTurkishLira: content?.otherSecuritiesTurkishLira ?? null,\n          repoTransactions: content?.repoTransactions ?? null,\n          termDeposits: content?.termDeposits ?? null,\n          treasuryBillsShort: content?.treasuryBillsShort ?? null,\n          variableRateDeposits: content?.variableRateDeposits ?? null,\n          moneyMarketInstruments: content?.moneyMarketInstruments ?? null,\n          moneyMarketInstrumentsGold: content?.moneyMarketInstrumentsGold ?? null,\n          moneyMarketInstrumentsForeignCurrency: content?.moneyMarketInstrumentsForeignCurrency ?? null,\n          moneyMarketInstrumentsTurkishLira: content?.moneyMarketInstrumentsTurkishLira ?? null,\n          variableIncomeInstruments: content?.variableIncomeInstruments ?? null,\n          investmentFundParticipations: content?.investmentFundParticipations ?? null,\n          investmentFundParticipationsBonds: content?.investmentFundParticipationsBonds ?? null,\n          investmentFundParticipationsOtherSecurities: content?.investmentFundParticipationsOtherSecurities ?? null,\n          investmentFundParticipationsForeignCurrency: content?.investmentFundParticipationsForeignCurrency ?? null,\n          investmentFundParticipationsMetals: content?.investmentFundParticipationsMetals ?? null,\n          preciousMetalsInvestmentFunds: content?.preciousMetalsInvestmentFunds ?? null,\n          preciousMetalsGovernmentDebtInstruments: content?.preciousMetalsGovernmentDebtInstruments ?? null,\n          preciousMetalsPublicLeaseCertificates: content?.preciousMetalsPublicLeaseCertificates ?? null,\n\n          // Fund detail fields\n          auditFirm: details?.auditFirm ?? null,\n          emailAddress: details?.emailAddress ?? null,\n          fundDuration: details?.fundDuration ?? null,\n          contactAddress: details?.contactAddress ?? null,\n          founder: details?.founder ?? null,\n          founderLogo: details?.founderLogo ?? null,\n          umbrellaFundType: details?.umbrellaFundType ?? null,\n          umbrellaFundName: details?.umbrellaFundName ?? null,\n          fundCategory: details?.fundCategory ?? null,\n          risk: details?.risk ?? null,\n          yearlyManagementFee: details?.yearlyManagementFee ?? null,\n          buyingValor: details?.buyingValor ?? null,\n          sellingValor: details?.sellingValor ?? null,\n          strategyStatement: details?.strategyStatement ?? null,\n          fundBenchmarks: details?.fundBenchmarks ?? []\n        };\n\n        mergedData.push(merged);\n      }\n\n      // Sort by date ascending for consistent ordering\n      mergedData.sort((a, b) => (a.date < b.date ? -1 : a.date > b.date ? 1 : 0));\n\n      const result = { results: mergedData };\n\n      // Store result in cache\n      await this.cache.set(cacheKey, result);\n\n      return result;\n    } catch (error) {\n      // Translate errors to business-focused messages\n      throw this.translateError(error);\n    }\n  }\n\n  /**\n   * Validates the TEFAS API configuration.\n   *\n   * @private\n   * @throws ConfigurationError if configuration is invalid\n   */\n  private validateConfiguration(): void {\n    if (!this.config.baseUrl || typeof this.config.baseUrl !== 'string') {\n      throw new ConfigurationError('Invalid base URL configuration');\n    }\n\n    if (this.config.timeout <= 0) {\n      throw new ConfigurationError('Invalid timeout configuration');\n    }\n\n    if (!this.config.headers || typeof this.config.headers !== 'object') {\n      throw new ConfigurationError('Invalid headers configuration');\n    }\n\n    // Validate required headers\n    const requiredHeaders = ['Content-Type', 'Accept'];\n    for (const header of requiredHeaders) {\n      if (!this.config.headers[header]) {\n        throw new ConfigurationError(`Missing required header: ${header}`);\n      }\n    }\n  }\n\n  /**\n   * Validates fund history request parameters.\n   *\n   * @private\n   * @param fundType - Fund type to validate (optional)\n   * @param startDate - Start date to validate\n   * @param endDate - End date to validate\n   * @param fundCode - Optional fund code to validate\n   * @throws InvalidFundTypeError if fund type is invalid\n   * @throws InvalidDateError if date format is invalid\n   * @throws DateRangeError if date range is invalid\n   */\n  private validateFundHistoryRequest(\n    fundType: FundType | undefined,\n    startDate: string,\n    endDate: string,\n    fundCode?: string\n  ): void {\n    // Validate fund type if provided\n    if (fundType) {\n      validateFundType(fundType);\n    }\n\n    // Validate date range (includes individual date validation)\n    validateDateRange(startDate, endDate);\n\n    // Validate fund code if provided\n    if (fundCode !== undefined) {\n      if (!fundCode || typeof fundCode !== 'string' || fundCode.trim().length === 0) {\n        throw new InvalidDateError(fundCode, 'Fund code must be a non-empty string');\n      }\n\n      // TEFAS fund codes are typically 3 characters\n      if (fundCode.trim().length !== 3) {\n        throw new InvalidDateError(fundCode, 'Fund code must be exactly 3 characters');\n      }\n    }\n  }\n\n  /**\n   * Validates fund content request parameters.\n   *\n   * @private\n   * @param fundType - Fund type to validate (optional)\n   * @param startDate - Start date to validate\n   * @param endDate - End date to validate\n   * @param fundCode - Optional fund code to validate\n   * @throws InvalidFundTypeError if fund type is invalid\n   * @throws InvalidDateError if date format is invalid\n   * @throws DateRangeError if date range is invalid\n   */\n  private validateFundContentRequest(\n    fundType: FundType | undefined,\n    startDate: string,\n    endDate: string,\n    fundCode?: string\n  ): void {\n    // Validate fund type if provided\n    if (fundType) {\n      validateFundType(fundType);\n    }\n\n    // Validate date range (includes individual date validation)\n    validateDateRange(startDate, endDate);\n\n    // Validate fund code if provided\n    if (fundCode !== undefined) {\n      if (!fundCode || typeof fundCode !== 'string' || fundCode.trim().length === 0) {\n        throw new InvalidDateError(fundCode, 'Fund code must be a non-empty string');\n      }\n\n      // TEFAS fund codes are typically 3 characters\n      if (fundCode.trim().length !== 3) {\n        throw new InvalidDateError(fundCode, 'Fund code must be exactly 3 characters');\n      }\n    }\n  }\n\n  /**\n   * Fetches fund history for a specific fund type.\n   *\n   * @private\n   * @param fundType - Fund type to fetch\n   * @param startDate - Start date\n   * @param endDate - End date\n   * @param fundCode - Optional fund code\n   * @param throwOnNotFound - Whether to throw error if fund not found (default: true)\n   * @returns Promise resolving to fund history data\n   */\n  private async fetchFundHistoryForType(\n    fundType: FundType,\n    startDate: string,\n    endDate: string,\n    fundCode?: string,\n    throwOnNotFound: boolean = true\n  ): Promise<ApiResponse<FundHistoryResponse>> {\n    // If the requested range exceeds maxDaysPerRequest, split into chunks\n    const totalDays = getDaysBetween(startDate, endDate);\n    const MAX_DAYS = this.maxDaysPerRequest;\n\n    let fundHistory: FundHistoryResponse[] = [];\n    if (totalDays > MAX_DAYS) {\n      // Calculate all chunks\n      const chunks: Array<{ start: string; end: string }> = [];\n      let chunkStart = startDate;\n      while (true) {\n        const tentativeEnd = addDays(chunkStart, MAX_DAYS - 1);\n        const chunkEnd = tentativeEnd > endDate ? endDate : tentativeEnd;\n        chunks.push({ start: chunkStart, end: chunkEnd });\n        if (chunkEnd === endDate) break;\n        chunkStart = addDays(chunkEnd, 1);\n      }\n\n      console.log(`Fetching fund history [${fundType}] [fund: ${fundCode || 'all'}]: ${chunks.length} chunks (${totalDays} days total)`);\n\n      // Execute chunks sequentially to avoid rate limiting\n      for (let i = 0; i < chunks.length; i++) {\n        const { start, end } = chunks[i];\n        const chunkNumber = i + 1;\n        const chunkName = `chunk ${chunkNumber}/${chunks.length} (${start} to ${end})`;\n\n        console.log(`Fetching fund history [${fundType}] [fund: ${fundCode || 'all'}]: ${chunkName}`);\n\n        const chunkHistory = await this.retryWithDelay(\n          async () => {\n            const chunkRequest = this.buildTefasRequest(fundType, start, end, fundCode);\n            const chunkResponse = await this.baseClient.post<TefasApiResponse>(\n              '/api/DB/BindHistoryInfo',\n              chunkRequest,\n              { contentType: 'form' }\n            );\n            return this.processTefasResponse(chunkResponse, fundType);\n          },\n          chunkName\n        );\n\n        fundHistory.push(...chunkHistory);\n      }\n\n      console.log(`Successfully fetched fund history [${fundType}] [fund: ${fundCode || 'all'}]: ${chunks.length}/${chunks.length} chunks (${totalDays} days)`);\n    } else {\n      // Single call path\n      console.log(`Fetching fund history [${fundType}] [fund: ${fundCode || 'all'}]: single call (${startDate} to ${endDate})`);\n      const tefasRequest = this.buildTefasRequest(fundType, startDate, endDate, fundCode);\n      const response = await this.baseClient.post<TefasApiResponse>('/api/DB/BindHistoryInfo', tefasRequest, {\n        contentType: 'form'\n      });\n      fundHistory = this.processTefasResponse(response, fundType);\n      console.log(`Successfully fetched fund history [${fundType}] [fund: ${fundCode || 'all'}]: ${fundHistory.length} records`);\n    }\n\n    // Ensure deterministic ordering by date ascending\n    fundHistory.sort((a, b) => (a.date < b.date ? -1 : a.date > b.date ? 1 : 0));\n\n    // If specific fund code was requested, validate it exists\n    if (fundCode && fundHistory.length === 0 && throwOnNotFound) {\n      throw new FundNotFoundError(fundCode);\n    }\n\n    return { results: fundHistory };\n  }\n\n  /**\n   * Fetches fund history for all fund types and merges results.\n   *\n   * @private\n   * @param startDate - Start date\n   * @param endDate - End date\n   * @param fundCode - Optional fund code\n   * @returns Promise resolving to merged fund history data\n   */\n  private async fetchAllFundTypes(\n    startDate: string,\n    endDate: string,\n    fundCode?: string\n  ): Promise<ApiResponse<FundHistoryResponse>> {\n    const allFundTypes = SUPPORTED_FUND_TYPES;\n    const allResults: FundHistoryResponse[] = [];\n\n    // Fetch data for each fund type sequentially to avoid rate limiting\n    for (const fundType of allFundTypes) {\n      try {\n        const response = await this.fetchFundHistoryForType(fundType, startDate, endDate, fundCode, false);\n        allResults.push(...response.results);\n      } catch {\n        // If a specific fund type fails, continue with others\n        continue;\n      }\n    }\n\n    // If specific fund code was requested, validate it exists in at least one fund type\n    if (fundCode && allResults.length === 0) {\n      throw new FundNotFoundError(`Fund '${fundCode}' not found in any fund type`);\n    }\n\n    return { results: allResults };\n  }\n\n  /**\n   * Fetches fund allocation data for a specific fund type.\n   *\n   * @private\n   * @param fundType - Fund type to fetch\n   * @param startDate - Start date\n   * @param endDate - End date\n   * @param fundCode - Optional fund code\n   * @param throwOnNotFound - Whether to throw error if fund not found (default: true)\n   * @returns Promise resolving to fund allocation data\n   */\n  private async fetchFundContentForType(\n    fundType: FundType,\n    startDate: string,\n    endDate: string,\n    fundCode?: string,\n    throwOnNotFound: boolean = true\n  ): Promise<ApiResponse<FundContentResponse>> {\n    // If the requested range exceeds maxDaysPerRequest, split into chunks\n    const totalDays = getDaysBetween(startDate, endDate);\n    const MAX_DAYS = this.maxDaysPerRequest;\n\n    let fundContent: FundContentResponse[] = [];\n    if (totalDays > MAX_DAYS) {\n      // Calculate all chunks\n      const chunks: Array<{ start: string; end: string }> = [];\n      let chunkStart = startDate;\n      while (true) {\n        const tentativeEnd = addDays(chunkStart, MAX_DAYS - 1);\n        const chunkEnd = tentativeEnd > endDate ? endDate : tentativeEnd;\n        chunks.push({ start: chunkStart, end: chunkEnd });\n        if (chunkEnd === endDate) break;\n        chunkStart = addDays(chunkEnd, 1);\n      }\n\n      console.log(`Fetching fund content [${fundType}] [fund: ${fundCode || 'all'}]: ${chunks.length} chunks (${totalDays} days total)`);\n\n      // Execute chunks sequentially to avoid rate limiting\n      for (let i = 0; i < chunks.length; i++) {\n        const { start, end } = chunks[i];\n        const chunkNumber = i + 1;\n        const chunkName = `chunk ${chunkNumber}/${chunks.length} (${start} to ${end})`;\n\n        console.log(`Fetching fund content [${fundType}] [fund: ${fundCode || 'all'}]: ${chunkName}`);\n\n        const chunkContent = await this.retryWithDelay(\n          async () => {\n            const chunkRequest = this.buildAllocationRequest(fundType, start, end, fundCode);\n            const chunkResponse = await this.baseClient.post<TefasAllocationApiResponse>(\n              TEFAS_ALLOCATION_ENDPOINT,\n              chunkRequest,\n              { contentType: 'form' }\n            );\n            return this.processAllocationResponse(chunkResponse, fundType);\n          },\n          chunkName\n        );\n\n        fundContent.push(...chunkContent);\n      }\n\n      console.log(`Successfully fetched fund content [${fundType}] [fund: ${fundCode || 'all'}]: ${chunks.length}/${chunks.length} chunks (${totalDays} days)`);\n    } else {\n      // Single call path\n      console.log(`Fetching fund content [${fundType}] [fund: ${fundCode || 'all'}]: single call (${startDate} to ${endDate})`);\n      const allocationRequest = this.buildAllocationRequest(fundType, startDate, endDate, fundCode);\n      const response = await this.baseClient.post<TefasAllocationApiResponse>(\n        TEFAS_ALLOCATION_ENDPOINT,\n        allocationRequest,\n        { contentType: 'form' }\n      );\n      fundContent = this.processAllocationResponse(response, fundType);\n      console.log(`Successfully fetched fund content [${fundType}] [fund: ${fundCode || 'all'}]: ${fundContent.length} records`);\n    }\n\n    // Ensure deterministic ordering by date ascending\n    fundContent.sort((a, b) => (a.date < b.date ? -1 : a.date > b.date ? 1 : 0));\n\n    // If specific fund code was requested, validate it exists\n    if (fundCode && fundContent.length === 0 && throwOnNotFound) {\n      throw new FundNotFoundError(fundCode);\n    }\n\n    return { results: fundContent };\n  }\n\n  /**\n   * Fetches fund allocation data for all fund types and merges results.\n   *\n   * @private\n   * @param startDate - Start date\n   * @param endDate - End date\n   * @param fundCode - Optional fund code\n   * @returns Promise resolving to merged fund allocation data\n   */\n  private async fetchAllFundTypesContent(\n    startDate: string,\n    endDate: string,\n    fundCode?: string\n  ): Promise<ApiResponse<FundContentResponse>> {\n    const allFundTypes = SUPPORTED_FUND_TYPES;\n    const allResults: FundContentResponse[] = [];\n\n    // Fetch data for each fund type sequentially to avoid rate limiting\n    for (const fundType of allFundTypes) {\n      try {\n        const response = await this.fetchFundContentForType(fundType, startDate, endDate, fundCode, false);\n        allResults.push(...response.results);\n      } catch {\n        // If a specific fund type fails, continue with others\n        continue;\n      }\n    }\n\n    // If specific fund code was requested, validate it exists in at least one fund type\n    if (fundCode && allResults.length === 0) {\n      throw new FundNotFoundError(`Fund '${fundCode}' not found in any fund type`);\n    }\n\n    return { results: allResults };\n  }\n\n  /**\n   * Builds TEFAS API request parameters.\n   *\n   * @private\n   * @param fundType - Fund type\n   * @param startDate - Start date\n   * @param endDate - End date\n   * @param fundCode - Optional fund code\n   * @returns TEFAS API request object\n   */\n  private buildTefasRequest(\n    fundType: FundType,\n    startDate: string,\n    endDate: string,\n    fundCode?: string\n  ): TefasApiRequest {\n    const request: TefasApiRequest = {\n      fontip: fundType,\n      bastarih: convertToTefasDateFormat(startDate),\n      bittarih: convertToTefasDateFormat(endDate)\n    };\n\n    // Add fund code if provided\n    if (fundCode) {\n      request.fonkod = fundCode.trim().toUpperCase();\n    }\n\n    return request;\n  }\n\n  /**\n   * Builds TEFAS allocation API request parameters.\n   *\n   * @private\n   * @param fundType - Fund type\n   * @param startDate - Start date\n   * @param endDate - End date\n   * @param fundCode - Optional fund code\n   * @returns TEFAS allocation API request object\n   */\n  private buildAllocationRequest(\n    fundType: FundType,\n    startDate: string,\n    endDate: string,\n    fundCode?: string\n  ): Record<string, string> {\n    const request: Record<string, string> = {\n      fontip: fundType,\n      bastarih: convertToTefasDateFormat(startDate),\n      bittarih: convertToTefasDateFormat(endDate)\n    };\n\n    // Add fund code if provided\n    if (fundCode) {\n      request.fonkod = fundCode.trim().toUpperCase();\n    }\n\n    return request;\n  }\n\n  /**\n   * Processes TEFAS API response and maps it to our typed response.\n   *\n   * @private\n   * @param response - Raw TEFAS API response\n   * @param fundType - Fund type from the request\n   * @returns Array of mapped fund history responses\n   */\n  private processTefasResponse(response: TefasApiResponse, fundType: FundType): FundHistoryResponse[] {\n    return mapTefasResponse(response, fundType);\n  }\n\n  /**\n   * Processes TEFAS allocation API response and maps it to our typed response.\n   *\n   * @private\n   * @param response - Raw TEFAS allocation API response\n   * @param fundType - Fund type from the request\n   * @returns Array of mapped fund allocation responses\n   */\n  private processAllocationResponse(response: TefasAllocationApiResponse, fundType: FundType): FundContentResponse[] {\n    return mapAllocationResponse(response, fundType);\n  }\n\n  /**\n   * Maps Fundfy response to FundDetail shape\n   */\n  private mapFundfyResponse(fund: FundfyFund): FundDetail {\n    return {\n      code: fund.code,\n      title: fund.title,\n      auditFirm: fund.auditFirm ?? null,\n      emailAddress: fund.emailAddress ?? null,\n      fundDuration: fund.fundDuration ?? null,\n      contactAddress: fund.contactAddress ?? null,\n      founder: fund.founder ?? null,\n      founderLogo: this.extractFilename(fund.founderLogo),\n      umbrellaFundType: fund.umbrellaFundType ?? null,\n      umbrellaFundName: fund.umbrellaFundName ?? null,\n      fundCategory: fund.fundCategory\n        ? {\n            abbreviation: fund.fundCategory.abbreviation,\n            fullName: fund.fundCategory.fullName\n          }\n        : null,\n      risk: fund.risk ?? null,\n      yearlyManagementFee: fund.yearlyManagementFee ?? null,\n      buyingValor: fund.buyingValor ?? null,\n      sellingValor: fund.sellingValor ?? null,\n      strategyStatement: fund.strategyStatement ?? null,\n      fundBenchmarks: fund.fundBenchmarks?.map(benchmark => ({\n        benchmark: benchmark.benchmark,\n        percentage: benchmark.percentage\n      })) ?? []\n    };\n  }\n\n  /**\n   * Extracts filename from a URL string; returns null if input is falsy.\n   */\n  private extractFilename(url?: string | null): string | null {\n    if (!url) return null;\n    try {\n      const parsed = new URL(url);\n      const segments = parsed.pathname.split('/');\n      return segments[segments.length - 1] || null;\n    } catch {\n      // If URL parsing fails, fall back to last path segment using string split\n      const parts = url.split('/');\n      return parts[parts.length - 1] || null;\n    }\n  }\n\n  /**\n   * Generates a unique cache key for getFund requests\n   *\n   * @private\n   * @param startDate - Start date (normalized)\n   * @param endDate - End date (normalized)\n   * @param fundCode - Optional fund code\n   * @param fundType - Optional fund type\n   * @returns Cache key string\n   */\n  private generateCacheKey(\n    startDate: string,\n    endDate: string,\n    fundCode?: string,\n    fundType?: FundType\n  ): string {\n    const fundTypeKey = fundType || 'all';\n    const fundCodeKey = fundCode || 'all';\n    return `getFund:${fundTypeKey}:${startDate}:${endDate}:${fundCodeKey}`;\n  }\n\n  /**\n   * Clears all cached fund data\n   *\n   * Use this method to force fresh data retrieval or to free up memory.\n   * Subsequent getFund() calls will fetch data from the API.\n   *\n   * @example\n   * ```typescript\n   * const client = new TefasClient();\n   * await client.getFund('2024-01-01', '2024-01-31'); // Fetches from API\n   * await client.getFund('2024-01-01', '2024-01-31'); // Returns cached data\n   * await client.clearCache();\n   * await client.getFund('2024-01-01', '2024-01-31'); // Fetches from API again\n   * ```\n   */\n  public async clearCache(): Promise<void> {\n    await this.cache.clear();\n  }\n\n  /**\n   * Invalidates cache for a specific fund/date range combination\n   *\n   * @param startDate - Start date in YYYY-MM-DD format or fuzzy date\n   * @param endDate - End date in YYYY-MM-DD format or fuzzy date\n   * @param fundCode - Optional specific fund code\n   * @param fundType - Optional fund type\n   *\n   * @example\n   * ```typescript\n   * const client = new TefasClient();\n   * // Invalidate specific cache entry\n   * await client.invalidateCache('2024-01-01', '2024-01-31', 'TGE', FundType.EMK);\n   * ```\n   */\n  public async invalidateCache(\n    startDate: string,\n    endDate: string,\n    fundCode?: string,\n    fundType?: FundType\n  ): Promise<void> {\n    const { startDate: normalizedStartDate, endDate: normalizedEndDate } =\n      normalizeFuzzyDateRange(startDate, endDate);\n    const cacheKey = this.generateCacheKey(\n      normalizedStartDate,\n      normalizedEndDate,\n      fundCode,\n      fundType\n    );\n    await this.cache.delete(cacheKey);\n  }\n\n  /**\n   * Gets cache statistics\n   *\n   * Returns undefined if the cache adapter doesn't support statistics.\n   *\n   * @returns Cache statistics including size, TTL, and configuration, or undefined\n   *\n   * @example\n   * ```typescript\n   * const client = new TefasClient();\n   * const stats = await client.getCacheStats();\n   * if (stats) {\n   *   console.log(`Cache has ${stats.size} entries, TTL: ${stats.ttl}ms`);\n   * }\n   * ```\n   */\n  public async getCacheStats(): Promise<CacheStats | undefined> {\n    if (isCacheAdapterWithStats(this.cache)) {\n      return await this.cache.getStats();\n    }\n    return undefined;\n  }\n\n  /**\n   * Removes expired entries from the cache\n   *\n   * This method is useful for freeing up memory by removing stale entries.\n   * The cache automatically checks expiration on access, but this method\n   * allows for proactive cleanup.\n   *\n   * Returns undefined if the cache adapter doesn't support manual pruning.\n   *\n   * @returns Number of entries removed, or undefined if not supported\n   *\n   * @example\n   * ```typescript\n   * const client = new TefasClient();\n   * const removed = await client.pruneCache();\n   * if (removed !== undefined) {\n   *   console.log(`Removed ${removed} expired cache entries`);\n   * }\n   * ```\n   */\n  public async pruneCache(): Promise<number | undefined> {\n    if (isCacheAdapterWithStats(this.cache) && this.cache.prune) {\n      return await this.cache.prune();\n    }\n    return undefined;\n  }\n\n  /**\n   * Retries an async operation with random delays between attempts\n   *\n   * @private\n   * @param operation - Async function to retry\n   * @param operationName - Name of the operation for logging\n   * @param maxRetries - Maximum number of retry attempts (default: 3)\n   * @returns Result of the operation\n   * @throws Last error if all retries fail\n   */\n  private async retryWithDelay<T>(\n    operation: () => Promise<T>,\n    operationName: string,\n    maxRetries: number = 3\n  ): Promise<T> {\n    let lastError: Error;\n\n    for (let attempt = 1; attempt <= maxRetries; attempt++) {\n      try {\n        return await operation();\n      } catch (error) {\n        lastError = error as Error;\n\n        if (attempt < maxRetries) {\n          // Random delay between 500ms and 1000ms\n          const delay = Math.floor(Math.random() * 500) + 500;\n          console.error(`ERROR: ${operationName} failed (attempt ${attempt}/${maxRetries}):`, lastError.message);\n          console.log(`Retrying ${operationName} in ${delay}ms...`);\n          await new Promise(resolve => setTimeout(resolve, delay));\n        } else {\n          // Final attempt failed\n          console.error(`ERROR: ${operationName} failed after ${maxRetries} attempts:`, lastError.message);\n        }\n      }\n    }\n\n    throw lastError!;\n  }\n\n  /**\n   * Translates errors to business-focused messages.\n   *\n   * @private\n   * @param error - Error to translate\n   * @returns Business-focused error\n   */\n  private translateError(error: unknown): Error {\n    // If it's already a TEFAS error, return as-is\n    if (error instanceof InvalidFundTypeError ||\n        error instanceof InvalidDateError ||\n        error instanceof DateRangeError ||\n        error instanceof FundNotFoundError ||\n        error instanceof TefasApiError ||\n        error instanceof NetworkError ||\n        error instanceof ConfigurationError ||\n        error instanceof ValidationError) {\n      return error;\n    }\n\n    // Handle network errors (check message first before HTTP status)\n    if (error && typeof error === 'object' && 'message' in error) {\n      const errorMessage = (error as Error).message.toLowerCase();\n      if (errorMessage.includes('fetch') || errorMessage.includes('network') || errorMessage.includes('timeout')) {\n        return new NetworkError('Unable to connect to TEFAS API. Please check your internet connection.');\n      }\n    }\n\n    // Handle HTTP errors from base client\n    if (error && typeof error === 'object' && 'status' in error) {\n      const httpError = error as { status: number; statusText?: string };\n      if (httpError.status === 404) {\n        return new FundNotFoundError('Requested fund data not found');\n      }\n      if (httpError.status >= 500) {\n        return new TefasApiError('TEFAS API is temporarily unavailable. Please try again later.');\n      }\n      if (httpError.status >= 400) {\n        return new TefasApiError('Invalid request. Please check your parameters and try again.');\n      }\n    }\n\n    // Default to generic TEFAS API error\n    return new TefasApiError(\n      error instanceof Error ? error.message : 'An unexpected error occurred',\n      error\n    );\n  }\n}\n\ninterface FundfyCategory {\n  abbreviation: string;\n  fullName: string;\n}\n\ninterface FundfyBenchmark {\n  benchmark: string;\n  percentage: number;\n}\n\ninterface FundfyFund {\n  code: string;\n  title: string;\n  auditFirm?: string | null;\n  emailAddress?: string | null;\n  fundDuration?: string | null;\n  contactAddress?: string | null;\n  founder?: string | null;\n  founderLogo?: string | null;\n  umbrellaFundType?: string | null;\n  umbrellaFundName?: string | null;\n  fundCategory?: FundfyCategory | null;\n  risk?: number | null;\n  yearlyManagementFee?: number | null;\n  buyingValor?: number | null;\n  sellingValor?: number | null;\n  strategyStatement?: string | null;\n  fundBenchmarks?: FundfyBenchmark[] | null;\n}\n\ninterface FundfyDetailResponse {\n  fund?: FundfyFund | null;\n}\n","/**\n * Cloudflare Workers KV cache adapter\n *\n * This adapter allows using Cloudflare Workers KV as a distributed cache\n * backend for the TEFAS API client. Perfect for edge computing scenarios\n * where you want to cache API responses globally.\n *\n * @example\n * ```typescript\n * // In a Cloudflare Worker\n * import { TefasClient, CloudflareKVCacheAdapter } from '@firstthumb/tefas-api';\n *\n * export default {\n *   async fetch(request: Request, env: Env): Promise<Response> {\n *     const cache = new CloudflareKVCacheAdapter({\n *       namespace: env.TEFAS_CACHE,\n *       ttl: 15 * 60 * 1000\n *     });\n *\n *     const client = new TefasClient({ cacheAdapter: cache });\n *     const data = await client.getFund('2024-01-01', '2024-12-31');\n *\n *     return Response.json(data);\n *   }\n * };\n * ```\n */\n\nimport { CacheAdapter } from '../cache-interface';\n\n/**\n * Minimal KV namespace interface\n *\n * This matches the Cloudflare Workers KV API without requiring\n * @cloudflare/workers-types as a dependency. If you have the types\n * package, they're fully compatible.\n */\nexport interface KVNamespace {\n  get(key: string, options?: { type?: 'text' | 'json' | 'arrayBuffer' | 'stream' }): Promise<any>;\n  put(key: string, value: string | ArrayBuffer | ReadableStream, options?: { expirationTtl?: number; expiration?: number }): Promise<void>;\n  delete(key: string): Promise<void>;\n  list(options?: { prefix?: string; limit?: number; cursor?: string }): Promise<{ keys: Array<{ name: string }>; list_complete: boolean; cursor?: string }>;\n}\n\n/**\n * Configuration options for Cloudflare KV cache adapter\n */\nexport interface CloudflareKVCacheOptions {\n  /**\n   * The Cloudflare KV namespace to use for caching\n   *\n   * This is typically bound to your Worker via wrangler.toml:\n   * ```toml\n   * [[kv_namespaces]]\n   * binding = \"TEFAS_CACHE\"\n   * id = \"your-kv-namespace-id\"\n   * ```\n   */\n  namespace: KVNamespace;\n\n  /**\n   * Default time-to-live in milliseconds\n   *\n   * Note: This will be converted to seconds for KV storage.\n   * Cloudflare KV minimum TTL is 60 seconds.\n   *\n   * @default 900000 (15 minutes)\n   */\n  ttl?: number;\n\n  /**\n   * Optional key prefix for namespace isolation\n   *\n   * Useful if sharing a KV namespace across multiple applications\n   * or environments.\n   *\n   * @default \"tefas:\"\n   * @example \"prod:tefas:\" or \"staging:tefas:\"\n   */\n  prefix?: string;\n}\n\n/**\n * Cloudflare Workers KV cache adapter\n *\n * Implements the CacheAdapter interface using Cloudflare Workers KV\n * as the storage backend. Values are automatically serialized to JSON.\n *\n * **Features:**\n * - Globally distributed edge caching\n * - Automatic JSON serialization\n * - TTL support (converted from milliseconds to seconds)\n * - Key prefixing for namespace isolation\n *\n * **Limitations:**\n * - `clear()` is not fully supported (KV has no bulk delete by pattern)\n * - `has()` performs a full `get()` operation (KV has no exists check)\n * - Eventual consistency (updates may take time to propagate globally)\n *\n * @template T - Type of values stored in the cache\n */\nexport class CloudflareKVCacheAdapter<T> implements CacheAdapter<T> {\n  private readonly kv: KVNamespace;\n  private readonly ttl: number;\n  private readonly prefix: string;\n\n  /**\n   * Creates a new Cloudflare KV cache adapter\n   *\n   * @param options - Configuration options including KV namespace\n   */\n  constructor(options: CloudflareKVCacheOptions) {\n    if (!options.namespace) {\n      throw new Error('CloudflareKVCacheAdapter requires a KV namespace');\n    }\n\n    this.kv = options.namespace;\n    this.ttl = options.ttl ?? 900000; // 15 minutes in milliseconds\n    this.prefix = options.prefix ?? 'tefas:';\n  }\n\n  /**\n   * Generates the full key with prefix\n   *\n   * @param key - Cache key\n   * @returns Prefixed key\n   */\n  private getKey(key: string): string {\n    return `${this.prefix}${key}`;\n  }\n\n  /**\n   * Converts TTL from milliseconds to seconds for KV\n   *\n   * Cloudflare KV requires TTL in seconds and has a minimum of 60 seconds.\n   *\n   * @param ttl - TTL in milliseconds\n   * @returns TTL in seconds, minimum 60\n   */\n  private convertTtl(ttl: number): number {\n    const seconds = Math.floor(ttl / 1000);\n    return Math.max(seconds, 60); // KV minimum TTL is 60 seconds\n  }\n\n  /**\n   * Retrieves a value from Cloudflare KV\n   *\n   * @param key - Cache key\n   * @returns Cached value or undefined if not found or expired\n   */\n  async get(key: string): Promise<T | undefined> {\n    try {\n      const value = await this.kv.get(this.getKey(key), { type: 'json' });\n      return value as T | undefined;\n    } catch (error) {\n      console.error(`CloudflareKVCacheAdapter.get() error for key \"${key}\":`, error);\n      return undefined;\n    }\n  }\n\n  /**\n   * Stores a value in Cloudflare KV\n   *\n   * Values are automatically serialized to JSON. TTL is converted from\n   * milliseconds to seconds with a minimum of 60 seconds.\n   *\n   * @param key - Cache key\n   * @param value - Value to cache\n   * @param ttl - Optional time-to-live in milliseconds (overrides default)\n   */\n  async set(key: string, value: T, ttl?: number): Promise<void> {\n    try {\n      const expirationTtl = this.convertTtl(ttl ?? this.ttl);\n      const jsonValue = JSON.stringify(value);\n\n      await this.kv.put(this.getKey(key), jsonValue, {\n        expirationTtl\n      });\n    } catch (error) {\n      console.error(`CloudflareKVCacheAdapter.set() error for key \"${key}\":`, error);\n      throw error;\n    }\n  }\n\n  /**\n   * Removes a specific entry from Cloudflare KV\n   *\n   * @param key - Cache key to delete\n   * @returns true (KV delete doesn't return a boolean, always returns true)\n   */\n  async delete(key: string): Promise<boolean> {\n    try {\n      await this.kv.delete(this.getKey(key));\n      return true;\n    } catch (error) {\n      console.error(`CloudflareKVCacheAdapter.delete() error for key \"${key}\":`, error);\n      return false;\n    }\n  }\n\n  /**\n   * Clears all cache entries with the configured prefix\n   *\n   * **⚠️ Limitation:** Cloudflare KV doesn't support efficient bulk deletion.\n   * This method lists all keys with the prefix and deletes them individually,\n   * which can be slow and may hit rate limits for large caches.\n   *\n   * For production use, consider these alternatives:\n   * 1. Use key versioning (e.g., prefix with \"v1:\", \"v2:\")\n   * 2. Let entries expire naturally via TTL\n   * 3. Manually track keys in a separate list\n   *\n   * @throws Error if listing or deletion fails\n   */\n  async clear(): Promise<void> {\n    console.warn(\n      'CloudflareKVCacheAdapter.clear() is not optimized for KV. ' +\n      'Consider using key versioning or TTL expiration instead.'\n    );\n\n    try {\n      // List all keys with our prefix\n      let cursor: string | undefined;\n      const keysToDelete: string[] = [];\n\n      do {\n        const result = await this.kv.list({\n          prefix: this.prefix,\n          limit: 1000,\n          cursor\n        });\n\n        keysToDelete.push(...result.keys.map((k) => k.name));\n        cursor = result.list_complete ? undefined : result.cursor;\n      } while (cursor);\n\n      // Delete all keys (in batches to avoid rate limits)\n      const deletePromises = keysToDelete.map((key) => this.kv.delete(key));\n      await Promise.all(deletePromises);\n    } catch (error) {\n      console.error('CloudflareKVCacheAdapter.clear() error:', error);\n      throw error;\n    }\n  }\n\n  /**\n   * Checks if a key exists in Cloudflare KV\n   *\n   * **Note:** KV doesn't have a dedicated exists check, so this performs\n   * a full `get()` operation. The value is not returned to save memory.\n   *\n   * @param key - Cache key\n   * @returns true if key exists and is valid\n   */\n  async has(key: string): Promise<boolean> {\n    try {\n      const value = await this.kv.get(this.getKey(key), { type: 'text' });\n      return value !== null;\n    } catch (error) {\n      console.error(`CloudflareKVCacheAdapter.has() error for key \"${key}\":`, error);\n      return false;\n    }\n  }\n}\n","/**\n * Performance Calculator Utilities\n *\n * This module provides utilities for calculating financial performance metrics\n * from fund history data. Uses simple-statistics for mathematical calculations.\n */\n\nimport { standardDeviation, mean } from 'simple-statistics';\nimport {\n  FundHistoryResponse,\n  PerformanceMetrics,\n  MetricsOptions,\n  FundType\n} from '../types';\n\n/**\n * Performance calculator for fund metrics\n *\n * Provides static methods for calculating various financial performance metrics\n * including returns, volatility, and risk-adjusted measures.\n */\nexport class PerformanceCalculator {\n  /**\n   * Calculate performance metrics for multiple funds\n   *\n   * @param fundHistory - Array of fund history data\n   * @param options - Configuration options for calculations\n   * @returns Array of performance metrics for each fund\n   */\n  static calculateMetrics(\n    fundHistory: FundHistoryResponse[],\n    options: MetricsOptions = {}\n  ): PerformanceMetrics[] {\n    const {\n      riskFreeRate = 0,\n      frequency = 'daily',\n      method = 'simple',\n      minObservations = 30\n    } = options;\n\n    // Group data by fund code\n    const fundGroups = this.groupByFund(fundHistory);\n    const periodsPerYear = this.getPeriodsPerYear(frequency);\n\n    const results: PerformanceMetrics[] = [];\n\n    for (const [fundCode, data] of fundGroups) {\n      // Sort by date to ensure chronological order\n      const sortedData = data.sort((a, b) =>\n        new Date(a.date).getTime() - new Date(b.date).getTime()\n      );\n\n      if (sortedData.length < minObservations) {\n        results.push(this.createEmptyMetrics(fundCode, sortedData[0]));\n        continue;\n      }\n\n      const prices = sortedData.map(d => d.price);\n      const returns = this.calculateReturns(prices, method);\n\n      if (returns.length === 0) {\n        results.push(this.createEmptyMetrics(fundCode, sortedData[0]));\n        continue;\n      }\n\n      const cumulativeReturn = this.calculateCumulativeReturn(returns, method);\n      const annualizedReturn = this.calculateAnnualizedReturn(returns, periodsPerYear, method);\n      const annualizedVolatility = this.calculateVolatility(returns, periodsPerYear);\n      const sharpeRatio = this.calculateSharpeRatio(returns, riskFreeRate, periodsPerYear, method);\n\n      results.push({\n        fundCode,\n        fundName: sortedData[0].fundName,\n        fundType: sortedData[0].fundType,\n        observations: prices.length,\n        cumulativeReturn,\n        annualizedReturn,\n        annualizedVolatility,\n        sharpeRatio\n      });\n    }\n\n    return results;\n  }\n\n  /**\n   * Group fund history data by fund code\n   *\n   * @param fundHistory - Array of fund history data\n   * @returns Map of fund code to fund data array\n   */\n  static groupByFund(fundHistory: FundHistoryResponse[]): Map<string, FundHistoryResponse[]> {\n    const groups = new Map<string, FundHistoryResponse[]>();\n\n    for (const data of fundHistory) {\n      const existing = groups.get(data.fundCode) || [];\n      existing.push(data);\n      groups.set(data.fundCode, existing);\n    }\n\n    return groups;\n  }\n\n  /**\n   * Calculate returns from price series\n   *\n   * @param prices - Array of prices in chronological order\n   * @param method - Return calculation method\n   * @returns Array of returns\n   */\n  static calculateReturns(prices: number[], method: 'simple' | 'logarithmic'): number[] {\n    if (prices.length < 2) return [];\n\n    const returns: number[] = [];\n\n    for (let i = 1; i < prices.length; i++) {\n      const currentPrice = prices[i];\n      const previousPrice = prices[i - 1];\n\n      // Skip if previous price is invalid (zero or negative)\n      if (previousPrice <= 0) continue;\n\n      // Skip if current price is invalid (zero or negative)\n      if (currentPrice <= 0) continue;\n\n      if (method === 'logarithmic') {\n        returns.push(Math.log(currentPrice / previousPrice));\n      } else {\n        returns.push((currentPrice / previousPrice) - 1);\n      }\n    }\n\n    return returns;\n  }\n\n  /**\n   * Calculate cumulative return over the period\n   *\n   * @param returns - Array of returns\n   * @param method - Return calculation method\n   * @returns Cumulative return\n   */\n  static calculateCumulativeReturn(returns: number[], method: string): number {\n    if (returns.length === 0) return 0;\n\n    if (method === 'logarithmic') {\n      const sum = returns.reduce((acc, ret) => acc + ret, 0);\n      return Math.exp(sum) - 1;\n    } else {\n      const product = returns.reduce((acc, ret) => acc * (1 + ret), 1);\n      return product - 1;\n    }\n  }\n\n  /**\n   * Calculate annualized return\n   *\n   * @param returns - Array of returns\n   * @param periodsPerYear - Number of periods per year\n   * @param method - Return calculation method\n   * @returns Annualized return\n   */\n  static calculateAnnualizedReturn(\n    returns: number[],\n    periodsPerYear: number,\n    method: string\n  ): number {\n    if (returns.length === 0) return 0;\n\n    const years = returns.length / periodsPerYear;\n    if (years <= 0) return 0;\n\n    if (method === 'logarithmic') {\n      const sum = returns.reduce((acc, ret) => acc + ret, 0);\n      return Math.exp(sum / years) - 1;\n    } else {\n      const product = returns.reduce((acc, ret) => acc * (1 + ret), 1);\n      return Math.pow(product, 1 / years) - 1;\n    }\n  }\n\n  /**\n   * Calculate annualized volatility\n   *\n   * @param returns - Array of returns\n   * @param periodsPerYear - Number of periods per year\n   * @returns Annualized volatility\n   */\n  static calculateVolatility(returns: number[], periodsPerYear: number): number {\n    if (returns.length < 2) return 0;\n\n    const stdDev = standardDeviation(returns);\n    return stdDev * Math.sqrt(periodsPerYear);\n  }\n\n  /**\n   * Calculate Sharpe ratio\n   *\n   * @param returns - Array of returns\n   * @param riskFreeRate - Risk-free rate\n   * @param periodsPerYear - Number of periods per year\n   * @param method - Return calculation method\n   * @returns Sharpe ratio\n   */\n  static calculateSharpeRatio(\n    returns: number[],\n    riskFreeRate: number,\n    periodsPerYear: number,\n    method: string\n  ): number {\n    const annualizedReturn = this.calculateAnnualizedReturn(returns, periodsPerYear, method);\n    const volatility = this.calculateVolatility(returns, periodsPerYear);\n\n    if (volatility === 0) return 0;\n\n    return (annualizedReturn - riskFreeRate) / volatility;\n  }\n\n  /**\n   * Calculate Sortino ratio (uses only downside volatility)\n   *\n   * @param returns - Array of returns\n   * @param riskFreeRate - Risk-free rate\n   * @param periodsPerYear - Number of periods per year\n   * @param method - Return calculation method\n   * @returns Sortino ratio\n   */\n  static calculateSortinoRatio(\n    returns: number[],\n    riskFreeRate: number,\n    periodsPerYear: number,\n    method: string\n  ): number {\n    const annualizedReturn = this.calculateAnnualizedReturn(returns, periodsPerYear, method);\n\n    // Calculate downside deviation (only negative returns)\n    const downsideReturns = returns.filter(ret => ret < 0);\n    if (downsideReturns.length === 0) {\n      // If no negative returns, Sortino is undefined (return high value or Infinity)\n      return annualizedReturn > riskFreeRate ? Infinity : 0;\n    }\n\n    const downsideStdDev = standardDeviation(downsideReturns);\n    const downsideVolatility = downsideStdDev * Math.sqrt(periodsPerYear);\n\n    if (downsideVolatility === 0) return 0;\n\n    return (annualizedReturn - riskFreeRate) / downsideVolatility;\n  }\n\n  /**\n   * Calculate maximum drawdown\n   *\n   * @param prices - Array of prices in chronological order\n   * @returns Maximum drawdown as a percentage (negative value)\n   */\n  static calculateMaximumDrawdown(prices: number[]): number {\n    if (prices.length < 2) return 0;\n\n    let maxDrawdown = 0;\n    let peak = prices[0];\n\n    for (let i = 1; i < prices.length; i++) {\n      if (prices[i] > peak) {\n        peak = prices[i];\n      } else {\n        const drawdown = (prices[i] - peak) / peak;\n        if (drawdown < maxDrawdown) {\n          maxDrawdown = drawdown;\n        }\n      }\n    }\n\n    return maxDrawdown;\n  }\n\n  /**\n   * Calculate Calmar ratio (annualized return / maximum drawdown)\n   *\n   * @param returns - Array of returns\n   * @param prices - Array of prices in chronological order\n   * @param periodsPerYear - Number of periods per year\n   * @param method - Return calculation method\n   * @returns Calmar ratio\n   */\n  static calculateCalmarRatio(\n    returns: number[],\n    prices: number[],\n    periodsPerYear: number,\n    method: string\n  ): number {\n    const annualizedReturn = this.calculateAnnualizedReturn(returns, periodsPerYear, method);\n    const maxDrawdown = Math.abs(this.calculateMaximumDrawdown(prices));\n\n    if (maxDrawdown === 0) return 0;\n\n    return annualizedReturn / maxDrawdown;\n  }\n\n  /**\n   * Calculate Beta (sensitivity to benchmark/market movements)\n   *\n   * @param fundReturns - Array of fund returns\n   * @param benchmarkReturns - Array of benchmark returns (must align with fund returns)\n   * @returns Beta coefficient\n   */\n  static calculateBeta(fundReturns: number[], benchmarkReturns: number[]): number {\n    if (fundReturns.length !== benchmarkReturns.length || fundReturns.length < 2) {\n      return 0;\n    }\n\n    const fundMean = mean(fundReturns);\n    const benchmarkMean = mean(benchmarkReturns);\n\n    // Calculate covariance\n    let covariance = 0;\n    for (let i = 0; i < fundReturns.length; i++) {\n      covariance += (fundReturns[i] - fundMean) * (benchmarkReturns[i] - benchmarkMean);\n    }\n    covariance /= fundReturns.length;\n\n    // Calculate benchmark variance\n    const benchmarkVariance = standardDeviation(benchmarkReturns) ** 2;\n\n    if (benchmarkVariance === 0) return 0;\n\n    return covariance / benchmarkVariance;\n  }\n\n  /**\n   * Calculate Information Ratio (active return / tracking error)\n   *\n   * @param fundReturns - Array of fund returns\n   * @param benchmarkReturns - Array of benchmark returns (must align with fund returns)\n   * @param periodsPerYear - Number of periods per year\n   * @returns Information Ratio\n   */\n  static calculateInformationRatio(\n    fundReturns: number[],\n    benchmarkReturns: number[],\n    periodsPerYear: number\n  ): number {\n    if (fundReturns.length !== benchmarkReturns.length || fundReturns.length < 2) {\n      return 0;\n    }\n\n    // Calculate active returns (excess returns)\n    const activeReturns = fundReturns.map((fundRet, i) => fundRet - benchmarkReturns[i]);\n    const activeReturnMean = mean(activeReturns);\n\n    // Calculate tracking error (standard deviation of active returns)\n    const trackingError = standardDeviation(activeReturns) * Math.sqrt(periodsPerYear);\n\n    if (trackingError === 0) return 0;\n\n    // Annualize active return\n    const annualizedActiveReturn = activeReturnMean * periodsPerYear;\n\n    return annualizedActiveReturn / trackingError;\n  }\n\n  /**\n   * Calculate Treynor Ratio (excess return / beta)\n   *\n   * @param returns - Array of fund returns\n   * @param benchmarkReturns - Array of benchmark returns (must align with fund returns)\n   * @param riskFreeRate - Risk-free rate\n   * @param periodsPerYear - Number of periods per year\n   * @param method - Return calculation method\n   * @returns Treynor Ratio\n   */\n  static calculateTreynorRatio(\n    returns: number[],\n    benchmarkReturns: number[],\n    riskFreeRate: number,\n    periodsPerYear: number,\n    method: string\n  ): number {\n    const annualizedReturn = this.calculateAnnualizedReturn(returns, periodsPerYear, method);\n    const beta = this.calculateBeta(returns, benchmarkReturns);\n\n    if (beta === 0) return 0;\n\n    return (annualizedReturn - riskFreeRate) / beta;\n  }\n\n  /**\n   * Get periods per year for frequency\n   *\n   * @param frequency - Data frequency\n   * @returns Number of periods per year\n   */\n  static getPeriodsPerYear(frequency: string): number {\n    const mapping: Record<string, number> = {\n      daily: 252,\n      weekly: 52,\n      monthly: 12,\n      quarterly: 4,\n      annual: 1\n    };\n\n    return mapping[frequency] || 252;\n  }\n\n  /**\n   * Create empty metrics for insufficient data\n   *\n   * @param fundCode - Fund code\n   * @param sample - Sample data point for fund info\n   * @returns Empty performance metrics\n   */\n  static createEmptyMetrics(fundCode: string, sample: FundHistoryResponse): PerformanceMetrics {\n    return {\n      fundCode,\n      fundName: sample?.fundName || '',\n      fundType: sample?.fundType || FundType.YAT,\n      observations: 0,\n      cumulativeReturn: 0,\n      annualizedReturn: 0,\n      annualizedVolatility: 0,\n      sharpeRatio: 0\n    };\n  }\n}\n"]}