move isCusipLike to a more generic place
This commit is contained in:
parent
9d86ccf2e5
commit
6d9374ab77
5 changed files with 35 additions and 39 deletions
|
|
@ -1,7 +1,7 @@
|
|||
const std = @import("std");
|
||||
const Candle = @import("../models/candle.zig").Candle;
|
||||
const Date = @import("../models/date.zig").Date;
|
||||
const OpenFigi = @import("../providers/openfigi.zig");
|
||||
const fmt = @import("../format.zig");
|
||||
|
||||
/// Daily return series statistics.
|
||||
pub const RiskMetrics = struct {
|
||||
|
|
@ -164,7 +164,7 @@ pub fn portfolioSummary(
|
|||
total_realized += pos.realized_pnl;
|
||||
|
||||
// For CUSIPs with a note, derive a short display label from the note.
|
||||
const display = if (OpenFigi.isCusipLike(pos.symbol) and pos.note != null)
|
||||
const display = if (fmt.isCusipLike(pos.symbol) and pos.note != null)
|
||||
shortLabel(pos.note.?)
|
||||
else
|
||||
pos.symbol;
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ fn enrichPortfolio(allocator: std.mem.Allocator, av_key: []const u8, file_path:
|
|||
|
||||
for (syms, 0..) |sym, i| {
|
||||
// Skip CUSIPs and known non-stock symbols
|
||||
if (zfin.OpenFigi.isCusipLike(sym)) {
|
||||
if (cli.fmt.isCusipLike(sym)) {
|
||||
// Find the display name for this CUSIP
|
||||
const display: []const u8 = sym;
|
||||
var note: ?[]const u8 = null;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ const zfin = @import("../root.zig");
|
|||
const cli = @import("common.zig");
|
||||
|
||||
pub fn run(allocator: std.mem.Allocator, svc: *zfin.DataService, cusip: []const u8, color: bool, out: *std.Io.Writer) !void {
|
||||
if (!zfin.OpenFigi.isCusipLike(cusip)) {
|
||||
if (!cli.fmt.isCusipLike(cusip)) {
|
||||
try cli.setFg(out, color, cli.CLR_MUTED);
|
||||
try out.print("Note: '{s}' doesn't look like a CUSIP (expected 9 alphanumeric chars with digits)\n", .{cusip});
|
||||
try cli.reset(out, color);
|
||||
|
|
|
|||
|
|
@ -336,6 +336,20 @@ pub fn isMonthlyExpiration(date: Date) bool {
|
|||
return d >= 15 and d <= 21; // 3rd Friday is between 15th and 21st
|
||||
}
|
||||
|
||||
/// Check if a string looks like a CUSIP (9 alphanumeric characters).
|
||||
/// CUSIPs have 6 alphanumeric issuer chars + 2 issue chars + 1 check digit.
|
||||
/// This is a heuristic — it won't catch all CUSIPs and may have false positives.
|
||||
pub fn isCusipLike(s: []const u8) bool {
|
||||
if (s.len != 9) return false;
|
||||
// Must contain at least one digit (all-alpha would be a ticker)
|
||||
var has_digit = false;
|
||||
for (s) |c| {
|
||||
if (!std.ascii.isAlphanumeric(c)) return false;
|
||||
if (std.ascii.isDigit(c)) has_digit = true;
|
||||
}
|
||||
return has_digit;
|
||||
}
|
||||
|
||||
/// Format an options contract line: strike + last + bid + ask + volume + OI + IV.
|
||||
pub fn fmtContractLine(alloc: std.mem.Allocator, prefix: []const u8, c: OptionContract) ![]const u8 {
|
||||
var last_buf: [12]u8 = undefined;
|
||||
|
|
@ -1045,6 +1059,17 @@ test "isMonthlyExpiration" {
|
|||
try std.testing.expect(!isMonthlyExpiration(Date.fromYmd(2024, 1, 17)));
|
||||
}
|
||||
|
||||
test "isCusipLike" {
|
||||
try std.testing.expect(isCusipLike("02315N600")); // Vanguard Target 2035
|
||||
try std.testing.expect(isCusipLike("02315N709")); // Vanguard Target 2040
|
||||
try std.testing.expect(isCusipLike("459200101")); // IBM
|
||||
try std.testing.expect(isCusipLike("06051XJ45")); // CD CUSIP
|
||||
try std.testing.expect(!isCusipLike("AAPL")); // Too short
|
||||
try std.testing.expect(!isCusipLike("ABCDEFGHI")); // No digits
|
||||
try std.testing.expect(isCusipLike("NON40OR52")); // Looks cusip-like (has digits, 9 chars)
|
||||
try std.testing.expect(!isCusipLike("12345")); // Too short
|
||||
}
|
||||
|
||||
test "lotSortFn" {
|
||||
const open_new = Lot{
|
||||
.symbol = "A",
|
||||
|
|
|
|||
|
|
@ -109,11 +109,9 @@ pub fn lookupCusips(
|
|||
var argv_list: std.ArrayList([]const u8) = .empty;
|
||||
defer argv_list.deinit(allocator);
|
||||
try argv_list.appendSlice(allocator, &.{
|
||||
"curl", "-sS", "--max-time", "30",
|
||||
"-X", "POST",
|
||||
"-H", "Content-Type: application/json",
|
||||
"-H", hdr,
|
||||
"-d", body_str,
|
||||
"curl", "-sS", "--max-time", "30",
|
||||
"-X", "POST", "-H", "Content-Type: application/json",
|
||||
"-H", hdr, "-d", body_str,
|
||||
api_url,
|
||||
});
|
||||
|
||||
|
|
@ -138,11 +136,9 @@ pub fn lookupCusips(
|
|||
var argv_list: std.ArrayList([]const u8) = .empty;
|
||||
defer argv_list.deinit(allocator);
|
||||
try argv_list.appendSlice(allocator, &.{
|
||||
"curl", "-sS", "--max-time", "30",
|
||||
"-X", "POST",
|
||||
"-H", "Content-Type: application/json",
|
||||
"-d", body_str,
|
||||
api_url,
|
||||
"curl", "-sS", "--max-time", "30",
|
||||
"-X", "POST", "-H", "Content-Type: application/json",
|
||||
"-d", body_str, api_url,
|
||||
});
|
||||
|
||||
const result = std.process.Child.run(.{
|
||||
|
|
@ -244,28 +240,3 @@ fn parseResponse(allocator: std.mem.Allocator, body: []const u8, expected_count:
|
|||
|
||||
return results;
|
||||
}
|
||||
|
||||
/// Check if a string looks like a CUSIP (9 alphanumeric characters).
|
||||
/// CUSIPs have 6 alphanumeric issuer chars + 2 issue chars + 1 check digit.
|
||||
/// This is a heuristic — it won't catch all CUSIPs and may have false positives.
|
||||
pub fn isCusipLike(s: []const u8) bool {
|
||||
if (s.len != 9) return false;
|
||||
// Must contain at least one digit (all-alpha would be a ticker)
|
||||
var has_digit = false;
|
||||
for (s) |c| {
|
||||
if (!std.ascii.isAlphanumeric(c)) return false;
|
||||
if (std.ascii.isDigit(c)) has_digit = true;
|
||||
}
|
||||
return has_digit;
|
||||
}
|
||||
|
||||
test "isCusipLike" {
|
||||
try std.testing.expect(isCusipLike("02315N600")); // Vanguard Target 2035
|
||||
try std.testing.expect(isCusipLike("02315N709")); // Vanguard Target 2040
|
||||
try std.testing.expect(isCusipLike("459200101")); // IBM
|
||||
try std.testing.expect(isCusipLike("06051XJ45")); // CD CUSIP
|
||||
try std.testing.expect(!isCusipLike("AAPL")); // Too short
|
||||
try std.testing.expect(!isCusipLike("ABCDEFGHI")); // No digits
|
||||
try std.testing.expect(isCusipLike("NON40OR52")); // Looks cusip-like (has digits, 9 chars)
|
||||
try std.testing.expect(!isCusipLike("12345")); // Too short
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue