This project is my attempt at Stockfighter in Clojure. I am a Clojure beginner, so many things are probably wrong or not idiomatic.
Code reviews and pull requests are most appreciated.
- Install leiningen
git clone https://github.com/davidkrisch/stockfighter-clj.git
Set an environment variable STARFIGHTER_API_KEY
with your Stockfighter
API key.
Then start a repl
$ lein repl
Functions to interact with the Stockfighter API are loaded into the
user
namespace at startup.
user=> (body (api-heartbeat))
{"ok" true "error" ""}
user=> (:status (stock-quote "TESTEX" "FOOBAR"))
200
user=> (-> (venue-heartbeat "TESTEX") body (get "ok"))
Each of the following functions return a map with keys like
:status
and :headers
and :body
.
=> (api-heartbeat)
=> (venue-heartbeat "TESTEX")
=> (stocks "TESTEX")
=> (orderbook "TESTEX" "FOOBAR")
=> (stock-quote "TESTEX" "FOOBAR")
See the official documentation.
=> (def request-body (order-body "EXB123456" ;; Account number
"TESTEX" ;; Venue
"FOOBAR" ;; Stock symbol
10 ;; quantity
9999 ;; price $99.99
"buy" ;; direction - "buy" or "sell"
"limit")) ;; order type
=> (order "TESTEX" "FOOBAR" request-body)
=> (def order-id 1234)
=> (order-status "TESTEX" "FOOBAR" order-id)
=> (def order-id 1234)
=> (cancel-order "TESTEX" "FOOBAR" order-id)
The methods above are request/response oriented. ticker
connects to a websocket and returns a stream of stock
quotes. This is done in a separate thread so it doesn't
tie up the repl while it is connected
=> (def system {:chan-out (chan (sliding/buffer 1))
:account "EXB123456"
:venue "TESTEX"
:stock "FOOBAR"
:qty 10
:order-type "limit"
})
=> (ticker system)
Message received on the ticker tape websocket will be placed
on the :chan-out
channel and can be retrieved with a blocking
call like:
=> (<!! (:chan-out system))
Closing the websocket is accomplished by closing :chan-out
=> (close! (:chan-out system))
There aren't any yet, but it would be nice to be able to turn on debugging for HTTP requests and maybe other parts of the system.
In the dev.user
namespace there is a top level atom
called system.
It is a map
that holds the application's state and configuration.
The state of the application is held in (:trades system)
. It is an unordered
vector of trade maps. Each trade represents a single order. The trade
could be in any state: failed, unfilled, partially filled, or fully filled.
(def system
:account "<account-id>"
:venue "<venue-id>"
:stock "<stock-symbol>"
...
<other-stuff>
...
:trades [{:order-body {:account account
:venue venue
:stock stock
:qty qty
:price price
:direction direction
:orderType orderType}
:response (manifold/deferred)
:ts <timestamp>
}
...])
- :order-body
- (map) The POST body we submitted with the order
- :response
- (manifold/deferred) The response, possibly not realized.
- :ts
- Timestamp - when the order was submitted.
:trades
contains every interaction we've had with the system, so we can
answer many questions with it:
- What does my inventory look like? (long or short)
- What is the averge buy/sell price?
- What is my profit?
- How many unfilled orders do I have and how many shares are involved?
- How will my unfilled orders effect my inventory?
These questions can be answered using the functions in the mostly incomplete
stockfighter.state
namespace. I'm workin' in it!
There are 5 processes
- Ticker - connects to the quote ticker websocket and puts messages on a channel.
Runs in its own thread until
async/channel
is closed. - Order - Place orders using quotes from the ticker.
- Inventory Maintenance - Its job is to minimize inventory. Single thread running in a loop possibly with some sort of delay.
- Order Status - manage orders once they've been placed. Thread per order using a dedicated thread pool
- Visibility - periodically wakes up and prints state of system.
Copyright © 2015 David Krisch
Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.