research: add per-year trade counts + turnover to trackD_timing
This commit is contained in:
@@ -77,7 +77,9 @@ def run_asset(df, bars_per_day, target_vol=TARGET_VOL, leverage=LEVERAGE,
|
|||||||
direction = sig_tsmom_blend(c, horizons=(h1, h3, h6))
|
direction = sig_tsmom_blend(c, horizons=(h1, h3, h6))
|
||||||
tgt = build_target(direction, vol, target_vol, leverage, long_only)
|
tgt = build_target(direction, vol, target_vol, leverage, long_only)
|
||||||
equity, net = equity_from_target(tgt, r, fee_side)
|
equity, net = equity_from_target(tgt, r, fee_side)
|
||||||
return dict(net=net, ts=df["datetime"], equity=equity, bpy=bpy)
|
# discrete position SIGN for trade counting (entry = sign change to a new non-zero state)
|
||||||
|
sign = np.sign(tgt)
|
||||||
|
return dict(net=net, ts=df["datetime"], equity=equity, bpy=bpy, sign=sign, target=tgt)
|
||||||
|
|
||||||
|
|
||||||
def portfolio_series(sleeves):
|
def portfolio_series(sleeves):
|
||||||
@@ -118,6 +120,20 @@ def per_year(idx, equity):
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def trades_per_year(sleeves):
|
||||||
|
"""Count entries per year, summed across both sleeves. An 'entry' = the position SIGN
|
||||||
|
changing to a new non-zero value (flat->long, flat->short, or a direction flip)."""
|
||||||
|
counts: dict[int, int] = {}
|
||||||
|
for a in ASSETS:
|
||||||
|
sign = sleeves[a]["sign"]
|
||||||
|
ts = pd.to_datetime(sleeves[a]["ts"].values)
|
||||||
|
for i in range(1, len(sign)):
|
||||||
|
s, prev = sign[i], sign[i - 1]
|
||||||
|
if s != 0 and s != prev: # entry: from flat or opposite into a non-zero state
|
||||||
|
counts[ts[i].year] = counts.get(ts[i].year, 0) + 1
|
||||||
|
return counts
|
||||||
|
|
||||||
|
|
||||||
ALL_YEARS = list(range(2018, 2027))
|
ALL_YEARS = list(range(2018, 2027))
|
||||||
|
|
||||||
|
|
||||||
@@ -139,14 +155,18 @@ def main():
|
|||||||
ov = overall_metrics(idx, combo, equity, sleeves["BTC"]["bpy"])
|
ov = overall_metrics(idx, combo, equity, sleeves["BTC"]["bpy"])
|
||||||
py = per_year(idx, equity)
|
py = per_year(idx, equity)
|
||||||
|
|
||||||
|
tpy = trades_per_year(sleeves)
|
||||||
|
total_trades = sum(tpy.values())
|
||||||
print(f"\n ── TF {tf_key:<3s} │ ret {ov['total']*100:>+8.0f}% CAGR {ov['cagr']*100:>+6.1f}% "
|
print(f"\n ── TF {tf_key:<3s} │ ret {ov['total']*100:>+8.0f}% CAGR {ov['cagr']*100:>+6.1f}% "
|
||||||
f"Sharpe {ov['sharpe']:>4.2f} maxDD {ov['max_dd']*100:>4.1f}% "
|
f"Sharpe {ov['sharpe']:>4.2f} maxDD {ov['max_dd']*100:>4.1f}% "
|
||||||
f"€/day(2k) {ov['daily_2k']:>+6.2f}")
|
f"€/day(2k) {ov['daily_2k']:>+6.2f} trades {total_trades}")
|
||||||
# per-year PnL row
|
# per-year PnL / DD / trades rows
|
||||||
print(f" {'PnL %':<8s}" + "".join(
|
print(f" {'PnL %':<8s}" + "".join(
|
||||||
(" . " if y not in py else f"{py[y][0]*100:>+7.0f}") for y in ALL_YEARS))
|
(" . " if y not in py else f"{py[y][0]*100:>+7.0f}") for y in ALL_YEARS))
|
||||||
print(f" {'maxDD %':<8s}" + "".join(
|
print(f" {'maxDD %':<8s}" + "".join(
|
||||||
(" . " if y not in py else f"{py[y][1]*100:>7.1f}") for y in ALL_YEARS))
|
(" . " if y not in py else f"{py[y][1]*100:>7.1f}") for y in ALL_YEARS))
|
||||||
|
print(f" {'trades':<8s}" + "".join(
|
||||||
|
(" . " if y not in py else f"{tpy.get(y,0):>7d}") for y in ALL_YEARS))
|
||||||
# year header for reference
|
# year header for reference
|
||||||
print("\n " + "year ".ljust(8) + "".join(f"{y:>7d}" for y in ALL_YEARS))
|
print("\n " + "year ".ljust(8) + "".join(f"{y:>7d}" for y in ALL_YEARS))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user