From 91378289962fe3b354840a6b3b65aec8ac8817af Mon Sep 17 00:00:00 2001 From: louiscklaw Date: Fri, 31 Jan 2025 19:53:54 +0800 Subject: [PATCH] update, --- hkdse_maths_tutor/meta.md | 5 + .../__pycache__/app.cpython-310.pyc | Bin 0 -> 772 bytes .../__pycache__/app.cpython-311.pyc | Bin 0 -> 1331 bytes .../from_customer/backtest_dashboard/app.py | 27 + .../backtest_dashboard/callbacks/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 155 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 182 bytes .../backtest_callbacks.cpython-310.pyc | Bin 0 -> 4428 bytes .../backtest_callbacks.cpython-311.pyc | Bin 0 -> 7885 bytes .../remove_strategy_callback.cpython-310.pyc | Bin 0 -> 1390 bytes .../strategy_callbacks.cpython-310.pyc | Bin 0 -> 370 bytes .../strategy_callbacks.cpython-311.pyc | Bin 0 -> 17639 bytes .../callbacks/backtest_callbacks.py | 178 + .../callbacks/strategy/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 164 bytes .../__pycache__/components.cpython-310.pyc | Bin 0 -> 8017 bytes .../__pycache__/constants.cpython-310.pyc | Bin 0 -> 930 bytes .../__pycache__/helpers.cpython-310.pyc | Bin 0 -> 3458 bytes .../remove_callback.cpython-310.pyc | Bin 0 -> 4348 bytes .../update_callback.cpython-310.pyc | Bin 0 -> 1989 bytes .../callbacks/strategy/components.py | 369 + .../callbacks/strategy/constants.py | 26 + .../callbacks/strategy/helpers.py | 97 + .../callbacks/strategy/remove_callback.py | 187 + .../callbacks/strategy/update_callback.py | 64 + .../callbacks/strategy_callbacks.py | 7 + .../backtest_dashboard/config.py | 5 + .../backtest_dashboard/data/data.csv | 42992 ++++++++++++++++ .../backtest_dashboard/layouts/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 153 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 180 bytes .../__pycache__/components.cpython-310.pyc | Bin 0 -> 1271 bytes .../__pycache__/components.cpython-311.pyc | Bin 0 -> 1891 bytes .../__pycache__/main_layout.cpython-310.pyc | Bin 0 -> 5193 bytes .../__pycache__/main_layout.cpython-311.pyc | Bin 0 -> 8917 bytes .../backtest_dashboard/layouts/components.py | 38 + .../backtest_dashboard/layouts/main_layout.py | 432 + .../backtest_dashboard/models/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 152 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 179 bytes .../__pycache__/backtest.cpython-310.pyc | Bin 0 -> 3339 bytes .../__pycache__/backtest.cpython-311.pyc | Bin 0 -> 5164 bytes .../backtest_dashboard/models/backtest.py | 116 + .../backtest_dashboard/utils/__init__.py | 0 .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 151 bytes .../__pycache__/__init__.cpython-311.pyc | Bin 0 -> 178 bytes .../utils/__pycache__/helpers.cpython-310.pyc | Bin 0 -> 1288 bytes .../utils/__pycache__/helpers.cpython-311.pyc | Bin 0 -> 1846 bytes .../backtest_dashboard/utils/helpers.py | 29 + hkdse_maths_tutor/quotation1/notes.md | 10 + 50 files changed, 44582 insertions(+) create mode 100644 hkdse_maths_tutor/meta.md create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/__pycache__/app.cpython-310.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/__pycache__/app.cpython-311.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/app.py create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/__init__.py create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/__pycache__/__init__.cpython-310.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/__pycache__/__init__.cpython-311.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/__pycache__/backtest_callbacks.cpython-310.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/__pycache__/backtest_callbacks.cpython-311.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/__pycache__/remove_strategy_callback.cpython-310.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/__pycache__/strategy_callbacks.cpython-310.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/__pycache__/strategy_callbacks.cpython-311.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/backtest_callbacks.py create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/strategy/__init__.py create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/strategy/__pycache__/__init__.cpython-310.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/strategy/__pycache__/components.cpython-310.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/strategy/__pycache__/constants.cpython-310.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/strategy/__pycache__/helpers.cpython-310.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/strategy/__pycache__/remove_callback.cpython-310.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/strategy/__pycache__/update_callback.cpython-310.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/strategy/components.py create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/strategy/constants.py create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/strategy/helpers.py create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/strategy/remove_callback.py create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/strategy/update_callback.py create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/strategy_callbacks.py create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/config.py create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/data/data.csv create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/layouts/__init__.py create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/layouts/__pycache__/__init__.cpython-310.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/layouts/__pycache__/__init__.cpython-311.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/layouts/__pycache__/components.cpython-310.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/layouts/__pycache__/components.cpython-311.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/layouts/__pycache__/main_layout.cpython-310.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/layouts/__pycache__/main_layout.cpython-311.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/layouts/components.py create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/layouts/main_layout.py create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/models/__init__.py create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/models/__pycache__/__init__.cpython-310.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/models/__pycache__/__init__.cpython-311.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/models/__pycache__/backtest.cpython-310.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/models/__pycache__/backtest.cpython-311.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/models/backtest.py create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/utils/__init__.py create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/utils/__pycache__/__init__.cpython-310.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/utils/__pycache__/__init__.cpython-311.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/utils/__pycache__/helpers.cpython-310.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/utils/__pycache__/helpers.cpython-311.pyc create mode 100644 hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/utils/helpers.py create mode 100644 hkdse_maths_tutor/quotation1/notes.md diff --git a/hkdse_maths_tutor/meta.md b/hkdse_maths_tutor/meta.md new file mode 100644 index 00000000..ce0a32f6 --- /dev/null +++ b/hkdse_maths_tutor/meta.md @@ -0,0 +1,5 @@ +--- +tags: +--- + +# notes diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/__pycache__/app.cpython-310.pyc b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/__pycache__/app.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..87f26527fa97e00a762500493fcd43f4002d8891 GIT binary patch literal 772 zcmZWm&2AGh5ccd}cDLDdY2g4=h-;)O+$)5TXgP8LgtSC5PCO*rdVgel+a@PO&V2&J z1MnU^g|D0tZ@_`#-INrlEq|Vw&z_mju974sc0AZWpZ#zM`PMprkCuIEm-=df6Apr8 z?d}LtKn8`AInes9a0@TAFifgm~F!ancW8D6+qvh>kg zk!D%-S&IkrLSOF$<2SZF&VDrXT zd!NTAj(ms7@vg10bzwOUFS|ADf454=i=ks&KAjqGt_v{)6N!p(%9&=?Nv0B+Mk?5% zYP1d9mpIFnMoA}ZdZJNjI%Pt%j{1jITH1@V#WnrPR=QhDJjGJeyv%jZ#5HU7R8w14 zsZEUH`4nrNSEX9^j-;&Q!9x>LTClvN)K>F3PMX>9w(&Sqb7{YgNY{c%8l4L@nxm*u zD(SVc-L(ca>aMRH>9@?@M#1Q&C>i{BT^M8a45RF?jMHF#HFL;jr1OCvP=+FrIwVT5!d;Z{NJHdGq_t?z>!02bB9CR~Kg$ zfG;xWN&1iTeTe|P02^$=z>sT#fkO=9B#Z=+vBDHbHPnu;vZRwTQXN0Z(vD_m9Y4h~ zj&A53Kh3gEpV3Dk0TdqRdI!=Iwq|P|GCPQ57vHWJ`HbvScQD-TCBZ1vFJ1t8@*?n2 zqaW)hZ5{0Z?cYJPyZCnH6}(BrGaJMvM*qY9Rk8|Cln1a%a9@fR;$1Xv4)$hXU8DJU zy@Oppo<$DX<%HbH&(QqaQ@t5~ugJ0Q-)T0vgZFy}O1ZDHb*_|%6uVlD)Ft0xB@$(r zYuTo4`4$GbDyNoDO=hjR4IdLDoG#XcPq{g7RhNA#e6wmXCIb;G3!mf0#kE*@=y12= z+VrS&7eNeroUYKiZ`SHHzh<$cYBVYu9#(}o%$f8_m3n^7t&8>aZO&ajS&TH(bgWw4 zG!aPBrt^)((&^C%ZQDRTo|dZOIf-VJz4Rz?rWm5=k~ z^1uDNoc{TH{#qA27 zSoYR_X*UsHgV9^Oh@<4MEey16g%A=#w%K_?I2-&=L%0ycHH51{Ttk=%;`+bL%>ZKB zPC>fRv^Q(pT6nn}UcJ^%Ceqix0h%AuG?lilkcqG`yq$TLeV%R3HfMi^=|UR_nK&|` zKCo2^2FF^}R`u=qmkX~KFhJec#}D-JKpp>yBDqe;Rf7{FTaQ{d_H&mGa+jLQrj9Vk zXaFOv>3t|3Krw(~2qWz|e3Kp76z^wYVGxz=EFZeKljpq>kQ+Eze4iydMJ Z@A#8GrNf?{oVudy<%g!ml)W+O{{~^udAa}q literal 0 HcmV?d00001 diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/app.py b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/app.py new file mode 100644 index 00000000..5f58a625 --- /dev/null +++ b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/app.py @@ -0,0 +1,27 @@ +import dash +from dash import dcc, html +from utils.helpers import load_data +from layouts.main_layout import create_layout +from callbacks.backtest_callbacks import register_backtest_callbacks +from callbacks.strategy_callbacks import register_strategy_callbacks + +# Create the Dash app +app = dash.Dash(__name__, + prevent_initial_callbacks=True, + suppress_callback_exceptions=True) + +# Load data +try: + full_data = load_data() +except Exception as e: + raise Exception(f"Error: {str(e)}") + +# Set up the layout +app.layout = create_layout(full_data) + +# Register callbacks +register_backtest_callbacks(app) +register_strategy_callbacks(app) + +if __name__ == '__main__': + app.run_server(debug=True) \ No newline at end of file diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/__init__.py b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/__pycache__/__init__.cpython-310.pyc b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d3c5a8d4374240766ad9a717d48b9771608801bf GIT binary patch literal 155 zcmd1j<>g`k0!iMAbP)X*L?8o3AjbiSi&=m~3PUi1CZpd2 zCSzPui?d7e3u2NIle0@wi%a5D5{omE@)L_vVv-YcazMhxG4b)4d6^~g@p=W7w>WHa S^HWN5Qtd$I7Bc|}76t&MQY2^q literal 0 HcmV?d00001 diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/__pycache__/__init__.cpython-311.pyc b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2ac990fd1a2027d5ac90d7db956beaf9857279b8 GIT binary patch literal 182 zcmZ3^%ge<81j{Ctr-SInAOZ#$p^VRLK*n^26oz01O-8?!3`I;p{%4TnFHdKyn9$ literal 0 HcmV?d00001 diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/__pycache__/backtest_callbacks.cpython-310.pyc b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/__pycache__/backtest_callbacks.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d8bfe5a5fd34f451d4cb8b427f16de2f2fca1cd8 GIT binary patch literal 4428 zcmb_g>u(#!5x>1V9*<8^67{ldS$VZ#S}70PaqPHuVoO2_v<_ms2y%76oVY9M>?4oa zU0M=*EDFel6`(?rert^Y_DAiq=-ASchsW|`T%;w z&Cbs5&Ccx3{$`}8RLUFhy!E@y=9O8)_z*h>9}_w+!7JVZLJdknqeVQ@GCi|pc@{yL z8QQIkm(jcxI$joJR_J;;-R4^buc*iDu+%DhWzA>8kygd40PoQ3ZNnR-)>WhC4qz3d zW~t2A+nugd&Z}LCZRV=NjpmCS-%4 zqVz!@M&P{!ulNCw*tlUxLJ2juO=vA@>o$Y^rX@|Z2_zPEX->C!TF`AVZA)oePTP^R zt)%T}+K#2|IJV6^tJMtT(w$?aaj^VCN`UoQo^Kjm4uk z@)Y8Y7HMhM>W{}`hx*lc{6>M6>BugDvFaXQjr076asIABD<7C`LPz@(Ai1k0V_Gua zpOi(|i)uU(Pl9(75QmdZlTPhs`crh8&H#Ue&H_D3j{%*d$AQk%M}R&`A8VRTr#~G} z-6+ZOjS;Y-0`I6CyEhJPl~(VYDPKXJK2D#YUkXS(O}{KB=##WYpQ2x(U!@DX8M?Te z13N7G^e*7Xp2jL@tfm^P2Q-5G3p76S1sbb~M*3`jhSvH=8D6qEV485v-YoQq zGee-P-K#L3zRAGe-kGgGIGnKjcj3NOr`(RN|&}APTK)^Z=j89*= zboTnU1mi-}>q|`Blu_sU5W2u%vA!Asyj@2$K(W{#+!HN!wp9LH7y#Nwt-Cp#DGQ#G|BYC|Jh!EO>Hlp_@2G| z7%qX9atx4~otTzc(`G)3^2d-IHs>^o9tQ%cdzeI&lOz|<5$ithFwFOmj@YKkHlncG zY76D)j3ibthJDe1BonloUcP?uT0QxuH*qmd_48M?lKCsX3?i7avNvUBB}tO=*C0zC zn#QYOQS;}2ee(LdKl{anCUoHW{e@aa**w~S^b2+fubfsEb)Bt*H?|(}pcl0zq$$zy z8(@+uB^K8=0xH)v{V*~eDkxwz8&@sIHN5%fHGx;tX<0~(&HyU zog!Px#wEQXQpsAK5blcSVs8!h#e-ZQMqI$vVqOjkhK+=EwRqL%01p;yu(CkVyz=gb~9xBEPTsf1mfkc4R6As zn~8{O!+RB8p#u~#*uf%(8h7m>^7gF7=hpNstC<=x^w}iW?(p6F2`WOZRiozcZvex; ziR3bpSCQ=5q_UhfB2Go`*fQr4pAXveDLBo;4l<6Ht9^F9c3fG0r^74owK6*tkSN0Z z!m#4w8I;-@p)wr2h5!oM5_R7KuxBc_1es`s`z@w2o#0j^RbE6KYd^UB9I@NW`IOcr z_Zv)^&B)8Xk`%uDDKxjHS8aFr1ojYYp_~mGQdup3lRpoAuhi|JIrY%rinSWe@Oh0>>y%5E3EX8r_q)(wP+KH- z4lX`+^u^;#=;$(%DIkilBv`Zj5tO0x{0SsFW#CR7*cwb@YW_bZWlV44BOq=yPi(Vj z=B+YuNyVy=B5{cGuxJ;dX98C-k-B!?%9Dx-<5rQ3S!2YsY?ujS#QD3E1DgKqPwhP9 zP52ezhvViy{p`=|PwY|WkM`t$KW~go5*zGEer4Ud#Q9sfXdloH7JX{F$($lskpFDj zt(f?&`#tmX-tzyYpaCp(hjkD~jN^z99DxV$#;EcUzq;ejRwP zikKa4EjGE|S+7T{H&{aouXL!(zl}3T(wW156eLJ9f}kCW#bI^GU6`vaJK+2N2Ds;7 Wem7+2v5*#se^sNXy~RL*pZO1Sn#^ke literal 0 HcmV?d00001 diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/__pycache__/backtest_callbacks.cpython-311.pyc b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/__pycache__/backtest_callbacks.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6fe93d45fa4a1fee6cd56f990b80307750b172f4 GIT binary patch literal 7885 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 EH&1`|A^-pY literal 0 HcmV?d00001 diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/__pycache__/remove_strategy_callback.cpython-310.pyc b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/__pycache__/remove_strategy_callback.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aac9e800e1ec33d8fbb5e812d25da93910c5f3dc GIT binary patch literal 1390 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 b@$q^EmA5!-a`RJ4b5iX<78El92^IzbF6<~k literal 0 HcmV?d00001 diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/strategy/__pycache__/components.cpython-310.pyc b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/strategy/__pycache__/components.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e9d2d905339874cdfa9d07673ad03c915e688ce4 GIT binary patch literal 8017 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 z zulE%GIyu(&5DNB`yMU*=%9;^TR!CV=ll%{}m<_PW0)W87rAf!w@|8XhV1*1%QiuCl zWP@j$aM$^(J&nH#h=z;KhPU8avJpUETE4GuDRpfNxg9pzFZFc(`#qh2nvLTEM6a2< z>W5Ib26H@|1fV~Aj++`g(lasgM9*SJVN{OYH!#|#=emixZvKDfO7mdHY5t`X@z?eM zZ`AO1(oYQ!sKcFQKWaEgO~HXd6ifLlMwux<0Nf&(pCTPg(@{fiz0SQl0P2pn$M8}h zPx0mguJG4D>NwH2zj{kuLx6+8NWrBt7$eH)VbwBd!cOXBPVdWr9j9gEL=i+GZke)3xwuP25*}FagoeH2tS)SCmAD? zae+W`1`Uw~vr@}0axP)_E6AaR$?!B$PN2Gw0rT?R@90fS(k96duqz5_hLHlr(-1VR8 zAFCg!6Z^Oy=sz|e$4{rg*+2bCqBm2Qn*nw?d>)1V@bSc4&|1XyF=qj?Ad|e;;6M1y zkZ)pEQV{qGN)L9Mj$frmBye&5je#Csa`*=E|4{PNQ(45Aw>h^mYLU zOO^y{m-X7+mPaeyIXM1D=tbDrt{UYwAqcumla36`lh^dBgek})MJ}goB|Hopb}}jD zq&y~;ad@!ni&73k{<}2M?-ALTB0BWh=k%{=Ds5a|M?mtF`49S}iDah!SXasBsgF!e z{{#Y}s*hFW_+$04`JtKx_d~rv&&j&=TOauut#bwbml&x^IfVX?%0swhFv3wtZQMyp zB@rf)ol7hltQiuP#Ah~r(_mR?m2$99X<1q&IMoX>MJnGO`i!^pJ&h72WJ#n2D8!{b z7QmD7Zp#Zpx9;_Q@6@>l76g>4W?o#F4-&7`xdsPipYZZO!CDf5J$$3#;p2uMI_g5eYVxNb!jK_$JoW;I@<-D&GPhj>)M0e_;~$)Ofi^ArBpXtKwYQ4C2gqCB?g z)HUg5!Ymw;Or+dZ!$PEPYFn^%@ogBg3>*g>;}&8|7&G&}TEpil$+i028loZ`EVZ~Pnu3T8F&!edQX0K!D38I&B6@?%qjVL%7#TeS zFkaruXtSMwFf4Q zzeQ6b&xrpP5syfXNFC%;p#W`4IZL#{gJmRtK+9Q>O4Vz^f_#VG{A~~f7hrIo>ia;j zpC|v+R9_-(xQaJMRaipxkUk198#N9|3cL46{~vvr+<9bma3O#GZf;;Liy!B$U0IjE zMK>j7zNF31&zFY9c=GLTs*X6TQ3)28fbO9v?5WCJ80-?9OE*lCX|K`rX|Kd+A_?Kt zU}M8%*XVPR%+1S0j|?It%1WK(G>u*BmpdUp7TA|zaJo(Jd6JrtK7iyiv?xkg*)#T+ N?S+$;I9{{;&bZjk^0 literal 0 HcmV?d00001 diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/strategy/__pycache__/constants.cpython-310.pyc b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/callbacks/strategy/__pycache__/constants.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8233e4dbd458b17e527f99ba2cb3be2060e1838a GIT binary patch literal 930 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 OdDRUpyb2nU)%YJUom1QZ literal 0 HcmV?d00001 diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/layouts/__pycache__/main_layout.cpython-311.pyc b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/layouts/__pycache__/main_layout.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8c14794cc44229be316d4e535fb204f0846893b5 GIT binary patch literal 8917 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 a8go)T?$(&7T6r7xn@-kcFvlZZ?tcMPTX|Rj literal 0 HcmV?d00001 diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/layouts/components.py b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/layouts/components.py new file mode 100644 index 00000000..c4c8c0f4 --- /dev/null +++ b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/layouts/components.py @@ -0,0 +1,38 @@ +from dash import html, dcc + +def create_metric_card(label, value, bg_color): + return html.Div([ + html.Div(label, style={ + 'fontSize': '13px', + 'color': '#555', + 'fontWeight': '500', + 'marginBottom': '4px', + 'whiteSpace': 'nowrap' + }), + html.Div(value, style={ + 'fontSize': '15px', + 'fontWeight': 'bold', + 'color': '#2c3e50' + }) + ], style={ + 'padding': '12px', + 'backgroundColor': bg_color, + 'borderRadius': '8px', + 'boxShadow': '0 1px 3px rgba(0,0,0,0.1)', + 'textAlign': 'center', + 'minWidth': '150px' + }) + +def date_range_picker(): + return html.Div([ + html.Label("Select Date Range:", style={'marginRight': '10px'}), + dcc.DatePickerRange( + id='date-range-picker', + start_date=None, + end_date=None + ), + html.Button('Apply', id='apply-date-range', n_clicks=0, + style={'marginLeft': '10px', 'backgroundColor': '#1f77b4', 'color': 'white', 'border': 'none', 'padding': '5px 15px', 'borderRadius': '4px'}), + html.Button('Reset', id='reset-date-range', n_clicks=0, + style={'marginLeft': '10px', 'backgroundColor': '#7f7f7f', 'color': 'white', 'border': 'none', 'padding': '5px 15px', 'borderRadius': '4px'}) + ], style={'marginBottom': '20px'}) \ No newline at end of file diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/layouts/main_layout.py b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/layouts/main_layout.py new file mode 100644 index 00000000..7c434b75 --- /dev/null +++ b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/layouts/main_layout.py @@ -0,0 +1,432 @@ +from dash import html, dcc +from layouts.components import create_metric_card + +def create_layout(full_data): + return html.Div([ + # Keep your existing stores + dcc.Store(id='backtest-results', storage_type='memory'), + dcc.Store(id='forwardtest-results', storage_type='memory'), + dcc.Store(id='current-params', storage_type='memory'), + # Add new store for selected strategies + dcc.Store(id='selected-strategies', data=[], storage_type='memory'), + + + html.Div([ + html.H1("Backtest Strategy Dashboard", + style={'textAlign': 'center', 'marginBottom': '20px'}), + + # Add the mode switch section here + html.Div([ + html.Div([ + html.Label("Dashboard Mode:", style={ + 'fontWeight': '500', + 'color': '#2c3e50', + 'marginRight': '10px', + 'fontSize': '16px' + }), + dcc.RadioItems( + id='dashboard-mode', + options=[ + {'label': 'Single Strategy Mode', 'value': 'single'}, + {'label': 'Aggregative Mode', 'value': 'aggregative'} + ], + value='single', + style={'display': 'inline-block'}, + inputStyle={'marginRight': '5px'}, + labelStyle={'marginRight': '20px', 'cursor': 'pointer'} + ), + # Changed ID for the second clear selection button + html.Button( + 'Clear Selection', + id='strategy-clear-selection', + style={ + 'marginLeft': '10px', + 'padding': '5px 10px', + 'backgroundColor': '#dc3545', + 'color': 'white', + 'border': 'none', + 'borderRadius': '4px', + 'cursor': 'pointer', + 'display': 'none' # Initially hidden + } + ), + html.Div( + id='selected-count', + style={ + 'marginLeft': '15px', + 'display': 'inline-block', + 'color': '#2c3e50' + } + ) + ], style={'display': 'flex', 'alignItems': 'center'}) + ], style={ + 'marginBottom': '20px', + 'padding': '15px', + 'backgroundColor': '#f8f9fa', + 'borderRadius': '8px', + 'border': '1px solid #e9ecef' + }), + + # Parameters input section + html.Div([ + # First row of inputs + # First row of inputs + html.Div([ + html.Div([ + html.Label("Backtest End Date:", style={ + 'fontWeight': '500', + 'color': '#2c3e50', + 'marginBottom': '5px', + 'display': 'block' + }), + dcc.DatePickerSingle( + id='bt-end-date', + min_date_allowed=full_data.index[0], + max_date_allowed=full_data.index[-1], + initial_visible_month=full_data.index[0], + date=full_data.index[len(full_data) // 2], + display_format='YYYY-MM-DD', + style={'width': '150px'} + ) + ], style={'marginRight': '20px', 'display': 'inline-block'}), + + html.Div([ + html.Label("Time Factor:", style={ + 'fontWeight': '500', + 'color': '#2c3e50', + 'marginBottom': '5px', + 'display': 'block' + }), + dcc.Input( + id='time-factor', + type='number', + value=8760, + style={ + 'width': '100px', + 'height': '36px', + 'padding': '8px', + 'borderRadius': '4px', + 'border': '1px solid #dcdfe6', + 'fontSize': '14px' + } + ) + ], style={'marginRight': '20px', 'display': 'inline-block'}), + + html.Div([ + html.Label("Commission (%):", style={ + 'fontWeight': '500', + 'color': '#2c3e50', + 'marginBottom': '5px', + 'display': 'block' + }), + dcc.Input( + id='commission', + type='number', + value=0.06, + step=0.01, + style={ + 'width': '80px', + 'height': '36px', + 'padding': '8px', + 'borderRadius': '4px', + 'border': '1px solid #dcdfe6', + 'fontSize': '14px' + } + ) + ], style={'marginRight': '20px', 'display': 'inline-block'}), + + html.Div([ + html.Label("Heatmap Title:", style={ + 'fontWeight': '500', + 'color': '#2c3e50', + 'marginBottom': '5px', + 'display': 'block' + }), + dcc.Input( + id='heatmap-title', + type='text', + placeholder='Enter heatmap title', + autoComplete='off', + style={ + 'width': '200px', + 'height': '36px', + 'padding': '8px', + 'borderRadius': '4px', + 'border': '1px solid #dcdfe6', + 'fontSize': '14px' + } + ) + ], style={'display': 'inline-block'}), + ], style={'marginBottom': '15px'}), + + # Second row of inputs + html.Div([ + html.Div([ + html.Label("Window Range:", style={ + 'fontWeight': '500', + 'color': '#2c3e50', + 'marginBottom': '5px', + 'display': 'block' + }), + html.Div([ + dcc.Input( + id='window-start', + type='number', + value=100, + placeholder='Start', + style={ + 'width': '80px', + 'height': '36px', + 'padding': '8px', + 'borderRadius': '4px', + 'border': '1px solid #dcdfe6', + 'fontSize': '14px', + 'marginRight': '10px' + } + ), + dcc.Input( + id='window-end', + type='number', + value=1000, + placeholder='End', + style={ + 'width': '80px', + 'height': '36px', + 'padding': '8px', + 'borderRadius': '4px', + 'border': '1px solid #dcdfe6', + 'fontSize': '14px', + 'marginRight': '10px' + } + ), + dcc.Input( + id='window-step', + type='number', + value=100, + placeholder='Step', + style={ + 'width': '80px', + 'height': '36px', + 'padding': '8px', + 'borderRadius': '4px', + 'border': '1px solid #dcdfe6', + 'fontSize': '14px' + } + ), + ], style={'display': 'flex'}) + ], style={'marginRight': '40px', 'display': 'inline-block'}), + + html.Div([ + html.Label("Threshold Range:", style={ + 'fontWeight': '500', + 'color': '#2c3e50', + 'marginBottom': '5px', + 'display': 'block' + }), + html.Div([ + dcc.Input( + id='threshold-start', + type='number', + value=0.0, + placeholder='Start', + style={ + 'width': '80px', + 'height': '36px', + 'padding': '8px', + 'borderRadius': '4px', + 'border': '1px solid #dcdfe6', + 'fontSize': '14px', + 'marginRight': '10px' + } + ), + dcc.Input( + id='threshold-end', + type='number', + value=2.0, + placeholder='End', + style={ + 'width': '80px', + 'height': '36px', + 'padding': '8px', + 'borderRadius': '4px', + 'border': '1px solid #dcdfe6', + 'fontSize': '14px', + 'marginRight': '10px' + } + ), + dcc.Input( + id='threshold-step', + type='number', + value=0.2, + placeholder='Step', + style={ + 'width': '80px', + 'height': '36px', + 'padding': '8px', + 'borderRadius': '4px', + 'border': '1px solid #dcdfe6', + 'fontSize': '14px' + } + ), + ], style={'display': 'flex'}) + ], style={'display': 'inline-block'}), + ], style={'marginBottom': '15px'}), + + html.Button( + 'Run Backtest', + id='run-backtest', + n_clicks=0, + style={ + 'width': '150px', + 'height': '36px', + 'marginTop': '10px', + 'backgroundColor': '#1a73e8', + 'color': 'white', + 'border': 'none', + 'borderRadius': '4px', + 'cursor': 'pointer', + 'fontSize': '14px', + 'fontWeight': '500' + } + ), + ], style={ + 'padding': '20px', + 'backgroundColor': 'white', + 'borderRadius': '8px', + 'marginBottom': '20px', + 'boxShadow': '0 1px 3px rgba(0,0,0,0.1)' + }), + + # Heatmap Container + html.Div([ + dcc.Graph(id='combined-heatmap'), + ], style={ + 'marginBottom': '30px', + 'backgroundColor': 'white', + 'borderRadius': '10px', + 'padding': '15px', + 'boxShadow': '0 2px 4px rgba(0,0,0,0.1)' + }), + + # Strategy details section + html.Div([ + html.H3("Strategy Details", + id='strategy-header', + style={'textAlign': 'center', 'marginTop': '30px', 'marginBottom': '20px'}), + + # Date range picker section + html.Div([ + html.Label("Select Date Range:", style={'fontWeight': 'bold', 'marginRight': '10px'}), + dcc.DatePickerRange( + id='date-range-picker', + min_date_allowed=full_data.index[0], + max_date_allowed=full_data.index[-1], + start_date=full_data.index[0], + end_date=full_data.index[-1], + display_format='YYYY-MM-DD' + ), + html.Button( + 'Apply', + id='apply-date-range', + style={ + 'marginLeft': '10px', + 'backgroundColor': '#2c3e50', + 'color': 'white', + 'border': 'none', + 'padding': '5px 15px', + 'borderRadius': '5px', + 'cursor': 'pointer' + } + ), + html.Button( + 'Reset', + id='reset-date-range', + style={ + 'marginLeft': '10px', + 'backgroundColor': '#7f7f7f', + 'color': 'white', + 'border': 'none', + 'padding': '5px 15px', + 'borderRadius': '5px', + 'cursor': 'pointer' + } + ) + ], style={ + 'marginBottom': '20px', + 'display': 'flex', + 'alignItems': 'center', + 'justifyContent': 'center' + }), + + # New flex container for metrics and plots + html.Div([ + # Left side - Metrics and Yearly Returns + html.Div([ + # Metrics + html.Div(id='strategy-metrics', style={ + 'display': 'grid', + 'gridTemplateColumns': 'repeat(2, 1fr)', + 'gap': '10px', # Reduced from 20px + 'padding': '10px', # Reduced from 20px + 'backgroundColor': '#f8f9fa', + 'borderRadius': '8px', # Reduced from 10px + 'boxShadow': '0 2px 4px rgba(0,0,0,0.1)', + 'height': 'fit-content', + 'marginBottom': '15px', # Reduced from 20px + 'fontSize': '12px' # Added to reduce text size + }), + + # Yearly Returns + html.Div([ + dcc.Graph(id='yearly-returns-plot') + ], style={ + 'backgroundColor': 'white', + 'borderRadius': '8px', # Reduced from 10px + 'padding': '10px', # Reduced from 15px + 'boxShadow': '0 2px 4px rgba(0,0,0,0.1)', + 'marginTop': '15px' # Reduced from 20px + }) + ], style={ + 'width': '30%', + 'marginRight': '15px' # Reduced from 20px + }), + + # Right side - Plots + html.Div([ + # Equity curve + html.Div([ + dcc.Graph(id='equity-curve') + ], style={ + 'marginBottom': '20px', + 'backgroundColor': 'white', + 'borderRadius': '10px', + 'padding': '15px', + 'boxShadow': '0 2px 4px rgba(0,0,0,0.1)' + }), + + # Underwater plot + html.Div([ + dcc.Graph(id='underwater-plot') + ], style={ + 'backgroundColor': 'white', + 'borderRadius': '10px', + 'padding': '15px', + 'boxShadow': '0 2px 4px rgba(0,0,0,0.1)' + }) + ], style={ + 'width': '70%', # Adjust this value to change the width of the plots section + 'display': 'flex', + 'flexDirection': 'column' + }) + ], style={ + 'display': 'flex', + 'marginTop': '20px' + }) + ]) + ], style={ + 'maxWidth': '1800px', + 'margin': '0 auto', + 'padding': '20px', + 'backgroundColor': '#ffffff' + }) + ]) \ No newline at end of file diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/models/__init__.py b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/models/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/models/__pycache__/__init__.cpython-310.pyc b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/models/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a0d493e1c873f70feb4954ee9aab238d7e021d13 GIT binary patch literal 152 zcmd1j<>g`k0x8~#bP)X*L?8o3AjbiSi&=m~3PUi1CZpd2 zCSzPui?d7e3u2NIle0@wi%a5D5{omE@)L_vVsi6SQge!9;^Q;(GE3s)^$IF)ao7L_ QOLJ1~K!z4G0SOic0504kN&o-= literal 0 HcmV?d00001 diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/models/__pycache__/__init__.cpython-311.pyc b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/models/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..81003a5316126478334b862ef9770acba9e77eab GIT binary patch literal 179 zcmZ3^%ge<81lkkJ(?RrO5CH>>P{wCAAY(d13PUi1CZpd+?N@8(FQhs7lN=$BkN@`AV wOniK1US>&ryk0@&FAf`^U};XOT@fqLSdhiV{6OLZGb1D82L>2X#0(Sz0K>p7ivR!s literal 0 HcmV?d00001 diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/models/__pycache__/backtest.cpython-310.pyc b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/models/__pycache__/backtest.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6d37b9f43e0194444da64d52b18cbe82b7fd1836 GIT binary patch literal 3339 zcmZuz&5s*N74NEUx9xU&#&%{hGnr)R&9a*q*ja=Si?U%i$qWlfA-mcMAepSJ9#`A$ zOuO5uZcj2po69U0q?M4ka4%)W*SN%Tg)vK!a zoK&lhfhT-+*!y+eFn&kG;=@7Q!kc~(APm7Gqs8>dTP8*xS%X5Wzzksu>m#FOi-NE* z76scin#HHEGVV2*vX9bEB7=@ijQO*Uv5a>cZ)%u^ksA|ZYK$=own4}@j7@l6r*G>f z@F5%98PDyU9dqys#1n-LV`5H?obOxX;v!u!_Kk5VvnGYSC`>(;ge{6wb814bq^(PO zCTqi3F>))ji4T9psd3C!3_WvM>y^PRVHQKsS9yxa;L*v=DfGtWyda!?F5|57(9K!S z^K#GZ8KUxYCftwtt^s*B%~>MfkYJoFPciePHl4{Uxr4YW z$86IWyO^^My~gAMq}YKUAxGM7h3KjIfRC%=rEE2;OfJ%D9+va@Y>*B(MdHxxGpShKL@N&XT7k%hs7CxC9mc6H6us#w^?y16dl%#>O-Wvw-Q-+(N zitl}CyWQ#cR5^@A*zvPOK7EcyYNsece;BN=Jj zr^{#5`+*D-apHdc(J0Igz3q`a2vqq_5O?~BdtViH5LK#*vxTN7^)rz-XEk3^Rwo%A zs+!-)!h@jg3xR~DK`JR(s*v`>Zl-uVRE5X=KnBY6_flnvu-jGEAn;>lrkPMy`ba`; zh;7(KP%x*etL)Bbkd6j=9QaR^IS6A_9466WH_RjoqM@8&C&T9ZaW7D|kjXIil^Mnv zY?2sfK~e!#Rv0B6Wp~3UB6$?V%FdD~OfyLZSJnW{b;U(jx8+BUX2Xchf~UK>=aL?5^hY0IUvNPFByLRzI$s#U>ujH0ZK zi;;a|$Xkf!#e>C&lM z0j|;?14?Y2*=&i`c!OQ$*V$E8;_J)-1(|FUd;Vioy6trGu-Rpx)U9RAFz@V{9u|EpEye_2}Me_mSVe{yR4 zk4~Nc!D-mPw^ps+$U6w9V*yT|Wqefd>K6Jo3POe!DHzHN7yM&Bw*HSUzCYpyWP*~BhOi?(t(Ll8g8YWsqW=t%6>DEC0-ZaKmULc>~qD_8^yzr=zVHUKG zQf}oXlwnxtv5;F6o7yzWaap%ai&_)7Kq}y9d1$Lz=H%tf$sOJDDsww)QqfJC88Xpr#YoVLQ;df3Pdc#)IYTtw5H7|XevqrK);;r`4)0j~PL zLJ}Ph&vr7{3**2W;CO|@DDZHZa5#Ioy`CQn4=24dim0yny`Btu^fd@F8FtbwM|*hR zmp(W+h&MF-`uU@2(e-Y4L-_L3EtDevT~gN)n*UqzaFlrheD86X^}YALd3XD}UMGo0 zgE-Y%3jy8m=(1Dkq>FIo$Lo}x$Yaa_00->VpoO!;F z+TqPAgx5_Z9K|C)3XgQ9)>`KU1*Y97@z0Hs+Ig+Ze&^B~~*pqkdjupJkS5F$yvUC#_e-s5&DUFnRa0%tzML~CO{0~RAe)#|Z literal 0 HcmV?d00001 diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/models/__pycache__/backtest.cpython-311.pyc b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/models/__pycache__/backtest.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..249e63848f5faa26932c32ac493c466dafc8a659 GIT binary patch literal 5164 zcmb_fYfM|$9lw4*FMb;g7)-K~7-$wCkFl0MfU-0MvH_)e5WI`;1q{a5x%Zj`_o5Q1 zlbi@Cw4RwH2C>np zN9LUW|D6B%{h#OD!zVbd$DqCO-i^uM=`ie{h$%FAp7{1S5FcP9M#_TN6S5z{LRJ?;-QIoPE%&Xi++XlQcNgZYVG(-8-c^ot#Z3_7NmILYojK>nd>~9ZX z>eJ%%0?3gvSCI!!zRN`o80e1z9?UCW1St+_$hYZ&7qB{AoPQt#eE8dG31xvQ9ZA(5 zDOKrefhN^^qz;Ew8j}JPw#REVXDTjXyqcFi0AD^W@rTqPZ^3KLZZs0w-95WhIWjMw z$yvFe`G58ru$dh2g0?KqP(3@1*N}<>R>qKv5#is9(^R?Y0(hO2_he|4{jatrV+3mt&%gCG3T@(y?4FZ?l2dm(`46Pn01a z3&ygH6e|p@cNXvyl?AGF+@lfh#^03OP-Wg%mF_Fhd-s>0kx|I|0=`hewHM@}c1mlY zx-ehnAvRr8nE&Btq71)SV+Y}v$8x&1pqJF{o}+S^NqIS+a2NKJju5$`4<)F&gHRKP zsKpYT66;6{I+5izEtt5@U295hDWyrp&Z#=crh_obb>+TdJEzNh(2g>#+8yajo~n^5K#9K54E{(HPccds>f_c{X@Y2)?3m63K{2fc$gLhs}a&{y&}^i{kS zdKYhlzM8i~U&EWAujS3qyLk)rb-V-mdfo}Wr_`&%oS;O>-Lrc>%I`41Z2(+PVf{zM zJp#B5MchN#vQWua4#5i+-lsX^AWGkmblem}u~T%Al+^4L z!GtMz2&-U?x+xhZBEg`Kp~5u7ZIb}CjqoPlvj6cH|A@Zxa?~y>9W{0n!C8XIbC!PM<_S*^L)-wxAtWv-yC{x`8s7|rkPMZBFuXc9 z=|M9&Jj7g}SvJR+2O_iM6yu>MJi`n@Qf$<6feuZ2dLj(cx3k4MGzIpS&l!ioPI?Zu zB2ZJ2`z6H$XfkSi?Rq4@&3n2d%p4`@x~PzU3hbVdR6Sr7>pdbV{d9OSlwFyvtftfE*3J<24tKQaqAxp9`bDJfn zEXSZ1IDPB7>d&!A7JWZ(j`ly3sOr5Q=@r)OsaEDM1K!)X$<}T}!UTu8iHed}F0AaU?Oa)+E?l)~^fplZ#!8^SAn+;-;mM z(}bj4QEpgXN86iv7tjS zb)+j^N>#ihRJ9!9aT?_6-%$b&&JzU&wTX4 zy%#?0TZH7ZZ zu1%ei6pIv1<>+U8tlR_1c@*Y8b(TY{}gv^9M`v|)Yd zMA%!FFZE(;_tyAdCY}ubeIz;Z=HIS}y_ba6OTzP)#pf>zw#&&;pJ4OFv>AwUW8Avx zOf=l97Og)?TfHf(SFkpV*5-A;U~P?QGB(G`nfQ^_#zgBq(DPi{)|9d}3APr|*0RnD zwzin|S=Dpv6++dS%_gC;CuYpp>VJPUG5+zfwPS+KyWS(%PR6uZrKM7zG1e!nSp`tf zjMeeptO}^C8pCUV)*xDo;huyot3$LN!)?nwSp%YtNakD?M|1^-SI5t0O^7z{%v%s` z#qj!sK5Ij?9pSjM4n#XKydiNSTZ!l@B!406LUc8T*TrvUYY<(F=DSvVKkC2N|KY{e zi|N|7RBfA3drGW5m31SDIt;IZKvkz*O(|EC;A#BeDC=LN2BOy%r?M$%#dxw%r!}Eb+%uI>Fblax^Lk&!@L?v9VS9>2{Olkl~izr zhvymeZ=Hlk;Oobpn4*GVxJh##z}z+X2aLG@>&$uRzSq78*JdP0y~O+qSoEY}&q0?} h$Yin%W=j@+8O)k2{4$s)S^mo^&&X=B*#0i3=|9B4RzLs% literal 0 HcmV?d00001 diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/models/backtest.py b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/models/backtest.py new file mode 100644 index 00000000..c2ee2b09 --- /dev/null +++ b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/models/backtest.py @@ -0,0 +1,116 @@ +import pandas as pd +import numpy as np +from utils.helpers import zscore + +def backtest(df, window, threshold, time_factor, commission, mode): + df = df.copy() + df['O2C_chg'] = df['Close'] / df['Open'] - 1 + factor = df.active_addresses + df['indicator'] = zscore(factor, window) + + longCondition = df['indicator'].shift() > threshold + shortCondition = df['indicator'].shift() < -threshold + + df['pos'] = np.where(longCondition, 1, np.where(shortCondition, -1, 0)) + df['pnl'] = df['pos'] * df['O2C_chg'] - commission * abs(df['pos'].diff()) + sharpe = df['pnl'].mean() / df['pnl'].std() * np.sqrt(time_factor) + + if mode == 'heatmap': + return pd.Series([window, threshold, sharpe], index=['window', 'threshold', 'sharpe']) + + elif mode == 'full_report': + ar = df.pnl.mean() * time_factor * 100 + mdd = (df.pnl.cumsum() - df.pnl.cumsum().cummax()).min() * 100 + calmar = ar / abs(mdd) + + beta, _ = np.polyfit(df.Close.pct_change()[1:], df.pnl.dropna(), 1) + num_trades = int( + pd.concat([abs(df.pos.diff()), pd.Series(abs(df.pos).iloc[-1])]).fillna(df.pos.iloc[0]).cumsum().iloc[ + -1] / 2) + long_duration = (len(df.pos[df.pos == 1]) / len(df.pos)) * 100 + short_duration = (len(df.pos[df.pos == -1]) / len(df.pos)) * 100 + + win_rate = len(df.pnl[df.pnl > 0]) / len(df.pnl[df.pnl > 0] + df.pnl[df.pnl < 0]) * 100 + avg_win = df.pnl[df.pnl > 0].mean() * 100 + avg_loss = df.pnl[df.pnl < 0].mean() * 100 + + turnover_pct = (long_duration + short_duration) / num_trades + turnover = turnover_pct * len(df) * 0.01 + + result_dict = { + 'Return (Ann.) [%]': ar, + 'Sharpe Ratio': sharpe, + 'Calmar Ratio': calmar, + 'Max. Drawdown [%]': mdd, + 'Beta': beta, + 'Win Rate [%]': win_rate, + 'Avg Win [%]': avg_win, + 'Avg Loss [%]': avg_loss, + 'Number of Trades': num_trades, + 'Long Duration [%]': long_duration, + 'Short Duration [%]': short_duration, + 'Turnover [%]': turnover_pct, + 'Turnover Period': turnover, + 'Equity Curve': df.pnl.cumsum().tolist(), + 'Benchmark': df.O2C_chg.cumsum().tolist(), + 'Dates': df.index.tolist(), + 'Returns': df.pnl.tolist(), # Added daily PnL + 'Benchmark Returns': df.O2C_chg.tolist() # Added benchmark returns + } + + return result_dict + +def combine_strategies(df, strategies, time_factor, commission): + """ + Combine multiple strategies and calculate aggregate metrics. + + Parameters: + ----------- + df : pandas.DataFrame + Input data with OHLCV columns + strategies : list of dict + List of strategies with 'window' and 'threshold' parameters + time_factor : int + Time factor for annualization + commission : float + Commission rate + + Returns: + -------- + dict + Dictionary containing combined backtest results + """ + combined_pnl = pd.Series(0.0, index=df.index) + combined_benchmark = pd.Series(0.0, index=df.index) + + # Combine strategies + for strategy in strategies: + result = backtest(df, strategy['window'], strategy['threshold'], + time_factor, commission, 'full_report') + combined_pnl += pd.Series(result['Returns'], index=pd.to_datetime(result['Dates'])) + combined_benchmark += pd.Series(result['Benchmark Returns'], index=pd.to_datetime(result['Dates'])) + + # Average the returns + n_strategies = len(strategies) + combined_pnl = combined_pnl / n_strategies + combined_benchmark = combined_benchmark / n_strategies + + # Calculate metrics + ar = combined_pnl.mean() * time_factor * 100 + mdd = (combined_pnl.cumsum() - combined_pnl.cumsum().cummax()).min() * 100 + calmar = ar / abs(mdd) + sharpe = combined_pnl.mean() / combined_pnl.std() * np.sqrt(time_factor) + beta, _ = np.polyfit(df.Close.pct_change()[1:], combined_pnl.dropna(), 1) + + return { + 'Returns': combined_pnl.values, + 'Benchmark Returns': combined_benchmark.values, + 'Dates': df.index, + 'Equity Curve': combined_pnl.cumsum().values, + 'Benchmark': combined_benchmark.cumsum().values, + 'Return (Ann.) [%]': ar, + 'Sharpe Ratio': sharpe, + 'Calmar Ratio': calmar, + 'Max. Drawdown [%]': mdd, + 'Beta': beta + } \ No newline at end of file diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/utils/__init__.py b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/utils/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/utils/__pycache__/__init__.cpython-310.pyc b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/utils/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..909a9b13dac02208463c4628bca48df3406a70f5 GIT binary patch literal 151 zcmd1j<>g`k0x8~#bP)X*L?8o3AjbiSi&=m~3PUi1CZpd2 zCSzPui?d7e3u2NIle0@wi%a5D5{omE@)L_vVoFOgbBbf)<1_OzOXB183My}L*yQG? Ql;)(`fy^vs0un3?0156Q_5c6? literal 0 HcmV?d00001 diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/utils/__pycache__/__init__.cpython-311.pyc b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/utils/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..42667daf64109c9d39c5080a13c8b073d6693acd GIT binary patch literal 178 zcmZ3^%ge<81c?*N(?RrO5CH>>P{wCAAY(d13PUi1CZpd+?N@8(FQhs7lN=#`=*VO%soY+~fW;NPOPo{bwZU@c|D|_cla;6U-Nv8N@Zt_A;4nMy35hgj$$sUU9Lo z!*5tI7u^x|=EJ-Y`_epg=PsAR2a8nHCYP1ptxx~C$ zupqkZsSqhE*Mm0vkF_g~{s0AqA>4uu2F=?3)a8p;h^3x1^kC|AHYD(iaN1@Gan zBNQ#x^BgX)lQ~>qU!&!%tPJZL)8^yPgo(_dhy+tbjs=TY#iIfW0m9c4LP*gn4CN*#L8qxl~%OWQ!#`*Ck7`9nZICm}4$7HZNRr7%^3> znu&KDX4e1_Rd-OOZoV3;mc2r(CFi6?QjXpoBzkGl3-TU5wHWsCQ4EwU3aq{LOToHA zacp8(*#0qMmd8s}9lj1)ztO2w;(fe!2?f*^&SQKLP7Q3o#^f-q*3h)QYF=U!)2i+& h(V_1AuN?dT*}=G!d|rrMS0wGO3rGM%8r)v%{R{7;M8p69 literal 0 HcmV?d00001 diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/utils/__pycache__/helpers.cpython-311.pyc b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/utils/__pycache__/helpers.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..905b5e81f78fdb248a546123e2cc9d29ff006c66 GIT binary patch literal 1846 zcma(RTSy#N^xm0S-M8x+iLnVz(14ULMSYu*rui=pZRUR`q7Wsun-v#gwl`x3HC$$>bbk_u3Pib!z4yH4 z+;h%PKA#%_eDG*tu1-YgPrfOEaLgRE0keS!B3MP!xUdS-B5@FL4Npr%T0_$gf+ta^ z=5MYtapWV4sq}Et9c8H~J~qUcTKK>bI=Ba?UrQUvh0GK8cglPkZ2`Ys-rtw1f(SVWZspzKBV?=6C>zdw*3~05j zh^DV{LY!t@WtSGKJ-;m$f>y+P;sRbki{d9}0f$@@wgitvr$X4SjU{wdiPDg0OE-0z zupMDlW7G~PQ8SjJGYTOFWsI__*OVZF2KWxzfqaiQAa`l>nQsNB@w#xQ99{}P_oVT+ z!UO*#dWyKWsHP_42?pMT?sshwwzfcK8Gj{NUOgHM4@N#_)L;=sO~n#3sumfe?5?Tl zk+89(n_A?y61{6uX3h|W&EM7(gG7>MOl6UIs_LNFtuGz9htwD|qgq@aUcR7e3ISm# z$stvXDk>WWrgY6;gIxl0xQeXisUl{3Uq*iq{?Tj=PXccC7FVDL{KuA?Z^RgjCFW#< zevynBl*q?FH7GB;<%DL+vsyAimd{)@49$?ae=f~K7?eX!ThNK^G$=#`Jz{%IjYmh# zSe)7}M$MU6g3v|VeM?c3v|z%nzZO&Jgl1mjc8f=@tBX;pn=vh6i_A1a4%;;gLNmHz z&f9@fWHZN+*>&+^%;f`24PHge0YnD4Vg!J*UFB>UEwJa$%>Dra>DdO15Wrk;JRcmlyyJUy z^%>XUU!L@omnDYt)ZzZ6sIl4sZyvU z5)y2@XxA)26>1BN+mq#DZPYBYsAf=uw>(b`8wIk8cD%lac*FI`wb`BvT+9b9z6rFy z4glYg4|L?b9Y2pezy8bh7qhvp%lWR$Z@NZaca7w_!uhUn&J(tzun`0^A#CsjhHC5@ zT}eP|8+^Lhjzlu9FWEvuFO*}!wZVI~pi~I!$T<|(NK&Q4MkieGgtD7J;1x06K@C<3 acTmtO;l5Olp(LtF2gYL<>g27o;P7AQp}~3p literal 0 HcmV?d00001 diff --git a/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/utils/helpers.py b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/utils/helpers.py new file mode 100644 index 00000000..02d19f5c --- /dev/null +++ b/hkdse_maths_tutor/quotation1/from_customer/backtest_dashboard/utils/helpers.py @@ -0,0 +1,29 @@ +import pandas as pd +import numpy as np + +def load_data(file_path='data/data.csv'): + try: + df = pd.read_csv(file_path) + df['date'] = pd.to_datetime(df['date']) + df.set_index('date', inplace=True) + + required_columns = ['Open', 'Close', 'active_addresses'] + missing_columns = [col for col in required_columns if col not in df.columns] + + if missing_columns: + raise ValueError(f"Missing required columns: {missing_columns}") + + return df + + except FileNotFoundError: + raise FileNotFoundError(f"{file_path} not found") + except Exception as e: + raise Exception(f"Error loading data: {str(e)}") + +def get_default_date_range(df): + """Get the earliest and latest dates from the dataframe""" + return df.index.min().strftime('%Y-%m-%d'), df.index.max().strftime('%Y-%m-%d') + + +def zscore(x, window): + return (x - x.rolling(window).mean()) / x.rolling(window).std() \ No newline at end of file diff --git a/hkdse_maths_tutor/quotation1/notes.md b/hkdse_maths_tutor/quotation1/notes.md new file mode 100644 index 00000000..60964fe8 --- /dev/null +++ b/hkdse_maths_tutor/quotation1/notes.md @@ -0,0 +1,10 @@ +--- +tags: python, dashboard +--- + +主要想 de 一個 bug , + +加整多幾個 + +第一功能就係新增一個 correlation heatmap, +另外就新增一個 export 功能,把我所選的數據 export 到 csv