work on calculations
This commit is contained in:
parent
3d910a13e0
commit
ab7ef2b678
20 changed files with 1343 additions and 222 deletions
|
|
@ -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 };
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue