diff --git a/lib/trade/src/quanta/quote/random.clj b/lib/trade/src/quanta/quote/random.clj index 9292d6ad..9fb1eea9 100644 --- a/lib/trade/src/quanta/quote/random.clj +++ b/lib/trade/src/quanta/quote/random.clj @@ -5,7 +5,9 @@ (:import [missionary Cancelled])) (defn initial-price [] - (rand 10000)) + ;(rand 10000) + 10000.0 + ) (defn update-price [p] (let [i (rand-int 5)] diff --git a/lib/trade/src/quanta/trade/position.clj b/lib/trade/src/quanta/trade/position.clj index 4b02f739..e4f73062 100644 --- a/lib/trade/src/quanta/trade/position.clj +++ b/lib/trade/src/quanta/trade/position.clj @@ -16,37 +16,9 @@ (order->position order) (error order))) - (defn ExitSignal - "returns a missionary task. - task will eventually return either of :time :profit :loss" - [algo-opts position] - (let [exit-time (get-exit-time algo-opts (:entry-date position)) - exit-tasks (->> [(when exit-time - (time-trigger exit-time))] - (remove nil?))] - (apply m/race exit-tasks))) + (defn ExitPosition [algo-opts position] ;(M/ap (m/?> ) - (comment - (require '[tick.core :as t]) - (def algo-opts {:calendar [:crypto :m] - :exit [:time 2]}) - (def position {:entry-date (t/instant)}) - - (m/? (ExitSignal algo-opts position)) - ;; => :time - - ;; this one has a position that is older and older, so - ;; it might be that this task returns immediately, because - ;; the current time is already below the time of the exit-date - - (m/? (ExitSignal algo-opts {:entry-date (t/instant)})) - ;; => :time - - - - - ; - ) + diff --git a/lib/trade/src/quanta/trade/position/size.clj b/lib/trade/src/quanta/trade/position/entry/size.clj similarity index 85% rename from lib/trade/src/quanta/trade/position/size.clj rename to lib/trade/src/quanta/trade/position/entry/size.clj index f2da437f..7d72c9de 100644 --- a/lib/trade/src/quanta/trade/position/size.clj +++ b/lib/trade/src/quanta/trade/position/entry/size.clj @@ -1,4 +1,4 @@ -(ns quanta.trade.position.size) +(ns quanta.trade.position.entry.size) (defmulti positionsize diff --git a/lib/trade/src/quanta/trade/position/exit/price.clj b/lib/trade/src/quanta/trade/position/exit/price.clj index 237c7efc..756b62dd 100644 --- a/lib/trade/src/quanta/trade/position/exit/price.clj +++ b/lib/trade/src/quanta/trade/position/exit/price.clj @@ -1,29 +1,54 @@ (ns quanta.trade.position.exit.price (:require - [tick.core :as t] - [missionary.core :as m] - [quanta.trade.position.exit :refer [get-exit-rule]])) + [missionary.core :as m] + [quanta.trade.position.exit.rule :refer [get-exit-rule]] + [quanta.trade.position.working :refer [working-position]])) +(defn trailing-return [position] + (let [working-position (working-position position)] + (m/latest :ret-prct working-position))) +(defn profit-trigger + "returns a missionary task that emits :profit when target is met. + if no profit-percent exit-rule is specified it returns nil + if not task continues running." + [algo-opts position] + (let [[_ target] (get-exit-rule algo-opts :profit-percent)] + (when target + (let [prct (trailing-return position) + rf (fn [_ tp] + (println "trailing profit " (:asset position) ": " tp " target: " target) + (when (> tp target) + (reduced :profit)))] + (m/reduce rf nil prct))))) +(defn loss-trigger + "returns a missionary task that emits :loss when target is met. + if no :loss-percent exit-rule is specified it returns nil. + if not task continues running." + [algo-opts position] + (let [[_ target] (get-exit-rule algo-opts :loss-percent)] + (when target + (let [prct (trailing-return position) + rf (fn [_ tp] + (println "trailing loss " (:asset position) ": " tp " target: " target) + (when (< tp (- 0.0 target)) + (reduced :loss)))] + (m/reduce rf nil prct))))) + (comment + (def algo-opts {:asset "BTCUSDT" + :exit [:profit-percent 1.0]}) -(defn get-exit-profit [algo-opts position] - (let [{:keys [calendar]} algo-opts - [exchange-kw interval-kw] calendar - bars (get-time-bars algo-opts)] - (when bars - (let [cal-seq (calendar-seq exchange-kw interval-kw entry-date) - window (take bars cal-seq)] - #_{:start (first window) - :end (last window)} - (last window))))) + (def position + {:asset "BTCUSDT" + :qty 500 + :entry-price 10000.0}) + + (m/? (profit-trigger algo-opts position)) + +; + ) -(defn profit-trigger [exit-time] - (let [exit-long (-> exit-time t/instant t/long) - now-long (-> t/instant t/long) - diff-ms (* 1000 (- exit-long now-long)) - diff-ms (max diff-ms 1)] - (m/sleep diff-ms :time))) \ No newline at end of file diff --git a/lib/trade/src/quanta/trade/position/exit.clj b/lib/trade/src/quanta/trade/position/exit/rule.clj similarity index 95% rename from lib/trade/src/quanta/trade/position/exit.clj rename to lib/trade/src/quanta/trade/position/exit/rule.clj index a4c2ea0a..232e18c0 100644 --- a/lib/trade/src/quanta/trade/position/exit.clj +++ b/lib/trade/src/quanta/trade/position/exit/rule.clj @@ -1,4 +1,4 @@ -(ns quanta.trade.position.exit) +(ns quanta.trade.position.exit.rule) (defn get-exit-rule [algo-opts rule-kw] (let [{:keys [exit]} algo-opts diff --git a/lib/trade/src/quanta/trade/position/exit/signal.clj b/lib/trade/src/quanta/trade/position/exit/signal.clj new file mode 100644 index 00000000..c1dd8031 --- /dev/null +++ b/lib/trade/src/quanta/trade/position/exit/signal.clj @@ -0,0 +1,46 @@ +(ns quanta.trade.position.exit.signal + (:require + [missionary.core :as m] + [quanta.trade.position.exit.time :refer [get-exit-time time-trigger]] + [quanta.trade.position.exit.price :refer [profit-trigger loss-trigger]])) + + + (defn exit-signal + "returns a missionary task. + task will eventually return either of :time :profit :loss" + [algo-opts position] + (let [exit-time (get-exit-time algo-opts (:entry-date position)) + exit-tasks (->> [(profit-trigger algo-opts position) + (loss-trigger algo-opts position) + (when exit-time + (time-trigger exit-time))] + (remove nil?))] + (apply m/race exit-tasks))) + + + (comment + + (require '[tick.core :as t]) + (def algo-opts {:calendar [:crypto :m] + :exit [:loss-percent 2.0 + :profit-percent 100.0 + :time 2]}) + (def position {:asset "BTCUSDT" + :side :long + :entry-date (t/instant) + :entry-price 10000.0 + :qty 0.1}) + + + (m/? (exit-signal algo-opts position)) + ;; => :time or :profit or :loss + + ;; this one has a position that is older and older, so + ;; it might be that this task returns immediately, because + ;; the current time is already below the time of the exit-date + + (m/? (exit-signal algo-opts {:entry-date (t/instant)})) + ;; => :time + + ; + ) \ No newline at end of file diff --git a/lib/trade/src/quanta/trade/position/exit/time.clj b/lib/trade/src/quanta/trade/position/exit/time.clj index 934a5258..7cbcfc7d 100644 --- a/lib/trade/src/quanta/trade/position/exit/time.clj +++ b/lib/trade/src/quanta/trade/position/exit/time.clj @@ -3,7 +3,7 @@ [tick.core :as t] [missionary.core :as m] [ta.calendar.core :refer [calendar-seq]] - [quanta.trade.position.exit :refer [get-exit-rule]] + [quanta.trade.position.exit.rule :refer [get-exit-rule]] )) (defn get-time-bars [algo-opts] diff --git a/lib/trade/src/quanta/trade/position/working.clj b/lib/trade/src/quanta/trade/position/working.clj index 76d6eb1d..0e6b48e1 100644 --- a/lib/trade/src/quanta/trade/position/working.clj +++ b/lib/trade/src/quanta/trade/position/working.clj @@ -21,7 +21,7 @@ :win? (> abs-ret 0.0)) roundtrip))) -(defn calculate-position [position] +(defn working-position [position] (m/ap ; startup (println "start calculating position: " position) @@ -33,7 +33,7 @@ (comment (require '[tick.core :as t]) (m/? (m/reduce println - (calculate-position {:asset "BTCUSDT" + (working-position {:asset "BTCUSDT" :qty 500 :entry-price 1000.0 :entry-date (t/instant)}))) @@ -67,7 +67,7 @@ :ret-prct :win?] positions)) - (let [flows (map calculate-position positions)] + (let [flows (map working-position positions)] (m/? (m/reduce (constantly nil) (apply m/latest print-positions flows)))) diff --git a/lib/trade/src/ta/trade/backtest/entry.clj b/lib/trade/src/ta/trade/backtest/entry.clj index 69d552b6..d07a8016 100644 --- a/lib/trade/src/ta/trade/backtest/entry.clj +++ b/lib/trade/src/ta/trade/backtest/entry.clj @@ -1,6 +1,6 @@ (ns ta.trade.backtest.entry (:require - [quanta.trade.position.size :refer [positionsize]] + [quanta.trade.position.entry.size :refer [positionsize]] ) )