1
0
mirror of https://gitlab.com/simple-stock-bots/simple-stock-bot.git synced 2026-06-03 21:00:26 +00:00

Compare commits

..

9 Commits

Author SHA1 Message Date
Anson 187714f8d0 added call to uptimerobot API 2023-04-09 11:04:07 -06:00
Anson 8fd3e4a6cd Merge branch '91-error-when-invalid-coingecko-coin-entered' into 'master'
Resolve "error when invalid coingecko coin entered"

Closes #91

See merge request simple-stock-bots/simple-telegram-stock-bot!42
2023-04-09 00:57:41 +00:00
Anson b3af8b3270 Resolve "error when invalid coingecko coin entered" 2023-04-09 00:57:40 +00:00
Anson 86349deba4 Merge branch '93-add-change-percent-to-market-data' into 'master'
Resolve "Add change percent to Market Data"

Closes #93

See merge request simple-stock-bots/simple-telegram-stock-bot!41
2023-04-09 00:41:57 +00:00
Anson 22ed74d194 Resolve "Add change percent to Market Data" 2023-04-09 00:41:57 +00:00
Anson e63dc7f46b Update .gitlab-ci.yml file 2023-04-07 20:51:14 +00:00
Anson 928a23c8c6 fighting CI 2023-04-07 14:50:29 -06:00
Anson 580fcbb84d Merge branch '88-switch-to-marketdata-app-for-stock-market-data' into 'master'
Resolve "Switch to marketdata.app for stock market data"

Closes #88

See merge request simple-stock-bots/simple-telegram-stock-bot!40
2023-04-07 20:46:23 +00:00
Anson 322dffa2f2 Resolve "Switch to marketdata.app for stock market data" 2023-04-07 20:46:23 +00:00
4 changed files with 60 additions and 27 deletions
+2 -2
View File
@@ -16,7 +16,7 @@ build:master:
--dockerfile "${CI_PROJECT_DIR}/Dockerfile" --dockerfile "${CI_PROJECT_DIR}/Dockerfile"
--destination "${CI_REGISTRY_IMAGE}:latest" --destination "${CI_REGISTRY_IMAGE}:latest"
rules: rules:
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"' - if: '$CI_COMMIT_BRANCH == "master"'
build:branch: build:branch:
@@ -31,4 +31,4 @@ build:branch:
--destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}" --destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}"
--destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_BRANCH}" --destination "${CI_REGISTRY_IMAGE}:${CI_COMMIT_BRANCH}"
rules: rules:
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME != "master"' - if: '$CI_COMMIT_BRANCH != "master"'
+33 -2
View File
@@ -85,7 +85,27 @@ class MarketData:
self.charts = {} self.charts = {}
def status(self) -> str: def status(self) -> str:
return "status isnt implemented by marketdata.app" # TODO: At the moment this API is poorly documented, this function likely needs to be revisited later.
try:
status = r.get(
"https://stats.uptimerobot.com/api/getMonitorList/6Kv3zIow0A",
timeout=5,
)
status.raise_for_status()
except r.HTTPError:
return f"API returned an HTTP error code {status.status_code} in {status.elapsed.total_seconds()} Seconds."
except r.Timeout:
return "API timed out before it was able to give status. This is likely due to a surge in usage or a complete outage."
statusJSON = status.json()
if statusJSON["status"] == "ok":
return (
f"CoinGecko API responded that it was OK with a {status.status_code} in {status.elapsed.total_seconds()} Seconds."
)
else:
return f"MarketData.app is currently reporting the following status: {statusJSON['status']}"
def price_reply(self, symbol: Stock) -> str: def price_reply(self, symbol: Stock) -> str:
"""Returns price movement of Stock for the last market day, or after hours. """Returns price movement of Stock for the last market day, or after hours.
@@ -101,8 +121,19 @@ class MarketData:
""" """
if quoteResp := self.get(f"stocks/quotes/{symbol}/"): if quoteResp := self.get(f"stocks/quotes/{symbol}/"):
return f"The current price of {quoteResp['symbol']} is ${quoteResp['last']}" price = round(quoteResp["last"][0], 2)
changePercent = round(quoteResp["changepct"][0], 2)
message = f"The current price of {symbol.name} is ${price} and "
if changePercent > 0.0:
message += f"is currently up {changePercent}% for the day."
elif changePercent < 0.0:
message += f"is currently down {changePercent}% for the day."
else:
message += "hasn't shown any movement for the day."
return message
else: else:
return f"Getting a quote for {symbol} encountered an error." return f"Getting a quote for {symbol} encountered an error."
+2 -2
View File
@@ -32,8 +32,8 @@ class Stock(Symbol):
def __init__(self, symbol: str) -> None: def __init__(self, symbol: str) -> None:
self.symbol = symbol self.symbol = symbol
self.id = symbol self.id = symbol
self.name = "$" + symbol self.name = "$" + symbol.upper()
self.tag = "$" + symbol.upper() self.tag = "$" + symbol.lower()
class Coin(Symbol): class Coin(Symbol):
+23 -21
View File
@@ -5,7 +5,7 @@ import datetime
import logging import logging
import random import random
import re import re
from logging import critical, debug, error, info, warning import logging as log
import pandas as pd import pandas as pd
import schedule import schedule
@@ -15,11 +15,13 @@ from cg_Crypto import cg_Crypto
from MarketData import MarketData from MarketData import MarketData
from Symbol import Coin, Stock, Symbol from Symbol import Coin, Stock, Symbol
from typing import Dict
class Router: class Router:
STOCK_REGEX = "(?:^|[^\\$])\\$([a-zA-Z.]{1,6})" STOCK_REGEX = "(?:^|[^\\$])\\$([a-zA-Z.]{1,6})"
CRYPTO_REGEX = "[$]{2}([a-zA-Z]{1,20})" CRYPTO_REGEX = "[$]{2}([a-zA-Z]{1,20})"
trending_count = {} trending_count: Dict[str, float] = {}
def __init__(self): def __init__(self):
self.stock = MarketData() self.stock = MarketData()
@@ -43,9 +45,9 @@ class Router:
t_copy.pop(dead) t_copy.pop(dead)
self.trending_count = t_copy.copy() self.trending_count = t_copy.copy()
info("Decayed trending symbols.") log.info("Decayed trending symbols.")
def find_symbols(self, text: str, *, trending_weight: int = 1) -> list[Symbol]: def find_symbols(self, text: str, *, trending_weight: int = 1) -> list[Stock | Symbol]:
"""Finds stock tickers starting with a dollar sign, and cryptocurrencies with two dollar signs """Finds stock tickers starting with a dollar sign, and cryptocurrencies with two dollar signs
in a blob of text and returns them in a list. in a blob of text and returns them in a list.
@@ -61,7 +63,7 @@ class Router:
""" """
schedule.run_pending() schedule.run_pending()
symbols = [] symbols: list[Symbol] = []
stocks = set(re.findall(self.STOCK_REGEX, text)) stocks = set(re.findall(self.STOCK_REGEX, text))
for stock in stocks: for stock in stocks:
# Market data lacks tools to check if a symbol is valid. # Market data lacks tools to check if a symbol is valid.
@@ -70,16 +72,16 @@ class Router:
coins = set(re.findall(self.CRYPTO_REGEX, text)) coins = set(re.findall(self.CRYPTO_REGEX, text))
for coin in coins: for coin in coins:
sym = self.crypto.symbol_list[self.crypto.symbol_list["symbol"].str.fullmatch(coin.lower(), case=False)] sym = self.crypto.symbol_list[self.crypto.symbol_list["symbol"].str.fullmatch(coin.lower(), case=False)]
if ~sym.empty: if sym.empty:
symbols.append(Coin(sym)) log.info(f"{coin} is not in list of coins")
else: else:
info(f"{coin} is not in list of coins") symbols.append(Coin(sym))
if symbols: if symbols:
info(symbols) log.info(symbols)
for symbol in symbols: for symbol in symbols:
self.trending_count[symbol.tag] = self.trending_count.get(symbol.tag, 0) + trending_weight self.trending_count[symbol.tag] = self.trending_count.get(symbol.tag, 0) + trending_weight
return symbols return symbols
def status(self, bot_resp) -> str: def status(self, bot_resp) -> str:
"""Checks for any issues with APIs. """Checks for any issues with APIs.
@@ -101,7 +103,7 @@ class Router:
{self.crypto.status()} {self.crypto.status()}
""" """
warning(stats) log.warning(stats)
return stats return stats
@@ -150,13 +152,13 @@ class Router:
replies = [] replies = []
for symbol in symbols: for symbol in symbols:
info(symbol) log.info(symbol)
if isinstance(symbol, Stock): if isinstance(symbol, Stock):
replies.append(self.stock.price_reply(symbol)) replies.append(self.stock.price_reply(symbol))
elif isinstance(symbol, Coin): elif isinstance(symbol, Coin):
replies.append(self.crypto.price_reply(symbol)) replies.append(self.crypto.price_reply(symbol))
else: else:
info(f"{symbol} is not a Stock or Coin") log.info(f"{symbol} is not a Stock or Coin")
return replies return replies
@@ -182,7 +184,7 @@ class Router:
elif isinstance(symbol, Coin): elif isinstance(symbol, Coin):
replies.append(self.crypto.info_reply(symbol)) replies.append(self.crypto.info_reply(symbol))
else: else:
debug(f"{symbol} is not a Stock or Coin") log.debug(f"{symbol} is not a Stock or Coin")
return replies return replies
@@ -206,7 +208,7 @@ class Router:
elif isinstance(symbol, Coin): elif isinstance(symbol, Coin):
return self.crypto.intra_reply(symbol) return self.crypto.intra_reply(symbol)
else: else:
debug(f"{symbol} is not a Stock or Coin") log.debug(f"{symbol} is not a Stock or Coin")
return pd.DataFrame() return pd.DataFrame()
def chart_reply(self, symbol: Symbol) -> pd.DataFrame: def chart_reply(self, symbol: Symbol) -> pd.DataFrame:
@@ -229,7 +231,7 @@ class Router:
elif isinstance(symbol, Coin): elif isinstance(symbol, Coin):
return self.crypto.chart_reply(symbol) return self.crypto.chart_reply(symbol)
else: else:
debug(f"{symbol} is not a Stock or Coin") log.debug(f"{symbol} is not a Stock or Coin")
return pd.DataFrame() return pd.DataFrame()
def stat_reply(self, symbols: list[Symbol]) -> list[str]: def stat_reply(self, symbols: list[Symbol]) -> list[str]:
@@ -254,7 +256,7 @@ class Router:
elif isinstance(symbol, Coin): elif isinstance(symbol, Coin):
replies.append(self.crypto.stat_reply(symbol)) replies.append(self.crypto.stat_reply(symbol))
else: else:
debug(f"{symbol} is not a Stock or Coin") log.debug(f"{symbol} is not a Stock or Coin")
return replies return replies
@@ -280,7 +282,7 @@ class Router:
elif isinstance(symbol, Coin): elif isinstance(symbol, Coin):
replies.append(self.crypto.cap_reply(symbol)) replies.append(self.crypto.cap_reply(symbol))
else: else:
debug(f"{symbol} is not a Stock or Coin") log.debug(f"{symbol} is not a Stock or Coin")
return replies return replies
@@ -306,7 +308,7 @@ class Router:
elif isinstance(symbol, Coin): elif isinstance(symbol, Coin):
replies.append(self.crypto.spark_reply(symbol)) replies.append(self.crypto.spark_reply(symbol))
else: else:
debug(f"{symbol} is not a Stock or Coin") log.debug(f"{symbol} is not a Stock or Coin")
return replies return replies
@@ -352,7 +354,7 @@ class Router:
if reply: if reply:
return reply return reply
else: else:
warning("Failed to collect trending data.") log.warning("Failed to collect trending data.")
return "Trending data is not currently available." return "Trending data is not currently available."
def random_pick(self) -> str: def random_pick(self) -> str:
@@ -385,7 +387,7 @@ class Router:
elif isinstance(symbol, Coin): elif isinstance(symbol, Coin):
coins.append(symbol) coins.append(symbol)
else: else:
debug(f"{symbol} is not a Stock or Coin") log.debug(f"{symbol} is not a Stock or Coin")
if stocks: if stocks:
# IEX batch endpoint doesnt seem to be working right now # IEX batch endpoint doesnt seem to be working right now