├── .chainlit
└── config.toml
├── .env.example
├── .gitignore
├── LICENSE
├── README.md
├── app.py
├── chainlit.md
├── create_assistant.py
├── public
├── idea.svg
├── learn.svg
├── terminal.svg
└── write.svg
├── render.yaml
├── requirements.txt
└── tesla-stock-price.csv
/.chainlit/config.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | # Whether to enable telemetry (default: true). No personal data is collected.
3 | enable_telemetry = true
4 |
5 |
6 | # List of environment variables to be provided by each user to use the app.
7 | user_env = []
8 |
9 | # Duration (in seconds) during which the session is saved when the connection is lost
10 | session_timeout = 3600
11 |
12 | # Enable third parties caching (e.g LangChain cache)
13 | cache = false
14 |
15 | # Authorized origins
16 | allow_origins = ["*"]
17 |
18 | # Follow symlink for asset mount (see https://github.com/Chainlit/chainlit/issues/317)
19 | # follow_symlink = false
20 |
21 | [features]
22 | # Show the prompt playground
23 | prompt_playground = true
24 |
25 | # Process and display HTML in messages. This can be a security risk (see https://stackoverflow.com/questions/19603097/why-is-it-dangerous-to-render-user-generated-html-or-javascript)
26 | unsafe_allow_html = false
27 |
28 | # Process and display mathematical expressions. This can clash with "$" characters in messages.
29 | latex = false
30 |
31 | # Automatically tag threads with the current chat profile (if a chat profile is used)
32 | auto_tag_thread = true
33 |
34 | # Authorize users to spontaneously upload files with messages
35 | [features.spontaneous_file_upload]
36 | enabled = true
37 | accept = ["*/*"]
38 | max_files = 20
39 | max_size_mb = 500
40 |
41 | [features.audio]
42 | # Threshold for audio recording
43 | min_decibels = -45
44 | # Delay for the user to start speaking in MS
45 | initial_silence_timeout = 3000
46 | # Delay for the user to continue speaking in MS. If the user stops speaking for this duration, the recording will stop.
47 | silence_timeout = 1500
48 | # Above this duration (MS), the recording will forcefully stop.
49 | max_duration = 15000
50 | # Duration of the audio chunks in MS
51 | chunk_duration = 1000
52 | # Sample rate of the audio
53 | sample_rate = 44100
54 |
55 | [UI]
56 | # Name of the app and chatbot.
57 | name = "Data Analyst"
58 |
59 | # Show the readme while the thread is empty.
60 | show_readme_as_default = false
61 |
62 | # Description of the app and chatbot. This is used for HTML tags.
63 | # description = ""
64 |
65 | # Large size content are by default collapsed for a cleaner ui
66 | default_collapse_content = true
67 |
68 | # The default value for the expand messages settings.
69 | default_expand_messages = false
70 |
71 | # Hide the chain of thought details from the user in the UI.
72 | hide_cot = false
73 |
74 | # Link to your github repo. This will add a github button in the UI's header.
75 | github = "https://github.com/willydouhard/data-analyst"
76 |
77 | # Specify a CSS file that can be used to customize the user interface.
78 | # The CSS file can be served from the public directory or via an external link.
79 | # custom_css = "/public/test.css"
80 |
81 | # Specify a Javascript file that can be used to customize the user interface.
82 | # The Javascript file can be served from the public directory.
83 | # custom_js = "/public/test.js"
84 |
85 | # Specify a custom font url.
86 | # custom_font = "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap"
87 |
88 | # Specify a custom build directory for the frontend.
89 | # This can be used to customize the frontend code.
90 | # Be careful: If this is a relative path, it should not start with a slash.
91 | # custom_build = "./public/build"
92 |
93 | [UI.theme]
94 | default = "light"
95 | #layout = "wide"
96 | #font_family = "Inter, sans-serif"
97 | # Override default MUI light theme. (Check theme.ts)
98 | [UI.theme.light]
99 | #background = "#FAFAFA"
100 | #paper = "#FFFFFF"
101 |
102 | [UI.theme.light.primary]
103 | #main = "#F80061"
104 | #dark = "#980039"
105 | #light = "#FFE7EB"
106 |
107 | # Override default MUI dark theme. (Check theme.ts)
108 | [UI.theme.dark]
109 | #background = "#FAFAFA"
110 | #paper = "#FFFFFF"
111 |
112 | [UI.theme.dark.primary]
113 | #main = "#F80061"
114 | #dark = "#980039"
115 | #light = "#FFE7EB"
116 |
117 |
118 | [meta]
119 | generated_by = "1.1.0rc1"
120 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | OPENAI_API_KEY=
2 | OPENAI_ASSISTANT_ID=
3 | LITERAL_API_KEY=
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .env
2 | translations
3 | .files
4 | __pycache__
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 chainlit
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Data Analyst with OpenAI Assistant
2 |
3 | ### Supported Assistant Features
4 |
5 | | Streaming | Files | Code Interpreter | File Search | Voice |
6 | | --------- | ----- | ---------------- | ----------- | ----- |
7 | | ✅ | ✅ | ✅ | ✅ | ✅ |
8 |
9 | ### Get an OpenAI API key
10 |
11 | Go to OpenAI's [API keys page](https://platform.openai.com/api-keys) and create one if you don't have one already.
12 |
13 | ### Create a .env file
14 |
15 | Copy the `.env.example` file to create your own `.env` file and set your `OPENAI_API_KEY`.
16 |
17 | ### Create the assistant
18 |
19 | `python create_assistant.py`
20 |
21 | This will print the id of your assistant, set it in your `.env` file.
22 |
23 | ### Run locally
24 |
25 | `chainlit run app.py`
26 |
27 | ### [Optional] Get a Literal AI API key
28 |
29 | > [!NOTE]
30 | > Literal AI is an all in one observability, evaluation and analytics platform for building LLM apps.
31 |
32 | Go to [Literal AI](https://cloud.getliteral.ai/), create a project and go to Settings to get your API key.
33 |
34 | ### Deploy
35 |
36 | Click on the button below, then set the API keys in the form and click on `Apply`.
37 |
38 | [](https://render.com/deploy)
39 |
--------------------------------------------------------------------------------
/app.py:
--------------------------------------------------------------------------------
1 | import os
2 | import plotly
3 | from io import BytesIO
4 | from pathlib import Path
5 | from typing import List
6 |
7 | from openai import AsyncAssistantEventHandler, AsyncOpenAI, OpenAI
8 |
9 | from literalai.helper import utc_now
10 |
11 | import chainlit as cl
12 | from chainlit.config import config
13 | from chainlit.element import Element
14 | from openai.types.beta.threads.runs import RunStep
15 |
16 |
17 | async_openai_client = AsyncOpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
18 | sync_openai_client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
19 |
20 | assistant = sync_openai_client.beta.assistants.retrieve(
21 | os.environ.get("OPENAI_ASSISTANT_ID")
22 | )
23 |
24 | config.ui.name = assistant.name
25 |
26 | class EventHandler(AsyncAssistantEventHandler):
27 |
28 | def __init__(self, assistant_name: str) -> None:
29 | super().__init__()
30 | self.current_message: cl.Message = None
31 | self.current_step: cl.Step = None
32 | self.current_tool_call = None
33 | self.assistant_name = assistant_name
34 |
35 | async def on_run_step_created(self, run_step: RunStep) -> None:
36 | cl.user_session.set("run_step", run_step)
37 |
38 | async def on_text_created(self, text) -> None:
39 | self.current_message = await cl.Message(author=self.assistant_name, content="").send()
40 |
41 | async def on_text_delta(self, delta, snapshot):
42 | if delta.value:
43 | await self.current_message.stream_token(delta.value)
44 |
45 | async def on_text_done(self, text):
46 | await self.current_message.update()
47 | if text.annotations:
48 | print(text.annotations)
49 | for annotation in text.annotations:
50 | if annotation.type == "file_path":
51 | response = await async_openai_client.files.with_raw_response.content(annotation.file_path.file_id)
52 | file_name = annotation.text.split("/")[-1]
53 | try:
54 | fig = plotly.io.from_json(response.content)
55 | element = cl.Plotly(name=file_name, figure=fig)
56 | await cl.Message(
57 | content="",
58 | elements=[element]).send()
59 | except Exception as e:
60 | element = cl.File(content=response.content, name=file_name)
61 | await cl.Message(
62 | content="",
63 | elements=[element]).send()
64 | # Hack to fix links
65 | if annotation.text in self.current_message.content and element.chainlit_key:
66 | self.current_message.content = self.current_message.content.replace(annotation.text, f"/project/file/{element.chainlit_key}?session_id={cl.context.session.id}")
67 | await self.current_message.update()
68 |
69 | async def on_tool_call_created(self, tool_call):
70 | self.current_tool_call = tool_call.id
71 | self.current_step = cl.Step(name=tool_call.type, type="tool")
72 | self.current_step.show_input = "python"
73 | self.current_step.start = utc_now()
74 | await self.current_step.send()
75 |
76 | async def on_tool_call_delta(self, delta, snapshot):
77 | if snapshot.id != self.current_tool_call:
78 | self.current_tool_call = snapshot.id
79 | self.current_step = cl.Step(name=delta.type, type="tool")
80 | self.current_step.start = utc_now()
81 | if snapshot.type == "code_interpreter":
82 | self.current_step.show_input = "python"
83 | if snapshot.type == "function":
84 | self.current_step.name = snapshot.function.name
85 | self.current_step.language = "json"
86 | await self.current_step.send()
87 |
88 | if delta.type == "function":
89 | pass
90 |
91 | if delta.type == "code_interpreter":
92 | if delta.code_interpreter.outputs:
93 | for output in delta.code_interpreter.outputs:
94 | if output.type == "logs":
95 | self.current_step.output += output.logs
96 | self.current_step.language = "markdown"
97 | self.current_step.end = utc_now()
98 | await self.current_step.update()
99 | elif output.type == "image":
100 | self.current_step.language = "json"
101 | self.current_step.output = output.image.model_dump_json()
102 | else:
103 | if delta.code_interpreter.input:
104 | await self.current_step.stream_token(delta.code_interpreter.input, is_input=True)
105 |
106 | async def on_event(self, event) -> None:
107 | if event.event == "error":
108 | return cl.ErrorMessage(content=str(event.data.message)).send()
109 |
110 | async def on_exception(self, exception: Exception) -> None:
111 | return cl.ErrorMessage(content=str(exception)).send()
112 |
113 | async def on_tool_call_done(self, tool_call):
114 | self.current_step.end = utc_now()
115 | await self.current_step.update()
116 |
117 | async def on_image_file_done(self, image_file, message):
118 | image_id = image_file.file_id
119 | response = await async_openai_client.files.with_raw_response.content(image_id)
120 | image_element = cl.Image(
121 | name=image_id,
122 | content=response.content,
123 | display="inline",
124 | size="large"
125 | )
126 | if not self.current_message.elements:
127 | self.current_message.elements = []
128 | self.current_message.elements.append(image_element)
129 | await self.current_message.update()
130 |
131 |
132 | @cl.step(type="tool")
133 | async def speech_to_text(audio_file):
134 | response = await async_openai_client.audio.transcriptions.create(
135 | model="whisper-1", file=audio_file
136 | )
137 |
138 | return response.text
139 |
140 |
141 | async def upload_files(files: List[Element]):
142 | file_ids = []
143 | for file in files:
144 | uploaded_file = await async_openai_client.files.create(
145 | file=Path(file.path), purpose="assistants"
146 | )
147 | file_ids.append(uploaded_file.id)
148 | return file_ids
149 |
150 |
151 | async def process_files(files: List[Element]):
152 | # Upload files if any and get file_ids
153 | file_ids = []
154 | if len(files) > 0:
155 | file_ids = await upload_files(files)
156 |
157 | return [
158 | {
159 | "file_id": file_id,
160 | "tools": [{"type": "code_interpreter"}, {"type": "file_search"}] if file.mime in ["application/vnd.openxmlformats-officedocument.wordprocessingml.document", "text/markdown", "application/pdf", "text/plain"] else [{"type": "code_interpreter"}],
161 | }
162 | for file_id, file in zip(file_ids, files)
163 | ]
164 |
165 |
166 | @cl.set_starters
167 | async def set_starters():
168 | return [
169 | cl.Starter(
170 | label="Run Tesla stock analysis",
171 | message="Make a data analysis on the tesla-stock-price.csv file I previously uploaded.",
172 | icon="/public/write.svg",
173 | ),
174 | cl.Starter(
175 | label="Run a data analysis on my CSV",
176 | message="Make a data analysis on the next CSV file I will upload.",
177 | icon="/public/write.svg",
178 | )
179 | ]
180 |
181 | @cl.on_chat_start
182 | async def start_chat():
183 | # Create a Thread
184 | thread = await async_openai_client.beta.threads.create()
185 | # Store thread ID in user session for later use
186 | cl.user_session.set("thread_id", thread.id)
187 |
188 |
189 | @cl.on_stop
190 | async def stop_chat():
191 | current_run_step: RunStep = cl.user_session.get("run_step")
192 | if current_run_step:
193 | await async_openai_client.beta.threads.runs.cancel(thread_id=current_run_step.thread_id, run_id=current_run_step.run_id)
194 |
195 |
196 | @cl.on_message
197 | async def main(message: cl.Message):
198 | thread_id = cl.user_session.get("thread_id")
199 |
200 | attachments = await process_files(message.elements)
201 |
202 | # Add a Message to the Thread
203 | oai_message = await async_openai_client.beta.threads.messages.create(
204 | thread_id=thread_id,
205 | role="user",
206 | content=message.content,
207 | attachments=attachments,
208 | )
209 |
210 | # Create and Stream a Run
211 | async with async_openai_client.beta.threads.runs.stream(
212 | thread_id=thread_id,
213 | assistant_id=assistant.id,
214 | event_handler=EventHandler(assistant_name=assistant.name),
215 | ) as stream:
216 | await stream.until_done()
217 |
218 |
219 | @cl.on_audio_chunk
220 | async def on_audio_chunk(chunk: cl.AudioChunk):
221 | if chunk.isStart:
222 | buffer = BytesIO()
223 | # This is required for whisper to recognize the file type
224 | buffer.name = f"input_audio.{chunk.mimeType.split('/')[1]}"
225 | # Initialize the session for a new audio stream
226 | cl.user_session.set("audio_buffer", buffer)
227 | cl.user_session.set("audio_mime_type", chunk.mimeType)
228 |
229 | # Write the chunks to a buffer and transcribe the whole audio at the end
230 | cl.user_session.get("audio_buffer").write(chunk.data)
231 |
232 |
233 | @cl.on_audio_end
234 | async def on_audio_end(elements: list[Element]):
235 | # Get the audio buffer from the session
236 | audio_buffer: BytesIO = cl.user_session.get("audio_buffer")
237 | audio_buffer.seek(0) # Move the file pointer to the beginning
238 | audio_file = audio_buffer.read()
239 | audio_mime_type: str = cl.user_session.get("audio_mime_type")
240 |
241 | input_audio_el = cl.Audio(
242 | mime=audio_mime_type, content=audio_file, name=audio_buffer.name
243 | )
244 | await cl.Message(
245 | type="user_message",
246 | content="",
247 | elements=[input_audio_el, *elements],
248 | ).send()
249 |
250 | whisper_input = (audio_buffer.name, audio_file, audio_mime_type)
251 | transcription = await speech_to_text(whisper_input)
252 |
253 | msg = cl.Message(author="You", content=transcription, elements=elements)
254 |
255 | await main(message=msg)
256 |
--------------------------------------------------------------------------------
/chainlit.md:
--------------------------------------------------------------------------------
1 | # Welcome to Chainlit! 🚀🤖
2 |
3 | Hi there, Developer! 👋 We're excited to have you on board. Chainlit is a powerful tool designed to help you prototype, debug and share applications built on top of LLMs.
4 |
5 | ## Useful Links 🔗
6 |
7 | - **Documentation:** Get started with our comprehensive [Chainlit Documentation](https://docs.chainlit.io) 📚
8 | - **Discord Community:** Join our friendly [Chainlit Discord](https://discord.gg/k73SQ3FyUh) to ask questions, share your projects, and connect with other developers! 💬
9 |
10 | We can't wait to see what you create with Chainlit! Happy coding! 💻😊
11 |
12 | ## Welcome screen
13 |
14 | To modify the welcome screen, edit the `chainlit.md` file at the root of your project. If you do not want a welcome screen, just leave this file empty.
15 |
--------------------------------------------------------------------------------
/create_assistant.py:
--------------------------------------------------------------------------------
1 | from dotenv import load_dotenv
2 | load_dotenv()
3 |
4 | import os
5 | from openai import OpenAI
6 |
7 | openai_client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
8 |
9 |
10 | instructions = """You are an assistant running data analysis on CSV files.
11 |
12 | You will use code interpreter to run the analysis.
13 |
14 | However, instead of rendering the charts as images, you will generate a plotly figure and turn it into json.
15 | You will create a file for each json that I can download through annotations.
16 | """
17 |
18 | tools = [
19 | {"type": "code_interpreter"},
20 | {"type": "file_search"}
21 | ]
22 |
23 | file = openai_client.files.create(
24 | file=open("tesla-stock-price.csv", "rb"),
25 | purpose='assistants'
26 | )
27 |
28 |
29 | assistant = openai_client.beta.assistants.create(
30 | model="gpt-4o",
31 | name="Data Analysis Assistant",
32 | instructions=instructions,
33 | temperature=0.1,
34 | tools=tools,
35 | tool_resources={
36 | "code_interpreter": {
37 | "file_ids": [file.id]
38 | }
39 | }
40 | )
41 |
42 | print(f"Assistant created with id: {assistant.id}")
--------------------------------------------------------------------------------
/public/idea.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/learn.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/terminal.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/public/write.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/render.yaml:
--------------------------------------------------------------------------------
1 | # Exported from Render on 2024-05-20T20:56:35Z
2 | services:
3 | - type: web
4 | name: Chainlit-Data-Analyst
5 | runtime: python
6 | repo: https://github.com/willydouhard/data-analyst
7 | plan: starter
8 | envVars:
9 | - key: OPENAI_API_KEY
10 | sync: false
11 | - key: OPENAI_ASSISTANT_ID
12 | sync: false
13 | - key: LITERAL_API_KEY
14 | sync: false
15 | region: frankfurt
16 | buildCommand: pip install -r requirements.txt
17 | startCommand: chainlit run app.py -h --port $PORT
18 | version: "1"
19 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | openai
2 | plotly
3 | chainlit==1.1.300rc4
--------------------------------------------------------------------------------
/tesla-stock-price.csv:
--------------------------------------------------------------------------------
1 | Date,Open,High,Low,Close,Adj Close,Volume
2 | 2023-05-30,200.100006,204.479996,197.529999,201.160004,201.160004,128818700
3 | 2023-05-31,199.779999,203.949997,195.119995,203.929993,203.929993,150711700
4 | 2023-06-01,202.589996,209.800003,199.369995,207.520004,207.520004,148029900
5 | 2023-06-02,210.149994,217.250000,209.750000,213.970001,213.970001,164129000
6 | 2023-06-05,217.800003,221.289993,214.520004,217.610001,217.610001,151143100
7 | 2023-06-06,216.139999,221.910004,212.529999,221.309998,221.309998,146911600
8 | 2023-06-07,228.000000,230.830002,223.199997,224.570007,224.570007,185710800
9 | 2023-06-08,224.220001,235.229996,223.009995,234.860001,234.860001,164489700
10 | 2023-06-09,249.070007,252.419998,242.020004,244.399994,244.399994,199882300
11 | 2023-06-12,247.940002,250.970001,244.589996,249.830002,249.830002,150337900
12 | 2023-06-13,253.509995,259.679993,251.339996,258.709991,258.709991,162384300
13 | 2023-06-14,260.170013,261.570007,250.500000,256.790009,256.790009,170575500
14 | 2023-06-15,248.399994,258.950012,247.289993,255.899994,255.899994,160171200
15 | 2023-06-16,258.920013,263.600006,257.209991,260.540009,260.540009,167563700
16 | 2023-06-20,261.500000,274.750000,261.119995,274.450012,274.450012,165611200
17 | 2023-06-21,275.130005,276.989990,257.779999,259.459991,259.459991,211797100
18 | 2023-06-22,250.770004,265.000000,248.250000,264.609985,264.609985,166875900
19 | 2023-06-23,259.290009,262.450012,252.800003,256.600006,256.600006,176584100
20 | 2023-06-26,250.070007,258.369995,240.699997,241.050003,241.050003,179990600
21 | 2023-06-27,243.240005,250.389999,240.850006,250.210007,250.210007,164968200
22 | 2023-06-28,249.699997,259.880005,248.889999,256.239990,256.239990,159770800
23 | 2023-06-29,258.029999,260.739990,253.610001,257.500000,257.500000,131283400
24 | 2023-06-30,260.600006,264.450012,259.890015,261.769989,261.769989,112267600
25 | 2023-07-03,276.489990,284.250000,275.109985,279.820007,279.820007,119685900
26 | 2023-07-05,278.820007,283.850006,277.600006,282.480011,282.480011,131530900
27 | 2023-07-06,278.089996,279.970001,272.880005,276.540009,276.540009,120332100
28 | 2023-07-07,278.429993,280.779999,273.769989,274.429993,274.429993,113602000
29 | 2023-07-10,276.470001,277.519989,265.100006,269.609985,269.609985,119425400
30 | 2023-07-11,268.649994,270.899994,266.369995,269.790009,269.790009,91972400
31 | 2023-07-12,276.329987,276.519989,271.459991,271.989990,271.989990,95672100
32 | 2023-07-13,274.589996,279.450012,270.600006,277.899994,277.899994,112681500
33 | 2023-07-14,277.010010,285.299988,276.309998,281.380005,281.380005,119771100
34 | 2023-07-17,286.630005,292.230011,283.570007,290.380005,290.380005,131569600
35 | 2023-07-18,290.149994,295.260010,286.010010,293.339996,293.339996,112434700
36 | 2023-07-19,296.040009,299.290009,289.519989,291.260010,291.260010,142355400
37 | 2023-07-20,279.559998,280.929993,261.200012,262.899994,262.899994,175158300
38 | 2023-07-21,268.000000,268.000000,255.800003,260.019989,260.019989,161050100
39 | 2023-07-24,255.850006,269.850006,254.119995,269.059998,269.059998,136508500
40 | 2023-07-25,272.380005,272.899994,265.000000,265.279999,265.279999,112757300
41 | 2023-07-26,263.250000,268.040009,261.750000,264.350006,264.350006,95856200
42 | 2023-07-27,268.309998,269.130005,255.300003,255.710007,255.710007,103697300
43 | 2023-07-28,259.859985,267.250000,258.230011,266.440002,266.440002,111446000
44 | 2023-07-31,267.480011,269.079987,263.779999,267.429993,267.429993,84582200
45 | 2023-08-01,266.260010,266.470001,260.250000,261.070007,261.070007,83166000
46 | 2023-08-02,255.570007,259.519989,250.490005,254.110001,254.110001,101752900
47 | 2023-08-03,252.039993,260.489990,252.000000,259.320007,259.320007,97569100
48 | 2023-08-04,260.970001,264.769989,253.110001,253.860001,253.860001,99242600
49 | 2023-08-07,251.449997,253.649994,242.759995,251.449997,251.449997,111097900
50 | 2023-08-08,247.449997,250.919998,245.009995,249.699997,249.699997,96642200
51 | 2023-08-09,250.869995,251.100006,241.899994,242.190002,242.190002,101596300
52 | 2023-08-10,245.399994,251.800003,243.000000,245.339996,245.339996,109498600
53 | 2023-08-11,241.770004,243.789993,238.020004,242.649994,242.649994,98866600
54 | 2023-08-14,235.699997,240.660004,233.750000,239.759995,239.759995,98595300
55 | 2023-08-15,238.729996,240.500000,232.610001,232.960007,232.960007,88197600
56 | 2023-08-16,228.020004,233.970001,225.380005,225.600006,225.600006,112484500
57 | 2023-08-17,226.059998,226.740005,218.830002,219.220001,219.220001,120718400
58 | 2023-08-18,214.119995,217.580002,212.360001,215.490005,215.490005,135813700
59 | 2023-08-21,221.550003,232.130005,220.580002,231.279999,231.279999,135702700
60 | 2023-08-22,240.250000,240.820007,229.550003,233.190002,233.190002,130597900
61 | 2023-08-23,229.339996,238.979996,229.289993,236.860001,236.860001,101077600
62 | 2023-08-24,238.660004,238.919998,228.179993,230.039993,230.039993,99777400
63 | 2023-08-25,231.309998,239.000000,230.350006,238.589996,238.589996,106612200
64 | 2023-08-28,242.580002,244.380005,235.350006,238.820007,238.820007,107673700
65 | 2023-08-29,238.580002,257.480011,237.770004,257.179993,257.179993,134047600
66 | 2023-08-30,254.199997,260.510010,250.589996,256.899994,256.899994,121988400
67 | 2023-08-31,255.979996,261.179993,255.050003,258.079987,258.079987,108861700
68 | 2023-09-01,257.260010,259.079987,242.009995,245.009995,245.009995,132272500
69 | 2023-09-05,245.000000,258.000000,244.860001,256.489990,256.489990,129469600
70 | 2023-09-06,255.139999,255.389999,245.059998,251.919998,251.919998,116959800
71 | 2023-09-07,245.070007,252.809998,243.270004,251.490005,251.490005,115312900
72 | 2023-09-08,251.220001,256.519989,246.669998,248.500000,248.500000,118367700
73 | 2023-09-11,264.269989,274.850006,260.609985,273.579987,273.579987,174667900
74 | 2023-09-12,270.760010,278.390015,266.600006,267.480011,267.480011,135999900
75 | 2023-09-13,270.070007,274.980011,268.100006,271.299988,271.299988,111673700
76 | 2023-09-14,271.320007,276.709991,270.420013,276.040009,276.040009,107709800
77 | 2023-09-15,277.549988,278.980011,271.000000,274.390015,274.390015,133422800
78 | 2023-09-18,271.160004,271.440002,263.760010,265.279999,265.279999,101543300
79 | 2023-09-19,264.350006,267.850006,261.200012,266.500000,266.500000,103704000
80 | 2023-09-20,267.040009,273.929993,262.459991,262.589996,262.589996,122514600
81 | 2023-09-21,257.850006,260.859985,254.210007,255.699997,255.699997,119531000
82 | 2023-09-22,257.399994,257.790009,244.479996,244.880005,244.880005,127524100
83 | 2023-09-25,243.380005,247.100006,238.309998,246.990005,246.990005,104636600
84 | 2023-09-26,242.979996,249.550003,241.660004,244.119995,244.119995,101993600
85 | 2023-09-27,244.259995,245.330002,234.580002,240.500000,240.500000,136597200
86 | 2023-09-28,240.020004,247.550003,238.649994,246.380005,246.380005,117058900
87 | 2023-09-29,250.000000,254.770004,246.350006,250.220001,250.220001,128346200
88 | 2023-10-02,244.809998,254.279999,242.619995,251.600006,251.600006,123810400
89 | 2023-10-03,248.610001,250.020004,244.449997,246.529999,246.529999,101985300
90 | 2023-10-04,248.139999,261.859985,247.600006,261.160004,261.160004,129721600
91 | 2023-10-05,260.000000,263.600006,256.250000,260.049988,260.049988,119159200
92 | 2023-10-06,253.979996,261.649994,250.649994,260.529999,260.529999,117947000
93 | 2023-10-09,255.309998,261.359985,252.050003,259.670013,259.670013,101377900
94 | 2023-10-10,257.750000,268.940002,257.649994,263.619995,263.619995,122656000
95 | 2023-10-11,266.200012,268.600006,260.899994,262.989990,262.989990,103706300
96 | 2023-10-12,262.920013,265.410004,256.630005,258.869995,258.869995,111508100
97 | 2023-10-13,258.899994,259.600006,250.220001,251.119995,251.119995,102073800
98 | 2023-10-16,250.050003,255.399994,248.479996,253.919998,253.919998,88917200
99 | 2023-10-17,250.100006,257.179993,247.080002,254.850006,254.850006,93562900
100 | 2023-10-18,252.699997,254.630005,242.080002,242.679993,242.679993,125147800
101 | 2023-10-19,225.949997,230.610001,216.779999,220.110001,220.110001,170772700
102 | 2023-10-20,217.009995,218.860001,210.419998,211.990005,211.990005,137734000
103 | 2023-10-23,210.000000,216.979996,202.509995,212.080002,212.080002,150683400
104 | 2023-10-24,216.500000,222.050003,214.110001,216.520004,216.520004,118231100
105 | 2023-10-25,215.880005,220.100006,212.199997,212.419998,212.419998,107065100
106 | 2023-10-26,211.320007,214.800003,204.880005,205.759995,205.759995,115112600
107 | 2023-10-27,210.600006,212.410004,205.770004,207.300003,207.300003,94881200
108 | 2023-10-30,209.279999,210.880005,194.669998,197.360001,197.360001,136448200
109 | 2023-10-31,196.119995,202.800003,194.070007,200.839996,200.839996,118068300
110 | 2023-11-01,204.039993,205.990005,197.850006,205.660004,205.660004,121661700
111 | 2023-11-02,212.970001,219.199997,211.449997,218.509995,218.509995,125987600
112 | 2023-11-03,221.149994,226.369995,218.399994,219.960007,219.960007,119281000
113 | 2023-11-06,223.979996,226.320007,215.000000,219.270004,219.270004,117335800
114 | 2023-11-07,219.979996,223.119995,215.720001,222.179993,222.179993,116900100
115 | 2023-11-08,223.149994,224.149994,217.639999,222.110001,222.110001,106584800
116 | 2023-11-09,219.750000,220.800003,206.679993,209.979996,209.979996,142110500
117 | 2023-11-10,210.029999,215.380005,205.690002,214.649994,214.649994,130994000
118 | 2023-11-13,215.600006,225.399994,211.610001,223.710007,223.710007,140447600
119 | 2023-11-14,235.029999,238.139999,230.720001,237.410004,237.410004,149771600
120 | 2023-11-15,239.289993,246.699997,236.449997,242.839996,242.839996,150354000
121 | 2023-11-16,239.490005,240.880005,230.960007,233.589996,233.589996,136816800
122 | 2023-11-17,232.000000,237.389999,226.539993,234.300003,234.300003,142532800
123 | 2023-11-20,234.039993,237.100006,231.020004,235.600006,235.600006,116320100
124 | 2023-11-21,235.039993,243.619995,233.339996,241.199997,241.199997,122288000
125 | 2023-11-22,242.039993,244.009995,231.399994,234.210007,234.210007,117950600
126 | 2023-11-24,233.750000,238.750000,232.330002,235.449997,235.449997,65125200
127 | 2023-11-27,236.889999,238.330002,232.100006,236.080002,236.080002,112031800
128 | 2023-11-28,236.679993,247.000000,234.009995,246.720001,246.720001,148549900
129 | 2023-11-29,249.210007,252.750000,242.759995,244.139999,244.139999,135401300
130 | 2023-11-30,245.139999,245.220001,236.910004,240.080002,240.080002,132353200
131 | 2023-12-01,233.139999,240.190002,231.899994,238.830002,238.830002,121173500
132 | 2023-12-04,235.750000,239.369995,233.289993,235.580002,235.580002,104099800
133 | 2023-12-05,233.869995,246.660004,233.699997,238.720001,238.720001,137971100
134 | 2023-12-06,242.919998,246.570007,239.169998,239.369995,239.369995,126436200
135 | 2023-12-07,241.550003,244.080002,236.979996,242.639999,242.639999,107142300
136 | 2023-12-08,240.270004,245.270004,239.270004,243.839996,243.839996,102980100
137 | 2023-12-11,242.740005,243.440002,237.449997,239.740005,239.740005,97913900
138 | 2023-12-12,238.550003,238.990005,233.869995,237.009995,237.009995,95328300
139 | 2023-12-13,234.190002,240.300003,228.199997,239.289993,239.289993,146286300
140 | 2023-12-14,241.220001,253.880005,240.789993,251.050003,251.050003,160829200
141 | 2023-12-15,251.210007,254.130005,248.300003,253.500000,253.500000,135720800
142 | 2023-12-18,253.779999,258.739990,251.360001,252.080002,252.080002,116416500
143 | 2023-12-19,253.479996,258.339996,253.009995,257.220001,257.220001,106737400
144 | 2023-12-20,256.410004,259.839996,247.000000,247.139999,247.139999,125097000
145 | 2023-12-21,251.899994,254.800003,248.550003,254.500000,254.500000,109594200
146 | 2023-12-22,256.760010,258.220001,251.369995,252.539993,252.539993,93249800
147 | 2023-12-26,254.490005,257.970001,252.910004,256.609985,256.609985,86892400
148 | 2023-12-27,258.350006,263.339996,257.519989,261.440002,261.440002,106494400
149 | 2023-12-28,263.660004,265.130005,252.710007,253.179993,253.179993,113619900
150 | 2023-12-29,255.100006,255.190002,247.429993,248.479996,248.479996,100615300
151 | 2024-01-02,250.080002,251.250000,244.410004,248.419998,248.419998,104654200
152 | 2024-01-03,244.979996,245.679993,236.320007,238.449997,238.449997,121082600
153 | 2024-01-04,239.250000,242.699997,237.729996,237.929993,237.929993,102629300
154 | 2024-01-05,236.860001,240.119995,234.899994,237.490005,237.490005,92379400
155 | 2024-01-08,236.139999,241.250000,235.300003,240.449997,240.449997,85166600
156 | 2024-01-09,238.110001,238.960007,232.039993,234.960007,234.960007,96705700
157 | 2024-01-10,235.100006,235.500000,231.289993,233.940002,233.940002,91628500
158 | 2024-01-11,230.570007,230.929993,225.369995,227.220001,227.220001,105873600
159 | 2024-01-12,220.080002,225.339996,217.149994,218.889999,218.889999,122889000
160 | 2024-01-16,215.100006,223.490005,212.179993,219.910004,219.910004,115355000
161 | 2024-01-17,214.860001,215.669998,212.009995,215.550003,215.550003,103164400
162 | 2024-01-18,216.880005,217.449997,208.740005,211.880005,211.880005,108595400
163 | 2024-01-19,209.990005,213.190002,207.559998,212.190002,212.190002,102095800
164 | 2024-01-22,212.259995,217.800003,206.270004,208.800003,208.800003,117952500
165 | 2024-01-23,211.300003,215.649994,207.750000,209.139999,209.139999,106605900
166 | 2024-01-24,211.880005,212.729996,206.770004,207.830002,207.830002,123369900
167 | 2024-01-25,189.699997,193.000000,180.059998,182.630005,182.630005,198076800
168 | 2024-01-26,185.500000,186.779999,182.100006,183.250000,183.250000,107343200
169 | 2024-01-29,185.630005,191.479996,183.669998,190.929993,190.929993,125013100
170 | 2024-01-30,195.330002,196.360001,190.610001,191.589996,191.589996,109982300
171 | 2024-01-31,187.000000,193.970001,185.850006,187.289993,187.289993,103221400
172 | 2024-02-01,188.500000,189.880005,184.279999,188.860001,188.860001,91843300
173 | 2024-02-02,185.039993,188.690002,182.000000,187.910004,187.910004,110505100
174 | 2024-02-05,184.259995,184.679993,175.009995,181.059998,181.059998,134294400
175 | 2024-02-06,177.210007,186.490005,177.110001,185.100006,185.100006,122676000
176 | 2024-02-07,188.179993,189.789993,182.679993,187.580002,187.580002,111535200
177 | 2024-02-08,189.000000,191.619995,185.580002,189.559998,189.559998,83034000
178 | 2024-02-09,190.179993,194.119995,189.479996,193.570007,193.570007,84476300
179 | 2024-02-12,192.110001,194.729996,187.279999,188.130005,188.130005,95498600
180 | 2024-02-13,183.990005,187.259995,182.110001,184.020004,184.020004,86759500
181 | 2024-02-14,185.300003,188.889999,183.350006,188.710007,188.710007,81203000
182 | 2024-02-15,189.160004,200.880005,188.860001,200.449997,200.449997,120831800
183 | 2024-02-16,202.059998,203.169998,197.399994,199.949997,199.949997,111173600
184 | 2024-02-20,196.130005,198.600006,189.130005,193.759995,193.759995,104545800
185 | 2024-02-21,193.360001,199.440002,191.949997,194.770004,194.770004,103844000
186 | 2024-02-22,194.000000,198.320007,191.360001,197.410004,197.410004,92739500
187 | 2024-02-23,195.309998,197.570007,191.500000,191.970001,191.970001,78841900
188 | 2024-02-26,192.289993,201.779999,192.000000,199.399994,199.399994,111747100
189 | 2024-02-27,204.039993,205.600006,198.259995,199.729996,199.729996,108645400
190 | 2024-02-28,200.419998,205.300003,198.440002,202.039993,202.039993,99806200
191 | 2024-02-29,204.179993,205.279999,198.449997,201.880005,201.880005,85907000
192 | 2024-03-01,200.520004,204.520004,198.500000,202.639999,202.639999,82099200
193 | 2024-03-04,198.729996,199.750000,186.720001,188.139999,188.139999,134334900
194 | 2024-03-05,183.050003,184.589996,177.570007,180.740005,180.740005,119660800
195 | 2024-03-06,179.990005,181.580002,173.699997,176.539993,176.539993,107920900
196 | 2024-03-07,174.350006,180.039993,173.699997,178.649994,178.649994,102129000
197 | 2024-03-08,181.500000,182.729996,174.699997,175.339996,175.339996,85315300
198 | 2024-03-11,175.449997,182.869995,174.800003,177.770004,177.770004,85391500
199 | 2024-03-12,177.770004,179.429993,172.410004,177.539993,177.539993,87391700
200 | 2024-03-13,173.050003,176.050003,169.149994,169.479996,169.479996,106524500
201 | 2024-03-14,167.770004,171.169998,160.509995,162.500000,162.500000,126325700
202 | 2024-03-15,163.160004,165.179993,160.759995,163.570007,163.570007,96971900
203 | 2024-03-18,170.020004,174.720001,165.899994,173.800003,173.800003,108214400
204 | 2024-03-19,172.360001,172.820007,167.419998,171.320007,171.320007,77271400
205 | 2024-03-20,173.000000,176.250000,170.820007,175.660004,175.660004,83846700
206 | 2024-03-21,176.389999,178.179993,171.800003,172.820007,172.820007,73178000
207 | 2024-03-22,166.690002,171.199997,166.300003,170.830002,170.830002,75454700
208 | 2024-03-25,168.759995,175.240005,168.729996,172.630005,172.630005,74228600
209 | 2024-03-26,178.580002,184.250000,177.380005,177.669998,177.669998,113186200
210 | 2024-03-27,181.410004,181.910004,176.000000,179.830002,179.830002,81804000
211 | 2024-03-28,177.449997,179.570007,175.300003,175.789993,175.789993,77654800
212 | 2024-04-01,176.169998,176.750000,170.210007,175.220001,175.220001,81562100
213 | 2024-04-02,164.750000,167.690002,163.429993,166.630005,166.630005,116650600
214 | 2024-04-03,164.020004,168.820007,163.279999,168.380005,168.380005,82950100
215 | 2024-04-04,170.070007,177.190002,168.009995,171.110001,171.110001,123162000
216 | 2024-04-05,169.080002,170.860001,160.509995,164.899994,164.899994,141250700
217 | 2024-04-08,169.339996,174.500000,167.789993,172.979996,172.979996,104423300
218 | 2024-04-09,172.910004,179.220001,171.919998,176.880005,176.880005,103232700
219 | 2024-04-10,173.039993,174.929993,170.009995,171.759995,171.759995,84532400
220 | 2024-04-11,172.550003,175.880005,168.509995,174.600006,174.600006,94516000
221 | 2024-04-12,172.339996,173.809998,170.360001,171.050003,171.050003,64506600
222 | 2024-04-15,170.240005,170.690002,161.380005,161.479996,161.479996,100245300
223 | 2024-04-16,156.740005,158.190002,153.750000,157.110001,157.110001,97000000
224 | 2024-04-17,157.639999,158.330002,153.779999,155.449997,155.449997,82439700
225 | 2024-04-18,151.250000,152.199997,148.699997,149.929993,149.929993,96098800
226 | 2024-04-19,148.970001,150.940002,146.220001,147.050003,147.050003,86005100
227 | 2024-04-22,140.559998,144.440002,138.800003,142.050003,142.050003,107097600
228 | 2024-04-23,143.330002,147.259995,141.110001,144.679993,144.679993,124545100
229 | 2024-04-24,162.839996,167.970001,157.509995,162.130005,162.130005,181178000
230 | 2024-04-25,158.960007,170.880005,158.360001,170.179993,170.179993,126427500
231 | 2024-04-26,168.850006,172.119995,166.369995,168.289993,168.289993,109815700
232 | 2024-04-29,188.419998,198.869995,184.539993,194.050003,194.050003,243869700
233 | 2024-04-30,186.979996,190.949997,182.839996,183.279999,183.279999,127031800
234 | 2024-05-01,182.000000,185.860001,179.009995,179.990005,179.990005,92829700
235 | 2024-05-02,182.860001,184.600006,176.020004,180.009995,180.009995,89148000
236 | 2024-05-03,182.100006,184.779999,178.419998,181.190002,181.190002,75491500
237 | 2024-05-06,183.800003,187.559998,182.199997,184.759995,184.759995,84390300
238 | 2024-05-07,182.399994,183.259995,177.399994,177.809998,177.809998,75045900
239 | 2024-05-08,171.589996,176.059998,170.149994,174.720001,174.720001,79969500
240 | 2024-05-09,175.009995,175.619995,171.369995,171.970001,171.970001,65950300
241 | 2024-05-10,173.050003,173.059998,167.750000,168.470001,168.470001,72627200
242 | 2024-05-13,170.000000,175.399994,169.000000,171.889999,171.889999,67018900
243 | 2024-05-14,174.500000,179.490005,174.070007,177.550003,177.550003,86407400
244 | 2024-05-15,179.899994,180.000000,173.110001,173.990005,173.990005,79663000
245 | 2024-05-16,174.100006,175.789993,171.429993,174.839996,174.839996,59812200
246 | 2024-05-17,173.550003,179.630005,172.750000,177.460007,177.460007,77445800
247 | 2024-05-20,177.559998,177.750000,173.520004,174.949997,174.949997,61727400
248 | 2024-05-21,175.509995,186.880005,174.710007,186.600006,186.600006,115266500
249 | 2024-05-22,182.850006,183.800003,178.119995,180.110001,180.110001,88313500
250 | 2024-05-23,181.800003,181.899994,173.259995,173.740005,173.740005,71975500
251 | 2024-05-24,174.839996,180.080002,173.729996,179.240005,179.240005,65479700
252 | 2024-05-28,176.399994,178.250000,173.160004,176.750000,176.750000,59736600
253 | 2024-05-29,174.190002,178.149994,173.929993,176.190002,176.190002,54684300
--------------------------------------------------------------------------------