work on calculations

This commit is contained in:
Bojan Kucera 2025-06-04 18:16:16 -04:00
parent 3d910a13e0
commit ab7ef2b678
20 changed files with 1343 additions and 222 deletions

View file

@ -253,7 +253,8 @@ export function momentum(prices: number[], period: number = 10): number[] {
const result: number[] = [];
for (let i = period; i < prices.length; i++) {
result.push(prices[i] - prices[i - period]);
const momentum = prices[i] - prices[i - period];
result.push(momentum);
}
return result;
@ -262,7 +263,7 @@ export function momentum(prices: number[], period: number = 10): number[] {
/**
* Rate of Change (ROC)
*/
export function rateOfChange(prices: number[], period: number = 10): number[] {
export function roc(prices: number[], period: number = 10): number[] {
if (period >= prices.length) return [];
const result: number[] = [];
@ -282,32 +283,33 @@ export function rateOfChange(prices: number[], period: number = 10): number[] {
/**
* Money Flow Index (MFI)
*/
export function moneyFlowIndex(ohlcv: OHLCVData[], period: number = 14): number[] {
export function mfi(ohlcv: OHLCVData[], period: number = 14): number[] {
if (period >= ohlcv.length) return [];
const typicalPrices = ohlcv.map(d => (d.high + d.low + d.close) / 3);
const rawMoneyFlows = ohlcv.map((d, i) => typicalPrices[i] * d.volume);
const moneyFlows = ohlcv.map((d, i) => typicalPrices[i] * d.volume);
const result: number[] = [];
for (let i = 1; i < ohlcv.length - period + 1; i++) {
for (let i = period; i < ohlcv.length; i++) {
let positiveFlow = 0;
let negativeFlow = 0;
for (let j = 0; j < period; j++) {
const currentIndex = i + j;
if (typicalPrices[currentIndex] > typicalPrices[currentIndex - 1]) {
positiveFlow += rawMoneyFlows[currentIndex];
} else if (typicalPrices[currentIndex] < typicalPrices[currentIndex - 1]) {
negativeFlow += rawMoneyFlows[currentIndex];
for (let j = i - period + 1; j <= i; j++) {
if (j > 0) {
if (typicalPrices[j] > typicalPrices[j - 1]) {
positiveFlow += moneyFlows[j];
} else if (typicalPrices[j] < typicalPrices[j - 1]) {
negativeFlow += moneyFlows[j];
}
}
}
if (negativeFlow === 0) {
result.push(100);
} else {
const moneyRatio = positiveFlow / negativeFlow;
const mfiValue = 100 - (100 / (1 + moneyRatio));
const mfiRatio = positiveFlow / negativeFlow;
const mfiValue = 100 - (100 / (1 + mfiRatio));
result.push(mfiValue);
}
}
@ -316,23 +318,24 @@ export function moneyFlowIndex(ohlcv: OHLCVData[], period: number = 14): number[
}
/**
* On Balance Volume (OBV)
* On-Balance Volume (OBV)
*/
export function onBalanceVolume(ohlcv: OHLCVData[]): number[] {
export function obv(ohlcv: OHLCVData[]): number[] {
if (ohlcv.length === 0) return [];
const result: number[] = [ohlcv[0].volume];
for (let i = 1; i < ohlcv.length; i++) {
let obvValue = result[i - 1];
const prev = ohlcv[i - 1];
const curr = ohlcv[i];
if (ohlcv[i].close > ohlcv[i - 1].close) {
obvValue += ohlcv[i].volume;
} else if (ohlcv[i].close < ohlcv[i - 1].close) {
obvValue -= ohlcv[i].volume;
if (curr.close > prev.close) {
result.push(result[result.length - 1] + curr.volume);
} else if (curr.close < prev.close) {
result.push(result[result.length - 1] - curr.volume);
} else {
result.push(result[result.length - 1]);
}
result.push(obvValue);
}
return result;
@ -403,63 +406,54 @@ export function chaikinMoneyFlow(ohlcv: OHLCVData[], period: number = 20): numbe
export function parabolicSAR(
ohlcv: OHLCVData[],
step: number = 0.02,
maximum: number = 0.2
maxStep: number = 0.2
): number[] {
if (ohlcv.length < 2) return [];
const result: number[] = [];
let isUptrend = ohlcv[1].close > ohlcv[0].close;
let sar = isUptrend ? ohlcv[0].low : ohlcv[0].high;
let ep = isUptrend ? ohlcv[1].high : ohlcv[1].low;
let af = step;
let trend = 1; // 1 for uptrend, -1 for downtrend
let acceleration = step;
let extremePoint = ohlcv[0].high;
let sar = ohlcv[0].low;
result.push(sar);
for (let i = 1; i < ohlcv.length; i++) {
const currentHigh = ohlcv[i].high;
const currentLow = ohlcv[i].low;
const currentClose = ohlcv[i].close;
const curr = ohlcv[i];
const prev = ohlcv[i - 1];
// Calculate new SAR
sar = sar + af * (ep - sar);
sar = sar + acceleration * (extremePoint - sar);
if (isUptrend) {
// Uptrend logic
if (currentLow <= sar) {
if (trend === 1) { // Uptrend
if (curr.low <= sar) {
// Trend reversal
isUptrend = false;
sar = ep;
ep = currentLow;
af = step;
trend = -1;
sar = extremePoint;
extremePoint = curr.low;
acceleration = step;
} else {
// Continue uptrend
if (currentHigh > ep) {
ep = currentHigh;
af = Math.min(af + step, maximum);
}
// Ensure SAR doesn't go above previous two lows
if (i >= 2) {
sar = Math.min(sar, ohlcv[i - 1].low, ohlcv[i - 2].low);
if (curr.high > extremePoint) {
extremePoint = curr.high;
acceleration = Math.min(acceleration + step, maxStep);
}
// Ensure SAR doesn't exceed previous lows
sar = Math.min(sar, prev.low, i > 1 ? ohlcv[i - 2].low : prev.low);
}
} else {
// Downtrend logic
if (currentHigh >= sar) {
} else { // Downtrend
if (curr.high >= sar) {
// Trend reversal
isUptrend = true;
sar = ep;
ep = currentHigh;
af = step;
trend = 1;
sar = extremePoint;
extremePoint = curr.high;
acceleration = step;
} else {
// Continue downtrend
if (currentLow < ep) {
ep = currentLow;
af = Math.min(af + step, maximum);
}
// Ensure SAR doesn't go below previous two highs
if (i >= 2) {
sar = Math.max(sar, ohlcv[i - 1].high, ohlcv[i - 2].high);
if (curr.low < extremePoint) {
extremePoint = curr.low;
acceleration = Math.min(acceleration + step, maxStep);
}
// Ensure SAR doesn't exceed previous highs
sar = Math.max(sar, prev.high, i > 1 ? ohlcv[i - 2].high : prev.high);
}
}
@ -468,3 +462,38 @@ export function parabolicSAR(
return result;
}
/**
* Aroon Indicator
*/
export function aroon(ohlcv: OHLCVData[], period: number = 14): { up: number[], down: number[] } {
if (period >= ohlcv.length) return { up: [], down: [] };
const up: number[] = [];
const down: number[] = [];
for (let i = period - 1; i < ohlcv.length; i++) {
const slice = ohlcv.slice(i - period + 1, i + 1);
// Find highest high and lowest low positions
let highestIndex = 0;
let lowestIndex = 0;
for (let j = 1; j < slice.length; j++) {
if (slice[j].high > slice[highestIndex].high) {
highestIndex = j;
}
if (slice[j].low < slice[lowestIndex].low) {
lowestIndex = j;
}
}
const aroonUp = ((period - 1 - highestIndex) / (period - 1)) * 100;
const aroonDown = ((period - 1 - lowestIndex) / (period - 1)) * 100;
up.push(aroonUp);
down.push(aroonDown);
}
return { up, down };
}