diff --git a/lib/algo/src/ta/algo/spec/type/bar_strategy.clj b/lib/algo/src/ta/algo/spec/type/bar_strategy.clj index 13c09211..8d9eac07 100644 --- a/lib/algo/src/ta/algo/spec/type/bar_strategy.clj +++ b/lib/algo/src/ta/algo/spec/type/bar_strategy.clj @@ -47,9 +47,8 @@ (assert algo) (let [algo-fn (chain/make-chain algo) load-fn (create-trailing-bar-loader spec)] - (assert algo-fn) + (assert algo-fn (str "could not compile algo-fn: " algo)) (fn [env _spec time] - ;(println "calculating barstrategy for time: " time) (when time (let [ds-bars (load-fn env spec time)] (if (nom/anomaly? ds-bars) diff --git a/lib/calendar/src/ta/calendar/align.clj b/lib/calendar/src/ta/calendar/align.clj index dd8cd2fb..581ceec4 100644 --- a/lib/calendar/src/ta/calendar/align.clj +++ b/lib/calendar/src/ta/calendar/align.clj @@ -1,6 +1,5 @@ (ns ta.calendar.align (:require - [taoensso.timbre :refer [trace debug info warn error]] [tablecloth.api :as tc] [tech.v3.datatype.argops :as argops] [tech.v3.tensor :as dtt])) diff --git a/lib/db/resources/quanta/notebook/bardb_duck_delete.clj b/lib/db/resources/quanta/notebook/bardb_duck_delete.clj new file mode 100644 index 00000000..a55c1a99 --- /dev/null +++ b/lib/db/resources/quanta/notebook/bardb_duck_delete.clj @@ -0,0 +1,17 @@ +(ns quanta.notebook.bardb-duck-delete + (:require + [tick.core :as t] + [tablecloth.api :as tc] + [ta.db.bars.protocol :as b] + [ta.db.bars.duckdb :as duck] + [ta.db.bars.dynamic.overview-db :refer [remove-asset]] + [modular.system])) + +(def ddb (modular.system/system :bardb-dynamic)) + +(def db (modular.system/system :duckdb)) + + +(duck/delete-bars db [:crypto :d] "ETHUSDT") + +(remove-asset (:overview-db ddb) {:asset "ETHUSDT" :calendar [:crypto :d]}) \ No newline at end of file diff --git a/lib/db/resources/quanta/notebook/bardb_duck.clj b/lib/db/resources/quanta/notebook/docs/bardb_duck.clj similarity index 97% rename from lib/db/resources/quanta/notebook/bardb_duck.clj rename to lib/db/resources/quanta/notebook/docs/bardb_duck.clj index ef731c12..e54f65ae 100644 --- a/lib/db/resources/quanta/notebook/bardb_duck.clj +++ b/lib/db/resources/quanta/notebook/docs/bardb_duck.clj @@ -1,4 +1,4 @@ -(ns quanta.notebook.bardb-duck +(ns quanta.notebook.docs.bardb-duck (:require [tick.core :as t] [tablecloth.api :as tc] diff --git a/lib/db/src/ta/db/bars/multi_asset.clj b/lib/db/src/ta/db/bars/multi_asset.clj index 77aba6a6..ccca2daf 100644 --- a/lib/db/src/ta/db/bars/multi_asset.clj +++ b/lib/db/src/ta/db/bars/multi_asset.clj @@ -1,5 +1,6 @@ (ns ta.db.bars.multi-asset (:require + [taoensso.timbre :as timbre :refer [debug info warn error]] [de.otto.nom.core :as nom] [tick.core :as t] [tablecloth.api :as tc] @@ -16,24 +17,37 @@ (defn- align-to-calendar2 [ds-bars ds-cal] (align/align-to-calendar ds-cal ds-bars)) +(defn- debug-ds [ds] + (info "aligned ds: " ds) + ds) + (defn load-aligned [bardb {:keys [asset] :as opts} window ds-cal] (nom/let-nom> [ds-bars (b/get-bars bardb opts window)] (-> ds-bars (tc/set-dataset-name asset) + ;debug-ds (align-to-calendar2 ds-cal) + ;debug-ds (align/fill-missing-close) (tc/add-column :asset asset)))) (defn load-aligned-assets [bardb opts assets cal-seq] - (let [window (calendar-seq->window cal-seq) - date-ds (calendar-seq->date-ds cal-seq) - load-asset (fn [asset] - [asset (load-aligned bardb (assoc opts :asset asset) window date-ds)])] - (->> (map load-asset assets) - (into {})))) + (try + (let [window (calendar-seq->window cal-seq) + date-ds (calendar-seq->date-ds cal-seq) + load-asset (fn [asset] + [asset (load-aligned bardb (assoc opts :asset asset) window date-ds)])] + (->> (map load-asset assets) + (into {}))) + (catch Exception ex + (error "exception: " ex) + (nom/fail ::load-aligned-bars {})))) (defn is-valid? [result asset] - (-> result (get asset) nom/anomaly? not)) + (if (nom/anomaly? result) + false + (let [ds (get result asset)] + (-> ds nom/anomaly? not)))) (comment (require '[modular.system]) diff --git a/lib/helper/src/ta/helper/date.clj b/lib/helper/src/ta/helper/date.clj index 674416c1..37427914 100644 --- a/lib/helper/src/ta/helper/date.clj +++ b/lib/helper/src/ta/helper/date.clj @@ -198,6 +198,10 @@ (subtract-days n) ;(t/date) )) +(defn fmt-yyyymmdd [dt] + (if dt + (t/format (t/formatter "YYYY-MM-dd") (t/zoned-date-time dt)) + "")) ; ***************************************************************************** (comment @@ -301,5 +305,8 @@ ;java.time.LocalDateTime + (-> (t/instant) + (fmt-yyyymmdd)) + ; ) diff --git a/lib/import/resources/quanta/notebook/docs/import_bardb_dynamic.clj b/lib/import/resources/quanta/notebook/docs/import_bardb_dynamic.clj new file mode 100644 index 00000000..a0654e8d --- /dev/null +++ b/lib/import/resources/quanta/notebook/docs/import_bardb_dynamic.clj @@ -0,0 +1,25 @@ +(ns quanta.notebook.docs.import-bardb-dynamic + (:require + [tick.core :as t] + [modular.system] + [ta.calendar.core :as cal] + [ta.db.bars.protocol :as b])) + +(def db (modular.system/system :bardb-dynamic)) + +(def window-daily + (cal/trailing-range [:us :d] 200 + (t/zoned-date-time "2024-05-02T17:30-05:00[America/New_York]"))) + +window-daily + + +(b/get-bars db {:asset "AEE.AU" + :calendar [:us :d] + :import :eodhd} + window-daily) + +(b/get-bars db {:asset "BTCUSDT" + :calendar [:us :d] + :import :bybit} + window-daily) \ No newline at end of file diff --git a/lib/import/resources/quanta/notebook/import_manager.clj b/lib/import/resources/quanta/notebook/docs/import_manager.clj similarity index 73% rename from lib/import/resources/quanta/notebook/import_manager.clj rename to lib/import/resources/quanta/notebook/docs/import_manager.clj index 24dbadea..7a3679d6 100644 --- a/lib/import/resources/quanta/notebook/import_manager.clj +++ b/lib/import/resources/quanta/notebook/docs/import_manager.clj @@ -1,4 +1,4 @@ -(ns quanta.notebook.import-manager +(ns quanta.notebook.docs.import-manager (:require [tick.core :as t] [ta.db.bars.protocol :as b] @@ -8,11 +8,11 @@ im -(def dt (t/instant "2024-02-01T00:00:00Z")) +(def dt (t/instant "2024-05-01T00:00:00Z")) dt ;; BYBIT -(b/get-bars im {:asset "BTCUSDT" ; crypto +(b/get-bars im {:asset "ETHUSDT" ; crypto :calendar [:crypto :d] :import :bybit} {:start (t/instant "2020-01-01T00:00:00Z") @@ -29,12 +29,12 @@ dt (-> ds :date meta :datatype)) ;; KIBOT -(-> (b/get-bars im {:asset "NG0" ; future - :calendar [:us :d] - :import :kibot} - {:start (t/instant "2020-01-01T00:00:00Z") - :end (t/instant "2024-01-01T00:00:00Z")}) - date-type) + +(b/get-bars im {:asset "NG0" ; future + :calendar [:us :d] + :import :kibot} + {:start (t/instant "2020-01-01T00:00:00Z") + :end (t/instant "2024-01-01T00:00:00Z")}) (b/get-bars im {:asset "EUR/USD" ; forex @@ -62,7 +62,13 @@ dt {:asset "AEE.AU" :calendar [:us :d] :import :eodhd} - ; fails because more than a year ago {:start (-> "2023-12-01T00:00:00Z" t/instant) :end (-> "2024-04-01T00:00:00Z" t/instant)}) +(b/get-bars im + {:asset "AEE.AU" + :calendar [:us :d] + :import :eodhd} + ; fails because more than a year ago + {:start (-> "2020-12-01T00:00:00Z" t/instant) + :end (-> "2024-04-01T00:00:00Z" t/instant)}) diff --git a/lib/import/resources/quanta/notebook/import_bardb_dynamic.clj b/lib/import/resources/quanta/notebook/import_bardb_dynamic.clj deleted file mode 100644 index c05ef5fd..00000000 --- a/lib/import/resources/quanta/notebook/import_bardb_dynamic.clj +++ /dev/null @@ -1,24 +0,0 @@ -(ns quanta.notebook.import-bardb-dynamic - (:require - [taoensso.timbre :refer [info warn error]] - [tick.core :as t] - [modular.system] - [ta.calendar.core :as cal] - [ta.db.bars.protocol :as b])) - -(def db (modular.system/system :bardb-dynamic)) - -(def window-daily - (cal/trailing-range [:us :d] 200 - (t/zoned-date-time "2024-03-07T17:30-05:00[America/New_York]"))) - -window-daily - -(defn get-eodhd-daily [asset] - (info "getting eodhd data for: " asset) - (b/get-bars db {:asset asset - :calendar [:us :d] - :import :eodhd} - window-daily)) - -(get-eodhd-daily "AEE.AU") diff --git a/lib/import/src/ta/db/bars/dynamic/overview_db.clj b/lib/import/src/ta/db/bars/dynamic/overview_db.clj index 070abd6b..ec211fe8 100644 --- a/lib/import/src/ta/db/bars/dynamic/overview_db.clj +++ b/lib/import/src/ta/db/bars/dynamic/overview_db.clj @@ -94,6 +94,14 @@ (info "overview tx: " tx) (d/transact conn [tx]))) +(defn remove-asset [conn {:keys [asset calendar] :as opts}] + (let [[market interval] calendar + id (find-id conn opts)] + (info "removing overview-db asset: " asset " " calendar " db-id: " id) + (d/transact + conn + {:tx-data [[:db/retractEntity id]]}))) + (comment (def conn (start-overview-db "/tmp/datahike-overview")) conn diff --git a/lib/import/src/ta/import/provider/bybit/ds.clj b/lib/import/src/ta/import/provider/bybit/ds.clj index 434635a5..f945ff39 100644 --- a/lib/import/src/ta/import/provider/bybit/ds.clj +++ b/lib/import/src/ta/import/provider/bybit/ds.clj @@ -141,11 +141,28 @@ (reduce or-fn false) not))) +(defn set-close-time [dt] + (-> dt + (t/date) + (t/at (t/time "23:59:59")) + (t/in "UTC") + (t/instant))) + +(defn set-close-time-vec [dt-vec] + (map set-close-time dt-vec)) + +(defn set-daily-time [{:keys [asset calendar] :as opts} ds] + (let [frequency (second calendar)] + (if (= frequency :d) + (tc/update-columns ds {:date set-close-time-vec}) + ds))) + (defn consolidate-datasets [opts range datasets] (if (all-ds-valid datasets) (->> datasets (apply tc/concat) - (sort-ds)) + (sort-ds) + (set-daily-time opts)) (nom/fail ::consolidate-datasets {:message "paged request failed!" :opts opts :range range}))) diff --git a/lib/import/src/ta/import/provider/eodhd/ds.clj b/lib/import/src/ta/import/provider/eodhd/ds.clj index a972247c..4477f51e 100644 --- a/lib/import/src/ta/import/provider/eodhd/ds.clj +++ b/lib/import/src/ta/import/provider/eodhd/ds.clj @@ -44,7 +44,7 @@ )) (defn error? [body] - (-> body first :warning)) + (-> body last :warning)) (defn get-bars-eodhd [api-token {:keys [asset calendar] :as opts} {:keys [start end] :as window}] (warn "get-bars: " opts window) diff --git a/lib/import/src/ta/import/provider/eodhd/raw.clj b/lib/import/src/ta/import/provider/eodhd/raw.clj index 55139cdf..3e76f228 100644 --- a/lib/import/src/ta/import/provider/eodhd/raw.clj +++ b/lib/import/src/ta/import/provider/eodhd/raw.clj @@ -1,9 +1,7 @@ (ns ta.import.provider.eodhd.raw (:require - [clojure.string :as str] [clojure.set] [taoensso.timbre :refer [info warn error]] - [clojure.edn :as edn] [cheshire.core :as cheshire] ; JSON Encoding [de.otto.nom.core :as nom] [ta.import.helper :refer [str->float http-get]] @@ -51,6 +49,9 @@ (defn get-exchange-tickers [api-token exchange-code] (make-request api-token (str "exchange-symbol-list/" exchange-code) {})) +(defn warning [result] + (-> result last :warning)) + (comment (def d (get-bars "65f0ad82c56400.56029279" "MCD.US" @@ -58,5 +59,14 @@ "2024-03-15")) d + + (require '[clojure.pprint :refer [print-table]]) + + (->> (get-bars "65f0ad82c56400.56029279" + "MCD.US" + "2020-01-01" + "2024-03-15") + warning) + ; ) diff --git a/lib/trade/resources/quanta/notebook/docs/mark2market.clj b/lib/trade/resources/quanta/notebook/docs/mark2market.clj new file mode 100644 index 00000000..29b94f75 --- /dev/null +++ b/lib/trade/resources/quanta/notebook/docs/mark2market.clj @@ -0,0 +1,30 @@ +(ns quanta.notebook.docs.mark2market + (:require + [tick.core :as t] + [modular.system] + [ta.trade.roundtrip.nav.mark2market :refer [portfolio]] + [ta.viz.trade.m2m.core :refer [m2m-chart]] + [ta.db.bars.protocol :as b])) + +(def bardb (modular.system/system :bardb-dynamic)) + +(def rts [{:asset "ETHUSDT" + :side :long + :qty 100.0 + :entry-date (t/instant "2023-03-09T23:59:59Z") + :entry-price 1400.78 + :exit-date (t/instant "2023-03-16T23:59:59Z") + :exit-price 1600.92}]) + + +(-> (portfolio bardb rts {:calendar [:crypto :d] + :import :bybit}) + (m2m-chart) + ;pr-str + ) + +; {:open# 0, :long$ 0.0, :short$ 0.0, :net$ 0.0, :pl-u 0.0, +; :pl-r 0.0, :date #inst \"2023-03-06T23:59:59.000000000-00:00\", +; :pl-r-cum 0.0, :pl-cum 0.0} + + diff --git a/lib/trade/resources/quanta/notebook/trade_entry_exit.clj b/lib/trade/resources/quanta/notebook/docs/trade_entry_exit.clj similarity index 79% rename from lib/trade/resources/quanta/notebook/trade_entry_exit.clj rename to lib/trade/resources/quanta/notebook/docs/trade_entry_exit.clj index 293a4ab2..7b6b3aba 100644 --- a/lib/trade/resources/quanta/notebook/trade_entry_exit.clj +++ b/lib/trade/resources/quanta/notebook/docs/trade_entry_exit.clj @@ -1,10 +1,10 @@ -(ns quanta.notebook.trade-entry-exit +(ns quanta.notebook.docs.trade-entry-exit (:require [tick.core :as t] [tablecloth.api :as tc] [ta.trade.backtest.from-entry :refer [entry-signal->roundtrips]] - [ta.trade.roundtrip.core :refer [metrics]] - [ta.viz.ds.metrics :refer [metrics-render-spec-impl]])) + [ta.trade.roundtrip.core :refer [roundtrip-stats]] + [ta.viz.trade.core :refer [roundtrip-stats-ui]])) (def ds (tc/dataset {:date (repeatedly 6 #(t/instant)) :close [100.0 104.0 106.0 103.0 102.0 108.0] @@ -24,6 +24,6 @@ ds rts -(-> (metrics rts) - (metrics-render-spec-impl) +(-> (roundtrip-stats rts) + (roundtrip-stats-ui) ) diff --git a/lib/trade/resources/quanta/notebook/trade_position.clj b/lib/trade/resources/quanta/notebook/docs/trade_position.clj similarity index 73% rename from lib/trade/resources/quanta/notebook/trade_position.clj rename to lib/trade/resources/quanta/notebook/docs/trade_position.clj index 5418e35c..c8fcb05d 100644 --- a/lib/trade/resources/quanta/notebook/trade_position.clj +++ b/lib/trade/resources/quanta/notebook/docs/trade_position.clj @@ -1,10 +1,10 @@ -(ns quanta.notebook.trade-position +(ns quanta.notebook.docs.trade-position (:require [tick.core :as t] [tablecloth.api :as tc] [ta.trade.backtest.from-position :refer [signal->roundtrips]] - [ta.trade.roundtrip.core :refer [metrics]] - [ta.viz.ds.metrics :refer [metrics-render-spec-impl]])) + [ta.trade.roundtrip.core :refer [roundtrip-stats]] + [ta.viz.trade.core :refer [roundtrip-stats-ui]])) (def signal-ds (tc/dataset {:date [(t/instant "2020-01-01T00:00:00Z") (t/instant "2020-01-02T00:00:00Z") @@ -13,6 +13,8 @@ (t/instant "2020-03-05T00:00:00Z") (t/instant "2020-04-06T00:00:00Z") (t/instant "2020-05-07T00:00:00Z")] + :asset ["BTCUSDT" "BTCUSDT" "BTCUSDT" "BTCUSDT" + "BTCUSDT" "BTCUSDT" "BTCUSDT"] :close [1.0 2.0 3.0 4.0 5.0 6.0 7.0] :signal [:long :hold :flat ;rt1 :short :hold :hold :flat ; rt2 @@ -21,12 +23,12 @@ signal-ds (def rts (signal->roundtrips signal-ds)) -(def r (metrics rts)) +(def r (roundtrip-stats rts)) (:roundtrip-ds r) (:metrics r) (:nav-ds r) -(metrics-render-spec-impl r) +(roundtrip-stats-ui r) diff --git a/lib/trade/resources/quanta/notebook/trade_stats.clj b/lib/trade/resources/quanta/notebook/docs/trade_stats.clj similarity index 98% rename from lib/trade/resources/quanta/notebook/trade_stats.clj rename to lib/trade/resources/quanta/notebook/docs/trade_stats.clj index 91948622..b4dc2eee 100644 --- a/lib/trade/resources/quanta/notebook/trade_stats.clj +++ b/lib/trade/resources/quanta/notebook/docs/trade_stats.clj @@ -1,4 +1,4 @@ -(ns quanta.notebook.trade-stats +(ns quanta.notebook.docs.trade-stats (:require [tablecloth.api :as tc] [ta.trade.roundtrip.metrics :as m])) diff --git a/lib/trade/src/ta/trade/backtest/from_position.clj b/lib/trade/src/ta/trade/backtest/from_position.clj index 9977f5e7..2b7bfc5f 100644 --- a/lib/trade/src/ta/trade/backtest/from_position.clj +++ b/lib/trade/src/ta/trade/backtest/from_position.clj @@ -1,7 +1,8 @@ (ns ta.trade.backtest.from-position (:require [tablecloth.api :as tc] - [ta.indicator.helper :refer [indicator]])) + [ta.indicator.helper :refer [indicator]] + [ta.trade.backtest.entry :refer [positionsize]])) (defn- new-signal [signal] (case signal @@ -10,7 +11,7 @@ :flat :flat nil)) -(defn signal-action [] +(defn signal-action [size-rule] (indicator [idx (volatile! 0) position (volatile! :flat) @@ -18,7 +19,7 @@ entry (volatile! {:side :flat :entry-idx 0 :id 1})] - (fn [[signal date price]] + (fn [[signal date price asset]] (let [prior-position @position new-position (or (new-signal signal) prior-position) chg? (not (= new-position prior-position)) @@ -40,26 +41,36 @@ (vreset! entry {:id @id :entry-idx @idx :side new-position + :qty (positionsize size-rule price) + :asset asset :entry-date date :entry-price price})) (vswap! idx inc) (vreset! position new-position) result)))) -(defn signal->roundtrips [signal-ds] - (assert (:signal signal-ds) "to create roundtrips :signal column needs to be present!") - (assert (:date signal-ds) "to create roundtrips :date column needs to be present!") - (assert (:close signal-ds) "to create roundtrips :close column needs to be present!") - (let [n (tc/row-count signal-ds) - fun (signal-action) - signal (:signal signal-ds) - date (:date signal-ds) - close (:close signal-ds) - vec (fn [idx] - [(signal idx) (date idx) (close idx)]) - map-of-vecs (map vec (range n)) - roundtrips (into [] fun map-of-vecs)] - (tc/dataset roundtrips))) +(defn signal->roundtrips + "returns roundtrips from :signal column of ds + size-rule default: [:fixed-amount 100000] + could be also: [:fixed-qty 3.1]" + ([signal-ds] + (signal->roundtrips signal-ds [:fixed-amount 100000])) + ([signal-ds size-rule] + (assert (:signal signal-ds) "to create roundtrips :signal column needs to be present!") + (assert (:date signal-ds) "to create roundtrips :date column needs to be present!") + (assert (:close signal-ds) "to create roundtrips :close column needs to be present!") + (assert (:asset signal-ds) "to create roundtrips :asset column needs to be present!") + (let [n (tc/row-count signal-ds) + fun (signal-action size-rule) + signal (:signal signal-ds) + date (:date signal-ds) + close (:close signal-ds) + asset (:asset signal-ds) + vec (fn [idx] + [(signal idx) (date idx) (close idx) (asset idx)]) + map-of-vecs (map vec (range n)) + roundtrips (into [] fun map-of-vecs)] + (tc/dataset roundtrips)))) (comment diff --git a/lib/trade/src/ta/trade/roundtrip/core.clj b/lib/trade/src/ta/trade/roundtrip/core.clj index fe33f012..20d5502b 100644 --- a/lib/trade/src/ta/trade/roundtrip/core.clj +++ b/lib/trade/src/ta/trade/roundtrip/core.clj @@ -8,7 +8,7 @@ [ta.trade.roundtrip.nav.metrics :refer [calc-nav-metrics]] [ta.trade.roundtrip.nav.grouped :refer [grouped-nav]])) -(defn metrics-impl [roundtrip-ds] +(defn- roundtrip-stats-impl [roundtrip-ds] (let [vr (validate-roundtrips (tds/mapseq-reader roundtrip-ds))] (if (nom/anomaly? vr) vr @@ -21,9 +21,9 @@ :nav nav-metrics} :nav-ds nav-ds})))) -(defn metrics [roundtrip-ds] +(defn roundtrip-stats [roundtrip-ds] (try - (metrics-impl roundtrip-ds) + (roundtrip-stats-impl roundtrip-ds) (catch Exception ex (nom/fail ::viz-calc {:message "metrics calc exception!" :location :metrics diff --git a/lib/trade/src/ta/trade/roundtrip/metrics.clj b/lib/trade/src/ta/trade/roundtrip/metrics.clj index dd5cec1f..79c7249f 100644 --- a/lib/trade/src/ta/trade/roundtrip/metrics.clj +++ b/lib/trade/src/ta/trade/roundtrip/metrics.clj @@ -67,7 +67,7 @@ (assert (:ret-log roundtrips-ds) "to calc metrics :ret-log column needs to be present!") (assert (:bars roundtrips-ds) "to calc metrics :bars column needs to be present!") (assert (:win? roundtrips-ds) "to calc metrics :win? column needs to be present!") - (println "calc-roundtrip-metrics ..") + ;(println "calc-roundtrip-metrics ..") (let [wl-stats (win-loss-stats roundtrips-ds) metrics (win-loss-performance-metrics wl-stats)] metrics)) diff --git a/lib/trade/src/ta/trade/roundtrip/nav/mark2market.clj b/lib/trade/src/ta/trade/roundtrip/nav/mark2market.clj index 1c7d3f03..ca18d80b 100644 --- a/lib/trade/src/ta/trade/roundtrip/nav/mark2market.clj +++ b/lib/trade/src/ta/trade/roundtrip/nav/mark2market.clj @@ -30,7 +30,7 @@ :pl-u (vec-const size 0.0) :pl-r (vec-const size 0.0)})) -(defn trade-stats [ds-bars {:keys [date-entry date-exit qty]}] +(defn trade-stats [ds-bars {:keys [date-entry date-exit]}] (let [ds-trade (filter-range ds-bars {:start date-entry :end date-exit})] (tc/add-columns ds-trade {:position/trades-open @@ -42,8 +42,7 @@ (defn trade-unrealized-effect [ds-eff idxs-win price-w w# {:keys [qty side - entry-price entry-date - exit-price exit-date] :as trade}] + entry-price entry-date] :as trade}] ;(info "calculate trade-un-realized..") (let [long? (= :long side) qty2 (if long? (+ 0.0 qty) (- 0.0 qty)) @@ -86,13 +85,15 @@ entry-date exit-date] :as trade}] (assert entry-date "trade does not have entry-dt") - ;(assert exit-date "trade does not have entry-dt") + ;(assert exit-date "trade does not have exit-dt") (assert side "trade does not have side") (assert qty "trade does not have qty") ;(info "calculating trade-effect " trade) (let [full# (tc/row-count ds-bars) ds-eff (no-effect full#) - warnings (atom []) + warnings (atom (if has-series? + [] + [(assoc trade :warning :no-bars)])) idxs-win (cond ; open trade (nil? exit-date) @@ -122,7 +123,9 @@ ds-x (tc/select-rows ds-bars idxs-exit) x# (tc/row-count ds-x)] (if (= x# 0) - (do (warn "cannot calculate REALIZED effect for trade: " trade) + (do (warn "cannot set REALIZED effect for trade: " trade) + (warn "exit date: " exit-date) + (warn "date-fd: " (:date ds-bars)) (swap! warnings conj (assoc trade :warning :realized))) (trade-realized-effect ds-eff idxs-exit trade)))) ; return @@ -152,6 +155,18 @@ (let [effects (map #(trade-effect bar-ds has-series? %) trades)] (effects-sum effects))) +(defn add-days [dt-inst days] + (-> (t/>> dt-inst (t/new-duration days :days)) + ;t/inst) ; casting to int is required, otherwise it returns an instance. + )) + +(defn subtract-days [dt-inst days] + ; (t/+ due (t/new-period 1 :months)) this does not work + ; https://github.com/juxt/tick/issues/65 + (-> (t/<< dt-inst (t/new-duration days :days)) + ;t/inst +) ; casting to int is required, otherwise it returns an instance. + ) (defn portfolio [bardb trades {:keys [calendar] :as opts}] (let [assets (->> trades (map :asset) @@ -160,7 +175,7 @@ start-dt (apply t/min (map :entry-date trades)) end-dt (apply t/max (->> (map :exit-date trades) (remove nil?))) - window {:start start-dt :end end-dt} + window {:start (subtract-days start-dt 3) :end (add-days end-dt 3)} cal-seq (cal/fixed-window calendar window) date-ds (calendar-seq->date-ds cal-seq) l (tc/row-count date-ds) @@ -222,17 +237,6 @@ (in-range? entry-date exit-date (parse-date "2022-03-08")) (in-range? entry-date exit-date (parse-date "2022-03-07")) - (def trade1 {:asset "EUR/USD" - :side :long - :qty 1000.0 - :entry-date (t/instant "2023-03-09T17:00:00Z") - :entry-price 94.78 - :exit-date (t/instant "2023-03-16T17:00:00Z") - :exit-price 96.92}) - - (def trades-test - [trade1]) - (require '[modular.system]) (def bardb (modular.system/system :duckdb ;:bardb-dynamic )) @@ -244,19 +248,23 @@ {:start (t/instant "2023-03-06T20:30:00Z") :end (t/instant "2023-03-16T20:30:00Z")})) - (-> (effects-asset - eurusd - true - trades-test) - :eff) + eurusd + + (def rts [{:asset "EUR/USD" + :side :long + :qty 1000000.0 + :entry-date (t/instant "2023-03-09T20:30:00Z") + :entry-price 1.1078 + :exit-date (t/instant "2023-03-16T20:30:00Z") + :exit-price 1.1292}]) - (trade-effect eurusd true trade1) + (-> (effects-asset eurusd true rts) + :eff) - (effects-sum [(trade-effect eurusd true trade1) - (trade-effect eurusd true trade1)]) + (trade-effect eurusd true (first rts)) - (portfolio bardb trades-test {:calendar [:forex :d] - :import :kibot}) + (effects-sum [(trade-effect eurusd true (first rts)) + (trade-effect eurusd true (first rts))]) ; ) diff --git a/lib/viz/src/ta/viz/renderfn/metrics.cljs b/lib/viz/src/ta/viz/renderfn/metrics.cljs deleted file mode 100644 index c6cdb2c3..00000000 --- a/lib/viz/src/ta/viz/renderfn/metrics.cljs +++ /dev/null @@ -1,20 +0,0 @@ -(ns ta.viz.renderfn.metrics - (:require - [ta.viz.renderfn.rtable :refer [rtable]] - [ta.viz.trade.metrics :refer [metrics-view]] - [ta.viz.renderfn.vega :refer [vega-lite]])) - -(defn metrics [spec {:keys [roundtrips metrics nav nav-chart rt] :as data}] - (with-meta - [:div.grid.grid-cols-2.w-full.h-full - [:div.bg-green-300.w-full.h-full - [:h1.bg-red-500 "metrics"] - [metrics-view metrics] - [:h1.bg-red-500 "chart"] - [vega-lite (:spec nav-chart) (:data nav-chart)]] - [:div.bg-blue-300.w-full.h-full - [rtable (:spec rt) (:data rt)]]] -;[:p (pr-str spec)] - ;[:p (pr-str data)] - - {:R true})) diff --git a/lib/viz/src/ta/viz/ds/metrics.clj b/lib/viz/src/ta/viz/trade/core.clj similarity index 63% rename from lib/viz/src/ta/viz/ds/metrics.clj rename to lib/viz/src/ta/viz/trade/core.clj index d1dcb249..99f0cfec 100644 --- a/lib/viz/src/ta/viz/ds/metrics.clj +++ b/lib/viz/src/ta/viz/trade/core.clj @@ -1,4 +1,4 @@ -(ns ta.viz.ds.metrics +(ns ta.viz.trade.core (:require [tech.v3.dataset :as tds] [de.otto.nom.core :as nom] @@ -12,23 +12,23 @@ (into [] (tds/mapseq-reader ds))) -(defn metrics-render-spec-impl [{:keys [roundtrip-ds nav-ds metrics]}] +(defn- roundtrip-stats-ui-impl [spec {:keys [roundtrip-ds metrics]}] ^{:render-fn 'ta.viz.renderfn/render-spec} ; needed for notebooks - {:render-fn 'ta.viz.renderfn.metrics/metrics - :data {:roundtrips (ds->map roundtrip-ds) - :nav (ds->map nav-ds) - :metrics metrics - :nav-chart (nav-chart roundtrip-ds) + {:render-fn 'ta.viz.trade.core/roundtrip-stats-ui + :data {:metrics metrics + :chart (nav-chart roundtrip-ds) :rt (roundtrip-ui {} roundtrip-ds)} - :spec {}}) + :spec spec}) -(defn metrics-render-spec +(defn roundtrip-stats-ui "returns a render specification {:render-fn :spec :data}. spec must follow chart-pane format. The ui shows a barchart with extra specified columns plotted with a specified style/position, created from the bar-algo-ds" - [spec metrics] - (if (nom/anomaly? metrics) - (error-render-spec metrics) - (metrics-render-spec-impl metrics))) + ([metrics] + (roundtrip-stats-ui {} metrics)) + ([spec metrics] + (if (nom/anomaly? metrics) + (error-render-spec metrics) + (roundtrip-stats-ui-impl spec metrics)))) diff --git a/lib/viz/src/ta/viz/trade/core.cljs b/lib/viz/src/ta/viz/trade/core.cljs new file mode 100644 index 00000000..a5749764 --- /dev/null +++ b/lib/viz/src/ta/viz/trade/core.cljs @@ -0,0 +1,23 @@ +(ns ta.viz.trade.core + (:require + [container :refer [tab]] + [ta.viz.renderfn.rtable :refer [rtable]] + [ta.viz.trade.metrics :refer [metrics-view]] + [ta.viz.renderfn.vega :refer [vega-lite]])) + +(defn roundtrip-stats-ui [{:keys [style class] + :or {style {:height "600px" + :width "800px"} + class "bg-red-500"} + :as spec} + {:keys [metrics chart rt] :as data}] + (with-meta + [tab {:class class + :style style} + "metrics" + [metrics-view metrics] + "chart" + [vega-lite (:spec chart) (:data chart)] + "roundtrips" + [rtable (:spec rt) (:data rt)]] + {:R true})) diff --git a/lib/viz/src/ta/viz/trade/format.cljs b/lib/viz/src/ta/viz/trade/format.cljs index 78585710..b7b5a606 100644 --- a/lib/viz/src/ta/viz/trade/format.cljs +++ b/lib/viz/src/ta/viz/trade/format.cljs @@ -1,12 +1,13 @@ (ns ta.viz.trade.format (:require - [goldly.js :refer [to-fixed]] - [tick.goldly] [tick.core :as t])) +(defn to-fixed [n d] + (.toFixed n d)) + (defn fmt-yyyymmdd [dt] (if dt - (t/format (t/formatter "YYYY-MM-dd") dt) + (t/format (t/formatter "YYYY-MM-dd") (t/zoned-date-time dt)) "")) (defn round-number-digits diff --git a/lib/viz/src/ta/viz/trade/m2m/core.clj b/lib/viz/src/ta/viz/trade/m2m/core.clj new file mode 100644 index 00000000..47da2884 --- /dev/null +++ b/lib/viz/src/ta/viz/trade/m2m/core.clj @@ -0,0 +1,16 @@ +(ns ta.viz.trade.m2m.core + (:require + [tech.v3.dataset :as tds])) + +(defn- ds->map [ds] + ;(tc/rows :as-maps) ; this does not work, type of it is a reified dataset. + ; this works in repl, but when sending data to the browser it fails. + (into [] + (tds/mapseq-reader ds))) + +(defn m2m-chart [m2m-data] + ; todo: add warnings (because they are relevant!) + (with-meta + (-> (update m2m-data :eff ds->map) + :eff) + {:render-fn 'ta.viz.trade.m2m.vega/m2m-chart})) \ No newline at end of file diff --git a/lib/viz/src/ta/viz/trade/nav_table.cljs b/lib/viz/src/ta/viz/trade/m2m/table.cljs similarity index 96% rename from lib/viz/src/ta/viz/trade/nav_table.cljs rename to lib/viz/src/ta/viz/trade/m2m/table.cljs index be1b099f..11a0a5cb 100644 --- a/lib/viz/src/ta/viz/trade/nav_table.cljs +++ b/lib/viz/src/ta/viz/trade/m2m/table.cljs @@ -1,10 +1,11 @@ -(ns ta.viz.trade.nav-table +(ns ta.viz.trade.m2m.table (:require - [goldly.js :refer [to-fixed]] - [tick.goldly] [tick.core :as t] [ui.aggrid :refer [aggrid]])) +(defn to-fixed [n d] + (.toFixed n d)) + (defn fmt-yyyymmdd [dt] (if dt (t/format (t/formatter "YYYY-MM-dd") dt) diff --git a/lib/viz/src/ta/viz/trade/nav_vega.cljs b/lib/viz/src/ta/viz/trade/m2m/vega.cljs similarity index 87% rename from lib/viz/src/ta/viz/trade/nav_vega.cljs rename to lib/viz/src/ta/viz/trade/m2m/vega.cljs index 88b0f23b..7c4cfd92 100644 --- a/lib/viz/src/ta/viz/trade/nav_vega.cljs +++ b/lib/viz/src/ta/viz/trade/m2m/vega.cljs @@ -1,4 +1,4 @@ -(ns ta.viz.trade.nav-vega +(ns ta.viz.trade.m2m.vega (:require [ui.vega :refer [vegalite]])) @@ -8,7 +8,8 @@ (def width 500) -(defn nav-vega [data] +(defn m2m-chart [data] + (println "m2m data: " data) [vegalite {:box :fl :spec {:description "Portfolio eval result." @@ -68,15 +69,3 @@ ;:gridDash {:condition {:test {:field "value" :timeUnit "month", :equal 1}, :value []} :value [2,2]} ;:tickDash {:condition {:test {:field "value", :timeUnit "month", :equal 1}, :value []} :value [2,2]} ; } - -(defn navs-chart [navs] - (let [navs-with-index (map-indexed (fn [i v] - {:index i - :nav (:nav v)}) navs) - navs-with-index (into [] navs-with-index)] - [:div.w-full.h-full - [:h1 "navs " (count navs-with-index)] - (when (> (count navs) 0) - [:div.w-full.h-full; {:style {:width "50vw"}} - ;(pr-str navs-with-index) - [nav-vega navs-with-index]])])) \ No newline at end of file diff --git a/lib/viz/src/ta/viz/trade/metrics.cljs b/lib/viz/src/ta/viz/trade/metrics.cljs index 79b66974..91bb19b9 100644 --- a/lib/viz/src/ta/viz/trade/metrics.cljs +++ b/lib/viz/src/ta/viz/trade/metrics.cljs @@ -1,8 +1,7 @@ -(ns ta.viz.trade.metrics - (:require - [tick.goldly] - [tick.core :as t] - [goldly.js :refer [to-fixed]])) +(ns ta.viz.trade.metrics) + +(defn to-fixed [n d] + (.toFixed n d)) (defn round-number-digits [digits number] ; digits is first parameter, so it can easily be applied (data last) @@ -12,8 +11,10 @@ (println "round-numnber-digits exception: " e) number))) -(defn metrics-view [{:keys [roundtrip nav]}] - [:div +(defn metrics-view [{:keys [class style roundtrip nav] + :or {class "w-full h-full" + style {}}}] + [:div {:class class :style style} [:h1.bg-blue-300.text-xl "performance-metrics"] [:table [:tr diff --git a/lib/viz/src/ta/viz/trade/nav_chart.clj b/lib/viz/src/ta/viz/trade/nav_chart.clj index 236c2fa4..300b0735 100644 --- a/lib/viz/src/ta/viz/trade/nav_chart.clj +++ b/lib/viz/src/ta/viz/trade/nav_chart.clj @@ -4,8 +4,8 @@ (defn nav-chart [roundtrip-ds] (let [cols [:exit-date :cum-ret-volume] - spec {:width "800" ;"100%" - :height "600" ;"100%" + spec {:width "700" + :height "550" :description "NAV" :mark "line" :encoding {:y {:field :cum-ret-volume diff --git a/lib/viz/src/ta/viz/trade/roundtrip.clj b/lib/viz/src/ta/viz/trade/roundtrip.clj index 180bd381..133947eb 100644 --- a/lib/viz/src/ta/viz/trade/roundtrip.clj +++ b/lib/viz/src/ta/viz/trade/roundtrip.clj @@ -4,14 +4,14 @@ (def default-spec {:class "table-head-fixed padding-sm table-blue table-striped table-hover" - :style {:width "1000px" - :height "400px" + :style {:width "100%" ;"1000px" + :height "100%" ;"400px" :border "1px solid blue"} :columns [{:path :entry-date :format 'ta.viz.trade.format/fmt-yyyymmdd :header "dt-e"} - {:path :asset :header "asset" :max-width 50} - {:path :side :header "side" :max-width 50} - {:path :qty :header "qty" :max-width 50 :attrs 'ta.viz.trade.format/align-right} - {:path :entry-price :header "px-e" :max-width 50 :attrs 'ta.viz.trade.format/align-right} + {:path :asset :header "asset"} + {:path :side :header "side"} + {:path :qty :header "qty" :attrs 'ta.viz.trade.format/align-right :format 'ta.viz.trade.format/round-digit-1} + {:path :entry-price :header "px-e" :attrs 'ta.viz.trade.format/align-right} {:path :entry-vol :format 'ta.viz.trade.format/round-digit-0 :header "vol-e" :attrs 'ta.viz.trade.format/align-right} {:path :exit-date :format 'ta.viz.trade.format/fmt-yyyymmdd :header "dt-x"} {:path :exit-price :header "px-x" :max-width 50 :attrs 'ta.viz.trade.format/align-right} diff --git a/lib/viz/src/ta/viz/trade/trades_table.cljs b/lib/viz/src/ta/viz/trade/trades_table.cljs index df18acbc..48382d45 100644 --- a/lib/viz/src/ta/viz/trade/trades_table.cljs +++ b/lib/viz/src/ta/viz/trade/trades_table.cljs @@ -1,10 +1,11 @@ (ns ta.viz.trade.trades-table (:require - [goldly.js :refer [to-fixed]] - [tick.goldly] [tick.core :as t] [ui.aggrid :refer [aggrid]])) +(defn to-fixed [n d] + (.toFixed n d)) + (defn fmt-yyyymmdd [dt] (if dt (t/format (t/formatter "YYYY-MM-dd") dt)