portfolio.lisp 5.21 KB
Newer Older
Carlos Ungil's avatar
Carlos Ungil committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144
(in-package :bloomie)

(defvar *portfolio-fields* '((:members . "PORTFOLIO_MEMBERS")
			     (:positions . "PORTFOLIO_MPOSITION")
			     (:weights . "PORTFOLIO_MWEIGHT")
			     (:data . "PORTFOLIO_DATA")))

(defun bport (name fields &optional date)
  "Retrieves :members, :positions, :weights, or :data for the given portfolio (and optionally date)."
  (unless (member fields (mapcar #'first *portfolio-fields*))
    (error "fields should be one of~{ ~S~}" (mapcar #'first *portfolio-fields*)))
  (let ((request (request "PortfolioData" :securities name :fields (cdr (assoc fields *portfolio-fields*))
			  :overrides (if date `(("REFERENCE_DATE" ,(bbdate date)))))))
    #+lispworks (let ((mb (mp:make-mailbox)))
		  (setf (gethash (send-request request) *mailboxes*) mb)
		  (loop for (tree . done) = (mp:mailbox-read mb) append (process-tree tree) until done))
    #-lispworks (progn (send-request request) (retrieve-response))))

(defun portfolio-error (response)
  (let ((security-error (ignore-errors (unify:match ('(:|PortfolioDataResponse|
						       :|securityData|
						       (:|securityData|
							(:|security| ?security)
							(:|eidData|)
							(:|securityError| . ?security-error)
							(:|fieldExceptions|)
							(:|sequenceNumber| ?sequence-number)
							(:|fieldData|)))
						      response)
					 (cons security security-error)))))
    (when security-error
      (error 'security-error :security (car security-error)
	     :category (cadr (assoc :|category| (cdr security-error)))
	     :subcategory (cadr (assoc :|subcategory| (cdr security-error)))
	     :message (cadr (assoc :|message| (cdr security-error)))))))

(defun portfolio-empty (response)
  (ignore-errors
    (unify:match ('(:|PortfolioDataResponse|
		    :|securityData|
		    (:|securityData|
		     (:|security| ?id)
		     (:|eidData|)
		     (:|fieldExceptions|)
		     (:|sequenceNumber| 0)
		     (:|fieldData|)))
		   response)
      t)))

(defun portfolio-tickers (id &optional date)
  "Retrieves members for the given portfolio (v.g. U12345678-1, see portfolios in PRTU page) and optionally date."
  (let ((output (bport (concatenate 'string id " Client") :members date)))
    (or (portfolio-error output)
	(unless (portfolio-empty output)
	  (unify:match ('(:|PortfolioDataResponse|
			  :|securityData|
			  (:|securityData|
			   (:|security| ?id)
			   (:|eidData|)
			   (:|fieldExceptions|)
			   (:|sequenceNumber| 0)
			   (:|fieldData|
			    #T(list :PORTFOLIO_MEMBERS &rest ?members))))
			 output)
	    (loop for member in members
	       collect (unify:match ('(:PORTFOLIO_MEMBERS
				       (:|Security| ?security))
				      member)
			 security)))))))

(defun portfolio-weights (id &optional date)
  "Retrieves weights for the given portfolio (v.g. U12345678-1, see portfolios in PRTU page) and optionally date."
  (let ((output (bport (concatenate 'string id " Client") :weights date)))
    (or (portfolio-error output)
	(unless (portfolio-empty output)
	  (unify:match ('(:|PortfolioDataResponse|
			  :|securityData|
			  (:|securityData|
			   (:|security| ?id)
			   (:|eidData|)
			   (:|fieldExceptions|)
			   (:|sequenceNumber| 0)
			   (:|fieldData|		  
			    #T(list :PORTFOLIO_MWEIGHT &rest ?members))))
			 output)
	    (loop for member in members 
	       collect (unify:match ('(:PORTFOLIO_MWEIGHT
				       (:|Security| ?security)
				       (:|Weight| ?weight))
				      member)
			 (list security weight))))))))

(defun portfolio-positions (id &optional date)
  "Retrieves positions for the given portfolio (v.g. U12345678-1, see portfolios in PRTU page) and optionally date."
  (let ((output (bport (concatenate 'string id " Client") :positions date)))
    (or (portfolio-error output)
	(unless (portfolio-empty output)
	(unify:match ('(:|PortfolioDataResponse|
			:|securityData|
			(:|securityData|
			 (:|security| ?id)
			 (:|eidData|)
			 (:|fieldExceptions|)
			 (:|sequenceNumber| 0)
			 (:|fieldData|		  
			  #T(list :PORTFOLIO_MPOSITION &rest ?members))))
		       output)
		     (loop for member in members 
			collect (unify:match ('(:PORTFOLIO_MPOSITION
						(:|Security| ?security)
						(:|Position| ?position))
					       member)
					     (list security position))))))))

(defstruct (pf-position (:conc-name pos-))
  security position market-value cost cost-date cost-fx-rate)

(defun portfolio-data (id &optional date)
  "Retrieves data for the given portfolio (v.g. U12345678-1, see portfolios in PRTU page) and optionally date."
  (let ((output (bport (concatenate 'string id " Client") :data date)))
    (or (portfolio-error output)
	(unless (portfolio-empty output)
	(unify:match ('(:|PortfolioDataResponse|
			:|securityData|
			(:|securityData|
			 (:|security| ?id)
			 (:|eidData|)
			 (:|fieldExceptions|)
			 (:|sequenceNumber| 0)
			 (:|fieldData|		  
			  #T(list :PORTFOLIO_DATA &rest ?members))))
		       output)
		     (loop for member in members 
			collect (unify:match ('(:PORTFOLIO_DATA
						(:|Security| ?security)
						(:|Position| ?position)
						(:|Market Value| ?value)
						(:|Cost| ?cost)
						(:|Cost Date| ?date)
						(:|Cost Fx Rate| ?fxrate))
					       member)
					     (make-pf-position :security security :position position :market-value value
							       :cost cost :cost-fx-rate fxrate
							       :cost-date date))))))))