Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: WebSocket Client for Live Data Streaming #2201

Open
wants to merge 3 commits into
base: dev
Choose a base branch
from

Conversation

dhruvan2006
Copy link
Contributor

@dhruvan2006 dhruvan2006 commented Jan 2, 2025

Changes

  • Introduced a new WebSocket class to stream live market data.
  • Added ProtoBuf files (pricing.proto and pricing_pb2.py) for data de serialization.
  • Updated requirements.txt and setup.py to include new dependencies.
  • Added documentation for usage

Usage

import asyncio
import yfinance as yf

def message_handler(message):
    print("Received message:", message)

async def main():
    client = yf.AsyncWebSocket(verbose=False)
    await client.subscribe(["AAPL", "BTC-USD", "ETH-USD"])
    await client.unsubscribe("ETH-USD")
    await client.listen()

def sync_example():
    client = yf.WebSocket()
    client.subscribe(["AAPL", "BTC-USD"])
    client.listen(message_handler)

Pick one from below to test feature:

asyncio.run(main()) # Async
sync_example()      # Sync
yf.Ticker("SOL-USD").live(message_handler)                              # Ticker
yf.Tickers(["ETH-USD", "SOL-USD"]).live(message_handler, False) # Tickers

@dhruvan2006
Copy link
Contributor Author

@ValueRaider any opinions on this websocket? Is it suitable for this package?

@ValueRaider
Copy link
Collaborator

ValueRaider commented Jan 6, 2025

I'm focusing on the Screener stuff right now. Can someone else try and feedback?

Previous discussions: #319 #290

Copy link
Contributor

@R5dan R5dan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could add a new function in WebSocket for initialization.
Also I think its worth considering adding an async option

@dhruvan2006
Copy link
Contributor Author

@ValueRaider Should the websocket be an extra feature like nospam & require or part of base yfinance?

@dhruvan2006 dhruvan2006 force-pushed the feature/websocket branch 2 times, most recently from 9415f4d to 5e14a92 Compare January 25, 2025 19:12
@dhruvan2006
Copy link
Contributor Author

dhruvan2006 commented Jan 25, 2025

This PR is completed

@ValueRaider
Copy link
Collaborator

ValueRaider commented Jan 26, 2025

Haven't used yet, was planning to try when markets open. My gut tells me this should be a separate package. It's basically a standalone app stapled onto yfinance, different way of interacting with Yahoo, nothing in common.

Also out-of-the-box is broken:

>>> import asyncio
>>> import yfinance as yf
  File "yfinance/yfinance/pricing_pb2.py", line 9, in <module>
    from google.protobuf import runtime_version as _runtime_version
ImportError: cannot import name 'runtime_version' from 'google.protobuf' (~/.local/lib/python3.12/site-packages/google/protobuf/__init__.py)

Before you do any more work, check this out: https://github.com/yahoofinancelive/yliveticker #319 #290

@R5dan
Copy link
Contributor

R5dan commented Jan 28, 2025

It's basically a standalone app stapled onto yfinance, different way of interacting with Yahoo, nothing in common.

I would disagree, although it is a different way to access data, it still is just accessing the data like the rest of yfinance. Especially as the main focus of yfinance is simpy to get the data on Yahoo! Finance

@ValueRaider
Copy link
Collaborator

What are your thoughts on https://github.com/yahoofinancelive/yliveticker?

@dhruvan2006
Copy link
Contributor Author

I always believed yfinance to be a wrapper over Yahoo! Finance API and that the goal was always to make it complete.

This package yliveticker hasn't been maintained for four years and lacks async support.

As the maintainer, if your gut differs we can close this PR.


Also out-of-the-box is broken:

I am unable to reproduce this. Can you try again, maybe it was a problem with versions.

@ValueRaider
Copy link
Collaborator

This package yliveticker hasn't been maintained for four years and lacks async support.

Fair point, looks like owner doesn't care yahoofinancelive/yliveticker#12

Can you try again, maybe it was a problem with versions.

Clean fetch, same error.

@dhruvan2006
Copy link
Contributor Author

Clean fetch, same error.

Can you help me with this please?

@ValueRaider
Copy link
Collaborator

I had protobuf version 3.20, updating to latest 5.29 fixed error. Can you set a minimum version? Try to get to at least MAJOR.MINOR.

@ValueRaider
Copy link
Collaborator

I think users need some working example codes for actual useful use cases, not printing to screen. Like live Dash visualisation or stream into database. I had a quick go at Dash but even AI struggles.

@dhruvan2006
Copy link
Contributor Author

Can you set a minimum version? Try to get to at least MAJOR.MINOR.

I already have this in both requirements.txt and setup.py protobuf>=5.29.2

@dhruvan2006
Copy link
Contributor Author

I had a quick go at Dash but even AI struggles.

Here is what I was able to come up with.

temp.mp4
pip install dash dash-bootstrap-components pandas plotly yfinance flask
import asyncio
import threading
from datetime import datetime

from dash import dcc, html
import dash_bootstrap_components as dbc
import dash
import pandas as pd
from dash.dependencies import Output, Input
from flask import Flask
import plotly.graph_objs as go
import yfinance as yf

server = Flask(__name__)
app = dash.Dash(__name__, server=server, external_stylesheets=[dbc.themes.CYBORG])

ws = yf.AsyncWebSocket(verbose=False)

STOCK = "MSFT"
df = pd.DataFrame(columns=["time", "price"])


def start_websocket():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)

    async def stream_data():
        await ws.subscribe(STOCK)

        def handle_message(msg):
            global df
            if "id" in msg and "price" in msg:
                now = datetime.now().strftime("%H:%M:%S")
                new_row = {"time": now, "price": float(msg["price"])}
                if df.empty:
                    df = pd.DataFrame([new_row])
                else:
                    if now in df["time"].values:
                        df.loc[df["time"] == now, "price"] = float(msg["price"])
                    else:
                        df = pd.concat([df, pd.DataFrame([new_row])]).tail(50)

        await ws.listen(handle_message)

    loop.run_until_complete(stream_data())


threading.Thread(target=start_websocket, daemon=True).start()

app.layout = html.Div([
    html.H2("Live Stock Price Stream", style={"textAlign": "center", "color": "white"}),
    dcc.Graph(id="live-graph"),
    dcc.Interval(id="interval-update", interval=500, n_intervals=0),
])


@app.callback(Output("live-graph", "figure"), Input("interval-update", "n_intervals"))
def update_graph(n):
    global df
    figure = go.Figure()
    if not df.empty:
        figure.add_trace(go.Scatter(x=df["time"], y=df["price"], mode="lines+markers", name="Price"))
    figure.update_layout(title=f"Live Price of {STOCK}", xaxis_title="Time", yaxis_title="Price")
    return figure


if __name__ == "__main__":
    app.run_server(debug=True)

@StorkST
Copy link

StorkST commented Feb 20, 2025

Hello,

Any news on this feature? Will it be added to main branch?

@ValueRaider
Copy link
Collaborator

ValueRaider commented Feb 20, 2025

I just need time to try it thoroughly. You don't have to wait for a merge

@dhruvan2006 How have you been using it in real world?

@dhruvan2006 dhruvan2006 force-pushed the feature/websocket branch 2 times, most recently from c99fd99 to 257d6e0 Compare February 24, 2025 11:08
@dhruvan2006
Copy link
Contributor Author

dhruvan2006 commented Feb 24, 2025

I rebased onto dev

@dhruvan2006 How have you been using it in real world?

I use it on my website to track live prices

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants