import ccxt import pandas as pd import numpy as np import mplfinance as mpf from talib import MACD, SMA from datetime import datetime, timedelta import logging import datetime as dt # Configure logging logging.basicConfig( filename='chanlun_trading.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) # Configuration (user to modify) BINANCE_API_KEY = 'your_api_key' # Replace with your Binance API key BINANCE_API_SECRET = 'your_api_secret' # Replace with your Binance API secret SIMULATION_MODE = True # Set to False for live trading # 1. Fetch K-line data from Binance (multi-timeframe support) def fetch_binance_data(symbol='BTC/USDT', timeframe='5m', limit=500): try: exchange = ccxt.binance({ 'apiKey': BINANCE_API_KEY if not SIMULATION_MODE else '', 'secret': BINANCE_API_SECRET if not SIMULATION_MODE else '', 'enableRateLimit': True, 'options': {'defaultType': 'spot'} }) since = exchange.parse8601((datetime.now(dt.UTC) - timedelta(days=7)).isoformat()) ohlcv = exchange.fetch_ohlcv(symbol, timeframe, since, limit) df = pd.DataFrame(ohlcv, columns=['Date', 'Open', 'High', 'Low', 'Close', 'Volume']) df['Date'] = pd.to_datetime(df['Date'], unit='ms') df.set_index('Date', inplace=True) logging.info(f"Fetched {len(df)} K-lines for {symbol} ({timeframe})") return df except Exception as e: logging.error(f"Failed to fetch data: {e}") raise # 2. K-line merging (vectorized) def merge_kline(df): try: df = df.copy() merged_data = [] trend = np.sign(df['Close'].diff().shift(-1)) # 1: up, -1: down, 0: neutral # Detect inclusion is_included = ((df['High'].shift(-1) <= df['High']) & (df['Low'].shift(-1) >= df['Low'])) | \ ((df['High'].shift(-1) >= df['High']) & (df['Low'].shift(-1) <= df['Low'])) i = 0 while i < len(df) - 1: if is_included.iloc[i]: current_k = df.iloc[i] next_k = df.iloc[i + 1] high = max(current_k['High'], next_k['High']) low = min(current_k['Low'], next_k['Low']) open_price = current_k['Open'] close_price = next_k['Close'] if trend.iloc[i] >= 0 else next_k['Close'] volume = current_k['Volume'] + next_k['Volume'] merged_data.append({ 'Date': next_k.name, 'Open': open_price, 'High': high, 'Low': low, 'Close': close_price, 'Volume': volume }) i += 2 else: current_k = df.iloc[i] merged_data.append({ 'Date': current_k.name, 'Open': current_k['Open'], 'High': current_k['High'], 'Low': current_k['Low'], 'Close': current_k['Close'], 'Volume': current_k['Volume'] }) i += 1 if i == len(df) - 1: last_k = df.iloc[i] merged_data.append({ 'Date': last_k.name, 'Open': last_k['Open'], 'High': last_k['High'], 'Low': last_k['Low'], 'Close': last_k['Close'], 'Volume': last_k['Volume'] }) merged_df = pd.DataFrame(merged_data) merged_df['Date'] = pd.to_datetime(merged_df['Date']) merged_df.set_index('Date', inplace=True) logging.info(f"Merged K-lines: {len(df)} -> {len(merged_df)}") return merged_df except Exception as e: logging.error(f"K-line merging failed: {e}") raise # 3. Detect fractals (vectorized) def detect_fractals(df): try: df = df.copy() df['is_top'] = (df['High'] > df['High'].shift(1)) & (df['High'] > df['High'].shift(-1)) & \ (df['High'] > df['High'].shift(2)) & (df['High'] > df['High'].shift(-2)) df['is_bottom'] = (df['Low'] < df['Low'].shift(1)) & (df['Low'] < df['Low'].shift(-1)) & \ (df['Low'] < df['Low'].shift(2)) & (df['Low'] < df['Low'].shift(-2)) df['is_top'] = df['is_top'].fillna(False) df['is_bottom'] = df['is_bottom'].fillna(False) logging.info(f"Detected {df['is_top'].sum()} top fractals and {df['is_bottom'].sum()} bottom fractals") return df except Exception as e: logging.error(f"Fractal detection failed: {e}") raise # 4. Detect strokes def detect_strokes(df): try: strokes = [] last_fractal = None last_price = None last_index = None for i in range(len(df)): if df['is_top'].iloc[i] or df['is_bottom'].iloc[i]: current_fractal = 'top' if df['is_top'].iloc[i] else 'bottom' current_price = df['High'].iloc[i] if current_fractal == 'top' else df['Low'].iloc[i] if last_fractal is None: last_fractal = current_fractal last_price = current_price last_index = df.index[i] continue if (last_fractal == 'top' and current_fractal == 'bottom' and current_price < last_price) or \ (last_fractal == 'bottom' and current_fractal == 'top' and current_price > last_price): strokes.append({ 'start_time': last_index, 'end_time': df.index[i], 'start_price': last_price, 'end_price': current_price, 'type': 'down' if current_fractal == 'bottom' else 'up', 'volume': df['Volume'].loc[last_index:df.index[i]].sum() }) last_fractal = current_fractal last_price = current_price last_index = df.index[i] logging.info(f"Detected {len(strokes)} strokes") return strokes except Exception as e: logging.error(f"Stroke detection failed: {e}") raise # 5. Detect segments def detect_segments(strokes): try: segments = [] if len(strokes) < 3: return segments i = 0 while i < len(strokes) - 2: stroke1, stroke2, stroke3 = strokes[i], strokes[i+1], strokes[i+2] if stroke1['type'] == 'up' and stroke2['type'] == 'down' and stroke3['type'] == 'up': if stroke3['end_price'] > stroke1['end_price']: segments.append({ 'start_time': stroke1['start_time'], 'end_time': stroke3['end_time'], 'start_price': stroke1['start_price'], 'end_price': stroke3['end_price'], 'type': 'up' }) i += 3 else: i += 1 elif stroke1['type'] == 'down' and stroke2['type'] == 'up' and stroke3['type'] == 'down': if stroke3['end_price'] < stroke1['end_price']: segments.append({ 'start_time': stroke1['start_time'], 'end_time': stroke3['end_time'], 'start_price': stroke1['start_price'], 'end_price': stroke3['end_price'], 'type': 'down' }) i += 3 else: i += 1 else: i += 1 logging.info(f"Detected {len(segments)} segments") return segments except Exception as e: logging.error(f"Segment detection failed: {e}") raise # 6. Detect pivots (midlines) def detect_pivots(strokes): try: pivots = [] if len(strokes) < 3: return pivots for i in range(len(strokes) - 2): s1, s2, s3 = strokes[i:i+3] high = min(s1['start_price'], s1['end_price'], s2['start_price'], s2['end_price'], s3['start_price'], s3['end_price']) low = max(s1['start_price'], s1['end_price'], s2['start_price'], s2['end_price'], s3['start_price'], s3['end_price']) if high > low: pivots.append({ 'start_time': s1['start_time'], 'end_time': s3['end_time'], 'high': high, 'low': low }) logging.info(f"Detected {len(pivots)} pivots") return pivots except Exception as e: logging.error(f"Pivot detection failed: {e}") raise # 7. Analyze higher timeframe (30m) def analyze_higher_timeframe(df_30m): try: df_30m = detect_fractals(df_30m) strokes_30m = detect_strokes(df_30m) if not strokes_30m: return 'neutral' last_stroke = strokes_30m[-1] logging.info(f"30m trend: {last_stroke['type']}") return last_stroke['type'] except Exception as e: logging.error(f"Higher timeframe analysis failed: {e}") raise # 8. Back-divergence detection (enhanced) def detect_back_divergence(df, strokes, higher_trend): try: macd, signal, hist = MACD(df['Close'], fastperiod=12, slowperiod=26, signalperiod=9) sma20 = SMA(df['Close'], timeperiod=20) df['macd'] = macd df['hist'] = hist df['sma20'] = sma20 df['buy_signal'] = False df['sell_signal'] = False stroke_metrics = [] for stroke in strokes: start_idx = df.index.get_loc(stroke['start_time']) end_idx = df.index.get_loc(stroke['end_time']) hist_segment = df['hist'].iloc[start_idx:end_idx+1] price_change = abs(stroke['end_price'] - stroke['start_price']) hist_area = sum(abs(h) for h in hist_segment if not np.isnan(h)) volume = stroke['volume'] stroke_metrics.append({ 'start_time': stroke['start_time'], 'end_time': stroke['end_time'], 'type': stroke['type'], 'price_change': price_change, 'hist_area': hist_area, 'volume': volume }) for i in range(2, len(stroke_metrics)): current_stroke = stroke_metrics[i] prev_stroke = stroke_metrics[i-2] if current_stroke['type'] != prev_stroke['type']: continue current_end_idx = df.index.get_loc(current_stroke['end_time']) # Uptrend back-divergence (sell signal) if current_stroke['type'] == 'up': price_increase = df['High'].loc[current_stroke['end_time']] > df['High'].loc[prev_stroke['end_time']] hist_decrease = current_stroke['hist_area'] < prev_stroke['hist_area'] volume_decrease = current_stroke['volume'] < prev_stroke['volume'] is_top_fractal = df['is_top'].loc[current_stroke['end_time']] hist_positive = df['hist'].iloc[current_end_idx] > 0 or \ (df['hist'].iloc[current_end_idx] < 0 and df['hist'].iloc[current_end_idx-1] > 0) sma_trend = df['Close'].iloc[current_end_idx] > df['sma20'].iloc[current_end_idx] trend_match = higher_trend in ['up', 'neutral'] if price_increase and hist_decrease and volume_decrease and is_top_fractal and \ hist_positive and sma_trend and trend_match: df.loc[df.index[current_end_idx], 'sell_signal'] = True # Downtrend back-divergence (buy signal) elif current_stroke['type'] == 'down': price_decrease = df['Low'].loc[current_stroke['end_time']] < df['Low'].loc[prev_stroke['end_time']] hist_decrease = current_stroke['hist_area'] < prev_stroke['hist_area'] volume_decrease = current_stroke['volume'] < prev_stroke['volume'] is_bottom_fractal = df['is_bottom'].loc[current_stroke['end_time']] hist_negative = df['hist'].iloc[current_end_idx] < 0 or \ (df['hist'].iloc[current_end_idx] > 0 and df['hist'].iloc[current_end_idx-1] < 0) sma_trend = df['Close'].iloc[current_end_idx] < df['sma20'].iloc[current_end_idx] trend_match = higher_trend in ['down', 'neutral'] if price_decrease and hist_decrease and volume_decrease and is_bottom_fractal and \ hist_negative and sma_trend and trend_match: df.loc[df.index[current_end_idx], 'buy_signal'] = True logging.info(f"Detected {df['buy_signal'].sum()} buy signals and {df['sell_signal'].sum()} sell signals") return df except Exception as e: logging.error(f"Back-divergence detection failed: {e}") raise # 9. Execute trade def execute_trade(exchange, symbol, signal, amount=0.001): try: if SIMULATION_MODE: msg = f"[SIMULATION] {'Buy' if signal == 'buy' else 'Sell'} {amount} {symbol} at {datetime.now(dt.UTC)}" print(msg) logging.info(msg) return if signal == 'buy': order = exchange.create_market_buy_order(symbol, amount) msg = f"Buy order executed: {order}" print(msg) logging.info(msg) elif signal == 'sell': order = exchange.create_market_sell_order(symbol, amount) msg = f"Sell order executed: {order}" print(msg) logging.info(msg) except Exception as e: msg = f"Trade execution failed: {e}" print(msg) logging.error(msg) # 10. Plot chart def plot_chart(df, strokes, segments, pivots): try: # Initialize additional plots apds = [] alines = [] # For line segments # Plot strokes as line segments for stroke in strokes: alines.append([(stroke['start_time'], stroke['start_price']), (stroke['end_time'], stroke['end_price'])]) # Plot segments as line segments for segment in segments: alines.append([(segment['start_time'], segment['start_price']), (segment['end_time'], segment['end_price'])]) # Plot pivots as horizontal lines for pivot in pivots: alines.append([(pivot['start_time'], pivot['high']), (pivot['end_time'], pivot['high'])]) alines.append([(pivot['start_time'], pivot['low']), (pivot['end_time'], pivot['low'])]) # Add alines to plot (single color for simplicity, can customize) if alines: apds.append(mpf.make_addplot( None, # No y-data needed for alines alines=alines, type='line', color=['blue' if i < len(strokes) else 'purple' if i < len(strokes) + len(segments) else 'orange' for i in range(len(alines))], linestyle=['--' if i < len(strokes) else '-' if i < len(strokes) + len(segments) else ':' for i in range(len(alines))] )) # Plot buy/sell signals buy_signals = df[df['buy_signal']]['Close'] sell_signals = df[df['sell_signal']]['Close'] apds.append(mpf.make_addplot(buy_signals, type='scatter', markersize=100, marker='^', color='green')) apds.append(mpf.make_addplot(sell_signals, type='scatter', markersize=100, marker='v', color='red')) # Plot K-line chart mpf.plot(df, type='candle', addplot=apds, title='Chanlun Advanced Analysis', style='yahoo') logging.info("Chart plotted successfully") except Exception as e: logging.error(f"Chart plotting failed: {e}") raise # 11. Main function def main(): try: # Initialize exchange exchange = ccxt.binance({ 'apiKey': BINANCE_API_KEY if not SIMULATION_MODE else '', 'secret': BINANCE_API_SECRET if not SIMULATION_MODE else '', 'enableRateLimit': True, 'options': {'defaultType': 'spot'} }) # Fetch data df_5m = fetch_binance_data(symbol='BTC/USDT', timeframe='5m', limit=500) df_30m = fetch_binance_data(symbol='BTC/USDT', timeframe='30m', limit=200) # Merge 5m K-lines df_5m = merge_kline(df_5m) # Detect fractals, strokes, segments, pivots df_5m = detect_fractals(df_5m) strokes = detect_strokes(df_5m) segments = detect_segments(strokes) pivots = detect_pivots(strokes) # Analyze 30m trend higher_trend = analyze_higher_timeframe(df_30m) print(f"30m Trend: {higher_trend}") # Detect back-divergence df_5m = detect_back_divergence(df_5m, strokes, higher_trend) # Plot chart plot_chart(df_5m, strokes, segments, pivots) # Output and execute trades print("Buy Signals:") buy_signals = df_5m[df_5m['buy_signal']][['Close']] print(buy_signals) for idx, row in buy_signals.iterrows(): execute_trade(exchange, 'BTC/USDT', 'buy', amount=0.001) print("Sell Signals:") sell_signals = df_5m[df_5m['sell_signal']][['Close']] print(sell_signals) for idx, row in sell_signals.iterrows(): execute_trade(exchange, 'BTC/USDT', 'sell', amount=0.001) logging.info("Main function completed successfully") except Exception as e: logging.error(f"Main function failed: {e}") raise if __name__ == "__main__": main()