├── data ├── yahoo │ └── price.csv └── sp500.txt ├── README.md ├── DownloadPrices.py └── trend_low_close.py /data/yahoo/price.csv: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LowTrend 2 | Download stock price from yahoo with yfinance. 3 | 4 | Scan stock data and find low trends. Sort results according different requirements such as how close to low trend. Results will be convert as image into output folder. 5 | -------------------------------------------------------------------------------- /DownloadPrices.py: -------------------------------------------------------------------------------- 1 | from pandas_datareader import data as pdr 2 | from datetime import date, timedelta 3 | import yfinance as yf 4 | import pandas as pd 5 | import numpy as np 6 | 7 | 8 | today = date.today() + timedelta(1) 9 | start_date= "2010-01-01" 10 | 11 | 12 | end_date= today.strftime("%Y-%m-%d") 13 | 14 | 15 | def getData(ticker): 16 | data = yf.download(ticker, start=start_date, end=end_date) 17 | dataname= ticker.split(".") 18 | data.to_csv("./data/yahoo/"+dataname[0]+".IS.csv") 19 | 20 | 21 | 22 | bist_list = np.genfromtxt('./data/sp500.txt', delimiter=";", unpack=True, dtype=None, encoding=None) 23 | len_of_bist_list: int = len(bist_list[0]) 24 | 25 | for i in range(len_of_bist_list): 26 | symbol = bist_list[0][i]+".IS" 27 | getData(symbol) 28 | 29 | 30 | input("finished ...") 31 | -------------------------------------------------------------------------------- /data/sp500.txt: -------------------------------------------------------------------------------- 1 | A 2 | AAL 3 | AAP 4 | AAPL 5 | ABBV 6 | ABC 7 | ABMD 8 | ABT 9 | ACN 10 | ADBE 11 | ADI 12 | ADM 13 | ADP 14 | ADSK 15 | AEE 16 | AEP 17 | AES 18 | AFL 19 | AIG 20 | AIZ 21 | AJG 22 | AKAM 23 | ALB 24 | ALGN 25 | ALK 26 | ALL 27 | ALLE 28 | AMAT 29 | AMCR 30 | AMD 31 | AME 32 | AMGN 33 | AMP 34 | AMT 35 | AMZN 36 | ANET 37 | ANSS 38 | AON 39 | AOS 40 | APA 41 | APD 42 | APH 43 | APTV 44 | ARE 45 | ATO 46 | ATVI 47 | AVB 48 | AVGO 49 | AVY 50 | AWK 51 | AXP 52 | AZO 53 | BA 54 | BAC 55 | BAX 56 | BBWI 57 | BBY 58 | BDX 59 | BEN 60 | BF.B 61 | BIIB 62 | BIO 63 | BK 64 | BKNG 65 | BKR 66 | BLK 67 | BMY 68 | BR 69 | BRK.B 70 | BRO 71 | BSX 72 | BWA 73 | BXP 74 | C 75 | CAG 76 | CAH 77 | CARR 78 | CAT 79 | CB 80 | CBOE 81 | CBRE 82 | CCI 83 | CCL 84 | CDAY 85 | CDNS 86 | CDW 87 | CE 88 | CEG 89 | CF 90 | CFG 91 | CHD 92 | CHRW 93 | CHTR 94 | CI 95 | CINF 96 | CL 97 | CLX 98 | CMA 99 | CMCSA 100 | CME 101 | CMG 102 | CMI 103 | CMS 104 | CNC 105 | CNP 106 | COF 107 | COO 108 | COP 109 | COST 110 | CPB 111 | CPRT 112 | CPT 113 | CRL 114 | CRM 115 | CSCO 116 | CSX 117 | CTAS 118 | CTLT 119 | CTRA 120 | CTSH 121 | CTVA 122 | CTXS 123 | CVS 124 | CVX 125 | CZR 126 | D 127 | DAL 128 | DD 129 | DE 130 | DFS 131 | DG 132 | DGX 133 | DHI 134 | DHR 135 | DIS 136 | DISH 137 | DLR 138 | DLTR 139 | DOV 140 | DOW 141 | DPZ 142 | DRE 143 | DRI 144 | DTE 145 | DUK 146 | DVA 147 | DVN 148 | DXC 149 | DXCM 150 | EA 151 | EBAY 152 | ECL 153 | ED 154 | EFX 155 | EIX 156 | EL 157 | EMBC 158 | EMN 159 | EMR 160 | ENPH 161 | EOG 162 | EPAM 163 | EQIX 164 | EQR 165 | ES 166 | ESS 167 | ETN 168 | ETR 169 | ETSY 170 | EVRG 171 | EW 172 | EXC 173 | EXPD 174 | EXPE 175 | EXR 176 | F 177 | FANG 178 | FAST 179 | FB 180 | FBHS 181 | FCX 182 | FDS 183 | FDX 184 | FE 185 | FFIV 186 | FIS 187 | FISV 188 | FITB 189 | FLT 190 | FMC 191 | FOX 192 | FOXA 193 | FRC 194 | FRT 195 | FTNT 196 | FTV 197 | GD 198 | GE 199 | GILD 200 | GIS 201 | GL 202 | GLW 203 | GM 204 | GNRC 205 | GOOG 206 | GOOGL 207 | GPC 208 | GPN 209 | GRMN 210 | GS 211 | GWW 212 | HAL 213 | HAS 214 | HBAN 215 | HCA 216 | HD 217 | HES 218 | HIG 219 | HII 220 | HLT 221 | HOLX 222 | HON 223 | HPE 224 | HPQ 225 | HRL 226 | HSIC 227 | HST 228 | HSY 229 | HUM 230 | HWM 231 | IBM 232 | ICE 233 | IDXX 234 | IEX 235 | IFF 236 | ILMN 237 | INCY 238 | INTC 239 | INTU 240 | IP 241 | IPG 242 | IQV 243 | IR 244 | IRM 245 | ISRG 246 | IT 247 | ITW 248 | IVZ 249 | J 250 | JBHT 251 | JCI 252 | JKHY 253 | JNJ 254 | JNPR 255 | JPM 256 | K 257 | KDP 258 | KEY 259 | KEYS 260 | KHC 261 | KIM 262 | KLAC 263 | KMB 264 | KMI 265 | KMX 266 | KO 267 | KR 268 | L 269 | LDOS 270 | LEN 271 | LH 272 | LHX 273 | LIN 274 | LKQ 275 | LLY 276 | LMT 277 | LNC 278 | LNT 279 | LOW 280 | LRCX 281 | LUMN 282 | LUV 283 | LVS 284 | LW 285 | LYB 286 | LYV 287 | MA 288 | MAA 289 | MAR 290 | MAS 291 | MCD 292 | MCHP 293 | MCK 294 | MCO 295 | MDLZ 296 | MDT 297 | MET 298 | MGM 299 | MHK 300 | MKC 301 | MKTX 302 | MLM 303 | MMC 304 | MMM 305 | MNST 306 | MO 307 | MOH 308 | MOS 309 | MPC 310 | MPWR 311 | MRK 312 | MRNA 313 | MRO 314 | MS 315 | MSCI 316 | MSFT 317 | MSI 318 | MTB 319 | MTCH 320 | MTD 321 | MU 322 | NCLH 323 | NDAQ 324 | NDSN 325 | NEE 326 | NEM 327 | NFLX 328 | NI 329 | NKE 330 | NLOK 331 | NLSN 332 | NOC 333 | NOW 334 | NRG 335 | NSC 336 | NTAP 337 | NTRS 338 | NUE 339 | NVDA 340 | NVR 341 | NWL 342 | NWS 343 | NWSA 344 | NXPI 345 | O 346 | ODFL 347 | OGN 348 | OKE 349 | OMC 350 | ON 351 | ORCL 352 | ORLY 353 | OTIS 354 | OXY 355 | PARA 356 | PAYC 357 | PAYX 358 | PCAR 359 | PEAK 360 | PEG 361 | PENN 362 | PEP 363 | PFE 364 | PFG 365 | PG 366 | PGR 367 | PH 368 | PHM 369 | PKG 370 | PKI 371 | PLD 372 | PM 373 | PNC 374 | PNR 375 | PNW 376 | POOL 377 | PPG 378 | PPL 379 | PRU 380 | PSA 381 | PSX 382 | PTC 383 | PVH 384 | PWR 385 | PXD 386 | PYPL 387 | QCOM 388 | QRVO 389 | RCL 390 | RE 391 | REG 392 | REGN 393 | RF 394 | RHI 395 | RJF 396 | RL 397 | RMD 398 | ROK 399 | ROL 400 | ROP 401 | ROST 402 | RSG 403 | RTX 404 | SBAC 405 | SBNY 406 | SBUX 407 | SCHW 408 | SEDG 409 | SEE 410 | SHW 411 | SIVB 412 | SJM 413 | SLB 414 | SNA 415 | SNPS 416 | SO 417 | SPG 418 | SPGI 419 | SRE 420 | STE 421 | STT 422 | STX 423 | STZ 424 | SWK 425 | SWKS 426 | SYF 427 | SYK 428 | SYY 429 | T 430 | TAP 431 | TDG 432 | TDY 433 | TECH 434 | TEL 435 | TER 436 | TFC 437 | TFX 438 | TGT 439 | TJX 440 | TMO 441 | TMUS 442 | TPR 443 | TRMB 444 | TROW 445 | TRV 446 | TSCO 447 | TSLA 448 | TSN 449 | TT 450 | TTWO 451 | TWTR 452 | TXN 453 | TXT 454 | TYL 455 | UAL 456 | UDR 457 | UHS 458 | ULTA 459 | UNH 460 | UNP 461 | UPS 462 | URI 463 | USB 464 | V 465 | VFC 466 | VICI 467 | VLO 468 | VMC 469 | VNO 470 | VRSK 471 | VRSN 472 | VRTX 473 | VTR 474 | VTRS 475 | VZ 476 | WAB 477 | WAT 478 | WBA 479 | WBD 480 | WDC 481 | WEC 482 | WELL 483 | WFC 484 | WHR 485 | WM 486 | WMB 487 | WMT 488 | WRB 489 | WRK 490 | WST 491 | WTW 492 | WY 493 | WYNN 494 | XEL 495 | XOM 496 | XRAY 497 | XYL 498 | YUM 499 | ZBH 500 | ZBRA 501 | ZION 502 | ZTS 503 | -------------------------------------------------------------------------------- /trend_low_close.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | from scipy.stats import linregress 4 | import math 5 | import matplotlib.pyplot as plt 6 | import glob 7 | import time 8 | import decimal 9 | 10 | start_date = '2012-1-1' 11 | 12 | data1_reg_loop_limit = 5 13 | 14 | lowtrenderror = [] 15 | 16 | def calculate_angle(stock): 17 | data = pd.read_csv("./data/yahoo_price/" + stock + ".csv", delimiter=',') 18 | data = data.dropna(how='any',axis=0) # BAZI DATALAR NULL onları yoktmek 19 | 20 | data0 = data.copy() 21 | data0['Date'] = pd.to_datetime(data0['Date'], errors='coerce') 22 | 23 | # date filter 24 | data0 = data0[data0.Date > start_date] 25 | 26 | 27 | data0['date_id'] = ((data0['Date'] - data0['Date'].min())).astype('timedelta64[D]') 28 | data0['date_id'] = data0['date_id'] + 1 29 | data0['Adj Close'] = data0['Adj Close'].astype(np.double) 30 | 31 | # low trend line 32 | # lower points are returned 33 | df_low = data0.copy() 34 | while len(df_low)>5: 35 | slope, intercept, r_value, p_value, std_err = linregress(x=df_low['date_id'], y=df_low['Adj Close']) 36 | df_low = df_low.loc[df_low['Adj Close'] < slope * df_low['date_id'] + intercept] 37 | 38 | if len(df_low) >= 2: 39 | slope, intercept, r_value, p_value, std_err = linregress(x=df_low['date_id'], y=df_low['Adj Close']) 40 | data0['lowtrend'] = slope * data0['date_id'] + intercept 41 | 42 | 43 | trend_low_min = min(data0['Adj Close']) 44 | trend_low_max = max(data0['Adj Close']) 45 | trend_low_count = max(data0['date_id']) - min(data0['date_id']) 46 | 47 | # negative or positive trend 48 | if data0["Adj Close"].iloc[0] > data0["Adj Close"].iloc[-1]: 49 | trend_low_positive = "false" 50 | else: 51 | trend_low_positive = "true" 52 | 53 | # eliminate minus figures 54 | if trend_low_min < 0: 55 | trend_low_min = trend_low_min + (trend_low_min * -1) + 1 56 | trend_low_max = trend_low_max + + (trend_low_min * -1) + 1 57 | 58 | 59 | trend_low_angle = np.rad2deg(np.arctan2(trend_low_max - trend_low_min, trend_low_count)) 60 | 61 | change = trend_low_max / trend_low_min 62 | 63 | # distance to trend line 64 | 65 | kapanis = (data0["Adj Close"].iloc[-1]) 66 | son_lowtrend = (data0["lowtrend"].iloc[-1]) 67 | fark_trendlow = kapanis - son_lowtrend # 68 | fark_trendlow_yuzde = (kapanis / son_lowtrend) - 1 # yüzde kaçı 69 | 70 | 71 | print(stock, '== min: ', trend_low_min, ' max: ', trend_low_max, ' count: ', trend_low_count, ' angle: ', 72 | trend_low_angle, ' fark_trendlow: ', fark_trendlow_yuzde) 73 | 74 | return stock, trend_low_min, trend_low_max, trend_low_count, trend_low_angle, change, fark_trendlow_yuzde, trend_low_positive 75 | 76 | else: 77 | print(stock, ": low trend tespit edilemedi.") 78 | lowtrenderror.append(stock) 79 | 80 | ########################################################################################################################## 81 | 82 | def plot(stock, rating): 83 | data = pd.read_csv("./data/yahoo_price/" + stock + ".csv", delimiter=',') 84 | data = data.dropna(how='any',axis=0) # BAZI DATALAR NULL onları yoktmek 85 | 86 | data0 = data.copy() 87 | data0['Date'] = pd.to_datetime(data0['Date'], errors='coerce') 88 | 89 | # date filter 90 | data0 = data0[data0.Date > start_date] 91 | 92 | 93 | data0['date_id'] = ((data0['Date'] - data0['Date'].min())).astype('timedelta64[D]') 94 | data0['date_id'] = data0['date_id'] + 1 95 | data0['Adj Close'] = data0['Adj Close'].astype(np.double) 96 | 97 | # low trend line 98 | df_low = data0.copy() 99 | while len(df_low)>5: 100 | slope, intercept, r_value, p_value, std_err = linregress(x=df_low['date_id'], y=df_low['Adj Close']) 101 | df_low = df_low.loc[df_low['Adj Close'] < slope * df_low['date_id'] + intercept] 102 | 103 | 104 | slope, intercept, r_value, p_value, std_err = linregress(x=df_low['date_id'], y=df_low['Adj Close']) 105 | data0['lowtrend'] = slope * data0['date_id'] + intercept 106 | 107 | # draw the closing price and related trendlines (uptrend and downtrend) 108 | filename = str(rating) + "." + stock + '.png' 109 | 110 | fig, ax1 = plt.subplots(figsize=(15,10)) 111 | 112 | color = 'tab:green' 113 | xdate = [x.date() for x in data0.Date] 114 | ax1.set_xlabel('Date', color=color) 115 | ax1.plot(xdate, data0["Adj Close"], label="adj close", color=color) 116 | ax1.tick_params(axis='x', labelcolor=color) 117 | ax1.legend() 118 | plt.title(stock, fontsize=30) 119 | 120 | ax2 = ax1.twiny() # ax2 and ax1 will have common y axis and different x axis, twiny 121 | # ax2.plot(data0.Number, data0.Uptrend, label="uptrend") 122 | ax2.plot(data0.Date, data0.lowtrend, label="low trend") 123 | 124 | plt.legend() 125 | plt.grid() 126 | plt.savefig('./output/trend_low/'+filename) 127 | # plt.show() 128 | 129 | return filename, stock 130 | 131 | ######################################################################################################################## 132 | 133 | df_output = pd.DataFrame(columns=['Stock', "trend_low_min", "trend_low_max", "trend_low_count", 'trend_low_angle', 'fark_trendlow', 'trend_low_positive']) 134 | 135 | path = './data/yahoo/' 136 | files = [f for f in glob.glob(path + "**/*.csv", recursive=True)] 137 | 138 | 139 | counter = 0 140 | for f in files: 141 | counter = counter + 1 142 | StockName = f.replace("./data/yahoo\\", "") 143 | StockName = StockName.replace('.csv', "") 144 | 145 | print(counter, "/", len(files), " -- ", StockName) 146 | try: 147 | stock, trend_low_min, trend_low_max, trend_low_count, trend_low_angle, change, fark_trendlow, trend_low_positive = calculate_angle(StockName) 148 | 149 | df_output = df_output.append({'Stock': stock, 150 | "trend_low_min": trend_low_min, 151 | "trend_low_max": trend_low_max, 152 | "trend_low_count": trend_low_count, 153 | 'trend_low_angle': trend_low_angle, 154 | 'change_percentage': change, 155 | 'fark_trendlow': fark_trendlow, 156 | 'trend_low_positive': trend_low_positive 157 | }, 158 | ignore_index=True) 159 | except: 160 | print("An exception occurred: ", StockName) 161 | 162 | 163 | 164 | 165 | 166 | 167 | # only positive trends 168 | df_output = df_output[df_output.trend_low_positive == "true"] 169 | 170 | # filter against to minus values 171 | df_output = df_output[df_output.fark_trendlow > 0] 172 | 173 | # filter acording trend line angle 174 | df_output = df_output[df_output.trend_low_angle > 0.01] 175 | 176 | # sorting 177 | df_output_sort = df_output.sort_values(by=['fark_trendlow'], ascending= True) 178 | 179 | pd.set_option('display.max_rows', 150) 180 | pd.set_option('display.max_columns', 10) 181 | print(df_output_sort.head(20)) 182 | print("unable to find low trend for: ", lowtrenderror) 183 | 184 | 185 | 186 | 187 | for i in range(40): 188 | try: 189 | filename, stock = plot(df_output_sort.iloc[i, 0], i) 190 | print("figure created for: ", stock) 191 | except: 192 | print("stock adı yok: ", stock) 193 | 194 | # f.close() 195 | --------------------------------------------------------------------------------