npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@nguyenquang1986/ai-core

v0.1.26

Published

AI-core shared library: pure indicator functions, domain types, and utilities.

Downloads

626

Readme

@linhquang/ai-core

A shared library for building systematic FX/CFD strategies on top of multi‑timeframe (M5 → D1) signals. It provides:

  • Pure indicator functions (BB, ATR, EMA, …)
  • Domain types & constants (timeframes, strategies, weights, thresholds)
  • A decision engine that blends Momentum & Mean‑Reversion logic across multiple timeframes
  • UI‑friendly scoring for transparent decisions

All logic is designed to work directly with the dataset shape you’re producing (candle, indicators[], signal.*).


Install

npm install @linhquang/ai-core

Quick Start

// Indicators
import { calcBB } from '@linhquang/ai-core/core/indicators/bb';
import { calcATR } from '@linhquang/ai-core/core/indicators/atr';
import { calcEMA } from '@linhquang/ai-core/core/indicators/ema';

// Utils & Domain
import { getIndicatorFromFrame } from '@linhquang/ai-core/core/utils';
import { TimeFrame } from '@linhquang/ai-core/core/domain/timeframe';

// Engine (decision & scoring for UI)
import { decideAuto, decideAllWithScores } from '@linhquang/ai-core/core/engine/decision';
// Given "allFrames" is a map of timeframe -> analyzed frame (your Redis payloads)
import type { AllFrames } from '@linhquang/ai-core/core/engine/decision';

const result = decideAllWithScores(allFrames);
console.log(result.best);           // => best { timeframe, decision }
console.log(result.byFrame.M15);    // => per‑TF decision + score breakdown

Data Model (what the engine expects)

type FramePacket = {
  candle: {
    timestamp: string;
    timeframe: '5m' | '15m' | '30m' | '1h' | '4h' | '1d';
    open: number; high: number; low: number; close: number;
    confirmed: boolean;
  };
  indicators: Array<{
    indicatorType: 'BB'|'ATR'|'EMA50'|'EMA200'|'ADX'|'TSADX'|'PSAR'|'SUPPORT_RESISTANCE'|'STOCH'|'STOCH_RSI'|'SUPER_TREND'|'RSI'|string;
    // … indicator-specific fields (value, upper/lower/middle, adx/pdi/mdi, signal1/2, bandwidth, position, …)
  }>;
  signal?: {
    // optional, if your upstream already computes single‑TF momentum/MR scores
    score?: {
      buy?:    { total: number };
      sell?:   { total: number };
      buyMR?:  { total: number };
      sellMR?: { total: number };
    };
    candlePattern?: { direction?: 'BULLISH'|'BEARISH'|'NEUTRAL', category?: string };
    trend?: any;
  };
};

Minimum fields used by the engine:

  • EMA50 / EMA200: .value
  • ADX / TSADX: .adx, .pdi, .mdi
  • BB: .middle, .upper, .lower, .position (0..1)
  • ATR: .value
  • SUPER_TREND: .signal1, .signal2
  • PSAR: .value (optional)
  • SUPPORT_RESISTANCE (optional for R/R idea)
  • STOCH_RSI: .k, .d (for MR hooks)
  • candlePattern (optional, only a small confirmation weight)

Strategy Model

Two complementary strategies are supported on each TF:

  1. Momentum (follow trend / buy pullback in uptrend, sell pullback in downtrend)
  2. Mean‑Reversion (MR) (fade extremes when volatility/trend strength are low)

The engine can run both and return the best actionable decision per TF, then rank TFs to pick the best overall entry window for execution.

Multi‑Timeframe (MTF) Context

Each entry TF blends higher TF context via a convex combination:

m5 : 0.60*m5 + 0.25*m15 + 0.15*m30
m15: 0.60*m15 + 0.25*m30 + 0.15*H1
m30: 0.60*m30 + 0.25*H1  + 0.15*H4
H1 : 0.75*H1  + 0.25*H4
H4 : 1.00*H4
D1 : 1.00*D1

Higher TFs act as context (trend, structure). Entry logic (zones/SL/TP) is taken from the selected TF.


Thresholds (TH) — dynamic & per‑TF

We provide global defaults and per‑TF overrides for both strategies:

const DEFAULT_THRESHOLDS = {
  // global
  momentum: { entry: 75, total: 80 },
  mr:       { entry: 70, total: 75 },

  // per‑TF overrides
  momentum_m5:  { entry: 80, total: 85 },
  momentum_m15: { entry: 78, total: 83 },
  momentum_m30: { entry: 75, total: 80 },
  momentum_h1:  { entry: 74, total: 79 },
  momentum_h4:  { entry: 72, total: 78 },
  momentum_d1:  { entry: 75, total: 80 },

  mr_m5:  { entry: 75, total: 80 },
  mr_m15: { entry: 72, total: 78 },
  mr_m30: { entry: 70, total: 75 },
  mr_h1:  { entry: 68, total: 74 },
  mr_h4:  { entry: 66, total: 72 },
  mr_d1:  { entry: 70, total: 75 },
};

They are normalized at runtime to a flat map TH:

MOM_entry, MOM_total,
MOM_entry_m5, MOM_total_m5, … MOM_entry_d1, MOM_total_d1,
MR_entry, MR_total,
MR_entry_m5, MR_total_m5, … MR_entry_d1, MR_total_d1

In code: const entryTh = TH[\MOM_entry_${tf}] ?? TH.MOM_entry;(orMR_…), where tf ∈ {'5m','15m','30m','1h','4h','1d'}`.


Decision Rules (per TF)

1) Trend‑Following SELL (symmetric BUY)

SELL is constructed when all of these hold:

  • Structure: close < EMA50 < EMA200
  • Momentum strength: ADX ≥ ADX_TREND_MIN and -DI > +DI
  • PSAR (optional): value above price (bearish)
  • Momentum score gap: scores.sell ≥ SCORE_SELL_MIN and scores.sell - max(scores.buy, scores.buyMR) ≥ SCORE_GAP_MIN
  • HTF Bias not contradicting (i.e. not BULL)

Entry Zones (pullback references): BB.middle, SUPER_TREND.signal2, SUPER_TREND.signal1, EMA50.
Pick the nearest reference above price (or nearest overall if none above).

Stops/Targets:

  • SL = entryRef + ATR * ATR_SL_MULT, bounded by EMA50 + ATR * SL_EMA50_BUFFER_ATR_MULT
  • TP1 = entryRef - ATR * ATR_TP1_MULT
  • TP2 = entryRef - ATR * ATR_TP2_MULT

Order type:

  • MARKET if |close - entryRef| ≤ MARKET_PROXIMITY_ATR * ATR
  • else SELL_LIMIT at entryRef

BUY rules mirror SELL with direction swapped.

2) Mean‑Reversion (MR)

Active only when trend is weak: ADX < MR_ADX_MAX.

  • MR SELL: BB.position > 0.9 and StochRSI k<d (rolling over from overbought).
    Entry at BB.upper, SL = +ATR*ATR_SL_MULT, TP1 = BB.middle, TP2 = entryRef - ATR*ATR_TP1_MULT

  • MR BUY: BB.position < 0.1 and StochRSI k>d (hook from oversold).
    Entry at BB.lower, SL = -ATR*ATR_SL_MULT, TP1 = BB.middle, TP2 = entryRef + ATR*ATR_TP1_MULT

BUY_DIP is the momentum variant that buys pullback in uptrend. In practice it selects the nearest anchor below price among BB.middle, ST.s2/s1, EMA50, then applies the momentum stop/targets above.

3) Penalties / Gates

  • Unconfirmed candle: multiply entryScore & MTF total by UNCONFIRMED_FACTOR (default 0.85).
  • Big bar: if (high-low)/ATR > BIG_BAR_ATR then subtract BIG_BAR_PENALTY from entry score.
  • (Optional) MR guard in strong trend: if ADX> threshold but no BB overshoot, down‑weight MR entries.

UI Scoring (for transparent ranking)

Function computeScore(decision) produces a 0..1 score and a breakdown:

  • Trend: normalized momentum score for the chosen direction
    pTrend = clamp01(trendScore / 100)
  • Gap: advantage vs opposite side
    pGap = clamp01(max(0, trendScore - oppScore) / 30)
  • ADX: trend strength
    pAdx = clamp01((ADX - ADX_TREND_MIN) / 20)
  • R/R: best of TP1/TP2 vs SL (if an order is formed)
    pRR = clamp01(rrBest / 2)
  • Proximity: closeness of current price to the entry reference
    pProx = clamp01(1 - |close-entry|/(0.5*ATR))
  • Candle: 1 if candlePattern.direction agrees with the chosen side (else 0)
  • Align: agreement with HTF bias (BULL with BUY or BEAR with SELL → 1; opposite → 0; neutral → 0.5)

Total score (weights chosen for UX):

score =
  0.30*Trend +
  0.15*Gap   +
  0.15*ADX   +
  0.20*R/R   +
  0.10*Prox  +
  0.10*Candle+
  0.20*Align

“BB pos” shown in snapshots is informational; it only feeds MR detection (pos > 0.9 or < 0.1), not the breakdown bars.


Configuration

const CFG = {
  BIAS_TF: ['1h','4h'],    // timeframes to compute HTF bias

  ADX_TREND_MIN: 30,       // momentum require at least this ADX
  ADX_STRONG: 45,          // very strong trend (rarely used by default)
  MR_ADX_MAX: 25,          // MR allowed only when ADX below this

  SCORE_SELL_MIN: 55,      // minimal raw momentum score for trend‑follow
  SCORE_GAP_MIN: 15,       // min gap vs opposite side

  ATR_SL_MULT: 1.3,        // stop distance
  ATR_TP1_MULT: 1.0,       // first target
  ATR_TP2_MULT: 1.5,       // second target
  SL_EMA50_BUFFER_ATR_MULT: 0.2, // ensure SL beyond EMA50 a bit

  MARKET_PROXIMITY_ATR: 0.10, // switch to MARKET if close to reference
};

You can override these per deployment or symbol profile.


API

decideForTf(all: AllFrames, entryTf: TimeFrame): Decision

Builds a specific decision for a single TF (BUY/SELL/LIMIT/MARKET/SL/TP) or returns WAIT with reasons & diagnostics.

decideAll(all: AllFrames, frames?: TimeFrame[])

Runs decideForTf on multiple TFs and returns { [tf]: Decision }.

decideAllWithScores(all: AllFrames, frames?: TimeFrame[])

Returns decisions + UI scores and a ranked list. Shape:

{
  byFrame: Partial<Record<TimeFrame, DecisionWithScore>>;
  ranked: Array<{ timeframe: TimeFrame, decision: DecisionWithScore }>;
  best: { timeframe: TimeFrame|null, decision: DecisionWithScore|null };
}

decideAuto(all: AllFrames): Decision

Picks the best TF using decideAllWithScores (ties broken by a preferred TF order).


Extending

  • Add candle‑pattern weighting per TF (e.g. give 2–6 points when reversal appears at BB edge).
  • Calibrate thresholds (TH) per symbol or session (Asia/EU/US) without touching engine code.
  • Swap anchors for BUY_DIP/Sell‑rally (e.g. prefer EMA50 over BB.middle) via config.

License

MIT — © linhquang