Пример #1
0
class TestStockData(unittest.TestCase):
    def setUp(self):
        self.stocks = StockData()

    def test_retrieve_stock_is_list(self):
        assert type(self.stocks.stock_list) is list

    def test_stock_list_has_data(self):
        assert len(self.stocks.stock_list) > 5400

    @unittest.skip("need to learn to mock things")
    def test_retrieve_single_stock_is_dataframe(self):
        history = self.stocks.get_history("AAPL")
        assert type(history) is pd.DataFrame

    @unittest.skip("need to learn to mock things")
    def test_single_stock_has_data(self):
        history = self.stocks.get_history("AAPL")
        assert len(history) > 245

    def test_set_histories_sets_variables(self):
        self.stocks.set_histories(from_file=True)
        prices, volumes = self.stocks.prices, self.stocks.volumes
        assert type(prices) is pd.DataFrame
        assert type(volumes) is pd.DataFrame
        assert type(prices['AAPL']) is pd.Series
        assert type(volumes['GOOG']) is pd.Series

    def test_filter_by_volume(self):
        self.stocks.set_histories(from_file=True)
        self.stocks.filter_by_volume()
        filtered = self.stocks.filter_by_volume(min=1000000)
        assert len(self.stocks.prices.columns) > len(filtered.columns)
Пример #2
0
class TestStockData(unittest.TestCase):

	def setUp(self):
		self.stocks = StockData()		

	def test_retrieve_stock_is_list(self):
		assert type(self.stocks.stock_list) is list

	def test_stock_list_has_data(self):
		assert len(self.stocks.stock_list) > 5400

	@unittest.skip("need to learn to mock things")
	def test_retrieve_single_stock_is_dataframe(self):
		history = self.stocks.get_history("AAPL")
		assert type(history) is pd.DataFrame

	@unittest.skip("need to learn to mock things")
	def test_single_stock_has_data(self):
		history = self.stocks.get_history("AAPL")
		assert len(history) > 245

	def test_set_histories_sets_variables(self):
		self.stocks.set_histories(from_file=True)
		prices, volumes = self.stocks.prices, self.stocks.volumes
		assert type(prices) is pd.DataFrame
		assert type(volumes) is pd.DataFrame
		assert type(prices['AAPL']) is pd.Series
		assert type(volumes['GOOG']) is pd.Series

	def test_filter_by_volume(self):
		self.stocks.set_histories(from_file=True)
		self.stocks.filter_by_volume()
		filtered = self.stocks.filter_by_volume(min=1000000)
		assert len(self.stocks.prices.columns) > len(filtered.columns)
Пример #3
0
    def load_data(self):
        """
		loads stock data .csv from inputted filepath string on the GUI
		as StockData object, also autocompletes all inputs
		using information provided by the csv.

		Error handling
			invalid filepath :
				empty filepath or file could not be found.
			invalid .csv :
				.csv file is empty, missing date column, etc.
		"""
        filepath = Path(self.filePathEdit.text())

        try:
            self.stock_data = StockData(filepath)
            start_date, end_date = self.stock_data.get_period()
            period = f"{start_date} to {end_date}"

            # auto-complete feauture
            self.startDateEdit.setText(start_date)
            self.endDateEdit.setText(end_date)
            self.periodEdit.setText(period)
            self.SMA1Edit.setText("15")
            self.SMA2Edit.setText("50")
            self.SMA1Checkbox.setChecked(False)
            self.SMA2Checkbox.setChecked(False)

            self.report(
                f"Data loaded from {filepath}; period auto-selected: {start_date} to {end_date}."
            )
            print(self.stock_data.data)

        except IOError as e:
            self.report(
                f"Filepath provided is invalid or fail to open .csv file. {e}")

        except TypeError as e:
            self.report(
                f"The return tuple is probably (nan, nan) because .csv is empty"
            )
Пример #4
0
def sync_data(stock_id):
    stock_data = read(str(stock_id))
    df_statement = _sync_statement(
        stock_id, None if stock_data is None else stock_data.df_statement)
    df_performance = _sync_performance(
        stock_id, df_statement,
        None if stock_data is None else stock_data.df_performance)
    df_profit_matrix = _sync_profit_matrix(
        stock_id, None if stock_data is None else stock_data.df_profit_matrix)
    return StockData(
        stock_id, df_performance, df_statement, df_profit_matrix
    ) if df_statement is not None and df_performance is not None and df_profit_matrix is not None else None
Пример #5
0
def stocks_api(stock_symbol):
    # The api fetching stock data
    url = 'https://financialmodelingprep.com/api/financials/'

    api_income_statement = requests.get(url + 'income-statement/' +
                                        stock_symbol)
    api_balance_statement = requests.get(url + 'balance-sheet-statement/' +
                                         stock_symbol)
    api_cash_statement = requests.get(url + 'cash-flow-statement/' +
                                      stock_symbol)

    income_statement = response_processing(api_income_statement)
    balance_statement = response_processing(api_balance_statement)
    cash_statement = response_processing(api_cash_statement)

    result = StockData(balance_statement, income_statement, cash_statement,
                       stock_symbol)

    return result
Пример #6
0
    def __find_stocks(self, html: str, institution: str = "") -> bool:
        if html is None:
            return False

        soup = BeautifulSoup(html, 'html.parser')
        tables = soup.find_all('div', id=B3StockParser.__STOCKS_TABLE_ID)

        if tables is None:
            return False

        for stocks_table in tables:
            try:
                rows = stocks_table.table.tbody.find_all('tr')
                for row in rows:
                    self._stocks_table.append(StockData(row, institution))
            except AttributeError:
                return False

        return True
Пример #7
0
	def test_lower_price_targets_is_series(self):
		sd = StockData()
		sd.set_histories(from_file=True)
		p = Predict(sd.prices)
		lower_targets = p.find_lower_targets(-30,.75,.85)
		assert type(lower_targets) is pd.Series
Пример #8
0
	def test_raise_price_targets_is_series(self):
		sd = StockData()
		sd.set_histories(from_file=True)
		p = Predict(sd.prices)
		raise_targets = p.find_raise_targets(-10,1.15,1.25)
		assert type(raise_targets) is pd.Series
Пример #9
0
 def test_lower_price_targets_is_series(self):
     sd = StockData()
     sd.set_histories(from_file=True)
     p = Predict(sd.prices)
     lower_targets = p.find_lower_targets(-30, .75, .85)
     assert type(lower_targets) is pd.Series
Пример #10
0
 def test_raise_price_targets_is_series(self):
     sd = StockData()
     sd.set_histories(from_file=True)
     p = Predict(sd.prices)
     raise_targets = p.find_raise_targets(-10, 1.15, 1.25)
     assert type(raise_targets) is pd.Series
Пример #11
0
def get_evaluate_performance(stock_id, since_year, to_year=None):
    params = normalize_params(stock_id, since_year, to_year)
    print(params)
    if params is None:
        return None

    stock_id = params.get('stock_id')
    evaluate_since_year = params.get('since_year')
    evaluate_to_year = params.get('to_year')
    get_recent_four_seasons = params.get('get_resent_four_seasons')

    dividend_policy_processor = DividendPolicyProcessor(stock_id)
    df_dividend_policy = dividend_policy_processor.get_data_frames(
        {'year': evaluate_to_year}, {'year': evaluate_to_year + 1})

    try:
        should_modify_since_year = False if df_dividend_policy is not None and str(
            evaluate_since_year) in df_dividend_policy.index else True
    except KeyError:
        should_modify_since_year = True
        print('get error')
        evaluate_since_year = evaluate_since_year - 1 if should_modify_since_year and evaluate_since_year >= evaluate_to_year - 1 else evaluate_since_year

    df_dividend_policy = dividend_policy_processor.get_data_frames(
        {'year': evaluate_since_year}, {'year': evaluate_to_year + 1})
    df_dividend_policy.sort_index(inplace=True)

    df_income_statement = SimpleIncomeStatementProcessor().get_data_frames(
        stock_id, {'year': evaluate_since_year - 1},
        {'year': evaluate_to_year})
    df_income_statement.sort_index(inplace=True)

    balance_sheet_processor = SimpleBalanceSheetProcessor(stock_id)
    df_balance_sheet = balance_sheet_processor.get_data_frames(
        {'year': evaluate_since_year - 1}, {'year': evaluate_to_year})

    price_measurement_processor = PriceMeasurementProcessor(stock_id)
    df_prices = price_measurement_processor.get_data_frame()

    results_data = []
    results_index = []
    for year in range(evaluate_since_year, evaluate_to_year + 1):
        count_of_effects = len(
            df_income_statement.loc["{}Q{}".format(year - 1, 1):"{}Q{}".
                                    format(year, 4)].index)
        if count_of_effects == 8:
            sum_of_eps_two_years = \
                df_income_statement.loc["{}Q{}".format(year - 1, 1):"{}Q{}".format(year, 4)].sum().loc["EPS"]
            value_in_two_years = df_balance_sheet.loc[
                "{}Q{}".format(year - 1, 1),
                "每股淨值"] + df_balance_sheet.loc["{}Q{}".format(year, 4), "每股淨值"]
        else:
            sum_of_eps_two_years = df_income_statement.iloc[
                -8:].loc[:, "EPS"].sum()
            value_in_two_years = df_balance_sheet.iloc[
                [-8, -1], ].loc[:, "每股淨值"].sum()

        has_dividend = str(year) in df_dividend_policy.index
        predict = not count_of_effects == 8 or not has_dividend
        if has_dividend:
            dr_in_two_years = df_dividend_policy.loc[str(year - 1):str(year),
                                                     "現金股利"].sum()
            print('dr_in_two_years in ', year, ' = ', dr_in_two_years)
            dividend_ratio_two_years = dr_in_two_years / sum_of_eps_two_years
        else:
            if len(results_data) > 0:
                dividend_ratio_two_years = results_data[-1].get('兩年現金股利發放率')
            else:
                dividend_ratio_two_years = 0
            print('year ', year, '兩年現金股利發放率 = ', dividend_ratio_two_years,
                  ' EPS = ', df_income_statement.iloc[-4:].loc[:, "EPS"].sum())
            dr_in_two_years = dividend_ratio_two_years * df_income_statement.iloc[
                -4:].loc[:, "EPS"].sum()

        # print('兩年現金股利發放率 in ', year, ':', dividend_ratio_two_years)
        avg_roe_two_years = sum_of_eps_two_years / value_in_two_years
        growth_ratio_of_eps = avg_roe_two_years * (1 -
                                                   dividend_ratio_two_years)
        try:
            dividend_yield = dr_in_two_years / 2 / float(
                df_prices.loc[str(year), '平均股價'])
        except Exception as e:
            print('get', e, ' when get price for ', stock_id, ' in ', year)
            dividend_yield = float(0)
        results_index.append(str(year))
        results_data.append({
            '兩年現金股利發放率':
            dividend_ratio_two_years,
            '兩年平均ROE':
            avg_roe_two_years,
            '保留盈餘成長率':
            growth_ratio_of_eps,
            '現金股利殖利率':
            dividend_yield,
            '高登模型預期報酬率':
            growth_ratio_of_eps + dividend_yield,
            '預估值':
            predict,
            '現金股利':
            df_dividend_policy.loc[str(year),
                                   "現金股利"] if has_dividend else dr_in_two_years
        })

    merged_statement = pd.concat([df_income_statement, df_balance_sheet],
                                 axis=1,
                                 sort=False)
    # print(df_results)
    return StockData(stock_id, pd.DataFrame(results_data, index=results_index),
                     merged_statement,
                     get_matrix_level(stock_id, since_year, to_year))
Пример #12
0
 def setUp(self):
     self.stocks = StockData()
Пример #13
0
	def setUp(self):
		self.stocks = StockData()		
Пример #14
0
from datetime import datetime, timedelta
from connect import DBConnection
from stock_data import StockData, Predict

if __name__ == '__main__':
	est = (datetime.utcnow() + timedelta(hours=-4)).timetuple()
	conn = DBConnection()
	count_untweeted = conn.get_num_untweeted_stocks()
	if est[6] == 6 or count_untweeted == 0:
		sd = StockData()
		sd.set_histories(from_file=False,num=5000)	
		p = Predict(sd.prices)
		raise_targets = p.find_raise_targets(-10,1.15,1.25)
		lower_targets = p.find_lower_targets(-30,.75,.85)
		p.save_targets(raise_targets, True)
		p.save_targets(lower_targets, False)
Пример #15
0
 def __init__(self, ticker_index, trend_period_days=21):
     self.ticker_index = ticker_index
     self.stock_data = StockData(ticker_index)
     self.close = self.stock_data.close
     self.trend_period = trend_period_days
     self.ema_trend = self.compute_ema()
Пример #16
0
 def deal_trade_data(self):
     stock_data = StockData()
     # 获取历史数据
     stock_data.history_data()
     # 获取实时数据
     stock_data.real_time_data_all_stock()
Пример #17
0
 def save_data_as_h5_file_(self, industry_name, hdf5_file_name):
     industry_idxs = self.stock_idxs[industry_name]
     for idx in industry_idxs:
         stock_data_obj = StockData(idx)
         stock_data = stock_data_obj.get_stocks_data()
         self.save_stock_data_to_hdf5(stock_data, hdf5_file_name, idx)
Пример #18
0
class Main(qtw.QWidget, Ui_Form):
    """
	handles user interaction, loads data and updates GUI

	Attributes
	.figure : Figure
		matplotlib Figure object to contain the Axes object(s) and the FigureCanvas object
	.ax : Axes
		matplotlib Axes object is the 'plot' itself, the region of the image which contains the data
	.canvas : FigureCanvas
		matplotlib FigureCanvas is the area onto which the figure is drawn
	.toolbar : NavigationToolbar
		matplotlib NavigationToolbar is the UI that users can use to interact with the drawn plot
	.canvasLayout : QVBoxLayout
		PyQt5's object used to demark the location and contain the FigureCanvas and NavigationToolbar
	.loadCSVButton : QPushButton
		PyQt5's object that user can push to activate the load_data() function
	.updateWindowButton : QPushButton
		PyQt5's object that user can push to activate the update_canvas() function
	.SMA1Checkbox : QCheckBox
		PyQt5's object that user can tick to specify whether to include SMA1 plot in the canvas
	.SMA2Checkbox : QCheckBox
		PyQt5's object that user can tick to specify whether to include SMA2 plot in the canvas
	.scrollWidget : QWidget
		PyQt5's base object for that receives user inputs, as such it is interactable on the screen
	.scrollLayout : QVBoxLayout
		PyQt5's object used to demark the location and contain the QScrollArea
	.scrollArea : QScrollArea
		PyQt5's object's scrollable area on which status reports of the GUI actions are written
	.filePathEdit : QLineEdit
		PyQt5's object used to create a box into which user can input filepath (e.g. ../data/GOOG.csv)
	.startDateEdit : QLineEdit
		PyQt5's object used to create a box into which user can input start date (YYYY-MM-DD)
	.endDateEdit : QLineEdit
		PyQt5's object used to create a box into which user can input end date (YYYY-MM-DD)
	.SMA1Edit : QLineEdit
		PyQt5's object used to create a box into which user can input SMA1 window value (e.g. 15)
	.SMA2Edit : QLineEdit
		PyQt5's object used to create a box into which user can input SMA2 window value (e.g. 50)
	.periodEdit : QLineEdit
		PyQt5's object used to create a box which user can use to see the period used for the graph
	"""
    def __init__(self):
        """
		initializes and sets up GUI widgets and its connections
		"""
        super().__init__()
        self.setupUi(self)
        self.setWindowTitle("Stock Chart & Moving Average Application")

        # sets up figure to plot on, instantiates canvas and toolbar
        self.figure, self.ax = plt.subplots()
        self.canvas = FigureCanvas(self.figure)
        self.toolbar = NavigationToolbar(self.canvas, self)

        # attaches the toolbar and canvas to the canvas layout
        self.canvasLayout.addWidget(self.toolbar)
        self.canvasLayout.addWidget(self.canvas)

        # sets up a scroll area to display GUI statuses
        self.scrollWidget = qtw.QWidget()
        self.scrollLayout = qtw.QVBoxLayout()
        self.scrollWidget.setLayout(self.scrollLayout)
        self.scrollArea.setWidget(self.scrollWidget)

        # button & checkbox connections
        self.loadCSVButton.clicked.connect(self.load_data)
        self.updateWindowButton.clicked.connect(self.update_canvas)
        self.SMA1Checkbox.stateChanged.connect(self.update_canvas)
        self.SMA2Checkbox.stateChanged.connect(self.update_canvas)

        # auto-complete feauture
        self.filePathEdit.setText("../data/GOOG.csv")

    def load_data(self):
        """
		loads stock data .csv from inputted filepath string on the GUI
		as StockData object, also autocompletes all inputs
		using information provided by the csv.

		Error handling
			invalid filepath :
				empty filepath or file could not be found.
			invalid .csv :
				.csv file is empty, missing date column, etc.
		"""
        filepath = Path(self.filePathEdit.text())

        try:
            self.stock_data = StockData(filepath)
            start_date, end_date = self.stock_data.get_period()
            period = f"{start_date} to {end_date}"

            # auto-complete feauture
            self.startDateEdit.setText(start_date)
            self.endDateEdit.setText(end_date)
            self.periodEdit.setText(period)
            self.SMA1Edit.setText("15")
            self.SMA2Edit.setText("50")
            self.SMA1Checkbox.setChecked(False)
            self.SMA2Checkbox.setChecked(False)

            self.report(
                f"Data loaded from {filepath}; period auto-selected: {start_date} to {end_date}."
            )
            print(self.stock_data.data)

        except IOError as e:
            self.report(
                f"Filepath provided is invalid or fail to open .csv file. {e}")

        except TypeError as e:
            self.report(
                f"The return tuple is probably (nan, nan) because .csv is empty"
            )

    def update_canvas(self):
        """
		creates a datetime object from the inputted date string
		of format YYYY-MM-DD. uses it to slice a copy of loaded
		stock_data to be used to update graphics. checks
		checkboxes first to see if SMA1, SMA2, Buy and Sell plots
		need to be drawn. finally, updates graphic accordingly.

		Error handling
		invalid date format:
			date format inside the .csv file is not YYYY-MM-DD
		non-existent stock_data :
			the selected range results in an empty dataframe
			or end date < start date
		non-existent data point :
			data of that date does not exist,
			or maybe because it is Out-Of-Bound
		raised exceptions :
			SMA1 and SMA2 values are the same,
			or other exceptions raised
		"""
        self.date_format = '%Y-%m-%d'

        try:
            start_date = str(
                datetime.strptime(self.startDateEdit.text(),
                                  self.date_format).date())
            end_date = str(
                datetime.strptime(self.endDateEdit.text(),
                                  self.date_format).date())
            period = f"{start_date} to {end_date}"
            self.periodEdit.setText(period)

            # builds a list of graphs to plot by checking the tickboxes
            column_headers = ['Close']
            formats = ['k-']

            if self.SMA1Checkbox.isChecked():
                self.stock_data._calculate_SMA(int(self.SMA1Edit.text()))
                column_headers.append(f"SMA{self.SMA1Edit.text()}")
                formats.append('b-')
            if self.SMA2Checkbox.isChecked():
                self.stock_data._calculate_SMA(int(self.SMA2Edit.text()))
                column_headers.append(f"SMA{self.SMA2Edit.text()}")
                formats.append('m-')
            if len(column_headers) == 3:
                self.stock_data._calculate_crossover(column_headers[1],
                                                     column_headers[2],
                                                     column_headers[1])
                column_headers.append('Sell')
                formats.append('rv')
                column_headers.append('Buy')
                formats.append('g^')

            self.selected_stock_data = self.stock_data.get_data(
                start_date, end_date)
            self.plot_graph(column_headers, formats)

            self.report(
                f"Plotting {column_headers} data from period: {start_date} to {end_date}."
            )
            print(self.selected_stock_data)

        except ValueError as e:
            self.report(
                f"Time period has not been specified or does not match YYYY-MM-DD format, {e}."
            )

        except AssertionError as e:
            self.report(f"Selected range is empty, {e}")

        except KeyError as e:
            self.report(f"Data for this date does not exist: {e}")

        except Exception as e:
            self.report(f"Exception encountered: {e}")

    def plot_graph(self, column_headers, formats):
        """
		plots graphs specified under column_headers using the formats

		Parameters
		column_headers : [str, str, ...]
			a list containing column header names with data to be plotted
		formats : [str, str, ...]
			a list of matplotlib built-in style strings to indicate
			whether to plot line or scatterplot and the colours
			corresponding to each value in col_headers
			(hence, must be same length)

		Error handling
		empty dataframe :
			selected dataframe is empty
		"""
        self.ax.clear()
        assert not self.selected_stock_data.empty

        # matplotlib has its own internal representation of datetime
        # date2num converts datetime.datetime to this internal representation
        x_data = list(
            mdates.date2num([
                datetime.strptime(dates, self.date_format).date()
                for dates in self.selected_stock_data.index.values
            ]))

        for i in range(len(column_headers)):
            if column_headers[i] in self.selected_stock_data.columns:
                y_data = list(self.selected_stock_data[column_headers[i]])
                self.ax.plot(x_data,
                             y_data,
                             formats[i],
                             label=column_headers[i])
                self.report(f"{column_headers[i]} data is being plotted.")
            else:
                self.report(f"{column_headers[i]} data does not exist.")

        # formatting
        months_locator = mdates.MonthLocator()
        months_format = mdates.DateFormatter('%b %Y')
        self.ax.xaxis.set_major_locator(months_locator)
        self.ax.xaxis.set_major_formatter(months_format)
        self.ax.format_xdata = mdates.DateFormatter(self.date_format)
        self.ax.format_ydata = lambda y: '$%1.2f' % y
        self.ax.grid(True)
        self.figure.autofmt_xdate()
        self.figure.legend()
        self.figure.tight_layout()
        self.canvas.draw()

    def report(self, string):
        """
		given a report (string), update the scroll area with this report

		Parameters
		string : str
			string of the report, usually the error message itself.
		"""
        report_text = qtw.QLabel(string)
        self.scrollLayout.addWidget(report_text)
        print(string)

    def center(self):
        """
		centers the fixed main window size according to user screen size
		"""
        screen = qtw.QDesktopWidget().screenGeometry()
        main_window = self.geometry()
        x = (screen.width() - main_window.width()) / 2

        # pulls the window up slightly (arbitrary)
        y = (screen.height() - main_window.height()) / 2 - 50
        self.setFixedSize(main_window.width(), main_window.height())
        self.move(x, y)