← Назад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}")