Pure-function stock/candle analysis algorithms — technical indicators (SMA/EMA/MACD/RSI/OBV), trend-regime segmentation, and a rule-based trading strategy generator + backtest simulator. No DOM, no chart, no I/O — every export is a plain function/namespace operating on plain data.
trend.ts): SMA, EMA, MACD (line/signal/histogram), RSI, OBV as standalone series functions.TrendRange): turns a MACD+RSI+OBV bar series into contiguous, direction-labeled "zones" (up/down/side, or your owngroupBylabels), with a prefix-stable, deterministic algorithm — the same input prefix always produces the same committed zones even as more bars are appended.TradingSimulator):findBestConfigderives a small set of buy/sell conditions (MA cross, MACD cross, RSI overbought/oversold) from candle physics-style features (velocity/acceleration/force/gravity relative to a long MA, PCA-projected "regime" score) plus an inlined gradient-boosting direction model;simulatereplays those conditions bar-by-bar into an actual trade ledger (fees, cooldowns, batching, stop-loss/take-profit, insufficient-funds handling).@dooboostore/core(forHashUtils). Safe to use in Node, browser, or worker contexts.@dooboostore/algorithm's root (src/index.ts) re-exports exactly these modules:
Candle— theinterface CandleOHLCV bar type (stock/Candle.ts)trend.ts—computeSmaSeries,computeEmaSeries,computeMacdSeries,computeRsiSeries,computeObvSeriesTrendRange— namespace withscoreBars,strengthBars,trendRangesand their typesTradingSimulator— namespace withestimateMarketRate,forecast,directionProbability,findBestConfig,simulateand their typesboostModel.ts/boostModelMacro.ts(the inlined gradient-boosting trees used internally bydirectionProbability) arenotre-exported from the package root — they're auto-generated model weights (seeInternal Model Filesbelow), not a public API surface.
# pnpmpnpm add @dooboostore/algorithm# npmnpm install @dooboostore/algorithm# yarnyarn add @dooboostore/algorithmimport{ computeSmaSeries, computeRsiSeries, computeMacdSeries }from'@dooboostore/algorithm';constcloses = [10,10.5,11,10.8,11.2,11.5,11.3,11.8,12,12.4];constsma5 =computeSmaSeries(closes,5);// (number | null)[] — null until the window has formedconstrsi14 =computeRsiSeries(closes,14);// number[] — same length as input, seeded to 50 until enough barsconst{ macd, signal, hist } =computeMacdSeries(closes,12,26,9);import{TrendRange}from'@dooboostore/algorithm';// One bar per candle — macd/rsi/obv usually come from computeMacdSeries/computeRsiSeries/computeObvSeriesconstbars:TrendRange.TrendBar[] = closes.map((c, i) =>({macd: macd[i],rsi: rsi14[i],obv:0,
}));constzones =TrendRange.trendRanges(bars);// zones: TrendRange.TendRange[] — { startIndex, endIndex, scoreRate, strengthRate, group, uuid, confirmed, forecastRate }// all zones but the last are `confirmed: true` and are prefix-stable as new bars are appended.import{TradingSimulator}from'@dooboostore/algorithm';importtype{Candle}from'@dooboostore/algorithm';constcandles:Candle[] = [
{date:'d0',open:10,high:10,low:10,close:10,volume:1000},
{date:'d1',open:11,high:11,low:11,close:11,volume:1000},// ... at least a few dozen bars for directionProbability to leave its 0.5 (neutral) fallback];constindicators:TradingSimulator.IndicatorParams= {macdFast:12,macdSlow:26,macdSignal:9,rsiPeriod:14,rsiOb:70,rsiOs:30,maShort:5,maMid:10,maLong:60,maExponential:false,
};// 1) Derive buy/sell conditions from the candlesconstplan =TradingSimulator.findBestConfig(candles, {strategyRate:0.5, indicators });// plan: { conditions, applyMode: 'min'|'max'|'combined', conviction, upProbability, execPolicy? }// 2) Replay the plan against the same (or a later/expanding) candle windowconstresult =TradingSimulator.simulate({
candles,config: plan,history: [],// prior TradingSimulator.UserTrade[]; [] for a fresh runindicators,capital:1_000_000,fee:0.001,
});// result: { trades: TradingSimulator.UserTrade[], returnPct: number, batchState }Candle(stock/Candle.ts)interfaceCandle{date:string;open:number;high:number;low:number;close:number;volume:number;
}The common OHLCV input type accepted by every other function in this package.
stock/trend.ts)All functions are pure and take/return plain arrays — no shared state between calls.
| Function | Signature | Notes |
|---|---|---|
computeSmaSeries | (closes: number[], period: number) => (number | null)[] | Simple moving average;nulluntil the window has fully formed. |
computeEmaSeries | (values: number[], period: number) => number[] | Exponential moving average; seeded with the first value, defined over the whole input. |
computeMacdSeries | (closes: number[], fast: number, slow: number, sig: number) => { macd: number[]; signal: number[]; hist: number[] } | macd = EMA(fast) - EMA(slow),signal = EMA(macd, sig),hist = macd - signal. Auto-correctsslowtofast + 1ifslow <= fast. |
computeRsiSeries | (closes: number[], period: number) => number[] | Wilder's RSI (smoothed average gain/loss). Returns50for the warm-up region. |
computeObvSeries | (closes: number[], volumes: number[]) => number[] | On-Balance Volume — cumulative signed volume by close-to-close direction. |
TrendRange(stock/TrendRange.ts)Turns per-bar MACD/RSI/OBV (optionally OHLC too) into contiguous, labeled trend zones. Everything is a pure function over arrays — no DOM/chart/engine dependency.
Types:
TrendRange.TrendBar—{ macd, rsi, obv, open?, high?, low?, close? }(allnumber | null, OHLC optional)TrendRange.TendRange— one output zone:{ startIndex, endIndex, scoreRate, strengthRate, group, uuid, confirmed, forecastRate }scoreRate(0~1): direction — 0 = bearish, 0.5 = neutral, 1 = bullish (average of the bar-level scores in the zone)strengthRate(0~1): trend strength regardless of direction — 0 = ranging, 1 = strongly trendingconfirmed:falseonly for the last zone (the "live edge") — it can still change shape as new bars arrive; every earlier zone is frozen and prefix-stableforecastRate: linear extrapolation of the last up-to-3 confirmed zones'scoreRateslope (causal — only ever looks atpriorconfirmed zones), ornullif there's no prior zoneuuid: deterministic hash (HashUtils.hash53) of the zone's full bar data — identical input always yields the identical idTrendRange.TrendRangeConfig— tuning knobs, all optional:obvPeriod(default 10),macdSignalMa(default 9),strengthPeriod(default 10),mergeCut(default 10 — shorter closed runs get absorbed into the next zone),decisiveCut(default 0.15 — a short run this decisive still freezes immediately instead of being absorbed),groupBy?: (rate, strength) => string(default:String(rate)— your own labeling function is also the sole merge criterion: adjacent runs only fuse when their materialized label is equal)Functions:
TrendRange.scoreBars(bars, config?): (number | null)[]— per-bar direction score (0~1), averaging whichever of {MACD-vs-zero-and-signal, RSI/100, OBV-vs-N-bar-average} are present on that bar.TrendRange.strengthBars(bars, scores, config?): (number | null)[]— per-bar strength (0~1), averaging distance-from-neutral, candle body ratio, and (if OHLC present) N-bar displacement-over-range efficiency.TrendRange.trendRanges(bars, config?): TendRange[]— the single entry point: scores + groups bars, merges/freezes runs left-to-right in one pass, and fills inforecastRate. Seetest/trendrange.test.tsandtest/trendrange-stability.test.tsfor the exact freeze/merge/prefix-stability contract this function guarantees.TradingSimulator(stock/TradingSimulator.ts)A two-step "generate a plan, then replay it" trading engine.
TradingSimulator.findBestConfig(candles, options?): AutoTradeResult
Derives trade conditions from a physics-flavored feature set computed over the candle window — price velocity/acceleration (via Catmull-Rom-smoothed closes), volume as "mass", force = mass × acceleration, "gravity" pull toward the long MA, and a PCA-projected regime score over those — blended with an inlined gradient-boostingdirectionProbabilitymodel.
options.strategyRate?: number(0~1, default 0.5) — 0 leans toward averaging down on dips, 1 leans toward adding to winnersoptions.marketRate?: number(0~1, default 0.5) — overall market condition; shiftsapplyMode(< 0.35→'min',> 0.65→'max', else'combined') and position-size scalingoptions.indicators?: IndicatorParams—{ macdFast, macdSlow, macdSignal, rsiPeriod, rsiOb, rsiOs, maShort, maMid, maLong, maExponential }options.macro?: MarketSeries— optional{ tnx, wti, spx }external-factor series (index-aligned withcandles); when present,directionProbabilityuses a 13-feature model instead of the base 10-feature oneoptions.invertActions?: boolean— flips every generated condition'sbuy/selloptions.tuning?: TuningOverrides— advanced knobs (quietBarsAdd,percentCapScale,cooldownScale,gateShift,maxConsecBars,consecRestBars,batchBars,minBudgetEquityPct,minEdge,convictionBlend) — all default to the current production behavior when omittedAutoTradeResult:{ conditions: TradeCondition[], applyMode, conviction, upProbability, execPolicy? }TradingSimulator.simulate(options): SimulateResult
ReplaysAutoTradeResult.conditionsbar-by-bar againstoptions.candles, one order at most per bar.
options.candles,options.config(anAutoTradeResult),options.history: UserTrade[](prior trades —initial: trueentries seed a starting position without moving cash),options.indicators,options.capital?,options.fee?(0~1, taken on both buy and sell)options.risk?: { stopLossPct?, takeProfitPct? }— checked before condition evaluation each bar, against the running average cost basisoptions.fromBar?/options.batchState?— resume points for incremental/streaming re-simulation of a growing candle window{ trades: UserTrade[], returnPct: number, batchState }—trades[].actionis'buy' | 'sell' | 'buy-failed' | 'sell-failed', with areasonstring literal (Korean values, seesrc/stock/TradingSimulator.ts):'잔액부족'insufficient funds,'보유없음'nothing to sell,'소액제외'order belowminBudgetEquityPctof total equity (all on*-failed), and'스탑로스'on a stop-losssell; pluscondition/candidatessnapshots of what firedStandalone helpers:
TradingSimulator.estimateMarketRate(candles, indicators?): number— 0~1 market-condition estimate (long-MA slope + volatility penalty + RSI position), meant as a drop-in for themarketRateoption above.TradingSimulator.forecast(candles, maSize, market?): number[]— naive near-term close forecast: reverses the lastmaSizebar-over-bar return rates and compounds them forward from the last close, with damping scaled bydirectionProbability's confidence.TradingSimulator.directionProbability(candles, market?): number— 0~1 probability of being up 5 bars from now; returns0.5(neutral) until at least 30 bars of history exist.Seetest/engine.test.tsfor further worked examples ofsimulate(partial fills,applyModevariants, cooldowns, batching) andtest/trendrange.test.ts/test/trendrange-stability.test.tsforTrendRange's exact merge/freeze/prefix-stability guarantees.
stock/boostModel.tsandstock/boostModelMacro.tshold auto-generated gradient-boosting tree weights (exported from a Pythonsklearn.HistGradientBoostingClassifier, per their own header comments) plus the smallpredictBoostLogOdds/predictMacroLogOddsevaluators.TradingSimulator.directionProbabilityimports and calls these directly; they arenotre-exported fromsrc/index.ts, are not meant to be consumed directly, and their bin/tree arrays are not hand-authored data you should read as documentation.
HashUtils)StockChartcomponent imports this package's indicator functions (computeMacdSeries,computeRsiSeries,computeObvSeries,computeSmaSeries,computeEmaSeries) directlyThis package is licensed under theMIT License.