TradingView Backtesting

Portfolio Heat: Aggregate Open Risk and Correlated Gaps

Portfolio heat is the sum of planned stop losses across positions open at the same time, divided by account equity. Convert every loss into the account currency before adding it. State whether the loss starts at entry or at the current mark: a profitable position can have zero entry-based risk while still exposing current equity to a large loss. Stops do not guarantee exit prices, so heat is a planning measure rather than a worst-case loss.

See the decision guide

Define which money is at risk

For a linear instrument, let q be the positive quantity, s be +1 for a long and -1 for a short, p the reference price, S the active stop, V the settlement-currency value of one price point per unit, F account-currency units per settlement-currency unit, and c the remaining exit-cost allowance in account currency. Planned risk is R = max(0, s × (p - S)) × q × V × F + c. Portfolio heat is 100 × sum(R) / E, with E the current marked account equity, including open P&L and liabilities.

  • A long entered at 100, now marked at 110, with a stop at 105 has zero adverse entry-to-stop price risk, but 5 per unit of mark-to-stop risk. Using abs(entry - stop) would wrongly label locked-in profit as an entry loss. Costs can still be positive.
  • The examples use the same $100,000 current-equity denominator for both heat measures. Comparing original risk fractions that used different historical equity values does not produce a consistent current total.
  • Check that the active stop is below the current mark for a long and above it for a short. A crossed or already triggered stop requires an execution-status review, not a reassuring zero-risk result. Partial stop coverage requires separate covered and uncovered quantities.
MeasureReference pWhat the result means
Entry-to-current-stop heatActual entry priceRemaining adverse outcome relative to entry, using the current stop; profitable stop outcomes do not offset other risks
Mark-to-stop heatCurrent executable markPotential decline from the current position valuation to its stop, before execution beyond the allowance
Original entry riskOriginal entry and original stopA saved decision-time budget; store it separately when stops or quantities change

Convert each position into account currency

CME explains linear futures P&L as price movement multiplied by the monetary value of that movement and the number of contracts. A quantity of one contract is not one dollar per point. For stocks, V is normally one unit of quote currency per price point per share; for contracts, verify the exchange or broker specification. The example uses an ES-style USD contract with V = $50 per point.

  • F is USD per settlement-currency unit for a USD account. An EUR loss of 400 at EUR/USD = 1.10 becomes $440. If the supplied quote is USD/EUR = 0.9090909 EUR per USD, use its reciprocal, approximately 1.10, rather than multiplying by 0.9090909.
  • A JPY loss of 15,000 with USD/JPY = 150 JPY per USD becomes $100: F = 1 / 150 USD per JPY. Same-currency positions use F = 1 explicitly. Never treat an absent foreign rate as 1.
  • Use a timestamp and rate source for every conversion. TradingView strategy currency conversion generally uses a prior daily rate; a current broker snapshot can differ. Choose one valuation convention for the research and explain any reconciliation difference.
  • This constant-point-value formula excludes nonlinear options and inverse contracts. It also does not measure FX gains or losses on the full foreign asset value or separate cash balances. Stress those exposures with the instrument-specific valuation model.

Recalculate four positions in one account

All inputs below are hypothetical and simultaneous. Current USD equity is $100,000. Stops cover the full quantities. Each position has a $10 remaining exit-cost allowance; prior entry fees are already reflected in equity. Stock A and Stock B share a growth-equity factor, the index future is an intended hedge, and Stock C adds foreign equity and currency exposure. Prices and FX are assumptions, not market quotes.

  • Stock A and Stock B together contribute $1,820, or 1.82% of current equity, on the mark basis. Their different tickers do not remove the shared factor. All three stock longs contribute $2,490, or 2.49%.
  • The short future adds $1,010 of its own adverse stop exposure. Subtracting it from the longs would report $1,480 or 1.48%, but that arithmetic assumes hedge gains occur when needed and ignores the short stop being hit on a different path.
  • Keep one row per economically distinct lot or stop allocation, identify duplicate holdings across strategies, and reconcile the list to the broker account. Add pending orders in a separate potential-fill scenario; they are not yet open positions.
Position / side / quantityEntry / mark / stopV / settlement / FEntry risk USDMark risk USD
Stock A / long / 100 shares100 / 105 / 951 / USD / 1(100 - 95) × 100 + 10 = 510(105 - 95) × 100 + 10 = 1,010
Stock B / long / 200 shares50 / 52 / 481 / USD / 1(50 - 48) × 200 + 10 = 410(52 - 48) × 200 + 10 = 810
Index future / short / 1 contract5,000 / 4,990 / 5,01050 / USD / 1(5,010 - 5,000) × 50 + 10 = 510(5,010 - 4,990) × 50 + 10 = 1,010
Stock C / long / 100 shares80 / 82 / 761 / EUR / 1.10(80 - 76) × 100 × 1.10 + 10 = 450(82 - 76) × 100 × 1.10 + 10 = 670
TotalSame snapshotAll converted to USD1,880 / 100,000 = 1.88%3,500 / 100,000 = 3.50%

Run the worksheet and reject incomplete inputs

The Python 3 worksheet reproduces both totals with no packages. Replace the sample with a complete snapshot before using it for research. The completeness flag is a required human reconciliation, not a connection to a broker. The function rejects invalid rows instead of dropping them from the denominator or substituting zero risk.

  • A missing stop, point value or FX rate invalidates the complete heat result. Report the known subtotal only if it is clearly labeled incomplete; never classify the whole account as low heat from that subtotal.
  • A reconciled empty account has zero open-position heat. An empty or stale file is not evidence of a flat account. Record the snapshot time, account, currency, FX source and reconciliation status alongside the worksheet.
  • Zero or negative equity makes the percentage undefined for this purpose. Reject it and review solvency and margin separately. A finite heat number does not establish enough buying power or liquidity.
An independent calculation for linear instruments. It uses manually supplied positions and rates and does not retrieve holdings, verify stop orders or place trades.python
from math import isfinite

# id, side, quantity, entry, mark, stop, point_value, USD_per_settlement, exit_cost_USD
positions = [
    ("A", 1, 100, 100, 105, 95, 1, 1, 10),
    ("B", 1, 200, 50, 52, 48, 1, 1, 10),
    ("Future", -1, 1, 5000, 4990, 5010, 50, 1, 10),
    ("C", 1, 100, 80, 82, 76, 1, 1.10, 10),
]

def finite_number(x):
    return type(x) in (int, float) and isfinite(x)

def heat(rows, equity, *, snapshot_complete=False):
    if snapshot_complete is not True:
        raise ValueError("Incomplete position/stop/FX snapshot")
    if not finite_number(equity) or equity <= 0:
        raise ValueError("Equity must be finite and positive")
    entry_total, mark_total, seen = 0.0, 0.0, set()
    detail = []
    for row in rows:
        if len(row) != 9:
            raise ValueError("Expected all nine position fields")
        name, side, qty, entry, mark, stop, point, fx, cost = row
        if not isinstance(name, str) or not name or name in seen:
            raise ValueError("Missing or duplicate position id")
        seen.add(name)
        if not all(finite_number(x) for x in row[1:]):
            raise ValueError(name + ": missing/non-finite numeric input")
        if side not in (-1, 1) or min(qty, point, fx) <= 0 or cost < 0:
            raise ValueError(name + ": invalid side, size, units or cost")
        if side * (mark - stop) <= 0:
            raise ValueError(name + ": stop reached or on wrong side")
        scale = qty * point * fx
        er = max(0, side * (entry - stop)) * scale + cost
        mr = side * (mark - stop) * scale + cost
        if not all(isfinite(x) for x in (scale, er, mr)):
            raise ValueError(name + ": numeric overflow")
        detail.append((name, er, mr))
        entry_total += er
        mark_total += mr
    eh, mh = 100 * entry_total / equity, 100 * mark_total / equity
    if not all(isfinite(x) for x in (entry_total, mark_total, eh, mh)):
        raise ValueError("Total or ratio overflow")
    return detail, entry_total, mark_total, eh, mh

result = heat(positions, 100000, snapshot_complete=True)
for name, er, mr in result[0]:
    print(name, round(er, 2), round(mr, 2))
print(tuple(round(x, 2) for x in result[1:]))
# (1880.0, 3500.0, 1.88, 3.5)

# Joint basis/FX stress: assumed exits, not guaranteed fills.
stress_exits = [90, 44, 5015, 68]
stress_fx = [1, 1, 1, 1.20]
losses = [
    side * (mark - fill) * qty * point * rate + cost
    for (_, side, qty, entry, mark, stop, point, fx, cost), fill, rate
    in zip(positions, stress_exits, stress_fx)
]
# C is a fully paid EUR cash equity. Revalue its entire holding in USD.
# Current value: 82 * 100 * 1.10; exit proceeds: 68 * 100 * 1.20.
losses[3] = 82 * 100 * 1.10 - 68 * 100 * 1.20 + 10
print([round(x, 2) for x in losses], round(sum(losses), 2))
# [1510, 1610, 1260, 870.0], 5250.0

A joint gap and failed hedge can exceed the planned total

Apply a hypothetical sector selloff to Stock A and B, a foreign-stock gap to C, and an index rally that squeezes the short future. This is a basis-risk stress: the held stocks and the index hedge move adversely together. Assume exits at 90, 44, 5,015 and 68, with USD per EUR rising to 1.20. No probability is assigned to this joint scenario.

  • The $5,250 loss estimate is $1,750 above the $3,500 planned mark-to-stop risk, even with the same $10 cost allowance per row. C is a fully paid EUR cash equity: revalue its entire holding from $9,020 to $8,160, then add $10. Converting only its local price decline at 1.20 would give $1,690 and omit the offsetting $820 FX gain on the original marked holding. Other cash balances are assumed to be USD; foreign borrowing or cash would need separate revaluation.
  • A common selloff with an effective hedge is a different scenario. Keeping the three stock exits above but setting the future exit to 4,900 produces a $4,490 gain after its $10 allowance. Signed net scenario loss is then -$500 (a $500 gain), while the losing stock rows total $3,990. Report both and do not deduct that possible hedge gain from ordinary stop heat.
  • FINRA explains that a triggered stock stop becomes a market order and may execute far from its stop price. A stop-limit order instead risks remaining unfilled. Verify trigger rules and session coverage for the actual instrument and broker.
  • Raw heat contains no correlation coefficient, likelihood or confidence level. Review factor groups and joint execution paths, including a hedge stopped out before the longs gap. A low historical correlation cannot guarantee a useful offset in that sequence.
PositionAssumed exit / FXLoss from current mark in USD
Stock A90 / 1(105 - 90) × 100 + 10 = 1,510
Stock B44 / 1(52 - 44) × 200 + 10 = 1,610
Short future5,015 / 1(5,015 - 4,990) × 50 + 10 = 1,260
Stock C68 / 1.2082 × 100 × 1.10 - 68 × 100 × 1.20 + 10 = 870
Total marked-value stressSame four positions$5,250 / $100,000 = 5.25%

Reconstruct overlapping risk before using a trade report

TradingView exports the selected Strategy Report tab separately. A List of Trades export describes simulated trade events; it is not a synchronized inventory of all live broker positions and protective orders. To reconstruct historical heat, build an event timeline with each fill, partial exit, stop amendment, mark and FX update, and value every still-open position at each observation.

  • Resolve time zones and event ordering, especially when one trade exits at the same timestamp another enters. Use the information available at that instant. A stop chosen after seeing the eventual exit introduces hindsight.
  • A trade list can identify some overlapping holding intervals, but exit prices and MAE do not reveal the active stop at every time. Without a timestamped stop history and marked account equity, historical heat remains incomplete.
  • Backtest Deep Report currently parses entry/exit times, prices, quantity, P&L and excursions. Its equity calculation accumulates imported trade P&L from initial capital. Those fields do not supply current protective stops, per-position point values, FX snapshots or a live consolidated account inventory.
  • Use Backtest Deep Report to inspect the supported trade records, and keep the heat worksheet as a separate research artifact. Ask Pine Script AI Coding Agent to draft or review explicit sizing and stop rules, then reconcile their outputs with independently calculated amounts. Neither workflow turns this page into a live portfolio-risk monitor.
Where Pineify fits

Backtest Deep Report

Inspect imported TradingView trade records and report calculations while keeping the timestamped portfolio heat worksheet separately.

Also useful: Pine Script AI Coding Agent. Draft or review sizing and stop rules with explicit quantities, contract units and timing assumptions.

Review the trade report workflow

Frequently asked questions

Educational information, not investment advice. Positions, prices, exchange rates, equity and costs are hypothetical. Portfolio heat is a planned stop-risk calculation, not a guaranteed loss limit, probability forecast or live account monitor.

Sources and verification

Published by Pineify. Pineify is an independent product and is not affiliated with TradingView. Sources support the technical explanations; examples do not establish trading performance.