-
-
Notifications
You must be signed in to change notification settings - Fork 57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Attempted to fix the Projection Upper band and lower band calculation #329
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,16 +3,18 @@ | |
|
||
import { | ||
add, | ||
addBy, | ||
divide, | ||
generateNumbers, | ||
multiply, | ||
multiplyBy, | ||
subtract, | ||
} from '../../helper/numArray'; | ||
import { movingLeastSquare } from '../../helper/regression'; | ||
import { ema } from '../trend/ema'; | ||
import { mmax } from '../trend/mmax'; | ||
import { mmin } from '../trend/mmin'; | ||
subtractBy, | ||
} from "../../helper/numArray"; | ||
import { movingLeastSquare } from "../../helper/regression"; | ||
import { ema } from "../trend/ema"; | ||
import { mmax } from "../trend/mmax"; | ||
import { mmin } from "../trend/mmin"; | ||
|
||
/** | ||
* Projection oscillator result object. | ||
|
@@ -22,15 +24,31 @@ export interface ProjectionOscillator { | |
spo: number[]; | ||
} | ||
|
||
// Just pads a number array with zeros to the right so that the length of the arr becomes desiredLength | ||
function padWithZeros(desiredLength: number, arr: number[]) { | ||
const currentLength: number = arr.length; | ||
const result: number[] = new Array(desiredLength); | ||
|
||
for (let i = 0; i < desiredLength; i++) { | ||
if (i < currentLength) { | ||
result[i] = arr[i]; | ||
} else { | ||
result[i] = 0; | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
/** | ||
* ProjectionOscillator calculates the Projection Oscillator (PO). The PO | ||
* uses the linear regression slope, along with highs and lows. | ||
* | ||
* Period defines the moving window to calculates the PO, and the smooth | ||
* period defines the moving windows to take EMA of PO. | ||
* | ||
* PL = Min(period, (high + MLS(period, x, high))) | ||
* PU = Max(period, (low + MLS(period, x, low))) | ||
* mHighs = MLS(period, x, high) | ||
* mLows = MLS(period, x, low) | ||
* PL[i] = Min(lows[i - period + 1, i] + mLows[i] * [0, period - 1]) | ||
* PU[i] = Max(highs[i - period + 1, i] + mHighs[i] * [0, period - 1]) | ||
* PO = 100 * (Closing - PL) / (PU - PL) | ||
* SPO = EMA(smooth, PO) | ||
* | ||
|
@@ -46,17 +64,64 @@ export function projectionOscillator( | |
smooth: number, | ||
highs: number[], | ||
lows: number[], | ||
closings: number[] | ||
closings: number[], | ||
): ProjectionOscillator { | ||
const x = generateNumbers(0, closings.length, 1); | ||
const lsHighs = movingLeastSquare(period, x, highs); | ||
const lsLows = movingLeastSquare(period, x, lows); | ||
// const vHighs = add(highs, multiply(lsHighs.m, x)); | ||
// const vLows = add(lows, multiply(lsLows.m, x)); | ||
|
||
const arr_period: number[] = generateNumbers(0, period, 1); | ||
const pu = new Array(closings.length); | ||
for (let i = 0; i < highs.length; i++) { | ||
// for (let j = 0; j < period; j++) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shall we remove these comments? |
||
// if (i < j) { | ||
// continue; | ||
// } | ||
// pu[i] = Math.max( | ||
// pu[i], | ||
// highs[i - j] + lsHighs.m[i] * arr_period[j], | ||
// ); | ||
// } | ||
/////////////////////////////////// | ||
// LOOK HERE | ||
/////////////////////////////////// | ||
const newHighs = padWithZeros( | ||
period, | ||
highs.slice(Math.max(0, i - period + 1), i + 1), | ||
); | ||
const vHighs = add(newHighs, multiplyBy(lsHighs.m[i], arr_period)); | ||
pu[i] = Math.max(...vHighs); | ||
} | ||
|
||
const pl = new Array(closings.length); | ||
for (let i = 0; i < lows.length; i++) { | ||
// for (let j = 0; j < period; j++) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shall we remove these comments? |
||
// if (i < j) { | ||
// continue; | ||
// } | ||
// pl[i] = Math.min( | ||
// pl[i], | ||
// lows[i - j] + lsLows.m[i] * arr_period[j], | ||
// ); | ||
// } | ||
/////////////////////////////////// | ||
// LOOK HERE | ||
/////////////////////////////////// | ||
const newLows = padWithZeros( | ||
period, | ||
lows.slice(Math.max(0, i - period + 1), i + 1), | ||
); | ||
const vLows = add(newLows, multiplyBy(lsLows.m[i], arr_period)); | ||
pl[i] = Math.min(...vLows); | ||
} | ||
|
||
const vHighs = add(highs, multiply(lsHighs.m, x)); | ||
const vLows = add(lows, multiply(lsLows.m, x)); | ||
console.log(pu); | ||
console.log(pl); | ||
|
||
const pu = mmax(period, vHighs); | ||
const pl = mmin(period, vLows); | ||
// const pu = mmax(period, vHighs); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shall we remove these comments? |
||
// const pl = mmin(period, vLows); | ||
|
||
const po = divide(multiplyBy(100, subtract(closings, pl)), subtract(pu, pl)); | ||
const spo = ema(smooth, po); | ||
|
@@ -77,7 +142,29 @@ export function projectionOscillator( | |
export function defaultProjectionOscillator( | ||
highs: number[], | ||
lows: number[], | ||
closings: number[] | ||
closings: number[], | ||
): ProjectionOscillator { | ||
return projectionOscillator(14, 3, highs, lows, closings); | ||
} | ||
function rng(mean: number, stdDev: number) { | ||
return mean + stdDev * (Math.random() - 0.5); | ||
} | ||
|
||
function generateRandomNumbers(count: number, mean: number, stdDev: number) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. May be the function can be named differently to reflect how you are using the mean and stdDev? I guess they are not just random number right? Also would be nice to have this in ../../helper/numArray.ts as well. |
||
const result: number[] = new Array(count); | ||
for (let i = 0; i < count; i++) { | ||
result[i] = rng(mean, stdDev); | ||
} | ||
return result; | ||
} | ||
|
||
const closings = generateRandomNumbers(100, 30, 5); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess some test code got left over here? |
||
const highs = addBy(0.5, closings); | ||
const lows = subtractBy(0.5, closings); | ||
|
||
const result = projectionOscillator(14, 5, highs, lows, closings); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since you have a test case here, can we put it into the unit test? |
||
|
||
// Check if any of the values are below 0 or above 100 in the result array | ||
console.log(result); | ||
console.log(result.po.some((v) => v < 0 || v > 100)); | ||
// This is returning false, which means that all the values are between 0 and 100 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a very nice helper function to add to ../../helper/numArray probably?