Positional Strategy – This strategy does not impose any intraday entry/exit time limits. Positions may remain open across multiple trading sessions until stoploss or target conditions are met.
Instrument Configuration
Parameter
Value
Symbol
RELIANCE
Exchange
NSE
Quantity
1
Order Type
MARKET
Product Type
MIS
Note: Even though MIS is used, positional behavior is controlled through logic, not by broker product type. This can be switched to CNC for true positional holding if desired.
Indicators Used
EMA 5: Exponential Moving Average over the last 5 closing prices (short-term trend).
EMA 10: Exponential Moving Average over the last 10 closing prices (medium-term trend).
Entry Conditions
Fetch 1-minute historical candle data every 5 seconds.
Calculate EMA-5 and EMA-10.
Identify confirmed crossover using the last two closed candles (i.e., not the current forming candle).
Buy Signal: Previous candle EMA-5 < EMA-10 and last closed candle EMA-5 > EMA-10.
Sell Signal: Previous candle EMA-5 > EMA-10 and last closed candle EMA-5 < EMA-10.
When a signal is confirmed:
Place a MARKET order (BUY or SELL).
Capture entry price and calculate:
Stoploss = Entry Price − 10.0
Target = Entry Price + 20.0
Exit Conditions
Use WebSocket streaming to receive live LTP updates.
Continuously monitor whether LTP hits stoploss or target levels.
When triggered, exit the position by placing a MARKET order in the opposite direction.
Strategy Architecture
Two dedicated threads:
WebSocket Thread: Listens to real-time LTP and checks SL/Target.
Strategy Thread: Periodically fetches historical data, calculates EMA signals, and initiates trades.
Uses threading.Event() to handle graceful shutdown via CTRL+C.
Shutdown Behavior
On receiving a keyboard interrupt, both threads are safely stopped.
WebSocket subscription is removed, and connection is closed.
The strategy exits cleanly and logs the shutdown.
Complete Code
import threading
import time
import signal
import pandas as pd
import pandas_ta as ta
from datetime import datetime, timedelta
from openalgo import api
# Initialize OpenAlgo client
client = api(
api_key="openalgo-api-key",
host="http://127.0.0.1:5000",
ws_url="ws://127.0.0.1:8765"
)
# Configuration
STRATEGY_NAME = "EMA_Crossover_RELIANCE"
SYMBOL = "RELIANCE"
EXCHANGE = "NSE"
QUANTITY = 1
PRODUCT = "MIS"
PRICE_TYPE = "MARKET"
STOPLOSS_BUFFER = 10.0
TARGET_BUFFER = 20.0
instrument = [{"exchange": EXCHANGE, "symbol": SYMBOL}]
# State Variables
ltp = None
in_position = False
entry_price = None
stoploss_price = None
target_price = None
current_position = None
exit_signal = False
stop_event = threading.Event()
# WebSocket LTP Handler
def on_data_received(data):
global ltp, exit_signal
if data.get("type") == "market_data" and data.get("symbol") == SYMBOL:
ltp = float(data["data"]["ltp"])
print(f"LTP Update {EXCHANGE}:{SYMBOL} => ₹{ltp}")
if in_position and not exit_signal:
if ltp <= stoploss_price or ltp >= target_price:
print(f"Exit Triggered: LTP ₹{ltp} hit stoploss or target.")
exit_signal = True
# WebSocket Thread
def websocket_thread():
try:
client.connect()
client.subscribe_ltp(instrument, on_data_received=on_data_received)
print("WebSocket LTP thread started.")
while not stop_event.is_set():
time.sleep(1)
finally:
print("Shutting down WebSocket...")
client.unsubscribe_ltp(instrument)
client.disconnect()
print("WebSocket connection closed.")
# EMA Signal Logic
def get_latest_signals():
end_date = datetime.now()
start_date = end_date - timedelta(days=3)
df = client.history(
symbol=SYMBOL,
exchange=EXCHANGE,
interval="5m",
start_date=start_date.strftime("%Y-%m-%d"),
end_date=end_date.strftime("%Y-%m-%d")
)
df.ta.ema(length=5, append=True)
df.ta.ema(length=10, append=True)
if len(df) < 3:
print("Waiting for sufficient data...")
return None
prev = df.iloc[-3]
last = df.iloc[-2]
print(f"{datetime.now().strftime('%H:%M:%S')} | EMA5: {last['EMA_5']:.2f}, EMA10: {last['EMA_10']:.2f}")
if prev['EMA_5'] < prev['EMA_10'] and last['EMA_5'] > last['EMA_10']:
print("Confirmed BUY crossover.")
return "BUY"
elif prev['EMA_5'] > prev['EMA_10'] and last['EMA_5'] < last['EMA_10']:
print("Confirmed SELL crossover.")
return "SELL"
return None
# Place Order
def place_order(action):
global in_position, entry_price, stoploss_price, target_price, current_position
print(f"Placing {action} order for {SYMBOL}")
resp = client.placeorder(
strategy=STRATEGY_NAME,
symbol=SYMBOL,
exchange=EXCHANGE,
action=action,
price_type=PRICE_TYPE,
product=PRODUCT,
quantity=QUANTITY
)
print("Order Response:", resp)
if resp.get("status") == "success":
order_id = resp.get("orderid")
time.sleep(1)
status = client.orderstatus(order_id=order_id, strategy=STRATEGY_NAME)
data = status.get("data", {})
if data.get("order_status", "").lower() == "complete":
entry_price = float(data["price"])
stoploss_price = round(entry_price - STOPLOSS_BUFFER, 2)
target_price = round(entry_price + TARGET_BUFFER, 2)
current_position = action
in_position = True
print(f"Entry @ ₹{entry_price} | SL ₹{stoploss_price} | Target ₹{target_price}")
# Exit Order
def exit_trade():
global in_position, exit_signal
action = "SELL" if current_position == "BUY" else "BUY"
print(f"Exiting trade with {action}")
client.placeorder(
strategy=STRATEGY_NAME,
symbol=SYMBOL,
exchange=EXCHANGE,
action=action,
price_type=PRICE_TYPE,
product=PRODUCT,
quantity=QUANTITY
)
in_position = False
exit_signal = False
# Strategy Thread
def strategy_thread():
global exit_signal
while not stop_event.is_set():
if not in_position:
signal = get_latest_signals()
if signal:
place_order(signal)
elif exit_signal:
exit_trade()
time.sleep(5)
# Main Execution
def main():
print("EMA Crossover Strategy is running...")
ws_thread = threading.Thread(target=websocket_thread)
strat_thread = threading.Thread(target=strategy_thread)
ws_thread.start()
strat_thread.start()
try:
while True:
time.sleep(1)
except KeyboardInterrupt:
print("KeyboardInterrupt received. Shutting down...")
stop_event.set()
ws_thread.join()
strat_thread.join()
print("Strategy shutdown complete.")
if __name__ == "__main__":
main()