Utilities.jl (3555B)
1 2 # -------------------------------------------------------------------------------------------------- 3 function check_integer(x::AbstractVector) 4 for i in x 5 !(typeof(i) <: Union{Missing, Number}) && return false 6 ismissing(i) && continue 7 isinteger(i) && continue 8 return false 9 end 10 return true 11 end 12 # -------------------------------------------------------------------------------------------------- 13 14 15 # -------------------------------------------------------------------------------------------------- 16 """ 17 open_wrds_pg(user, password) 18 19 Open a Postgres connection on WRDS server. 20 21 !!! note "Duo Two-Factor Authentication" 22 WRDS requires Duo 2FA. You may receive a push notification or need to approve 23 a login request on your device. If the connection fails due to SSL/timeout, 24 do NOT retry automatically — repeated attempts can trigger an account lockout. 25 """ 26 function open_wrds_pg(user::AbstractString, password::AbstractString) 27 @info "Connecting to WRDS — you may receive a Duo 2FA push notification" 28 conn_str = """ 29 host = wrds-pgdata.wharton.upenn.edu 30 port = 9737 31 user='$user' 32 password='$password' 33 sslmode = 'require' dbname = wrds 34 """ 35 try 36 return Connection(conn_str) 37 catch e 38 @error "WRDS connection failed. If this is an SSL/timeout error, check your network and Duo 2FA before retrying — repeated failures may lock your account." exception=(e, catch_backtrace()) 39 rethrow(e) 40 end 41 end 42 43 function open_wrds_pg() 44 # prompt to input 45 print("Enter WRDS username: ") 46 user = readline() 47 password_buffer = Base.getpass("Enter WRDS password") 48 password_bytes = copy(password_buffer.data[1:password_buffer.size]) 49 Base.shred!(password_buffer) 50 try 51 return open_wrds_pg(user, String(password_bytes)) 52 finally 53 fill!(password_bytes, 0x00) # zero out the password bytes 54 end 55 end 56 57 58 """ 59 with_wrds_connection(f; user="", password="") 60 61 Open a WRDS PostgreSQL connection, execute `f(conn)`, and ensure the connection is closed. 62 63 # Examples 64 ```julia 65 df = with_wrds_connection(user="myuser", password="mypwd") do conn 66 import_MSF(conn; date_range=(Date("2020-01-01"), Date("2023-12-31"))) 67 end 68 ``` 69 """ 70 function with_wrds_connection(f::Function; user::AbstractString="", password::AbstractString="") 71 conn = user == "" ? open_wrds_pg() : open_wrds_pg(user, password) 72 try 73 return f(conn) 74 finally 75 close(conn) 76 end 77 end 78 # -------------------------------------------------------------------------------------------------- 79 80 81 # -------------------------------------------------------------------------------------------------- 82 """ 83 _validate_date_range(date_range; earliest, latest) -> Tuple{Date, Date} 84 85 Validate and normalize a date range. Swaps reversed dates, warns on suspicious ranges. 86 """ 87 function _validate_date_range(date_range::Tuple{Date, Date}; 88 earliest::Date=Date("1925-01-01"), 89 latest::Date=Dates.today() + Month(6)) 90 91 start_date, end_date = date_range 92 if start_date > end_date 93 @warn "Start date after end date, swapping" start_date end_date 94 start_date, end_date = end_date, start_date 95 end 96 start_date < earliest && @warn "Start date $start_date is before earliest available data ($earliest)" 97 end_date > latest && @warn "End date $end_date is in the future" 98 return (start_date, end_date) 99 end 100 # --------------------------------------------------------------------------------------------------