← Назад
import json from collections import defaultdict with open('/home/app/trading-bot-bybit/data/trade_log.json') as f: trades = json.load(f) closes = [t for t in trades if t.get('event') == 'DCA_CLOSE'] print(f'Total closed trades: {len(closes)}\n') SCALE = 1.3; MAX_SO = 4; LEV = 3 trade_data = [] for t in closes: sos = t.get('safety_orders_filled', 0) pnl = t['pnl_usdt'] reason = t['reason'] invested = t.get('total_invested', 0) entry = t.get('avg_entry', 0) close_p = t.get('close_price', 0) side = t['side'] if entry > 0 and close_p > 0: if side == 'BUY': pnl_pct = (close_p - entry) / entry * 100 else: pnl_pct = (entry - close_p) / entry * 100 else: pnl_pct = (pnl / invested * 100) if invested > 0 else 0 trade_data.append({ 'pnl_pct': pnl_pct, 'sos': sos, 'reason': reason, 'symbol': t['symbol'], 'pnl_usd': pnl, 'invested': invested, }) def calc_invested(bo, sos_filled): total = bo for i in range(sos_filled): total += bo * (SCALE ** i) return total def calc_pnl_with_fees(pnl_pct_gross, bo, sos_filled, reason): invested = calc_invested(bo, sos_filled) notional = invested * LEV gross = notional * pnl_pct_gross / 100 entry_fee = bo * LEV * 0.00055 so_fees = sum(bo * (SCALE ** i) * LEV * 0.0002 for i in range(sos_filled)) exit_rate = 0.0002 if reason == 'TP%' else 0.00055 exit_fee = notional * exit_rate return gross - entry_fee - so_fees - exit_fee bo_sizes = [3, 4, 5, 7, 10, 15, 20] all_trades = trade_data last_54 = trade_data[-54:] print('=' * 100) print('PROFIT SIMULATION: Different BO sizes') print('SO = BO, scale 1.3x, max 4 SOs, 3x leverage') print('=' * 100) header = f" {'BO':>5} | {'Max/Deal':>9} | {'6 Deals':>9} | {'All 244':>10} | {'Last 54':>10} | {'Avg/trade':>10} | {'Daily ~':>10}" print(f"\n{header}") print(" " + '-' * 80) for bo in bo_sizes: max_deal = calc_invested(bo, MAX_SO) max_6 = max_deal * 6 pnl_all = sum(calc_pnl_with_fees(t['pnl_pct'], bo, t['sos'], t['reason']) for t in all_trades) pnl_last = sum(calc_pnl_with_fees(t['pnl_pct'], bo, t['sos'], t['reason']) for t in last_54) avg = pnl_all / len(all_trades) daily = pnl_last * 2 print(f" ${bo:<3} | ${max_deal:>7.0f} | ${max_6:>7.0f} | ${pnl_all:>+8.2f} | ${pnl_last:>+8.2f} | ${avg:>+8.4f} | ${daily:>+8.2f}") # Detailed last 54 print(f"\n{'=' * 100}") print("LAST 54 TRADES (post SL 10% + Time Stop + SO Guard) — by reason") print('=' * 100) for bo in [3, 4, 5, 7, 10]: max_deal = calc_invested(bo, MAX_SO) by_reason = defaultdict(lambda: {'count': 0, 'pnl': 0, 'wins': 0}) for t in last_54: pnl = calc_pnl_with_fees(t['pnl_pct'], bo, t['sos'], t['reason']) by_reason[t['reason']]['count'] += 1 by_reason[t['reason']]['pnl'] += pnl if pnl > 0: by_reason[t['reason']]['wins'] += 1 total = sum(d['pnl'] for d in by_reason.values()) print(f"\n BO ${bo} | Total: ${total:+.2f} | Max/deal: ${max_deal:.0f} | 6 deals: ${max_deal*6:.0f}") for r in ['TP%', 'Z-TP', 'SL', 'TIME-STOP', 'SO-GUARD']: d = by_reason.get(r) if not d: continue wr = d['wins'] / d['count'] * 100 print(f" {r:<12} {d['count']:>3}x WR {wr:>5.1f}% PnL ${d['pnl']:+.2f}") # Top losers print(f"\n{'=' * 100}") print("TOP LOSERS scaled (last 54 trades)") print('=' * 100) worst = sorted(last_54, key=lambda t: t['pnl_pct'])[:7] line = f" {'Symbol':<15} {'SOs':>4} {'PnL%':>7} | {'BO$3':>8} {'BO$4':>8} {'BO$5':>8} {'BO$7':>8} {'BO$10':>8}" print(f"\n{line}") print(" " + "-" * 80) for t in worst: pnls = [calc_pnl_with_fees(t['pnl_pct'], bo, t['sos'], t['reason']) for bo in [3, 4, 5, 7, 10]] pnl_str = ' '.join(f'${p:>+6.2f}' for p in pnls) print(f" {t['symbol']:<15} {t['sos']:>4} {t['pnl_pct']:>+6.1f}% | {pnl_str}") # Balance print(f"\n{'=' * 100}") print("BALANCE REQUIREMENT") print('=' * 100) line2 = f" {'BO':>5} | {'Per Deal':>9} | {'6 Deals':>9} | {'+20% buf':>10} | {'$70':>6} | {'$100':>6} | {'$150':>6}" print(f"\n{line2}") print(" " + "-" * 65) for bo in bo_sizes: max_d = calc_invested(bo, MAX_SO) six = max_d * 6 buf = six * 1.2 f70 = 'ok' if buf <= 70 else 'NO' f100 = 'ok' if buf <= 100 else 'NO' f150 = 'ok' if buf <= 150 else 'NO' print(f" ${bo:<3} | ${max_d:>7.0f} | ${six:>7.0f} | ${buf:>8.0f} | {f70:>4} | {f100:>4} | {f150:>4}")