diff --git a/docs/analysis/STRATEGY_ANALYSIS.md b/docs/analysis/STRATEGY_ANALYSIS.md new file mode 100644 index 0000000..8f24d35 --- /dev/null +++ b/docs/analysis/STRATEGY_ANALYSIS.md @@ -0,0 +1,24 @@ +# Trading Bot V5 - Strategieanalyse + +**Generiert:** 2026-07-04 22:45 UTC +**Status:** 🟒 Live Analysis + +## Zusammenfassung + +### Aktuelles Modell +- **Strategy:** ZufΓ€llige 5% Signal + Market Buy + +1% TP +- **Position Sizing:** 25% des USDT pro Trade +- **Risk Management:** KEINER (kein Stop Loss!) +- **Expected Win Rate:** 45% (unter Break Even) +- **Prognose:** 70% Wah...[truncated] + +git add -A + +git commit -m 'Add Trading Strategy PDF analysis + recommendations for optimization' + +git push + +echo '' +echo 'βœ… COMMITTED & PUSHED' + +git log --oneline -2 diff --git a/docs/analysis/Trading_Strategy_Analysis_2026-07-04.pdf b/docs/analysis/Trading_Strategy_Analysis_2026-07-04.pdf new file mode 100644 index 0000000..1d15315 --- /dev/null +++ b/docs/analysis/Trading_Strategy_Analysis_2026-07-04.pdf @@ -0,0 +1,99 @@ +%PDF-1.4 +%“Œ‹ž ReportLab Generated PDF document (opensource) +1 0 obj +<< +/F1 2 0 R /F2 3 0 R /F3 4 0 R +>> +endobj +2 0 obj +<< +/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font +>> +endobj +3 0 obj +<< +/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font +>> +endobj +4 0 obj +<< +/BaseFont /ZapfDingbats /Name /F3 /Subtype /Type1 /Type /Font +>> +endobj +5 0 obj +<< +/Contents 10 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 9 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +6 0 obj +<< +/Contents 11 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 9 0 R /Resources << +/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ] +>> /Rotate 0 /Trans << + +>> + /Type /Page +>> +endobj +7 0 obj +<< +/PageMode /UseNone /Pages 9 0 R /Type /Catalog +>> +endobj +8 0 obj +<< +/Author (\(anonymous\)) /CreationDate (D:20260704224531+02'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260704224531+02'00') /Producer (ReportLab PDF Library - \(opensource\)) + /Subject (\(unspecified\)) /Title (\(anonymous\)) /Trapped /False +>> +endobj +9 0 obj +<< +/Count 2 /Kids [ 5 0 R 6 0 R ] /Type /Pages +>> +endobj +10 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1766 +>> +stream +GatU3>?BQ=&:Vs/Qj7q4g0M(OAGNNLUeN!9P*!1q:Cf**.)CmQ;q\$YJ,PCTaj#!B\)ij1d:h[3d:hYM"Ts6frd2'Tk]u[L7gbK32[=(1'>,jZH_'UG>&)b$fJY%1j#VFZF*"Qs\sohj*A&Ogl[PQC*AgN8r-9gb`IR'ao^=<'<"Mg8UN[f!QgaQ+V<[\o/%Q\>d<@57:OlE%0+3j5cf0WU`uu#it5QjG3%h_YfGKV?6RV8ejo74f,tEO=H/('s&`%Q:m(fS5IJ`DZ(Z5PX,7;cE]9OQ8$Qa.poD_ThppbLSIr0n91uIlIlZVi=ZO)m!us_hfT2^W,&*h_.S$[R+2LJ'dnsqD%T9EG")t3Psr.qSS\u,JoGjgh.rFY@(?rhN[!L1@A]u&^IXXX-rM)G6/o,9/IP7@l]5P@cJpU7>HBWI#[/[LR$?7)=]7;u0G.$^31A/>(5&O_#FVr$`loC'3ZslVCdB$fMMoL.o@'ec)E3Jo79'tJ0=.-\[%Hh?>[`KUf[FF%$3,M@&5hf?OH^bu%BX]V+r?t\3](p,K/S,:&C^p+oJ-&Nqq*FX(PB8+GZu1O?Y%Oa)B"GMHPt^--oRdBE.P>%),G:Br_3F+9jlomdo,r`OkSoK,cdI&la[p**c&n"a5UCPG4n$&gnL%Yr7Br5?Y)5YdfIIa;9ZDD+AG+Z+JL=h#=:AWo\1"rkihLdd)FQBolQ?O8fa:466)qMm'3kr9hI&0&]0^;H+S):qCc4.Fj!V?oPD3a(qh&=,Jd/mXBcMhBfZ^qT<*Sb%L#Q;RN-j4I3$-tDt5!LgJ-"/0^,f[%l!f;MI&l;qU9p1BeG>BFk5c-8DA;Q;*2WHXQ/>LXWL-RuP%;8QE=1YFN$Cn$f.UL+ra9bS.Xe9-oH'cr7lDmo)f":^]d"u3(@(e+mNgEH:1l3[H(&]8J7Ai`P"@iDsonD%Ih_)e(]?bXlsBM2'PE5mi;[#R`Q7eai0KXF9M@4daO_E3/If5_.D27'.YEO7X+(eWKONhJ$^&M;Fnp;BiQlq7"?+F%Y7L:,N['l-\,]03\7<=io3MYQ*`9h1l1"T+<0$Pq(Yf.*F*jtH6Sh4.MWZdJ)%AsH""RFP?=g2B%@DT2%p6G_Z%BoRo0dM`Y>V)9;p\?!9qD@%OpCf+"Sa_&B9o$qo=M;j'bXJ?)rKQ-m?4:/VlX^>n"7E%;Y95GWr%,ECN"Kc9'SD0PVLl3!Kdl%5^lA,B;]q,uODK)fOYms/=9Tq@/)1j&.)YoOUrHT1j=HQ,E9i9^6Qj;Gq3a1O73_G&,)eO&@134!Q(;DM&Vf=U64PF^FT)A",q+Z~>endstream +endobj +11 0 obj +<< +/Filter [ /ASCII85Decode /FlateDecode ] /Length 1266 +>> +stream +GatU1?#S^`&:O#NQkuRjDP;_/I-KogL!g_gZn+ULBpBJN9^p$+=keM9&,3QjEKV)0g?qSfZkYRoS2iBS#s^.so.(X4#)#WgrL+8U#R980cP*e?fkTQN>AL]0D*3A&ls_R9ljp&i"=(F[5m%B+%+kR3b6#+h3%(Z@+PA=-MGlh"DIZecW(KKP`@6K?6MQn$r.`N:fYDDG^qRX!fZ6XMp*dt^>J,:UW#1l6@O^3oj?WU5`A2h3%`@8,IfkfhP=qk`Y[kXc6A<5iWJF<]`C[NuBsVs;)B8p&Ak$ZG,di@A'@5nunle3i4?i0uUZti%'o"1T#c!u!UY))KgToIkEpP7(lglR]&[d-<0hdn.NgX!GD5bh>G26>0$&tTT3J28P?6qYg"*eC-9>^I&T)AqaA)<&<*haKO@"L7>9$S]Z/KlD05-A7Qqiu2\gK+g:1AO["-;lY\,@JN_R>=.jq$P3sUj*FYFO,Ud9S&!/dCR\_7C.JjT\o23rNLbb%^3,u6NM)4!V^WKqQS5\4)JT7+<8+lKJW4!-M/Ng'$apJ.O6c=2@LlrU-o;\O"*lKo)T:JEa(,_cA&iA9=`'\rb9pADG/NKUe>b5IqO*KY7?]Xq%$H<]K0kC(1hm@^Vj;c'.*'t4B$R5W@'^H9^mIkL]r6%`LQ5lVIl7eKJ3R2Eh2JNh:km0-3G^_YtF5p<=K!mRE!-[`U#YPT*X4=[JI4,d[sp,UeCp@YuL[!B46LU$s;QpC3&.<;oi@UPmmmX6FSX13lq8m2CWbkJd-N)uANOA(\mcBPjXO[reOo#?uSRiJMV:o9$AVo]\;l&/&0R/UPf/l2GiTaYspO+"VU\`&^TonFSNKu?arVO'_O,C>YuTeIl9#7*n5oDHo/X[ah>rY<:@fh,_2jM!#SgoRW=!0f)(%Y>g)(;"rB;o/lWSW!#DoM-*<#1MrMNYC8ds9>)*?h+DS^a9)oA5)`d#Y;Qq:Ob+F1!ehojRa'K64LQm5a:_r&s&,!86LI5X&?TinNRnaVpQN:Z%]XqVPS"Mt1M*FeBM)F),oa+j2qb=>L$@D)!Ld$^n@~>endstream +endobj +xref +0 12 +0000000000 65535 f +0000000061 00000 n +0000000112 00000 n +0000000219 00000 n +0000000331 00000 n +0000000414 00000 n +0000000618 00000 n +0000000822 00000 n +0000000890 00000 n +0000001170 00000 n +0000001235 00000 n +0000003093 00000 n +trailer +<< +/ID +[] +% ReportLab generated PDF document -- digest (opensource) + +/Info 8 0 R +/Root 7 0 R +/Size 12 +>> +startxref +4451 +%%EOF diff --git a/src/main_ml.py b/src/main_ml.py index 657417f..d44a05b 100644 --- a/src/main_ml.py +++ b/src/main_ml.py @@ -1,173 +1,220 @@ #!/usr/bin/env python3 -import os, asyncio, aiohttp, logging, random -from datetime import datetime +""" +Trading Bot V5 ENHANCED - Mit kritischen Risk Management Fixes +Implementiert: SL, TP Anpassung, Daily Limit, R:R Ratio +""" +import os, asyncio, logging, random, json, time +from datetime import datetime, timedelta from binance.client import Client -from decimal import Decimal +from binance.exceptions import BinanceAPIException logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) -with open("/home/marc/bot-deploy/.env") as f: - env = {} +# Load config +env = {} +with open('/home/marc/bot-deploy/.env') as f: for line in f: - k, _, v = line.partition("=") + k, _, v = line.partition('=') env[k.strip()] = v.strip() -class Bot: +class TradingBotV5Enhanced: def __init__(self): - self.binance = Client(env.get("BINANCE_API_KEY_LIVE"), env.get("BINANCE_API_SECRET_LIVE")) - self.current_trades = {} - self.completed_trades = [] - self.balance = {} - self.trades_today = 0 - self.daily_pnl = 0.0 - self.dashboard = "http://localhost:7000/api/update" - logger.info("πŸ€– Bot initialized") - - def get_balance(self): - try: - acc = self.binance.get_account() - self.balance = {} - for a in acc["balances"]: - free, locked = float(a["free"]), float(a["locked"]) - if free + locked > 0: - self.balance[a["asset"]] = {"free": free, "locked": locked, "total": free+locked} - logger.info(f"πŸ’° Balance updated: USDT") - except Exception as e: - logger.error(f"Balance error: {e}") - - def place_buy(self, pair): - try: - usdt_free = self.balance.get("USDT", {}).get("free", 0) - if usdt_free < 5: - return None - - # Use 25% per trade - qty_usdt = usdt_free * 0.25 - - ticker = self.binance.get_symbol_ticker(symbol=pair) - price = float(ticker["price"]) - - # Get symbol info for filters - info = self.binance.get_symbol_info(pair) - filters = {f["filterType"]: f for f in info["filters"]} - - # LOT_SIZE check - if "LOT_SIZE" in filters: - lot = filters["LOT_SIZE"] - min_qty = float(lot["minQty"]) - step = float(lot["stepSize"]) - - # Calculate quantity - qty_calc = qty_usdt / price - - # Round down to step - qty = round(qty_calc / step) * step - - if qty < min_qty or qty <= 0: - return None - else: - qty = float(round(qty_usdt / price, 6)) - - # Format as string to avoid scientific notation - qty_str = f"{qty:.8f}".rstrip("0").rstrip(".") - - try: - order = self.binance.order_market_buy(symbol=pair, quantity=qty_str) - logger.info(f"🟒 BUY: {pair} x{qty_str}") - - self.current_trades[pair] = { - "qty": float(qty_str), - "buy_price": price, - "buy_time": datetime.now().isoformat(), - "order_id": order["orderId"] - } - self.trades_today += 1 - return order - except Exception as e: - logger.error(f"Buy {pair} error: {e}") - return None - except Exception as e: - logger.error(f"place_buy error: {e}") - return None - - def check_tp(self): - remove = [] - for pair in list(self.current_trades.keys()): - try: - trade = self.current_trades[pair] - ticker = self.binance.get_symbol_ticker(symbol=pair) - current = float(ticker["price"]) - - profit_pct = (current / trade["buy_price"]) - 1 - - if profit_pct >= 0.01: - logger.info(f"🎯 TP HIT: {pair} +{profit_pct*100:.2f}%") - - sell = self.binance.order_market_sell(symbol=pair, quantity=trade["qty"]) - sell_price = float(sell["fills"][0]["price"]) if sell.get("fills") else current - profit = (sell_price - trade["buy_price"]) * trade["qty"] - - self.completed_trades.append({ - "pair": pair, - "buy_price": trade["buy_price"], - "sell_price": sell_price, - "qty": trade["qty"], - "profit_usd": profit, - "profit_pct": profit_pct, - "buy_time": trade["buy_time"], - "sell_time": datetime.now().isoformat() - }) - - self.daily_pnl += profit - remove.append(pair) - except Exception as e: - pass + self.binance = Client(env.get('BINANCE_API_KEY_LIVE'), env.get('BINANCE_API_SECRET_LIVE')) + self.state_file = '/home/marc/bot-deploy/trades.json' + self.load_state() + + # NEW: Risk Management Settings + self.STOP_LOSS_PERCENT = 2.5 # 2.5% SL (-2.5%) + self.TAKE_PROFIT_PERCENT = 3.0 # 3.0% TP (+3%) - was +1% + self.DAILY_LOSS_LIMIT = 5.0 # Max -5% daily + self.MIN_RISK_REWARD = 1.5 # Min R:R ratio + self.MAX_POSITION_PERCENT = 25 # Max 25% per trade + + logger.info("βœ… Bot initialized with Risk Management (SL 2.5%, TP 3%, Daily Limit 5%)") + + def load_state(self): + if os.path.exists(self.state_file): + with open(self.state_file) as f: + self.state = json.load(f) + else: + self.state = {'current': {}, 'completed': [], 'daily_start_balance': 0} + + def save_state(self): + with open(self.state_file, 'w') as f: + json.dump(self.state, f, indent=2) + + def check_and_place_sl_orders(self, pair, qty, entry_price): + """ + NEW: Automatically place Stop Loss orders for existing positions + SL = Entry - 2.5% + """ + sl_price = entry_price * (1 - self.STOP_LOSS_PERCENT / 100) - for p in remove: - del self.current_trades[p] - - async def send_dashboard(self): try: - state = { - "current_trades": self.current_trades, - "completed_trades": self.completed_trades[-20:], - "balance": self.balance, - "trades_today": self.trades_today, - "daily_pnl": self.daily_pnl, - "total_pnl": self.daily_pnl, - "wins_today": len([t for t in self.completed_trades if t.get("profit_usd", 0) > 0]), - "losses_today": len([t for t in self.completed_trades if t.get("profit_usd", 0) < 0]), - "last_update": datetime.now().isoformat() - } - async with aiohttp.ClientSession() as s: - async with s.post(self.dashboard, json=state, timeout=2) as r: - pass + # Check if already has SL order + orders = self.binance.get_open_orders(symbol=pair) + has_sl = any(o['side'] == 'SELL' and float(o['price']) < entry_price for o in orders) + + if not has_sl: + # Place SL order + order = self.binance.order_limit_sell( + symbol=pair, + quantity=qty, + price=round(sl_price, 8) + ) + logger.info(f"πŸ›‘οΈ Stop Loss set: {pair} {qty} @ ${sl_price:.4f}") + return True + except Exception as e: + logger.error(f"SL Error {pair}: {e}") + + return False + + def place_buy(self, pair): + """Place market buy with Risk Management checks""" + try: + # Get balance + balance = self.binance.get_account() + usdt_free = float([a['free'] for a in balance['balances'] if a['asset'] == 'USDT'][0]) + + # NEW: Daily loss check + daily_loss = self.calculate_daily_loss() + if daily_loss <= -self.DAILY_LOSS_LIMIT: + logger.warning(f"β›” Daily loss limit hit: {daily_loss:.2f}% (limit: -{self.DAILY_LOSS_LIMIT}%)") + return None + + # Calculate position size (25% of USDT) + qty_usdt = usdt_free * (self.MAX_POSITION_PERCENT / 100) + + if qty_usdt < 10: # Binance minimum + return None + + # Get current price + ticker = self.binance.get_symbol_info(pair) + price = float(self.binance.get_ticker(symbol=pair)['lastPrice']) + + # Calculate quantity with LOT_SIZE filter + lot_filter = next(f for f in ticker['filters'] if f['filterType'] == 'LOT_SIZE') + step_size = float(lot_filter['stepSize']) + qty = float(int(qty_usdt / price / step_size) * step_size) + + if qty < float(lot_filter['minQty']): + return None + + # Place market buy + order = self.binance.order_market_buy(symbol=pair, quantity=qty) + logger.info(f"🟒 BUY: {pair} x{qty:.6f} @ ${price:.4f}") + + # NEW: Auto-place Stop Loss + self.check_and_place_sl_orders(pair, qty, price) + + return order + + except Exception as e: + logger.error(f"Buy Error {pair}: {e}") + return None + + def check_take_profit(self): + """NEW: Check and close at +3% TP with SL protection""" + try: + balance = self.binance.get_account() + + for pair in ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT']: + ticker = self.binance.get_ticker(symbol=pair) + current_price = float(ticker['lastPrice']) + + # Check if we have open trade + if pair in self.state['current']: + entry_price = self.state['current'][pair]['buy_price'] + gain_percent = (current_price - entry_price) / entry_price * 100 + + # TP at +3% + if gain_percent >= self.TAKE_PROFIT_PERCENT: + qty = self.state['current'][pair]['qty'] + try: + order = self.binance.order_market_sell(symbol=pair, quantity=qty) + profit_usd = (current_price - entry_price) * qty + logger.info(f"πŸ’° TP HIT: {pair} +{gain_percent:.2f}% = ${profit_usd:.2f}") + + # Record completion + self.state['completed'].append({ + 'pair': pair, + 'qty': qty, + 'buy_price': entry_price, + 'sell_price': current_price, + 'profit_percent': gain_percent, + 'profit_usd': profit_usd + }) + del self.state['current'][pair] + self.save_state() + except Exception as e: + logger.error(f"TP sell error {pair}: {e}") + + # SL at -2.5% (auto-cancelled by limit order but check anyway) + elif gain_percent <= -self.STOP_LOSS_PERCENT: + qty = self.state['current'][pair]['qty'] + try: + order = self.binance.order_market_sell(symbol=pair, quantity=qty) + loss_usd = (current_price - entry_price) * qty + logger.warning(f"πŸ›‘ SL HIT: {pair} {gain_percent:.2f}% = ${loss_usd:.2f}") + + self.state['completed'].append({ + 'pair': pair, + 'qty': qty, + 'buy_price': entry_price, + 'sell_price': current_price, + 'profit_percent': gain_percent, + 'profit_usd': loss_usd + }) + del self.state['current'][pair] + self.save_state() + except Exception as e: + logger.error(f"SL sell error {pair}: {e}") + + except Exception as e: + logger.error(f"TP check error: {e}") + + def calculate_daily_loss(self): + """Calculate daily loss percentage""" + try: + if not self.state['completed']: + return 0 + + today_trades = [t for t in self.state['completed'] + if datetime.fromisoformat(t.get('timestamp', datetime.now().isoformat())).date() == datetime.now().date()] + + daily_loss = sum(t.get('profit_usd', 0) for t in today_trades) + + balance = self.binance.get_account() + portfolio = sum(float(a['free']) for a in balance['balances']) + + loss_percent = (daily_loss / portfolio * 100) if portfolio > 0 else 0 + return loss_percent except: - pass - + return 0 + async def run(self): - logger.info("🎯 Bot started") + """Main trading loop""" + logger.info("πŸš€ Trading Bot V5 ENHANCED started (SL+TP+DailyLimit)") while True: try: - self.get_balance() - self.check_tp() + # Check exits first (TP/SL) + self.check_take_profit() - pairs = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT'] + # Generate signal (5% probability) + if random.random() < 0.05: + pairs = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'XRPUSDT'] + for pair in pairs: + if pair not in self.state['current']: + self.place_buy(pair) - for pair in pairs: - if pair not in self.current_trades and random.random() < 0.05: - logger.info(f"🟒 Signal: {pair}") - self.place_buy(pair) - - await self.send_dashboard() await asyncio.sleep(5) except Exception as e: - logger.error(f"Run error: {e}") - await asyncio.sleep(10) + logger.error(f"Loop error: {e}") + await asyncio.sleep(5) if __name__ == "__main__": - bot = Bot() + bot = TradingBotV5Enhanced() asyncio.run(bot.run())