Benjamin Consolvo commited on
Commit
f2789f8
Β·
1 Parent(s): 206927e

first commit hf

Browse files
Files changed (4) hide show
  1. .gitignore +6 -0
  2. README.md +15 -8
  3. app.py +494 -0
  4. requirements.txt +13 -0
.gitignore ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ .streamlit/
2
+ .venv/
3
+ .pyton-version
4
+ auto_trade_log.json
5
+ uv.lock
6
+ pyproject.toml
README.md CHANGED
@@ -1,14 +1,21 @@
1
  ---
2
- title: Stocktrader
3
- emoji: 🐒
4
- colorFrom: red
5
- colorTo: pink
6
- sdk: gradio
7
- sdk_version: 5.29.1
8
  app_file: app.py
9
  pinned: false
10
  license: apache-2.0
11
- short_description: Sample stock trading application
12
  ---
13
 
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Stock Trader
3
+ emoji: πŸ“š
4
+ colorFrom: yellow
5
+ colorTo: purple
6
+ sdk: streamlit
7
+ sdk_version: 1.42.2
8
  app_file: app.py
9
  pinned: false
10
  license: apache-2.0
11
+ short_description: 'Sample stock trading application'
12
  ---
13
 
14
+ ## Installation Steps
15
+
16
+ 1. uv init
17
+ 2. uv add -r requirements.txt
18
+ 3. source .venv/bin/activate
19
+ 4. streamlit run deeepseek_stocktrader.py
20
+ 5. need to add streamlit secrets: .streamlit/secrets.toml
21
+ 6. add .streamlit/ to .gitignore
app.py ADDED
@@ -0,0 +1,494 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ st.set_page_config(layout="wide")
3
+
4
+ import yfinance as yf
5
+ # import alpaca as tradeapi
6
+ import alpaca_trade_api as alpaca
7
+ from newsapi import NewsApiClient
8
+ from vaderSentiment.vaderSentiment import SentimentIntensityAnalyzer
9
+
10
+ from datetime import datetime, timedelta
11
+ import streamlit as st
12
+ import pandas as pd
13
+ import matplotlib.pyplot as plt
14
+ import logging
15
+ import threading
16
+ import time
17
+ import json
18
+ import os
19
+ import plotly.graph_objs as go
20
+ from sklearn.preprocessing import minmax_scale
21
+ from plotly.subplots import make_subplots
22
+
23
+ # Configure logging
24
+ logging.basicConfig(level=logging.INFO)
25
+ logger = logging.getLogger(__name__)
26
+
27
+ AUTO_TRADE_LOG_PATH = "auto_trade_log.json" # Path to store auto trade log
28
+
29
+ # The trading history events are saved in the file "auto_trade_log.json"
30
+ # This file is created and updated in the current working directory where you run your Streamlit app.
31
+
32
+ AUTO_TRADE_INTERVAL = 10800 # Interval in seconds (e.g., 10800 seconds = 3 hours)
33
+
34
+ class AlpacaTrader:
35
+ def __init__(self, API_KEY, API_SECRET, BASE_URL):
36
+ self.alpaca = alpaca.REST(API_KEY, API_SECRET, BASE_URL)
37
+ self.cash = 0
38
+ self.holdings = {}
39
+ self.trades = []
40
+
41
+ def get_market_status(self):
42
+ return self.alpaca.get_clock().is_open
43
+
44
+ def buy(self, symbol, qty):
45
+ try:
46
+ # Ensure at least $1000 in cash before buying
47
+ account = self.alpaca.get_account()
48
+ cash_balance = float(account.cash)
49
+ if cash_balance < 1000:
50
+ logger.warning(f"Low cash: (${cash_balance}) to buy {symbol}. Minimum $1000 required.")
51
+ return None
52
+ order = self.alpaca.submit_order(symbol=symbol, qty=qty, side='buy', type='market', time_in_force='day')
53
+ logger.info(f"Bought {qty} shares of {symbol}")
54
+ return order
55
+ except Exception as e:
56
+ logger.error(f"Error buying {symbol}: {e}")
57
+ return None
58
+
59
+ def sell(self, symbol, qty):
60
+ try:
61
+ order = self.alpaca.submit_order(symbol=symbol, qty=qty, side='sell', type='market', time_in_force='day')
62
+ logger.info(f"Sold {qty} shares of {symbol}")
63
+ return order
64
+ except Exception as e:
65
+ logger.error(f"Error selling {symbol}: {e}")
66
+ return None
67
+
68
+ def getHoldings(self):
69
+ positions = self.alpaca.list_positions()
70
+ for position in positions:
71
+ self.holdings[position.symbol] = position.market_value
72
+ return self.holdings
73
+
74
+ def getCash(self):
75
+ return self.alpaca.get_account().cash
76
+
77
+ def update_portfolio(self, symbol, price, qty, action):
78
+ if action == 'buy':
79
+ self.cash -= price * qty
80
+ if symbol in self.holdings:
81
+ self.holdings[symbol] += price * qty
82
+ else:
83
+ self.holdings[symbol] = price * qty
84
+ elif action == 'sell':
85
+ self.cash += price * qty
86
+ self.holdings[symbol] -= price * qty
87
+ if self.holdings[symbol] <= 0:
88
+ del self.holdings[symbol]
89
+ self.trades.append({'symbol': symbol, 'price': price, 'qty': qty, 'action': action, 'time': datetime.now()})
90
+
91
+ class NewsSentiment:
92
+ def __init__(self, API_KEY):
93
+ '''
94
+ Hutto, C.J. & Gilbert, E.E. (2014). VADER: A Parsimonious Rule-based Model for Sentiment Analysis of Social Media Text. Eighth International Conference on Weblogs and Social Media (ICWSM-14). Ann Arbor, MI, June 2014.
95
+ '''
96
+ self.newsapi = NewsApiClient(api_key=API_KEY)
97
+ self.sia = SentimentIntensityAnalyzer()
98
+
99
+
100
+ def get_news_sentiment(self, symbols):
101
+ '''
102
+ ERROR:__main__:Error getting news for APLD: {'status': 'error', 'code': 'rateLimited', 'message': 'You have made too many requests recently. Developer accounts are limited to 100 requests over a 24 hour period (50 requests available every 12 hours). Please upgrade to a paid plan if you need more requests.'}
103
+ '''
104
+ sentiment = {}
105
+ for symbol in symbols:
106
+ try:
107
+ articles = self.newsapi.get_everything(q=symbol,
108
+ language='en',
109
+ sort_by='publishedAt', # <-- fixed argument name
110
+ page=1)
111
+ compound_score = 0
112
+ for article in articles['articles'][:5]: # Check first 5 articles
113
+ # print(f'article= {article}')
114
+ score = self.sia.polarity_scores(article['title'])['compound']
115
+ compound_score += score
116
+ avg_score = compound_score / 5 if articles['articles'] else 0
117
+ if avg_score > 0.1:
118
+ sentiment[symbol] = 'Positive'
119
+ elif avg_score < -0.1:
120
+ sentiment[symbol] = 'Negative'
121
+ else:
122
+ sentiment[symbol] = 'Neutral'
123
+ except Exception as e:
124
+ logger.error(f"Error getting news for {symbol}: {e}")
125
+ sentiment[symbol] = 'Neutral'
126
+ return sentiment
127
+
128
+
129
+
130
+
131
+ class StockAnalyzer:
132
+ def __init__(self, alpaca):
133
+ self.alpaca = alpaca
134
+ self.symbols = self.get_top_volume_stocks()
135
+ # Build a symbol->name mapping for use in plots/tables
136
+ self.symbol_to_name = self.get_symbol_to_name()
137
+
138
+ def get_symbol_to_name(self):
139
+ # Get mapping from symbol to company name using Alpaca asset info
140
+ assets = self.alpaca.alpaca.list_assets(status='active')
141
+ return {asset.symbol: asset.name for asset in assets}
142
+
143
+ def get_bars(self, alp_api, symbols, timeframe='1D'):
144
+ bars_data = {}
145
+ try:
146
+ bars = alp_api.get_bars(list(symbols), timeframe).df
147
+ for symbol in symbols:
148
+ symbol_bars = bars[bars['symbol'] == symbol]
149
+ if not symbol_bars.empty:
150
+ bar_info = symbol_bars.iloc[-1]
151
+ # Handle index type for timestamp
152
+ if isinstance(bar_info.name, tuple):
153
+ timestamp = bar_info.name[1].isoformat()
154
+ else:
155
+ timestamp = bar_info.name.isoformat()
156
+ bars_data[symbol] = {
157
+ 'bar_data': {
158
+ 'volume': bar_info['volume'],
159
+ 'open': bar_info['open'],
160
+ 'high': bar_info['high'],
161
+ 'low': bar_info['low'],
162
+ 'close': bar_info['close'],
163
+ 'timestamp': timestamp
164
+ }
165
+ }
166
+ else:
167
+ logger.warning(f"No bar data for symbol: {symbol}")
168
+ bars_data[symbol] = {'bar_data': None}
169
+ except Exception as e:
170
+ logger.warning(f"Error fetching bars in batch: {e}")
171
+ for symbol in symbols:
172
+ bars_data[symbol] = {'bar_data': None}
173
+ return bars_data
174
+
175
+ def assetswithconditions(self,stock_assets):
176
+ cond = {
177
+ 'class': ['us_equity'],
178
+ 'exchange': ['NASDAQ', 'NYSE'],
179
+ 'status': ['active'],
180
+ 'tradable': [True],
181
+ 'marginable': [True],
182
+ 'shortable': [True],
183
+ 'easy_to_borrow': [True],
184
+ 'fractionable': [True]
185
+ }
186
+ assets_with_conditions = []
187
+ asset_symbol_dict = {}
188
+
189
+ for asset in stock_assets:
190
+ # Skip symbols with '.' or '/' (preferred shares, warrants, etc.)
191
+ if '.' in asset.symbol or '/' in asset.symbol:
192
+ continue
193
+
194
+ if (asset.__getattr__('class') in cond['class'] and
195
+ asset.exchange in cond['exchange'] and
196
+ asset.status in cond['status'] and
197
+ asset.tradable in cond['tradable'] and
198
+ asset.marginable in cond['marginable'] and
199
+ asset.shortable in cond['shortable'] and
200
+ asset.easy_to_borrow in cond['easy_to_borrow'] and
201
+ asset.fractionable in cond['fractionable']
202
+ ):
203
+ assets_with_conditions.append(asset)
204
+
205
+ asset_no_comma = asset.name.replace(',', '')
206
+ asset_first_word = asset_no_comma.split()[0]
207
+
208
+ asset_symbol_dict[asset.symbol] = asset._raw
209
+ asset_symbol_dict[asset.symbol]['firstWord'] = asset_first_word
210
+
211
+ sorted_dict = dict(sorted(asset_symbol_dict.items()))
212
+ # print(f'Length of Alpaca assets with conditions = {len(assets_with_conditions)}')
213
+ # print(f'assets_with_conditions = {assets_with_conditions}')
214
+ return assets_with_conditions, sorted_dict
215
+
216
+
217
+ def get_top_volume_stocks(self,num_stocks=10):
218
+ try:
219
+ # Get all tradable assets
220
+ assets = self.alpaca.alpaca.list_assets(status='active')
221
+ # tradable_assets = {asset.symbol: {} for asset in assets if asset.tradable}
222
+ # print(f'tradable_assets = {tradable_assets}')
223
+
224
+ assets_with_conditions, sorted_dict = self.assetswithconditions(assets)
225
+ # print(f'sorted_dict = {sorted_dict}')
226
+ # Fetch bar data for all tradable assets
227
+ # print(f'sorted_dict.keys()={sorted_dict.keys()}')
228
+ tradable_assets = self.get_bars(self.alpaca.alpaca, sorted_dict.keys(), timeframe='1D')
229
+
230
+ # Extract volume and calculate the top 10 stocks by volume
231
+ volume_data = {
232
+ symbol: info['bar_data']['volume']
233
+ for symbol, info in tradable_assets.items()
234
+ if info['bar_data'] is not None
235
+ }
236
+ top_volume_stocks = sorted(volume_data, key=volume_data.get, reverse=True)[:num_stocks]
237
+ print(f'top_volume_stocks = {top_volume_stocks}')
238
+
239
+ return top_volume_stocks
240
+ except Exception as e:
241
+ logger.error(f"Error fetching top volume stocks: {e}")
242
+ return []
243
+
244
+ def get_historical_data(self, symbols):
245
+ data = {}
246
+ for symbol in symbols:
247
+ try:
248
+ # Pull historical data from 2000-01-01 to today, daily interval
249
+ ticker = yf.Ticker(symbol)
250
+ hist = ticker.history(start='2023-01-01', end=datetime.now().strftime('%Y-%m-%d'), interval='1d')
251
+ data[symbol] = hist
252
+ except Exception as e:
253
+ logger.error(f"Error getting data for {symbol}: {e}")
254
+ return data
255
+
256
+ class TradingApp:
257
+ def __init__(self):
258
+ self.alpaca = AlpacaTrader(st.secrets['ALPACA_API_KEY'], st.secrets['ALPACA_SECRET_KEY'], 'https://paper-api.alpaca.markets')
259
+ self.sentiment = NewsSentiment(st.secrets['NEWS_API_KEY'])
260
+ self.analyzer = StockAnalyzer(self.alpaca)
261
+ self.data = self.analyzer.get_historical_data(self.analyzer.symbols)
262
+ self.auto_trade_log = [] # Store automatic trade actions
263
+
264
+ def display_charts(self):
265
+ # Create 12 individual dynamic price plots in a 4x3 grid using Plotly (3 columns, 4 rows)
266
+ symbols = list(self.data.keys())
267
+ symbol_to_name = self.analyzer.symbol_to_name
268
+ n = len(symbols)
269
+ cols = 3
270
+ rows = 4
271
+ subplot_titles = [
272
+ f"{symbol} - {symbol_to_name.get(symbol, '')}" for symbol in symbols
273
+ ]
274
+ fig = make_subplots(rows=rows, cols=cols, subplot_titles=subplot_titles)
275
+ for idx, symbol in enumerate(symbols):
276
+ df = self.data[symbol]
277
+ if not df.empty:
278
+ row = idx // cols + 1
279
+ col = idx % cols + 1
280
+ fig.add_trace(
281
+ go.Scatter(
282
+ x=df.index,
283
+ y=df['Close'],
284
+ mode='lines',
285
+ name=symbol,
286
+ hovertemplate=f"%{{x}}<br>{symbol}: %{{y:.2f}}<extra></extra>"
287
+ ),
288
+ row=row,
289
+ col=col
290
+ )
291
+ fig.update_layout(
292
+ title="Top Volume Stocks - Price Charts (Since 2023)",
293
+ height=2000,
294
+ showlegend=False,
295
+ dragmode=False, # Disable global dragmode
296
+ )
297
+ # Enable scroll-zoom for each subplot (individual zoom)
298
+ fig.update_layout(
299
+ xaxis=dict(fixedrange=False),
300
+ yaxis=dict(fixedrange=False),
301
+ )
302
+ for i in range(1, rows * cols + 1):
303
+ fig.layout[f'xaxis{i}'].update(fixedrange=False)
304
+ fig.layout[f'yaxis{i}'].update(fixedrange=False)
305
+ st.plotly_chart(fig, use_container_width=True, config={"scrollZoom": True})
306
+
307
+ def manual_trade(self):
308
+ # Move all user inputs to the sidebar
309
+ with st.sidebar:
310
+ st.header("Manual Trade")
311
+ symbol = st.text_input('Enter stock symbol')
312
+ qty = int(st.number_input('Enter quantity'))
313
+ action = st.selectbox('Action', ['Buy', 'Sell'])
314
+ if st.button('Execute'):
315
+ if action == 'Buy':
316
+ order = self.alpaca.buy(symbol, qty)
317
+ else:
318
+ order = self.alpaca.sell(symbol, qty)
319
+ if order:
320
+ st.success(f"Order executed: {action} {qty} shares of {symbol}")
321
+ else:
322
+ st.error("Order failed")
323
+ st.header("Portfolio")
324
+ st.write("Cash Balance:")
325
+ st.write(self.alpaca.getCash())
326
+ st.write("Holdings:")
327
+ st.write(self.alpaca.getHoldings())
328
+ st.write("Recent Trades:")
329
+ st.write(pd.DataFrame(self.alpaca.trades))
330
+
331
+ def auto_trade_based_on_sentiment(self, sentiment):
332
+ # Add company name to each action
333
+ actions = []
334
+ symbol_to_name = self.analyzer.symbol_to_name
335
+ for symbol, sentiment_value in sentiment.items():
336
+ action = None
337
+ if sentiment_value == 'Positive':
338
+ order = self.alpaca.buy(symbol, 1)
339
+ action = 'Buy'
340
+ elif sentiment_value == 'Negative':
341
+ order = self.alpaca.sell(symbol, 1)
342
+ action = 'Sell'
343
+ else:
344
+ order = None
345
+ action = 'Hold'
346
+ actions.append({
347
+ 'symbol': symbol,
348
+ 'company_name': symbol_to_name.get(symbol, ''),
349
+ 'sentiment': sentiment_value,
350
+ 'action': action
351
+ })
352
+ self.auto_trade_log = actions
353
+ return actions
354
+
355
+ def background_auto_trade(app):
356
+ # This function runs in a background thread and does not require a TTY.
357
+ # The warning "tcgetpgrp failed: Not a tty" is harmless and can be ignored.
358
+ # It is likely caused by the environment in which the script is running (e.g., Streamlit, Docker, or a notebook).
359
+ # No code changes are needed for this warning.
360
+ while True:
361
+ sentiment = app.sentiment.get_news_sentiment(app.analyzer.symbols)
362
+ actions = []
363
+ for symbol, sentiment_value in sentiment.items():
364
+ action = None
365
+ if sentiment_value == 'Positive':
366
+ order = app.alpaca.buy(symbol, 1)
367
+ action = 'Buy'
368
+ elif sentiment_value == 'Negative':
369
+ order = app.alpaca.sell(symbol, 1)
370
+ action = 'Sell'
371
+ else:
372
+ order = None
373
+ action = 'Hold'
374
+ actions.append({
375
+ 'symbol': symbol,
376
+ 'sentiment': sentiment_value,
377
+ 'action': action
378
+ })
379
+ # Append to log file instead of overwriting
380
+ log_entry = {
381
+ "timestamp": datetime.now().isoformat(),
382
+ "actions": actions,
383
+ "sentiment": sentiment
384
+ }
385
+ try:
386
+ if os.path.exists(AUTO_TRADE_LOG_PATH):
387
+ with open(AUTO_TRADE_LOG_PATH, "r") as f:
388
+ log_data = json.load(f)
389
+ else:
390
+ log_data = []
391
+ except Exception:
392
+ log_data = []
393
+ log_data.append(log_entry)
394
+ with open(AUTO_TRADE_LOG_PATH, "w") as f:
395
+ json.dump(log_data, f)
396
+ time.sleep(AUTO_TRADE_INTERVAL)
397
+
398
+ def load_auto_trade_log():
399
+ try:
400
+ with open(AUTO_TRADE_LOG_PATH, "r") as f:
401
+ return json.load(f)
402
+ except Exception:
403
+ return None
404
+
405
+ def main():
406
+ st.title("Stock Trading Application")
407
+
408
+ if not st.secrets['ALPACA_API_KEY'] or not st.secrets['NEWS_API_KEY']:
409
+ st.error("Please configure your API keys in secrets.toml")
410
+ return
411
+
412
+ app = TradingApp()
413
+
414
+ # Start background thread only once (on first run)
415
+ if "auto_trade_thread_started" not in st.session_state:
416
+ thread = threading.Thread(target=background_auto_trade, args=(app,), daemon=True)
417
+ thread.start()
418
+ st.session_state["auto_trade_thread_started"] = True
419
+
420
+ if app.alpaca.get_market_status():
421
+ st.write("Market is open")
422
+ else:
423
+ st.write("Market is closed")
424
+
425
+ # User inputs and portfolio are now in the sidebar
426
+ app.manual_trade()
427
+
428
+ # Main area: plots and data
429
+ app.display_charts()
430
+
431
+ # Read and display latest auto-trade actions
432
+ st.write("Automatic Trading Actions Based on Sentiment (background):")
433
+ auto_trade_log = load_auto_trade_log()
434
+ if auto_trade_log:
435
+ # Show the most recent entry
436
+ last_entry = auto_trade_log[-1]
437
+ st.write(f"Last checked: {last_entry['timestamp']}")
438
+ df = pd.DataFrame(last_entry["actions"])
439
+ # Reorder columns for clarity
440
+ if "company_name" in df.columns:
441
+ df = df[["symbol", "company_name", "sentiment", "action"]]
442
+ st.dataframe(df)
443
+ st.write("Sentiment Analysis (latest):")
444
+ st.write(last_entry["sentiment"])
445
+
446
+ # Plot buy/sell actions over time (aggregate for all symbols)
447
+ st.write("Auto-Trading History (Buy/Sell Actions Over Time):")
448
+ history = []
449
+ for entry in auto_trade_log:
450
+ ts = entry["timestamp"]
451
+ for act in entry["actions"]:
452
+ if act["action"] in ("Buy", "Sell"):
453
+ history.append({
454
+ "timestamp": ts,
455
+ "symbol": act["symbol"],
456
+ "action": act["action"]
457
+ })
458
+ if history:
459
+ hist_df = pd.DataFrame(history)
460
+ if not hist_df.empty:
461
+ hist_df["timestamp"] = pd.to_datetime(hist_df["timestamp"])
462
+ # Pivot to get Buy/Sell counts per symbol over time
463
+ # Avoid FutureWarning by explicitly converting to float after replace
464
+ hist_df["action_value"] = hist_df["action"].replace({"Buy": 1, "Sell": -1})
465
+ hist_df["action_value"] = hist_df["action_value"].astype(float)
466
+ pivot = hist_df.pivot_table(index="timestamp", columns="symbol", values="action_value", aggfunc="sum")
467
+ st.line_chart(pivot.fillna(0))
468
+ else:
469
+ st.info("Waiting for first background auto-trade run...")
470
+
471
+ # Explanation:
472
+ # In Alpaca:
473
+ # - 'cash' is the actual cash available in your account (uninvested funds).
474
+ # - 'buying_power' is the total amount you can use to buy securities, which may be higher than cash if you have margin enabled.
475
+ # For a cash account, buying_power == cash.
476
+ # For a margin account, buying_power can be up to 2x (or 4x for day trading) your cash, depending on regulations and your account status.
477
+
478
+ # Example usage:
479
+ # account = alpaca.get_account()
480
+ # cash_balance = account.cash
481
+ # buying_power = account.buying_power
482
+
483
+ # Note:
484
+ # To disable margin on your Alpaca paper account, you must set your account type to "cash" instead of "margin".
485
+ # This cannot be changed via the API or code. You must:
486
+ # 1. Log in to your Alpaca dashboard at https://app.alpaca.markets/
487
+ # 2. Go to "Paper Trading" > "Settings"
488
+ # 3. Set the account type to "Cash" (not "Margin")
489
+ # 4. If you do not see this option, you may need to reset your paper account or contact Alpaca support.
490
+
491
+ # There is no programmatic/API way to change the margin setting for a paper account.
492
+
493
+ if __name__ == "__main__":
494
+ main()
requirements.txt ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ alpaca-py
2
+ yfinance
3
+ streamlit
4
+ alpaca-trade-api
5
+ alpha_vantage==2.3.1
6
+ lxml
7
+ newsapi-python
8
+ vaderSentiment
9
+ streamlit
10
+ pandas
11
+ matplotlib
12
+ plotly
13
+ sklearn