import { functor , merge , path } from "../utils" ; import atr from "./atr" ; import { Kagi as defaultOptions } from "./defaultOptionsForComputation" ; export default function ( ) { let options = defaultOptions ; let dateAccessor = ( d ) => d . date ; let dateMutator = ( d , date ) => { d . date = date ; } ; const calculator = ( data ) => { const { reversalType , windowSize , reversal , sourcePath } = options ; const source = path ( sourcePath ) ; let reversalThreshold ; if ( reversalType === "ATR" ) { const atrAlgorithm = atr ( ) . options ( { windowSize } ) ; const atrCalculator = merge ( ) . algorithm ( atrAlgorithm ) . merge ( ( d , c ) => { d [ "atr" + windowSize ] = c ; } ) ; atrCalculator ( data ) ; reversalThreshold = ( d ) => d [ "atr" + windowSize ] ; } else { reversalThreshold = functor ( reversal ) ; } const kagiData = [ ] ; let prevPeak ; let prevTrough ; let direction ; let line = { } ; data . forEach ( function ( d ) { if ( line . from === undefined ) { dateMutator ( line , dateAccessor ( d ) ) ; line . from = dateAccessor ( d ) ; if ( ! line . open ) { line . open = d . open ; } line . high = d . high ; line . low = d . low ; if ( ! line . close ) { line . close = source ( d ) ; } line . startOfYear = d . startOfYear ; line . startOfQuarter = d . startOfQuarter ; line . startOfMonth = d . startOfMonth ; line . startOfWeek = d . startOfWeek ; } if ( ! line . startOfYear ) { line . startOfYear = d . startOfYear ; if ( line . startOfYear ) { line . date = d . date ; } } if ( ! line . startOfQuarter ) { line . startOfQuarter = d . startOfQuarter ; if ( line . startOfQuarter && ! line . startOfYear ) { line . date = d . date ; } } if ( ! line . startOfMonth ) { line . startOfMonth = d . startOfMonth ; if ( line . startOfMonth && ! line . startOfQuarter ) { line . date = d . date ; } } if ( ! line . startOfWeek ) { line . startOfWeek = d . startOfWeek ; if ( line . startOfWeek && ! line . startOfMonth ) { line . date = d . date ; } } line . volume = ( line . volume || 0 ) + d . volume ; line . high = Math . max ( line . high , d . high ) ; line . low = Math . min ( line . low , d . low ) ; line . to = dateAccessor ( d ) ; const priceMovement = source ( d ) - line . close ; if ( ( line . close >= line . open && priceMovement > 0 ) || ( line . close < line . open && priceMovement < 0 ) ) { line . close = source ( d ) ; if ( prevTrough && line . close < prevTrough ) { line . changePoint = prevTrough ; if ( line . startAs !== "yin" ) { line . changeTo = "yin" ; } } if ( prevPeak && line . close > prevPeak ) { line . changePoint = prevPeak ; if ( line . startAs !== "yang" ) { line . changeTo = "yang" ; } } } else if ( ( line . close >= line . open && priceMovement < 0 && Math . abs ( priceMovement ) > reversalThreshold ( d ) ) || ( line . close < line . open && priceMovement > 0 && Math . abs ( priceMovement ) > reversalThreshold ( d ) ) ) { const nextLineOpen = line . close ; direction = ( line . close - line . open ) / Math . abs ( line . close - line . open ) ; let nextChangePoint ; let nextChangeTo ; if ( direction < 0 ) { if ( prevPeak === undefined ) { prevPeak = line . open ; } prevTrough = line . close ; if ( source ( d ) > prevPeak ) { nextChangePoint = prevPeak ; nextChangeTo = "yang" ; } } else { if ( prevTrough === undefined ) { prevTrough = line . open ; } prevPeak = line . close ; if ( source ( d ) < prevTrough ) { nextChangePoint = prevTrough ; nextChangeTo = "yin" ; } } if ( line . startAs === undefined ) { line . startAs = direction > 0 ? "yang" : "yin" ; } const startAs = line . changeTo || line . startAs ; line . added = true ; kagiData . push ( line ) ; direction = - 1 * direction ; line = { ... line } ; line . open = nextLineOpen ; line . close = source ( d ) ; line . startAs = startAs ; line . changePoint = nextChangePoint ; line . changeTo = nextChangeTo ; line . added = false ; line . from = undefined ; line . volume = 0 ; } else { } line . current = source ( d ) ; let dir = line . close - line . open ; dir = dir === 0 ? 1 : dir / Math . abs ( dir ) ; line . reverseAt = dir > 0 ? line . close - reversalThreshold ( d ) : line . open - reversalThreshold ( d ) ; } ) ; if ( ! line . added ) { kagiData . push ( line ) ; } return kagiData ; } ; calculator . options = ( newOptions ? ) => { if ( newOptions === undefined ) { return options ; } options = { ... defaultOptions , ... newOptions } ; return calculator ; } ; calculator . dateMutator = ( newDateMutator ? ) => { if ( newDateMutator === undefined ) { return dateMutator ; } dateMutator = newDateMutator ; return calculator ; } ; calculator . dateAccessor = ( newDateAccessor ? ) => { if ( newDateAccessor === undefined ) { return dateAccessor ; } dateAccessor = newDateAccessor ; return calculator ; } ; return calculator ; }