diff --git a/src/book/core.clj b/src/book/core.clj index 2417f2e..4751503 100644 --- a/src/book/core.clj +++ b/src/book/core.clj @@ -17,19 +17,19 @@ ;; sell-book contains all the sell orders. The second ;; called buy-book contains all the buy orders. -(defn create-book [& args] - (let [cmp #(< (:price %1) (:price %2))] +(defn create-sell-book [& args] + (let [cmp #(compare (:price %1) (:price %2))] + (apply sorted-list-by cmp args))) + +(defn create-buy-book [& args] + (let [cmp #(compare (:price %2) (:price %1))] (apply sorted-list-by cmp args))) -(defn sorted-set-rest [s] - (print "=>" s) - (if (empty? s) - s - (disj s (first s)))) -(def book {:sell (create-book) :buy (create-book)}) + +(def book {:sell (create-sell-book) :buy (create-sell-book)}) (defn buy? [order] @@ -43,23 +43,35 @@ :order-id (:book.order/id order-to-fulfill) :book-id (:book.order/id book-order)}) -(defn create-sell-trade-factory [order-to-fulfill] - (fn [trade book-order] - (let [already-fulfilled (:book.order/already-fulfilled trade 0) - qtn-to-fulfill (min (:quantity book-order) (- (:quantity order-to-fulfill) already-fulfilled)) - can-fulfill? (<= (:price book-order) (:price order-to-fulfill))] - (when (and can-fulfill? (< 0 qtn-to-fulfill)) - #:book.order{:type :sell - :quantity qtn-to-fulfill - :already-fulfilled (+ already-fulfilled qtn-to-fulfill) - :price (min (:price book-order) (:price order-to-fulfill)) - :order-id (:book.order/id order-to-fulfill) - :book-id (:book.order/id book-order)})))) +(defn sell? [order] + (= (:type order) :sell)) + +(defn sell-order-fullfillable? [book-order order] + (>= (:price book-order) (:price order))) + +(defn buy-order-fullfillable? [book-order order] + (<= (:price book-order) (:price order))) + +(defn create-trade-factory [order-to-fulfill] + (let [trade-type (if (sell? order-to-fulfill) :buy :sell) + trade-price (if (sell? order-to-fulfill) max min) + fulfillable? (if (sell? order-to-fulfill) sell-order-fullfillable? buy-order-fullfillable?)] + (fn [trade book-order] + (let [already-fulfilled (:book.order/already-fulfilled trade 0) + qtn-to-fulfill (min (:quantity book-order) (- (:quantity order-to-fulfill) already-fulfilled)) + can-fulfill? (fulfillable? book-order order-to-fulfill)] + (when (and can-fulfill? (< 0 qtn-to-fulfill)) + #:book.order{:type trade-type + :quantity qtn-to-fulfill + :already-fulfilled (+ already-fulfilled qtn-to-fulfill) + :price (trade-price (:price book-order) (:price order-to-fulfill)) + :order-id (:book.order/id order-to-fulfill) + :book-id (:book.order/id book-order)}))))) (defn create-sell-trades [order-to-fulfill sell-book] - (let [create-sell-trade (create-sell-trade-factory order-to-fulfill) - trades (reductions create-sell-trade {} sell-book) + (let [create-trade (create-trade-factory order-to-fulfill) + trades (reductions create-trade {} sell-book) ret (rest (take-while some? trades))] (map #(dissoc % :book.order/already-fulfilled) ret))) @@ -92,6 +104,12 @@ new-buy-order (leftover-order order trades)] [trades new-book new-buy-order])) +(defn match-sell-order-in-buy-book [buy-book order] + (let [trades (create-sell-trades order buy-book) + new-book (compute-new-sell-book-after-trades buy-book trades) + new-buy-order (leftover-order order trades)] + [trades new-book new-buy-order])) + (defn place-buy-order [book order] (match-buy-order-in-sell-book (:sell book) order) diff --git a/test/book/core_test.clj b/test/book/core_test.clj index 4c79272..96f2971 100644 --- a/test/book/core_test.clj +++ b/test/book/core_test.clj @@ -5,52 +5,44 @@ (deftest test-match-buy-order-in-sell-book (testing "Simplest case I." - (let [sell-book (create-book {:type :sell :quantity 10 :price 2 :book.order/id 101}) + (let [sell-book (create-sell-book {:type :sell :quantity 10 :price 2 :book.order/id 101}) order {:type :buy :quantity 10 :price 2 :book.order/id 102} [trades new-book] (match-buy-order-in-sell-book sell-book order)] - (is (= new-book [])) + (is (empty? new-book)) (is (= trades [#:book.order{:quantity 10 :price 2 :book-id 101 :order-id 102 :type :sell}])))) (testing "Simplest case II." - (let [sell-book (create-book {:type :sell :quantity 10 :price 2 :book.order/id 103}) + (let [sell-book (create-sell-book {:type :sell :quantity 10 :price 2 :book.order/id 103}) order {:type :buy :quantity 10 :price 3 :book.order/id 104} [trades new-book] (match-buy-order-in-sell-book sell-book order)] - (is (= new-book [])) + (is (empty? new-book)) (is (= trades [#:book.order{:quantity 10 :price 2 :book-id 103 :order-id 104 :type :sell}])))) (testing "Simplest case III." - (let [sell-book (create-book {:type :sell :quantity 10 :price 2 :book.order/id 105}) + (let [sell-book (create-sell-book {:type :sell :quantity 10 :price 2 :book.order/id 105}) order {:type :buy :quantity 8 :price 2 :book.order/id 106} [trades new-book] (match-buy-order-in-sell-book sell-book order)] (is (= new-book [{:type :sell :quantity 2 :price 2 :book.order/id 105}])) (is (= trades [#:book.order{:quantity 8 :price 2 :book-id 105 :order-id 106 :type :sell}])))) (testing "Simplest case IV." - (let [sell-book (create-book {:type :sell :quantity 2 :price 12 :book.order/id 107} {:type :sell :quantity 8 :price 13 :book.order/id 108}) + (let [sell-book (create-sell-book {:type :sell :quantity 2 :price 12 :book.order/id 107} {:type :sell :quantity 8 :price 13 :book.order/id 108}) order {:type :buy :quantity 10 :price 13 :book.order/id 109} [trades new-book] (match-buy-order-in-sell-book sell-book order)] - (is (= new-book [])) + (is (empty? new-book)) (is (= trades [#:book.order{:quantity 2 :price 12 :book-id 107 :order-id 109 :type :sell} #:book.order{:quantity 8 :price 13 :book-id 108 :order-id 109 :type :sell}])))) (testing "Simplest case V." - (let [sell-book (create-book {:type :sell :quantity 2 :price 12 :book.order/id 110} {:type :sell :quantity 8 :price 13 :book.order/id 111}) - order {:type :buy :quantity 10 :price 13 :book.order/id 112} - [trades new-book] (match-buy-order-in-sell-book sell-book order)] - (is (empty? new-book)) - (is (= trades [#:book.order{:quantity 2 :price 12 :book-id 110 :order-id 112 :type :sell} - #:book.order{:quantity 8 :price 13 :book-id 111 :order-id 112 :type :sell}])))) - - (testing "Simplest case VI." - (let [sell-book (create-book {:type :sell :quantity 2 :price 12 :book.order/id 113} {:type :sell :quantity 8 :price 13 :book.order/id 114}) + (let [sell-book (create-sell-book {:type :sell :quantity 2 :price 12 :book.order/id 113} {:type :sell :quantity 8 :price 13 :book.order/id 114}) order {:type :buy :quantity 9 :price 13 :book.order/id 115} [trades new-book] (match-buy-order-in-sell-book sell-book order)] - (is (= new-book (create-book {:type :sell :quantity 1 :price 13 :book.order/id 114}))) + (is (= new-book (create-sell-book {:type :sell :quantity 1 :price 13 :book.order/id 114}))) (is (= trades [#:book.order{:quantity 2 :price 12 :book-id 113 :order-id 115 :type :sell} #:book.order{:quantity 7 :price 13 :book-id 114 :order-id 115 :type :sell}])))) - (testing "Simplest case VII." - (let [sell-book (create-book {:type :sell :quantity 2 :price 12 :book.order/id 116} {:type :sell :quantity 8 :price 13 :book.order/id 117}) + (testing "Simplest case VI." + (let [sell-book (create-sell-book {:type :sell :quantity 2 :price 12 :book.order/id 116} {:type :sell :quantity 8 :price 13 :book.order/id 117}) order {:type :buy :quantity 21 :price 13 :book.order/id 118} [trades new-book leftover-order] (match-buy-order-in-sell-book sell-book order)] (is (empty? new-book)) @@ -58,8 +50,8 @@ #:book.order{:quantity 8 :price 13 :book-id 117 :order-id 118 :type :sell}])) (is (= leftover-order {:type :buy :quantity 11 :price 13 :book.order/id 118})))) - (testing "Simplest case IIX." - (let [sell-book (create-book {:type :sell :quantity 2 :price 12 :book.order/id 116} {:type :sell :quantity 8 :price 13 :book.order/id 117}) + (testing "Simplest case VII." + (let [sell-book (create-sell-book {:type :sell :quantity 2 :price 12 :book.order/id 116} {:type :sell :quantity 8 :price 13 :book.order/id 117}) order {:type :buy :quantity 21 :price 10 :book.order/id 118} [trades new-book leftover-order] (match-buy-order-in-sell-book sell-book order)] (is (= new-book sell-book)) @@ -67,11 +59,52 @@ (is (= leftover-order order))))) -; -;(deftest test-match-sell-order-in-buy-book -; (testing "Simplest case I." -; (let [sell-book (create-book {:type :sell :quantity 10 :price 2 :book.order/id 101}) -; order {:type :buy :quantity 10 :price 2 :book.order/id 102} -; [trades new-book] (match-sell-order-in-buy-book sell-book order)] -; (is (= new-book [])) -; (is (= trades [#:book.order{:quantity 10 :price 2 :book-id 101 :order-id 102 :type :sell}]))))) +(deftest test-match-sell-order-in-buy-book + (testing "Simplest case I." + (let [buy-book (create-buy-book {:type :buy :quantity 10 :price 2 :book.order/id 101}) + order {:type :sell :quantity 10 :price 2 :book.order/id 102} + [trades new-book] (match-sell-order-in-buy-book buy-book order)] + (is (= new-book [])) + (is (= trades [#:book.order{:quantity 10 :price 2 :book-id 101 :order-id 102 :type :buy}])))) + + (testing "Simplest case II." + (let [buy-book (create-buy-book {:type :buy :quantity 10 :price 4 :book.order/id 103}) + order {:type :sell :quantity 10 :price 3 :book.order/id 104} + [trades new-book] (match-sell-order-in-buy-book buy-book order)] + (is (= new-book [])) + (is (= trades [#:book.order{:quantity 10 :price 4 :book-id 103 :order-id 104 :type :buy}])))) + + (testing "Simplest case III." + (let [buy-book (create-buy-book {:type :buy :quantity 10 :price 2 :book.order/id 105}) + order {:type :sell :quantity 8 :price 2 :book.order/id 106} + [trades new-book] (match-sell-order-in-buy-book buy-book order)] + (is (= new-book [{:type :buy :quantity 2 :price 2 :book.order/id 105}])) + (is (= trades [#:book.order{:quantity 8 :price 2 :book-id 105 :order-id 106 :type :buy}])))) + + (testing "Simplest case IV." + (let [buy-book (create-buy-book {:type :buy :quantity 2 :price 14 :book.order/id 107} + {:type :buy :quantity 8 :price 13 :book.order/id 108}) + order {:type :sell :quantity 10 :price 13 :book.order/id 109} + [trades new-book] (match-sell-order-in-buy-book buy-book order)] + (is (= new-book [])) + (is (= trades [#:book.order{:quantity 2 :price 14 :book-id 107 :order-id 109 :type :buy} + #:book.order{:quantity 8 :price 13 :book-id 108 :order-id 109 :type :buy}])))) + + (testing "Simplest case V." + (let [buy-book (create-buy-book {:type :buy :quantity 2 :price 14 :book.order/id 113} + {:type :buy :quantity 8 :price 13 :book.order/id 114}) + order {:type :sell :quantity 9 :price 13 :book.order/id 115} + [trades new-book] (match-sell-order-in-buy-book buy-book order)] + (= new-book (create-buy-book {:type :buy :quantity 1 :price 13 :book.order/id 114})) + (is (= trades [#:book.order{:quantity 2 :price 14 :book-id 113 :order-id 115 :type :buy} + #:book.order{:quantity 7 :price 13 :book-id 114 :order-id 115 :type :buy}])))) + + (testing "Simplest case VI." + (let [sell-book (create-buy-book {:type :buy :quantity 2 :price 14 :book.order/id 116} + {:type :buy :quantity 8 :price 13 :book.order/id 117}) + order {:type :sell :quantity 21 :price 13 :book.order/id 118} + [trades new-book leftover-order] (match-sell-order-in-buy-book sell-book order)] + (is (empty? new-book)) + (is (= trades [#:book.order{:quantity 2 :price 14 :book-id 116 :order-id 118 :type :buy} + #:book.order{:quantity 8 :price 13 :book-id 117 :order-id 118 :type :buy}])) + (is (= leftover-order {:type :sell :quantity 11 :price 13 :book.order/id 118}))))) \ No newline at end of file