Package: truedata-ws
Official Python repository for TrueData (Market Data APIs)This Python library attempts to make it easy for you to connect to TrueData Market Data Apis, thereby allowing you to concentrate on startegy development, while this library works to get you all the data you need from the TrueData backend both for Real Time & Historical.Please make sure you follow us on our Telegram channel where we push a lot of information with regards to API updates, implementation ideas, tips & tricks to use this library & raw feed, etc...TrueData Market Data API - Telegram ChannelTrueData Market Data APIs - WebsiteWe have also built a sandbox environmemt for testing the raw feed. Please feel free to use this environment to check/test/compare your results with the raw data feed (real time & historical).TrueData Market Data API - Sandbox EnvironmentWe are trying to improve this library continuously and feedback for the same is welcome.It is essential to ensure that the data received through our APIs is not being utilized for any commercial purposes. Additionally, please be mindful that all information or data provided to you is exclusively intended for your internal use and must be utilized and discontinued at your end as required.What have we covered so far ?WebSocket APIsLive data (Streaming Ticks) - Enabled by DefaultLive Data (Streaming 1 min bars) - Needs to be enabled from our backendLive Data (Streaming 5 min bars) - Needs to be enabled from our backendLive data (Streaming Ticks + 1 min bars) - Needs to be enabled from our backendLive Data (Streaming Ticks + 5 min bars) - Needs to be enabled from our backendLive Data (Streaming Ticks + 1 min bars + 5 min bars) - Needs to be enabled from our backendLive Data (Streaming 1 min + 5 min bars) - Needs to be enabled from our backendOption Greek streaming - Needs to be enabled from our backendNote:- Kindly note that data that is not enabled by default may require exchange approvals, which vary depending on the specific exchange and segment in question. For any inquiries or clarifications, please feel free to reach out to our dedicated support team.REST APIsHistorical DataGetting StartedInstallationInstalling the truedata-ws library from PyPipython3-mpipinstalltruedata_wsorpip3installtruedata_wsMinimum RequirementsThe minimum required versions are:-- Python >= 3.7In-built dependenciesAll these dependencies should get installed automatically when you install the truedata_ws library. In case of an issue, make sure you meet the requirements as mentioned below.- websocket-client>=0.57.0
- colorama>=0.4.3
- python-dateutil>=2.8.1
- pandas>=1.0.3
- setuptools>=50.3.2
- requests>=2.25.0
- lz4==3.1.3 (Note lz4 versions >3.1.3 currently have some errors and thus
  lz4 should not be upgraded till these dependency issues are resolved).Connecting / Logging inConnecting / Logging in (Both Real time & Historical data feed subscriptions)fromtruedata_ws.websocket.TDimportTDtd_obj=TD('<enter_your_login_id>','<enter_your_password>')# This connects you to the default real time port which is 8082 & the REST History feed.# If you have been authorized on another live port please enter another parameter# Example# td_obj = TD('<enter_your_login_id>', '<enter_your_password>', live_port=8084)Connecting / Logging in (For Historical Data Subscription Only)fromtruedata_ws.websocket.TDimportTDtd_obj=TD('<enter_your_login_id>','<enter_your_password>',live_port=None)Connecting / Logging in (For Real time Data Subscription Only)fromtruedata_ws.websocket.TDimportTDtd_obj=TD('<enter_your_login_id>','<enter_your_password>',historical_api=False)Automatic Reconnection of Real Time Streaming FeedIn case of a Websocket disconnection, the library will check for the internet connection and once the connection is steady, it will try to re-connect the Websocket, automatically.Once the websocket connection is re-established, the library will automatically re-subscribe the symbols & restart the live data seamlessly.LoggingWe have integrated the python stdlib logger.You can provide LOG_LEVEL and LOG_FORMAT if you want.Please try with various log levels & formats to understand what works best for you for a particular setting. Below are 2 samples provided to you. Please test with both to see what works best for you.fromtruedata_ws.websocket.TDimportTDimportloggingtd_obj=TD('<enter_your_login_id>','<enter_your_password>',live_port=realtime_port,url=url,log_level=logging.WARNING,log_format="%(message)s")# td_obj = TD('<enter_your_login_id>', '<enter_your_password>', log_level=logging.WARNING, log_format="(%(asctime)s) %(levelname)s :: %(message)s (PID:%(process)d Thread:%(thread)d)")Also, If you have subscribed only for a 1 min streaming feed, the bar data would stream every 1 min for all the symbols.It is therefore recommended enabling logging the Heartbeat responses received from the server. These Heartbeats come every 5 seconds and help establish a steady connection with the server.To enable theHeartbeats, change your logging level to DEBUG as follows:-fromtruedata_ws.websocket.TDimportTDimportloggingtd_obj=TD('<enter_your_login_id>','<enter_your_password>',live_port=realtime_port,url=url,log_level=logging.DEBUG,log_format="%(message)s")The logging level to DEBUG, enables you to see:-Market Status messages (Market Open / Close messages as and when they happen)Automatic Touchline update messagesSymbol Add Remove messagesHeartbeat messages (every 5 seconds)If you do not want to see these messages, set your logging level to WARNINGAdditional LOG_LEVEL info can be found athttps://docs.python.org/3/library/logging.html#logging-levels.Additional LOG_FORMAT info can be found athttps://docs.python.org/3/library/logging.html#logrecord-attributes.If you really know what you are doing, you can simply provide a log handler.td_obj=TD('<enter_your_login_id>','<enter_your_password>',log_handler=log_handler_obj)Real Time Data Streaming (Live)Starting Live Data For Multiple symbolsreq_ids=td_obj.start_live_data(['<symbol_1>','<symbol_2>','<symbol_3>',...])# Example:# req_ids = td_obj.start_live_data(['CRUDEOIL-I', 'BANKNIFTY-I', 'RELIANCE', 'ITC'])# This returns a list that can be used to reference data laterAccessing Touchline DataThe touchline data is useful post market hours as this provides the last updates / settlement updates / snap quotes.Please note that it is not recommended to use this during market hours as all the fields of the touchline data already form a part of the real time market data feed.importtimetime.sleep(1)# You need to wait until for 1 sec for all of the touchline data to populateforreq_idinreq_ids:print(td_obj.touchline_data[req_id])Accessing live streaming dataAll updated relevant live data can be found in the object attd_obj.live_data[req_id].For more details try:print(f"{type(td_obj.live_data[req_id])} -> {td_obj.live_data[req_id]})last_traded_price=td_obj.live_data[req_id].ltpchange_perc=td_obj.live_data[req_id].change_percday_high=td_obj.live_data[req_id].day_highhighest_available_bid=td_obj.live_data[req_id].best_bid_priceVERY IMPORTANT(Streaming Tick & Bar data[1 min , 5 min]Simultaneously)The same code works without any changes, if you are subscribed toTick onlyorBar onlystreaming.td_obj.live_data[req_id]However, if you are subscribed to bothtick & barstreaming, then:-Tickstreaming data could be found attd_obj.live_data[req_id].1 Minstreaming bar data can be found attd_obj.one_min_live_data[req_id].5 Minstreaming bar data can be found attd_obj.five_min_live_data[req_id].Stopping live datatd_obj.stop_live_data(['<symbol_1>','<symbol_2>','<symbol_3>',...])Disconnect from the WebSocket servicetd_obj.disconnect()Option Greeks streamingIf user is subscribed to Nse options and also opt for option greek then whenever new greek data arrived the below function executed.@td_obj.greek_callbackdefmygreek_callback(greek_data):print("Greek > ",greek_data)These are available fields for greek_data -> timestamp, symbol_id, symbol, iv, delta, gamma, theta, vega, rho .Each field can be accessed with dot access such as greek_data.symbol , greek_data.delta etc...Bidask StreamingIf user is subscribed to bidask then whenever new bidask changed in exchange the below function executed.@td_obj.bidask_callbackdefmybidask_callback(bidask_data):print("BidAsk > ",bidask_data)These are available fields for bidask_data ->  timestamp, symbol_id, symbol, bid, ask .Each field can be accessed with dot access such as bidask_data.symbol , bidask_data.ask etc...For Nse symbols we offer level 1 bid ask which have one best bid and ask data.For bse symbols we offer level 2 bid ask which have five best bid and ask data.For level 1 bid ask -> bidask_data.ask return with a list of tuples, containing[ ( ask, ask_qnty )]in the format, This has total length of one.For level 2 bid ask -> bidask_data.ask return with a list of tuples, containing[ ( ask_1, ask_1_qnty, ask_1_no_of_trades ) , ( ask_2, ask_2_qnty, ask_2_no_of_trades ) , ....... ]in the format , This has total length of five in bid and also same as the ask.QUICK START LIVE DATA CODE (STREAM USING CALLBACKS (Recommended))The code snippet below shows you how easy it is to stream the data using Callbacks.fromtruedata_ws.websocket.TDimportTDimporttimeimportloggingusername='your_username'password='your_password'realtime_port=8082url='push.truedata.in'# Disable the Production url above and Enable the Replay url below, when you need to work with the replay feed# Make sure to re-enable the Production url prior to live market start# url = 'replay.truedata.in'symbols=['<symbol_1>','<symbol_2>','<symbol_3>',...]td_obj=TD(username,password,live_port=realtime_port,url=url,log_level=logging.DEBUG,log_format="%(message)s")print('\nStarting Real Time Feed.... ')req_ids=td_obj.start_live_data(symbols)live_data_objs={}time.sleep(1)# very important - to ensure that the touchine data is populatedforreq_idinreq_ids:print(f'touchlinedata ->{td_obj.touchline_data[req_id]}')#for trade tick call back . whenever trade happens this function execute with tick data#all data can be accessed with dotaccess egs -> tick_data.symbol , tick_data.ltp#each function callback are provided with python dataclass so support all inbuilt dataclass functions@td_obj.trade_callbackdefmytrade_callback(tick_data):print("Tick > ",tick_data)@td_obj.bidask_callbackdefmybidask_callback(bidask_data):print("BidAsk > ",bidask_data)@td_obj.full_feed_trade_callbackdefmyfullfeed_callback(tick_data):print("Tick > ",tick_data)@td_obj.greek_callbackdefmygreek_callback(greek_data):print("Greek > ",greek_data)@td_obj.one_min_bar_callbackdefmy_onemin_bar(symbol_id,tick_data):print("one min > ",tick_data)@td_obj.five_min_bar_callbackdefmy_five_min_bar(symbol_id,tick_data):print(f"five min > ",tick_data)# Keep your thread alivewhileTrue:time.sleep(120)QUICK START LIVE DATA CODE (Using While Loop)You can also, use a While Loop to stream the data which is recommended for beginners to intermediate developers.In this example we will simply print data in the format given below:-Symbol > LTP > Changefromtruedata_ws.websocket.TDimportTDfromcopyimportdeepcopyimporttimeimportloggingusername='your_username'password='your_password'realtime_port=8082# url = 'push.truedata.in'# Disable the Production url above and Enable the Replay url below, when you need to work with the replay feed# Make sure to re-enable the Production url prior to live market starturl='replay.truedata.in'symbols=['<symbol_1>','<symbol_2>','<symbol_3>',...]td_obj=TD(username,password,live_port=realtime_port,url=url,log_level=logging.WARNING,log_format="%(message)s")req_ids=td_obj.start_live_data(symbols)subs=td_obj.live_websocket.subslive_data_objs={}one_min_live_data_objs={}five_min_live_data_objs={}time.sleep(3)if'tick'insubs:forreq_idinreq_ids:live_data_objs[req_id]=deepcopy(td_obj.live_data[req_id])if'1min'insubs:forreq_idinreq_ids:one_min_live_data_objs[req_id]=deepcopy(td_obj.one_min_live_data[req_id])if'5min'insubs:forreq_idinreq_ids:five_min_live_data_objs[req_id]=deepcopy(td_obj.five_min_live_data[req_id])whileTrue:forreq_idinreq_ids:if'tick'insubsandnottd_obj.live_data[req_id]==live_data_objs[req_id]:live_data_objs[req_id]=deepcopy(td_obj.live_data[req_id])print('tick ->',td_obj.live_data[req_id])if'1min'insubsandnottd_obj.one_min_live_data[req_id]==one_min_live_data_objs[req_id]:one_min_live_data_objs[req_id]=deepcopy(td_obj.one_min_live_data[req_id])print('one min ->',td_obj.one_min_live_data[req_id])if'5min'insubsandnot(td_obj.five_min_live_data[req_id]==five_min_live_data_objs[req_id]):five_min_live_data_objs[req_id]=deepcopy(td_obj.five_min_live_data[req_id])print('five min ->',td_obj.five_min_live_data[req_id])time.sleep(0.05)# important otherwise cpu will overthrottle.CONVERTING REAL TIME STREAM TO DICTIn some cases, some people may need to convert the real time stream to dict. This could be either to push the real time
stream into pandas, a SQL database or work with processing the stream differently at your end.So, if you need to convert the Real Time Stream to dict, use theto_dict()function.td_obj.live_data[req_id].to_dict()Here is a sample code to push the data into a pandas Dataframe.
Please adapt to your code as this is just a sample and there could be better ways to do the same thing more efficiently.fromtruedata_ws.websocket.TDimportTDimporttimeimportpandasaspdfromcopyimportdeepcopytd_obj=TD('<enter_your_login_id>','<enter_your_password>')symbols=['CRUDEOIL-I','GOLD-I','NIFTY-I']symbol_ids=td_obj.get_req_id_list(2000,len(symbols))# symbol_ids = list(range(2000, 2000+len(symbols)))live_data_objs={}td_obj.start_live_data(symbols,req_id=symbol_ids)time.sleep(1)forsymbol_idinsymbol_ids:live_data_objs[symbol_id]=deepcopy(td_obj.live_data[symbol_id])df=pd.DataFrame()# pd.set_option("display.max_rows", None, "display.max_columns", None)whileTrue:time.sleep(0.05)forreq_idinsymbol_ids:iftd_obj.live_data[req_id]!=live_data_objs[req_id]:live_data_objs[req_id]=deepcopy(td_obj.live_data[req_id])# picking up trade packets & discarding only bid ask packetsiftd_obj.live_data[req_id].tick_type==1:op_dict=deepcopy(td_obj.live_data[req_id].to_dict())temp_df=pd.DataFrame.from_records([op_dict])# temp_df = pd.DataFrame(op_dict, index=[0])  # BOTH WORKdf=df.append(temp_df,ignore_index=True)print(df)# just printing out the df with each updateOption Chain StreamingIt is possible to stream single or multiple option chains within respective pandas dataframes, using the steps & sample as explained below.The Option Chain Streaming allows the following fields in the dataframe. (In case you don't need any of these columns,
with pandas you can pick and choose and create your dataframe columns accordingly)symbols, strike, type (CE/PE), ltp, ltt (last traded time), ltq (last traded qty), volume (total volume), price change, price change %, oi, oi Change, OI Change %, bid, bid qty, ask, ask qtyStarting Option Chain data for a symbolfromdatetimeimportdatetimeasdtfromtruedata_ws.websocket.TDimportTDtd_obj=TD('<enter_your_login_id>','<enter_your_password>')nifty_chain=td_obj.start_option_chain('NIFTY',dt(2021,8,26))sensex_chain=td_obj.start_option_chain("SENSEX",dt(2023,9,1),chain_length=80,bse_option=True)#enabling option chain for NIFTY with corresponding expiry.start_option_chain function takes following arguments:symbols for egs: NIFTY , BANKNIFTY , SBIN etc......expiry : date (date object)chain_length : number of strike need to pull with respect to future prices. default value is 10 (int)bid_ask : enable live quote . default value is false (boolean)market_open_post_hours : extra parameter for updating the chain in extra NSE session like (muhurat session) , default is Falseput bse_option = True if pulling sensex or bankex option chainPulling an Option Chaindf=nifty_chain.get_option_chain()#returns a dataframe that contain option chain for repective symbolthis get_option_chain function can call anywhere  that will return respective option chainStop Option Chain Updatesnifty_chain.stop_option_chain()#this function call will stop updating the respective option chain.An example for pulling option chain and live data simultaneouslyA common mistake people make, is leaving while loop without putting proper time sleep, which could overclock your CPU. However, if you use a larger number of symbols, this sleep would need to be smaller else your code would insert an unwanted lag into the data stream.fromtruedata_ws.websocket.TDimportTDfromcopyimportdeepcopyimporttimeimportloggingfromdatetimeimportdatetimeasdttd_obj=TD('<your_login>','<your_password>',log_level=logging.WARNING)symbols=['BANKNIFTY-I','NIFTY 50','NIFTY21072915600CE','SBIN']#starting live data for symbolsreq_ids=td_obj.start_live_data(symbols)live_data_objs={}time.sleep(1)# initilizing option chain with symbol expiry chain length(optional) , bid ask (optional)sbi_chain=td_obj.start_option_chain('SBIN',dt(2021,8,26))bnf_chain=td_obj.start_option_chain('BANKNIFTY',dt(2021,8,26),chain_length=20)nifty_chain=td_obj.start_option_chain('NIFTY',dt(2021,8,26),chain_length=10,bid_ask=True)time.sleep(2)forreq_idinreq_ids:live_data_objs[req_id]=deepcopy(td_obj.live_data[req_id])print(sbi_chain.get_option_chain())print(nifty_chain.get_option_chain())print(bnf_chain.get_option_chain())count=1whileTrue:forreq_idinreq_ids:ifnottd_obj.live_data[req_id]==live_data_objs[req_id]:#this will priint live tick that requestedprint(f'{td_obj.live_data[req_id].symbol}==>{td_obj.live_data[req_id].ltp}')live_data_objs[req_id]=deepcopy(td_obj.live_data[req_id])# fetching option chain every 50 ticksifcount%50==0:print(sbi_chain.get_option_chain())print(nifty_chain.get_option_chain())print(bnf_chain.get_option_chain())count+=1time.sleep(0.05)# important otherwise cpu will overthrottle.Historical DataHistorical Data is provided over REST (from the backend) using Start time, End time or DurationUsing no parametershist_data_1=td_obj.get_historic_data('BANKNIFTY-I')# This returns 1 minute bars from the start of the present day until current timeUsing a given duration (For available duration options, please read the limitations section)hist_data_2=td_obj.get_historic_data('BANKNIFTY-I',duration='3 D')Using a specified bar_size (For available bar_size options, please read the limitations section)hist_data_3=td_obj.get_historic_data('BANKNIFTY-I',bar_size='30 mins')Using start time INSTEAD of durationfromdateutil.relativedeltaimportrelativedeltahist_data_4=td_obj.get_historic_data('BANKNIFTY-I',start_time=datetime.now()-relativedelta(days=3))Using a specific ending timefromdatetimeimportdatetimehist_data_5=td_obj.get_historic_data('BANKNIFTY-I',end_time=datetime(2021,3,5,12,30))# Any time can be given hereUsing a specific number of bars (Enabled for Tick, 1 min & EOD bars)You can specify the number of bars (or ticks) which you want to see or use in your code.
This is currently enabled for Ticks , 1 min bars & EOD Bars.hist_data_6=td_app.get_n_historical_bars(symbol,no_of_bars=30,bar_size=barsizeEnabling / Disabling Bid Ask data with Tick Data HistoryIf you have subscribed for Historical Bid Ask (not activated by default), you can control the visibility of this data depending upon your needs by setting the bidask parameter to True, like,bidask=True.hist_data_7=td_obj.get_historic_data(symbol,duration='1 D',bar_size='tick',bidask=True)Defaultbidask=FalseStoring data for later useUntil v3.x.x, requested data was stored in thetd_objattd_obj.hist_data[req_id].As of v4.0.1, you need toexplicitlyprovide a ticker_id to store the data for later use. For example,td_obj.get_historic_data('BANKNIFTY-I',ticker_id=1000)# At any later pointprint(td_obj.hist_data[1000])IMPORTANT NOTE:Now that we have covered the basic parameters, you can mix and match the parameters as you please. If a parameter is not specified, the defaults are as followsend_time=datetime.now()duration="1 D"bar_size="1 min"Example of mix and matchhist_data_8=td_obj.get_historic_data('BANKNIFTY-I',duration='3 D',bar_size='15 mins')On a side note: You can convert historical data toPandas DataFrameswith a single lineimportpandasaspddf=pd.DataFrame(hist_data_1)Get BhavcopyThis function enables you to get the NSE & MCX bhavcopies for the day / date.eq_bhav=td_obj.get_bhavcopy('EQ')fo_bhav=td_obj.get_bhavcopy('FO')mcx_bhav=td_obj.get_bhavcopy('MCX')The request checks if the latest completed bhavcopy has arrived for that segment and, if arrived, it returns the data.In case it has not arrived it provides the date and time of the last bhavcopy available which can also be pulled by providing the bhavcopy date.*No complete bhavcopy found for requested date. Last available for 2021-03-19 16:46:00.*In this case, if you need the bhavcopy of the date provided, you can get it by giving the date for which the bhavcopy is required as shown below..specific_bhav=td_obj.get_bhavcopy('EQ',date=datetime.datetime(2021,3,19))Limitations and caveats for historical dataIf you provide both duration and start time, duration will be used and start time will be ignored.If you provide neither duration nor start time, duration = "1 D" will be usedIf you do not provide the bar size bar_size = "1 min" will be usedThe following BAR_SIZES are available:tick1 min2 mins3 mins5 mins10 mins15 mins30 mins60 minseod (or EOD)week (or WEEK)month (or MONTH)The following annotation can be used for DURATION:-D = DaysW = WeeksM = MonthsY = YearsGet Gainers losers informationThis function enables you to get the NSE gainers losers information at present state.gainers=td_obj.get_gainers(segment="NSEEQ",topn=10,df_style=False)losers=td_obj.get_losers(segment="NSEEQ",topn=10)The function call return a dataframe or list that contains gainers losers information accoriding to your style preferredgainers , losers function takes following arguments:segment (string) : NSEEQ, NSEFUT, NSEOPT, MCXtopn (int) : default 10 , top n numberdf_style : default is True , output style customizationExample StrategiesHere we cover 4 sample strategies. The first 2 use tick data and the last 2 use bar data.
We also explain how you can use these strategies using both callbacks and with a constant state check mechanism.Differences between Callback and State Check strategiesIn the context of this library, callbacks are meant to be light-weight. Primarily used for quick POCs and other speedy development.The callback execution happens on the websocket thread and will therefore be harder to divide computation across processing cores.Strategy 1Tick basedCallbackBUY when past 50 ticks SMA goes above 100 ticks SMA.SELLS when past 50 ticks SMA goes below 100 ticks SMA.Strategy 2Tick basedState CheckBUY when past 50 ticks SMA goes above 100 ticks SMA.SELLS when past 50 ticks SMA goes below 100 ticks SMA.Strategy 3Minute Bar basedCallbackBUY when past 50 bars close SMA goes above 100 bars close SMA.SELLS when past 50 bars close SMA goes below 100 bars close SMA.Strategy 4Minute Bar basedState CheckBUY when past 50 bars close SMA goes above 100 bars close SMA.SELLS when past 50 bars close SMA goes below 100 bars close SMA.Strategy 1fromtruedata_ws.websocket.TDimportTDfromcopyimportdeepcopyimporttime,osimportloggingtd_obj=TD('<enter_your_login_id>','<enter_your_password>',log_level=logging.WARNING)symbols=['CRUDEOIL-I','GOLD-I','SILVER-I']# generating symbol_id for distinguishing which symbol it is for comparison etc....symbol_ids=td_obj.get_req_id_list(2000,len(symbols))is_market_open=Truedata={}# fetching historical data for strategy calculation ,forsymbol_id,symbolinzip(symbol_ids,symbols):print(f"Requesting historical data for{symbol}")symbol_hist_data=td_obj.get_historic_data(symbol,duration='1 D',bar_size='tick')data[symbol_id]=list(map(lambdax:x['ltp'],symbol_hist_data))[-100:]# live data initilizing for required symbols , passing symbol_id that generatedsymbol_ids=td_obj.start_live_data(symbols,req_id=symbol_ids)time.sleep(1)# trade_callback callback function , strategy is logic is inside call back function@td_obj.trade_callbackdefnew_tick(tick_data):symbol_id=tick_data.symbol_iddata[symbol_id]=data[symbol_id][1:]+[td_obj.live_data[symbol_id].ltp]ifsum(data[symbol_id][-50:])/50>sum(data[symbol_id])/100:print(f'{td_obj.live_data[symbol_id].symbol}-SENDOMSLONGSIGNAL{sum(data[symbol_id][-50:])/50:.2f}>{sum(data[symbol_id])/100:.2f},' , f'ltp={td_obj.[symbol_id].ltp:.2f}')else:print(f'{td_obj.live_data[symbol_id].symbol}- SEND OMS SHORT SIGNAL{sum(data[symbol_id][-50:])/50:.2f}<{sum(data[symbol_id])/100:.2f}' ,f'ltp ={td_obj.live_data[symbol_id].ltp:.2f}')# keeping the while loop to keep the program runningwhileis_market_open:time.sleep(10)# Graceful exittd_obj.clear_bidask_callback()td_obj.stop_live_data(symbols)td_obj.disconnect()Strategy 2fromtruedata_ws.websocket.TDimportTDimporttimetd_obj=TD('<enter_your_login_id>','<enter_your_password>')symbols=['CRUDEOIL-I','GOLD-I','SILVER-I']symbol_ids=td_obj.get_req_id_list(2000,len(symbols))is_market_open=Truedata={}live_data_objs={}forsym_id,symbolinzip(symbol_ids,symbols):print(f"Requesting historical data for{symbol}")symbol_hist_data=td_obj.get_historic_data(symbol,duration='1 D',bar_size='tick')data[sym_id]=list(map(lambdax:x['ltp'],symbol_hist_data))[-100:]td_obj.start_live_data(symbols,req_id=symbol_ids)forsymbol_idinsymbol_ids:live_data_objs[symbol_id]=deepcopy(td_obj.live_data[symbol_id])time.sleep(1)whileis_market_open:# Remember to keep your main thread alive.forsymbol_idinsymbol_ids:iflive_data_objs[symbol_id]!=td_obj.live_data[symbol_id]:live_data_objs[symbol_id]=deepcopy(td_obj.live_data[symbol_id])ifsum(data[symbol_id][-50:])/50>sum(data[symbol_id])/100:print(f'{live_data_objs[symbol_id].symbol}- SEND OMS LONG SIGNAL{sum(data[symbol_id][-50:])/50:.2f}>{sum(data[symbol_id])/100:.2f}',f'ltp ={live_data_objs[symbol_id].ltp:.2f}')else:print(f'{live_data_objs[symbol_id].symbol}- SEND OMS SHORT SIGNAL{sum(data[symbol_id][-50:])/50:.2f}<{sum(data[symbol_id])/100:.2f}',f'ltp ={live_data_objs[symbol_id].ltp:.2f}')time.sleep(0.01)Strategy 3fromtruedata_ws.websocket.TDimportTDfromcopyimportdeepcopyimporttimetd_obj=TD('<enter_your_login_id>','<enter_your_password>')symbols=['CRUDEOIL-I','GOLD-I','SILVER-I']symbol_ids=td_obj.get_req_id_list(2000,len(symbols))is_market_open=Truedata={}live_data_objs={}forsym_id,symbolinzip(symbol_ids,symbols):print(f"Requesting historical data for{symbol}")symbol_hist_data=td_obj.get_historic_data(symbol,duration='1 D',bar_size='1 min')data[sym_id]=list(map(lambdax:x['c'],symbol_hist_data))[-100:]td_obj.start_live_data(symbols,req_id=symbol_ids)time.sleep(1)forsym_idinsymbol_ids:live_data_objs[sym_id]=deepcopy(td_obj.live_data[sym_id])@td_obj.bar_callbackdefstrategy_callback(min_data):symbol_id=min_data.symbol_iddata[symbol_id]=data[symbol_id][1:]+[min_data.close]ifsum(data[symbol_id][-50:])/50>sum(data[symbol_id])/100:print(f'{min_data.symbol}- SEND OMS LONG SIGNAL{sum(data[symbol_id][-50:])/50:.2f}>{sum(data[symbol_id])/100:.2f}',f'ltp ={min_data.ltp:.2f}')else:print(f'{min_data.symbol}- SEND OMS SHORT SIGNAL{sum(data[symbol_id][-50:])/50:.2f}<{sum(data[symbol_id])/100:.2f},'f'ltp ={min_data.ltp:.2f}')whileis_market_open:# Remember to keep your main thread alive.time.sleep(120)# Graceful exittd_obj.clear_bar_callback()td_obj.stop_live_data(symbols)td_obj.disconnect()Strategy 4fromtruedata_ws.websocket.TDimportTDfromcopyimportdeepcopyimporttimetd_obj=TD('<enter_your_login_id>','<enter_your_password>')symbols=['CRUDEOIL-I','GOLD-I','SILVER-I']symbol_ids=td_obj.get_req_id_list(2000,len(symbols))is_market_open=Truedata={}live_data_objs={}forsymbol_id,symbolinzip(symbol_ids,symbols):print(f"Requesting historical data for{symbol}")symbol_hist_data=td_obj.get_historic_data(symbol,duration='1 D',bar_size='1 min')data[symbol_id]=list(map(lambdax:x['c'],symbol_hist_data))[-100:]td_obj.start_live_data(symbols,req_id=symbol_ids)time.sleep(1)forsymbol_idinsymbol_ids:# If you are subscribed ONLY to min data, just USE live_data NOT min_live_datalive_data_objs[symbol_id]=deepcopy(td_obj.one_min_live_data[symbol_id])whileis_market_open:time.sleep(0.1)# Adding this reduces CPU overthrottleforsymbol_idinsymbol_ids:# If you are subscribed ONLY to min data, just USE live_data NOT min_live_dataiflive_data_objs[symbol_id]!=td_obj.one_min_live_data[symbol_id]:live_data_objs[symbol_id]=deepcopy(td_obj.one_min_live_data[symbol_id])# Here is where you can do your manipulation on the min datadata[symbol_id]=data[symbol_id][1:]+[live_data_objs[symbol_id].close]ifsum(data[symbol_id][-50:])/50>sum(data[symbol_id])/100:print(f'{live_data_objs[symbol_id].symbol}- SEND OMS LONG SIGNAL{sum(data[symbol_id][-50:])/50:.2f}>{sum(data[symbol_id])/100:.2f},'f'ltp ={live_data_objs[symbol_id].close:.2f}')else:print(f'{live_data_objs[symbol_id].symbol}- SEND OMS SHORT SIGNAL{sum(data[symbol_id][-50:])/50:.2f}<{sum(data[symbol_id])/100:.2f},'f'ltp ={live_data_objs[symbol_id].close:.2f}')# Graceful exittd_obj.stop_live_data(symbols)td_obj.disconnect()Release Notes(Version number significance>> 3 numbers denote major_update.minor_update.micro_update. A major_update indicates code-breaking changes for all users, whereas a micro_update, on the contrary, implies the library changes should not be affecting anyone.)Version 5.0.11Bugfix: option chain not updating after connection dropVersion 5.0.10Bidask l2 total bid and total ask are added ..td analytics addedVersion 5.0.8Bug Fix - annoying behaviour of historical login ..Version 5.0.7Bug Fix - reconnection of previously subscribed symbols implemented..Bug Fix - greeks order fixed..Version 5.0.6Bug Fix - path issue fixed for linux enviornment..Version 5.0.3Bug Fix - feed optimized with symbolid mapping..Version 5.0.2Bug Fix - if Bid ask not enabled then zero is appended in bid ask fields, which come with trade ticks to keep the structure intact..Version 5.0.1BidAsk Level 2 (Top 5 Bid Ask) option added to facilitate L2 in BSEFO segmentBidask structure changedBidask call back decorator renamed & now a dataclassTick call back moved to dataclass from plain dictGreeks call back introduced (includes IV & Greeks)Lib optimized .New Sample codes added to each section of this ReadmeBugs fixedVersion 4.3.5Index segment masters IND updated to INBugs removedVersion 4.3.3Websocket Feed Reconnection - Handling ImprovedDelivery data parameter added to EOD DataBugs fixedVersion 4.3.2week&monthnow available in bar sizesReal Time Streaming of Larger Datasets OptimizedBugs fixedVersion 4.3.1min_live_datadict in td_obj changed toone_min_live_datafive_min_live_datadict added totd_objfor 5 min bar minCallback for respective bar data changed to@td_obj.one_min_bar_callbackand@td_obj.five_min_bar_callbackMarket status message added to loggerDEBUGlevel.All option chain feature works for respective subscriptionsVersion 4.2.3Option Chain Streaming OptimizedLTQ, Price Change & Price Change % added to Options Change StreamingRate Limiting now handled gracefully by the libraryBugs RemovedVersion 4.2.2OI when Null (first few ticks of first time traded options) throws error - FixedReadme Updated with sample codes for Logging, callbacks etc...Bugs RemovedVersion 4.2.1Decompression added for pulling smaller files and for faster downloadsDecompression added for gethistoric data & getnhistoric dataRequirements updated (lz4==3.1.3  pivoted due to build issue in new version)Bugs fixedVersion 4.0.8Gainers Losers function includedCurrency option chains addedBugfix: Automatic Reconnection due to Network Issue - FixedVersion 4.0.7Option chains now include Previous OI, OI Change & Volumemarket_closeparameter changed tomarket_open_post_hours. Default is False. Set to true when markets live post normal trading hours (eg. Mahurat Trading)Bugfix - Python 32 bit - Float error handled.Bugfix - Removed the dict in case stoplivedata is called.Version 4.0.6Option chains into pandas enabled > td_obj.start_option_chain(Symbol, Expiry_date, chain length, bid_ask)The option chain is enabled for tick and 1 min bar streamingThe option chain also works post and pre market hours using the touchline dataHistory - Call Specific Number of historical Bars (Tick, 1 min & EOD)Code cleanedVersion 4.0.1Breaking changes made totd_obj.get_historic_data-> ONLY if theticker_idparameter is given, data can later be accessed attd_obj.hist_data[ticker_id].If you are subscribed to tick+min streaming, you can access the min bar data attd_obj.min_live_data[req_id]in additional to your tick feed attd_obj.live_data[req_id].Log Handler added + logging convenience parameters added + logging README sectionExamples added to repo.Examples + descriptions + differences updated in README.Version 3.0.2Automatic streaming websocket reconnect enabled.Automatic subscription of symbols & restart of live data after reconnectNow Avoiding server calls for repeated / duplicate streaming symbols...Version 3.0.1Historical feed via Websocket - DeprecatedHistorical feed via REST - AddedMore time frames have been addedget_bhavcopyaddedCode cleaned up to improve dependancy handling (eg. for websocket-client)Version 0.3.11Refactoredquery_timetoend_timeintd_obj.get_historic_data()function.Refactoredtruedata_idtosymbol_idintd_obj.touchline_dataobjects.Filled missing values intd_obj.live_dataobjects upon initialization.Added better debugging information for error reporting.Cleaned up the code base.Stay Updated with the latest on this APIPlease make sure you follow us on our Telegram channel where we push a lot of information with regards to API updates, implementation ideas, tips & tricks to use this library & raw feed, etc...TrueData Market Data API - Telegram ChannelTrueData Market Data APIs - WebsiteSandbox EnvironmentWe have also built a sandbox environmemt for testing the raw feed. Please feel free to use this environment to check/test/compare your results with the raw data feed (real time & historical).TrueData Market Data API - Sandbox EnvironmentWe are trying to improve this library continuously and feedback for the same is welcome.Note: - It is essential to ensure that the data received through our APIs is not being utilized for any commercial purposes. Additionally, please be mindful that all information or data provided to you is exclusively intended for your internal use and must be utilized and discontinued at your end as required.