louisnw01 / lightweight-charts-python

Python framework for TradingView's Lightweight Charts JavaScript library.
MIT License
1.19k stars 219 forks source link

Chart events are not firing when using brokers websocket api #127

Closed mtech2008 closed 1 year ago

mtech2008 commented 1 year ago

Question

I am using Zerodha kiteconnect to receive the live ticks from websocket. I am receiving the ticks but chart events are not firing. I have gone through your asyncio events and tried it but either I get events fired or ticks. I am really stuck and tried every way but did not find the solution. I need your suggetion and help. It has connect() method which runs infinite loop on main thread. Nothing after it will run. so we have to use the pre-defined callbacks to manage the subscription. Ref: https://github.com/zerodha/pykiteconnect/tree/master#websocket-usage Here is a sample code without asyncio:

Code example

from lightweight_charts import Chart
from apimaster import Ticker

def on_search(chart, searched_string):
  chart.topbar['symbol'].set(searched_string)

def on_timeframe_selection(chart):
  print(chart.topbar['symbol'].value, chart.topbar['timeframe'].value)

def on_ticks(ws, ticks):
  # Callback to receive ticks.
  for tick in ticks:
    print(tick)
  # while chart.is_alive:
    print("connected")

def on_connect(ws, response):
  # Callback on successful connect.
  # Subscribe to a list of instrument_tokens (RELIANCE and ACC here).
  ws.subscribe([738561, 5633])

  # Set RELIANCE to tick in `full` mode.
  ws.set_mode(ws.MODE_FULL, [738561])

def on_close(ws, code, reason):
  # On connection close stop the main loop
  # Reconnection will not happen after executing `ws.stop()`
  ws.stop()    

if __name__ == '__main__':
  kws = Ticker.ticker()  
  chart = Chart(toolbox=True, debug=True)
  chart.legend(True)
  chart.events.search += on_search
  chart.topbar.textbox('symbol', 'n/a')
  chart.topbar.switcher(
      'timeframe',
      ('1m', '5m', '30m', '1d', '1wk'),
      default='5m',
      func=on_timeframe_selection
  )
  # Assign the callbacks.
  kws.on_ticks = on_ticks
  kws.on_connect = on_connect
  kws.on_close = on_close
  chart.show()
  # Infinite loop on the main thread. Nothing after this will run.
  # You have to use the pre-defined callbacks to manage subscriptions.
  kws.connect()
louisnw01 commented 1 year ago

Hey @mtech2008,

The only simple option I can see is to run kws.connect and chart.show in separate threads/processes and make sure your code is thread-safe.

Ideally you would want an implementation of the kws object which uses asyncio, so both show and connect could be run in parallel.

Louis

mtech2008 commented 1 year ago

I am receiving in the following error run_forever raise RuntimeError('This event loop is already running') RuntimeError: This event loop is already running

async def main():
    kite = Zerodha()
    kite.set_session_token()    
    print(kite.profile())
    kws = kite.ticker() 
    kws.on_connect = on_connect
    kws.on_ticks = on_ticks    
    chart = Chart()
    chart.events.new_bar += on_new_bar
    chart.topbar.switcher('timeframe', ('1min', '5min'), func=on_timeframe_selection)
    df = pd.read_csv('ohlc.csv')
    chart.set(df)
    await asyncio.gather(chart.show_async(block=True), kws.connect_ws())

if __name__ == '__main__':  
    asyncio.run(main())

Is is with async implementation of Ticker by https://github.com/ranjanrak/async-ticker

louisnw01 commented 1 year ago

There's a slight issue with their implementation; you'll need to create a child class of MainTicker, and overwrite the connect_ws method:

async def connect_ws(self):
        """
        Establish ws connection
        """
        self.factory = TickerClientFactory(self.ws_url)
        self.factory.protocol = TickerClientProtocol

        self.ws = self.factory.ws

        # Register private callback
        self.factory.on_connect = self._on_connect
        self.factory.on_message = self._on_message

        # Run an infinite loop using asyncio
        loop = asyncio.get_event_loop()
        await loop.create_connection(self.factory, "ws.kite.trade", 443, ssl=True)

Should work.

Louis

mtech2008 commented 1 year ago

Thank you so very much for this help. It worked perfectly well. Thank you.