snap performance for weekly returns
This commit is contained in:
parent
efb4d57c23
commit
3f9cb33be0
1 changed files with 46 additions and 1 deletions
|
|
@ -487,15 +487,33 @@ pub fn formatReturn(buf: []u8, value: f64) []const u8 {
|
|||
|
||||
/// Compute 1-week return from candle data: (latest_close / close_7_days_ago) - 1.
|
||||
/// Candles must be sorted by date ascending.
|
||||
///
|
||||
/// Bounded by `max_snap_days`, same as `findNearestCandle`. The backward scan used
|
||||
/// to be unbounded with a comment asserting "at most ~10 steps for daily candles"
|
||||
/// - an assumption about the data, not a constraint on the code. A hole in the
|
||||
/// candle cache therefore turned this into a month's return still labelled "Week",
|
||||
/// silently and with no way to tell from the output. That is exactly the failure
|
||||
/// `max_snap_days` was introduced to prevent ("Beyond this, the data is likely
|
||||
/// missing and the result would be misleading"); this function simply never
|
||||
/// applied it.
|
||||
///
|
||||
/// Real caches do have holes: a snapshot taken 2026-08-29 carried
|
||||
/// `stale_count:num:3`, with three symbols a full day behind their peers.
|
||||
///
|
||||
/// Returns null rather than a wrong number, so the caller renders "--" and the
|
||||
/// gap is visible instead of plausible.
|
||||
pub fn weekReturn(candles: []const Candle) ?f64 {
|
||||
if (candles.len < 2) return null;
|
||||
const latest = candles[candles.len - 1];
|
||||
const target_date = latest.date.addDays(-7);
|
||||
|
||||
// Linear scan backward (at most ~10 steps for daily candles)
|
||||
var i: usize = candles.len - 2;
|
||||
while (true) {
|
||||
if (candles[i].date.days <= target_date.days) {
|
||||
// How far BEFORE the target we landed. A weekend or holiday puts the
|
||||
// nearest bar a few days early, which is normal and fine; a cache hole
|
||||
// puts it weeks early, which is not a week's return.
|
||||
if (target_date.days - candles[i].date.days > max_snap_days) return null;
|
||||
if (candles[i].close == 0) return null;
|
||||
return (latest.close / candles[i].close) - 1.0;
|
||||
}
|
||||
|
|
@ -543,6 +561,33 @@ test "weekReturn snaps to nearest trading day" {
|
|||
try std.testing.expectApproxEqAbs(@as(f64, 0.1667), r, 0.001);
|
||||
}
|
||||
|
||||
test "weekReturn refuses a gap wider than max_snap_days" {
|
||||
// A hole in the candle cache. Latest is Feb 10; 7 days back is Feb 3, but the
|
||||
// previous bar is Jan 1 - forty days earlier. The old unbounded scan happily
|
||||
// returned (105/90 - 1) = +16.67% and labelled it "Week".
|
||||
const holed = [_]Candle{
|
||||
.{ .date = Date.fromYmd(2024, 1, 1), .open = 90, .high = 90, .low = 90, .close = 90, .adj_close = 90, .volume = 0 },
|
||||
.{ .date = Date.fromYmd(2024, 2, 10), .open = 105, .high = 105, .low = 105, .close = 105, .adj_close = 105, .volume = 0 },
|
||||
};
|
||||
try std.testing.expect(weekReturn(&holed) == null);
|
||||
|
||||
// The boundary is inclusive and generous enough for a holiday week: landing
|
||||
// exactly `max_snap_days` before the target still counts. Target is Jan 21 - 7
|
||||
// = Jan 14; Jan 4 is 10 days before that.
|
||||
const at_limit = [_]Candle{
|
||||
.{ .date = Date.fromYmd(2024, 1, 4), .open = 90, .high = 90, .low = 90, .close = 90, .adj_close = 90, .volume = 0 },
|
||||
.{ .date = Date.fromYmd(2024, 1, 21), .open = 99, .high = 99, .low = 99, .close = 99, .adj_close = 99, .volume = 0 },
|
||||
};
|
||||
try std.testing.expectApproxEqAbs(@as(f64, 0.10), weekReturn(&at_limit).?, 0.001);
|
||||
|
||||
// One day past it is refused, so the cutoff is actually enforced rather than
|
||||
// merely present.
|
||||
const past_limit = [_]Candle{
|
||||
.{ .date = Date.fromYmd(2024, 1, 3), .open = 90, .high = 90, .low = 90, .close = 90, .adj_close = 90, .volume = 0 },
|
||||
.{ .date = Date.fromYmd(2024, 1, 21), .open = 99, .high = 99, .low = 99, .close = 99, .adj_close = 99, .volume = 0 },
|
||||
};
|
||||
try std.testing.expect(weekReturn(&past_limit) == null);
|
||||
}
|
||||
test "weekReturn zero close returns null" {
|
||||
const candles = [_]Candle{
|
||||
.{ .date = Date.fromYmd(2024, 1, 1), .open = 0, .high = 0, .low = 0, .close = 0, .adj_close = 0, .volume = 0 },
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue