gloomberb 0.12.0 → 0.13.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/package.json +1 -2
- package/src/api-client/auth.ts +2 -1
- package/src/api-client/index.test.ts +34 -0
- package/src/api-client/index.ts +10 -2
- package/src/api-client/request.ts +4 -1
- package/src/api-client/research-activity.ts +61 -0
- package/src/api-client/types.ts +2 -1
- package/src/cli/commands/plugins.ts +4 -3
- package/src/cli/desktop-pane-shot.ts +79 -10
- package/src/cli/pane-functions/capabilities.ts +76 -23
- package/src/cli/pane-functions/cloud-session.test.ts +54 -0
- package/src/cli/pane-functions/cloud-session.ts +38 -0
- package/src/cli/pane-functions/dom.test.ts +41 -0
- package/src/cli/pane-functions/dom.ts +199 -0
- package/src/cli/pane-functions/index.test.ts +13 -2
- package/src/cli/pane-functions/index.ts +9 -5
- package/src/cli/pane-functions/intraday-shot.test.ts +307 -0
- package/src/cli/pane-functions/intraday-shot.ts +334 -0
- package/src/cli/pane-functions/report.test.ts +29 -1
- package/src/cli/pane-functions/report.ts +68 -7
- package/src/cli/pane-functions/screenshot.ts +195 -42
- package/src/cli/restore-plugins.ts +10 -3
- package/src/components/chart/composite/composite-chart.test.tsx +68 -1
- package/src/components/chart/composite/composite-chart.tsx +87 -21
- package/src/components/chart/composite/rasterizer.ts +33 -0
- package/src/components/chart/composite/renderers.test.ts +51 -4
- package/src/components/chart/composite/scene.test.ts +128 -2
- package/src/components/chart/composite/scene.ts +92 -16
- package/src/components/chart/composite/text-renderer.ts +13 -0
- package/src/components/chart/composite/time-scale.ts +30 -10
- package/src/components/chart/composite/types.ts +26 -0
- package/src/components/command-bar/assist/runtime.ts +38 -30
- package/src/components/command-bar/surface/assist.test.tsx +63 -9
- package/src/components/command-bar/surface/test-harness.tsx +23 -4
- package/src/components/onboarding/account-step/chooser-panel.tsx +2 -2
- package/src/components/onboarding/account-step/qr-panel.tsx +2 -10
- package/src/components/onboarding/onboarding-wizard.test.tsx +38 -12
- package/src/components/onboarding/onboarding-wizard.tsx +49 -14
- package/src/data/config/store/index.test.ts +23 -0
- package/src/data/config/store/node.ts +7 -1
- package/src/data/config/store/normalize.ts +4 -0
- package/src/news/types.ts +1 -0
- package/src/plugins/builtin/cds/client.test.ts +20 -0
- package/src/plugins/builtin/cds/client.ts +13 -3
- package/src/plugins/builtin/cds/headless.test.ts +30 -0
- package/src/plugins/builtin/cds/headless.ts +113 -0
- package/src/plugins/builtin/cds/index.tsx +4 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +4 -1
- package/src/plugins/builtin/chart-composer/index.tsx +1 -1
- package/src/plugins/builtin/chat/controller/channels.ts +2 -1
- package/src/plugins/builtin/chat/controller/index.ts +13 -5
- package/src/plugins/builtin/chat/controller/realtime.ts +24 -0
- package/src/plugins/builtin/chat/controller/session-runtime.ts +76 -8
- package/src/plugins/builtin/chat/controller/state.ts +1 -0
- package/src/plugins/builtin/chat/controller.test.ts +260 -4
- package/src/plugins/builtin/cloud/askg/executor.test.ts +160 -0
- package/src/plugins/builtin/cloud/askg/executor.ts +429 -0
- package/src/plugins/builtin/cloud/askg/manifest.test.ts +153 -0
- package/src/plugins/builtin/cloud/askg/manifest.ts +314 -0
- package/src/plugins/builtin/cloud/askg/protocol.test.ts +190 -0
- package/src/plugins/builtin/cloud/askg/protocol.ts +286 -0
- package/src/plugins/builtin/cloud/askg/undo.test.ts +44 -0
- package/src/plugins/builtin/cloud/askg/undo.ts +277 -0
- package/src/plugins/builtin/cloud/askg/verify.ts +130 -0
- package/src/plugins/builtin/cloud/auth-dialog.tsx +7 -1
- package/src/plugins/builtin/cloud/device-signin-dialog.tsx +21 -8
- package/src/plugins/builtin/cloud/plugin.tsx +2 -0
- package/src/plugins/builtin/cloud/verification-panel.tsx +56 -0
- package/src/plugins/builtin/congress-trades/client.ts +11 -0
- package/src/plugins/builtin/congress-trades/detail.tsx +3 -3
- package/src/plugins/builtin/congress-trades/headless.test.ts +98 -0
- package/src/plugins/builtin/congress-trades/headless.ts +145 -0
- package/src/plugins/builtin/congress-trades/pane.tsx +3 -3
- package/src/plugins/builtin/credit-conditions/client.ts +1 -1
- package/src/plugins/builtin/credit-conditions/headless.test.ts +31 -0
- package/src/plugins/builtin/credit-conditions/headless.ts +55 -0
- package/src/plugins/builtin/credit-conditions/index.tsx +4 -0
- package/src/plugins/builtin/dividend-yield/headless.test.ts +88 -0
- package/src/plugins/builtin/dividend-yield/headless.ts +135 -0
- package/src/plugins/builtin/dividend-yield/index.tsx +12 -8
- package/src/plugins/builtin/dividend-yield/model.ts +4 -22
- package/src/plugins/builtin/dividend-yield/view.ts +17 -0
- package/src/plugins/builtin/earnings/headless.test.ts +82 -0
- package/src/plugins/builtin/earnings/headless.ts +162 -0
- package/src/plugins/builtin/earnings/index.tsx +2 -0
- package/src/plugins/builtin/earnings-calls/data.ts +29 -8
- package/src/plugins/builtin/earnings-calls/headless.test.ts +192 -0
- package/src/plugins/builtin/earnings-calls/headless.ts +300 -0
- package/src/plugins/builtin/earnings-calls/index.tsx +19 -9
- package/src/plugins/builtin/earnings-calls/model.ts +238 -0
- package/src/plugins/builtin/earnings-calls/pane.tsx +4 -0
- package/src/plugins/builtin/earnings-calls/transcript-view.tsx +13 -11
- package/src/plugins/builtin/fear-greed/headless.test.ts +53 -0
- package/src/plugins/builtin/fear-greed/headless.ts +84 -0
- package/src/plugins/builtin/fear-greed/index.tsx +4 -0
- package/src/plugins/builtin/holders/client.ts +29 -0
- package/src/plugins/builtin/holders/headless.test.ts +58 -0
- package/src/plugins/builtin/holders/headless.ts +162 -0
- package/src/plugins/builtin/holders/index.tsx +14 -8
- package/src/plugins/builtin/holders/pane.tsx +7 -1
- package/src/plugins/builtin/insider/client.ts +50 -0
- package/src/plugins/builtin/insider/headless.test.ts +122 -0
- package/src/plugins/builtin/insider/headless.ts +117 -0
- package/src/plugins/builtin/insider/index.tsx +21 -50
- package/src/plugins/builtin/insider/insider-data.ts +1 -1
- package/src/plugins/builtin/insider/model.ts +72 -0
- package/src/plugins/builtin/ipo-calendar/client.ts +15 -0
- package/src/plugins/builtin/ipo-calendar/headless.test.ts +89 -0
- package/src/plugins/builtin/ipo-calendar/headless.ts +127 -0
- package/src/plugins/builtin/ipo-calendar/index.tsx +2 -0
- package/src/plugins/builtin/ipo-calendar/model.ts +3 -15
- package/src/plugins/builtin/market-movers/client.ts +76 -0
- package/src/plugins/builtin/market-movers/headless.test.ts +34 -0
- package/src/plugins/builtin/market-movers/headless.ts +73 -0
- package/src/plugins/builtin/market-movers/index.tsx +11 -51
- package/src/plugins/builtin/news/headless.test.ts +138 -0
- package/src/plugins/builtin/news/headless.ts +190 -0
- package/src/plugins/builtin/news/index.tsx +13 -9
- package/src/plugins/builtin/news/wire/index.tsx +2 -1
- package/src/plugins/builtin/research/analyst-headless.test.ts +60 -0
- package/src/plugins/builtin/research/analyst-headless.ts +172 -0
- package/src/plugins/builtin/research/analyst-model.ts +219 -0
- package/src/plugins/builtin/research/analyst-pane.tsx +31 -221
- package/src/plugins/builtin/research/client.ts +12 -0
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -237
- package/src/plugins/builtin/research/event-model.ts +245 -0
- package/src/plugins/builtin/research/events-headless.test.ts +32 -0
- package/src/plugins/builtin/research/events-headless.ts +87 -0
- package/src/plugins/builtin/research/headless.test.ts +93 -0
- package/src/plugins/builtin/research/headless.ts +161 -0
- package/src/plugins/builtin/research/index.tsx +32 -18
- package/src/plugins/builtin/research-search/pane.tsx +4 -0
- package/src/plugins/builtin/scanner/headless.test.ts +59 -0
- package/src/plugins/builtin/scanner/headless.ts +125 -0
- package/src/plugins/builtin/scanner/index.ts +4 -0
- package/src/plugins/builtin/sec/client.ts +16 -0
- package/src/plugins/builtin/sec/headless.test.ts +72 -0
- package/src/plugins/builtin/sec/headless.ts +69 -0
- package/src/plugins/builtin/sec/index.tsx +20 -75
- package/src/plugins/builtin/sec/model.ts +89 -0
- package/src/plugins/builtin/sectors/client.ts +54 -0
- package/src/plugins/builtin/sectors/headless.test.ts +28 -0
- package/src/plugins/builtin/sectors/headless.ts +80 -0
- package/src/plugins/builtin/sectors/index.tsx +6 -51
- package/src/plugins/builtin/shared/cloud-upgrade.ts +8 -4
- package/src/plugins/builtin/short-interest/client.ts +5 -2
- package/src/plugins/builtin/short-interest/headless.test.ts +96 -0
- package/src/plugins/builtin/short-interest/headless.ts +111 -0
- package/src/plugins/builtin/short-interest/index.tsx +15 -9
- package/src/plugins/builtin/thirteenf/data.ts +13 -11
- package/src/plugins/builtin/thirteenf/headless.test.ts +112 -0
- package/src/plugins/builtin/thirteenf/headless.ts +286 -0
- package/src/plugins/builtin/thirteenf/index.tsx +4 -0
- package/src/plugins/builtin/thirteenf/model.ts +13 -0
- package/src/plugins/builtin/thirteenf/table.ts +3 -15
- package/src/plugins/builtin/ticker-detail/pane.tsx +14 -0
- package/src/plugins/builtin/treasury-auctions/headless.test.ts +34 -0
- package/src/plugins/builtin/treasury-auctions/headless.ts +95 -0
- package/src/plugins/builtin/treasury-auctions/index.tsx +4 -0
- package/src/plugins/builtin/volatility/client.ts +16 -3
- package/src/plugins/builtin/volatility/headless.test.ts +35 -0
- package/src/plugins/builtin/volatility/headless.ts +75 -0
- package/src/plugins/builtin/volatility/index.tsx +4 -0
- package/src/plugins/builtin/world-indices/client.ts +43 -0
- package/src/plugins/builtin/world-indices/headless.test.ts +32 -0
- package/src/plugins/builtin/world-indices/headless.ts +109 -0
- package/src/plugins/builtin/world-indices/index.tsx +4 -0
- package/src/plugins/builtin/yield-curve/headless.test.ts +24 -0
- package/src/plugins/builtin/yield-curve/headless.ts +67 -0
- package/src/plugins/builtin/yield-curve/index.tsx +6 -8
- package/src/plugins/builtin/yield-curve/treasury-data.ts +16 -6
- package/src/plugins/bundle.test.ts +58 -1
- package/src/plugins/bundle.ts +4 -2
- package/src/plugins/catalog-backend.test.ts +26 -0
- package/src/plugins/catalog-backend.ts +9 -2
- package/src/plugins/catalog-browser.test.ts +0 -13
- package/src/plugins/catalog-browser.ts +4 -0
- package/src/plugins/catalog-manifest.test.ts +15 -0
- package/src/plugins/catalog-manifest.ts +12 -5
- package/src/plugins/catalog-ui.ts +0 -6
- package/src/plugins/host-contract.ts +9 -0
- package/src/plugins/host-modules.ts +12 -0
- package/src/plugins/loader.ts +63 -15
- package/src/plugins/prediction-markets/headless.test.ts +119 -0
- package/src/plugins/prediction-markets/headless.ts +255 -0
- package/src/plugins/prediction-markets/index.tsx +2 -0
- package/src/plugins/seed.ts +3 -0
- package/src/public/broker.ts +6 -0
- package/src/remote/schema.test.ts +65 -0
- package/src/remote/schema.ts +322 -39
- package/src/remote/types.ts +49 -2
- package/src/renderers/browser/app-services.ts +3 -0
- package/src/renderers/browser/config-host.ts +24 -8
- package/src/renderers/browser/deeplink-bridge.test.ts +11 -0
- package/src/renderers/browser/deeplink-bridge.ts +15 -3
- package/src/renderers/browser/main.tsx +3 -1
- package/src/renderers/browser/research-entry.test.ts +8 -0
- package/src/renderers/browser/research-entry.ts +7 -0
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +2 -2
- package/src/renderers/electrobun/bun/desktop/initialization.ts +10 -3
- package/src/renderers/electrobun/bun/external-plugins.ts +20 -4
- package/src/renderers/electrobun/view/build-assets.ts +0 -2
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +63 -10
- package/src/sources/gloomberb-cloud/news.ts +1 -0
- package/src/sync/core-contributors.test.ts +111 -0
- package/src/sync/core-contributors.ts +155 -1
- package/src/time-series/resolve.test.ts +118 -1
- package/src/time-series/resolve.ts +14 -16
- package/src/types/config.ts +2 -0
- package/src/plugins/broker-sync/broker-sync.test.ts +0 -188
- package/src/plugins/broker-sync/native-loader.ts +0 -10
- package/src/plugins/broker-sync/normalize.ts +0 -308
- package/src/plugins/broker-sync/public.ts +0 -122
- package/src/plugins/broker-sync/robinhood-native.ts +0 -367
- package/src/plugins/broker-sync/robinhood.ts +0 -114
- package/src/plugins/broker-sync/simplefin-native.ts +0 -201
- package/src/plugins/broker-sync/simplefin.ts +0 -70
- package/src/renderers/electrobun/view/native-stubs/broker-sync-native-loader.ts +0 -7
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
HeadlessPaneColumn,
|
|
3
|
+
HeadlessPaneContext,
|
|
4
|
+
HeadlessPaneDefinition,
|
|
5
|
+
HeadlessPaneLoadArgs,
|
|
6
|
+
} from "../../../types/plugin";
|
|
7
|
+
import { formatCompact, formatNumber } from "../../../utils/format";
|
|
8
|
+
import { fetchShortInterest } from "./client";
|
|
9
|
+
import {
|
|
10
|
+
buildRows,
|
|
11
|
+
sortRows,
|
|
12
|
+
type SortPreference,
|
|
13
|
+
} from "./model";
|
|
14
|
+
import type { ShortInterestRecord } from "./types";
|
|
15
|
+
|
|
16
|
+
const SHORT_INTEREST_COLUMNS: HeadlessPaneColumn[] = [
|
|
17
|
+
{ key: "settlementDate", header: "Date" },
|
|
18
|
+
{
|
|
19
|
+
key: "sharesShort",
|
|
20
|
+
header: "Shares Short",
|
|
21
|
+
align: "right",
|
|
22
|
+
format: (value) => formatCompact(Number(value)),
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
key: "daysToCover",
|
|
26
|
+
header: "Days to Cover",
|
|
27
|
+
align: "right",
|
|
28
|
+
format: (value) => value == null ? "-" : formatNumber(Number(value), 2),
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
key: "averageDailyVolume",
|
|
32
|
+
header: "Avg Daily Vol",
|
|
33
|
+
align: "right",
|
|
34
|
+
format: (value) => value == null ? "-" : formatCompact(Number(value)),
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
key: "shortPercentFloat",
|
|
38
|
+
header: "% Float",
|
|
39
|
+
align: "right",
|
|
40
|
+
format: (value) => value == null ? "-" : `${formatNumber(Number(value), 2)}%`,
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
export interface ShortInterestHeadlessDependencies {
|
|
45
|
+
loadRecords(
|
|
46
|
+
symbol: string,
|
|
47
|
+
args: HeadlessPaneLoadArgs,
|
|
48
|
+
ctx: HeadlessPaneContext,
|
|
49
|
+
): Promise<ShortInterestRecord[]>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const defaultDependencies: ShortInterestHeadlessDependencies = {
|
|
53
|
+
loadRecords: (symbol, _args, ctx) => fetchShortInterest(symbol, ctx.apiClient),
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export function createShortInterestHeadless(
|
|
57
|
+
dependencies: ShortInterestHeadlessDependencies = defaultDependencies,
|
|
58
|
+
): HeadlessPaneDefinition<"rows"> {
|
|
59
|
+
return {
|
|
60
|
+
shape: "rows",
|
|
61
|
+
argument: {
|
|
62
|
+
kind: "ticker",
|
|
63
|
+
placeholder: "ticker",
|
|
64
|
+
description: "US equity ticker.",
|
|
65
|
+
},
|
|
66
|
+
options: [
|
|
67
|
+
{
|
|
68
|
+
key: "order",
|
|
69
|
+
description: "Settlement date order.",
|
|
70
|
+
type: "enum",
|
|
71
|
+
values: [{ value: "newest" }, { value: "oldest" }],
|
|
72
|
+
defaultValue: "newest",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
key: "limit",
|
|
76
|
+
aliases: ["count", "rows"],
|
|
77
|
+
description: "Maximum settlement rows.",
|
|
78
|
+
type: "integer",
|
|
79
|
+
defaultValue: 24,
|
|
80
|
+
minimum: 1,
|
|
81
|
+
maximum: 100,
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
columns: SHORT_INTEREST_COLUMNS,
|
|
85
|
+
describe: (args) => `Short Interest | ${args.symbols[0]}`,
|
|
86
|
+
async load(args, ctx) {
|
|
87
|
+
const symbol = args.symbols[0]!;
|
|
88
|
+
const records = await dependencies.loadRecords(symbol, args, ctx);
|
|
89
|
+
const preference: SortPreference = {
|
|
90
|
+
columnId: "settlementDate",
|
|
91
|
+
direction: args.options.order === "oldest" ? "asc" : "desc",
|
|
92
|
+
};
|
|
93
|
+
const limit = Number(args.options.limit);
|
|
94
|
+
const rows = sortRows(buildRows(records), preference)
|
|
95
|
+
.slice(0, limit)
|
|
96
|
+
.map((row) => ({
|
|
97
|
+
settlementDate: row.settlementDate,
|
|
98
|
+
sharesShort: row.record.sharesShort,
|
|
99
|
+
daysToCover: row.record.shortRatio,
|
|
100
|
+
averageDailyVolume: row.record.averageDailyVolume,
|
|
101
|
+
shortPercentFloat: row.record.shortPercentFloat,
|
|
102
|
+
}));
|
|
103
|
+
return {
|
|
104
|
+
rows,
|
|
105
|
+
metadata: { symbol, order: args.options.order },
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export const shortInterestHeadless = createShortInterestHeadless();
|
|
@@ -5,8 +5,11 @@ import {
|
|
|
5
5
|
resetShortInterestHealth,
|
|
6
6
|
YAHOO_SHORT_INTEREST_CONNECTION_ID,
|
|
7
7
|
} from "./client";
|
|
8
|
+
import { shortInterestHeadless } from "./headless";
|
|
8
9
|
import { ShortInterestView } from "./pane";
|
|
9
10
|
|
|
11
|
+
export { shortInterestHeadless } from "./headless";
|
|
12
|
+
|
|
10
13
|
let disposeConnection: (() => void) | null = null;
|
|
11
14
|
|
|
12
15
|
export const shortInterestModule: PluginModule = {
|
|
@@ -50,14 +53,17 @@ export const shortInterestModule: PluginModule = {
|
|
|
50
53
|
],
|
|
51
54
|
|
|
52
55
|
paneTemplates: [
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
{
|
|
57
|
+
...createTickerSurfacePaneTemplate({
|
|
58
|
+
id: "short-interest-pane",
|
|
59
|
+
paneId: "short-interest",
|
|
60
|
+
label: "Short Interest",
|
|
61
|
+
// Yahoo's key-statistics module only carries the current and prior settlement dates.
|
|
62
|
+
description: "Bi-monthly short interest settlements from FINRA with days to cover and average daily volume.",
|
|
63
|
+
keywords: ["short", "interest", "si", "shorts", "borrow", "days", "cover"],
|
|
64
|
+
shortcut: "SI",
|
|
65
|
+
}),
|
|
66
|
+
headless: shortInterestHeadless,
|
|
67
|
+
},
|
|
62
68
|
],
|
|
63
69
|
};
|
|
@@ -45,35 +45,37 @@ export async function loadBrowserRows(
|
|
|
45
45
|
tab: ThirteenFBrowserTab,
|
|
46
46
|
query: string,
|
|
47
47
|
signal?: AbortSignal,
|
|
48
|
-
options: { forceRefresh?: boolean; offset?: number } = {},
|
|
48
|
+
options: { forceRefresh?: boolean; offset?: number; limit?: number } = {},
|
|
49
49
|
): Promise<BrowserLoadResult> {
|
|
50
50
|
const now = new Date();
|
|
51
51
|
const from = dateYearsAgo(2, now);
|
|
52
52
|
const to = todayIso(now);
|
|
53
53
|
const quarter = latestLikely13FQuarter(now);
|
|
54
54
|
const offset = Math.max(0, options.offset ?? 0);
|
|
55
|
+
const browserLimit = Math.max(1, options.limit ?? BROWSER_PAGE_LIMIT);
|
|
56
|
+
const latestLimit = Math.max(1, options.limit ?? LATEST_FILINGS_PAGE_LIMIT);
|
|
55
57
|
const apiOptions = { forceRefresh: options.forceRefresh };
|
|
56
58
|
const pageApiOptions = { forceRefresh: options.forceRefresh, offset };
|
|
57
59
|
|
|
58
60
|
if (tab === "performance") {
|
|
59
|
-
const topFunds = await listTopThirteenFFunds(quarter,
|
|
61
|
+
const topFunds = await listTopThirteenFFunds(quarter, browserLimit, signal, pageApiOptions);
|
|
60
62
|
const funds: ThirteenFFund[] = topFunds.map((fund) => ({ cik: fund.cik, name: fund.name }));
|
|
61
|
-
const forms = await loadLatestFormsForFunds(funds.slice(0, FORM_ENRICHMENT_LIMIT), { from, to, signal, concurrency: 5, forceRefresh: options.forceRefresh });
|
|
63
|
+
const forms = await loadLatestFormsForFunds(funds.slice(0, Math.min(browserLimit, FORM_ENRICHMENT_LIMIT)), { from, to, signal, concurrency: 5, forceRefresh: options.forceRefresh });
|
|
62
64
|
return {
|
|
63
65
|
rows: buildBrowserRows({ topFunds, forms, source: "performance" }),
|
|
64
66
|
quarter,
|
|
65
67
|
period: topFunds[0]?.periodOfReport,
|
|
66
|
-
hasMore: topFunds.length >=
|
|
68
|
+
hasMore: topFunds.length >= browserLimit,
|
|
67
69
|
nextOffset: offset + topFunds.length,
|
|
68
70
|
};
|
|
69
71
|
}
|
|
70
72
|
|
|
71
73
|
if (tab === "latest") {
|
|
72
|
-
const filings = await listThirteenFFilings(recentIso(21, now), to,
|
|
74
|
+
const filings = await listThirteenFFilings(recentIso(21, now), to, latestLimit, signal, pageApiOptions);
|
|
73
75
|
return {
|
|
74
76
|
rows: buildBrowserRows({ latestFilings: filings, source: "latest" }),
|
|
75
77
|
period: filings[0]?.periodOfReport,
|
|
76
|
-
hasMore: filings.length >=
|
|
78
|
+
hasMore: filings.length >= latestLimit,
|
|
77
79
|
nextOffset: offset + filings.length,
|
|
78
80
|
};
|
|
79
81
|
}
|
|
@@ -97,7 +99,7 @@ export async function loadBrowserRows(
|
|
|
97
99
|
try {
|
|
98
100
|
const holders = await lookupThirteenFHoldersByCusip(cusip, periodOfReport, signal, apiOptions);
|
|
99
101
|
const forms = new Map<string, ThirteenFFormSummary>();
|
|
100
|
-
const pageCiks = holders.ciks.slice(offset, offset +
|
|
102
|
+
const pageCiks = holders.ciks.slice(offset, offset + browserLimit);
|
|
101
103
|
const funds = await Promise.all(pageCiks.map(async (cik): Promise<ThirteenFFund> => {
|
|
102
104
|
const fundForms = await listThirteenFForms(cik, from, to, 1, signal, apiOptions);
|
|
103
105
|
if (fundForms[0]) forms.set(cik, fundForms[0]);
|
|
@@ -136,10 +138,10 @@ export async function loadBrowserRows(
|
|
|
136
138
|
nextOffset: offset + 1,
|
|
137
139
|
};
|
|
138
140
|
}
|
|
139
|
-
const funds = await searchThirteenFFunds(trimmed,
|
|
141
|
+
const funds = await searchThirteenFFunds(trimmed, browserLimit, signal, pageApiOptions);
|
|
140
142
|
const [forms, topFunds] = await Promise.all([
|
|
141
|
-
loadLatestFormsForFunds(funds.slice(0, FORM_ENRICHMENT_LIMIT), { from, to, signal, concurrency: 5, forceRefresh: options.forceRefresh }),
|
|
142
|
-
listTopThirteenFFunds(quarter,
|
|
143
|
+
loadLatestFormsForFunds(funds.slice(0, Math.min(browserLimit, FORM_ENRICHMENT_LIMIT)), { from, to, signal, concurrency: 5, forceRefresh: options.forceRefresh }),
|
|
144
|
+
listTopThirteenFFunds(quarter, browserLimit, signal, { forceRefresh: options.forceRefresh }).catch(() => []),
|
|
143
145
|
]);
|
|
144
146
|
const topByCik = new Map(topFunds.map((fund) => [fund.cik, fund]));
|
|
145
147
|
const matchedTopFunds = funds
|
|
@@ -153,7 +155,7 @@ export async function loadBrowserRows(
|
|
|
153
155
|
source: "funds",
|
|
154
156
|
}),
|
|
155
157
|
quarter,
|
|
156
|
-
hasMore: funds.length >=
|
|
158
|
+
hasMore: funds.length >= browserLimit,
|
|
157
159
|
nextOffset: offset + funds.length,
|
|
158
160
|
};
|
|
159
161
|
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { createTestDataProvider } from "../../../test-support/data-provider";
|
|
3
|
+
import { createDefaultConfig } from "../../../types/config";
|
|
4
|
+
import type { HeadlessPaneContext, HeadlessPaneLoadArgs } from "../../../types/plugin";
|
|
5
|
+
import { createThirteenFHeadless } from "./headless";
|
|
6
|
+
import type { FundDetailData } from "./types";
|
|
7
|
+
|
|
8
|
+
const detail: FundDetailData = {
|
|
9
|
+
cik: "0001067983",
|
|
10
|
+
name: "Berkshire Hathaway",
|
|
11
|
+
forms: [
|
|
12
|
+
{
|
|
13
|
+
url: "https://www.sec.gov/latest",
|
|
14
|
+
accessionNumber: "latest",
|
|
15
|
+
submissionType: "13F-HR",
|
|
16
|
+
periodOfReport: "2026-06-30",
|
|
17
|
+
filedAsOfDate: "2026-08-14",
|
|
18
|
+
cik: "0001067983",
|
|
19
|
+
companyName: "Berkshire Hathaway",
|
|
20
|
+
tableValueTotal: 300,
|
|
21
|
+
tableEntryTotal: 2,
|
|
22
|
+
isAmendment: false,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
url: "https://www.sec.gov/previous",
|
|
26
|
+
accessionNumber: "previous",
|
|
27
|
+
submissionType: "13F-HR",
|
|
28
|
+
periodOfReport: "2026-03-31",
|
|
29
|
+
filedAsOfDate: "2026-05-15",
|
|
30
|
+
cik: "0001067983",
|
|
31
|
+
companyName: "Berkshire Hathaway",
|
|
32
|
+
tableValueTotal: 200,
|
|
33
|
+
tableEntryTotal: 1,
|
|
34
|
+
isAmendment: false,
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
latestForm: null,
|
|
38
|
+
previousForm: null,
|
|
39
|
+
latestHoldings: [],
|
|
40
|
+
previousHoldings: [],
|
|
41
|
+
};
|
|
42
|
+
detail.latestForm = detail.forms[0]!;
|
|
43
|
+
detail.previousForm = detail.forms[1]!;
|
|
44
|
+
detail.latestHoldings = [
|
|
45
|
+
{
|
|
46
|
+
accessionNumber: "latest",
|
|
47
|
+
cik: detail.cik,
|
|
48
|
+
issuer: "Apple Inc",
|
|
49
|
+
titleOfClass: "COM",
|
|
50
|
+
cusip: "037833100",
|
|
51
|
+
ticker: "AAPL",
|
|
52
|
+
value: 300,
|
|
53
|
+
shares: 3,
|
|
54
|
+
shareType: "SH",
|
|
55
|
+
investmentDiscretion: "DFND",
|
|
56
|
+
votingAuthoritySole: 3,
|
|
57
|
+
votingAuthorityShared: 0,
|
|
58
|
+
votingAuthorityNone: 0,
|
|
59
|
+
putCall: "",
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
detail.previousHoldings = [{ ...detail.latestHoldings[0]!, accessionNumber: "previous", value: 200, shares: 2 }];
|
|
63
|
+
|
|
64
|
+
function context(): HeadlessPaneContext {
|
|
65
|
+
return {
|
|
66
|
+
marketData: createTestDataProvider(),
|
|
67
|
+
apiClient: {} as HeadlessPaneContext["apiClient"],
|
|
68
|
+
config: createDefaultConfig("/tmp/gloomberb-headless-thirteenf"),
|
|
69
|
+
signal: new AbortController().signal,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function args(view: string, limit = 50): HeadlessPaneLoadArgs {
|
|
74
|
+
return {
|
|
75
|
+
rawArgument: "1067983",
|
|
76
|
+
argument: "1067983",
|
|
77
|
+
symbols: [],
|
|
78
|
+
options: { view, limit },
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
describe("13F headless model", () => {
|
|
83
|
+
test("projects browser rows and switches to fund holdings", async () => {
|
|
84
|
+
const headless = createThirteenFHeadless({
|
|
85
|
+
loadBrowser: async () => ({
|
|
86
|
+
rows: [
|
|
87
|
+
{ id: "one", cik: detail.cik, name: detail.name, estQuarterReturn: 8.2, source: "performance" },
|
|
88
|
+
{ id: "two", cik: "0000000002", name: "Second Fund", estQuarterReturn: 4.1, source: "performance" },
|
|
89
|
+
],
|
|
90
|
+
quarter: "2026Q2",
|
|
91
|
+
}),
|
|
92
|
+
loadDetail: async () => detail,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const browser = await headless.load(args("performance", 1), context());
|
|
96
|
+
expect(browser.rows).toEqual([expect.objectContaining({
|
|
97
|
+
cik: detail.cik,
|
|
98
|
+
name: detail.name,
|
|
99
|
+
estQuarterReturn: 8.2,
|
|
100
|
+
})]);
|
|
101
|
+
|
|
102
|
+
const holdings = await headless.load(args("holdings", 5), context());
|
|
103
|
+
expect(holdings.rows).toEqual([expect.objectContaining({
|
|
104
|
+
ticker: "AAPL",
|
|
105
|
+
value: 300,
|
|
106
|
+
sharesChange: 1,
|
|
107
|
+
action: "add",
|
|
108
|
+
actionLabel: "Add",
|
|
109
|
+
})]);
|
|
110
|
+
expect(holdings.metadata).toMatchObject({ view: "holdings", fund: detail.name });
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
HeadlessPaneColumn,
|
|
3
|
+
HeadlessPaneContext,
|
|
4
|
+
HeadlessPaneDefinition,
|
|
5
|
+
HeadlessPaneLoadArgs,
|
|
6
|
+
} from "../../../types/plugin";
|
|
7
|
+
import { normalizeCik } from "./api";
|
|
8
|
+
import {
|
|
9
|
+
loadBrowserRows,
|
|
10
|
+
loadFundDetail,
|
|
11
|
+
type BrowserLoadResult,
|
|
12
|
+
} from "./data";
|
|
13
|
+
import {
|
|
14
|
+
DEFAULT_BROWSER_SORT,
|
|
15
|
+
DEFAULT_HOLDING_SORT,
|
|
16
|
+
DEFAULT_TIMELINE_SORT,
|
|
17
|
+
buildFundHoldingRows,
|
|
18
|
+
buildTimelineRows,
|
|
19
|
+
inferBrowserTabFromQuery,
|
|
20
|
+
isCikQuery,
|
|
21
|
+
positionType,
|
|
22
|
+
sortBrowserRows,
|
|
23
|
+
sortHoldingRows,
|
|
24
|
+
sortTimelineRows,
|
|
25
|
+
} from "./model";
|
|
26
|
+
import {
|
|
27
|
+
actionLabel,
|
|
28
|
+
formatChangeShares,
|
|
29
|
+
formatMoneyCompact,
|
|
30
|
+
formatPercentMaybe,
|
|
31
|
+
formatRawPercentMaybe,
|
|
32
|
+
formatShares,
|
|
33
|
+
formatShortDate,
|
|
34
|
+
} from "./format";
|
|
35
|
+
import type {
|
|
36
|
+
FundDetailData,
|
|
37
|
+
ThirteenFBrowserTab,
|
|
38
|
+
} from "./types";
|
|
39
|
+
|
|
40
|
+
const BROWSER_COLUMNS: HeadlessPaneColumn[] = [
|
|
41
|
+
{ key: "name", header: "Fund" },
|
|
42
|
+
{ key: "cik", header: "CIK" },
|
|
43
|
+
{ key: "periodOfReport", header: "Period" },
|
|
44
|
+
{
|
|
45
|
+
key: "estQuarterReturn",
|
|
46
|
+
header: "Est 13F",
|
|
47
|
+
align: "right",
|
|
48
|
+
format: (value) => formatRawPercentMaybe(value == null ? null : Number(value)),
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
key: "tableValueTotal",
|
|
52
|
+
header: "Value",
|
|
53
|
+
align: "right",
|
|
54
|
+
format: (value) => formatMoneyCompact(value == null ? null : Number(value)),
|
|
55
|
+
},
|
|
56
|
+
{ key: "tableEntryTotal", header: "Rows", align: "right" },
|
|
57
|
+
{
|
|
58
|
+
key: "filedAsOfDate",
|
|
59
|
+
header: "Filed",
|
|
60
|
+
format: (value) => formatShortDate(typeof value === "string" ? value : null),
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
const HOLDING_COLUMNS: HeadlessPaneColumn[] = [
|
|
65
|
+
{ key: "ticker", header: "Ticker" },
|
|
66
|
+
{ key: "type", header: "Type" },
|
|
67
|
+
{ key: "issuer", header: "Issuer" },
|
|
68
|
+
{
|
|
69
|
+
key: "value",
|
|
70
|
+
header: "Value",
|
|
71
|
+
align: "right",
|
|
72
|
+
format: (value) => formatMoneyCompact(value == null ? null : Number(value)),
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
key: "estimatedPnl",
|
|
76
|
+
header: "Est P&L",
|
|
77
|
+
align: "right",
|
|
78
|
+
format: (value) => formatMoneyCompact(value == null ? null : Number(value)),
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
key: "weight",
|
|
82
|
+
header: "Weight",
|
|
83
|
+
align: "right",
|
|
84
|
+
format: (value) => formatPercentMaybe(value == null ? null : Number(value)),
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
key: "shares",
|
|
88
|
+
header: "Shares",
|
|
89
|
+
align: "right",
|
|
90
|
+
format: (value) => formatShares(value == null ? null : Number(value)),
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
key: "sharesChange",
|
|
94
|
+
header: "QoQ",
|
|
95
|
+
align: "right",
|
|
96
|
+
format: (value) => formatChangeShares(value == null ? null : Number(value)),
|
|
97
|
+
},
|
|
98
|
+
{ key: "actionLabel", header: "Action" },
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
const FILING_COLUMNS: HeadlessPaneColumn[] = [
|
|
102
|
+
{ key: "periodOfReport", header: "Period" },
|
|
103
|
+
{
|
|
104
|
+
key: "filedAsOfDate",
|
|
105
|
+
header: "Filed",
|
|
106
|
+
format: (value) => formatShortDate(typeof value === "string" ? value : null),
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
key: "tableValueTotal",
|
|
110
|
+
header: "Value",
|
|
111
|
+
align: "right",
|
|
112
|
+
format: (value) => formatMoneyCompact(value == null ? null : Number(value)),
|
|
113
|
+
},
|
|
114
|
+
{ key: "tableEntryTotal", header: "Rows", align: "right" },
|
|
115
|
+
{
|
|
116
|
+
key: "valueChangePercent",
|
|
117
|
+
header: "Value%",
|
|
118
|
+
align: "right",
|
|
119
|
+
format: (value) => formatPercentMaybe(value == null ? null : Number(value)),
|
|
120
|
+
},
|
|
121
|
+
{ key: "submissionType", header: "Form" },
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
type HeadlessThirteenFView =
|
|
125
|
+
| "auto"
|
|
126
|
+
| "performance"
|
|
127
|
+
| "funds"
|
|
128
|
+
| "by-ticker"
|
|
129
|
+
| "latest"
|
|
130
|
+
| "holdings"
|
|
131
|
+
| "filings";
|
|
132
|
+
|
|
133
|
+
export interface ThirteenFHeadlessDependencies {
|
|
134
|
+
loadBrowser(
|
|
135
|
+
tab: ThirteenFBrowserTab,
|
|
136
|
+
query: string,
|
|
137
|
+
limit: number,
|
|
138
|
+
args: HeadlessPaneLoadArgs,
|
|
139
|
+
ctx: HeadlessPaneContext,
|
|
140
|
+
): Promise<BrowserLoadResult>;
|
|
141
|
+
loadDetail(
|
|
142
|
+
cik: string,
|
|
143
|
+
name: string,
|
|
144
|
+
args: HeadlessPaneLoadArgs,
|
|
145
|
+
ctx: HeadlessPaneContext,
|
|
146
|
+
): Promise<FundDetailData>;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const defaultDependencies: ThirteenFHeadlessDependencies = {
|
|
150
|
+
loadBrowser: (tab, query, limit, _args, ctx) => loadBrowserRows(
|
|
151
|
+
tab,
|
|
152
|
+
query,
|
|
153
|
+
ctx.signal,
|
|
154
|
+
{ limit },
|
|
155
|
+
),
|
|
156
|
+
loadDetail: (cik, name, _args, ctx) => loadFundDetail(cik, name, ctx.signal),
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
function browserTab(view: HeadlessThirteenFView, query: string): ThirteenFBrowserTab {
|
|
160
|
+
if (view === "performance" || view === "funds" || view === "latest") return view;
|
|
161
|
+
if (view === "by-ticker") return "byTicker";
|
|
162
|
+
return inferBrowserTabFromQuery(query);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function resolveFund(
|
|
166
|
+
query: string,
|
|
167
|
+
limit: number,
|
|
168
|
+
args: HeadlessPaneLoadArgs,
|
|
169
|
+
ctx: HeadlessPaneContext,
|
|
170
|
+
dependencies: ThirteenFHeadlessDependencies,
|
|
171
|
+
): Promise<{ cik: string; name: string }> {
|
|
172
|
+
if (!query) throw new Error("13F holdings and filings require a fund name or CIK.");
|
|
173
|
+
if (isCikQuery(query)) return { cik: normalizeCik(query), name: query };
|
|
174
|
+
const result = await dependencies.loadBrowser("funds", query, Math.min(limit, 25), args, ctx);
|
|
175
|
+
const exact = result.rows.find((row) => row.name.toLocaleLowerCase() === query.toLocaleLowerCase());
|
|
176
|
+
const fund = exact ?? result.rows[0];
|
|
177
|
+
if (!fund) throw new Error(`No 13F fund found for "${query}".`);
|
|
178
|
+
return { cik: fund.cik, name: fund.name };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function createThirteenFHeadless(
|
|
182
|
+
dependencies: ThirteenFHeadlessDependencies = defaultDependencies,
|
|
183
|
+
): HeadlessPaneDefinition<"rows"> {
|
|
184
|
+
return {
|
|
185
|
+
shape: "rows",
|
|
186
|
+
argument: {
|
|
187
|
+
kind: "free-text",
|
|
188
|
+
placeholder: "fund, ticker, or CIK",
|
|
189
|
+
description: "Optional fund name, ticker, CIK, or latest keyword.",
|
|
190
|
+
optional: true,
|
|
191
|
+
},
|
|
192
|
+
options: [
|
|
193
|
+
{
|
|
194
|
+
key: "view",
|
|
195
|
+
description: "Fund browser mode or detail tab.",
|
|
196
|
+
type: "enum",
|
|
197
|
+
values: [
|
|
198
|
+
{ value: "auto" },
|
|
199
|
+
{ value: "performance" },
|
|
200
|
+
{ value: "funds" },
|
|
201
|
+
{ value: "by-ticker", aliases: ["ticker", "byTicker"] },
|
|
202
|
+
{ value: "latest" },
|
|
203
|
+
{ value: "holdings" },
|
|
204
|
+
{ value: "filings" },
|
|
205
|
+
],
|
|
206
|
+
defaultValue: "auto",
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
key: "limit",
|
|
210
|
+
aliases: ["count", "rows"],
|
|
211
|
+
description: "Maximum rows.",
|
|
212
|
+
type: "integer",
|
|
213
|
+
defaultValue: 50,
|
|
214
|
+
minimum: 1,
|
|
215
|
+
maximum: 200,
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
describe: (args) => `13F Funds | ${String(args.options.view)}`,
|
|
219
|
+
async load(args, ctx) {
|
|
220
|
+
const query = typeof args.argument === "string" ? args.argument.trim() : "";
|
|
221
|
+
const requestedView = String(args.options.view) as HeadlessThirteenFView;
|
|
222
|
+
const view = requestedView === "auto" && isCikQuery(query) ? "holdings" : requestedView;
|
|
223
|
+
const limit = Number(args.options.limit);
|
|
224
|
+
|
|
225
|
+
if (view === "holdings" || view === "filings") {
|
|
226
|
+
const fund = await resolveFund(query, limit, args, ctx, dependencies);
|
|
227
|
+
const detail = await dependencies.loadDetail(fund.cik, fund.name, args, ctx);
|
|
228
|
+
if (view === "filings") {
|
|
229
|
+
const rows = sortTimelineRows(buildTimelineRows(detail.forms), DEFAULT_TIMELINE_SORT)
|
|
230
|
+
.slice(0, limit)
|
|
231
|
+
.map((row) => ({ ...row }));
|
|
232
|
+
return {
|
|
233
|
+
columns: FILING_COLUMNS,
|
|
234
|
+
rows,
|
|
235
|
+
metadata: {
|
|
236
|
+
view,
|
|
237
|
+
cik: detail.cik,
|
|
238
|
+
fund: detail.name,
|
|
239
|
+
latestPeriod: detail.latestForm?.periodOfReport ?? null,
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
const rows = sortHoldingRows(
|
|
244
|
+
buildFundHoldingRows(detail).filter((row) => row.value != null),
|
|
245
|
+
DEFAULT_HOLDING_SORT,
|
|
246
|
+
)
|
|
247
|
+
.slice(0, limit)
|
|
248
|
+
.map((row) => ({
|
|
249
|
+
...row,
|
|
250
|
+
type: positionType(row),
|
|
251
|
+
actionLabel: actionLabel(row.action),
|
|
252
|
+
}));
|
|
253
|
+
return {
|
|
254
|
+
columns: HOLDING_COLUMNS,
|
|
255
|
+
rows,
|
|
256
|
+
metadata: {
|
|
257
|
+
view,
|
|
258
|
+
cik: detail.cik,
|
|
259
|
+
fund: detail.name,
|
|
260
|
+
latestPeriod: detail.latestForm?.periodOfReport ?? null,
|
|
261
|
+
previousPeriod: detail.previousForm?.periodOfReport ?? null,
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const tab = browserTab(view, query);
|
|
267
|
+
const result = await dependencies.loadBrowser(tab, query, limit, args, ctx);
|
|
268
|
+
return {
|
|
269
|
+
columns: BROWSER_COLUMNS,
|
|
270
|
+
rows: sortBrowserRows(result.rows, DEFAULT_BROWSER_SORT)
|
|
271
|
+
.slice(0, limit)
|
|
272
|
+
.map((row) => ({ ...row })),
|
|
273
|
+
...(result.warning ? { errors: [result.warning] } : {}),
|
|
274
|
+
metadata: {
|
|
275
|
+
view: tab,
|
|
276
|
+
query: query || null,
|
|
277
|
+
period: result.period ?? null,
|
|
278
|
+
quarter: result.quarter ?? null,
|
|
279
|
+
hasMore: result.hasMore ?? false,
|
|
280
|
+
},
|
|
281
|
+
};
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export const thirteenFHeadless = createThirteenFHeadless();
|
|
@@ -11,6 +11,9 @@ import {
|
|
|
11
11
|
attachThirteenFApiPersistence,
|
|
12
12
|
resetThirteenFApiPersistence,
|
|
13
13
|
} from "./api";
|
|
14
|
+
import { thirteenFHeadless } from "./headless";
|
|
15
|
+
|
|
16
|
+
export { thirteenFHeadless } from "./headless";
|
|
14
17
|
|
|
15
18
|
function queryFromOptions(options?: PaneTemplateCreateOptions): string {
|
|
16
19
|
return (options?.arg ?? options?.values?.query ?? "").trim();
|
|
@@ -55,6 +58,7 @@ export const thirteenFModule: PluginModule = {
|
|
|
55
58
|
argKind: "text",
|
|
56
59
|
argOptional: true,
|
|
57
60
|
},
|
|
61
|
+
headless: thirteenFHeadless,
|
|
58
62
|
createInstance(_context: PaneTemplateContext, options?: PaneTemplateCreateOptions) {
|
|
59
63
|
const query = queryFromOptions(options);
|
|
60
64
|
const browserMode = inferBrowserTabFromQuery(query);
|
|
@@ -615,6 +615,19 @@ export function selectedIndexById<T extends { id: string }>(rows: T[], selectedI
|
|
|
615
615
|
return index >= 0 ? index : rows.length > 0 ? 0 : -1;
|
|
616
616
|
}
|
|
617
617
|
|
|
618
|
+
export function positionType(row: {
|
|
619
|
+
putCall: string;
|
|
620
|
+
titleOfClass: string;
|
|
621
|
+
shareType: string;
|
|
622
|
+
}): string {
|
|
623
|
+
const putCall = row.putCall.trim().toUpperCase();
|
|
624
|
+
if (putCall === "PUT" || putCall === "CALL") return putCall;
|
|
625
|
+
const title = row.titleOfClass.trim().toUpperCase();
|
|
626
|
+
if (title) return title;
|
|
627
|
+
const shareType = row.shareType.trim().toUpperCase();
|
|
628
|
+
return shareType || "--";
|
|
629
|
+
}
|
|
630
|
+
|
|
618
631
|
export function isCikQuery(query: string): boolean {
|
|
619
632
|
return CIK_RE.test(query.trim());
|
|
620
633
|
}
|