Sell book works!
This commit is contained in:
1 parent
0d126be10c
commit
1fa719ffe3
6 files changed
+239
-9
No files matched your search
@@ -6,6 +6,7 @@
|
||||
<exclude-output />
|
||||
<option name="createStubs" value="true" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/resources" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/dev-resources" isTestSource="false" />
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
:license {:name "Eclipse Public License"
|
||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||
:dependencies [[org.clojure/clojure "1.9.0"]]
|
||||
:java-source-paths ["src/java"]
|
||||
:main ^:skip-aot book.core
|
||||
:target-path "target/%s"
|
||||
:profiles {:uberjar {:aot :all}})
|
||||
+25
-5
@@ -1,10 +1,17 @@
|
||||
(ns book.core
|
||||
(:import [book.PersistentSortedList])
|
||||
(:require [book.order]
|
||||
[clojure.spec.alpha :as s]
|
||||
[clojure.edn :as edn])
|
||||
(:gen-class))
|
||||
|
||||
|
||||
(defn sorted-list [& keys]
|
||||
(book.PersistentSortedList/create (seq keys)))
|
||||
|
||||
(defn sorted-list-by [comparator & keys]
|
||||
(book.PersistentSortedList/create comparator (seq keys)))
|
||||
|
||||
|
||||
;; Let say a book is two collections. The first called
|
||||
;; sell-book contains all the sell orders. The second
|
||||
@@ -12,7 +19,14 @@
|
||||
|
||||
(defn create-book [& args]
|
||||
(let [cmp #(< (:price %1) (:price %2))]
|
||||
(apply sorted-set-by cmp args)))
|
||||
(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)})
|
||||
@@ -65,12 +79,18 @@
|
||||
(rest new-book))))
|
||||
|
||||
|
||||
(defn leftover-order [order trades]
|
||||
(let [traded-quantity (reduce + (map :book.order/quantity trades))
|
||||
leftover-quantity (- (:quantity order) traded-quantity)]
|
||||
(when (pos? leftover-quantity)
|
||||
(assoc order :quantity leftover-quantity))))
|
||||
|
||||
|
||||
(defn match-buy-order-in-sell-book [sell-book order]
|
||||
(let [trades-1 [(create-trade order (first sell-book))]
|
||||
trades (create-sell-trades order sell-book)
|
||||
new-book (compute-new-sell-book-after-trades sell-book trades)]
|
||||
[trades new-book]))
|
||||
(let [trades (create-sell-trades order sell-book)
|
||||
new-book (compute-new-sell-book-after-trades sell-book trades)
|
||||
new-buy-order (leftover-order order trades)]
|
||||
[trades new-book new-buy-order]))
|
||||
|
||||
|
||||
(defn place-buy-order [book order]
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
package book;
|
||||
|
||||
import clojure.lang.*;
|
||||
import java.util.*;
|
||||
|
||||
public class PersistentSortedList extends ASeq implements IPersistentList, IReduce, List, Counted{
|
||||
final private PersistentTreeMap impl;
|
||||
final private int _count;
|
||||
static public final PersistentSortedList EMPTY = new PersistentSortedList(null, PersistentTreeMap.EMPTY, 0);
|
||||
|
||||
public static PersistentSortedList create(ISeq items){
|
||||
PersistentSortedList ret = EMPTY;
|
||||
for(; items != null; items = items.next()){
|
||||
ret = ret.cons(items.first());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static PersistentSortedList create(Comparator comp, ISeq items){
|
||||
PersistentSortedList ret = new PersistentSortedList(null, new PersistentTreeMap(null, comp), 0);
|
||||
for(; items != null; items = items.next()){
|
||||
ret = ret.cons(items.first());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
PersistentSortedList(IPersistentMap meta, PersistentTreeMap impl, int count){
|
||||
super(meta);
|
||||
this.impl = impl;
|
||||
this._count = count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int count(){
|
||||
return _count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistentSortedList cons(final Object o){
|
||||
final IPersistentCollection f = (IPersistentCollection) impl.get(o);
|
||||
IPersistentCollection newList = f == null ? new PersistentList(o) : f.cons(o);
|
||||
return new PersistentSortedList(meta(), impl.assoc(o, newList), _count + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object peek() {
|
||||
if(_count == 0){
|
||||
return null;
|
||||
}
|
||||
final PersistentTreeMap.NodeIterator ite = this.impl.iterator();
|
||||
final AMapEntry node = (AMapEntry) ite.next();
|
||||
return ((IPersistentCollection) node.val()).seq().first();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPersistentStack pop() {
|
||||
if(_count == 0){
|
||||
throw new IllegalStateException("Can't pop empty list");
|
||||
}
|
||||
final PersistentTreeMap.NodeIterator ite = this.impl.iterator();
|
||||
final AMapEntry node = (AMapEntry) ite.next();
|
||||
if(((IPersistentCollection) node.val()).count() == 1){
|
||||
final PersistentTreeMap nextImpl = impl.without(node.getKey());
|
||||
return new PersistentSortedList(meta(), nextImpl, _count - 1);
|
||||
}
|
||||
final PersistentTreeMap nextImpl = impl.assoc(node.getKey(), ((IPersistentStack) node.getValue()).pop());
|
||||
return new PersistentSortedList(meta(), nextImpl, _count -1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object reduce(IFn f) {
|
||||
Object ret = first();
|
||||
for(ISeq s = next(); s != null; s = s.next()) {
|
||||
ret = f.invoke(ret, s.first());
|
||||
if (RT.isReduced(ret)) return ((IDeref)ret).deref();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object reduce(IFn f, Object start) {
|
||||
Object ret = f.invoke(start, first());
|
||||
for(ISeq s = next(); s != null; s = s.next()) {
|
||||
if (RT.isReduced(ret)) return ((IDeref)ret).deref();
|
||||
ret = f.invoke(ret, s.first());
|
||||
}
|
||||
if (RT.isReduced(ret)) return ((IDeref)ret).deref();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object first() {
|
||||
return peek();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistentSortedList next() {
|
||||
if(_count == 0){
|
||||
return null;
|
||||
}
|
||||
final PersistentTreeMap.NodeIterator ite = this.impl.iterator();
|
||||
final AMapEntry node = (AMapEntry) ite.next();
|
||||
final ISeq nextValue = ((IPersistentCollection) node.val()).seq().next();
|
||||
if(nextValue == null){
|
||||
final PersistentTreeMap nextImpl = impl.without(node.getKey());
|
||||
return impl.count() > 1 ? new PersistentSortedList(meta(), nextImpl, _count - 1) : null;
|
||||
}
|
||||
final PersistentTreeMap nextImpl = impl.assoc(node.getKey(), nextValue);
|
||||
return new PersistentSortedList(meta(), nextImpl, _count - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Obj withMeta(IPersistentMap meta) {
|
||||
if(meta != meta()){
|
||||
return new PersistentSortedList(meta, impl, _count);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
+45
-4
@@ -3,10 +3,8 @@
|
||||
[book.core :refer :all]))
|
||||
|
||||
|
||||
|
||||
|
||||
(deftest test-match-buy-order-in-sell-book
|
||||
(testing "Simplest case."
|
||||
(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-buy-order-in-sell-book sell-book order)]
|
||||
@@ -33,4 +31,47 @@
|
||||
[trades new-book] (match-buy-order-in-sell-book sell-book order)]
|
||||
(is (= 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}])))))
|
||||
#: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})
|
||||
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 (= 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})
|
||||
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))
|
||||
(is (= trades [#:book.order{:quantity 2 :price 12 :book-id 116 :order-id 118 :type :sell}
|
||||
#: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})
|
||||
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))
|
||||
(is (= trades []))
|
||||
(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}])))))
|
||||
@@ -0,0 +1,47 @@
|
||||
(ns book.test-PersistentSortedList
|
||||
(:require [clojure.test :refer :all]
|
||||
[book.core :refer [sorted-list sorted-list-by]]))
|
||||
|
||||
|
||||
|
||||
(deftest test-match-buy-order-in-sell-book
|
||||
(testing "Test ordering."
|
||||
(is (= (sorted-list 1 2 3) (sorted-list 3 2 1)))
|
||||
(is (= (sorted-list 1 2 2 3) (sorted-list 3 2 1 2))))
|
||||
(testing "count"
|
||||
(is (= (count (sorted-list)) 0))
|
||||
(is (= (count (sorted-list 1 2 3)) 3))
|
||||
(is (= (count (sorted-list 1 2 3 2)) 4)))
|
||||
(testing "rest"
|
||||
(is (= (rest (sorted-list 1 2 3)) (sorted-list 2 3)))
|
||||
(is (= (rest (sorted-list 1 2 3 2)) (sorted-list 2 2 3)))
|
||||
(is (= (rest (sorted-list 1)) (list)))
|
||||
(is (= (rest (sorted-list)) (list))))
|
||||
(testing "next"
|
||||
(is (= (next (sorted-list 1 2 3)) (sorted-list 2 3)))
|
||||
(is (= (next (sorted-list 1 2 3 2)) (sorted-list 2 2 3)))
|
||||
(is (= (next (sorted-list 1)) nil))
|
||||
(is (= (next (sorted-list)) nil)))
|
||||
(testing "drop"
|
||||
(is (= (drop 2 (sorted-list 1 2 3)) (sorted-list 3)))
|
||||
(is (= (drop 3 (sorted-list 1 2 3)) (list))))
|
||||
(testing "reduce"
|
||||
(is (= (reduce + (sorted-list 1 2 3)) 6))
|
||||
(is (= (reduce + 10 (sorted-list 1 2 3)) 16)))
|
||||
(testing "peek/pop"
|
||||
(is (= (peek (sorted-list 1 2 3)) 1))
|
||||
(is (= (peek (sorted-list 1 1 1 2 3)) 1))
|
||||
(is (= (peek (sorted-list)) nil))
|
||||
(is (= (pop (sorted-list 1 2 3)) (sorted-list 2 3)))
|
||||
(is (= (pop (sorted-list 1 1 2 3)) (sorted-list 1 2 3)))
|
||||
(is (= (pop (sorted-list 1 1 1 2 3)) (sorted-list 1 1 2 3)))
|
||||
(is (thrown? IllegalStateException (pop (sorted-list)) nil)))
|
||||
(testing "count after change"
|
||||
(let [l (sorted-list 1 2 3)]
|
||||
(is (= (count (conj l 4)) 4))
|
||||
(is (= (count (conj l 3)) 4))
|
||||
(is (= (count (cons 3 l)) 4))
|
||||
(is (= (count (rest l)) 2))
|
||||
(is (= (count (pop l)) 2))))
|
||||
(testing "sorted-list-by"
|
||||
(is (= (sorted-list-by > 1 2 3) [3 2 1]))))
|
||||
Reference in new issue
Block a user