├── resources ├── simulator │ └── conf │ │ └── dev │ │ ├── webserver.edn │ │ └── system.edn └── client │ └── conf │ └── dev │ ├── loader.edn │ ├── sender.edn │ ├── receiver.edn │ ├── system.edn │ └── simulator.edn ├── .coveralls.yml ├── src └── clj │ └── xicotrader │ ├── util.clj │ ├── service.clj │ ├── loader.clj │ ├── main.clj │ ├── controller.clj │ ├── receiver.clj │ ├── sender.clj │ ├── config.clj │ ├── strategy.clj │ ├── system.clj │ └── schema.clj ├── .gitignore ├── arbitrage ├── test │ └── xicotrader │ │ └── arbitrage │ │ └── core_test.clj ├── src │ └── xicotrader │ │ ├── arbitrage │ │ ├── util.clj │ │ └── core.clj │ │ └── strategy │ │ └── impl.clj ├── README.md └── project.clj ├── externals ├── simulator │ └── src │ │ └── xicotrader │ │ ├── webserver.clj │ │ ├── system.clj │ │ ├── private.clj │ │ ├── public.clj │ │ └── webhandler.clj └── plugin-connector-simulator │ └── src │ └── xicotrader │ └── service │ └── core.clj ├── test └── clj │ └── xicotrader │ ├── schema_test.clj │ └── config_test.clj ├── project.clj ├── .circleci └── config.yml ├── dev └── user.clj ├── README.md └── LICENSE /resources/simulator/conf/dev/webserver.edn: -------------------------------------------------------------------------------- 1 | {:port 3000} -------------------------------------------------------------------------------- /resources/client/conf/dev/loader.edn: -------------------------------------------------------------------------------- 1 | {:strategy "arbitrage.jar"} -------------------------------------------------------------------------------- /.coveralls.yml: -------------------------------------------------------------------------------- 1 | repo_token: IaKjZHKyOUo36HwRBpioPEC3tL9H1pVUP 2 | -------------------------------------------------------------------------------- /resources/client/conf/dev/sender.edn: -------------------------------------------------------------------------------- 1 | {:trade ["ETHBTC" "ETHUSD" "BTCUSD"]} -------------------------------------------------------------------------------- /resources/client/conf/dev/receiver.edn: -------------------------------------------------------------------------------- 1 | {:trade ["ETHBTC" "ETHUSD" "BTCUSD"]} -------------------------------------------------------------------------------- /resources/simulator/conf/dev/system.edn: -------------------------------------------------------------------------------- 1 | {:webserver {:handler :webhandler}} -------------------------------------------------------------------------------- /src/clj/xicotrader/util.clj: -------------------------------------------------------------------------------- 1 | (ns xicotrader.util) 2 | 3 | (defmacro ignoring-exceptions [& body] 4 | `(try ~@body 5 | (catch Exception e#))) 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | .hgignore 11 | .hg/ 12 | idea/ -------------------------------------------------------------------------------- /src/clj/xicotrader/service.clj: -------------------------------------------------------------------------------- 1 | (ns xicotrader.service) 2 | 3 | (defprotocol Service 4 | (init [this trade-assets])) 5 | 6 | (defn init-service [service trade-assets] 7 | (.init service trade-assets)) 8 | -------------------------------------------------------------------------------- /arbitrage/test/xicotrader/arbitrage/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns xicotrader.arbitrage.core-test 2 | (:require [clojure.test :refer :all] 3 | [xicotrader.arbitrage.core :refer :all])) 4 | 5 | (deftest a-test 6 | (testing "FIXME, I fail." 7 | (is (= 0 1)))) 8 | -------------------------------------------------------------------------------- /resources/client/conf/dev/system.edn: -------------------------------------------------------------------------------- 1 | {:controller {:receiver :receiver 2 | :sender :sender 3 | :strategy :strategy} 4 | :strategy {:strategy "arbitrage.jar"} 5 | :receiver {} 6 | :sender {:service :simulator} 7 | :simulator {}} -------------------------------------------------------------------------------- /externals/simulator/src/xicotrader/webserver.clj: -------------------------------------------------------------------------------- 1 | (ns xicotrader.webserver 2 | (:require [com.stuartsierra.component :as component] 3 | [modular.http-kit :refer [new-webserver]])) 4 | 5 | (defn new [config] 6 | (apply new-webserver (or (seq (remove nil? (flatten (into [] config))))))) 7 | -------------------------------------------------------------------------------- /resources/client/conf/dev/simulator.edn: -------------------------------------------------------------------------------- 1 | {:host "localhost" 2 | :port 3000 3 | :private-url "api/private" 4 | :public-url "api/public" 5 | :user-id "javier" 6 | :secret-key "AEIOU" 7 | :polltime 1000 8 | :translation {"ETHUSD" "ETHUSD" 9 | "ETHBTC" "ETHBTC" 10 | "BTCUSD" "BTCUSD"}} -------------------------------------------------------------------------------- /src/clj/xicotrader/loader.clj: -------------------------------------------------------------------------------- 1 | (ns xicotrader.loader 2 | (:require 3 | [cemerick.pomegranate :as p] 4 | [xicotrader.strategy :as strategy])) 5 | 6 | (defn load-strategy [config system-map strategy-jar] 7 | (p/add-classpath strategy-jar) 8 | (require '[xicotrader.strategy.impl]) 9 | (eval '(xicotrader.strategy.impl/new {}))) 10 | 11 | (defn load-service [config system-map strategy-key] 12 | ;; todo 13 | ) 14 | -------------------------------------------------------------------------------- /src/clj/xicotrader/main.clj: -------------------------------------------------------------------------------- 1 | (ns xicotrader.main 2 | (:require 3 | [xicotrader 4 | [config :as config] 5 | [system :as system]]) 6 | (:gen-class)) 7 | 8 | (def prod-key :dev) 9 | (def prod-profile {:profile prod-key}) 10 | 11 | (defn -main 12 | [& args] 13 | (system/start-system 14 | (system/make-system-component 15 | (config/system-config prod-profile (keys (config/system-deps prod-profile)))))) 16 | -------------------------------------------------------------------------------- /arbitrage/src/xicotrader/arbitrage/util.clj: -------------------------------------------------------------------------------- 1 | (ns xicotrader.arbitrage.util 2 | (:gen-class)) 3 | 4 | (defn print-trade [buy? target-currency source-currency qty source-funds last-price] 5 | (println (if buy? "buy " "sell") 6 | (if buy? target-currency source-currency) 7 | (format "%.4f" (if buy? qty source-funds)) 8 | (format "at %.4f" (double (if buy? last-price (/ 1 last-price)))) 9 | (if buy? "with" "for") 10 | (if buy? source-currency target-currency) 11 | (format "%.4f" (if buy? source-funds qty)))) -------------------------------------------------------------------------------- /arbitrage/README.md: -------------------------------------------------------------------------------- 1 | # Xicotrader Arbitrage 2 | 3 | Simple arbitrage strategy for Xicotrader 4 | 5 | ## Build 6 | 7 | Build this project with 8 | 9 | ```bash 10 | lein uberjar 11 | ``` 12 | 13 | ## Load the JAR into Xicotrader 14 | 15 | Place the JAR file built in the previous step somewhere in Xicotrader's classpath. 16 | Specify the JAR file as a dependence in the Strategy module: 17 | 18 | ```clojure 19 | {:strategy "arbitrage.jar"} 20 | ``` 21 | 22 | Run the Xicotrader system 23 | 24 | ## License 25 | 26 | Copyright © 2018 Javier Arriero 27 | 28 | Distributed under the Eclipse Public License either version 1.0 or (at 29 | your option) any later version. 30 | -------------------------------------------------------------------------------- /arbitrage/src/xicotrader/strategy/impl.clj: -------------------------------------------------------------------------------- 1 | (ns xicotrader.strategy.impl 2 | (:require 3 | [xicotrader.arbitrage.core :refer [do-when-previous-action-filled do-arbitrage]]) 4 | (:import [xicotrader.strategy Strategy] 5 | [com.stuartsierra.component Lifecycle]) 6 | (:gen-class)) 7 | 8 | (defrecord Component [config] 9 | Lifecycle 10 | (start [this] 11 | this) 12 | (stop [this] 13 | this) 14 | 15 | Strategy 16 | (compute [strategy portfolio portfolio-updates market tick-data] 17 | (do-when-previous-action-filled portfolio-updates) 18 | (do-arbitrage portfolio market tick-data))) 19 | 20 | (defn new [config] 21 | (Component. config)) 22 | -------------------------------------------------------------------------------- /externals/simulator/src/xicotrader/system.clj: -------------------------------------------------------------------------------- 1 | (ns xicotrader.system 2 | (:require 3 | [clojure.core.async :as a] 4 | [com.stuartsierra.component :as component] 5 | [xicotrader 6 | [webhandler :as webhandler] 7 | [webserver :as webserver]])) 8 | 9 | (defn- create-webserver [config] 10 | (assoc (webserver/new config) :ch (a/chan))) 11 | 12 | (defn make-system-component 13 | [{:keys [webserver] :as config}] 14 | (component/system-map 15 | :webserver (create-webserver webserver) 16 | :webhandler (webhandler/new))) 17 | 18 | (defn start-system [system] 19 | (component/start system)) 20 | 21 | (defn stop-system [system] 22 | (component/stop system)) -------------------------------------------------------------------------------- /src/clj/xicotrader/controller.clj: -------------------------------------------------------------------------------- 1 | (ns xicotrader.controller 2 | (:require 3 | [clojure.core.async :as a :refer [go-loop >! ! s-to data) 11 | (recur)))) 12 | 13 | (defn- send-loop [{:keys [send s-from]}] 14 | (go-loop [] 15 | (when-let [data (! send data) 17 | (recur)))) 18 | 19 | (defrecord Component [config] 20 | component/Lifecycle 21 | (start [this] 22 | (receive-loop this) 23 | (send-loop this) 24 | this) 25 | (stop [this] 26 | this)) 27 | 28 | (defn new [config] 29 | (Component. config)) 30 | -------------------------------------------------------------------------------- /src/clj/xicotrader/receiver.clj: -------------------------------------------------------------------------------- 1 | (ns xicotrader.receiver 2 | (:require 3 | [clojure.core.async :as a :refer [go-loop >! !! alts!!]] 4 | [com.stuartsierra.component :as component] 5 | [schema.core :as s] 6 | [xicotrader 7 | [service :as service] 8 | [schema :refer [Event]]])) 9 | 10 | (defprotocol Receiver 11 | (receive [this data])) 12 | 13 | (defrecord Component [config] 14 | component/Lifecycle 15 | (start [this] 16 | (let [c-in (a/chan) 17 | this (assoc this :c-in c-in)] 18 | this)) 19 | (stop [this] 20 | (a/close! (:c-in this)) 21 | this) 22 | 23 | Receiver 24 | (receive [{:keys [c-in] :as this} data] 25 | (s/validate Event data) 26 | (>! c-in data))) 27 | 28 | (defn new [config] 29 | (Component. config)) 30 | -------------------------------------------------------------------------------- /arbitrage/project.clj: -------------------------------------------------------------------------------- 1 | (defproject xicotrader/arbitrage "0.1.0-SNAPSHOT" 2 | :description "Arbitrage strategy example to run with xicotrader's simulator" 3 | :url "https://github.com/analyticbastard/xicotrader" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :profiles {:provided {:dependencies [[org.clojure/clojure "1.8.0"] 7 | [com.stuartsierra/component "0.3.0" :exclusions [org.clojure/clojure]] 8 | [xicotrader "0.1.0-SNAPSHOT" :exclusions [org.clojure/clojure]]]} 9 | :dev {:dependencies [[org.clojure/tools.namespace "0.2.10" :exclusions [org.clojure/clojure]] 10 | [org.clojure/tools.nrepl "0.2.12" :exclusions [org.clojure/clojure]]]} 11 | :uberjar {:aot :all}} 12 | :target-path "target/%s") 13 | -------------------------------------------------------------------------------- /src/clj/xicotrader/sender.clj: -------------------------------------------------------------------------------- 1 | (ns xicotrader.sender 2 | (:require 3 | [clojure.core.async :as a :refer [go-loop >! !! alts!!]] 4 | [com.stuartsierra.component :as component] 5 | [schema.core :as s] 6 | [xicotrader 7 | [service :as service] 8 | [schema :refer [Action]]])) 9 | 10 | (defprotocol Connector 11 | (trade [this data])) 12 | 13 | (defn- send-loop [{:keys [c-out]} sender] 14 | (go-loop [] 15 | (when-let [action (! !! alts!!]] 4 | [clojure.tools.logging :as log] 5 | [com.stuartsierra.component :as component] 6 | [schema.core :as s] 7 | [xicotrader 8 | [schema :refer [Event Action]]]) 9 | (:gen-class)) 10 | 11 | (defprotocol Strategy 12 | (compute [this event])) 13 | 14 | (defn safe-compute [strategy event] 15 | (try 16 | (s/validate Event event) 17 | (s/validate Action (.compute strategy event)) 18 | (catch Throwable t 19 | (log/error (.getMessage t))))) 20 | 21 | (defn- strategy-loop [strategy ch-from c-to] 22 | (go-loop [] 23 | (when-let [event (! c-to action)) 26 | (recur))))) 27 | 28 | (defrecord Component [config] 29 | component/Lifecycle 30 | (start [this] 31 | (let [c-from (a/chan) 32 | c-to (a/chan)] 33 | (strategy-loop (:strategy this) c-from c-to) 34 | (assoc this :c-from c-from 35 | :c-to c-to))) 36 | (stop [this] 37 | (a/close! (:c-from this)) 38 | (a/close! (:c-to this)) 39 | this)) 40 | 41 | (defn new [config] 42 | (Component. config)) 43 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject xicotrader "0.1.0-SNAPSHOT" 2 | :description "Automatically trade cryoptocurrency" 3 | :url "https://github.com/analyticbastard/xicotrader" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.8.0"] 7 | [org.clojure/core.async "0.3.442"] 8 | [org.clojure/tools.logging "0.3.1"] 9 | [clj-time/clj-time "0.12.0"] 10 | [medley "1.0.0"] 11 | [prismatic/schema "1.0.4"] 12 | [com.stuartsierra/component "0.3.0"] 13 | [com.cemerick/pomegranate "0.3.1"]] 14 | :source-paths ["src/clj"] 15 | :test-paths ["test/clj"] 16 | :resource-paths ["resources/common" "resources/client"] 17 | :target-path "target/%s" 18 | :plugins [[lein-cloverage "1.0.7-SNAPSHOT"]] 19 | :aot [clojure.tools.logging.impl com.stuartsierra.component 20 | xicotrader.strategy xicotrader.schema] 21 | :profiles {:dev {:dependencies [[org.clojure/tools.namespace "0.2.10" :exclusions [org.clojure/clojure]] 22 | [org.clojure/tools.nrepl "0.2.12" :exclusions [org.clojure/clojure]]] 23 | :source-paths ["dev"]} 24 | :uberjar {:aot :all}}) 25 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Clojure CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-clojure/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # specify the version you desire here 10 | - image: circleci/clojure:lein-2.7.1 11 | 12 | # Specify service dependencies here if necessary 13 | # CircleCI maintains a library of pre-built images 14 | # documented at https://circleci.com/docs/2.0/circleci-images/ 15 | # - image: circleci/postgres:9.4 16 | 17 | working_directory: ~/xicotrader 18 | 19 | environment: 20 | LEIN_ROOT: "true" 21 | # Customize the JVM maximum heap limit 22 | JVM_OPTS: -Xmx3200m 23 | 24 | steps: 25 | - checkout 26 | 27 | # Download and cache dependencies 28 | - restore_cache: 29 | keys: 30 | - v1-dependencies-{{ checksum "project.clj" }} 31 | # fallback to using the latest cache if no exact match is found 32 | - v1-dependencies- 33 | 34 | - run: lein deps 35 | 36 | - save_cache: 37 | paths: 38 | - ~/.m2 39 | key: v1-dependencies-{{ checksum "project.clj" }} 40 | 41 | # run tests! 42 | - run: lein test 43 | - run: CLOVERAGE_VERSION=1.0.7-SNAPSHOT lein cloverage --codecov 44 | - run: curl -s https://codecov.io/bash | bash -s - -f target/coverage/codecov.json 45 | -------------------------------------------------------------------------------- /externals/simulator/src/xicotrader/private.clj: -------------------------------------------------------------------------------- 1 | (ns xicotrader.private 2 | (:require 3 | [xicotrader 4 | [public :as public] 5 | [util :as util]])) 6 | 7 | (def user-secrets 8 | {"javier" "AEIOU"}) 9 | 10 | (def user-portfolios 11 | (atom {"javier" {"USD" 5000. 12 | "BTC" 1. 13 | "ETH" 5.}})) 14 | 15 | (defn get-portfolio [user-id] 16 | (@user-portfolios user-id)) 17 | 18 | (defn trade! [user-id operation pair qty] 19 | (let [portfolio (@user-portfolios user-id) 20 | source-currency (.substring pair 3) 21 | target-currency (.substring pair 0 3) 22 | buy? (= :buy operation) 23 | [source-currency target-currency] (if buy? 24 | [source-currency target-currency] 25 | [target-currency source-currency]) 26 | last-price (:last (public/get-tick pair)) 27 | last-price (if buy? last-price (/ 1 last-price)) 28 | source-funds (* last-price qty)] 29 | (when (>= (portfolio source-currency) source-funds) 30 | (util/print-trade buy? target-currency source-currency qty source-funds last-price) 31 | (swap! user-portfolios update 32 | user-id (fn [$] 33 | (-> $ 34 | (update target-currency #(+ qty %)) 35 | (update source-currency #(- % source-funds)))))))) 36 | -------------------------------------------------------------------------------- /dev/user.clj: -------------------------------------------------------------------------------- 1 | (ns user 2 | (:require 3 | [clojure.tools.namespace.repl :refer [refresh refresh-all]] 4 | [com.stuartsierra.component :as component] 5 | [xicotrader 6 | [config :as config] 7 | [system :as system]])) 8 | 9 | (def system nil) 10 | 11 | (def dev-key :dev) 12 | (def dev-profile {:profile dev-key}) 13 | 14 | (defmacro when-not-system [& body] 15 | `(if-not system 16 | (do ~@body) 17 | (println "System found!"))) 18 | 19 | (defmacro when-system [& body] 20 | `(if system 21 | (do ~@body) 22 | (println "System not found!"))) 23 | 24 | (defn- dev-config [] 25 | (merge dev-profile 26 | (config/system-config 27 | (config/system-deps dev-profile) 28 | dev-profile))) 29 | 30 | (defn make-system 31 | "Makes dev system, assocs the :dev to the profile in config" 32 | [config] 33 | (-> (system/make-system-component config) 34 | (component/system-using 35 | (config/system-deps (select-keys config [:profile]))))) 36 | 37 | (defn start 38 | ([] (start (dev-config))) 39 | ([config] 40 | (when-not-system 41 | (let [s (make-system (merge (dev-config) config))] 42 | (alter-var-root #'user/system (constantly s)) 43 | (alter-var-root #'user/system system/start-system) 44 | (println "System started!"))))) 45 | 46 | (defn stop [] 47 | (when-system 48 | (alter-var-root #'user/system system/stop-system) 49 | (alter-var-root #'user/system (constantly nil)) 50 | (println "System stopped!"))) 51 | 52 | (defn restart 53 | ([] 54 | (restart (dev-config))) 55 | ([config] 56 | (stop) 57 | (start config))) 58 | -------------------------------------------------------------------------------- /externals/simulator/src/xicotrader/public.clj: -------------------------------------------------------------------------------- 1 | (ns xicotrader.public 2 | (require 3 | [clojure.set :as set] 4 | [clojure.string :as string] 5 | [clojure.java.io :as io] 6 | [clj-time.core :as time :refer [now]] 7 | [cheshire.core :as cheshire])) 8 | 9 | (defn get-pairs [] 10 | ["BTCUSD" "ETHBTC" "ETHUSD"]) 11 | 12 | (def base-time (atom nil)) 13 | 14 | (defn load-data [pair] 15 | (some-> (format "%s.json" (string/lower-case pair)) 16 | (io/resource) 17 | (io/reader) 18 | (cheshire/parsed-seq keyword) 19 | (first) 20 | (#(do (when-not @base-time 21 | (reset! base-time ((first %) :date))) 22 | %)))) 23 | 24 | ;; 10 min in reality is 1 sec in simulation assuming 5 min unix timestamps in milliseconds 25 | (def simulation-time-factor (* 600)) 26 | 27 | (defn- shift-time [right-now] 28 | (fn [$] 29 | (-> (set/rename-keys $ {:close :last}) 30 | (update :date #(time/plus right-now 31 | (-> (- % @base-time) 32 | (/ simulation-time-factor) 33 | (int) 34 | (time/seconds))))))) 35 | 36 | (def data 37 | (let [right-now (time/now)] 38 | (atom 39 | {"ETHBTC" (->> (load-data "ethbtc") 40 | (map (shift-time right-now))) 41 | "BTCUSD" (->> (load-data "btcusd") 42 | (map (shift-time right-now))) 43 | "ETHUSD" (->> (load-data "ethusd") 44 | (map (shift-time right-now)))}))) 45 | 46 | (defn get-tick [pair] 47 | (-> (swap! data update pair (partial drop-while #(time/before? (% :date) (time/now)))) 48 | (get pair) 49 | first)) 50 | -------------------------------------------------------------------------------- /test/clj/xicotrader/config_test.clj: -------------------------------------------------------------------------------- 1 | (ns xicotrader.config-test 2 | (:require 3 | [clojure.test :refer [deftest testing is]] 4 | [com.stuartsierra.component :as component] 5 | [xicotrader.config :refer :all] 6 | [xicotrader.strategy :as strategy])) 7 | 8 | (defrecord Component [config] 9 | component/Lifecycle 10 | (start [this] this) 11 | (stop [this] this) 12 | 13 | xicotrader.strategy/Strategy 14 | (compute [this event])) 15 | 16 | (defn fake-strategy-loader [config system-map strategy-jar] 17 | (->Component config)) 18 | 19 | (deftest read-system-test 20 | (testing "Read system dependencies" 21 | (is (= (system-deps user/dev-profile) 22 | {:controller {:receiver :receiver 23 | :sender :sender 24 | :strategy :strategy} 25 | :strategy {:strategy "arbitrage.jar"} 26 | :receiver {} 27 | :sender {:service :simulator} 28 | :simulator {}})))) 29 | 30 | (deftest read-config-test 31 | (testing "Read events module config" 32 | (is (= (get-config user/dev-profile :receiver) 33 | {:trade ["ETHBTC" "ETHUSD" "BTCUSD"]})))) 34 | 35 | (deftest system-config-test 36 | (testing "Read all modules config" 37 | (with-redefs [xicotrader.loader/load-strategy fake-strategy-loader] 38 | (is (= (system-config (system-deps user/dev-profile) user/dev-profile) 39 | {:controller (get-config user/dev-profile :controller) 40 | :strategy (get-config user/dev-profile :strategy) 41 | :receiver (get-config user/dev-profile :receiver) 42 | :sender (get-config user/dev-profile :receiver) 43 | :simulator (get-config user/dev-profile :simulator)}))))) 44 | -------------------------------------------------------------------------------- /src/clj/xicotrader/system.clj: -------------------------------------------------------------------------------- 1 | (ns xicotrader.system 2 | (:require 3 | [com.stuartsierra.component :as component] 4 | [xicotrader 5 | [controller :as controller] 6 | [receiver :as recv] 7 | [strategy :as strategy]] 8 | [xicotrader.loader :as loader] 9 | [xicotrader.config :as config] 10 | [xicotrader.service :as service])) 11 | 12 | (defn handle-init-fail [service-key strategy-key service-component strategy-component] 13 | (let [found-keys {"service-key" service-key 14 | "strategy-key" strategy-key} 15 | found-components {"service-component" service-component 16 | "strategy-component" strategy-component}] 17 | (throw (Exception. (str "Failed to start system" 18 | found-keys 19 | found-components))))) 20 | 21 | (defn make-system-component [{:keys [engine events strategy] :as config}] 22 | (let [system-map (config/system-deps config) 23 | strategy-jar (get-in system-map [:strategy :strategy]) 24 | strategy-component (loader/load-strategy config system-map strategy-jar) 25 | service-key (get-in system-map [:receiver :service]) 26 | service-component (loader/load-service config system-map service-key)] 27 | (if (and strategy-jar strategy-component 28 | service-key service-component) 29 | (component/system-map 30 | :controller (controller/new engine) 31 | :receiver (recv/new events) 32 | :strategy (strategy/new strategy) 33 | service-key service-component 34 | strategy-jar strategy-component) 35 | (handle-init-fail service-key strategy-jar service-component strategy-component)))) 36 | 37 | (defn start-system [system] 38 | (component/start system)) 39 | 40 | (defn stop-system [system] 41 | (component/stop system)) 42 | -------------------------------------------------------------------------------- /src/clj/xicotrader/schema.clj: -------------------------------------------------------------------------------- 1 | (ns xicotrader.schema 2 | (:require 3 | [schema.core :as s :refer [defschema optional-key]] 4 | [clj-time.core :as t] 5 | [medley.core :as m]) 6 | (:import (org.joda.time DateTime))) 7 | 8 | (defn constrain-ticker-either-ohcl-or-last [m] 9 | (not (and (seq (select-keys m [:open :close :high :low])) 10 | (:last m)))) 11 | 12 | (defschema Pos 13 | (s/constrained s/Num (comp not neg?))) 14 | 15 | (defschema Time 16 | {:time DateTime}) 17 | 18 | (defschema OHCL 19 | {:open Pos 20 | :close Pos 21 | :high Pos 22 | :low Pos}) 23 | 24 | (defschema Last 25 | {:last Pos}) 26 | 27 | (defschema Price 28 | {:price Pos}) 29 | 30 | (defschema Volume 31 | {:volume Pos}) 32 | 33 | (defschema MarketCap 34 | {:marketcap Pos}) 35 | 36 | (defschema Supply 37 | {:supply Pos}) 38 | 39 | (defschema Coin 40 | {:name s/Str 41 | :ticker s/Str}) 42 | 43 | (defschema Pair 44 | {:num Coin 45 | :den Coin}) 46 | 47 | (defschema Tick 48 | (-> (merge 49 | Time 50 | Pair 51 | (m/map-keys optional-key OHCL) 52 | (m/map-keys optional-key Last) 53 | (m/map-keys optional-key Volume) 54 | (m/map-keys optional-key MarketCap) 55 | (m/map-keys optional-key Supply)) 56 | (s/constrained constrain-ticker-either-ohcl-or-last))) 57 | 58 | (defschema Position 59 | {:amount Pos 60 | :side (s/enum :long :short)}) 61 | 62 | (defschema Portfolio 63 | {Coin Position}) 64 | 65 | (defschema Operation 66 | {:operation (s/enum [:buy :sell :cancel]) 67 | :type (s/enum :market :limit)}) 68 | 69 | (defschema Amount 70 | {:qty Pos}) 71 | 72 | (defschema ValidUntil 73 | {:valid-until DateTime}) 74 | 75 | (defschema Trade 76 | (merge 77 | Operation 78 | Pair 79 | Amount)) 80 | 81 | (defschema Order 82 | (merge 83 | Trade 84 | (m/map-keys optional-key Price) 85 | (m/map-keys optional-key ValidUntil))) 86 | 87 | (defschema Action 88 | Order) 89 | 90 | (defschema Fill 91 | (merge 92 | Trade 93 | Price)) 94 | 95 | (defschema Event 96 | (merge 97 | (m/map-keys optional-key Portfolio) 98 | {(optional-key :ticks) [Tick]} 99 | {(optional-key :orders) [Order]} 100 | {(optional-key :fills) [Fill]})) 101 | -------------------------------------------------------------------------------- /externals/plugin-connector-simulator/src/xicotrader/service/core.clj: -------------------------------------------------------------------------------- 1 | (ns xicotrader.service.core 2 | (:require 3 | [clojure.set :as set] 4 | [clojure.edn :as edn] 5 | [clojure.tools.logging :as log] 6 | [clojure.core.async :as a :refer [go-loop ! !!]] 7 | [clojure.core.async.impl.protocols :as async-protocols] 8 | [com.stuartsierra.component :as component] 9 | [clj-http.client :as http] 10 | [cheshire.core :as cheshire] 11 | [xicotrader.service :as service])) 12 | 13 | (defn get-user-data [{:keys [host port private-url user-id secret-key]}] 14 | (let [url (format "http://%s:%s/%s/%s" host port private-url "portfolio") 15 | response (http/get url 16 | {:query-params {:user-id user-id 17 | :secret-key secret-key}})] 18 | (cheshire/parse-string (:body response)))) 19 | 20 | (def ch (a/chan)) 21 | 22 | (defn parse-response [body] 23 | (cheshire/parse-string body keyword)) 24 | 25 | (defn get-tick [{:keys [host port public-url]} pair & [success-callback error-callback]] 26 | (let [url (format "http://%s:%s/%s/%s" host port public-url "tick") 27 | on-success (or success-callback (fn [response] (>!! ch response)))] 28 | (http/get url 29 | {:query-params {:pair pair} 30 | :async? true} 31 | (fn [response] 32 | (-> (:body response) parse-response on-success)) 33 | (fn [exeption])) 34 | (when-not success-callback 35 | ( (:body response) edn/read-string)] 46 | (if (empty? body) nil body))) 47 | 48 | (defn poll-loop [{:keys [ch-in]} config pairs] 49 | (go-loop [] 50 | (when-not (async-protocols/closed? ch-in) 51 | (doseq [pair pairs] 52 | (get-tick config ((config :translation) pair) 53 | (fn [response] 54 | (>!! ch-in {:tick-data (assoc response :pair pair) 55 | :portfolio-updates {}})))) 56 | (Thread/sleep (:polltime config)) 57 | (recur)))) 58 | 59 | (defn order-loop [{:keys [ch-out ch-in]} config] 60 | (go-loop [] 61 | (when-let [{:keys [pair] :as action} (! ch-in {:portfolio-updates (portfolio-updates (config :user-id))})) 67 | (recur)))) 68 | 69 | (defrecord Component [config] 70 | component/Lifecycle 71 | (start [this] 72 | (assoc this :ch-in (a/chan) 73 | :ch-out (a/chan))) 74 | (stop [this] 75 | (a/close! (:ch-in this)) 76 | (a/close! (:ch-out this)) 77 | this) 78 | 79 | service/Service 80 | (init [this trade-assets] 81 | (poll-loop this config trade-assets) 82 | (order-loop this config) 83 | (get-user-data config))) 84 | 85 | (defn new [config] 86 | (Component. config)) 87 | -------------------------------------------------------------------------------- /externals/simulator/src/xicotrader/webhandler.clj: -------------------------------------------------------------------------------- 1 | (ns xicotrader.webhandler 2 | (:require 3 | [clojure.string :as string] 4 | [clojure.java.io :as clj-io] 5 | [clojure.tools.logging :as log] 6 | [com.stuartsierra.component :as component] 7 | [ring.util.http-response :as http-response] 8 | [compojure.api.sweet :as compojure-api 9 | :refer [context defroutes GET PATCH POST PUT DELETE]] 10 | [ring.middleware 11 | [params :as params]] 12 | [modular.ring :refer [WebRequestHandler]] 13 | [ring.middleware.json :refer [wrap-json-response wrap-json-body]] 14 | [schema.core :as s] 15 | [cheshire.core :as cheshire] 16 | [xicotrader 17 | [public :as public] 18 | [private :as private]])) 19 | 20 | (defmacro with-validation [user-id secret-key & body] 21 | `(if (= (~private/user-secrets ~user-id) ~secret-key) 22 | (do ~@body) 23 | (http-response/forbidden))) 24 | 25 | (defmulti handler (fn [e] (type e))) 26 | 27 | (defmethod handler Exception [e] 28 | (http-response/internal-server-error)) 29 | 30 | (defn public-endpoint-pairs [] 31 | (http-response/ok 32 | (public/get-pairs))) 33 | 34 | (defn public-endpoint-get-tick [pair] 35 | (http-response/ok 36 | (cheshire/encode (public/get-tick pair)))) 37 | 38 | (defn private-endpoint-portfolio [user-id] 39 | (http-response/ok 40 | (private/get-portfolio user-id))) 41 | 42 | (defn private-endpoint-trade [user-id operation pair qty] 43 | (http-response/ok 44 | (or (private/trade! user-id operation pair qty) {}))) 45 | 46 | (compojure-api/defapi xicotrader-api 47 | {:swagger 48 | {:ui "/sw" 49 | :spec "/sw/docs/swagger.json" 50 | :data {:info {:title "Xicotrader Simulator"}}} 51 | :exceptions 52 | {:handlers {::compojure.api.exception/default #(handler %)}}} 53 | (context "/api/public" [] 54 | :tags ["Public API"] 55 | (GET "/pairs" [] 56 | :summary "Get consumer files" 57 | (public-endpoint-pairs)) 58 | (GET "/tick" [] 59 | :summary "Get last tick for a trading pair" 60 | :query-params [pair :- (apply s/enum (public/get-pairs))] 61 | (public-endpoint-get-tick pair))) 62 | (context "/api/private" [] 63 | :tags ["Private API"] 64 | (GET "/portfolio" [] 65 | :summary "Get user's current portfolio" 66 | :query-params [user-id :- s/Str 67 | secret-key :- s/Str] 68 | (with-validation user-id secret-key 69 | (private-endpoint-portfolio user-id))) 70 | (POST "/trade" [] 71 | :summary "Trade a pair" 72 | :query-params [user-id :- s/Str 73 | secret-key :- s/Str] 74 | :body-params [operation :- (s/enum :buy :sell) 75 | pair :- (apply s/enum (public/get-pairs)) 76 | qty :- Double] 77 | (with-validation 78 | user-id secret-key 79 | (private-endpoint-trade user-id operation pair qty))))) 80 | 81 | (defroutes app xicotrader-api) 82 | 83 | (defn- reloadably-attach-things [h cmpnt req] 84 | (h (merge req cmpnt {:consumer-input-io (-> cmpnt :consumer :task :input-io)}))) 85 | 86 | (defn- wrap-request-with-component 87 | "Merge all the state in the component into the request for easy access." 88 | [handler cmpnt] 89 | (fn [req] 90 | (reloadably-attach-things handler cmpnt req))) 91 | 92 | (defn- build-request-handler [this] 93 | (-> ;;api 94 | app 95 | (wrap-request-with-component this) 96 | wrap-json-response 97 | (params/wrap-params))) 98 | 99 | (defrecord WebHandler [] 100 | WebRequestHandler 101 | (request-handler [this] 102 | (build-request-handler this))) 103 | 104 | (defn new [] 105 | (WebHandler.)) 106 | -------------------------------------------------------------------------------- /arbitrage/src/xicotrader/arbitrage/core.clj: -------------------------------------------------------------------------------- 1 | (ns xicotrader.arbitrage.core 2 | (:require 3 | [clojure.set :as set] 4 | [clojure.walk :as walk] 5 | [com.stuartsierra.component :as component] 6 | [xicotrader.strategy :as strategy] 7 | [xicotrader.arbitrage.util :as util :refer [print-trade]]) 8 | (:gen-class)) 9 | 10 | 11 | (def currently-trading? (atom false)) 12 | 13 | (defn do-when-previous-action-filled [portfolio-updates] 14 | (when (seq portfolio-updates) 15 | (reset! currently-trading? false))) 16 | 17 | (defn cycle-usd [portfolio {:keys [last-ethusd last-btcusd last-ethbtc]}] 18 | (-> (get-in portfolio ["USD"]) 19 | (/ last-ethusd) 20 | (* last-ethbtc) 21 | (* last-btcusd))) 22 | 23 | (defn cycle-eth [portfolio {:keys [last-ethusd last-btcusd last-ethbtc]}] 24 | (-> (get-in portfolio ["ETH"]) 25 | (* last-ethbtc) 26 | (* last-btcusd) 27 | (/ last-ethusd))) 28 | 29 | (defn cycle-btc [portfolio {:keys [last-ethusd last-btcusd last-ethbtc]}] 30 | (-> (get-in portfolio ["BTC"]) 31 | (* last-btcusd) 32 | (/ last-ethusd) 33 | (* last-ethbtc))) 34 | 35 | (defn get-pair-to-trade [max-usdethbtcusd max-ethbtcusdeth max-btcusdethbtc 36 | {:keys [last-ethusd last-btcusd last-ethbtc]}] 37 | (let [pair (key (apply max-key val 38 | {"ETHUSD" max-usdethbtcusd 39 | "ETHBTC" (* max-ethbtcusdeth last-ethusd) 40 | "BTCUSD" (* max-btcusdethbtc last-btcusd)}))] 41 | [pair (if (= pair "ETHUSD") :buy :sell)])) 42 | 43 | (defn get-qty [portfolio {:keys [last-ethusd last-btcusd last-ethbtc]} what-to-buy] 44 | (case what-to-buy 45 | "ETHUSD" (/ (portfolio "USD") last-ethusd) 46 | "ETHBTC" (* (portfolio "ETH") last-ethbtc) 47 | "BTCUSD" (* (portfolio "BTC") last-btcusd))) 48 | 49 | (defn get-source-asset [what-to-buy] 50 | ({"ETHUSD" "USD" 51 | "ETHBTC" "ETH" 52 | "BTCUSD" "BTC"} what-to-buy)) 53 | 54 | (defn do-arbitrage [portfolio market tick-data] 55 | (when (or (not @currently-trading?) (seq tick-data)) 56 | (let [k-market (-> (set/rename-keys market {"ETHUSD" "last-ethusd" 57 | "ETHBTC" "last-ethbtc" 58 | "BTCUSD" "last-btcusd"}) 59 | (walk/keywordize-keys) 60 | (select-keys [:last-ethusd :last-ethbtc :last-btcusd])) 61 | all-exist? (fn [[last-ethusd last-btcusd last-ethbtc]] 62 | (and last-ethusd last-btcusd last-ethbtc))] 63 | (when (all-exist? (vals k-market)) 64 | (let [[max-usdethbtcusd 65 | max-ethbtcusdeth 66 | max-btcusdethbtc] ((juxt (partial cycle-usd portfolio) 67 | (partial cycle-eth portfolio) 68 | (partial cycle-btc portfolio)) k-market)] 69 | (when (or (> max-usdethbtcusd (portfolio "USD")) 70 | (> max-ethbtcusdeth (portfolio "ETH")) 71 | (> max-btcusdethbtc (portfolio "BTC"))) 72 | (let [[what-to-trade 73 | operation] (get-pair-to-trade max-usdethbtcusd max-ethbtcusdeth 74 | max-btcusdethbtc k-market) 75 | qty (get-qty portfolio k-market what-to-trade) 76 | what-to-spend (get-source-asset what-to-trade) 77 | buy? (= :buy operation) 78 | last-price (market what-to-trade) 79 | last-price (if buy? last-price (/ 1 last-price)) 80 | source-funds (* last-price qty)] 81 | (when-not @currently-trading? 82 | (print-trade buy? what-to-trade what-to-spend qty source-funds last-price)) 83 | (reset! currently-trading? true) 84 | {:operation operation 85 | :pair what-to-trade 86 | :qty qty}))))))) 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # xicotrader 2 | 3 | [![CircleCI](https://circleci.com/gh/analyticbastard/xicotrader.svg?style=shield)](https://circleci.com/gh/analyticbastard/xicotrader) 4 | [![Deps](https://versions.deps.co/analyticbastard/xicotrader/status.svg)](https://versions.deps.co/analyticbastard/xicotrader) 5 | [![codecov](https://codecov.io/gh/analyticbastard/xicotrader/branch/master/graph/badge.svg)](https://codecov.io/gh/analyticbastard/xicotrader) 6 | 7 | Automatically trade cryptocurrency: a growing ex-academic example. 8 | 9 | ## Current state 10 | 11 | The software currently consists of a automatic trading client, and a simulator that mocks 12 | a data and order service. 13 | 14 | The system loads the strategy from an external JAR file using [pomegranate](https://github.com/cemerick/pomegranate), which 15 | must implement a Clojure protocol (Java interface) defined in this project. This 16 | protocol is the entry point to the strategy, which is fed with data as this trading 17 | client passes it on, along with the current portfolio state. 18 | 19 | ### Example of a trading strategy 20 | 21 | A simple arbitrage strategy is implemented in the subproject folder `arbitrage` 22 | to illustrate the automatic trading. 23 | Upon each tick and current state, the strategy selects the best assets to cycle 24 | from USD, BTC and ETH. 25 | 26 | You need to build this project (see instructions within) and place the resulting 27 | JAR file anywhere in the classpath **TBD**, so that the project can load up the classes within the JAR. 28 | 29 | ## Usage 30 | 31 | ### Grab some data 32 | 33 | The simulator understands the Poloniex API format and assumes a 5 min inter-tick period, e.g., 34 | 35 | ``` 36 | https://poloniex.com/public?command=returnChartData¤cyPair=BTC_ETH&end=9999999999&period=300&start=1405699200 37 | ``` 38 | 39 | Under resources, create a directory called ```marketdata``` and place your 40 | three market data files called ```btcusd.json```, ```ethbtc.json``` and 41 | ```ethusd.json```. 42 | 43 | Download the data from all three pairs and store them in files with the aforementioned names, e.g., 44 | 45 | ``` 46 | curl https://poloniex.com/public?command=returnChartData¤cyPair=BTC_ETH&end=9999999999&period=300&start=1405699200 > ethbtc.json 47 | curl https://poloniex.com/public?command=returnChartData¤cyPair=USDT_BTC&end=9999999999&period=300&start=1405699200 > btcusd.json 48 | curl https://poloniex.com/public?command=returnChartData¤cyPair=USDT_ETH&end=9999999999&period=300&start=1405699200 > ethusd.json 49 | ``` 50 | 51 | This is an example intended to use with a couple of REPLs. 52 | 53 | ```bash 54 | lein with-profile simulator,dev repl 55 | ``` 56 | 57 | On another terminal 58 | 59 | ```bash 60 | lein with-profile client,dev repl 61 | ``` 62 | 63 | Then issue ```(start)``` on each of the REPLs, starting with the simulator. 64 | 65 | The simulator runs so that every 10 minutes is scaled down to 1 second, so you 66 | can see things very quickly. 67 | 68 | The simulator gets the first data not already in the past according to the simulator 69 | time. So things are expected not to exactly align with a real situation. 70 | 71 | 72 | ### Use in real trading 73 | 74 | The software is simple so the basic components should be robust. However the 75 | software comes as is and the author offers no warranty. 76 | 77 | For a real situation, a service that implements connectivity with your exchange 78 | can be implemented and added to the system dependencies in sustitution of the 79 | component that implements connectivity with the simulator. 80 | 81 | ### Building 82 | 83 | ```bash 84 | lein uberjar 85 | ``` 86 | 87 | ### Testing 88 | 89 | ```bash 90 | lein test 91 | ``` 92 | 93 | ### Code coverage 94 | 95 | ```bash 96 | CLOVERAGE_VERSION=1.0.7-SNAPSHOT lein cloverage --codecov 97 | ``` 98 | 99 | ## Architecture 100 | 101 | The software uses Stuart Sierra's [component](https://github.com/stuartsierra/component) library 102 | and each component feeds data into the system and receives actions from the inner components that 103 | are processed and passed on outwards. This component communication is done with 104 | [core async](https://github.com/clojure/core.async). 105 | 106 | The system is very simple and consists of only a small number of components 107 | 108 | ``` 109 | +------------+ +------------+c-from s-to+------------+ 110 | | | | <-----------+ | 111 | | | compute | | | | 112 | | Strategy +-----------+ Strategy | | Controller | 113 | | impl | | holder | | | 114 | | | | | s-from| | 115 | | | | +-----------> | 116 | +------------+ +------------+c-to +--+------^--+ 117 | send| |rec 118 | | | 119 | | | 120 | | | 121 | +------------+ | | +------------+ 122 | | | | | | | 123 | | Service <--+ +--+ Service | 124 | | sender |con con| receiver | 125 | | | | | 126 | | | | | 127 | | | | | 128 | +-----+------+ +------+-----+ 129 | |send | 130 | | | 131 | | +------------+ | 132 | | | | | 133 | | | | | 134 | +------+ connector +------+ 135 | | impl |receive 136 | | | 137 | | | 138 | +------------+ 139 | ``` 140 | 141 | When the system is initialized from the configuration files, it loads up the strategy implementation 142 | and the service connector implementations from external JARs using [pomegranate](https://github.com/cemerick/pomegranate). 143 | 144 | The service connector implementation is expected to connect to an exchange (with parameters potentially 145 | passed as configuration from files) and start receiving data. It will then call a method from the 146 | recevier (whose reference has been passed on at init time in the form of an interface called 147 | and injected by a dependency). The receiver will then 148 | asynchronously send the data to the Controller, which oversees that everything is well (alarms and monitoring 149 | will go here) and passes it on to the strategy holder, which has a reference to the externally 150 | loaded strategy implementation. It will call it with the data feed (which includes tick data 151 | and potentially the most up to date portfolio according to what the exchange has sent). The strategy will then 152 | compute a number of actions, one per coin trading pair, and sent it all the way back. 153 | The service sender will synchronously call the service connector with the actions to be processed. 154 | 155 | Notice that the asynchronicity is dealt with in the internal components (strategy holder, 156 | sender and receiver), thus sparing the implementations 157 | from dealing with asynchronicity or core.async details. 158 | 159 | The names of the endpoints in the diagram are the names of the core.async channels and the 160 | interface methods. 161 | 162 | ### TODO 163 | 164 | Immediate roadmap: 165 | 166 | - ~~I/O schema for the ticker and trades service and for the strategy~~ 167 | - Loading system for independent ticker and trades service like the one for strategies and 168 | separate the code for the simulator service into an independent project generating its own JAR 169 | - Carry over these changes to the arbitrage example strategy 170 | - Improve code coverage 171 | - Increase number of log messages 172 | - Build strategy and data feed service JARs and use them in tests 173 | 174 | Future roadmap: 175 | 176 | - Binance ticker and trades service (as a separate JAR) 177 | - Dockerization 178 | - Clojurescript interface 179 | 180 | ## License 181 | 182 | Copyright © 2017 Javier Arriero 183 | 184 | Distributed under the Eclipse Public License either version 1.0 or (at 185 | your option) any later version. 186 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Eclipse Public License - v 1.0 2 | 3 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 4 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 5 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 6 | 7 | 1. DEFINITIONS 8 | 9 | "Contribution" means: 10 | 11 | a) in the case of the initial Contributor, the initial code and documentation 12 | distributed under this Agreement, and 13 | b) in the case of each subsequent Contributor: 14 | i) changes to the Program, and 15 | ii) additions to the Program; 16 | 17 | where such changes and/or additions to the Program originate from and are 18 | distributed by that particular Contributor. A Contribution 'originates' 19 | from a Contributor if it was added to the Program by such Contributor 20 | itself or anyone acting on such Contributor's behalf. Contributions do not 21 | include additions to the Program which: (i) are separate modules of 22 | software distributed in conjunction with the Program under their own 23 | license agreement, and (ii) are not derivative works of the Program. 24 | 25 | "Contributor" means any person or entity that distributes the Program. 26 | 27 | "Licensed Patents" mean patent claims licensable by a Contributor which are 28 | necessarily infringed by the use or sale of its Contribution alone or when 29 | combined with the Program. 30 | 31 | "Program" means the Contributions distributed in accordance with this 32 | Agreement. 33 | 34 | "Recipient" means anyone who receives the Program under this Agreement, 35 | including all Contributors. 36 | 37 | 2. GRANT OF RIGHTS 38 | a) Subject to the terms of this Agreement, each Contributor hereby grants 39 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 40 | reproduce, prepare derivative works of, publicly display, publicly 41 | perform, distribute and sublicense the Contribution of such Contributor, 42 | if any, and such derivative works, in source code and object code form. 43 | b) Subject to the terms of this Agreement, each Contributor hereby grants 44 | Recipient a non-exclusive, worldwide, royalty-free patent license under 45 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 46 | transfer the Contribution of such Contributor, if any, in source code and 47 | object code form. This patent license shall apply to the combination of 48 | the Contribution and the Program if, at the time the Contribution is 49 | added by the Contributor, such addition of the Contribution causes such 50 | combination to be covered by the Licensed Patents. The patent license 51 | shall not apply to any other combinations which include the Contribution. 52 | No hardware per se is licensed hereunder. 53 | c) Recipient understands that although each Contributor grants the licenses 54 | to its Contributions set forth herein, no assurances are provided by any 55 | Contributor that the Program does not infringe the patent or other 56 | intellectual property rights of any other entity. Each Contributor 57 | disclaims any liability to Recipient for claims brought by any other 58 | entity based on infringement of intellectual property rights or 59 | otherwise. As a condition to exercising the rights and licenses granted 60 | hereunder, each Recipient hereby assumes sole responsibility to secure 61 | any other intellectual property rights needed, if any. For example, if a 62 | third party patent license is required to allow Recipient to distribute 63 | the Program, it is Recipient's responsibility to acquire that license 64 | before distributing the Program. 65 | d) Each Contributor represents that to its knowledge it has sufficient 66 | copyright rights in its Contribution, if any, to grant the copyright 67 | license set forth in this Agreement. 68 | 69 | 3. REQUIREMENTS 70 | 71 | A Contributor may choose to distribute the Program in object code form under 72 | its own license agreement, provided that: 73 | 74 | a) it complies with the terms and conditions of this Agreement; and 75 | b) its license agreement: 76 | i) effectively disclaims on behalf of all Contributors all warranties 77 | and conditions, express and implied, including warranties or 78 | conditions of title and non-infringement, and implied warranties or 79 | conditions of merchantability and fitness for a particular purpose; 80 | ii) effectively excludes on behalf of all Contributors all liability for 81 | damages, including direct, indirect, special, incidental and 82 | consequential damages, such as lost profits; 83 | iii) states that any provisions which differ from this Agreement are 84 | offered by that Contributor alone and not by any other party; and 85 | iv) states that source code for the Program is available from such 86 | Contributor, and informs licensees how to obtain it in a reasonable 87 | manner on or through a medium customarily used for software exchange. 88 | 89 | When the Program is made available in source code form: 90 | 91 | a) it must be made available under this Agreement; and 92 | b) a copy of this Agreement must be included with each copy of the Program. 93 | Contributors may not remove or alter any copyright notices contained 94 | within the Program. 95 | 96 | Each Contributor must identify itself as the originator of its Contribution, 97 | if 98 | any, in a manner that reasonably allows subsequent Recipients to identify the 99 | originator of the Contribution. 100 | 101 | 4. COMMERCIAL DISTRIBUTION 102 | 103 | Commercial distributors of software may accept certain responsibilities with 104 | respect to end users, business partners and the like. While this license is 105 | intended to facilitate the commercial use of the Program, the Contributor who 106 | includes the Program in a commercial product offering should do so in a manner 107 | which does not create potential liability for other Contributors. Therefore, 108 | if a Contributor includes the Program in a commercial product offering, such 109 | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify 110 | every other Contributor ("Indemnified Contributor") against any losses, 111 | damages and costs (collectively "Losses") arising from claims, lawsuits and 112 | other legal actions brought by a third party against the Indemnified 113 | Contributor to the extent caused by the acts or omissions of such Commercial 114 | Contributor in connection with its distribution of the Program in a commercial 115 | product offering. The obligations in this section do not apply to any claims 116 | or Losses relating to any actual or alleged intellectual property 117 | infringement. In order to qualify, an Indemnified Contributor must: 118 | a) promptly notify the Commercial Contributor in writing of such claim, and 119 | b) allow the Commercial Contributor to control, and cooperate with the 120 | Commercial Contributor in, the defense and any related settlement 121 | negotiations. The Indemnified Contributor may participate in any such claim at 122 | its own expense. 123 | 124 | For example, a Contributor might include the Program in a commercial product 125 | offering, Product X. That Contributor is then a Commercial Contributor. If 126 | that Commercial Contributor then makes performance claims, or offers 127 | warranties related to Product X, those performance claims and warranties are 128 | such Commercial Contributor's responsibility alone. Under this section, the 129 | Commercial Contributor would have to defend claims against the other 130 | Contributors related to those performance claims and warranties, and if a 131 | court requires any other Contributor to pay any damages as a result, the 132 | Commercial Contributor must pay those damages. 133 | 134 | 5. NO WARRANTY 135 | 136 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 137 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 138 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 140 | Recipient is solely responsible for determining the appropriateness of using 141 | and distributing the Program and assumes all risks associated with its 142 | exercise of rights under this Agreement , including but not limited to the 143 | risks and costs of program errors, compliance with applicable laws, damage to 144 | or loss of data, programs or equipment, and unavailability or interruption of 145 | operations. 146 | 147 | 6. DISCLAIMER OF LIABILITY 148 | 149 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 150 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 151 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 152 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 153 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 154 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 155 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 156 | OF SUCH DAMAGES. 157 | 158 | 7. GENERAL 159 | 160 | If any provision of this Agreement is invalid or unenforceable under 161 | applicable law, it shall not affect the validity or enforceability of the 162 | remainder of the terms of this Agreement, and without further action by the 163 | parties hereto, such provision shall be reformed to the minimum extent 164 | necessary to make such provision valid and enforceable. 165 | 166 | If Recipient institutes patent litigation against any entity (including a 167 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 168 | (excluding combinations of the Program with other software or hardware) 169 | infringes such Recipient's patent(s), then such Recipient's rights granted 170 | under Section 2(b) shall terminate as of the date such litigation is filed. 171 | 172 | All Recipient's rights under this Agreement shall terminate if it fails to 173 | comply with any of the material terms or conditions of this Agreement and does 174 | not cure such failure in a reasonable period of time after becoming aware of 175 | such noncompliance. If all Recipient's rights under this Agreement terminate, 176 | Recipient agrees to cease use and distribution of the Program as soon as 177 | reasonably practicable. However, Recipient's obligations under this Agreement 178 | and any licenses granted by Recipient relating to the Program shall continue 179 | and survive. 180 | 181 | Everyone is permitted to copy and distribute copies of this Agreement, but in 182 | order to avoid inconsistency the Agreement is copyrighted and may only be 183 | modified in the following manner. The Agreement Steward reserves the right to 184 | publish new versions (including revisions) of this Agreement from time to 185 | time. No one other than the Agreement Steward has the right to modify this 186 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 187 | Eclipse Foundation may assign the responsibility to serve as the Agreement 188 | Steward to a suitable separate entity. Each new version of the Agreement will 189 | be given a distinguishing version number. The Program (including 190 | Contributions) may always be distributed subject to the version of the 191 | Agreement under which it was received. In addition, after a new version of the 192 | Agreement is published, Contributor may elect to distribute the Program 193 | (including its Contributions) under the new version. Except as expressly 194 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 195 | licenses to the intellectual property of any Contributor under this Agreement, 196 | whether expressly, by implication, estoppel or otherwise. All rights in the 197 | Program not expressly granted under this Agreement are reserved. 198 | 199 | This Agreement is governed by the laws of the State of New York and the 200 | intellectual property laws of the United States of America. No party to this 201 | Agreement will bring a legal action under this Agreement more than one year 202 | after the cause of action arose. Each party waives its rights to a jury trial in 203 | any resulting litigation. 204 | --------------------------------------------------------------------------------