Combined Strategy Supertrend + MACD + EMA

@Imranali

This code is working, but it only keeps on searching. It doesn’t take any orders. and no any error mentioning.
please help me

import pdb
import time
import datetime
import traceback
from Dhan_Tradehull import Tradehull
import pandas as pd
from pprint import pprint
import talib
import pandas_ta as ta
import xlwings as xw
import winsound

client_code = "client_code"
token_id    = "token_id"
tsl         = Tradehull(client_code,token_id)

opening_balance = tsl.get_balance()
base_capital = 900
market_money = max(0, opening_balance - base_capital)
if market_money == 0:
    base_capital = opening_balance

market_money_risk = (market_money * 1) / 100
base_capital_risk = (base_capital * 0.5) / 100
max_risk_for_today = base_capital_risk + market_money_risk

max_order_for_today = 5
risk_per_trade = max_risk_for_today / max_order_for_today
atr_multipler = 2
risk_reward = 3

watchlist = ['IDFCFIRSTB', 'NMDC', 'UJJIVANSFB', 'SOUTHBANK', 'PATELENG', 'COALINDIA', 'LLOYDSENGG', 'DABUR', 'ITC']

single_order = {
    'name': None, 'date': None, 'entry_time': None, 'entry_price': None, 'buy_sell': None,
    'qty': None, 'sl': None, 'exit_time': None, 'exit_price': None, 'pnl': None,
    'remark': None, 'traded': None
}
orderbook = {name: single_order.copy() for name in watchlist}

wb = xw.Book('Live Trade Data.xlsx')
live_Trading = wb.sheets['Live_Trading']
completed_orders_sheet = wb.sheets['completed_orders']
reentry = "yes"
completed_orders = []

bot_token           = "bot_token"
receiver_chat_id    = "receiver_chat_id"

live_Trading.range("A2:Z100").value = None
completed_orders_sheet.range("A2:Z100").value = None

while True:
    current_time = datetime.datetime.now().time()
    if current_time < datetime.time(9, 15):
        print(f"Waiting for market to open: {current_time}")
        time.sleep(1)
        continue

    if current_time > datetime.time(15, 15):
        tsl.cancel_all_orders()
        print(f"Market over. Closing all trades at {current_time}.")
        break

    all_ltp = tsl.get_ltp_data(names=watchlist)

    for name in watchlist:
        try:
            orderbook_df = pd.DataFrame(orderbook).T
            live_Trading.range('A1').value = orderbook_df

            completed_orders_df = pd.DataFrame(completed_orders)
            completed_orders_sheet.range('A1').value = completed_orders_df

            current_time = datetime.datetime.now()
            print(f"Scanning {name} at {current_time}")

            chart = tsl.get_historical_data(tradingsymbol=name, exchange='NSE', timeframe="5")
            chart['ema9'] = ta.ema(chart['close'], length=9)
            chart['ema21'] = ta.ema(chart['close'], length=21)

            macd = ta.macd(chart['close'], fast=12, slow=26, signal=9)
            chart['macd'] = macd['MACD_12_26_9']
            chart['macd_signal'] = macd['MACDs_12_26_9']

            supertrend = ta.supertrend(high=chart['high'], low=chart['low'], close=chart['close'], period=10, multiplier=3.0)
            chart['supertrend'] = supertrend['SUPERT_10_3.0']
            chart['supertrend_direction'] = supertrend['SUPERTd_10_3.0']
            chart['atr'] = talib.ATR(chart['high'], chart['low'], chart['close'], timeperiod=14)

            cc = chart.iloc[-2]

            bc1 = cc['macd'] > cc['macd_signal']
            bc2 = cc['close'] > cc['supertrend']
            bc3 = cc['ema9'] > cc['ema21']
            bc4 = cc['supertrend_direction'] == 1.0

            if bc1 and bc2 and bc3 and bc4 and orderbook[name]['traded'] is None:
                print(f"Buy signal for {name}")
                margin_avialable = tsl.get_balance()
                margin_required = cc['close'] / 4.5
                if margin_avialable < margin_required:
                    print(f"Insufficient margin for {name}")
                    continue

                orderbook[name] = single_order.copy()
                orderbook[name].update({
                    'name': name,
                    'date': str(current_time.date()),
                    'entry_time': str(current_time.time())[:8],
                    'max_holding_time': datetime.datetime.now() + datetime.timedelta(hours=2),
                    'buy_sell': "BUY"
                })

                sl_points = cc['atr'] * atr_multipler
                orderbook[name]['qty'] = int((risk_per_trade * 0.7) / sl_points)

                try:
                    entry_orderid = tsl.order_placement(
                        tradingsymbol=name, exchange='NSE', quantity=orderbook[name]['qty'],
                        price=0, trigger_price=0, order_type='MARKET',
                        transaction_type='BUY', trade_type='MIS')

                    orderbook[name]['entry_orderid'] = entry_orderid
                    orderbook[name]['entry_price'] = tsl.get_executed_price(orderid=entry_orderid)
                    orderbook[name]['sl'] = round(orderbook[name]['entry_price'] - sl_points, 1)
                    orderbook[name]['tg'] = round(orderbook[name]['entry_price'] + sl_points * risk_reward, 1)

                    sl_orderid = tsl.order_placement(
                        tradingsymbol=name, exchange='NSE', quantity=orderbook[name]['qty'],
                        price=0, trigger_price=orderbook[name]['sl'], order_type='STOPMARKET',
                        transaction_type='SELL', trade_type='MIS')

                    orderbook[name]['sl_orderid'] = sl_orderid
                    orderbook[name]['traded'] = "yes"

                    message = f"Entry_done {name}\n\n" + "\n".join(
                        f"{key}: {repr(value)}" for key, value in orderbook[name].items())
                    tsl.send_telegram_alert(message=message, receiver_chat_id=receiver_chat_id, bot_token=bot_token)

                except Exception as e:
                    print(f"Error during entry for {name}: {e}")
                    continue

            if orderbook[name]['traded'] == "yes":
                try:
                    ltp = all_ltp[name]
                    sl_hit = tsl.get_order_status(orderid=orderbook[name]['sl_orderid']) == "TRADED"
                    tg_hit = ltp > orderbook[name]['tg']
                    holding_time_exceeded = datetime.datetime.now() > orderbook[name]['max_holding_time']
                    current_pnl = round((ltp - orderbook[name]['entry_price']) * orderbook[name]['qty'], 1)

                    def handle_exit(reason):
                        tsl.cancel_order(OrderID=orderbook[name]['sl_orderid'])
                        time.sleep(2)
                        exit_order = tsl.order_placement(
                            tradingsymbol=name, exchange='NSE', quantity=orderbook[name]['qty'],
                            price=0, trigger_price=0, order_type='MARKET',
                            transaction_type='SELL', trade_type='MIS')
                        orderbook[name]['exit_time'] = str(current_time.time())[:8]
                        orderbook[name]['exit_price'] = tsl.get_executed_price(orderid=exit_order)
                        orderbook[name]['pnl'] = round((orderbook[name]['exit_price'] - orderbook[name]['entry_price']) * orderbook[name]['qty'], 1)
                        orderbook[name]['remark'] = reason

                        tsl.send_telegram_alert(
                            message=f"{reason} {name}\n\n" + "\n".join(f"{key}: {repr(value)}" for key, value in orderbook[name].items()),
                            receiver_chat_id=receiver_chat_id, bot_token=bot_token)

                        if reentry == "yes":
                            completed_orders.append(orderbook[name])
                            orderbook[name] = single_order.copy()

                        winsound.Beep(1500, 10000)

                    if sl_hit:
                        orderbook[name]['exit_price'] = tsl.get_executed_price(orderid=orderbook[name]['sl_orderid'])
                        orderbook[name]['exit_time'] = str(current_time.time())[:8]
                        orderbook[name]['pnl'] = round((orderbook[name]['exit_price'] - orderbook[name]['entry_price']) * orderbook[name]['qty'], 1)
                        orderbook[name]['remark'] = "Bought_SL_hit"
                        tsl.send_telegram_alert(
                            message=f"SL_HIT {name}\n\n" + "\n".join(f"{key}: {repr(value)}" for key, value in orderbook[name].items()),
                            receiver_chat_id=receiver_chat_id, bot_token=bot_token)

                        if reentry == "yes":
                            completed_orders.append(orderbook[name])
                            orderbook[name] = single_order.copy()

                    elif tg_hit:
                        handle_exit("Bought_TG_hit")

                    elif holding_time_exceeded and current_pnl < 0:
                        handle_exit("holding_time_exceeded_and_I_am_still_facing_loss")

                except Exception as e:
                    print(f"Error during exit check for {name}: {e}")
                    continue

        except Exception as e:
            print(f"General error in scanning loop for {name}: {e}")
            continue
1 Like

The most likely reason your bot is continuously scanning without placing any orders (and showing no errors) is that the buy signal conditions are never fully met during market hours.

Your strategy requires all four conditions to be true simultaneously on the previous completed 5-minute candle (cc = chart.iloc[-2]):

  1. MACD > MACD Signal
  2. Close > Supertrend
  3. EMA9 > EMA21
  4. Supertrend direction == 1.0 (uptrend)

This is a fairly strict multi-indicator alignment for a strong uptrend/momentum breakout. In real market conditions (as of late 2025), these stocks (mostly mid/small-cap banks, PSU, and large-caps like ITC/DABUR) may not frequently produce candles where all four align perfectly, especially:

  • The watchlist includes several banking stocks (IDFCFIRSTB, UJJIVANSFB, SOUTHBANK) that have been range-bound or under pressure in recent markets.
  • Broader market volatility or sideways movement reduces simultaneous crossovers.
  • Supertrend (period 10, multiplier 3) can lag, and combined with EMA crossover + MACD, signals are infrequent.

Since the code has print(f"Buy signal for {name}") right before the order block, if you never see this message in the console, the conditions simply aren’t triggering.

Quick Debugging Steps to Confirm/Fix

  1. Add more logging to see why signals aren’t firing:

    print(f"{name} - MACD: {cc['macd'] > cc['macd_signal']} | Close > ST: {cc['close'] > cc['supertrend']} | EMA9 > EMA21: {cc['ema9'] > cc['ema21']} | ST Dir: {cc['supertrend_direction']}")
    

    Place this right after calculating bc1 to bc4. Run the bot and watch – you’ll see which condition(s) fail most often.

  2. Test on historical data: Temporarily modify the loop to load older dates where you know strong uptrends occurred (e.g., bull runs in COALINDIA or NMDC). If signals appear there, it confirms the issue is current market conditions.

  3. Relax conditions temporarily for testing (e.g., comment out one or two bc checks) to force a signal and verify orders place successfully. This proves the order placement code works.

Other Less Likely Causes (Ruled Out Based on Code Review)

  • Insufficient margin check: The code has if margin_avialable < margin_required: print("Insufficient margin...") continue. You’d see this message if it triggered. The check uses a very rough estimate (close / 4.5, assuming ~4-5x leverage), which is conservative – actual MIS leverage on Dhan is up to 5x for many stocks, so it shouldn’t block often.
  • Data issues: tsl.get_historical_data(..., timeframe="5") should work (Dhan supports 5-min intraday data).
  • Exceptions: All critical sections are in try-except with prints, so errors would show.
  • Quantity = 0: Unlikely, as risk_per_trade is small but positive, and ATR-based qty would need extreme values.

Suggestions to Improve Signal Frequency

If you want more trades:

  • Reduce Supertrend multiplier to 2.0-2.5 for faster signals.
  • Use current candle (chart.iloc[-1]) instead of previous for more responsive (but noisier) signals.
  • Add volume filter or remove one condition (e.g., just Supertrend + EMA).
  • Expand watchlist to more volatile/trending stocks.

Run with extra prints first – that will confirm exactly what’s happening.

2 Likes