| 50 | return self.run_with_cache(endpoint, params_str, verbose) |
| 51 | |
| 52 | def run_with_cache(self, endpoint: str, params_str: str, verbose: bool = False): |
| 53 | params = json.loads(params_str) |
| 54 | |
| 55 | # 创建缓存文件路径 |
| 56 | endpoint_clean = endpoint.replace('/', '_').lstrip('_') # 清理endpoint路径 |
| 57 | cache_key = f"{endpoint_clean}_{hashlib.md5(params_str.encode()).hexdigest()}" |
| 58 | endpoint_cache_dir = self.cache_dir / endpoint_clean |
| 59 | if not endpoint_cache_dir.exists(): |
| 60 | endpoint_cache_dir.mkdir(parents=True, exist_ok=True) |
| 61 | cache_file = endpoint_cache_dir / f"{cache_key}.pkl" |
| 62 | |
| 63 | # 尝试从缓存加载 |
| 64 | if cache_file.exists(): |
| 65 | if verbose: |
| 66 | print(f"📁 从缓存加载: {cache_file}") |
| 67 | with open(cache_file, "rb") as f: |
| 68 | return pickle.load(f) |
| 69 | else: |
| 70 | if verbose: |
| 71 | print(f"🌐 API请求: {endpoint} 参数: {params}") |
| 72 | |
| 73 | # 限制API请求频率 |
| 74 | time.sleep(self.rate_limit_delay) |
| 75 | |
| 76 | try: |
| 77 | # 构建完整URL |
| 78 | url = f"{self.base_url}{endpoint}" |
| 79 | params['apikey'] = self.api_key |
| 80 | |
| 81 | # 发送请求 |
| 82 | response = requests.get(url, params=params) |
| 83 | response.raise_for_status() |
| 84 | result = response.json() |
| 85 | |
| 86 | # 保存到缓存 |
| 87 | if verbose: |
| 88 | print(f"💾 保存缓存: {cache_file}") |
| 89 | with open(cache_file, "wb") as f: |
| 90 | pickle.dump(result, f) |
| 91 | |
| 92 | return result |
| 93 | except Exception as e: |
| 94 | if verbose: |
| 95 | print(f"❌ API请求失败: {e}") |
| 96 | raise e |
| 97 | |
| 98 | def get_historical_price(self, symbol: str, from_date: str = None, to_date: str = None, |
| 99 | adjusted: bool = True, adj_base_date: str = None, verbose: bool = False): |