Common trading concepts, terminology, data structures, and utility functions shared across all trading strategies (ICT, AMT, etc.)
This skill provides common terminology, data structures, and utility functions used across all trading strategies. Reference this skill when you need standardized definitions or reusable code patterns.
Use this skill when:
Trend Definitions:
Swing Points:
TypeScript Implementation:
enum Trend {
UPTREND = 'uptrend',
DOWNTREND = 'downtrend',
RANGING = 'ranging'
}
interface SwingPoint {
type: 'high' | 'low';
index: number; // Candle index
price: number;
timestamp: number;
}
// Detect swing highs and lows using lookback window
function findSwingPoints(candles: Candle[], lookback: number = 5): SwingPoint[] {
const swings: SwingPoint[] = [];
for (let i = lookback; i < candles.length - lookback; i++) {
const current = candles[i];
const leftWindow = candles.slice(i - lookback, i);
const rightWindow = candles.slice(i + 1, i + lookback + 1);
// Swing high: current high is higher than all candles in window
const isSwingHigh =
leftWindow.every(c => current.high > c.high) &&
rightWindow.every(c => current.high > c.high);
if (isSwingHigh) {
swings.push({
type: 'high',
index: i,
price: current.high,
timestamp: current.timestamp
});
}
// Swing low: current low is lower than all candles in window
const isSwingLow =
leftWindow.every(c => current.low < c.low) &&
rightWindow.every(c => current.low < c.low);
if (isSwingLow) {
swings.push({
type: 'low',
index: i,
price: current.low,
timestamp: current.timestamp
});
}
}
return swings;
}
// Determine current trend from swings
function determineTrend(swings: SwingPoint[]): Trend {
if (swings.length < 4) return Trend.RANGING;
const recentSwings = swings.slice(-4); // Last 4 swings
const highs = recentSwings.filter(s => s.type === 'high');
const lows = recentSwings.filter(s => s.type === 'low');
if (highs.length >= 2 && lows.length >= 2) {
const isHigherHighs = highs[1].price > highs[0].price;
const isHigherLows = lows[1].price > lows[0].price;
if (isHigherHighs && isHigherLows) return Trend.UPTREND;
const isLowerHighs = highs[1].price < highs[0].price;
const isLowerLows = lows[1].price < lows[0].price;
if (isLowerHighs && isLowerLows) return Trend.DOWNTREND;
}
return Trend.RANGING;
}
Definitions:
Detection:
interface SupportResistanceLevel {
price: number;
strength: number; // Number of touches
touches: number[]; // Candle indices where touched
type: 'support' | 'resistance';
}
function findSupportResistance(
candles: Candle[],
tolerance: number = 0.001 // 0.1% tolerance for "same" price
): SupportResistanceLevel[] {
const levels: SupportResistanceLevel[] = [];
// Group swing highs and lows into clusters
const swings = findSwingPoints(candles);
const clusters = clusterPrices(
swings.map(s => s.price),
tolerance
);
clusters.forEach(cluster => {
const touchIndices = swings
.filter(s => Math.abs(s.price - cluster.price) / s.price <= tolerance)
.map(s => s.index);
if (touchIndices.length >= 2) { // At least 2 touches to be valid
// Determine if support or resistance by context
const avgSwingType = swings
.filter(s => touchIndices.includes(s.index))
.reduce((sum, s) => sum + (s.type === 'high' ? 1 : 0), 0) / touchIndices.length;
levels.push({
price: cluster.price,
strength: touchIndices.length,
touches: touchIndices,
type: avgSwingType > 0.5 ? 'resistance' : 'support'
});
}
});
return levels.sort((a, b) => b.strength - a.strength); // Sort by strength
}
function clusterPrices(prices: number[], tolerance: number): { price: number; count: number }[] {
const clusters: { price: number; count: number }[] = [];
const used = new Set<number>();
prices.forEach((price, i) => {
if (used.has(i)) return;
const cluster: number[] = [price];
used.add(i);
for (let j = i + 1; j < prices.length; j++) {
if (used.has(j)) continue;
if (Math.abs(price - prices[j]) / price <= tolerance) {
cluster.push(prices[j]);
used.add(j);
}
}
clusters.push({
price: cluster.reduce((sum, p) => sum + p, 0) / cluster.length, // Average price
count: cluster.length
});
});
return clusters;
}
Candle (OHLCV):
interface Candle {
timestamp: number; // Unix timestamp in milliseconds
open: number;
high: number;
low: number;
close: number;
volume: number;
}
// Helper functions
function isBullish(candle: Candle): boolean {
return candle.close > candle.open;
}
function isBearish(candle: Candle): boolean {
return candle.close < candle.open;
}
function candleRange(candle: Candle): number {
return candle.high - candle.low;
}
function candleBody(candle: Candle): number {
return Math.abs(candle.close - candle.open);
}
function upperWick(candle: Candle): number {
return candle.high - Math.max(candle.open, candle.close);
}
function lowerWick(candle: Candle): number {
return Math.min(candle.open, candle.close) - candle.low;
}
Trade:
interface Trade {
id: string;
symbol: string;
direction: 'long' | 'short';
entry: {
price: number;
timestamp: number;
size: number; // Contracts or shares
};
exit?: {
price: number;
timestamp: number;
reason: 'take_profit' | 'stop_loss' | 'manual';
};
stopLoss: number;
takeProfit: number;
strategy: string;
status: 'open' | 'closed';
pnl?: number; // Profit/loss
}
function calculatePnL(trade: Trade, pointValue: number): number | null {
if (!trade.exit) return null;
const direction = trade.direction === 'long' ? 1 : -1;
const priceChange = (trade.exit.price - trade.entry.price) * direction;
return priceChange * trade.entry.size * pointValue;
}
Order:
enum OrderType {
MARKET = 'market',
LIMIT = 'limit',
STOP = 'stop',
STOP_LIMIT = 'stop_limit'
}
enum OrderSide {
BUY = 'buy',
SELL = 'sell'
}
enum OrderStatus {
PENDING = 'pending',
FILLED = 'filled',
CANCELLED = 'cancelled',
REJECTED = 'rejected'
}
interface Order {
id: string;
symbol: string;
type: OrderType;
side: OrderSide;
quantity: number;
price?: number; // For limit orders
stopPrice?: number; // For stop orders
status: OrderStatus;
filledQuantity: number;
timestamp: number;
}
type Timeframe = '1m' | '5m' | '15m' | '30m' | '1h' | '4h' | '1d';
function timeframeToMinutes(timeframe: Timeframe): number {
const map: Record<Timeframe, number> = {
'1m': 1,
'5m': 5,
'15m': 15,
'30m': 30,
'1h': 60,
'4h': 240,
'1d': 1440
};
return map[timeframe];
}
// Resample lower timeframe candles to higher timeframe
function resampleCandles(candles: Candle[], targetTimeframe: Timeframe): Candle[] {
const minutes = timeframeToMinutes(targetTimeframe);
const periodMs = minutes * 60 * 1000;
const resampled: Candle[] = [];
let currentPeriod: Candle[] = [];
let periodStart = 0;
candles.forEach(candle => {
const candlePeriod = Math.floor(candle.timestamp / periodMs);
if (periodStart === 0) {
periodStart = candlePeriod;
}
if (candlePeriod === periodStart) {
currentPeriod.push(candle);
} else {
// Aggregate current period
if (currentPeriod.length > 0) {
resampled.push(aggregateCandles(currentPeriod));
}
// Start new period
currentPeriod = [candle];
periodStart = candlePeriod;
}
});
// Aggregate final period
if (currentPeriod.length > 0) {
resampled.push(aggregateCandles(currentPeriod));
}
return resampled;
}
function aggregateCandles(candles: Candle[]): Candle {
return {
timestamp: candles[0].timestamp,
open: candles[0].open,
high: Math.max(...candles.map(c => c.high)),
low: Math.min(...candles.map(c => c.low)),
close: candles[candles.length - 1].close,
volume: candles.reduce((sum, c) => sum + c.volume, 0)
};
}
// Round price to valid tick size
function roundToTick(price: number, tickSize: number): number {
return Math.round(price / tickSize) * tickSize;
}
// Get instrument tick size
function getTickSize(symbol: string): number {
const tickSizes: Record<string, number> = {
'ES': 0.25, // E-mini S&P 500
'NQ': 0.25, // E-mini Nasdaq
'YM': 1.00, // E-mini Dow
'RTY': 0.10, // E-mini Russell 2000
// Add more as needed
};
return tickSizes[symbol] || 0.01;
}
// Get instrument point value (dollar value per point)
function getPointValue(symbol: string): number {
const pointValues: Record<string, number> = {
'ES': 50, // $50 per point
'NQ': 20, // $20 per point
'YM': 5, // $5 per point
'RTY': 50, // $50 per point
};
return pointValues[symbol] || 1;
}
// Calculate distance in ticks
function tickDistance(price1: number, price2: number, tickSize: number): number {
return Math.abs(price1 - price2) / tickSize;
}
// Average True Range (ATR)
function calculateATR(candles: Candle[], period: number = 14): number {
if (candles.length < period + 1) {
throw new Error(`Need at least ${period + 1} candles for ATR`);
}
const trueRanges: number[] = [];
for (let i = 1; i < candles.length; i++) {
const current = candles[i];
const previous = candles[i - 1];
const tr = Math.max(
current.high - current.low,
Math.abs(current.high - previous.close),
Math.abs(current.low - previous.close)
);
trueRanges.push(tr);
}
// Simple average of true ranges
const recentTRs = trueRanges.slice(-period);
return recentTRs.reduce((sum, tr) => sum + tr, 0) / period;
}
// Standard Deviation
function standardDeviation(values: number[]): number {
const mean = values.reduce((sum, v) => sum + v, 0) / values.length;
const squaredDiffs = values.map(v => Math.pow(v - mean, 2));
const variance = squaredDiffs.reduce((sum, d) => sum + d, 0) / values.length;
return Math.sqrt(variance);
}
// Simple Moving Average (SMA)
function calculateSMA(candles: Candle[], period: number, priceType: 'close' | 'high' | 'low' = 'close'): number {
if (candles.length < period) {
throw new Error(`Need at least ${period} candles for SMA`);
}
const recent = candles.slice(-period);
const sum = recent.reduce((s, c) => s + c[priceType], 0);
return sum / period;
}
// Exponential Moving Average (EMA)
function calculateEMA(candles: Candle[], period: number, priceType: 'close' | 'high' | 'low' = 'close'): number {
if (candles.length < period) {
throw new Error(`Need at least ${period} candles for EMA`);
}
const multiplier = 2 / (period + 1);
let ema = calculateSMA(candles.slice(0, period), period, priceType); // Start with SMA
for (let i = period; i < candles.length; i++) {
ema = (candles[i][priceType] - ema) * multiplier + ema;
}
return ema;
}
// Convert timestamp to EST/New York time
function toEST(timestamp: number): Date {
const date = new Date(timestamp);
return new Date(date.toLocaleString('en-US', { timeZone: 'America/New_York' }));
}
// Check if timestamp is during market hours
function isDuringMarketHours(timestamp: number, symbol: string): boolean {
const est = toEST(timestamp);
const hour = est.getHours();
const dayOfWeek = est.getDay(); // 0 = Sunday, 6 = Saturday
// Weekend check
if (dayOfWeek === 0 || dayOfWeek === 6) return false;
// Regular trading hours for ES/NQ (9:30 AM - 4:00 PM EST)
if (symbol === 'ES' || symbol === 'NQ') {
return hour >= 9.5 && hour < 16;
}
return false;
}
// Get session start time
function getSessionStart(date: Date, symbol: string): Date {
const est = new Date(date.toLocaleString('en-US', { timeZone: 'America/New_York' }));
// ES/NQ regular session starts at 9:30 AM EST
if (symbol === 'ES' || symbol === 'NQ') {
est.setHours(9, 30, 0, 0);
}
return est;
}
Definition: Rapid, strong price movement indicating institutional activity.
Detection:
interface Displacement {
startIndex: number;
endIndex: number;
direction: 'up' | 'down';
strength: number; // Magnitude of move
}
function detectDisplacement(
candles: Candle[],
minConsecutive: number = 3,
minPercentMove: number = 0.005 // 0.5%
): Displacement[] {
const displacements: Displacement[] = [];
for (let i = minConsecutive; i < candles.length; i++) {
const window = candles.slice(i - minConsecutive, i);
// Bullish displacement: all bullish candles in window
const allBullish = window.every(c => c.close > c.open);
if (allBullish) {
const move = (window[window.length - 1].close - window[0].open) / window[0].open;
if (Math.abs(move) >= minPercentMove) {
displacements.push({
startIndex: i - minConsecutive,
endIndex: i,
direction: 'up',
strength: move
});
}
}
// Bearish displacement: all bearish candles in window
const allBearish = window.every(c => c.close < c.open);
if (allBearish) {
const move = (window[0].open - window[window.length - 1].close) / window[0].open;
if (Math.abs(move) >= minPercentMove) {
displacements.push({
startIndex: i - minConsecutive,
endIndex: i,
direction: 'down',
strength: move
});
}
}
}
return displacements;
}
Definition: Price trading in a tight range, building energy for next move.
Detection:
interface Consolidation {
startIndex: number;
endIndex: number;
high: number;
low: number;
range: number;
}
function detectConsolidation(
candles: Candle[],
minCandles: number = 10,
maxRangePercent: number = 0.02 // 2% max range
): Consolidation[] {
const consolidations: Consolidation[] = [];
for (let i = minCandles; i < candles.length; i++) {
const window = candles.slice(i - minCandles, i);
const high = Math.max(...window.map(c => c.high));
const low = Math.min(...window.map(c => c.low));
const range = high - low;
const rangePercent = range / low;
if (rangePercent <= maxRangePercent) {
consolidations.push({
startIndex: i - minCandles,
endIndex: i,
high,
low,
range
});
}
}
return consolidations;
}
When implementing strategies:
For consistency:
roundToTick()toEST()For utilities:
resampleCandles() to convert between timeframescalculateATR() for dynamic stop-loss placementfindSwingPoints() for market structure analysisLast Updated: January 2025 Version: 1.0.0 Part of Wolf Skills Marketplace