samjulien commited on
Commit
79a1132
1 Parent(s): c099819

Add application

Browse files
.gitignore ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ venv
2
+ __pycache__
3
+ .env
4
+ tmp*.pdf
5
+ .DS_Store
Dockerfile ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Build stage
2
+ FROM python:3.11-slim-buster AS Build
3
+
4
+ # Set environment variables for Python and Poetry
5
+ ENV PYTHONUNBUFFERED=1 \
6
+ PIP_NO_CACHE_DIR=1 \
7
+ POETRY_NO_INTERACTION=1 \
8
+ POETRY_VIRTUALENVS_CREATE=false \
9
+ POETRY_VERSION=1.7.1
10
+
11
+ # Set the working directory in the container
12
+ WORKDIR /app
13
+
14
+ # Copy the dependencies file to the working directory
15
+ COPY ./pyproject.toml /app/
16
+
17
+ # Update, install dependencies, and prepare the Python environment
18
+ RUN apt-get update && \
19
+ apt-get install -y gcc g++ unixodbc-dev && \
20
+ pip install "poetry==$POETRY_VERSION" && \
21
+ poetry export --without-hashes --format requirements.txt --output requirements.txt && \
22
+ python3 -m pip wheel --no-cache-dir --no-deps -w /app/wheels -r requirements.txt
23
+
24
+ # Runtime stage
25
+ FROM python:3.11-slim-buster AS Run
26
+
27
+ # Set environment variables for Python and Poetry
28
+ ENV HOME=/home/user \
29
+ PATH=/home/user/.local/bin:$PATH
30
+
31
+ # Create a non-root user
32
+ RUN useradd -m -u 1000 user
33
+
34
+ # Switch to the non-root user
35
+ USER user
36
+
37
+ # Copy wheel files from the build stage
38
+ COPY --from=build /app/wheels $HOME/app/wheels
39
+
40
+ # Set the working directory to where the wheels are
41
+ WORKDIR $HOME/app/wheels
42
+
43
+ # Install the wheel files
44
+ RUN pip3 --no-cache-dir install *.whl
45
+
46
+ # Copy the application files to the working directory (change to your app name)
47
+ COPY --chown=user ./finance-app-tutorial $HOME/app
48
+
49
+ # Set the working directory to the application files
50
+ WORKDIR $HOME/app
51
+
52
+ # Specify the command to run the application
53
+ ENTRYPOINT [ "writer", "run" ]
54
+
55
+ # Expose the port the app runs on
56
+ EXPOSE 8080
57
+
58
+ # Set the default command to run the app
59
+ CMD [ ".", "--port", "8080", "--host", "0.0.0.0" ]
README.md CHANGED
@@ -5,6 +5,7 @@ colorFrom: green
5
  colorTo: red
6
  sdk: docker
7
  pinned: false
 
8
  ---
9
 
10
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
5
  colorTo: red
6
  sdk: docker
7
  pinned: false
8
+ app_port: 8080
9
  ---
10
 
11
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
finance-app-tutorial/README.md ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ This app was created using Writer Framework.
2
+
3
+ To learn more about it, visit https://dev.writer.com/framework
finance-app-tutorial/charts.py ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import plotly.express as px
3
+ import plotly.graph_objects as go
4
+ from plotly.subplots import make_subplots
5
+
6
+ def handle_click(state, context: dict, ui):
7
+ # Resetting the classes for active button
8
+ if state["active_button"]:
9
+ active_button = ui.find(state["active_button"])
10
+ active_button.content["cssClasses"] = ""
11
+
12
+ event_target = context["target"]
13
+ button = ui.find(event_target)
14
+
15
+ # Storing the clicked button ID in state
16
+ state["active_button"] = event_target
17
+
18
+ button_text = button.content["text"]
19
+ _handle_time_period(state, button_text)
20
+ button.content["cssClasses"] = "button-click"
21
+
22
+ button_max = ui.find("e13teponreio9yyz")
23
+ button_max.content["cssClasses"] = ""
24
+
25
+ ui.component_tree.updated = True
26
+
27
+ def _handle_time_period(state, period):
28
+ state["main_df_subset"] = state["main_df"]
29
+ if period == "5D":
30
+ state["main_df_subset"] = state["main_df_subset"][:5]
31
+ elif period == "1M":
32
+ state["main_df_subset"] = state["main_df_subset"][:30]
33
+ elif period == "3M":
34
+ state["main_df_subset"] = state["main_df_subset"][:90]
35
+ elif period == "1Y":
36
+ state["main_df_subset"] = state["main_df_subset"][:360]
37
+ elif period == "5Y":
38
+ state["main_df_subset"] = state["main_df_subset"][:1800]
39
+ elif period == "Max":
40
+ # No need to slice, already has the full data
41
+ pass
42
+ update_scatter_chart(state)
43
+
44
+ def update_scatter_chart(state):
45
+ fig = px.line(state["main_df_subset"], x="Date", y="Open", height=400)
46
+
47
+ df1 = state["main_df_subset"]
48
+ df2 = state["another_df"]
49
+ df2 = df2.head(len(df1))
50
+
51
+ # Add a new column to each dataframe to identify the source
52
+ df1["Source"] = "Main_DF"
53
+ df2["Source"] = "Another_DF"
54
+
55
+ # Concatenate the dataframes
56
+ combined_df = pd.concat([df1, df2])
57
+ state["main_df_subset"] = combined_df
58
+
59
+ # Plot the lines
60
+ fig = make_subplots(specs=[[{"secondary_y": True}]])
61
+
62
+ # Add traces for the primary y-axis (Main_DF)
63
+ fig.add_trace(
64
+ go.Scatter(x=df1["Date"], y=df1["Open"], name=state["symbol"], mode='lines'),
65
+ secondary_y=False,
66
+ )
67
+
68
+ # Add traces for the secondary y-axis (Another_DF)
69
+ fig.add_trace(
70
+ go.Scatter(x=df2["Date"], y=df2["Open"], name="S&P 500", mode='lines'),
71
+ secondary_y=True,
72
+ )
73
+
74
+ # Set axis titles
75
+ fig.update_yaxes(title_text=f"{state["symbol"]} Stock Price", secondary_y=False)
76
+ fig.update_yaxes(title_text="S&P 500", secondary_y=True)
77
+
78
+ # Update layout
79
+ fig.update_layout(height=550, title_text=f"{state["symbol"]} Stock vs the S&P 500", title_x = 0.5, title_y = 0.9, legend=dict(
80
+ orientation='h',
81
+ yanchor='top',
82
+ y=-0.2, # Adjust this value as needed
83
+ xanchor='center',
84
+ x=0.5
85
+ ))
86
+
87
+ state["scatter_chart"] = fig
88
+
89
+ def update_bar_graph(state):
90
+ fig = px.line(state["main_df_subset"], x="Date", y="Open", height=400)
91
+
92
+ df = state["income_statement_df"]
93
+ selected_metrics = ["Total Revenue", "Net Income", "Operating Income"]
94
+ df_filtered = df.loc[selected_metrics]
95
+
96
+ # Transpose the DataFrame for easier plotting
97
+ df_transposed = df_filtered.transpose().reset_index()
98
+ df_transposed = df_transposed.melt(
99
+ id_vars=["index"], var_name="Metric", value_name="Value"
100
+ )
101
+
102
+ # Create the bar graph using Plotly Express
103
+ fig = px.bar(
104
+ df_transposed,
105
+ x="index",
106
+ y="Value",
107
+ color="Metric",
108
+ barmode="group",
109
+ labels={"index": "", "Value": ""},
110
+ title="Summary of Quarterly Income Statement",
111
+ )
112
+
113
+ fig.update_layout(
114
+ legend=dict(orientation="h", yanchor="top", y=-0.2, xanchor="center", x=0.5)
115
+ )
116
+
117
+ state["bar_graph"] = fig
finance-app-tutorial/daily_APPLE.csv ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ timestamp,open,high,low,close,volume
2
+ 2024-06-17,213.3700,218.9500,212.7200,216.6700,93728300
3
+ 2024-06-14,213.8500,215.1700,211.3000,212.4900,70122748
4
+ 2024-06-13,214.7400,216.7500,211.6000,214.2400,97862729
5
+ 2024-06-12,207.3700,220.2000,206.9000,213.0700,198134293
6
+ 2024-06-11,193.6500,207.1600,193.6300,207.1500,172373296
7
+ 2024-06-10,196.9000,197.3000,192.1500,193.1200,97262077
8
+ 2024-06-07,194.6500,196.9400,194.1400,196.8900,53103912
9
+ 2024-06-06,195.6850,196.5000,194.1700,194.4800,41181753
10
+ 2024-06-05,195.4000,196.9000,194.8700,195.8700,54156785
11
+ 2024-06-04,194.6350,195.3200,193.0342,194.3500,47471445
12
+ 2024-06-03,192.9000,194.9900,192.5200,194.0300,50080539
13
+ 2024-05-31,191.4400,192.5700,189.9100,192.2500,75158277
14
+ 2024-05-30,190.7600,192.1800,190.6300,191.2900,49947941
15
+ 2024-05-29,189.6100,192.2470,189.5100,190.2900,53068016
16
+ 2024-05-28,191.5100,193.0000,189.1000,189.9900,52280051
17
+ 2024-05-24,188.8200,190.5800,188.0404,189.9800,36326975
18
+ 2024-05-23,190.9800,191.0000,186.6250,186.8800,51005924
19
+ 2024-05-22,192.2650,192.8231,190.2700,190.9000,34648547
20
+ 2024-05-21,191.0900,192.7300,190.9201,192.3500,42309401
21
+ 2024-05-20,189.3250,191.9199,189.0100,191.0400,44361275
22
+ 2024-05-17,189.5100,190.8100,189.1800,189.8700,41282925
23
+ 2024-05-16,190.4700,191.0950,189.6601,189.8400,52845230
24
+ 2024-05-15,187.9100,190.6500,187.3700,189.7200,70399988
25
+ 2024-05-14,187.5100,188.3000,186.2900,187.4300,52393619
26
+ 2024-05-13,185.4350,187.1000,184.6200,186.2800,72044809
27
+ 2024-05-10,184.9000,185.0900,182.1300,183.0500,50759496
28
+ 2024-05-09,182.5600,184.6600,182.1100,184.5700,48982972
29
+ 2024-05-08,182.8500,183.0700,181.4500,182.7400,45057087
30
+ 2024-05-07,183.4500,184.9000,181.3200,182.4000,77305771
31
+ 2024-05-06,182.3540,184.2000,180.4200,181.7100,78569667
32
+ 2024-05-03,186.6450,187.0000,182.6600,183.3800,163224109
33
+ 2024-05-02,172.5100,173.4150,170.8900,173.0300,94214915
34
+ 2024-05-01,169.5800,172.7050,169.1100,169.3000,50383147
35
+ 2024-04-30,173.3300,174.9900,170.0000,170.3300,65934776
36
+ 2024-04-29,173.3700,176.0300,173.1000,173.5000,68169419
37
+ 2024-04-26,169.8800,171.3400,169.1800,169.3000,44838354
38
+ 2024-04-25,169.5250,170.6100,168.1511,169.8900,50558329
39
+ 2024-04-24,166.5400,169.3000,166.2100,169.0200,48251835
40
+ 2024-04-23,165.3500,167.0500,164.9200,166.9000,49537761
41
+ 2024-04-22,165.5150,167.2600,164.7700,165.8400,48116443
42
+ 2024-04-19,166.2100,166.4000,164.0750,165.0000,68149377
43
+ 2024-04-18,168.0300,168.6400,166.5500,167.0400,43122903
44
+ 2024-04-17,169.6100,170.6500,168.0000,168.0000,50901210
45
+ 2024-04-16,171.7500,173.7600,168.2700,169.3800,73711235
46
+ 2024-04-15,175.3600,176.6300,172.5000,172.6900,73531773
47
+ 2024-04-12,174.2600,178.3600,174.2100,176.5500,101670886
48
+ 2024-04-11,168.3400,175.4600,168.1600,175.0400,91070275
49
+ 2024-04-10,168.8000,169.0900,167.1100,167.7800,49709336
50
+ 2024-04-09,168.7000,170.0800,168.3500,169.6700,42231444
51
+ 2024-04-08,169.0300,169.2000,168.2400,168.4500,37216858
52
+ 2024-04-05,169.5900,170.3900,168.9500,169.5800,41975776
53
+ 2024-04-04,170.2900,171.9200,168.8200,168.8200,53355055
54
+ 2024-04-03,168.7900,170.6800,168.5800,169.6500,45571129
55
+ 2024-04-02,169.0800,169.3400,168.2302,168.8400,49013991
56
+ 2024-04-01,171.1900,171.2500,169.4750,170.0300,43772506
57
+ 2024-03-28,171.7500,172.2300,170.5100,171.4800,65672690
58
+ 2024-03-27,170.4100,173.6000,170.1100,173.3100,60273265
59
+ 2024-03-26,170.0000,171.4200,169.5800,169.7100,57388449
60
+ 2024-03-25,170.5650,171.9400,169.4500,170.8500,54288328
61
+ 2024-03-22,171.7600,173.0500,170.0600,172.2800,71160138
62
+ 2024-03-21,177.0500,177.4900,170.8400,171.3700,106181270
63
+ 2024-03-20,175.7200,178.6700,175.0900,178.6700,53423102
64
+ 2024-03-19,174.3400,176.6050,173.0300,176.0800,55215244
65
+ 2024-03-18,175.5700,177.7100,173.5200,173.7200,75604184
66
+ 2024-03-15,171.1700,172.6200,170.2850,172.6200,121752699
67
+ 2024-03-14,172.9100,174.3078,172.0500,173.0000,72571635
68
+ 2024-03-13,172.7700,173.1850,170.7600,171.1300,51948951
69
+ 2024-03-12,173.1500,174.0300,171.0100,173.2300,59544927
70
+ 2024-03-11,172.9400,174.3800,172.0500,172.7500,58929918
71
+ 2024-03-08,169.0000,173.7000,168.9400,170.7300,76267041
72
+ 2024-03-07,169.1500,170.7300,168.4900,169.0000,71765061
73
+ 2024-03-06,171.0600,171.2400,168.6800,169.1200,68587707
74
+ 2024-03-05,170.7600,172.0400,169.6200,170.1200,95132355
75
+ 2024-03-04,176.1500,176.9000,173.7900,175.1000,81510101
76
+ 2024-03-01,179.5500,180.5300,177.3800,179.6600,73563082
77
+ 2024-02-29,181.2700,182.5700,179.5300,180.7500,136682597
78
+ 2024-02-28,182.5100,183.1200,180.1300,181.4200,48953939
79
+ 2024-02-27,181.1000,183.9225,179.5600,182.6300,54318851
80
+ 2024-02-26,182.2400,182.7600,180.6500,181.1600,40867421
81
+ 2024-02-23,185.0100,185.0400,182.2300,182.5200,45119677
82
+ 2024-02-22,183.4800,184.9550,182.4600,184.3700,52292208
83
+ 2024-02-21,181.9400,182.8888,180.6600,182.3200,41529674
84
+ 2024-02-20,181.7900,182.4300,180.0000,181.5600,53665553
85
+ 2024-02-16,183.4200,184.8500,181.6650,182.3100,49752465
86
+ 2024-02-15,183.5500,184.4900,181.3500,183.8600,65434496
87
+ 2024-02-14,185.3200,185.5300,182.4400,184.1500,54630517
88
+ 2024-02-13,185.7700,186.2100,183.5128,185.0400,56529529
89
+ 2024-02-12,188.4150,188.6700,186.7900,187.1500,41781934
90
+ 2024-02-09,188.6500,189.9900,188.0000,188.8500,45155216
91
+ 2024-02-08,189.3850,189.5350,187.3500,188.3200,40962046
92
+ 2024-02-07,190.6400,191.0500,188.6100,189.4100,53438955
93
+ 2024-02-06,186.8600,189.3100,186.7695,189.3000,43490759
94
+ 2024-02-05,188.1500,189.2500,185.8400,187.6800,69668820
95
+ 2024-02-02,179.8600,187.3300,179.2500,185.8500,102551680
96
+ 2024-02-01,183.9850,186.9500,183.8200,186.8600,64885408
97
+ 2024-01-31,187.0400,187.0950,184.3500,184.4000,55467803
98
+ 2024-01-30,190.9400,191.8000,187.4700,188.0400,55859370
99
+ 2024-01-29,192.0100,192.2000,189.5800,191.7300,47145622
100
+ 2024-01-26,194.2700,194.7600,191.9400,192.4200,44594011
101
+ 2024-01-25,195.2200,196.2675,193.1125,194.1700,54822126
finance-app-tutorial/daily_IBM.csv ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ timestamp,open,high,low,close,volume
2
+ 2024-06-17,168.7600,169.7200,167.5000,169.5000,3239815
3
+ 2024-06-14,168.2900,169.4700,167.2300,169.2100,2777717
4
+ 2024-06-13,169.0100,169.5900,168.3350,169.1200,3525717
5
+ 2024-06-12,171.3500,172.4700,168.1010,169.0000,3522698
6
+ 2024-06-11,169.9800,170.0000,166.8100,169.3200,2951251
7
+ 2024-06-10,169.5500,170.7600,168.8800,170.3800,3444684
8
+ 2024-06-07,168.1800,171.3050,168.0600,170.0100,3475495
9
+ 2024-06-06,167.3800,168.4400,166.8000,168.2000,2207263
10
+ 2024-06-05,166.4100,167.7900,165.7800,167.3800,3049377
11
+ 2024-06-04,164.6000,166.4000,163.8800,165.8100,2594203
12
+ 2024-06-03,166.5400,166.7800,163.5300,165.2800,2776058
13
+ 2024-05-31,165.7000,166.9700,163.8400,166.8500,4905002
14
+ 2024-05-30,165.5600,166.7300,164.2300,165.6300,3852963
15
+ 2024-05-29,168.0000,168.6300,166.2100,167.0500,4206576
16
+ 2024-05-28,170.4400,171.0850,168.6500,169.6600,2629645
17
+ 2024-05-24,171.4800,172.0100,170.2100,170.8900,2587829
18
+ 2024-05-23,175.3900,175.4600,170.4350,170.6700,3341335
19
+ 2024-05-22,173.3900,174.9900,172.7600,173.6900,3294900
20
+ 2024-05-21,169.9400,174.9700,169.9400,173.4700,6459800
21
+ 2024-05-20,169.0000,170.1600,168.3800,169.9200,2726261
22
+ 2024-05-17,168.9700,169.1100,167.3300,169.0300,2956387
23
+ 2024-05-16,168.2600,169.6300,167.7900,168.9700,3492267
24
+ 2024-05-15,167.9400,168.3500,167.3400,168.2600,4468823
25
+ 2024-05-14,167.8600,168.1300,166.4800,167.3600,2600967
26
+ 2024-05-13,167.5000,168.0600,166.7600,167.5600,2414859
27
+ 2024-05-10,167.1300,168.0700,166.3200,167.1500,2255370
28
+ 2024-05-09,167.5000,167.5500,165.8800,166.2700,4266616
29
+ 2024-05-08,168.0100,170.2600,167.9000,169.9000,3522011
30
+ 2024-05-07,169.0000,169.2900,167.9400,168.3800,3155260
31
+ 2024-05-06,166.5000,168.6700,166.3800,168.6100,4222266
32
+ 2024-05-03,165.0000,166.6100,164.9200,165.7100,3400405
33
+ 2024-05-02,164.3500,164.8800,162.6200,164.6900,3829853
34
+ 2024-05-01,165.6900,166.2700,164.3000,164.4300,4030960
35
+ 2024-04-30,166.4900,166.7600,165.2605,166.2000,6011634
36
+ 2024-04-29,167.4000,168.2200,166.2250,167.4300,5263342
37
+ 2024-04-26,167.5000,167.8700,165.7300,167.1300,8983796
38
+ 2024-04-25,168.2000,172.4500,165.6600,168.9100,16702150
39
+ 2024-04-24,183.1700,184.2900,181.4000,184.1000,7616643
40
+ 2024-04-23,182.7300,184.6800,179.0000,182.1900,5950229
41
+ 2024-04-22,182.4500,183.3150,180.4500,181.9000,3076451
42
+ 2024-04-19,182.4300,182.8000,180.5700,181.5800,3037990
43
+ 2024-04-18,182.3500,183.4600,180.1700,181.4700,2886733
44
+ 2024-04-17,184.1600,184.6700,181.7800,183.1000,3003033
45
+ 2024-04-16,185.5900,185.7100,182.8600,183.7500,4473654
46
+ 2024-04-15,185.5700,187.4800,180.8800,181.2500,3528140
47
+ 2024-04-12,184.0000,185.1699,181.6850,182.2700,3547378
48
+ 2024-04-11,186.0400,186.7950,184.5800,185.9000,2861736
49
+ 2024-04-10,187.4200,187.9150,185.5200,186.0400,3081915
50
+ 2024-04-09,190.5400,191.2500,186.6600,189.3100,2790673
51
+ 2024-04-08,189.2400,190.2400,188.9118,189.8200,2673611
52
+ 2024-04-05,188.5900,190.3200,188.0200,189.1400,2012428
53
+ 2024-04-04,192.0000,193.2800,187.3400,187.9400,2924438
54
+ 2024-04-03,188.6000,191.3500,188.4850,190.9000,2818910
55
+ 2024-04-02,189.1400,189.8000,187.6000,188.8800,2689711
56
+ 2024-04-01,190.0000,190.4600,188.5200,189.8300,2362586
57
+ 2024-03-28,190.9400,191.9299,190.3400,190.9600,3742169
58
+ 2024-03-27,189.6000,190.9600,188.6000,190.8000,3693305
59
+ 2024-03-26,189.0200,190.0000,188.5000,188.5000,4229535
60
+ 2024-03-25,190.2600,190.8200,188.7500,188.7900,3718289
61
+ 2024-03-22,192.0000,192.9850,190.5100,190.8400,3988398
62
+ 2024-03-21,193.0000,193.3700,190.0100,191.9000,6013561
63
+ 2024-03-20,192.8700,193.9800,191.3100,193.9600,3238643
64
+ 2024-03-19,191.4900,193.5800,190.2800,193.3400,5317341
65
+ 2024-03-18,191.7000,193.2300,190.3200,191.6900,5410562
66
+ 2024-03-15,191.9900,193.0573,190.7000,191.0700,8828184
67
+ 2024-03-14,196.9500,197.7480,192.1200,193.4300,4102202
68
+ 2024-03-13,197.5500,198.1000,195.3200,196.7000,3960737
69
+ 2024-03-12,192.4600,199.1800,192.1500,197.7800,5862512
70
+ 2024-03-11,195.0900,195.3800,190.8800,191.7300,4712688
71
+ 2024-03-08,196.0600,197.7700,194.3800,195.9500,3943113
72
+ 2024-03-07,197.5800,198.7300,196.1400,196.5400,4604458
73
+ 2024-03-06,193.5000,198.1300,192.9600,196.1600,6945818
74
+ 2024-03-05,192.0000,193.9400,190.5700,191.9500,5653641
75
+ 2024-03-04,187.7600,193.8980,187.6000,193.0600,7938266
76
+ 2024-03-01,185.4900,188.3800,185.1800,188.2000,4018354
77
+ 2024-02-29,186.1500,186.8495,184.6900,185.0300,6458487
78
+ 2024-02-28,184.6300,185.3700,183.5500,185.3000,3216345
79
+ 2024-02-27,184.1600,185.1300,182.6200,184.8700,3641378
80
+ 2024-02-26,185.6000,186.1250,184.0600,184.1300,4620815
81
+ 2024-02-23,184.9000,186.4550,184.5700,185.7200,3433800
82
+ 2024-02-22,182.4500,184.5500,181.9300,184.2100,5078398
83
+ 2024-02-21,182.5600,183.0300,178.7500,179.7000,4728473
84
+ 2024-02-20,187.6400,188.7700,183.0600,183.4400,4247181
85
+ 2024-02-16,186.6300,188.9500,185.9452,187.6400,4842840
86
+ 2024-02-15,183.6200,186.9800,183.6200,186.8700,4714301
87
+ 2024-02-14,185.0000,185.0000,182.2600,183.5700,3173391
88
+ 2024-02-13,184.2800,184.7700,182.3600,183.7000,4290453
89
+ 2024-02-12,185.9000,186.4800,184.0300,186.1600,4724021
90
+ 2024-02-09,184.4400,187.1800,183.8500,186.3400,5064641
91
+ 2024-02-08,182.6300,184.5500,181.4900,184.3600,5161185
92
+ 2024-02-07,183.3400,184.0200,182.6250,183.7400,4841188
93
+ 2024-02-06,183.5500,184.6800,183.0400,183.4100,3338196
94
+ 2024-02-05,185.5100,185.7800,183.2550,183.4200,4379602
95
+ 2024-02-02,187.1000,187.3900,185.6150,185.7900,4055411
96
+ 2024-02-01,183.6300,187.5100,182.7100,186.9000,4669444
97
+ 2024-01-31,187.0500,187.6500,183.1400,183.6600,8876055
98
+ 2024-01-30,187.7100,188.6500,186.7700,187.8700,4575058
99
+ 2024-01-29,187.4600,189.4600,186.0500,187.1400,6107908
100
+ 2024-01-26,191.3100,192.3896,186.1600,187.4200,9895941
101
+ 2024-01-25,184.9600,196.9000,184.8300,190.4300,29596239
finance-app-tutorial/earnings-data.json ADDED
The diff for this file is too large to render. See raw diff
 
finance-app-tutorial/main.py ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import writer as wf
2
+ import writer.ai
3
+ import pandas as pd
4
+ from prompts import stock_prompts, income_prompts, earnings_prompt
5
+ from stock_data import download_data, download_sp500, stock_news, _one_day_data, income_statement, earnings_calls
6
+ from charts import update_scatter_chart
7
+ from dotenv import load_dotenv
8
+ import os
9
+
10
+ load_dotenv()
11
+
12
+ writer.ai.init(os.getenv("WRITER_API_KEY"))
13
+
14
+ # Update all data
15
+ def updates(state):
16
+ state["message"] = "% Refreshing stock data..."
17
+ earnings_calls(state)
18
+ download_sp500(state)
19
+ stock_news(state)
20
+ download_data(state)
21
+ income_statement(state)
22
+ update_scatter_chart(state)
23
+ _one_day_data(state)
24
+ _refresh_window(state)
25
+
26
+ # Refresh the window
27
+ def _refresh_window(state):
28
+ state["show_income_metrics"]["visible"] = False
29
+ state["show_bar_graph"]["visible"] = False
30
+ state["show_analysis_text"]["visible"] = False
31
+ state["show_analysis_text"]["language"] = False
32
+ state["message"] = "Writer AI insights will be generated here"
33
+
34
+ # Summarize earnings call using Palmyra-Fin model
35
+ def summarize_earnings(state):
36
+ _refresh_window(state)
37
+ state["message"] = f"% {state["symbol"]} earnings call will be summarized here"
38
+
39
+ earnings_transcript = state["earnings_transcript"]
40
+ prompt = earnings_prompt.format(earnings_transcript=earnings_transcript)
41
+ submission = writer.ai.complete(prompt, config={"model": "palmyra-fin-32k", "temperature": 0.7, "max_tokens": 8192})
42
+ state["message"] = f"+ {state["symbol"]} earnings call summary"
43
+ state["analysis"] = submission.strip()
44
+ state["show_analysis_text"]["visible"] = True
45
+
46
+ def prompt_parameters_words(state,payload):
47
+ state["prompt_parameters_words"] = payload
48
+ _refresh_window(state)
49
+
50
+ def prompt_parameters_lang(state,payload):
51
+ state["prompt_parameters_lang"] = payload
52
+ generate_stock_analysis(state)
53
+
54
+ def generate_stock_analysis(state):
55
+ _refresh_window(state)
56
+ if(state["prompt_parameters_lang"] == ""):
57
+ state["prompt_parameters_lang"] == "English"
58
+
59
+ state["message"] = f"% {state["symbol"]} trends will be analyzed here in {state['prompt_parameters_lang']}"
60
+ stock_name = state["symbol"]
61
+ stock_data = state["main_df"][:365]
62
+
63
+ rounded_value = round(state["prompt_parameters_words"], 0)
64
+ language = state["prompt_parameters_lang"]
65
+ # Convert the rounded value to an integer
66
+ integer_value = int(rounded_value)
67
+
68
+ prompt = stock_prompts.format(language=language, stock_name=stock_name,words=integer_value,stock_data=stock_data)
69
+ submission = writer.ai.complete(prompt, config={"model": "palmyra-fin-32k", "temperature": 0.7, "max_tokens": 8192})
70
+ state["analysis"] = submission.strip()
71
+ state["message"] = f"+ {state["symbol"]} trends analyzed"
72
+ state["show_analysis_text"]["visible"] = True
73
+ state["show_analysis_text"]["language"] = True
74
+
75
+ return submission
76
+
77
+
78
+ def generate_income_analysis(state):
79
+ _refresh_window(state)
80
+ state["message"] = f"% {state["symbol"]} income statement will be visualized here"
81
+ stock_name = state["symbol"]
82
+ stock_data = state["main_df"][:365]
83
+ income_statement_data = state["income_statement_df"][:365]
84
+ prompt = income_prompts.format(
85
+ income_statement_data=income_statement_data,
86
+ stock_data=stock_data,
87
+ stock_name=stock_name,
88
+ )
89
+ submission = writer.ai.complete(prompt, config={"model": "palmyra-fin-32k", "temperature": 0.7, "max_tokens": 8192})
90
+ state["analysis"] = submission.strip()
91
+ state["message"] = f"+ {state["symbol"]} income statement visualized"
92
+ state["show_income_metrics"]["visible"] = True
93
+ state["show_analysis_text"]["visible"] = True
94
+ state["show_bar_graph"]["visible"] = True
95
+ return submission
96
+
97
+ def stock_tags(state, payload):
98
+ state["symbol"] = payload
99
+ updates(state)
100
+
101
+ def _get_main_df(filename):
102
+ main_df = pd.read_csv(filename)
103
+ return main_df
104
+
105
+ initial_state = wf.init_state(
106
+ {
107
+ "last_24_hours_open": "168.76",
108
+ "last_24_hours_high": "169.72",
109
+ "last_24_hours_low": "167.50",
110
+ "message": None,
111
+ "main_df": _get_main_df("daily_IBM.csv"),
112
+ "main_df_subset": _get_main_df("daily_IBM.csv"),
113
+ "symbol": "AAPL",
114
+ "articles": {
115
+ "EU seeks views on Microsoft, OpenAI, Google and Samsung deals, EU's Vestager says": {
116
+ "source": "Reuters",
117
+ "published_at": "June 28, 2024 at 15:40",
118
+ "url": "https://finance.yahoo.com/news/eu-seeks-views-microsoft-openai-144044802.html",
119
+ },
120
+ "Forget the S&P 500 -- Buy This Magnificent ETF Instead": {
121
+ "source": "Motley Fool",
122
+ "published_at": "June 28, 2024 at 15:00",
123
+ "url": "https://finance.yahoo.com/m/662fcc10-7b6c-3bd1-8b7f-6cc25e8a1e61/forget-the-s%26p-500-buy.html",
124
+ },
125
+ "Microsoft Corporation (MSFT) is Attracting Investor Attention: Here is What You Should Know": {
126
+ "source": "Zacks",
127
+ "published_at": "June 28, 2024 at 14:00",
128
+ "url": "https://finance.yahoo.com/news/microsoft-corporation-msft-attracting-investor-130015018.html",
129
+ },
130
+ },
131
+ "show_analysis_text": {
132
+ "visible": False,
133
+ "language": False
134
+ },
135
+ "show_income_metrics": {
136
+ "visible": False,
137
+ },
138
+ "tab_message": "- **Performance** tab highlights stock trends using an interactive graph where time filters can be selected.<br><li>**Stock data** tab shows the stock data from Yahoo Finance. <br><li>**Income data** tab shows the income statement from Yahoo Finance. <br><li>**View 10-K** tab shows the selected stock 10-K in a PDF viewer shown via an _API integration_.",
139
+ "prompt_parameters_lang": "English",
140
+ "prompt_parameters_words": 100,
141
+ "message": "Writer AI insights will be generated here",
142
+ "show_bar_graph": {"visible": False},
143
+ "output_language": {
144
+ "English": "English",
145
+ "Arabic": "Arabic",
146
+ "Bengali": "Bengali",
147
+ "Bulgarian": "Bulgarian",
148
+ "Chinese simplified": "Chinese simplified",
149
+ "Chinese traditional": "Chinese traditional",
150
+ "Croatian": "Croatian",
151
+ "Czech": "Czech",
152
+ "Danish": "Danish",
153
+ "Dutch": "Dutch",
154
+ "Finnish": "Finnish",
155
+ "French": "French",
156
+ "German": "German",
157
+ "Greek": "Greek",
158
+ "Hebrew": "Hebrew",
159
+ "Hindi": "Hindi",
160
+ "Hungarian": "Hungarian",
161
+ "Indonesian": "Indonesian",
162
+ "Italian": "Italian",
163
+ "Japanese": "Japanese",
164
+ "Korean": "Korean",
165
+ "Lithuanian": "Lithuanian",
166
+ "Polish": "Polish",
167
+ "Portuguese": "Portuguese",
168
+ "Romanian": "Romanian",
169
+ "Russian": "Russian",
170
+ "Spanish": "Spanish",
171
+ "Swahili": "Swahili",
172
+ "Swedish": "Swedish",
173
+ "Thai": "Thai",
174
+ "Turkish": "Turkish",
175
+ "Ukrainian": "Ukrainian",
176
+ "Vietnamese": "Vietnamese",
177
+ },
178
+ }
179
+ )
180
+
181
+ updates(initial_state)
182
+
183
+ initial_state.import_stylesheet("theme", "/static/custom.css?18")
finance-app-tutorial/prompts.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # BASE PROMPTS (TAILORED TO EACH COMPANY)
2
+
3
+ # base_prompts = {}
4
+
5
+ stock_prompts = """
6
+
7
+ Variables:
8
+
9
+ {language}, {stock_name}, {words}, {stock_data}
10
+
11
+ ************************
12
+
13
+ Prompt:
14
+ You will be acting as a stock market analyst. When I provide the stock data in the specified format between the <stock_data> tags, your task is to carefully review the data and provide insights, trends, and financial analysis for the stock specified in the {stock_name} variable.
15
+
16
+ <stock_data>
17
+ {stock_data}
18
+ </stock_data>
19
+
20
+ Please analyze the stock data concisely in {words} words using paragraphs, considering the following steps:
21
+
22
+ 1. Identify any notable trends in the stock's price, volume, or other key metrics over the given time period. Discuss potential reasons behind these trends.
23
+
24
+ 2. Compare the stock's performance and financial metrics to industry averages and key competitors. Discuss how the stock stacks up against its peers.
25
+
26
+ 3. Based on your analysis, provide an overall assessment of the stock's current position and future prospects. Consider factors such as growth potential, risk level, and market sentiment.
27
+
28
+ Before giving your final recommendation, please provide detailed reasoning and analysis to support your conclusions inside <reasoning> tags.
29
+
30
+ Finally, offer a clear recommendation inside <recommendation> tags on whether to buy, hold, or sell {stock_name}, taking into account both the stock's current valuation and its long-term potential.
31
+
32
+ Remember to base your analysis and recommendation solely on the provided stock data for
33
+ {stock_name}. If there is insufficient information to draw a conclusion, state this limitation in your response.
34
+
35
+ Output the analysis in {language}.
36
+
37
+ """
38
+
39
+ income_prompts = """
40
+
41
+ Variables:
42
+
43
+ {income_statement_data}, {stock_name}
44
+
45
+ ************************
46
+
47
+ Prompt:
48
+ You are a financial analyst tasked with analyzing the income statement of a
49
+ company. To assist in your analysis, you have been provided with the following data:
50
+
51
+ <stock_name>
52
+ {stock_name}
53
+ </stock_name>
54
+
55
+ <income_statement_data>
56
+ {income_statement_data}
57
+ </income_statement_data>
58
+
59
+ Using the provided data, please conduct an executive summary analysis in 100 words, using paragraphs, of the company's financial health and
60
+ future prospects. Your analysis should include:
61
+
62
+ <scratchpad>
63
+ - Analyze key financial metrics from the income statement, such as revenue growth, profit margins,
64
+ and expenses
65
+ - Assess the company's profitability and efficiency based on the income statement data
66
+ - Identify any potential risks or opportunities for the company based on the financial data
67
+ - Consider industry trends and market conditions that may impact the company's performance
68
+ </scratchpad>
69
+
70
+ After conducting your analysis, please provide your findings and conclusions in the following
71
+ format:
72
+
73
+ <analysis>
74
+ Income Statement Analysis:
75
+ - [Discuss the company's revenue growth and profitability based on the income statement data]
76
+ - [Identify any significant changes or trends in expenses or profit margins]
77
+ - [Assess the company's overall financial health and efficiency based on the income statement
78
+ metrics]
79
+
80
+ Risks and Opportunities:
81
+ - [Identify any potential risks or challenges the company may face based on the financial data]
82
+ - [Discuss any opportunities for growth or improvement based on the analysis]
83
+
84
+ Conclusion:
85
+ - [Provide a summary of your overall assessment of the company's financial performance and future
86
+ prospects]
87
+ - [Include any recommendations or insights for investors or stakeholders]
88
+ </analysis>
89
+
90
+ Please ensure that your analysis is clear, concise, and well-supported by the provided financial
91
+ data. Use specific examples and figures from the stock data and income statement to support your
92
+ conclusions.
93
+
94
+ """
95
+
96
+ earnings_prompt = """
97
+
98
+ Variables:
99
+
100
+ {earnings_transcript}
101
+
102
+ ************************
103
+
104
+ Prompt:
105
+ You are an expert at analyzing quarterly earnings reports. Please carefully review the following earnings call transcript:
106
+
107
+ <transcript>
108
+ {earnings_transcript}
109
+ </transcript>
110
+
111
+ After reading through the transcript, take some time to think through the key insights and takeaways
112
+ in a <scratchpad>. Consider the following aspects:
113
+ - Financial performance: How did the company perform financially this quarter? Were revenue,
114
+ profits, margins, etc. up or down compared to prior periods? Did they meet, exceed or fall short of
115
+ expectations?
116
+ - Future outlook: What is the company's outlook for upcoming quarters? Are they optimistic or
117
+ cautious? What are their projections for key metrics?
118
+ - Strategic initiatives: Did the company discuss any major strategic initiatives, partnerships, new
119
+ products, expansion plans or other projects? What is the rationale and potential impact?
120
+ - Significant changes: Were there any notable leadership changes, restructurings, pivots in strategy
121
+ or other significant developments disclosed?
122
+ </scratchpad>
123
+
124
+ Once you've thought through the main points, please provide a summary of the key insights from the
125
+ earnings call. The summary should concisely hit on the major takeaways around
126
+ financial performance, outlook, strategy and changes while including specific details that support
127
+ the main points. Aim for around 4-6 paragraphs.
128
+
129
+ Remember, your goal is to extract and communicate the most important information from the earnings
130
+ call in a clear, insightful executive summary. Focus on the high-level story and don't get too in
131
+ the weeds with minor details. Put yourself in the shoes of an analyst or investor and highlight what
132
+ you think they would care about most.
133
+
134
+ """
finance-app-tutorial/static/README.md ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # Serving static files
2
+
3
+ You can use this folder to store files which will be served statically in the "/static" route.
4
+
5
+ This is useful to store images and other files which will be served directly to the user of your application.
6
+
7
+ For example, if you store an image named "myimage.jpg" in this folder, it'll be accessible as "static/myimage.jpg".
8
+ You can use this relative route as the source in an Image component.
finance-app-tutorial/static/custom.css ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .link {
2
+ text-align: right !important;
3
+ }
4
+
5
+ .button-click {
6
+ background-color: #5551ff !important;
7
+ color: white !important;
8
+ }
9
+
10
+ .custom-iframe {
11
+ height: 700px !important;
12
+ }
finance-app-tutorial/static/favicon.png ADDED
finance-app-tutorial/stock_data.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ from datetime import datetime
3
+ import yfinance as yf
4
+ from charts import update_bar_graph
5
+ from dotenv import load_dotenv
6
+ import json
7
+
8
+ load_dotenv()
9
+
10
+ # Download stock data and format into a DataFrame
11
+ def download_data(state):
12
+ df = yf.download(state["symbol"], period="max", interval="1d")
13
+ df = df.reset_index()
14
+ df = df.sort_values(by="Date", ascending=False)
15
+ df = df.round({"Open": 2, "High": 2, "Low": 2, "Close": 2, "Adj Close": 2})
16
+ df["Date"] = pd.to_datetime(df["Date"])
17
+ state["main_df_subset"] = df
18
+ state["main_df"] = state["main_df_subset"]
19
+
20
+ # Download S&P 500 data and format into a DataFrame
21
+ def download_sp500(state):
22
+ df = yf.download(tickers="^GSPC", period="max", interval="1d")
23
+ df = df.reset_index()
24
+ df = df.sort_values(by="Date", ascending=False)
25
+ df = df.round({"Open": 2, "High": 2, "Low": 2, "Close": 2, "Adj Close": 2})
26
+ df["Date"] = pd.to_datetime(df["Date"])
27
+ state["another_df"] = df
28
+
29
+ # Retrieve latest stock news
30
+ def stock_news(state):
31
+ msft = yf.Ticker(state["symbol"])
32
+ articles = {}
33
+ data = msft.news
34
+ latest_articles = sorted(
35
+ data, key=lambda x: x["providerPublishTime"], reverse=True
36
+ )[:4]
37
+
38
+ for item in latest_articles:
39
+ provider_publish_time = item.get("providerPublishTime", "")
40
+ if provider_publish_time:
41
+ # Convert the timestamp to a readable date
42
+ formatted_date = datetime.fromtimestamp(provider_publish_time)
43
+ readable_date = formatted_date.strftime("%B %d, %Y at %H:%M")
44
+ else:
45
+ readable_date = "Date not available"
46
+
47
+ title = item.get("title", "No Title")
48
+ articles[title] = {
49
+ "source": item.get("publisher", ""),
50
+ "published_at": readable_date,
51
+ "url": item.get("link", ""),
52
+ }
53
+ state["articles"] = articles
54
+
55
+ # Retrieve income statement data
56
+ def income_statement(state):
57
+ quarterly_income_stmt = yf.Ticker(state["symbol"]).quarterly_income_stmt
58
+ df = pd.DataFrame(quarterly_income_stmt)
59
+ df.columns = pd.to_datetime(df.columns).strftime("%Y-%m-%d")
60
+ state["income_statement_df"] = df
61
+ update_bar_graph(state)
62
+ show_fin_metrics(state)
63
+
64
+ # Show financial metrics
65
+ def show_fin_metrics(state):
66
+ stock = yf.Ticker(state["symbol"])
67
+ operating_margins = stock.info['operatingMargins']
68
+ gross_margin = stock.info['grossMargins']
69
+ ebitda_margin = stock.info['ebitdaMargins']
70
+
71
+ state["operating_margin"] = f"{operating_margins * 100:.2f}%"
72
+ state["gross_margin"] = f"{gross_margin * 100:.2f}%"
73
+ state["ebitda_margin"] = f"{ebitda_margin * 100:.2f}%"
74
+
75
+ def _one_day_data(state):
76
+ state["last_24_hours_open"] = round(state["main_df"]["Open"].iloc[0], 2)
77
+ state["last_24_hours_high"] = round(state["main_df"]["High"].iloc[0], 2)
78
+ state["last_24_hours_low"] = round(state["main_df"]["Low"].iloc[0], 2)
79
+
80
+ # Retrieve earnings call transcript from JSON file
81
+ # You could replace this with a call to an API like Financial Modeling Prep
82
+ def earnings_calls(state):
83
+ ticker = state["symbol"]
84
+ with open('earnings-data.json', 'r') as file:
85
+ earnings_transcript = json.load(file)
86
+ if earnings_transcript:
87
+ for item in earnings_transcript:
88
+ if item['symbol'] == ticker:
89
+ state["earnings_transcript"] = item["content"]
90
+ else:
91
+ print("No earnings transcript found.")
finance-app-tutorial/ui.json ADDED
@@ -0,0 +1,749 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "metadata": {
3
+ "writer_version": "0.7.0rc2"
4
+ },
5
+ "components": {
6
+ "root": {
7
+ "id": "root",
8
+ "type": "root",
9
+ "content": {
10
+ "appName": "Finance Dashboard"
11
+ },
12
+ "isCodeManaged": false,
13
+ "position": 0,
14
+ "handlers": {},
15
+ "visible": true
16
+ },
17
+ "c0f99a9e-5004-4e75-a6c6-36f17490b134": {
18
+ "id": "c0f99a9e-5004-4e75-a6c6-36f17490b134",
19
+ "type": "page",
20
+ "content": {
21
+ "pageMode": ""
22
+ },
23
+ "isCodeManaged": false,
24
+ "position": 0,
25
+ "parentId": "root",
26
+ "handlers": {},
27
+ "visible": true
28
+ },
29
+ "bebc5fe9-63a7-46a7-b0fa-62303555cfaf": {
30
+ "id": "bebc5fe9-63a7-46a7-b0fa-62303555cfaf",
31
+ "type": "header",
32
+ "content": {
33
+ "text": "Finance Research Dashboard"
34
+ },
35
+ "isCodeManaged": false,
36
+ "position": 0,
37
+ "parentId": "c0f99a9e-5004-4e75-a6c6-36f17490b134",
38
+ "handlers": {},
39
+ "visible": true
40
+ },
41
+ "ahwykl4cny005thy": {
42
+ "id": "ahwykl4cny005thy",
43
+ "type": "dataframe",
44
+ "content": {
45
+ "dataframe": "@{main_df_subset}",
46
+ "showIndex": "no",
47
+ "enableSearch": "no"
48
+ },
49
+ "isCodeManaged": false,
50
+ "position": 0,
51
+ "parentId": "vyij4ox2ad95xyr9",
52
+ "handlers": {},
53
+ "visible": true
54
+ },
55
+ "opn16nq04zq9xha8": {
56
+ "id": "opn16nq04zq9xha8",
57
+ "type": "plotlygraph",
58
+ "content": {
59
+ "spec": "@{scatter_chart}"
60
+ },
61
+ "isCodeManaged": false,
62
+ "position": 2,
63
+ "parentId": "1db76a00z7imlyz1",
64
+ "handlers": {
65
+ "plotly-click": "charts.update_scatter_chart"
66
+ }
67
+ },
68
+ "e13teponreio9yyz": {
69
+ "id": "e13teponreio9yyz",
70
+ "type": "button",
71
+ "content": {
72
+ "text": "Max",
73
+ "buttonColor": "#BFCBFF",
74
+ "cssClasses": "",
75
+ "buttonTextColor": "#000000"
76
+ },
77
+ "isCodeManaged": false,
78
+ "position": 0,
79
+ "parentId": "g5mgb80xt38atz4f",
80
+ "handlers": {
81
+ "wf-click": "charts.handle_click"
82
+ },
83
+ "visible": true
84
+ },
85
+ "g5mgb80xt38atz4f": {
86
+ "id": "g5mgb80xt38atz4f",
87
+ "type": "horizontalstack",
88
+ "content": {},
89
+ "isCodeManaged": false,
90
+ "position": 1,
91
+ "parentId": "1db76a00z7imlyz1",
92
+ "handlers": {},
93
+ "visible": true
94
+ },
95
+ "juip2rw7hywumv35": {
96
+ "id": "juip2rw7hywumv35",
97
+ "type": "button",
98
+ "content": {
99
+ "text": "5D",
100
+ "buttonColor": "#BFCBFF",
101
+ "cssClasses": "",
102
+ "buttonTextColor": "#000000"
103
+ },
104
+ "isCodeManaged": false,
105
+ "position": 1,
106
+ "parentId": "g5mgb80xt38atz4f",
107
+ "handlers": {
108
+ "wf-click": "charts.handle_click"
109
+ },
110
+ "visible": true
111
+ },
112
+ "afamuurw1lpjkkze": {
113
+ "id": "afamuurw1lpjkkze",
114
+ "type": "button",
115
+ "content": {
116
+ "text": "1M",
117
+ "buttonColor": "#BFCBFF",
118
+ "cssClasses": "",
119
+ "buttonTextColor": "#000000"
120
+ },
121
+ "isCodeManaged": false,
122
+ "position": 2,
123
+ "parentId": "g5mgb80xt38atz4f",
124
+ "handlers": {
125
+ "wf-click": "charts.handle_click"
126
+ },
127
+ "visible": true
128
+ },
129
+ "77u7x4pf0ximsf0j": {
130
+ "id": "77u7x4pf0ximsf0j",
131
+ "type": "button",
132
+ "content": {
133
+ "text": "3M",
134
+ "buttonColor": "#BFCBFF",
135
+ "cssClasses": "",
136
+ "buttonTextColor": "#000000"
137
+ },
138
+ "isCodeManaged": false,
139
+ "position": 3,
140
+ "parentId": "g5mgb80xt38atz4f",
141
+ "handlers": {
142
+ "wf-click": "charts.handle_click"
143
+ },
144
+ "visible": true
145
+ },
146
+ "oh2g51ixvuu8gdwa": {
147
+ "id": "oh2g51ixvuu8gdwa",
148
+ "type": "metric",
149
+ "content": {
150
+ "name": "Open",
151
+ "note": "L24",
152
+ "description": "",
153
+ "metricValue": "@{last_24_hours_open}"
154
+ },
155
+ "isCodeManaged": false,
156
+ "position": 0,
157
+ "parentId": "rgv1dgdrvgiosh42",
158
+ "handlers": {},
159
+ "visible": true
160
+ },
161
+ "rgv1dgdrvgiosh42": {
162
+ "id": "rgv1dgdrvgiosh42",
163
+ "type": "horizontalstack",
164
+ "content": {},
165
+ "isCodeManaged": false,
166
+ "position": 0,
167
+ "parentId": "1db76a00z7imlyz1",
168
+ "handlers": {},
169
+ "visible": true
170
+ },
171
+ "6646510g33an9r0g": {
172
+ "id": "6646510g33an9r0g",
173
+ "type": "metric",
174
+ "content": {
175
+ "note": "L24",
176
+ "name": "High",
177
+ "description": "",
178
+ "metricValue": "@{last_24_hours_high}"
179
+ },
180
+ "isCodeManaged": false,
181
+ "position": 2,
182
+ "parentId": "rgv1dgdrvgiosh42",
183
+ "handlers": {},
184
+ "visible": true
185
+ },
186
+ "df3mr8my8ivc8yrt": {
187
+ "id": "df3mr8my8ivc8yrt",
188
+ "type": "metric",
189
+ "content": {
190
+ "note": "L24",
191
+ "name": "Low",
192
+ "description": "",
193
+ "metricValue": "@{last_24_hours_low}"
194
+ },
195
+ "isCodeManaged": false,
196
+ "position": 4,
197
+ "parentId": "rgv1dgdrvgiosh42",
198
+ "handlers": {},
199
+ "visible": true
200
+ },
201
+ "9v92jyh672s5wjkg": {
202
+ "id": "9v92jyh672s5wjkg",
203
+ "type": "tab",
204
+ "content": {
205
+ "name": "Stock data"
206
+ },
207
+ "isCodeManaged": false,
208
+ "position": 1,
209
+ "parentId": "yzujnjalyh2s2x16",
210
+ "handlers": {},
211
+ "visible": true
212
+ },
213
+ "bha2uz0y2jr7eszp": {
214
+ "id": "bha2uz0y2jr7eszp",
215
+ "type": "separator",
216
+ "content": {},
217
+ "isCodeManaged": false,
218
+ "position": 1,
219
+ "parentId": "rgv1dgdrvgiosh42",
220
+ "handlers": {},
221
+ "visible": true
222
+ },
223
+ "nzp2whzndpknrk55": {
224
+ "id": "nzp2whzndpknrk55",
225
+ "type": "separator",
226
+ "content": {},
227
+ "isCodeManaged": false,
228
+ "position": 3,
229
+ "parentId": "rgv1dgdrvgiosh42",
230
+ "handlers": {},
231
+ "visible": true
232
+ },
233
+ "suu5ceib1bqj0si5": {
234
+ "id": "suu5ceib1bqj0si5",
235
+ "type": "columns",
236
+ "content": {},
237
+ "isCodeManaged": false,
238
+ "position": 0,
239
+ "parentId": "9v92jyh672s5wjkg",
240
+ "handlers": {},
241
+ "visible": true
242
+ },
243
+ "vyij4ox2ad95xyr9": {
244
+ "id": "vyij4ox2ad95xyr9",
245
+ "type": "column",
246
+ "content": {
247
+ "width": "1"
248
+ },
249
+ "isCodeManaged": false,
250
+ "position": 0,
251
+ "parentId": "suu5ceib1bqj0si5",
252
+ "handlers": {},
253
+ "visible": true
254
+ },
255
+ "chwivjdj78wyyi9x": {
256
+ "id": "chwivjdj78wyyi9x",
257
+ "type": "button",
258
+ "content": {
259
+ "text": "Analyze trends"
260
+ },
261
+ "isCodeManaged": false,
262
+ "position": 0,
263
+ "parentId": "1yuilwwrhm40gxtx",
264
+ "handlers": {
265
+ "wf-click": "generate_stock_analysis"
266
+ },
267
+ "visible": true
268
+ },
269
+ "km4vvfnq6bwlakmp": {
270
+ "id": "km4vvfnq6bwlakmp",
271
+ "type": "text",
272
+ "content": {
273
+ "text": "@{analysis}",
274
+ "useMarkdown": "yes"
275
+ },
276
+ "isCodeManaged": false,
277
+ "position": 4,
278
+ "parentId": "i23vdas64ziuiazm",
279
+ "handlers": {},
280
+ "visible": "show_analysis_text.visible"
281
+ },
282
+ "h72djsz1oqncb5ls": {
283
+ "id": "h72djsz1oqncb5ls",
284
+ "type": "section",
285
+ "content": {
286
+ "title": "Latest news"
287
+ },
288
+ "isCodeManaged": false,
289
+ "position": 1,
290
+ "parentId": "0p1levffmcf4xlpl",
291
+ "handlers": {},
292
+ "visible": true
293
+ },
294
+ "zinu9f9cyur23m66": {
295
+ "id": "zinu9f9cyur23m66",
296
+ "type": "repeater",
297
+ "content": {
298
+ "repeaterObject": "@{articles}",
299
+ "keyVariable": "itemId",
300
+ "valueVariable": "item"
301
+ },
302
+ "isCodeManaged": false,
303
+ "position": 0,
304
+ "parentId": "h72djsz1oqncb5ls",
305
+ "handlers": {},
306
+ "visible": true
307
+ },
308
+ "dbdmqg2z74k4frq4": {
309
+ "id": "dbdmqg2z74k4frq4",
310
+ "type": "columns",
311
+ "content": {
312
+ "cssClasses": ""
313
+ },
314
+ "isCodeManaged": false,
315
+ "position": 0,
316
+ "parentId": "zinu9f9cyur23m66",
317
+ "handlers": {},
318
+ "visible": true
319
+ },
320
+ "p13mw4f6rr4z8d47": {
321
+ "id": "p13mw4f6rr4z8d47",
322
+ "type": "column",
323
+ "content": {
324
+ "title": "",
325
+ "width": "1",
326
+ "isSticky": "yes",
327
+ "isCollapsible": "",
328
+ "startCollapsed": "",
329
+ "cssClasses": ""
330
+ },
331
+ "isCodeManaged": false,
332
+ "position": 0,
333
+ "parentId": "dbdmqg2z74k4frq4",
334
+ "handlers": {},
335
+ "visible": true
336
+ },
337
+ "tji08jpu1y52kzz1": {
338
+ "id": "tji08jpu1y52kzz1",
339
+ "type": "link",
340
+ "content": {
341
+ "url": "@{item.url}",
342
+ "text": "@{item.source}",
343
+ "primaryTextColor": "#1d53d3",
344
+ "cssClasses": "link"
345
+ },
346
+ "isCodeManaged": false,
347
+ "position": 1,
348
+ "parentId": "4avlmncllyffxniu",
349
+ "handlers": {},
350
+ "visible": true
351
+ },
352
+ "4avlmncllyffxniu": {
353
+ "id": "4avlmncllyffxniu",
354
+ "type": "section",
355
+ "content": {
356
+ "title": "@{itemId}",
357
+ "primaryTextColor": "#000000",
358
+ "containerBackgroundColor": "#f7f7f7",
359
+ "containerShadow": "5px 5px 5px 2px #f8f7f7"
360
+ },
361
+ "isCodeManaged": false,
362
+ "position": 0,
363
+ "parentId": "p13mw4f6rr4z8d47",
364
+ "handlers": {},
365
+ "visible": true
366
+ },
367
+ "w3wt3976ggvcuwrg": {
368
+ "id": "w3wt3976ggvcuwrg",
369
+ "type": "text",
370
+ "content": {
371
+ "text": "**@{item.published_at}**",
372
+ "alignment": "right",
373
+ "useMarkdown": "yes"
374
+ },
375
+ "isCodeManaged": false,
376
+ "position": 0,
377
+ "parentId": "4avlmncllyffxniu",
378
+ "handlers": {},
379
+ "visible": true
380
+ },
381
+ "1db76a00z7imlyz1": {
382
+ "id": "1db76a00z7imlyz1",
383
+ "type": "section",
384
+ "content": {
385
+ "title": "@{symbol} stock performance"
386
+ },
387
+ "isCodeManaged": false,
388
+ "position": 1,
389
+ "parentId": "18wepug77yr8m1iu",
390
+ "handlers": {},
391
+ "visible": true
392
+ },
393
+ "i23vdas64ziuiazm": {
394
+ "id": "i23vdas64ziuiazm",
395
+ "type": "section",
396
+ "content": {
397
+ "title": ""
398
+ },
399
+ "isCodeManaged": false,
400
+ "position": 0,
401
+ "parentId": "0p1levffmcf4xlpl",
402
+ "handlers": {},
403
+ "visible": true
404
+ },
405
+ "8dbcbgiukyg74dkb": {
406
+ "id": "8dbcbgiukyg74dkb",
407
+ "type": "columns",
408
+ "content": {},
409
+ "isCodeManaged": false,
410
+ "position": 3,
411
+ "parentId": "c0f99a9e-5004-4e75-a6c6-36f17490b134",
412
+ "handlers": {},
413
+ "visible": true
414
+ },
415
+ "0p1levffmcf4xlpl": {
416
+ "id": "0p1levffmcf4xlpl",
417
+ "type": "column",
418
+ "content": {
419
+ "width": "0.75"
420
+ },
421
+ "isCodeManaged": false,
422
+ "position": 0,
423
+ "parentId": "8dbcbgiukyg74dkb",
424
+ "handlers": {},
425
+ "visible": true
426
+ },
427
+ "334d69p14onyi9wq": {
428
+ "id": "334d69p14onyi9wq",
429
+ "type": "column",
430
+ "content": {
431
+ "width": "1"
432
+ },
433
+ "isCodeManaged": false,
434
+ "position": 1,
435
+ "parentId": "8dbcbgiukyg74dkb",
436
+ "handlers": {},
437
+ "visible": true
438
+ },
439
+ "yzujnjalyh2s2x16": {
440
+ "id": "yzujnjalyh2s2x16",
441
+ "type": "tabs",
442
+ "content": {},
443
+ "isCodeManaged": false,
444
+ "position": 0,
445
+ "parentId": "334d69p14onyi9wq",
446
+ "handlers": {},
447
+ "visible": true
448
+ },
449
+ "18wepug77yr8m1iu": {
450
+ "id": "18wepug77yr8m1iu",
451
+ "type": "tab",
452
+ "content": {
453
+ "name": "Performance"
454
+ },
455
+ "isCodeManaged": false,
456
+ "position": 0,
457
+ "parentId": "yzujnjalyh2s2x16",
458
+ "handlers": {}
459
+ },
460
+ "9eieaedqpr1ickbb": {
461
+ "id": "9eieaedqpr1ickbb",
462
+ "type": "section",
463
+ "content": {
464
+ "title": "Investment research options",
465
+ "cssClasses": "",
466
+ "containerBackgroundColor": "#e9d3fd"
467
+ },
468
+ "isCodeManaged": false,
469
+ "position": 2,
470
+ "parentId": "c0f99a9e-5004-4e75-a6c6-36f17490b134",
471
+ "handlers": {},
472
+ "visible": true
473
+ },
474
+ "1yuilwwrhm40gxtx": {
475
+ "id": "1yuilwwrhm40gxtx",
476
+ "type": "horizontalstack",
477
+ "content": {},
478
+ "isCodeManaged": false,
479
+ "position": 0,
480
+ "parentId": "9eieaedqpr1ickbb",
481
+ "handlers": {},
482
+ "visible": true
483
+ },
484
+ "1vyfwym5dfh07ihb": {
485
+ "id": "1vyfwym5dfh07ihb",
486
+ "type": "message",
487
+ "content": {
488
+ "message": "@{message}"
489
+ },
490
+ "isCodeManaged": false,
491
+ "position": 0,
492
+ "parentId": "i23vdas64ziuiazm",
493
+ "handlers": {},
494
+ "visible": true
495
+ },
496
+ "fwmg7b84wstpxs7m": {
497
+ "id": "fwmg7b84wstpxs7m",
498
+ "type": "button",
499
+ "content": {
500
+ "text": "1Y",
501
+ "buttonColor": "#BFCBFF",
502
+ "cssClasses": "",
503
+ "buttonTextColor": "#000000"
504
+ },
505
+ "isCodeManaged": false,
506
+ "position": 4,
507
+ "parentId": "g5mgb80xt38atz4f",
508
+ "handlers": {
509
+ "wf-click": "charts.handle_click"
510
+ },
511
+ "visible": true
512
+ },
513
+ "jcs5oslp27w5v52j": {
514
+ "id": "jcs5oslp27w5v52j",
515
+ "type": "button",
516
+ "content": {
517
+ "text": "5Y",
518
+ "buttonColor": "#BFCBFF",
519
+ "cssClasses": "",
520
+ "buttonTextColor": "#000000"
521
+ },
522
+ "isCodeManaged": false,
523
+ "position": 5,
524
+ "parentId": "g5mgb80xt38atz4f",
525
+ "handlers": {
526
+ "wf-click": "charts.handle_click"
527
+ },
528
+ "visible": true
529
+ },
530
+ "s2jsn9u5xb23hase": {
531
+ "id": "s2jsn9u5xb23hase",
532
+ "type": "tags",
533
+ "content": {
534
+ "tags": "{\n \"AAPL\": \"AAPL\",\n \"IBM\": \"IBM\",\n \"NVDA\": \"NVDA\",\n \"MSFT\": \"MSFT\",\n \"TSLA\": \"TSLA\"\n}"
535
+ },
536
+ "isCodeManaged": false,
537
+ "position": 0,
538
+ "parentId": "kr54hbg2r5fkhsct",
539
+ "handlers": {
540
+ "wf-tag-click": "stock_tags"
541
+ },
542
+ "visible": true
543
+ },
544
+ "kr54hbg2r5fkhsct": {
545
+ "id": "kr54hbg2r5fkhsct",
546
+ "type": "section",
547
+ "content": {
548
+ "title": "Select a stock ticker"
549
+ },
550
+ "isCodeManaged": false,
551
+ "position": 1,
552
+ "parentId": "c0f99a9e-5004-4e75-a6c6-36f17490b134",
553
+ "handlers": {},
554
+ "visible": true
555
+ },
556
+ "8yxe1itvlcq4zhpi": {
557
+ "id": "8yxe1itvlcq4zhpi",
558
+ "type": "tab",
559
+ "content": {
560
+ "name": "Income data"
561
+ },
562
+ "isCodeManaged": false,
563
+ "position": 2,
564
+ "parentId": "yzujnjalyh2s2x16",
565
+ "handlers": {},
566
+ "visible": true
567
+ },
568
+ "tmiso5qe0e0jfl4i": {
569
+ "id": "tmiso5qe0e0jfl4i",
570
+ "type": "dataframe",
571
+ "content": {
572
+ "dataframe": "@{income_statement_df}"
573
+ },
574
+ "isCodeManaged": false,
575
+ "position": 0,
576
+ "parentId": "8yxe1itvlcq4zhpi",
577
+ "handlers": {},
578
+ "visible": true
579
+ },
580
+ "x6p3vxia2xlpbbib": {
581
+ "id": "x6p3vxia2xlpbbib",
582
+ "type": "button",
583
+ "content": {
584
+ "text": "Visualize income statement"
585
+ },
586
+ "isCodeManaged": false,
587
+ "position": 1,
588
+ "parentId": "1yuilwwrhm40gxtx",
589
+ "handlers": {
590
+ "wf-click": "generate_income_analysis"
591
+ },
592
+ "visible": true
593
+ },
594
+ "oo7y10uo68z3ayf8": {
595
+ "id": "oo7y10uo68z3ayf8",
596
+ "type": "plotlygraph",
597
+ "content": {
598
+ "spec": "@{bar_graph}"
599
+ },
600
+ "isCodeManaged": false,
601
+ "position": 2,
602
+ "parentId": "i23vdas64ziuiazm",
603
+ "handlers": {
604
+ "plotly-click": "charts.update_bar_graph"
605
+ },
606
+ "visible": "show_bar_graph.visible"
607
+ },
608
+ "a8a854zgzdrcel9j": {
609
+ "id": "a8a854zgzdrcel9j",
610
+ "type": "dropdowninput",
611
+ "content": {
612
+ "label": "Output language",
613
+ "options": "@{output_language}"
614
+ },
615
+ "isCodeManaged": false,
616
+ "position": 3,
617
+ "parentId": "i23vdas64ziuiazm",
618
+ "handlers": {
619
+ "wf-option-change": "prompt_parameters_lang"
620
+ },
621
+ "visible": "show_analysis_text.language"
622
+ },
623
+ "1c3vvmgyhvkm9d6e": {
624
+ "id": "1c3vvmgyhvkm9d6e",
625
+ "type": "horizontalstack",
626
+ "content": {},
627
+ "isCodeManaged": false,
628
+ "position": 1,
629
+ "parentId": "i23vdas64ziuiazm",
630
+ "handlers": {},
631
+ "visible": "show_income_metrics.visible"
632
+ },
633
+ "e7kux7h89zec8cmh": {
634
+ "id": "e7kux7h89zec8cmh",
635
+ "type": "metric",
636
+ "content": {
637
+ "name": "Operating Margin",
638
+ "note": "",
639
+ "metricValue": "@{operating_margin}"
640
+ },
641
+ "isCodeManaged": false,
642
+ "position": 0,
643
+ "parentId": "1c3vvmgyhvkm9d6e",
644
+ "handlers": {},
645
+ "visible": true
646
+ },
647
+ "0u3afw3gbxbj8h1x": {
648
+ "id": "0u3afw3gbxbj8h1x",
649
+ "type": "separator",
650
+ "content": {},
651
+ "isCodeManaged": false,
652
+ "position": 1,
653
+ "parentId": "1c3vvmgyhvkm9d6e",
654
+ "handlers": {},
655
+ "visible": true
656
+ },
657
+ "yxm3uezkrgpl11fc": {
658
+ "id": "yxm3uezkrgpl11fc",
659
+ "type": "metric",
660
+ "content": {
661
+ "name": "Gross Margin",
662
+ "note": "",
663
+ "metricValue": "@{gross_margin}"
664
+ },
665
+ "isCodeManaged": false,
666
+ "position": 2,
667
+ "parentId": "1c3vvmgyhvkm9d6e",
668
+ "handlers": {},
669
+ "visible": true
670
+ },
671
+ "nmdddxrvpgirasj6": {
672
+ "id": "nmdddxrvpgirasj6",
673
+ "type": "metric",
674
+ "content": {
675
+ "name": "EBIDTA Margin",
676
+ "note": "",
677
+ "metricValue": "@{ebitda_margin}"
678
+ },
679
+ "isCodeManaged": false,
680
+ "position": 4,
681
+ "parentId": "1c3vvmgyhvkm9d6e",
682
+ "handlers": {},
683
+ "visible": true
684
+ },
685
+ "xlzf1vlkmxokw9yj": {
686
+ "id": "xlzf1vlkmxokw9yj",
687
+ "type": "separator",
688
+ "content": {},
689
+ "isCodeManaged": false,
690
+ "position": 3,
691
+ "parentId": "1c3vvmgyhvkm9d6e",
692
+ "handlers": {},
693
+ "visible": true
694
+ },
695
+ "c52e2wkln9shvcoq": {
696
+ "id": "c52e2wkln9shvcoq",
697
+ "type": "section",
698
+ "content": {
699
+ "title": "",
700
+ "containerBackgroundColor": "#FFE999"
701
+ },
702
+ "isCodeManaged": false,
703
+ "position": 0,
704
+ "parentId": "18wepug77yr8m1iu",
705
+ "handlers": {},
706
+ "visible": "demo_mode.visible"
707
+ },
708
+ "3knng5h0zv8i3xra": {
709
+ "id": "3knng5h0zv8i3xra",
710
+ "type": "text",
711
+ "content": {
712
+ "text": "@{tab_message}",
713
+ "useMarkdown": "yes"
714
+ },
715
+ "isCodeManaged": false,
716
+ "position": 0,
717
+ "parentId": "c52e2wkln9shvcoq",
718
+ "handlers": {},
719
+ "visible": ""
720
+ },
721
+ "rhomvmilcrm8f2r1": {
722
+ "id": "rhomvmilcrm8f2r1",
723
+ "type": "button",
724
+ "content": {
725
+ "text": "Summarize earnings"
726
+ },
727
+ "isCodeManaged": false,
728
+ "position": 2,
729
+ "parentId": "1yuilwwrhm40gxtx",
730
+ "handlers": {
731
+ "wf-click": "summarize_earnings"
732
+ },
733
+ "visible": true
734
+ },
735
+ "1o0ui3hahmjblams": {
736
+ "id": "1o0ui3hahmjblams",
737
+ "type": "text",
738
+ "parentId": "c0f99a9e-5004-4e75-a6c6-36f17490b134",
739
+ "content": {
740
+ "text": "Made with \u2764\ufe0f using [Writer Framework](https://github.com/writer/writer-framework/)",
741
+ "useMarkdown": "yes",
742
+ "alignment": "center"
743
+ },
744
+ "handlers": {},
745
+ "position": 4,
746
+ "visible": true
747
+ }
748
+ }
749
+ }
pyproject.toml ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [tool.poetry]
2
+ name = "writer-framework-default"
3
+ version = "0.1.0"
4
+ description = ""
5
+ authors = ["Your Name <[email protected]>"]
6
+ readme = "README.md"
7
+
8
+ [tool.poetry.dependencies]
9
+ python = "^3.10.0"
10
+ writer = {version = "^0.6.0"}
11
+
12
+
13
+ [build-system]
14
+ requires = ["poetry-core"]
15
+ build-backend = "poetry.core.masonry.api"