first draft of the stock exchange
This commit is contained in:
1 parent
b1ba21f391
commit
c099924a89
7 files changed
+296
-24
No files matched your search
@@ -17,8 +17,16 @@
|
|||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
<orderEntry type="library" name="Leiningen: clojure-complete:0.2.4" level="project" />
|
<orderEntry type="library" name="Leiningen: clojure-complete:0.2.4" level="project" />
|
||||||
<orderEntry type="library" name="Leiningen: org.clojure/clojure:1.9.0" level="project" />
|
<orderEntry type="library" name="Leiningen: org.clojure/clojure:1.9.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Leiningen: org.clojure/core.async:0.4.474" level="project" />
|
||||||
|
<orderEntry type="library" name="Leiningen: org.clojure/core.cache:0.6.5" level="project" />
|
||||||
|
<orderEntry type="library" name="Leiningen: org.clojure/core.memoize:0.5.9" level="project" />
|
||||||
<orderEntry type="library" name="Leiningen: org.clojure/core.specs.alpha:0.1.24" level="project" />
|
<orderEntry type="library" name="Leiningen: org.clojure/core.specs.alpha:0.1.24" level="project" />
|
||||||
|
<orderEntry type="library" name="Leiningen: org.clojure/data.priority-map:0.0.7" level="project" />
|
||||||
<orderEntry type="library" name="Leiningen: org.clojure/spec.alpha:0.1.143" level="project" />
|
<orderEntry type="library" name="Leiningen: org.clojure/spec.alpha:0.1.143" level="project" />
|
||||||
|
<orderEntry type="library" name="Leiningen: org.clojure/tools.analyzer.jvm:0.7.0" level="project" />
|
||||||
|
<orderEntry type="library" name="Leiningen: org.clojure/tools.analyzer:0.6.9" level="project" />
|
||||||
<orderEntry type="library" name="Leiningen: org.clojure/tools.nrepl:0.2.12" level="project" />
|
<orderEntry type="library" name="Leiningen: org.clojure/tools.nrepl:0.2.12" level="project" />
|
||||||
|
<orderEntry type="library" name="Leiningen: org.clojure/tools.reader:1.0.0-beta4" level="project" />
|
||||||
|
<orderEntry type="library" name="Leiningen: org.ow2.asm/asm-all:4.2" level="project" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
||||||
+2
-1
@@ -3,7 +3,8 @@
|
|||||||
:url "http://example.com/FIXME"
|
:url "http://example.com/FIXME"
|
||||||
:license {:name "Eclipse Public License"
|
:license {:name "Eclipse Public License"
|
||||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||||
:dependencies [[org.clojure/clojure "1.9.0"]]
|
:dependencies [[org.clojure/clojure "1.9.0"]
|
||||||
|
[org.clojure/core.async "0.4.474"]]
|
||||||
:java-source-paths ["src/java"]
|
:java-source-paths ["src/java"]
|
||||||
:main ^:skip-aot book.core
|
:main ^:skip-aot book.core
|
||||||
:target-path "target/%s"
|
:target-path "target/%s"
|
||||||
|
|||||||
+77
-2
@@ -1,5 +1,80 @@
|
|||||||
(ns book.core)
|
(ns book.core
|
||||||
|
(:require [book.match :refer [sell?
|
||||||
|
create-buy-book
|
||||||
|
create-sell-book
|
||||||
|
match-order-in-book]]
|
||||||
|
[book.util :refer :all]
|
||||||
|
[book.order] ;; for spec side effect
|
||||||
|
[clojure.edn :as edn]
|
||||||
|
[clojure.core.async :refer [chan go >!! <!!]]
|
||||||
|
[clojure.spec.alpha :as s])
|
||||||
|
(:gen-class))
|
||||||
|
|
||||||
|
(def incoming-order-channel (chan))
|
||||||
|
|
||||||
|
(def trades-channel (chan))
|
||||||
|
|
||||||
|
(def new-book-order-channel (chan))
|
||||||
|
|
||||||
|
(defn print-chan [prefix c]
|
||||||
|
(loop [shit (<!! c)]
|
||||||
|
(println prefix shit)
|
||||||
|
(recur (<!! c))))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(def master-book {:sell (create-sell-book)
|
||||||
|
:buy (create-buy-book)})
|
||||||
|
|
||||||
|
;
|
||||||
|
;(defn process-buy-order [order]
|
||||||
|
; (let [[x y x] (match-order-in-book @buy-book order)]))
|
||||||
|
|
||||||
|
|
||||||
|
;;; repl things.
|
||||||
|
(defn read-order []
|
||||||
|
(try
|
||||||
|
(print "$ ")
|
||||||
|
(let [order (edn/read-string (read-line))
|
||||||
|
valid? (s/valid? :book.order/order order)]
|
||||||
|
(if valid? order nil))
|
||||||
|
(catch Exception e nil)))
|
||||||
|
|
||||||
|
(def order-ids (atom -1))
|
||||||
|
|
||||||
|
(defn add-id-to-order [order]
|
||||||
|
(assoc order :book.order/id (swap! order-ids inc)))
|
||||||
|
|
||||||
|
|
||||||
|
(defn process-orders [master-book orders-channel trades-channel new-book-order-channel]
|
||||||
|
(loop [order (<!! orders-channel)
|
||||||
|
{sell-book :sell buy-book :buy} master-book]
|
||||||
|
(if (sell? order)
|
||||||
|
(do
|
||||||
|
(let [[trades new-buy-book leftover-order] (match-order-in-book buy-book order)
|
||||||
|
new-sell-book (if leftover-order (conj sell-book) leftover-order)]
|
||||||
|
(when (seq trades) (>!! trades-channel trades))
|
||||||
|
(when leftover-order (>!! new-book-order-channel leftover-order))
|
||||||
|
(recur (<!! orders-channel) {:sell new-sell-book :buy new-buy-book})))
|
||||||
|
(do
|
||||||
|
(let [[trades new-sell-book leftover-order] (match-order-in-book sell-book order)
|
||||||
|
new-buy-book (if leftover-order (conj buy-book) leftover-order)]
|
||||||
|
(when (seq trades) (>!! trades-channel trades))
|
||||||
|
(when leftover-order (>!! new-book-order-channel leftover-order))
|
||||||
|
(recur (<!! orders-channel) {:sell new-sell-book :buy new-buy-book}))))))
|
||||||
|
|
||||||
|
|
||||||
(defn -main [& args]
|
(defn -main [& args]
|
||||||
(println "hello workd"))
|
(println "enter orders in edn format.")
|
||||||
|
(go (print-chan "trade > " trades-channel))
|
||||||
|
(go (print-chan "order > " new-book-order-channel))
|
||||||
|
(go (process-orders master-book incoming-order-channel trades-channel new-book-order-channel))
|
||||||
|
(loop [order (read-order)]
|
||||||
|
(if (some? order)
|
||||||
|
(do
|
||||||
|
(let [order-with-id (add-id-to-order order)]
|
||||||
|
(>!! incoming-order-channel order-with-id)
|
||||||
|
(println "\n\norder accepted with id: " (:book.order/id order-with-id))))
|
||||||
|
|
||||||
|
(println "invalid order"))
|
||||||
|
(recur (read-order))))
|
||||||
+6
-14
@@ -1,17 +1,9 @@
|
|||||||
(ns book.match
|
(ns book.match
|
||||||
(:import [book.PersistentSortedList])
|
(:require [book.util :refer [sorted-list-by]]
|
||||||
(:require [book.order]
|
|
||||||
[clojure.spec.alpha :as s]
|
[clojure.spec.alpha :as s]
|
||||||
[clojure.edn :as edn])
|
[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
|
;; Let say a book is two collections. The first called
|
||||||
;; sell-book contains all the sell orders. The second
|
;; sell-book contains all the sell orders. The second
|
||||||
@@ -95,9 +87,9 @@
|
|||||||
(when (pos? leftover-quantity)
|
(when (pos? leftover-quantity)
|
||||||
(assoc order :quantity leftover-quantity))))
|
(assoc order :quantity leftover-quantity))))
|
||||||
|
|
||||||
(defn match-order-in-book [sell-book order]
|
(defn match-order-in-book [book order]
|
||||||
(let [trades (create-trades order sell-book)
|
(let [trades (create-trades order book)
|
||||||
new-book (compute-new-book-after-trades sell-book trades)
|
new-book (compute-new-book-after-trades book trades)
|
||||||
new-buy-order (leftover-order order trades)]
|
new-buy-order (leftover-order order trades)]
|
||||||
[trades new-book new-buy-order]))
|
[trades new-book new-buy-order]))
|
||||||
|
|
||||||
@@ -112,7 +104,7 @@
|
|||||||
;
|
;
|
||||||
;(defn -main
|
;(defn -main
|
||||||
; [& args]
|
; [& args]
|
||||||
; (let [order (-> args first edn/read-string)]
|
; (let [order (-> a+rgs first edn/read-string)]
|
||||||
; (if (not (s/valid? :book.order/order order))
|
; (if (not (s/valid? :book.order/order order))
|
||||||
; (println "invalid order :( ")
|
; (println "invalid order :( ")
|
||||||
; (println (place-order book order)))))
|
; (println (place-order book order)))))
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
(ns book.util
|
||||||
|
(:import [book.PersistentSortedList]))
|
||||||
|
|
||||||
|
(defn sorted-list [& keys]
|
||||||
|
(book.PersistentSortedList/create (seq keys)))
|
||||||
|
|
||||||
|
(defn sorted-list-by [comparator & keys]
|
||||||
|
(book.PersistentSortedList/create comparator (seq keys)))
|
||||||
|
|
||||||
@@ -3,10 +3,18 @@ package book;
|
|||||||
import clojure.lang.*;
|
import clojure.lang.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public class PersistentSortedList extends ASeq implements IPersistentList, IReduce, List, Counted{
|
//IPersistentVector, Iterable,
|
||||||
|
// List,
|
||||||
|
// RandomAccess, Comparable,
|
||||||
|
// Serializable, IHashEq
|
||||||
|
|
||||||
|
public class PersistentSortedList extends Obj implements Iterable, RandomAccess, Comparable,
|
||||||
|
IHashEq, IPersistentList, IReduce, ISeq,
|
||||||
|
List, Counted{
|
||||||
final private PersistentTreeMap impl;
|
final private PersistentTreeMap impl;
|
||||||
final private int _count;
|
final private int _count;
|
||||||
static public final PersistentSortedList EMPTY = new PersistentSortedList(null, PersistentTreeMap.EMPTY, 0);
|
static public final PersistentSortedList EMPTY = new PersistentSortedList(null, PersistentTreeMap.EMPTY, 0);
|
||||||
|
private int _hasheq = 0;
|
||||||
|
|
||||||
public static PersistentSortedList create(ISeq items){
|
public static PersistentSortedList create(ISeq items){
|
||||||
PersistentSortedList ret = EMPTY;
|
PersistentSortedList ret = EMPTY;
|
||||||
@@ -42,6 +50,24 @@ public class PersistentSortedList extends ASeq implements IPersistentList, IRedu
|
|||||||
return new PersistentSortedList(meta(), impl.assoc(o, newList), _count + 1);
|
return new PersistentSortedList(meta(), impl.assoc(o, newList), _count + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IPersistentCollection empty() {
|
||||||
|
return PersistentSortedList.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equiv(Object obj) {
|
||||||
|
if(!(obj instanceof Sequential || obj instanceof List))
|
||||||
|
return false;
|
||||||
|
ISeq ms = RT.seq(obj);
|
||||||
|
for(ISeq s = seq(); s != null; s = s.next(), ms = ms.next()){
|
||||||
|
if(ms == null || !Util.equiv(s.first(), ms.first())){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ms == null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object peek() {
|
public Object peek() {
|
||||||
if(_count == 0){
|
if(_count == 0){
|
||||||
@@ -70,7 +96,7 @@ public class PersistentSortedList extends ASeq implements IPersistentList, IRedu
|
|||||||
@Override
|
@Override
|
||||||
public Object reduce(IFn f) {
|
public Object reduce(IFn f) {
|
||||||
Object ret = first();
|
Object ret = first();
|
||||||
for(ISeq s = next(); s != null; s = s.next()) {
|
for(ISeq s = (ISeq) next(); s != null; s = s.next()) {
|
||||||
ret = f.invoke(ret, s.first());
|
ret = f.invoke(ret, s.first());
|
||||||
if (RT.isReduced(ret)) return ((IDeref)ret).deref();
|
if (RT.isReduced(ret)) return ((IDeref)ret).deref();
|
||||||
}
|
}
|
||||||
@@ -80,7 +106,7 @@ public class PersistentSortedList extends ASeq implements IPersistentList, IRedu
|
|||||||
@Override
|
@Override
|
||||||
public Object reduce(IFn f, Object start) {
|
public Object reduce(IFn f, Object start) {
|
||||||
Object ret = f.invoke(start, first());
|
Object ret = f.invoke(start, first());
|
||||||
for(ISeq s = next(); s != null; s = s.next()) {
|
for(PersistentSortedList s = next(); s != null; s = s.next()) {
|
||||||
if (RT.isReduced(ret)) return ((IDeref)ret).deref();
|
if (RT.isReduced(ret)) return ((IDeref)ret).deref();
|
||||||
ret = f.invoke(ret, s.first());
|
ret = f.invoke(ret, s.first());
|
||||||
}
|
}
|
||||||
@@ -88,12 +114,10 @@ public class PersistentSortedList extends ASeq implements IPersistentList, IRedu
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object first() {
|
public Object first() {
|
||||||
return peek();
|
return peek();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public PersistentSortedList next() {
|
public PersistentSortedList next() {
|
||||||
if(_count == 0){
|
if(_count == 0){
|
||||||
return null;
|
return null;
|
||||||
@@ -109,6 +133,18 @@ public class PersistentSortedList extends ASeq implements IPersistentList, IRedu
|
|||||||
return new PersistentSortedList(meta(), nextImpl, _count - 1);
|
return new PersistentSortedList(meta(), nextImpl, _count - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Things that i did not want to implement.
|
||||||
|
|
||||||
|
private List reify(){
|
||||||
|
return Collections.unmodifiableList(new ArrayList(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ISeq more() {
|
||||||
|
ISeq s = next();
|
||||||
|
return s == null ? PersistentSortedList.EMPTY : s;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Obj withMeta(IPersistentMap meta) {
|
public Obj withMeta(IPersistentMap meta) {
|
||||||
if(meta != meta()){
|
if(meta != meta()){
|
||||||
@@ -116,5 +152,151 @@ public class PersistentSortedList extends ASeq implements IPersistentList, IRedu
|
|||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hasheq() {
|
||||||
|
if(_hasheq == 0){
|
||||||
|
_hasheq = Murmur3.hashOrdered(this);
|
||||||
|
}
|
||||||
|
return _hasheq;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ISeq seq() {
|
||||||
|
return count() == 0 ? null : (ISeq) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Object o) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return count();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return seq() == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
return impl.containsKey(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] toArray() {
|
||||||
|
return RT.seqToArray(seq());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(Object o) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(Collection collection) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(int i, Collection collection) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get(int index) {
|
||||||
|
return RT.nth(this, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object set(int i, Object o) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(int i, Object o) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object remove(int i) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int indexOf(Object o) {
|
||||||
|
// TODO: make this function fast by using the underlying sorted set. This function should be constant.
|
||||||
|
ISeq s = seq();
|
||||||
|
for(int i = 0; s != null; s = s.next(), i++) {
|
||||||
|
if(Util.equiv(s.first(), o)){
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int lastIndexOf(Object o) {
|
||||||
|
// TODO: make this function fast by using the underlying sorted set. This function should be constant.
|
||||||
|
return reify().lastIndexOf(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListIterator listIterator() {
|
||||||
|
return reify().listIterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ListIterator listIterator(int index) {
|
||||||
|
return reify().listIterator(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List subList(int fromIndex, int toIndex) {
|
||||||
|
return reify().subList(fromIndex, toIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean retainAll(Collection collection) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAll(Collection collection) {
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAll(Collection collection) {
|
||||||
|
for(Object o : collection) {
|
||||||
|
if(!contains(o)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] toArray(Object[] objects) {
|
||||||
|
return RT.seqToPassedArray(seq(), objects);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator iterator() {
|
||||||
|
return new SeqIterator(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
(ns book.test-PersistentSortedList
|
(ns book.test-PersistentSortedList
|
||||||
(:require [clojure.test :refer :all]
|
(:require [clojure.test :refer :all]
|
||||||
[book.match :refer [sorted-list sorted-list-by]]))
|
[book.util :refer [sorted-list sorted-list-by]]))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
(deftest test-match-buy-order-in-sell-book
|
(deftest test-match-buy-order-in-sell-book
|
||||||
|
(testing "constructing"
|
||||||
|
(is (empty? (map nil? (sorted-list)))))
|
||||||
|
;; The following are tests that fails...
|
||||||
|
;(is (empty? (vec (sorted-list)))))
|
||||||
(testing "Test ordering."
|
(testing "Test ordering."
|
||||||
(is (= (sorted-list 1 2 3) (sorted-list 3 2 1)))
|
(is (= (sorted-list 1 2 3) (sorted-list 3 2 1)))
|
||||||
(is (= (sorted-list 1 2 2 3) (sorted-list 3 2 1 2))))
|
(is (= (sorted-list 1 2 2 3) (sorted-list 3 2 1 2))))
|
||||||
@@ -16,7 +20,8 @@
|
|||||||
(is (= (rest (sorted-list 1 2 3)) (sorted-list 2 3)))
|
(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 2 3 2)) (sorted-list 2 2 3)))
|
||||||
(is (= (rest (sorted-list 1)) (list)))
|
(is (= (rest (sorted-list 1)) (list)))
|
||||||
(is (= (rest (sorted-list)) (list))))
|
(is (empty? (rest (sorted-list))))
|
||||||
|
(is (= (str (type (rest (sorted-list)))) "class book.PersistentSortedList")))
|
||||||
(testing "next"
|
(testing "next"
|
||||||
(is (= (next (sorted-list 1 2 3)) (sorted-list 2 3)))
|
(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 2 3 2)) (sorted-list 2 2 3)))
|
||||||
|
|||||||
Reference in new issue
Block a user