Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion documentation/cookbook/demo-data-schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,6 @@ The FX dataset includes several materialized views providing pre-aggregated data
#### FX trades OHLC

- **`fx_trades_ohlc_1m`** - OHLC candlesticks from trade executions at 1-minute intervals
- **`fx_trades_ohlc_1h`** - OHLC candlesticks from trade executions at 1-hour intervals
- **`fx_trades_ohlc_1d`** - OHLC candlesticks from trade executions at 1-day intervals

These views are continuously updated and optimized for dashboard and analytics queries on FX data.
Expand Down
94 changes: 94 additions & 0 deletions documentation/cookbook/sql/finance/atr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
---
title: ATR (Average True Range)
sidebar_label: ATR
description: Calculate Average True Range to measure market volatility for position sizing and stop-loss placement
---

Average True Range (ATR) measures market volatility by calculating the average of true ranges over a period. Unlike simple high-low range, true range accounts for gaps between periods, making it more accurate for volatile markets.

## Problem

You want to measure volatility to set appropriate stop-losses or position sizes. Simple high-low range misses overnight gaps, and standard deviation assumes normal distribution which markets don't follow.

## Solution

```questdb-sql demo title="Calculate 14-period ATR"
DECLARE
@symbol := 'EURUSD',
@lookback := '$now - 1M..$now'

WITH with_prev AS (
SELECT
timestamp,
symbol,
high,
low,
close,
lag(close) OVER (PARTITION BY symbol ORDER BY timestamp) AS prev_close
FROM market_data_ohlc_15m
WHERE symbol = @symbol
AND timestamp IN @lookback
),
true_range AS (
SELECT
timestamp,
symbol,
high,
low,
close,
greatest(
high - low,
abs(high - prev_close),
abs(low - prev_close)
) AS tr
FROM with_prev
WHERE prev_close IS NOT NULL
)
SELECT
timestamp,
symbol,
round(close, 5) AS close,
round(tr, 6) AS true_range,
round(avg(tr, 'period', 14) OVER (PARTITION BY symbol ORDER BY timestamp), 6) AS atr
FROM true_range
ORDER BY timestamp;
```

The query:
1. Gets previous close using `lag()` to detect gaps
2. Calculates true range as the greatest of:
- Current high - current low (intraday range)
- |Current high - previous close| (gap up)
- |Current low - previous close| (gap down)
3. Applies 14-period EMA smoothing to get ATR

## Interpreting results

- **High ATR**: Market is volatile, use wider stops
- **Low ATR**: Market is quiet, can use tighter stops
- **Rising ATR**: Volatility increasing, often during trends or breakouts
- **Falling ATR**: Volatility decreasing, often during consolidation

## Common uses

**Stop-loss placement:**
```sql
-- Stop at 2x ATR below entry
entry_price - 2 * atr AS stop_loss
```

**Position sizing:**
```sql
-- Risk 1% of account, sized by ATR
(account_size * 0.01) / atr AS position_size
```

:::note EMA vs Wilder's smoothing
This recipe uses standard EMA smoothing via `avg(value, 'period', 14)` where α = 2/(N+1). Wilder's original ATR uses α = 1/N, which is more gradual. For exact Wilder smoothing with a 14-period lookback, use `avg(value, 'period', 27)`. Most modern platforms offer both variants.
:::

:::info Related documentation
- [EMA window function](/docs/query/functions/window-functions/reference/#avg)
- [lag() function](/docs/query/functions/window-functions/reference/#lag)
- [greatest() function](/docs/query/functions/numeric/#greatest)
:::
77 changes: 77 additions & 0 deletions documentation/cookbook/sql/finance/bid-ask-spread.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
title: Bid-ask spread
sidebar_label: Bid-ask spread
description: Calculate bid-ask spread metrics for transaction cost analysis and liquidity measurement
---

The bid-ask spread is the difference between the best ask (lowest sell price) and best bid (highest buy price). It represents the cost of immediately executing a round-trip trade and is a key measure of market liquidity.

## Problem

You want to measure market liquidity and transaction costs. Narrow spreads indicate liquid markets with low trading costs, while wide spreads suggest illiquidity or market stress.

## Solution

```questdb-sql demo title="Calculate bid-ask spread metrics"
DECLARE
@symbol := 'EURUSD',
@lookback := '$now - 1h..$now'

SELECT
timestamp,
symbol,
round(bid_price, 5) AS bid,
round(ask_price, 5) AS ask,
round(ask_price - bid_price, 6) AS spread_absolute,
round((ask_price - bid_price) / ((bid_price + ask_price) / 2) * 10000, 2) AS spread_bps,
round((bid_price + ask_price) / 2, 5) AS mid_price
FROM core_price
WHERE symbol = @symbol
AND timestamp IN @lookback
ORDER BY timestamp;
```

The query calculates:
- **Absolute spread**: ask - bid
- **Spread in basis points**: spread / mid_price × 10,000
- **Mid price**: (bid + ask) / 2

## Aggregated spread analysis

```questdb-sql demo title="Average spread by time period"
DECLARE
@symbol := 'EURUSD',
@lookback := '$now - 1d..$now'

SELECT
timestamp,
symbol,
round(avg((ask_price - bid_price) / ((bid_price + ask_price) / 2) * 10000), 2) AS avg_spread_bps,
round(min((ask_price - bid_price) / ((bid_price + ask_price) / 2) * 10000), 2) AS min_spread_bps,
round(max((ask_price - bid_price) / ((bid_price + ask_price) / 2) * 10000), 2) AS max_spread_bps,
count() AS quote_count
FROM core_price
WHERE symbol = @symbol
AND timestamp IN @lookback
SAMPLE BY 1h
ORDER BY timestamp;
```

## Interpreting results

- **Tight spread (< 1 bps for FX majors)**: Highly liquid, low transaction costs
- **Wide spread**: Illiquid or volatile period, higher transaction costs
- **Spread widening**: Often precedes or accompanies volatility
- **Intraday patterns**: Spreads typically widen during off-hours and narrow during active sessions

:::note Spread conventions
- FX majors: typically 0.1-1.0 basis points
- FX minors: 1-5 basis points
- Crypto: varies widely, 1-50+ basis points
- Equities: often quoted in cents rather than bps
:::

:::info Related documentation
- [SAMPLE BY](/docs/query/sql/sample-by/)
- [Aggregation functions](/docs/query/functions/aggregation/)
:::
69 changes: 69 additions & 0 deletions documentation/cookbook/sql/finance/donchian-channels.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
---
title: Donchian Channels
sidebar_label: Donchian Channels
description: Calculate Donchian Channels to identify breakouts and trading ranges using highest high and lowest low
---

Donchian Channels plot the highest high and lowest low over a period, creating a channel that tracks price range. Breakouts above the upper channel or below the lower channel often signal trend continuation.

## Problem

You want to identify breakout levels and trading ranges. Moving averages smooth price but don't show clear breakout levels. Donchian Channels show exactly where price needs to go to break out of its recent range.

## Solution

```questdb-sql demo title="Calculate 20-period Donchian Channels"
DECLARE
@symbol := 'EURUSD',
@lookback := '$now - 1M..$now'

SELECT
timestamp,
symbol,
round(close, 5) AS close,
round(max(high) OVER (
PARTITION BY symbol
ORDER BY timestamp
ROWS BETWEEN 19 PRECEDING AND CURRENT ROW
), 5) AS upper_channel,
round(min(low) OVER (
PARTITION BY symbol
ORDER BY timestamp
ROWS BETWEEN 19 PRECEDING AND CURRENT ROW
), 5) AS lower_channel,
round((max(high) OVER (
PARTITION BY symbol
ORDER BY timestamp
ROWS BETWEEN 19 PRECEDING AND CURRENT ROW
) + min(low) OVER (
PARTITION BY symbol
ORDER BY timestamp
ROWS BETWEEN 19 PRECEDING AND CURRENT ROW
)) / 2, 5) AS middle_channel
FROM market_data_ohlc_15m
WHERE symbol = @symbol
AND timestamp IN @lookback
ORDER BY timestamp;
```

The query calculates:
- **Upper channel**: 20-period highest high
- **Lower channel**: 20-period lowest low
- **Middle channel**: Average of upper and lower

## Interpreting results

- **Price breaks above upper**: Bullish breakout, potential long entry
- **Price breaks below lower**: Bearish breakout, potential short entry
- **Price at middle**: Neutral zone
- **Narrow channel**: Low volatility, breakout likely coming
- **Wide channel**: High volatility, trend in progress

:::note Turtle Trading
Donchian Channels were famously used by the Turtle Traders. Their system entered on 20-day breakouts and exited on 10-day breakouts in the opposite direction.
:::

:::info Related documentation
- [min/max window functions](/docs/query/functions/window-functions/reference/#min)
- [Window functions overview](/docs/query/functions/window-functions/overview/)
:::
89 changes: 89 additions & 0 deletions documentation/cookbook/sql/finance/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
---
title: Capital Markets Recipes
sidebar_label: Overview
description: SQL recipes for financial analysis including technical indicators, volatility metrics, volume analysis, and risk measurement in QuestDB.
---

# Capital Markets Recipes

This section contains SQL recipes for financial market analysis. Each recipe uses the
[demo dataset](/docs/cookbook/demo-data-schema/) available in the QuestDB web console.

## Price-Based Indicators

Foundation recipes for price analysis and trend identification.

| Recipe | Description |
|--------|-------------|
| [OHLC Aggregation](ohlc.md) | Aggregate tick data into candlestick bars |
| [VWAP](vwap.md) | Volume-Weighted Average Price |
| [Bollinger Bands](bollinger-bands.md) | Price channels based on standard deviation |
| [Bollinger BandWidth](bollinger-bandwidth.md) | Measure band expansion and contraction |

## Momentum Indicators

Measure the speed and strength of price movements.

| Recipe | Description |
|--------|-------------|
| [RSI](rsi.md) | Relative Strength Index for overbought/oversold conditions |
| [MACD](macd.md) | Moving Average Convergence Divergence |
| [Stochastic Oscillator](stochastic.md) | Compare closing price to price range |
| [Rate of Change](rate-of-change.md) | Percentage price change over N periods |

## Volatility Indicators

Quantify market uncertainty and price variability.

| Recipe | Description |
|--------|-------------|
| [ATR](atr.md) | Average True Range |
| [Rolling Std Dev](rolling-stddev.md) | Moving standard deviation of returns |
| [Donchian Channels](donchian-channels.md) | High/low price channels |
| [Keltner Channels](keltner-channels.md) | EMA-based volatility channels |
| [Realized Volatility](realized-volatility.md) | Historical volatility from returns |

## Volume & Order Flow

Analyze trading activity and order flow dynamics.

| Recipe | Description |
|--------|-------------|
| [OBV](obv.md) | On-Balance Volume |
| [Volume Profile](volume-profile.md) | Volume distribution by price level |
| [Volume Spike](volume-spike.md) | Detect abnormal volume |
| [Aggressor Imbalance](aggressor-volume-imbalance.md) | Buy vs sell pressure |

## Risk Metrics

Portfolio risk measurement and drawdown analysis.

| Recipe | Description |
|--------|-------------|
| [Maximum Drawdown](maximum-drawdown.md) | Peak-to-trough decline |

## Market Microstructure

Analyze market quality and trading costs.

| Recipe | Description |
|--------|-------------|
| [Bid-Ask Spread](bid-ask-spread.md) | Spread metrics and analysis |
| [Liquidity Comparison](liquidity-comparison.md) | Compare liquidity across instruments |

## Market Breadth

Measure overall market participation and sentiment.

| Recipe | Description |
|--------|-------------|
| [TICK & TRIN](tick-trin.md) | Market breadth indicators |

## Math Utilities

General-purpose financial calculations.

| Recipe | Description |
|--------|-------------|
| [Compound Interest](compound-interest.md) | Interest and growth calculations |
| [Cumulative Product](cumulative-product.md) | Running product for returns |
Loading