FinanceRoutines.jl

Financial data routines for Julia
Log | Files | Refs | README | LICENSE

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 # --------------------------------------------------------------------------------------------------