diff --git a/app/demo/src/demo/goldly/lib/layout.cljs b/app/demo/src/demo/goldly/lib/layout.cljs new file mode 100644 index 00000000..d8fd5826 --- /dev/null +++ b/app/demo/src/demo/goldly/lib/layout.cljs @@ -0,0 +1,11 @@ +(ns demo.goldly.lib.layout + (:require + [spaces])) + + +(defn left-right-top [{:keys [top left right]}] + [spaces/viewport + [spaces/top-resizeable {:size 50} top] + [spaces/fill + [spaces/left-resizeable {:size "50%" :scrollable false} left] + [spaces/fill {:scrollable false} right]]]) diff --git a/app/demo/src/demo/goldly/page/main.cljs b/app/demo/src/demo/goldly/page/main.cljs index 29126ab0..ee9793cb 100644 --- a/app/demo/src/demo/goldly/page/main.cljs +++ b/app/demo/src/demo/goldly/page/main.cljs @@ -32,9 +32,17 @@ [link-dispatch [:bidi/goto :joseph/nav] "joseph-nav"] ] + ; test + [:div.bg-blue-300.m-5 + [:p.text-blue.text-xl "test"] + [link-dispatch [:bidi/goto :test/test] "test-page"] + [link-dispatch [:bidi/goto :test/layout1] "layout-1"] + [link-dispatch [:bidi/goto :test/layout2] "layout-2"] + + ] ; goldly developer tools [:div.bg-blue-300.m-5 - [link-dispatch [:bidi/goto :user/test] "test-page"] + [:p.text-blue.text-xl "goldly developer tools"] [link-dispatch [:bidi/goto :viewer :query-params {}] "notebook viewer"] [link-dispatch [:bidi/goto :scratchpad] "scratchpad"] diff --git a/app/demo/src/demo/goldly/page/test/layout.cljs b/app/demo/src/demo/goldly/page/test/layout.cljs new file mode 100644 index 00000000..6924f499 --- /dev/null +++ b/app/demo/src/demo/goldly/page/test/layout.cljs @@ -0,0 +1,19 @@ +(ns demo.goldly.page.test.layout + (:require + [goldly.page :as page] + [site :refer [ipsum]] + [demo.goldly.lib.layout :as layout])) + +(defn page-layout-1 [_route] + (layout/left-right-top {:top "top" + :left [:div.bg-blue-500.w-full.h-full "left"] + :right [:div.bg-green-500.w-full.h-full "right"]})) + +(defn page-layout-2 [_route] + (layout/left-right-top {:top "menu" + :left [:div.bg-blue-500.w-full.h-full.overflow-scroll (ipsum 5)] + :right [:div.bg-green-500.w-full.h-full (ipsum 100)]})) + + +(page/add page-layout-1 :test/layout1) +(page/add page-layout-2 :test/layout2) \ No newline at end of file diff --git a/app/demo/src/demo/goldly/page/test.cljs b/app/demo/src/demo/goldly/page/test/test.cljs similarity index 94% rename from app/demo/src/demo/goldly/page/test.cljs rename to app/demo/src/demo/goldly/page/test/test.cljs index 5f906c62..594fd0bb 100644 --- a/app/demo/src/demo/goldly/page/test.cljs +++ b/app/demo/src/demo/goldly/page/test/test.cljs @@ -1,4 +1,4 @@ -(ns demo.goldly.page.test +(ns demo.goldly.page.test.test (:require [reagent.core :as r] [goldly.page :as page] @@ -38,4 +38,4 @@ -(page/add test-page :user/test) +(page/add test-page :test/test) diff --git a/app/demo/src/ta-config.edn b/app/demo/src/ta-config.edn index 2824b51d..f6c51978 100644 --- a/app/demo/src/ta-config.edn +++ b/app/demo/src/ta-config.edn @@ -19,9 +19,13 @@ :goldly {:lazy true :src-dir "src" ; runtime - :autoload-cljs-dir ["demo/goldly/page"] + :autoload-cljs-dir ["demo/goldly/page" + "demo/goldly/page/test"] :routes {:app {"" :user/main - "test" :user/test} + "test/" {"test" :test/test + "layout1" :test/layout1 + "layout2" :test/layout2 + }} :api {"arrow" :dataset/arrow}}} :reval {:rdocument {:storage-root "../../output/rdocument/" diff --git a/lib/joseph/src/joseph/nav.clj b/lib/joseph/src/joseph/nav.clj index 90170ad1..09ffab94 100644 --- a/lib/joseph/src/joseph/nav.clj +++ b/lib/joseph/src/joseph/nav.clj @@ -7,21 +7,29 @@ (defn calc-nav [symbol] (let [trades (load-trades-valid) + symbols (->> trades (map :symbol) (into #{}) (into [])) + accounts (->> trades (map :account) (into #{}) (into [])) trades (if (or (nil? symbol) (str/blank? symbol)) trades (filter #(= symbol (:symbol %)) trades)) ds-nav (portfolio trades)] {:nav ds-nav - :trades trades})) + :trades trades + :symbols symbols + :accounts accounts + })) (defn calc-nav-browser [symbol] - (let [{:keys [nav trades]} (calc-nav symbol)] - {:nav (ds->map nav) - :trades trades})) + (let [{:keys [nav] :as data} (calc-nav symbol)] + (merge data + {:nav (ds->map nav)}))) (comment + (-> (calc-nav nil) + :symbols) + (-> (calc-nav nil) :nav) @@ -33,6 +41,11 @@ (-> (calc-nav-browser nil) :nav) + (-> (calc-nav-browser nil) + :symbols) + + (-> (calc-nav-browser nil) + :accounts) ; ) diff --git a/lib/joseph/src/joseph/page/nav.cljs b/lib/joseph/src/joseph/page/nav.cljs index ee5eb5fb..934e52e7 100644 --- a/lib/joseph/src/joseph/page/nav.cljs +++ b/lib/joseph/src/joseph/page/nav.cljs @@ -1,12 +1,16 @@ (ns joseph.page.nav (:require [tick.core :as t] + [re-frame.core :as rf] [goldly.page :as page] + [goldly :refer [eventhandler]] [container :refer [tab]] [ta.viz.nav-vega :refer [nav-vega]] [ta.viz.nav-table :refer [nav-table]] [ta.viz.trades-table :refer [trades-table]] - [demo.goldly.lib.loader :refer [clj->p]])) + [demo.goldly.lib.loader :refer [clj->p]] + [demo.goldly.lib.layout :as layout] + )) (defn hack-date [row] ; inst converts a tick data to a javascript/date @@ -16,24 +20,56 @@ (->> nav (map hack-date))) +(defn layout-test [{:keys [top left right]}] + [:div.w-screen.h-screen.flex.flex-cols + ;vega nav chart + [:div.w-full.h-full ;.overflow-scroll + left + ] + ;aggrid table (nav or trades) + [:div.w-full.h-full.overflow-scroll + ]]) + + +(defn select-string [{:keys [items value on-change]}] + (into [:select {:value value + :on-change on-change + }] + (map (fn [s] [:option {:value s} s]) items))) + (defn page-joseph-nav [{:keys [query-params] :as route}] - (let [symbol (:symbol query-params) - nav (clj->p 'joseph.nav/calc-nav-browser symbol)] + (let [symbol (or (:symbol query-params) + "") + account (or (:account query-params) + "") + nav (clj->p 'joseph.nav/calc-nav-browser symbol) + on-change-symbol (fn [symbol _e] + (println (str "selected: " symbol)) + (rf/dispatch [:bidi/goto :joseph/nav :query-params {:symbol symbol :account account}])) + on-change-account (fn [account _e] + (println (str "selected: " account)) + (rf/dispatch [:bidi/goto :joseph/nav :query-params {:symbol symbol :account account}])) + ] (fn [& args] (case (:status @nav) :loading [:p "loading"] :error [:p "error!"] - :data [:div.w-screen.h-screen.flex.flex-cols - ;vega nav chart - [:div.w-full.h-full.overflow-scroll - [nav-vega (vega-data-hacks (:nav (:data @nav)))]] - ;aggrid table (nav or trades) - [:div.w-full.h-full.overflow-scroll - [tab {:box :fl} - "nav" - [nav-table (:nav (:data @nav))] - "trades" - [trades-table (:trades (:data @nav))]]]] + :data [layout/left-right-top + {:top [:div + [select-string {:value symbol + :items (concat [""] (:symbols (:data @nav))) + :on-change (eventhandler on-change-symbol)}] + [select-string {:value account + :items (concat [""] (:accounts (:data @nav))) + :on-change (eventhandler on-change-account)}] + ] + :left [:div.w-full.h-full.overflow-y-scroll + [nav-vega (vega-data-hacks (:nav (:data @nav)))]] + :right [tab {:box :fl} + "nav" + [nav-table (:nav (:data @nav))] + "trades" + [trades-table (:trades (:data @nav))]]}] [:p "unknown: status:" (pr-str @nav)])))) diff --git a/lib/joseph/src/joseph/trades.clj b/lib/joseph/src/joseph/trades.clj index 50b07f4d..915de812 100644 --- a/lib/joseph/src/joseph/trades.clj +++ b/lib/joseph/src/joseph/trades.clj @@ -34,12 +34,17 @@ :exit-price 420.10}]) +(defn entry-vol [{:keys [entry-price qty direction]}] + (* entry-price qty)) + (defn load-trades [] (->> (slurp "../resources/trades-upload.edn") (edn/read-string) ;(filter #(= :equity (:category %))) (map #(update % :entry-date parse-date)) - (map #(update % :exit-date parse-date)))) + (map #(update % :exit-date parse-date)) + (map #(assoc % :entry-vol (entry-vol %))) + )) (def cutoff-date (parse-date "2022-01-01")) diff --git a/lib/trateg/src/ta/multi/nav_trades.clj b/lib/trateg/src/ta/multi/nav_trades.clj index 53462607..543a3962 100644 --- a/lib/trateg/src/ta/multi/nav_trades.clj +++ b/lib/trateg/src/ta/multi/nav_trades.clj @@ -40,9 +40,6 @@ #(map inc (% :position/trades-open))}) )) -(defn idxs-last [idxs] - (let [v (last idxs)] - [v])) (defn set-col-win [ds win col val] ;(println "set-col val: " val) @@ -91,8 +88,8 @@ )) (defn trade-effect [ds-bars {:keys [qty direction - entry-price entry-date - exit-price exit-date] :as trade}] + 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 direction "trade does not have side") @@ -100,19 +97,28 @@ ;(info "calculating trade-effect " trade) (let [full# (tc/row-count ds-bars) ds-eff (no-effect full#) + idxs-exit (argops/argfilter #(t/= exit-date %) (:date ds-bars)) idxs-win (if (t/= entry-date exit-date) - (argops/argfilter #(t/= entry-date %) (:date ds-bars)) - (argops/argfilter #(t/<= entry-date % exit-date) (:date ds-bars))) - idxs-exit (idxs-last idxs-win) - ds-w (tc/select-rows ds-bars idxs-win) - price-w (:close ds-w) - w# (tc/row-count ds-w)] - (if (= w# 0) - (do (warn "cannot calculate effect for trade: " trade) - ds-eff) - (do (trade-unrealized-effect ds-eff idxs-win price-w w# trade) - (trade-realized-effect ds-eff idxs-exit trade) - ds-eff)))) + nil + (argops/argfilter #(and (t/<= entry-date %) + (t/< % exit-date)) + (:date ds-bars)))] + ; unrealized effect + (when idxs-win + (let [ds-w (tc/select-rows ds-bars idxs-win) + price-w (:close ds-w) + w# (tc/row-count ds-w)] + (if (= w# 0) + (warn "cannot calculate unrealized effect for trade: " trade) + (trade-unrealized-effect ds-eff idxs-win price-w w# trade)))) + ; realized effect + (let [ds-x (tc/select-rows ds-bars idxs-exit) + x# (tc/row-count ds-x)] + (if (= x# 0) + (warn "cannot calculate REALIZED effect for trade: " trade) + (trade-realized-effect ds-eff idxs-exit trade))) + ; return + ds-eff)) (defn effects+ [a b] (tc/dataset @@ -153,14 +159,14 @@ ds (reduce effects+ (no-effect (tc/row-count (:calendar calendar))) (map calc-symbol symbols)) - - ] - (tc/add-columns ds {:date (-> calendar :calendar :date) - :pl-r-cum (dfn/cumsum (:pl-r ds)) - }) - )) - - + ; result + pl-r-cum (dfn/cumsum (:pl-r ds))] + + (tc/add-columns + ds + {:date (-> calendar :calendar :date) + :pl-r-cum pl-r-cum + :pl-cum (dfn/+ (:pl-u ds) pl-r-cum)}))) (comment @@ -190,14 +196,9 @@ (tc/select-rows ds1 [0 5]) - (require '[joseph.trades :refer [load-trades]]) - (def cutoff-date (parse-date "2022-01-01")) - (defn invalid-date [{:keys [entry-date exit-date]}] - (or (t/<= entry-date cutoff-date) - (t/<= exit-date cutoff-date))) - (def trades - (->> (load-trades) - (remove invalid-date))) + (require '[joseph.trades :refer [load-trades-valid]]) + + (def trades (load-trades-valid)) (count trades) (def trades-googl diff --git a/lib/trateg/src/ta/viz/nav_table.cljs b/lib/trateg/src/ta/viz/nav_table.cljs index fe885784..15aa9eb0 100644 --- a/lib/trateg/src/ta/viz/nav_table.cljs +++ b/lib/trateg/src/ta/viz/nav_table.cljs @@ -15,15 +15,16 @@ (if (nil? number) "" (to-fixed number digits))) (defn nav-table [nav] - [aggrid {:data nav + [aggrid {:box :fl + :data nav :columns [{:field :date :format fmt-yyyymmdd} - {:field :open# } - {:field :long$ :format #(round-number-digits 0 %)} - {:field :short$ :format #(round-number-digits 0 %)} - {:field :net$ :format #(round-number-digits 0 %)} - {:field :pl-u :format #(round-number-digits 0 %)} - {:field :pl-r :format #(round-number-digits 0 %)} - {:field :pl-r-cum :format #(round-number-digits 0 %)}] - :box :fl + {:field :open# :type "rightAligned"} + {:field :long$ :format #(round-number-digits 0 %) :type "rightAligned"} + {:field :short$ :format #(round-number-digits 0 %) :type "rightAligned"} + {:field :net$ :format #(round-number-digits 0 %) :type "rightAligned"} + {:field :pl-u :format #(round-number-digits 0 %) :type "rightAligned"} + {:field :pl-r :format #(round-number-digits 0 %) :type "rightAligned"} + {:field :pl-r-cum :format #(round-number-digits 0 %) :type "rightAligned"}] + :pagination :false :paginationAutoPageSize false}]) \ No newline at end of file diff --git a/lib/trateg/src/ta/viz/nav_vega.cljs b/lib/trateg/src/ta/viz/nav_vega.cljs index 86ad5a41..60d0c232 100644 --- a/lib/trateg/src/ta/viz/nav_vega.cljs +++ b/lib/trateg/src/ta/viz/nav_vega.cljs @@ -6,37 +6,48 @@ ; :open# :long$ :short$ :net$ ; :pl-u :pl-r :pl-r-cum +(def width 500) + (defn nav-vega [data] [vegalite - {:width "full" - :height 400 + {:box :fl :spec {:description "Portfolio eval result." :data {:values data} - :width "800" - :vconcat [{:width "full" - :height 300 - :mark "line" - :encoding {:x {:field "date" :type "temporal"} - :y {:field "pl-r-cum", :type "quantitative"}}} - {:width "full" - :height 100 + :width width + :height "500" + :overflow true + :box :fl + :vconcat [{:height 300 + :width width + :layer [{:mark "line" + :encoding {:x {:field "date" :type "temporal"} + :y {:field "pl-cum", :type "quantitative"} + :color {:value "red"}}} + {:mark "line" + :encoding {:x {:field "date" :type "temporal"} + :y {:field "pl-r-cum", :type "quantitative"} + :color {:value "blue"} + }}] + } + {:height 100 + :width width :mark "bar" :encoding {:x {:field "date" :type "temporal" :axis {:labels false :description ""}} :y {:field "pl-u", :type "quantitative"}}} - {:width "full" - :height 100 + {:height 100 + :width width :mark "bar" :encoding {:x {:field "date" :type "temporal" :axis {:labels false}} :y {:field "net$", :type "quantitative"}}} - {:width "full" - :height 100 + {:height 100 + :width width :mark "bar" :encoding {:x {:field "date" :type "temporal" :axis {:labels false} } :y {:field "open#", :type "quantitative"}}} - ] + ] }}]) diff --git a/lib/trateg/src/ta/viz/trades_table.cljs b/lib/trateg/src/ta/viz/trades_table.cljs index 495cb83b..bf60148a 100644 --- a/lib/trateg/src/ta/viz/trades_table.cljs +++ b/lib/trateg/src/ta/viz/trades_table.cljs @@ -16,15 +16,16 @@ (defn trades-table [trades] - [aggrid {:data trades + [aggrid {:box :fl + :data trades :columns [{:field :symbol} - {:field :direction} + {:field :direction :headerName "side"} {:field :qty} - {:field :entry-date :format fmt-yyyymmdd} - {:field :exit-date :format fmt-yyyymmdd} - {:field :entry-price} - {:field :exit-price} - {:field :pl :format #(round-number-digits 0 %)}] - :box :fl + {:field :entry-date :format fmt-yyyymmdd :headerName "dt-E"} + {:field :entry-vol :format #(round-number-digits 0 %) :headerName "dt-E"} + {:field :exit-date :format fmt-yyyymmdd :headerName "dt-X"} + {:field :entry-price :headerName "px-E"} + {:field :exit-price :headerName "px-X"} + {:field :pl :format #(round-number-digits 0 %) :type "rightAligned"}] :pagination :false :paginationAutoPageSize true}]) \ No newline at end of file