Agent skill
amt-strategy
Auction Market Theory (AMT) and Market Profile - volume-based price discovery, value area, POC, TPO charts, and profile analysis for trading bot implementation
Install this agent skill to your Project
npx add-skill https://github.com/Nice-Wolf-Studio/wolf-skills-marketplace/tree/main/amt-strategy
SKILL.md
Auction Market Theory (AMT) & Market Profile
Overview
Auction Market Theory (AMT) views financial markets as continuous two-way auctions where price discovery occurs through negotiation between buyers and sellers. Market Profile is the visual representation of this theory, showing where and for how long prices traded throughout a session.
Core Philosophy: Markets constantly seek fair value through price discovery. When buyers and sellers agree on value, the market consolidates (balance). When they disagree, the market trends (imbalance).
Use this skill when:
- Building trading bots that use volume analysis
- Understanding value area and point of control (POC) concepts
- Coding market profile algorithms for Discord bots
- Implementing auction-based trading logic
- Analyzing market balance/imbalance
Current as of: January 2025
Core AMT Concepts
1. TPO (Time Price Opportunity)
What it is: A measurement of how much time the market spends at each price level. Each 30-minute period is represented by a letter (A-Z), creating a visual distribution.
Visualization:
5850 [ C DEF ] <- Low TPO count (rejection)
5845 [ AB CDEF GH ]
5840 [ ABCDEFGH IJ ] <- High TPO count (acceptance)
5835 [ ABCDEFGHIJ K ] <- POC (highest TPO)
5830 [ BCDEFGHIJ ]
5825 [ DEFGH ]
Data Structure:
interface TPOProfile {
date: string; // Trading session date
periods: TPOPeriod[]; // 30-min periods
distribution: Map<number, string[]>; // price -> letters
}
interface TPOPeriod {
letter: string; // A-Z (A=first 30min, B=second, etc.)
startTime: Date;
endTime: Date;
high: number;
low: number;
}
// Build TPO profile from intraday candles
function buildTPOProfile(
candles: Candle[], // 1-min or 5-min candles
sessionStart: Date,
periodMinutes: number = 30
): TPOProfile {
const periods: TPOPeriod[] = [];
const distribution = new Map<number, string[]>();
// Group candles into 30-minute periods
const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
let periodIndex = 0;
for (let i = 0; i < candles.length; i += periodMinutes) {
const periodCandles = candles.slice(i, i + periodMinutes);
if (periodCandles.length === 0) break;
const periodHigh = Math.max(...periodCandles.map(c => c.high));
const periodLow = Math.min(...periodCandles.map(c => c.low));
const letter = letters[periodIndex % letters.length];
periods.push({
letter,
startTime: new Date(periodCandles[0].timestamp),
endTime: new Date(periodCandles[periodCandles.length - 1].timestamp),
high: periodHigh,
low: periodLow
});
// Add TPO letter to each price level in range
const tickSize = 0.25; // Adjust based on instrument
for (let price = periodLow; price <= periodHigh; price += tickSize) {
const roundedPrice = Math.round(price / tickSize) * tickSize;
if (!distribution.has(roundedPrice)) {
distribution.set(roundedPrice, []);
}
distribution.get(roundedPrice)!.push(letter);
}
periodIndex++;
}
return {
date: sessionStart.toISOString().split('T')[0],
periods,
distribution
};
}
2. Value Area (VA)
What it is: The price range containing 70% of the session's TPO count (or volume). Represents where the market spent most of its time, indicating "fair value."
Components:
- VAH (Value Area High): Upper boundary of value area
- VAL (Value Area Low): Lower boundary of value area
- POC (Point of Control): Price level with most TPO/volume (center of value)
TypeScript Implementation:
interface ValueArea {
high: number; // VAH
low: number; // VAL
poc: number; // Point of Control
percentage: number; // Should be ~70%
}
function calculateValueArea(
tpoProfile: TPOProfile,
targetPercentage: number = 0.70
): ValueArea {
// Count total TPOs
let totalTPOs = 0;
const priceTPOCounts = new Map<number, number>();
tpoProfile.distribution.forEach((letters, price) => {
const count = letters.length;
priceTPOCounts.set(price, count);
totalTPOs += count;
});
// Find POC (price with highest TPO count)
let poc = 0;
let maxTPOs = 0;
priceTPOCounts.forEach((count, price) => {
if (count > maxTPOs) {
maxTPOs = count;
poc = price;
}
});
// Build value area from POC outward
const targetTPOs = totalTPOs * targetPercentage;
let currentTPOs = priceTPOCounts.get(poc) || 0;
let vah = poc;
let val = poc;
const sortedPrices = Array.from(priceTPOCounts.keys()).sort((a, b) => a - b);
const pocIndex = sortedPrices.indexOf(poc);
let upperIndex = pocIndex + 1;
let lowerIndex = pocIndex - 1;
// Expand value area until we capture 70% of TPOs
while (currentTPOs < targetTPOs && (upperIndex < sortedPrices.length || lowerIndex >= 0)) {
const upperPrice = sortedPrices[upperIndex];
const lowerPrice = sortedPrices[lowerIndex];
const upperTPOs = upperPrice !== undefined ? (priceTPOCounts.get(upperPrice) || 0) : 0;
const lowerTPOs = lowerPrice !== undefined ? (priceTPOCounts.get(lowerPrice) || 0) : 0;
// Expand in direction with more TPOs
if (upperTPOs >= lowerTPOs && upperIndex < sortedPrices.length) {
vah = upperPrice;
currentTPOs += upperTPOs;
upperIndex++;
} else if (lowerIndex >= 0) {
val = lowerPrice;
currentTPOs += lowerTPOs;
lowerIndex--;
} else {
break;
}
}
return {
high: vah,
low: val,
poc,
percentage: currentTPOs / totalTPOs
};
}
Volume-Based Value Area (preferred for futures):
function calculateVolumeValueArea(
candles: Candle[], // Intraday candles with volume
tickSize: number = 0.25
): ValueArea {
// Aggregate volume by price level
const volumeByPrice = new Map<number, number>();
let totalVolume = 0;
candles.forEach(candle => {
// Distribute candle volume across its price range
const priceRange = candle.high - candle.low;
const numTicks = Math.ceil(priceRange / tickSize);
const volumePerTick = candle.volume / Math.max(numTicks, 1);
for (let price = candle.low; price <= candle.high; price += tickSize) {
const roundedPrice = Math.round(price / tickSize) * tickSize;
volumeByPrice.set(
roundedPrice,
(volumeByPrice.get(roundedPrice) || 0) + volumePerTick
);
totalVolume += volumePerTick;
}
});
// Find POC
let poc = 0;
let maxVolume = 0;
volumeByPrice.forEach((volume, price) => {
if (volume > maxVolume) {
maxVolume = volume;
poc = price;
}
});
// Build value area from POC (same algorithm as TPO, but with volume)
const targetVolume = totalVolume * 0.70;
let currentVolume = volumeByPrice.get(poc) || 0;
let vah = poc;
let val = poc;
const sortedPrices = Array.from(volumeByPrice.keys()).sort((a, b) => a - b);
const pocIndex = sortedPrices.indexOf(poc);
let upperIndex = pocIndex + 1;
let lowerIndex = pocIndex - 1;
while (currentVolume < targetVolume && (upperIndex < sortedPrices.length || lowerIndex >= 0)) {
const upperPrice = sortedPrices[upperIndex];
const lowerPrice = sortedPrices[lowerIndex];
const upperVolume = upperPrice !== undefined ? (volumeByPrice.get(upperPrice) || 0) : 0;
const lowerVolume = lowerPrice !== undefined ? (volumeByPrice.get(lowerPrice) || 0) : 0;
if (upperVolume >= lowerVolume && upperIndex < sortedPrices.length) {
vah = upperPrice;
currentVolume += upperVolume;
upperIndex++;
} else if (lowerIndex >= 0) {
val = lowerPrice;
currentVolume += lowerVolume;
lowerIndex--;
} else {
break;
}
}
return { high: vah, low: val, poc, percentage: currentVolume / totalVolume };
}
3. Initial Balance (IB)
What it is: The price range established during the first hour of trading. Provides crucial context for the day's character.
Significance:
- IB Expansion: Price breaks and holds above/below IB → Trend day likely
- IB Rejection: Price tests IB extremes and returns → Range day likely
- IB Size: Narrow IB → Potential for expansion; Wide IB → Likely consolidation
TypeScript Implementation:
interface InitialBalance {
high: number;
low: number;
range: number;
sessionStart: Date;
broken: 'above' | 'below' | 'neither' | 'both';
}
function getInitialBalance(
candles: Candle[],
sessionStart: Date,
ibDurationMinutes: number = 60
): InitialBalance {
const ibEndTime = new Date(sessionStart.getTime() + ibDurationMinutes * 60 * 1000);
// Filter candles within IB period
const ibCandles = candles.filter(c => {
const candleTime = new Date(c.timestamp);
return candleTime >= sessionStart && candleTime < ibEndTime;
});
if (ibCandles.length === 0) {
throw new Error('No candles found during initial balance period');
}
const high = Math.max(...ibCandles.map(c => c.high));
const low = Math.min(...ibCandles.map(c => c.low));
// Check if IB was broken by subsequent candles
const postIBCandles = candles.filter(c => new Date(c.timestamp) >= ibEndTime);
let brokenAbove = false;
let brokenBelow = false;
postIBCandles.forEach(candle => {
if (candle.high > high) brokenAbove = true;
if (candle.low < low) brokenBelow = true;
});
let broken: 'above' | 'below' | 'neither' | 'both' = 'neither';
if (brokenAbove && brokenBelow) broken = 'both';
else if (brokenAbove) broken = 'above';
else if (brokenBelow) broken = 'below';
return {
high,
low,
range: high - low,
sessionStart,
broken
};
}
4. Profile Types (Day Types)
Market Profile days fall into distinct categories based on IB behavior and distribution shape:
Type Classification:
enum ProfileType {
NORMAL = 'normal',
NORMAL_VARIATION = 'normal_variation',
TREND_DAY = 'trend_day',
NEUTRAL_DAY = 'neutral_day',
DOUBLE_DISTRIBUTION = 'double_distribution'
}
interface ProfileCharacteristics {
type: ProfileType;
ibBroken: boolean;
ibBreakDirection: 'up' | 'down' | 'both' | 'neither';
ibBreakCount: number; // Number of times IB was broken
valueAreaPosition: 'upper' | 'middle' | 'lower'; // Relative to full range
confidence: number; // 0-1
}
function classifyProfileType(
tpoProfile: TPOProfile,
ib: InitialBalance,
va: ValueArea,
candles: Candle[]
): ProfileCharacteristics {
const sessionHigh = Math.max(...candles.map(c => c.high));
const sessionLow = Math.min(...candles.map(c => c.low));
const fullRange = sessionHigh - sessionLow;
// Count IB breaks
let ibBreakCount = 0;
let lastBreakDirection: 'up' | 'down' | 'neither' = 'neither';
candles.forEach((candle, i) => {
if (i === 0) return;
const prev = candles[i - 1];
// Break above IB
if (prev.high <= ib.high && candle.high > ib.high) {
ibBreakCount++;
lastBreakDirection = 'up';
}
// Break below IB
if (prev.low >= ib.low && candle.low < ib.low) {
ibBreakCount++;
lastBreakDirection = 'down';
}
});
// Determine value area position in full range
const vaMiddle = (va.high + va.low) / 2;
const rangePosition = (vaMiddle - sessionLow) / fullRange;
let valueAreaPosition: 'upper' | 'middle' | 'lower';
if (rangePosition > 0.66) valueAreaPosition = 'upper';
else if (rangePosition < 0.33) valueAreaPosition = 'lower';
else valueAreaPosition = 'middle';
// Classify profile type
let type: ProfileType;
let confidence = 0.5;
if (ibBreakCount === 0) {
// Normal Day: IB not broken, value area centered
type = ProfileType.NORMAL;
confidence = valueAreaPosition === 'middle' ? 0.9 : 0.6;
} else if (ibBreakCount === 1) {
// Normal Variation: IB broken once in one direction
type = ProfileType.NORMAL_VARIATION;
confidence = 0.8;
} else if (ibBreakCount >= 2 && lastBreakDirection !== 'neither') {
// Trend Day: Multiple IB breaks in same direction
type = ProfileType.TREND_DAY;
confidence = 0.85;
} else if (ibBreakCount >= 2 && ib.broken === 'both') {
// Double Distribution: Value shifted dramatically (news event)
type = ProfileType.DOUBLE_DISTRIBUTION;
confidence = 0.75;
} else {
// Neutral/Non-trend day
type = ProfileType.NEUTRAL_DAY;
confidence = 0.6;
}
return {
type,
ibBroken: ibBreakCount > 0,
ibBreakDirection: lastBreakDirection,
ibBreakCount,
valueAreaPosition,
confidence
};
}
Profile Type Descriptions:
-
Normal Day: IB not broken, value area centered, classic bell curve distribution
- Trading: Mean reversion around VA, fade extremes
-
Normal Variation: IB broken once, value area shifts toward break direction
- Trading: Trade in direction of IB break, look for continuation
-
Trend Day: IB broken multiple times (≥2) in same direction, value area at extreme
- Trading: Momentum trading, avoid counter-trend trades, ride the trend
-
Neutral Day: Choppy, multiple IB breaks in both directions, wide distribution
- Trading: Difficult to trade, consider staying out or tight scalps
-
Double Distribution: Two separate value areas (typically due to news)
- Trading: Identify new value area, trade from new POC
AMT Trading Strategies
1. Balanced Market (Mean Reversion)
When to use: Normal days, neutral days, or when price is oscillating around VA.
Strategy:
- Price above VAH → Look for shorts back to POC
- Price below VAL → Look for longs back to POC
- Price at POC → Wait for directional clues
TypeScript Implementation:
interface AMTSignal {
direction: 'long' | 'short' | 'neutral';
entry: number;
stopLoss: number;
target: number;
reasoning: string[];
confidence: number;
}
function generateBalancedMarketSignal(
currentPrice: number,
va: ValueArea,
profile: ProfileCharacteristics
): AMTSignal | null {
// Only trade balanced markets (normal, neutral days)
if (profile.type === ProfileType.TREND_DAY) {
return null; // Don't fade trend days
}
const vaRange = va.high - va.low;
const atr = vaRange * 0.3; // Rough ATR estimate
// Price above VAH → short back to POC
if (currentPrice > va.high) {
const distance = currentPrice - va.high;
const confidence = Math.min(distance / vaRange, 1); // Further = higher confidence
return {
direction: 'short',
entry: currentPrice,
target: va.poc,
stopLoss: currentPrice + atr,
reasoning: [
`Price above value area high (${va.high.toFixed(2)})`,
'Mean reversion expected back to POC',
`Profile type: ${profile.type} (favorable for mean reversion)`
],
confidence: confidence * 0.7
};
}
// Price below VAL → long back to POC
if (currentPrice < va.low) {
const distance = va.low - currentPrice;
const confidence = Math.min(distance / vaRange, 1);
return {
direction: 'long',
entry: currentPrice,
target: va.poc,
stopLoss: currentPrice - atr,
reasoning: [
`Price below value area low (${va.low.toFixed(2)})`,
'Mean reversion expected back to POC',
`Profile type: ${profile.type} (favorable for mean reversion)`
],
confidence: confidence * 0.7
};
}
// Price within value area → neutral
return {
direction: 'neutral',
entry: currentPrice,
target: currentPrice,
stopLoss: currentPrice,
reasoning: ['Price within value area', 'No clear edge for entry'],
confidence: 0
};
}
2. Imbalanced Market (Trend Following)
When to use: Trend days, normal variation days with IB break, strong directional conviction.
Strategy:
- IB broken above + price holding above IB high → Look for longs on pullbacks
- IB broken below + price holding below IB low → Look for shorts on pullbacks
- Avoid counter-trend trades on trend days
TypeScript Implementation:
function generateImbalancedMarketSignal(
currentPrice: number,
ib: InitialBalance,
va: ValueArea,
profile: ProfileCharacteristics,
candles: Candle[]
): AMTSignal | null {
// Only trade imbalanced markets (trend days, normal variation)
if (profile.type !== ProfileType.TREND_DAY && profile.type !== ProfileType.NORMAL_VARIATION) {
return null;
}
const atr = ib.range * 0.5; // Rough ATR from IB range
const recentCandle = candles[candles.length - 1];
// Bullish imbalance: IB broken above
if (profile.ibBreakDirection === 'up' && currentPrice > ib.high) {
// Wait for pullback to IB high or VAH
const pullbackTarget = Math.max(ib.high, va.high);
const distanceFromPullback = currentPrice - pullbackTarget;
if (distanceFromPullback < atr * 0.5) { // Close to pullback level
return {
direction: 'long',
entry: pullbackTarget,
target: currentPrice + (currentPrice - ib.low), // Project IB range higher
stopLoss: ib.high - atr,
reasoning: [
`Trend day with IB broken above (${ib.high.toFixed(2)})`,
'Pullback to IB high provides entry',
`Value area in ${profile.valueAreaPosition} third (confirms trend)`
],
confidence: 0.75
};
}
}
// Bearish imbalance: IB broken below
if (profile.ibBreakDirection === 'down' && currentPrice < ib.low) {
const pullbackTarget = Math.min(ib.low, va.low);
const distanceFromPullback = pullbackTarget - currentPrice;
if (distanceFromPullback < atr * 0.5) {
return {
direction: 'short',
entry: pullbackTarget,
target: currentPrice - (ib.high - currentPrice), // Project IB range lower
stopLoss: ib.low + atr,
reasoning: [
`Trend day with IB broken below (${ib.low.toFixed(2)})`,
'Pullback to IB low provides entry',
`Value area in ${profile.valueAreaPosition} third (confirms trend)`
],
confidence: 0.75
};
}
}
return null; // No clear setup
}
3. Complete AMT Analysis Function
async function performAMTAnalysis(
symbol: string,
sessionStart: Date,
candles: Candle[]
): Promise<{
profile: TPOProfile;
va: ValueArea;
ib: InitialBalance;
characteristics: ProfileCharacteristics;
signal: AMTSignal | null;
}> {
// 1. Build TPO profile
const profile = buildTPOProfile(candles, sessionStart);
// 2. Calculate value area (prefer volume-based for futures)
const va = calculateVolumeValueArea(candles);
// 3. Get initial balance
const ib = getInitialBalance(candles, sessionStart);
// 4. Classify profile type
const characteristics = classifyProfileType(profile, ib, va, candles);
// 5. Generate signal based on market type
const currentPrice = candles[candles.length - 1].close;
let signal: AMTSignal | null = null;
if (characteristics.type === ProfileType.TREND_DAY ||
characteristics.type === ProfileType.NORMAL_VARIATION) {
signal = generateImbalancedMarketSignal(currentPrice, ib, va, characteristics, candles);
} else {
signal = generateBalancedMarketSignal(currentPrice, va, characteristics);
}
return { profile, va, ib, characteristics, signal };
}
Integration with Discord Bot
Example: AMT Analysis Command
import { SlashCommandBuilder, EmbedBuilder } from 'discord.js';
const amtAnalyzeCommand = new SlashCommandBuilder()
.setName('amt-analyze')
.setDescription('Analyze market using Auction Market Theory')
.addStringOption(option =>
option.setName('symbol')
.setDescription('Symbol to analyze')
.setRequired(true));
async function handleAMTAnalyze(interaction) {
const symbol = interaction.options.getString('symbol');
await interaction.deferReply();
try {
// Fetch intraday data for current session
const sessionStart = getTodaySessionStart(); // 9:30 AM for ES/NQ
const candles = await fetchIntradayCandles(symbol, sessionStart);
const analysis = await performAMTAnalysis(symbol, sessionStart, candles);
const embed = new EmbedBuilder()
.setTitle(`📊 AMT Analysis: ${symbol}`)
.setColor(getColorForProfileType(analysis.characteristics.type))
.addFields([
{
name: 'Profile Type',
value: `**${analysis.characteristics.type.toUpperCase()}**`,
inline: true
},
{
name: 'IB Status',
value: analysis.ib.broken !== 'neither'
? `Broken ${analysis.ib.broken}`
: 'Intact',
inline: true
},
{
name: 'Confidence',
value: `${(analysis.characteristics.confidence * 100).toFixed(0)}%`,
inline: true
},
{
name: 'Initial Balance',
value: `${analysis.ib.high.toFixed(2)} - ${analysis.ib.low.toFixed(2)} (${analysis.ib.range.toFixed(2)})`,
inline: false
},
{
name: 'Value Area',
value: `VAH: ${analysis.va.high.toFixed(2)}\nPOC: ${analysis.va.poc.toFixed(2)}\nVAL: ${analysis.va.low.toFixed(2)}`,
inline: true
},
{
name: 'Signal',
value: analysis.signal
? `${analysis.signal.direction.toUpperCase()} @ ${analysis.signal.entry.toFixed(2)}\nTarget: ${analysis.signal.target.toFixed(2)}\nStop: ${analysis.signal.stopLoss.toFixed(2)}`
: 'No clear setup',
inline: true
}
]);
if (analysis.signal && analysis.signal.reasoning.length > 0) {
embed.addFields([{
name: 'Reasoning',
value: analysis.signal.reasoning.join('\n'),
inline: false
}]);
}
embed.setTimestamp();
await interaction.editReply({ embeds: [embed] });
} catch (error) {
await interaction.editReply(`Error: ${error.message}`);
}
}
function getColorForProfileType(type: ProfileType): number {
switch (type) {
case ProfileType.TREND_DAY: return 0x00FF00; // Green
case ProfileType.NORMAL: return 0x0000FF; // Blue
case ProfileType.NORMAL_VARIATION: return 0xFFFF00; // Yellow
case ProfileType.NEUTRAL_DAY: return 0x808080; // Gray
case ProfileType.DOUBLE_DISTRIBUTION: return 0xFF00FF; // Magenta
default: return 0xFFFFFF;
}
}
Common Pitfalls When Coding AMT
1. Tick Size and Price Rounding
- Different instruments have different tick sizes (ES = 0.25, NQ = 0.25, etc.)
- Always round prices to valid ticks
- Incorrect rounding causes POC/VA calculation errors
2. Volume Distribution
- TPO-based profiles work on any timeframe
- Volume-based profiles require actual volume data (not available in all markets)
- For futures, prefer volume-based; for forex, use TPO-based
3. Session Definition
- Clearly define session start (e.g., ES regular session = 9:30 AM ET)
- Handle overnight sessions separately
- ETH (Extended Trading Hours) creates different profiles than RTH
4. IB Duration
- Standard IB = first 60 minutes
- Some traders use 90 minutes for volatile markets
- Be consistent with your IB definition
5. Profile Type Misclassification
- Profile type is subjective and context-dependent
- Use multiple criteria (IB breaks, VA position, range)
- Don't over-rely on automated classification
AMT + ICT Integration
Combine AMT with ICT for enhanced analysis:
Complementary Concepts:
- POC = Institutional Reference: Both represent fair value
- VAH/VAL = Liquidity Pools: Stops cluster at value area extremes
- IB Break + Killzone: IB break during NY AM killzone = high probability
- Trend Day + FVG: FVGs more reliable on trend days
Integration Example:
async function generateCombinedSignal(
symbol: string,
candles: Candle[]
): Promise<{ ict: ICTSignal | null; amt: AMTSignal | null; combined: string }> {
const ictSignal = await generateICTSignal(symbol, '15m');
const sessionStart = getTodaySessionStart();
const amtAnalysis = await performAMTAnalysis(symbol, sessionStart, candles);
const amtSignal = amtAnalysis.signal;
let combined = 'No clear setup';
// Both agree on direction
if (ictSignal && amtSignal && ictSignal.direction === amtSignal.direction) {
combined = `STRONG ${ictSignal.direction.toUpperCase()} - ICT and AMT aligned`;
}
// ICT says long, AMT says trend day with IB broken up
else if (ictSignal?.direction === 'long' &&
amtAnalysis.characteristics.type === ProfileType.TREND_DAY &&
amtAnalysis.characteristics.ibBreakDirection === 'up') {
combined = 'STRONG LONG - ICT entry on AMT trend day';
}
// Conflicting signals
else if (ictSignal && amtSignal && ictSignal.direction !== amtSignal.direction) {
combined = 'CONFLICTING - Stay out or wait for clarity';
}
return { ict: ictSignal, amt: amtSignal, combined };
}
After Using This Skill
If building AMT features for your Discord bot:
- Implement volume/TPO aggregation (data-intensive, consider caching)
- Use
databentoskill for volume data - Use
trading-bot-developmentskill for Discord integration patterns
If combining with ICT:
- ICT provides entry precision (FVG, Order Block)
- AMT provides context (trend day vs balanced day)
- Use AMT to filter ICT signals (e.g., only take ICT longs on trend days with IB broken up)
For backtesting:
- Profile characteristics change intraday (calculate real-time)
- Historical profile data useful for understanding typical behavior
- Test both balanced and imbalanced strategies separately
References & Further Learning
AMT/Market Profile Resources:
- Market Profile by J. Peter Steidlmayer (original book)
- Auction Market Theory guides (FTMO, Topstep, ATAS)
- Volume Profile analysis (QuantConnect, TradingView implementations)
Code Libraries:
- py-market-profile (Python): GitHub reference implementation
- Volume Profile indicators (QuantConnect, TradingView)
2024-2025 Updates:
- Increased focus on volume-based profiles (more reliable than TPO for futures)
- Integration with order flow analysis
- Real-time profile calculations during session
Last Updated: January 2025 Version: 1.0.0 Part of Wolf Skills Marketplace
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
daily-summary
Use when preparing daily standups or status reports - automates PR summary generation with categorization, metrics, and velocity analysis; eliminates manual report compilation and ensures consistent format
wolf-scripts-core
Core automation scripts for archetype selection, evidence validation, quality scoring, and safe bash execution
wolf
Master skill for Wolf Agents institutional knowledge and behavioral patterns (v1.1.0 with skill-chaining)
wolf-archetypes
Behavioral archetypes for automatic agent adaptation based on work type
ict-strategy
Inner Circle Trader (ICT) methodology - smart money concepts, liquidity pools, fair value gaps, order blocks, and market structure for trading bot implementation
databento
Use when working with ES/NQ futures market data, before calling any Databento API - follow mandatory four-step workflow (cost check, availability check, fetch, validate); prevents costly API errors and ensures data quality
Didn't find tool you were looking for?