Would Adding Transaction Costs Change the Conclusion? Checking Accounting and Risk Paths in a Synthetic Portfolio
Evidence and scope — Synthetic calculations and supplied run records
Synthetic SYN_A/B/C inputs illustrate cost, turnover, and drawdown accounting. The PASS cited in the article comes from project-supplied run records, not an independent rerun. It does not validate real ETF performance or broker costs.
Once we matched quantities and ending marked values in Part 4, the next question is whether the accounting still reconciles when it costs money to buy those positions. Using the same synthetic SYN_A/B/C inputs, this article adds transaction costs and shows that ending return, turnover, and maximum drawdown answer different questions.
SPY, IEF, and GLD were mentioned in Parts 1–4 as candidates for U.S. ETF research, but the underlying historical prices, trading dates, dividend data, and split data have not yet been obtained. This is therefore not a market backtest of real ETFs or an analysis of live trading performance. The cost rate is not an estimate of actual broker commissions; it is a sensitivity assumption for examining how the results change when the accounting rule changes.
The synthetic inputs are the same as in Part 4. SYN_A, SYN_B, and SYN_C all have an execution price of 100, with ending marked prices of 120, 90, and 120, respectively. Initial capital is 1,000. The buy-and-hold strategy initially buys the three assets at equal weights and then holds fixed quantities. The momentum strategy holds only SYN_A, which received the positive signal. The ending value discussed here is the marked value of the holdings; it does not include liquidation costs from selling all positions at the end.
Apply the cost rate to the actual purchase notional, not initial capital
Define the cost rate in basis points as follows.
c = bp / 10000To use all 1,000 of initial capital exactly across purchase notional and transaction costs, the actual purchase notional N must satisfy the following budget constraint.
N + cN = 1000N = 1000 / (1 + c)Cost = cNIn other words, N + cost = 1000. Calculating the cost simply as c × 1000 and subtracting it from initial capital can be a convenient approximation, but it is distinct from the formula above, which exhausts the budget exactly after including costs.
For example, 10 bp means c = 0.001. The actual purchase notional is then 1000 / 1.001, and the cost is 0.1% of that purchase notional. Fraction-based calculations are well suited to checking these identities without rounding error. Python’s standard-library Fraction provides rational-number arithmetic. [S1]
from fractions import Fraction as Finitial_cash = F(1000)cost_bp = F(10)c = cost_bp / F(10000)notional = initial_cash / (F(1) + c)entry_cost = c * notionalassert notional + entry_cost == initial_cashHere, notional is the actual purchase amount and entry_cost is the initial entry cost. This small check does not judge whether a strategy is good or bad. Instead, it verifies which amount the cost was applied to and whether any cash was left unused or overspent.
Rankings remain unchanged when both strategies have the same entry cost
Without costs, Part 4 produced ending marked values of 1,100 for buy and hold and 1,200 for momentum. Applying only an initial entry cost reduces the amount actually invested at inception to 1000 / (1+c). The ending marked value therefore declines by the same proportion.
Buy-and-hold ending marked value = 1100 / (1 + c)Momentum ending marked value = 1200 / (1 + c)Return = Ending marked value / 1000 - 1| Cost scenario | Initial cost | Buy-and-hold ending marked value | Momentum ending marked value |
|---|---|---|---|
| 0bp | 0 | 1100 | 1200 |
| 5bp | 0.49975012493753124 | 1099.4502748625687 | 1199.400299850075 |
| 10bp | 0.999000999000999 | 1098.901098901099 | 1198.8011988011988 |
| 20bp | 1.996007984031936 | 1097.804391217565 | 1197.6047904191616 |
In this synthetic example, both strategies are scaled by the same factor, 1 / (1+c). As a result, momentum’s ending marked value remains higher than buy and hold under every scenario: 0, 5, 10, and 20 bp.
That does not establish a general rule that costs never change conclusions. The effect of costs can differ when strategies have different trade counts, rebalancing dates, buy and sell directions, cost rates, or cash holdings. This result is only an arithmetic check for one synthetic ledger in which the same initial entry cost is applied. In particular, the table reports the marked value of positions at the end date—not cash after liquidation—because liquidation costs have not yet been deducted.
The project-provided execution record states that a synthetic cost check using standard Python fractions passed on 2026-09-18. However, it could not be independently rerun in the current research environment, so that PASS result should be treated only as a project-provided record. The formula representation using Fraction itself is documented in the standard library. [S1]
Define turnover before evaluating a full replacement
Unlike an initial entry, a complete replacement involves both selling the existing asset and buying another. Assume portfolio value immediately before trading is 1,000, the entire position is replaced at the same price, and the cost rate is c.
Sale proceeds are 1,000. After accounting for both the selling cost and the cost of buying the new asset, the actual purchase notional is:
Sale proceeds = 1000Purchase notional N = 1000 × (1 - c) / (1 + c)Total cost = c × (1000 + N)N + Total cost = 1000For this project’s cost-sensitivity exercise, turnover is defined as follows.
turnover = (Sale proceeds + Purchase notional) / Portfolio value immediately before tradingThis definition includes both the sale and the purchase in the numerator and does not use a 1/2 factor. Cash itself is also not counted as trading notional. At 10 bp, total cost and turnover are both exactly 2000 / 1001; 1.998001998 is the rounded display value.
from fractions import Fraction as Fdef entry_notional(cash, cost_rate): return cash / (F(1) + cost_rate)def switch_notional(value_before_trade, cost_rate): return value_before_trade * (F(1) - cost_rate) / (F(1) + cost_rate)cash = F(1000)c = F(10, 10000)entry = entry_notional(cash, c)assert entry + c * entry == cashbuy = switch_notional(cash, c)cost = c * (cash + buy)turnover = (cash + buy) / cashassert buy + cost == cashassert cost == F(2000, 1001)assert turnover == F(2000, 1001)This turnover measure should not be confused with portfolio turnover disclosed by funds. U.S. fund disclosure turnover is a separate metric based on measures such as average portfolio value during the most recent fiscal year. High turnover may be associated with transaction costs and taxes, but a disclosed turnover figure cannot be inserted directly into this project’s cost formula. [S3]
Transaction costs may also include more than commissions. Spreads, market impact, and opportunity costs are also discussed as cost components. [S4] When real data are added, specify what c includes and avoid deducting spreads or slippage again as separate items if they have already been included.
The same ending return can conceal different loss paths
Looking only at ending value can make two strategies or portfolios appear to have the same risk too easily. The following separate synthetic paths are unrelated to the Part 4 strategies and are included only to illustrate the concept of a loss path.
| Synthetic path | Ending return | Maximum drawdown at observed points |
|---|---|---|
[1000, 1050, 1100] | 10% | 0% |
[1000, 800, 1100] | 10% | -20% |
Both paths end with 1100 / 1000 - 1 = 10%. But the first path never falls below its prior high at any observed point, so its maximum drawdown at the observed points is 0%. The second falls from 1,000 to 800, producing a maximum drawdown of -20%.
Maximum drawdown measures the decline from a prior peak to a subsequent trough, and its value can vary with the analysis window and observation frequency. [S5] Therefore, the -20% above is not a drawdown reconstructed from daily prices for an actual strategy; it is calculated from the three points shown. Ending return asks, “Where did the portfolio end up?” Maximum drawdown asks, “What losses did it experience along the way?”
Why CAGR, annualized volatility, and Sharpe are not calculated yet
The current synthetic example provides only an execution price and ending marked price for a single undated interval. It does not specify actual trading dates, daily observation intervals, a sufficient return sample, a benchmark or risk-free rate, an annualization factor, or a sample-standard-deviation formula.
For that reason, no CAGR, annualized volatility, or Sharpe figures are reported. An ex post Sharpe ratio uses the mean and standard deviation of excess returns, so a single ending value cannot provide the required return sample. [S6] Omitting those figures is not incomplete calculation; it is scope control intended to avoid manufacturing plausible-looking metrics without the data and assumptions they require.
Once real data are available, the work should begin with checks of the raw data, date alignment, mapping signal dates to execution and valuation dates, creating daily—or explicitly stated periodic—returns, selecting a benchmark or risk-free rate and annualization factor, and documenting the sample-standard-deviation formula. Ending return and maximum drawdown can then be calculated from the same daily path.
Preparing for the model comparison in Part 6
Before moving to the logistic comparison in Part 6, the project needs real time-series data and a validation design that respects time order. It must verify the raw price data and dates, fix the definitions of cost components and turnover, and calculate returns and loss paths at the same observation frequency.
In time-series prediction, autocorrelation and nonstationarity can make ordinary cross-validation difficult to apply directly, and the validity of standard K-fold also depends on assumptions that include the error structure. [S7] Rather than using arbitrary shuffling or claiming completed monthly retraining, the project should first design validation rules that preserve time order.
The SYN_A/B/C examples and cost scenarios in this article are synthetic accounting examples for education and research. They do not represent the performance of actual SPY, IEF, or GLD, actual transaction costs, or the superiority of any strategy. They are not a recommendation to buy or sell any ETF and do not guarantee returns.
Sources
- [S1] fractions — Rational numbers | Python Software Foundation | 2026-09-16 | https://docs.python.org/3.14/library/fractions.html ↩
- [S3] Form N-1A | U.S. Securities and Exchange Commission | 2023-12-11 | https://www.sec.gov/files/form-n-1a.pdf ↩
- [S4] Request for Comments on Measures To Improve Disclosure of Mutual Fund Transaction Costs | U.S. Securities and Exchange Commission | 2003-12-19 | https://www.sec.gov/rules-regulations/2003/12/request-comments-measures-improve-disclosure-mutual-fund-transaction-costs ↩
- [S5] Sculpting Investment Portfolios: Maximum Drawdown and Optimal Portfolio Strategy | Prasad Ramani, CFA / CFA Institute | 2013-02-12 | https://blogs.cfainstitute.org/blog/2013/02/12/sculpting-investment-portfolios-maximum-drawdown-and-optimal-portfolio-strategy/ ↩
- [S6] The Sharpe Ratio | William F. Sharpe / Stanford University | Fall 1994 | https://web.stanford.edu/~wfsharpe/art/sr/sr.htm ↩
- [S7] A note on the validity of cross-validation for evaluating autoregressive time series prediction | Christoph Bergmeir, Rob J. Hyndman, Bonsoo Koo | 2018-01-01 | https://talks.robjhyndman.com/publications/cv-time-series/ ↩
Report an error or share feedback
Open a draft with this article’s title and URL. Review the message and recipient before sending.
Open email draftIf no email app opens, copy these details into your usual email service.
Related posts
Quant & Data Research What to Look at Before Returns: The Path of Losses and Trading Costs
Use hypothetical examples to examine loss paths and trading costs alongside returns, and interpret volatility, drawdown, Sharpe ratios, and turnover together.
Quant & Data Research The First Baseline: Building a Simple Momentum Strategy
Before comparing AI and ML models, fix a simple long/cash momentum baseline that will not change after you see the results. How to build the 12-month signal, monthly rebalancing, equal weighting, and trading-cost scenarios as reproducible Python code.