Skip to content

Commit 0fbb859

Browse files
expanded python comparison
1 parent 68ec203 commit 0fbb859

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+586
-171
lines changed

dist/index.cjs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,6 +1166,63 @@ var OpenFrame = class {
11661166
const b = this.beta(assetColumn, marketColumn);
11671167
return assetMean - riskfreeRate - b * (marketMean - riskfreeRate);
11681168
}
1169+
/**
1170+
* Ordinary Least Squares fit of y on x.
1171+
* Regresses tsdf column yColumn (dependent) on xColumn (explanatory).
1172+
* Matches Python openseries ord_least_squares_fit.
1173+
*
1174+
* @param yColumn - Column index of dependent variable y
1175+
* @param xColumn - Column index of exogenous variable x
1176+
* @param opts.fittedSeries - If true, add fitted values as new column (default true)
1177+
* @returns Object with coefficient (slope), intercept, and rsquared
1178+
*/
1179+
ordLeastSquaresFit(yColumn, xColumn, opts) {
1180+
const fittedSeries = opts?.fittedSeries ?? true;
1181+
const yCol = this.tsdf.columns[yColumn];
1182+
const xCol = this.tsdf.columns[xColumn];
1183+
if (!yCol || !xCol) {
1184+
return { coefficient: NaN, intercept: NaN, rsquared: NaN };
1185+
}
1186+
const pairs = [];
1187+
for (let i = 0; i < Math.min(yCol.length, xCol.length); i++) {
1188+
const yi = yCol[i];
1189+
const xi = xCol[i];
1190+
if (Number.isFinite(yi) && Number.isFinite(xi)) pairs.push([yi, xi]);
1191+
}
1192+
if (pairs.length < 2) {
1193+
return { coefficient: NaN, intercept: NaN, rsquared: NaN };
1194+
}
1195+
const n = pairs.length;
1196+
const my = pairs.reduce((s, [y]) => s + y, 0) / n;
1197+
const mx = pairs.reduce((s, [, x]) => s + x, 0) / n;
1198+
let cov = 0;
1199+
let vx = 0;
1200+
for (const [yi, xi] of pairs) {
1201+
cov += (yi - my) * (xi - mx);
1202+
vx += (xi - mx) ** 2;
1203+
}
1204+
const coefficient = vx > 0 ? cov / vx : NaN;
1205+
const intercept = Number.isFinite(coefficient) ? my - coefficient * mx : NaN;
1206+
let rsquared = NaN;
1207+
if (Number.isFinite(coefficient) && Number.isFinite(intercept)) {
1208+
let ssTot = 0;
1209+
let ssRes = 0;
1210+
for (const [yi, xi] of pairs) {
1211+
const yHat = intercept + coefficient * xi;
1212+
ssTot += (yi - my) ** 2;
1213+
ssRes += (yi - yHat) ** 2;
1214+
}
1215+
rsquared = ssTot > 0 ? 1 - ssRes / ssTot : NaN;
1216+
}
1217+
if (fittedSeries && Number.isFinite(coefficient) && Number.isFinite(intercept)) {
1218+
const fitted = xCol.map((xi) => Number.isFinite(xi) ? intercept + coefficient * xi : NaN);
1219+
this.tsdf.columns.push(fitted);
1220+
const yLabel = this.columnLabels[yColumn] ?? "y";
1221+
const xLabel = this.columnLabels[xColumn] ?? "x";
1222+
this.columnLabels.push(`OLS fit (${yLabel} vs ${xLabel})`);
1223+
}
1224+
return { coefficient, intercept, rsquared };
1225+
}
11691226
addTimeseries(series) {
11701227
this.constituents.push(series);
11711228
this.mergeSeries("outer");

dist/index.d.cts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,12 @@ declare class OpenTimeSeries {
210210
*/
211211
declare function timeseriesChain(front: OpenTimeSeries, back: OpenTimeSeries, oldFee?: number): OpenTimeSeries;
212212

213+
/** Result of Ordinary Least Squares regression (ord_least_squares_fit). */
214+
interface OrdLeastSquaresResult {
215+
coefficient: number;
216+
intercept: number;
217+
rsquared: number;
218+
}
213219
/** Collection of aligned timeseries with portfolio and correlation methods. */
214220
declare class OpenFrame {
215221
constituents: OpenTimeSeries[];
@@ -247,6 +253,19 @@ declare class OpenFrame {
247253
infoRatio(baseColumn?: number): number[];
248254
beta(assetColumn: number, marketColumn: number): number;
249255
jensenAlpha(assetColumn: number, marketColumn: number, riskfreeRate?: number): number;
256+
/**
257+
* Ordinary Least Squares fit of y on x.
258+
* Regresses tsdf column yColumn (dependent) on xColumn (explanatory).
259+
* Matches Python openseries ord_least_squares_fit.
260+
*
261+
* @param yColumn - Column index of dependent variable y
262+
* @param xColumn - Column index of exogenous variable x
263+
* @param opts.fittedSeries - If true, add fitted values as new column (default true)
264+
* @returns Object with coefficient (slope), intercept, and rsquared
265+
*/
266+
ordLeastSquaresFit(yColumn: number, xColumn: number, opts?: {
267+
fittedSeries?: boolean;
268+
}): OrdLeastSquaresResult;
250269
addTimeseries(series: OpenTimeSeries): this;
251270
/** Filters tsdf to retain only business days. Mutates in place. */
252271
filterToBusinessDays(): this;
@@ -498,4 +517,4 @@ declare function sharpeplotHtml(simulated: SimulatedPortfolio[], frontier: Effic
498517
*/
499518
declare function sharpeplot(simulated: SimulatedPortfolio[], frontier: EfficientFrontierPoint[], pointFrame: SharpePlotPoint[], options?: SharpePlotOptions): Promise<string>;
500519

501-
export { type CaptorSeriesResponse, type CountryCode, DateAlignmentError, type DateRangeOptions, type EfficientFrontierPoint, IncorrectArgumentComboError, InitialValueZeroError, type LiteralBizDayFreq, type LiteralPortfolioWeightings, MixedValuetypesError, NoWeightsError, OpenFrame, OpenTimeSeries, type PlotSeriesOptions, type RandomGenerator, type ReportOptions, ResampleDataLossError, type ResampleFreq, ReturnSimulation, type SharpePlotOptions, type SharpePlotPoint, type SimulatedPortfolio, ValueType, dateFix, dateToStr, efficientFrontier, fetchCaptorSeries, fetchCaptorSeriesBatch, filterBusinessDays, filterToBusinessDays, generateCalendarDateRange, getPreviousBusinessDayBeforeToday, isBusinessDay, lastBusinessDayOfMonth, lastBusinessDayOfYear, mean, offsetBusinessDays, pctChange, plotSeries, plotSeriesHtml, preparePlotData, prevBusinessDay, quantile, randomGenerator, reportHtml, resampleToPeriodEnd, sharpeplot, sharpeplotHtml, simulatePortfolios, std, timeseriesChain };
520+
export { type CaptorSeriesResponse, type CountryCode, DateAlignmentError, type DateRangeOptions, type EfficientFrontierPoint, IncorrectArgumentComboError, InitialValueZeroError, type LiteralBizDayFreq, type LiteralPortfolioWeightings, MixedValuetypesError, NoWeightsError, OpenFrame, OpenTimeSeries, type OrdLeastSquaresResult, type PlotSeriesOptions, type RandomGenerator, type ReportOptions, ResampleDataLossError, type ResampleFreq, ReturnSimulation, type SharpePlotOptions, type SharpePlotPoint, type SimulatedPortfolio, ValueType, dateFix, dateToStr, efficientFrontier, fetchCaptorSeries, fetchCaptorSeriesBatch, filterBusinessDays, filterToBusinessDays, generateCalendarDateRange, getPreviousBusinessDayBeforeToday, isBusinessDay, lastBusinessDayOfMonth, lastBusinessDayOfYear, mean, offsetBusinessDays, pctChange, plotSeries, plotSeriesHtml, preparePlotData, prevBusinessDay, quantile, randomGenerator, reportHtml, resampleToPeriodEnd, sharpeplot, sharpeplotHtml, simulatePortfolios, std, timeseriesChain };

dist/index.d.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,12 @@ declare class OpenTimeSeries {
210210
*/
211211
declare function timeseriesChain(front: OpenTimeSeries, back: OpenTimeSeries, oldFee?: number): OpenTimeSeries;
212212

213+
/** Result of Ordinary Least Squares regression (ord_least_squares_fit). */
214+
interface OrdLeastSquaresResult {
215+
coefficient: number;
216+
intercept: number;
217+
rsquared: number;
218+
}
213219
/** Collection of aligned timeseries with portfolio and correlation methods. */
214220
declare class OpenFrame {
215221
constituents: OpenTimeSeries[];
@@ -247,6 +253,19 @@ declare class OpenFrame {
247253
infoRatio(baseColumn?: number): number[];
248254
beta(assetColumn: number, marketColumn: number): number;
249255
jensenAlpha(assetColumn: number, marketColumn: number, riskfreeRate?: number): number;
256+
/**
257+
* Ordinary Least Squares fit of y on x.
258+
* Regresses tsdf column yColumn (dependent) on xColumn (explanatory).
259+
* Matches Python openseries ord_least_squares_fit.
260+
*
261+
* @param yColumn - Column index of dependent variable y
262+
* @param xColumn - Column index of exogenous variable x
263+
* @param opts.fittedSeries - If true, add fitted values as new column (default true)
264+
* @returns Object with coefficient (slope), intercept, and rsquared
265+
*/
266+
ordLeastSquaresFit(yColumn: number, xColumn: number, opts?: {
267+
fittedSeries?: boolean;
268+
}): OrdLeastSquaresResult;
250269
addTimeseries(series: OpenTimeSeries): this;
251270
/** Filters tsdf to retain only business days. Mutates in place. */
252271
filterToBusinessDays(): this;
@@ -498,4 +517,4 @@ declare function sharpeplotHtml(simulated: SimulatedPortfolio[], frontier: Effic
498517
*/
499518
declare function sharpeplot(simulated: SimulatedPortfolio[], frontier: EfficientFrontierPoint[], pointFrame: SharpePlotPoint[], options?: SharpePlotOptions): Promise<string>;
500519

501-
export { type CaptorSeriesResponse, type CountryCode, DateAlignmentError, type DateRangeOptions, type EfficientFrontierPoint, IncorrectArgumentComboError, InitialValueZeroError, type LiteralBizDayFreq, type LiteralPortfolioWeightings, MixedValuetypesError, NoWeightsError, OpenFrame, OpenTimeSeries, type PlotSeriesOptions, type RandomGenerator, type ReportOptions, ResampleDataLossError, type ResampleFreq, ReturnSimulation, type SharpePlotOptions, type SharpePlotPoint, type SimulatedPortfolio, ValueType, dateFix, dateToStr, efficientFrontier, fetchCaptorSeries, fetchCaptorSeriesBatch, filterBusinessDays, filterToBusinessDays, generateCalendarDateRange, getPreviousBusinessDayBeforeToday, isBusinessDay, lastBusinessDayOfMonth, lastBusinessDayOfYear, mean, offsetBusinessDays, pctChange, plotSeries, plotSeriesHtml, preparePlotData, prevBusinessDay, quantile, randomGenerator, reportHtml, resampleToPeriodEnd, sharpeplot, sharpeplotHtml, simulatePortfolios, std, timeseriesChain };
520+
export { type CaptorSeriesResponse, type CountryCode, DateAlignmentError, type DateRangeOptions, type EfficientFrontierPoint, IncorrectArgumentComboError, InitialValueZeroError, type LiteralBizDayFreq, type LiteralPortfolioWeightings, MixedValuetypesError, NoWeightsError, OpenFrame, OpenTimeSeries, type OrdLeastSquaresResult, type PlotSeriesOptions, type RandomGenerator, type ReportOptions, ResampleDataLossError, type ResampleFreq, ReturnSimulation, type SharpePlotOptions, type SharpePlotPoint, type SimulatedPortfolio, ValueType, dateFix, dateToStr, efficientFrontier, fetchCaptorSeries, fetchCaptorSeriesBatch, filterBusinessDays, filterToBusinessDays, generateCalendarDateRange, getPreviousBusinessDayBeforeToday, isBusinessDay, lastBusinessDayOfMonth, lastBusinessDayOfYear, mean, offsetBusinessDays, pctChange, plotSeries, plotSeriesHtml, preparePlotData, prevBusinessDay, quantile, randomGenerator, reportHtml, resampleToPeriodEnd, sharpeplot, sharpeplotHtml, simulatePortfolios, std, timeseriesChain };

dist/index.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,63 @@ var OpenFrame = class {
10931093
const b = this.beta(assetColumn, marketColumn);
10941094
return assetMean - riskfreeRate - b * (marketMean - riskfreeRate);
10951095
}
1096+
/**
1097+
* Ordinary Least Squares fit of y on x.
1098+
* Regresses tsdf column yColumn (dependent) on xColumn (explanatory).
1099+
* Matches Python openseries ord_least_squares_fit.
1100+
*
1101+
* @param yColumn - Column index of dependent variable y
1102+
* @param xColumn - Column index of exogenous variable x
1103+
* @param opts.fittedSeries - If true, add fitted values as new column (default true)
1104+
* @returns Object with coefficient (slope), intercept, and rsquared
1105+
*/
1106+
ordLeastSquaresFit(yColumn, xColumn, opts) {
1107+
const fittedSeries = opts?.fittedSeries ?? true;
1108+
const yCol = this.tsdf.columns[yColumn];
1109+
const xCol = this.tsdf.columns[xColumn];
1110+
if (!yCol || !xCol) {
1111+
return { coefficient: NaN, intercept: NaN, rsquared: NaN };
1112+
}
1113+
const pairs = [];
1114+
for (let i = 0; i < Math.min(yCol.length, xCol.length); i++) {
1115+
const yi = yCol[i];
1116+
const xi = xCol[i];
1117+
if (Number.isFinite(yi) && Number.isFinite(xi)) pairs.push([yi, xi]);
1118+
}
1119+
if (pairs.length < 2) {
1120+
return { coefficient: NaN, intercept: NaN, rsquared: NaN };
1121+
}
1122+
const n = pairs.length;
1123+
const my = pairs.reduce((s, [y]) => s + y, 0) / n;
1124+
const mx = pairs.reduce((s, [, x]) => s + x, 0) / n;
1125+
let cov = 0;
1126+
let vx = 0;
1127+
for (const [yi, xi] of pairs) {
1128+
cov += (yi - my) * (xi - mx);
1129+
vx += (xi - mx) ** 2;
1130+
}
1131+
const coefficient = vx > 0 ? cov / vx : NaN;
1132+
const intercept = Number.isFinite(coefficient) ? my - coefficient * mx : NaN;
1133+
let rsquared = NaN;
1134+
if (Number.isFinite(coefficient) && Number.isFinite(intercept)) {
1135+
let ssTot = 0;
1136+
let ssRes = 0;
1137+
for (const [yi, xi] of pairs) {
1138+
const yHat = intercept + coefficient * xi;
1139+
ssTot += (yi - my) ** 2;
1140+
ssRes += (yi - yHat) ** 2;
1141+
}
1142+
rsquared = ssTot > 0 ? 1 - ssRes / ssTot : NaN;
1143+
}
1144+
if (fittedSeries && Number.isFinite(coefficient) && Number.isFinite(intercept)) {
1145+
const fitted = xCol.map((xi) => Number.isFinite(xi) ? intercept + coefficient * xi : NaN);
1146+
this.tsdf.columns.push(fitted);
1147+
const yLabel = this.columnLabels[yColumn] ?? "y";
1148+
const xLabel = this.columnLabels[xColumn] ?? "x";
1149+
this.columnLabels.push(`OLS fit (${yLabel} vs ${xLabel})`);
1150+
}
1151+
return { coefficient, intercept, rsquared };
1152+
}
10961153
addTimeseries(series) {
10971154
this.constituents.push(series);
10981155
this.mergeSeries("outer");

docs/assets/navigation.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)