Forge Fate
Quant & Data Research

Would Adding Transaction Costs Change the Conclusion? Checking Accounting and Risk Paths in a Synthetic Portfolio

7 min read

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.

text
c = bp / 10000

To 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.

text
N + cN = 1000N = 1000 / (1 + c)Cost = cN

In 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]

python
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_cash

Here, 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.

text
Buy-and-hold ending marked value = 1100 / (1 + c)Momentum ending marked value = 1200 / (1 + c)Return = Ending marked value / 1000 - 1
Cost scenarioInitial costBuy-and-hold ending marked valueMomentum ending marked value
0bp011001200
5bp0.499750124937531241099.45027486256871199.400299850075
10bp0.9990009990009991098.9010989010991198.8011988011988
20bp1.9960079840319361097.8043912175651197.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:

text
Sale proceeds = 1000Purchase notional N = 1000 × (1 - c) / (1 + c)Total cost = c × (1000 + N)N + Total cost = 1000

For this project’s cost-sensitivity exercise, turnover is defined as follows.

text
turnover = (Sale proceeds + Purchase notional) / Portfolio value immediately before trading

This 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.

python
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 pathEnding returnMaximum 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

Report an error or share feedback

Open a draft with this article’s title and URL. Review the message and recipient before sending.

To: [email protected]

Open email draft

If no email app opens, copy these details into your usual email service.

Contact information