From 497b1d3c2d5084e9eb318f4bfe015789734d2d9f Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Fri, 6 Mar 2026 16:02:12 -0800 Subject: [PATCH] remove unused polygon candle fetching --- src/providers/polygon.zig | 120 +------------------------------------- 1 file changed, 2 insertions(+), 118 deletions(-) diff --git a/src/providers/polygon.zig b/src/providers/polygon.zig index 3b6a578..37d36df 100644 --- a/src/providers/polygon.zig +++ b/src/providers/polygon.zig @@ -1,15 +1,13 @@ -//! Polygon.io API provider -- primary source for dividend/split reference data -//! and secondary source for daily OHLCV bars. +//! Polygon.io API provider -- primary source for dividend/split reference data. //! API docs: https://polygon.io/docs //! -//! Free tier: 5 requests/min, unlimited daily, 2yr historical bars. +//! Free tier: 5 requests/min, unlimited daily. //! Dividends and splits are available for all history. const std = @import("std"); const http = @import("../net/http.zig"); const RateLimiter = @import("../net/RateLimiter.zig"); const Date = @import("../models/date.zig").Date; -const Candle = @import("../models/candle.zig").Candle; const Dividend = @import("../models/dividend.zig").Dividend; const DividendType = @import("../models/dividend.zig").DividendType; const Split = @import("../models/split.zig").Split; @@ -135,39 +133,6 @@ pub const Polygon = struct { return parseSplitsResponse(allocator, response.body); } - - /// Fetch daily OHLCV bars. Polygon free tier: 2 years max history. - /// Polygon endpoint: GET /v2/aggs/ticker/{ticker}/range/1/day/{from}/{to} - pub fn fetchCandles( - self: *Polygon, - allocator: std.mem.Allocator, - symbol: []const u8, - from: Date, - to: Date, - ) ![]Candle { - self.rate_limiter.acquire(); - - var from_buf: [10]u8 = undefined; - var to_buf: [10]u8 = undefined; - const from_str = from.format(&from_buf); - const to_str = to.format(&to_buf); - - // Build URL manually since the path contains the date range - const path = try std.fmt.allocPrint( - allocator, - "{s}/v2/aggs/ticker/{s}/range/1/day/{s}/{s}?adjusted=true&sort=asc&limit=5000", - .{ base_url, symbol, from_str, to_str }, - ); - defer allocator.free(path); - - const authed = try appendApiKey(allocator, path, self.api_key); - defer allocator.free(authed); - - var response = try self.client.get(authed); - defer response.deinit(); - - return parseCandlesResponse(allocator, response.body); - } }; // -- JSON parsing -- @@ -290,65 +255,6 @@ fn parseSplitsResponse(allocator: std.mem.Allocator, body: []const u8) ![]Split return try splits.toOwnedSlice(allocator); } -fn parseCandlesResponse(allocator: std.mem.Allocator, body: []const u8) ![]Candle { - const parsed = std.json.parseFromSlice(std.json.Value, allocator, body, .{}) catch - return error.ParseError; - defer parsed.deinit(); - - const root = parsed.value.object; - - if (root.get("status")) |s| { - if (s == .string and std.mem.eql(u8, s.string, "ERROR")) - return error.RequestFailed; - } - - const results = root.get("results") orelse { - return try allocator.alloc(Candle, 0); - }; - const items = switch (results) { - .array => |a| a.items, - else => { - return try allocator.alloc(Candle, 0); - }, - }; - - var candles: std.ArrayList(Candle) = .empty; - errdefer candles.deinit(allocator); - - for (items) |item| { - const obj = switch (item) { - .object => |o| o, - else => continue, - }; - - // Polygon returns timestamp in milliseconds - const ts_ms = blk: { - const v = obj.get("t") orelse continue; - break :blk switch (v) { - .integer => |i| i, - .float => |f| @as(i64, @intFromFloat(f)), - else => continue, - }; - }; - const days: i32 = @intCast(@divFloor(ts_ms, 86400 * 1000)); - const date = Date{ .days = days }; - - const close = parseJsonFloat(obj.get("c")); - - try candles.append(allocator, .{ - .date = date, - .open = parseJsonFloat(obj.get("o")), - .high = parseJsonFloat(obj.get("h")), - .low = parseJsonFloat(obj.get("l")), - .close = close, - .adj_close = close, // Polygon adjusted=true gives adjusted values - .volume = @intFromFloat(parseJsonFloat(obj.get("v"))), - }); - } - - return try candles.toOwnedSlice(allocator); -} - // -- Helpers -- fn appendApiKey(allocator: std.mem.Allocator, url: []const u8, api_key: []const u8) ![]const u8 { @@ -506,25 +412,3 @@ test "parseSplitsResponse empty results" { try std.testing.expectEqual(@as(usize, 0), splits.len); } - -test "parseCandlesResponse basic" { - const body = - \\{ - \\ "status": "OK", - \\ "results": [ - \\ {"t": 1704067200000, "o": 185.5, "h": 186.2, "l": 184.1, "c": 185.8, "v": 52000000} - \\ ] - \\} - ; - - const allocator = std.testing.allocator; - const candles = try parseCandlesResponse(allocator, body); - defer allocator.free(candles); - - try std.testing.expectEqual(@as(usize, 1), candles.len); - try std.testing.expectApproxEqAbs(@as(f64, 185.5), candles[0].open, 0.01); - try std.testing.expectApproxEqAbs(@as(f64, 186.2), candles[0].high, 0.01); - try std.testing.expectApproxEqAbs(@as(f64, 185.8), candles[0].close, 0.01); - try std.testing.expectApproxEqAbs(candles[0].close, candles[0].adj_close, 0.01); - try std.testing.expectEqual(@as(u64, 52000000), candles[0].volume); -}