@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-coreQuick 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 breakdownData 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:
- Momentum (follow trend / buy pullback in uptrend, sell pullback in downtrend)
- 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*D1Higher 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_d1In code:
const entryTh = TH[\MOM_entry_${tf}] ?? TH.MOM_entry;(orMR_…), wheretf ∈ {'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_MINand-DI > +DI - PSAR (optional): value above price (bearish)
- Momentum score gap:
scores.sell ≥ SCORE_SELL_MINandscores.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 byEMA50 + ATR * SL_EMA50_BUFFER_ATR_MULTTP1 = entryRef - ATR * ATR_TP1_MULTTP2 = 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.9andStochRSI k<d(rolling over from overbought).
Entry atBB.upper,SL = +ATR*ATR_SL_MULT,TP1 = BB.middle,TP2 = entryRef - ATR*ATR_TP1_MULTMR BUY:
BB.position < 0.1andStochRSI k>d(hook from oversold).
Entry atBB.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& MTFtotalbyUNCONFIRMED_FACTOR(default 0.85). - Big bar: if
(high-low)/ATR > BIG_BAR_ATRthen subtractBIG_BAR_PENALTYfrom 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.directionagrees 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
EMA50overBB.middle) via config.
License
MIT — © linhquang
