Quantstrat自定义订单规模函数问题:全仓比特币与B&H策略对比
Hey there! Let's dig into this quantstrat order sizing problem you're dealing with—getting 100% of your available equity into Bitcoin to match a buy-and-hold (B&H) strategy is a super common goal, so let's figure out why your order quantities aren't calculating right and fix it step by step.
Your hunch about price matching is probably on point. Here are the two most likely culprits:
- Future data bias: If you're using the current bar's
Cl(mktdata)(closing price) to calculate order size, quantstrat might be blocking that (since you can't know the close price at the start of the bar) or using a lagged value, throwing off your quantity. - Misaligned equity calculation: You might be pulling total account equity (including open positions) instead of available equity that's actually free to deploy.
Let's write a function that strictly uses available equity and a tradeable price (like the bar's open price, which avoids future data issues) to calculate how much Bitcoin to buy. We'll also add handling for minimum trade units (common in crypto):
full_btc_allocation <- function(data, timestamp, orderqty, ordertype, orderside, portfolio, account, symbol, ruletype, ...) { # Grab available account equity at the current timestamp available_eq <- getEndEq(account, timestamp) # Use the bar's open price (no future data here!) current_price <- as.numeric(Op(mktdata[timestamp, ])) # Calculate max BTC we can buy, rounded down to 0.0001 (common minimum unit) max_qty <- floor(available_eq / current_price * 10000) / 10000 # Return positive quantity for long positions (adjust for short if needed) if (orderside == "long") { return(max_qty) } else { return(-max_qty) } }
Then bind this function to your strategy so quantstrat uses it for long orders:
# Replace "your_strategy_name" with your actual strategy object addOrderSize(strategy = your_strategy_name, type = "long", fun = full_btc_allocation)
To confirm you're not hitting future data issues:
Always use
Op(mktdata)(open price) orHi(mktdata)/Lo(mktdata)for order sizing in backtests. UsingCl(mktdata)assumes you can trade at the close price before the bar ends, which is unrealistic and will lead to skewed results.
Also, double-check your data timestamps:
- Make sure your Bitcoin bar data (daily, hourly, etc.) has timestamps that align with quantstrat's account equity calculations. For daily bars, this means using timestamps at the start of the trading period (not the close) to ensure equity and price are synced.
Add debug prints to your order sizing function to spot discrepancies between expected and actual quantities:
full_btc_allocation <- function(data, timestamp, orderqty, ordertype, orderside, portfolio, account, symbol, ruletype, ...) { available_eq <- getEndEq(account, timestamp) current_price <- as.numeric(Op(mktdata[timestamp, ])) max_qty <- floor(available_eq / current_price * 10000) / 10000 # Print debug info to cross-check cat(paste0("Timestamp: ", timestamp, " | Available Equity: $", round(available_eq, 2), " | BTC Open Price: $", round(current_price, 2), " | Order Qty: ", round(max_qty, 4), "\n")) if (orderside == "long") { return(max_qty) } else { return(-max_qty) } }
Run your backtest and compare the printed values to manual calculations (available equity ÷ BTC open price). If they don't match, check:
- Are fees being deducted? Your
addFeesettings will reduce available equity—make sure they're configured correctly. - Do you have existing open positions? If your strategy starts with a position, available equity will be reduced by that position's value.
To make sure your strategy matches B&H exactly:
- Add a rule to only place one buy order on the first bar (no rebalancing or additional trades).
- Compare your strategy's final equity to a manual B&H calculation:
(initial_equity / first_bar_open_price) * last_bar_close_price. Any small difference should come from fees or slippage (which you can adjust in your strategy settings).
内容的提问来源于stack exchange,提问作者DerWanderer

