mirror of
https://github.com/NicolasBohn/NexQuant.git
synced 2026-07-29 16:37:43 +00:00
Compare commits
822 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 94ba147569 | |||
| 41b202aa3b | |||
| 4a04d598ef | |||
| aed2ecc9d6 | |||
| d1c6f88b77 | |||
| c69a3c0f71 | |||
| fb97a67e47 | |||
| 62879d46d1 | |||
| 38ad943df8 | |||
| 08a08fa5b0 | |||
| bcaf1c48dd | |||
| 1cb09d73ea | |||
| b7e095d24d | |||
| faa891eb41 | |||
| 8831833fcc | |||
| 78c90f768d | |||
| d80d93eb82 | |||
| af27850bc6 | |||
| dd7cfae685 | |||
| b16904a24f | |||
| f56f178a9d | |||
| 619c43f139 | |||
| cc342cd5c3 | |||
| 673a726146 | |||
| cd986190f4 | |||
| 3409e6a370 | |||
| f0500b4cfe | |||
| d67321d57c | |||
| 67430ac20f | |||
| cf3befb718 | |||
| 58e9a98d52 | |||
| 7951ee0bdf | |||
| 3b21238590 | |||
| 27b2c82d55 | |||
| 21c29fd93a | |||
| 2bc01799d3 | |||
| fe1b37c281 | |||
| 4ed7c0b602 | |||
| d057926d18 | |||
| 4ea4d4fdf0 | |||
| 0af23ebcdc | |||
| bb20fcebd9 | |||
| d670d08c92 | |||
| 3f9d4f45ef | |||
| a015d0127e | |||
| a5d79912e1 | |||
| 4412c2731e | |||
| bfc5fc5178 | |||
| 91e538734b | |||
| 3a2fb424f9 | |||
| 7800805e04 | |||
| 77fdb00e25 | |||
| 342d78e234 | |||
| 747a764664 | |||
| 56bd807d63 | |||
| 9b52be686d | |||
| 6cd9a8c9fc | |||
| 24f0818146 | |||
| b39a71e09e | |||
| 58abebded5 | |||
| a027f6851c | |||
| 5e0d969206 | |||
| e891a6bd7f | |||
| ca56c8a7ea | |||
| b485cdefb2 | |||
| c8901ab8a0 | |||
| 91e0e734df | |||
| 903d4beeef | |||
| 68d5eb4aad | |||
| 1134c0b327 | |||
| f44a7bea0b | |||
| a3ba1db7e4 | |||
| 02e021db2d | |||
| 6eac96c380 | |||
| ef7a62fd7d | |||
| 9f6881c050 | |||
| 9b169fcd75 | |||
| dc3941e331 | |||
| 8e126b0b57 | |||
| 718a7ab779 | |||
| 4e6adc1f20 | |||
| 07c228ef9f | |||
| 121b2dbe48 | |||
| e78398996c | |||
| 6577f61df0 | |||
| 4f0b2be7a7 | |||
| 7f94e3a9c3 | |||
| 4de999c4a3 | |||
| 6c132204c1 | |||
| 262081b2a5 | |||
| 33fde3a0bb | |||
| 337389bd3f | |||
| 2e09996154 | |||
| 39de6bd168 | |||
| 956df9906c | |||
| 2eaa69536c | |||
| 71689b2cac | |||
| 4347c2e98c | |||
| dd4edb8331 | |||
| c8830f3f3d | |||
| 88a1b08b8c | |||
| f0b7fa2466 | |||
| 19eb7ae138 | |||
| ac31473bfc | |||
| fbc1beb927 | |||
| 1b15b53c1a | |||
| 7735e121b4 | |||
| da8f61f874 | |||
| 11b0f1b9ee | |||
| 64734fba4c | |||
| 40f26ac54d | |||
| 0018429dc4 | |||
| b4e11f1895 | |||
| 3b57ea2666 | |||
| 2f54199b44 | |||
| 2173fc8c25 | |||
| 08db63e0fe | |||
| 4728df7f38 | |||
| 5785bcf2ba | |||
| ceabfe9686 | |||
| 0885d4d218 | |||
| c3ef9e2d48 | |||
| 00b588ffd4 | |||
| 0dae88e2cd | |||
| bfbdbcfbbd | |||
| 254eb56da3 | |||
| f87dc2a5b8 | |||
| 5b785346f7 | |||
| aa2a5bae15 | |||
| 30d6afbb45 | |||
| b94414a8ed | |||
| 137bdc15c5 | |||
| 5b6a7d1d9b | |||
| dbbf43558d | |||
| 3fe2ba44df | |||
| 2764da68a3 | |||
| bad433ac47 | |||
| 6dab8fd7dd | |||
| 16aadb7a0d | |||
| acfbd1b097 | |||
| 0334805744 | |||
| 3244ba6833 | |||
| 912163d753 | |||
| f5d17a1508 | |||
| 0284b73596 | |||
| 50916fa6c9 | |||
| 067b5908f4 | |||
| fcfea1943c | |||
| 84b7c30a90 | |||
| 5b541841cf | |||
| f3cb3f8870 | |||
| cf070a004e | |||
| 88e346b6b5 | |||
| 4b444097b2 | |||
| a99b9aad39 | |||
| ea7483f5a7 | |||
| d08ce38fb8 | |||
| 8c454afd17 | |||
| fe9b625091 | |||
| 692f3006ce | |||
| 6b0fae5000 | |||
| 684e41f43a | |||
| 66659e9de2 | |||
| e554b3a680 | |||
| 1aa5cc2535 | |||
| ce25bafbf7 | |||
| af7b1f6c4b | |||
| dd2e06ade6 | |||
| 53dd98abf4 | |||
| 82011ea444 | |||
| 8790503e8d | |||
| 60cae0f588 | |||
| 99a3cad9c9 | |||
| b03b070138 | |||
| 3149557351 | |||
| b08cb3b7c8 | |||
| d531faab38 | |||
| e63961f00e | |||
| 7a8b11e7b6 | |||
| 1eb60d9cb7 | |||
| 061870a71e | |||
| 4fc5eb2285 | |||
| 9e0612ec67 | |||
| a7b5141501 | |||
| 66d07c73f6 | |||
| 47458d612c | |||
| 7ed3cd1240 | |||
| 2f87089f21 | |||
| bbd08460c7 | |||
| 2f48a95d43 | |||
| 8bab00d558 | |||
| f603f2b20f | |||
| 16b552a6e0 | |||
| cb3064e57d | |||
| 34c0848b6c | |||
| c44e593ba1 | |||
| 4de0a85114 | |||
| f40c1b1df5 | |||
| 88eed1474f | |||
| f99ceb4158 | |||
| f113e88461 | |||
| e8c62ab46b | |||
| 105b5791d9 | |||
| ec1a5e7a88 | |||
| 32ecc61153 | |||
| dd259f5302 | |||
| fd2c3b0c50 | |||
| 9897739ad3 | |||
| 11c2298958 | |||
| 0f7f6d176b | |||
| ac55b8bfa1 | |||
| 07407b770d | |||
| a6acd3d959 | |||
| 3e6bb27dd1 | |||
| 4c78a601a5 | |||
| fa3846df31 | |||
| 2e1d0624d2 | |||
| 33c0362296 | |||
| be9490f417 | |||
| c1834c634e | |||
| 020e39d0d7 | |||
| e8a45db801 | |||
| bd23d1759e | |||
| f0f5c221bd | |||
| c28fe11bca | |||
| 8d5fd9943d | |||
| 369bd8bc78 | |||
| a03e0e81c9 | |||
| 0424cd1baf | |||
| e5260cb4ac | |||
| faadc1312e | |||
| 1459ec3bac | |||
| 21809f2a9d | |||
| f1d38d5750 | |||
| 7c41fce8b9 | |||
| 5c7030964b | |||
| 8ee602f178 | |||
| ed92831712 | |||
| 10c0d0c70a | |||
| 061554ef87 | |||
| 156549715d | |||
| 12cf0fb459 | |||
| af67910000 | |||
| 717f9b9e53 | |||
| 11be3a480b | |||
| a7cc6fa5f1 | |||
| ed086dcec4 | |||
| b05c2cba09 | |||
| f71a005d55 | |||
| a0015c1511 | |||
| ea86b537eb | |||
| 4bf16d03cf | |||
| 6dbef13837 | |||
| eff5dcc879 | |||
| 4608a0c63e | |||
| 6e57d0adc4 | |||
| 76660b1ace | |||
| 05bb19ad4b | |||
| 7a7f865733 | |||
| 92bb49fc8f | |||
| a67ec5ba01 | |||
| c4007b4655 | |||
| 3d227787e8 | |||
| 92ce8a898b | |||
| 55f7ccc93d | |||
| 49aec3135e | |||
| 69500fc52d | |||
| d89fb2a82e | |||
| 976b86664f | |||
| 05b584b184 | |||
| c701a8105b | |||
| f075824a16 | |||
| 5c5508c8cc | |||
| a5c5c7172b | |||
| 58b5cc1458 | |||
| 80d81c8ea9 | |||
| 1b762ceddd | |||
| ed84b6b0a3 | |||
| 46367e15f5 | |||
| 0859b9f3a2 | |||
| e03fca3206 | |||
| 977136f1f3 | |||
| bf9efe127d | |||
| 243d5259f7 | |||
| dd10ddf9fd | |||
| a619ff999b | |||
| c215e735c2 | |||
| 0c219b81e0 | |||
| 9f793d58b3 | |||
| f0deace7a7 | |||
| 682f657637 | |||
| af5898211b | |||
| 1a1e88c47c | |||
| 9890bb4b00 | |||
| 5a3a2bf2be | |||
| 77e9f186d5 | |||
| 2187081905 | |||
| 698f89e07c | |||
| 304d3fad2e | |||
| ba76063069 | |||
| d18271275e | |||
| a4986c415c | |||
| 155675d4bf | |||
| 15e3ba9004 | |||
| 671771a626 | |||
| 63ba3296dd | |||
| 805f337fc4 | |||
| bdaa376569 | |||
| de6b7f7648 | |||
| 5bb5722921 | |||
| 1a61d51fa8 | |||
| 2b7e0c4bd0 | |||
| 311c2bf977 | |||
| 50702e254e | |||
| 543fb2c1ef | |||
| 51a5247285 | |||
| a1843a9c4e | |||
| b1029cbd5a | |||
| 0611a0846a | |||
| 4974277f1c | |||
| a7c6fca8b4 | |||
| 1091d5ea29 | |||
| 63ebdb5ada | |||
| d6ce70b551 | |||
| 628deade72 | |||
| 29c245c6e3 | |||
| 2e39700eeb | |||
| 370adbfa1e | |||
| dbaf70ce57 | |||
| 3c6f9ef76d | |||
| 0109b044a0 | |||
| 5a62b47e95 | |||
| 5b47c0ab5c | |||
| 3e60749f70 | |||
| cfa5f382aa | |||
| 9ea44be5f8 | |||
| 37e9912ac0 | |||
| bc9cee49ba | |||
| 763dff1596 | |||
| fe11ee92e6 | |||
| 2c11752a54 | |||
| 7aea9a27dc | |||
| 0c97fe4ae7 | |||
| 586349af36 | |||
| 5b3f5f66cf | |||
| 69e5c3a7de | |||
| b76abe839b | |||
| c8c8e26812 | |||
| 42ea6f106b | |||
| 7742a9e759 | |||
| 8acd24a016 | |||
| 54465d6e82 | |||
| 90f6c94d3e | |||
| 7f4e5096e8 | |||
| a294bc74e0 | |||
| e71d8f6c3c | |||
| 5c7cdf298c | |||
| 8a0a0fa3c4 | |||
| 69c630e983 | |||
| ad12fccb61 | |||
| 9986b5f9ce | |||
| eb1cefb202 | |||
| 4f5cce0607 | |||
| 495063c385 | |||
| 318acb4922 | |||
| 8340ab5d09 | |||
| 22a0c7fe56 | |||
| 02534cfd88 | |||
| 9a8426f373 | |||
| 6eef7517ad | |||
| 29a14e1e22 | |||
| 6f9843a7df | |||
| aa97d5a09d | |||
| 6b7988fba1 | |||
| 93303873d0 | |||
| d0e3fc1573 | |||
| cbedbc7d54 | |||
| 03040de35f | |||
| a73f2dbc12 | |||
| 45931b1012 | |||
| 6c179442bf | |||
| 8affbf89a9 | |||
| 8968ab8c9a | |||
| c89321f5a2 | |||
| d6990a12b8 | |||
| 86e05e6d35 | |||
| fda5c281a8 | |||
| 4f2a915da5 | |||
| ff99e4dc2c | |||
| c5beaeeaf8 | |||
| 82b037cb63 | |||
| 456ed100a0 | |||
| 0798c05cb7 | |||
| 7dbffc516d | |||
| 4509d92ea7 | |||
| 19bb2e740e | |||
| 2fea82bee7 | |||
| c68b597069 | |||
| bbb9d36f77 | |||
| e44bb2dcc1 | |||
| 08fc2dcef3 | |||
| c2759a2a0a | |||
| 05092c5c5b | |||
| d9cee55063 | |||
| fe3337d6ff | |||
| 90f6fde3d4 | |||
| 0e3dcc2d8d | |||
| aedd0a0f72 | |||
| 214da4826f | |||
| f25d729a02 | |||
| 773b7ac439 | |||
| 19dd259165 | |||
| ab52e1b65c | |||
| 959f2fa076 | |||
| f06e6986c8 | |||
| b5631b9f78 | |||
| b662f45021 | |||
| 92d007e179 | |||
| c40f22361c | |||
| e22fb84fb3 | |||
| 80c5eb209e | |||
| 67114540b1 | |||
| 8517eb4a18 | |||
| a16c79e45b | |||
| ce23e1c30b | |||
| 92e7932126 | |||
| 31752bb8f0 | |||
| 7372d1011e | |||
| a918f941dd | |||
| dee11fbd9c | |||
| 0dcc990bd4 | |||
| 3f41e868b3 | |||
| fac39e5588 | |||
| fce5342ad4 | |||
| 44ada185f4 | |||
| 6818ada2cf | |||
| 11e8acc88b | |||
| 9415113e01 | |||
| d3414ea07d | |||
| 8bd2714814 | |||
| 200764d38b | |||
| c1c1342a9e | |||
| 756dfba53f | |||
| 1f0032bc50 | |||
| 9eded6faa8 | |||
| 8783781daa | |||
| da8fbcf4ee | |||
| 22ed8b522b | |||
| 7fc5e73f76 | |||
| e30768969d | |||
| bc8e9dc23a | |||
| 1474478768 | |||
| 260b563710 | |||
| 3ef0b21350 | |||
| 473b9000b9 | |||
| 00b31fa752 | |||
| cc8ae7e1f0 | |||
| 752de9dc67 | |||
| a99276d59b | |||
| 450571e86e | |||
| 7fc9e18a71 | |||
| 031d264957 | |||
| 6da6fb5ee5 | |||
| 525b1a2136 | |||
| 61813cba67 | |||
| b33be5ed5b | |||
| 6b856e2217 | |||
| f5f3e050ab | |||
| 9a43e15bf4 | |||
| 251800396d | |||
| 5f3e382248 | |||
| fa0dbb69fe | |||
| f1e4e9900c | |||
| 203be39ad6 | |||
| 60b66aa6c9 | |||
| b91471181c | |||
| a48abf4a84 | |||
| 5b14779f69 | |||
| 8ea8817e7d | |||
| fd747fe741 | |||
| b666397786 | |||
| 31381743f7 | |||
| e564b418e1 | |||
| d1031ed24d | |||
| d66c045115 | |||
| 7b3473cf30 | |||
| 1d6e97cb99 | |||
| 3d7b2b4567 | |||
| b8a699a0df | |||
| 93b41c1414 | |||
| bb91201dd2 | |||
| e0ae91b497 | |||
| b20692cbd4 | |||
| 78e2665617 | |||
| 40f2b8f98b | |||
| b033215ca9 | |||
| 1f4432c39b | |||
| a9f0fd9786 | |||
| 05bf05ca49 | |||
| 9bba28093c | |||
| 3519b718ea | |||
| cde173688b | |||
| 68668d7a5b | |||
| 55b914fe22 | |||
| b2c1b68d51 | |||
| 14270354d1 | |||
| 7b981cbf09 | |||
| 27596473c7 | |||
| 14421c6cde | |||
| 918b0b0d43 | |||
| 4cd20a464d | |||
| 1c628c6c1a | |||
| a55af6fa00 | |||
| 824390e33c | |||
| 2171e99416 | |||
| b6ad5a6ed3 | |||
| b49505be8e | |||
| 2ed6ec53d9 | |||
| 30b52d7d57 | |||
| d753daf758 | |||
| 74cfc5b906 | |||
| 58910d2ce6 | |||
| bd258fe970 | |||
| 68bf5665f0 | |||
| a57e625b17 | |||
| d7da15c826 | |||
| ac7c2155e4 | |||
| bccf5aed8a | |||
| 5bb73ff6cb | |||
| b772fe2191 | |||
| 28f072da31 | |||
| 34e143cd66 | |||
| e73db7e0d7 | |||
| f120341c9c | |||
| 54cd73eb85 | |||
| 20bd55c3a5 | |||
| 403d7fcf80 | |||
| f5761b1f2d | |||
| d4ade36184 | |||
| 982b43e4fd | |||
| b64ab18caa | |||
| 433594297a | |||
| dff89d2950 | |||
| 0bd8366254 | |||
| 2222897b3f | |||
| fc5f295de7 | |||
| 05c7712770 | |||
| 1737aff7c9 | |||
| 464222094f | |||
| 42d15623ca | |||
| 6c20263e2e | |||
| 50e637f9cd | |||
| 1605cddb40 | |||
| 75e496d461 | |||
| 9fcd5d473e | |||
| 5f1c814355 | |||
| 21d88f38d0 | |||
| fd27842a98 | |||
| bddb5eb935 | |||
| ffc85936f1 | |||
| 241c05ccac | |||
| 50e020b75d | |||
| d1c005ff6d | |||
| 96ee62c7a2 | |||
| c321ebfbcb | |||
| 1a44a954df | |||
| 4d8e334ef8 | |||
| b7be0f35f8 | |||
| 97c1f7a021 | |||
| f362a1618d | |||
| 43703d45d2 | |||
| f06412e628 | |||
| dc2bf4b442 | |||
| 8f1f19d4b9 | |||
| 064733d82b | |||
| da7169675c | |||
| 6de33fa13b | |||
| c1085921b5 | |||
| 18de5eea43 | |||
| 13214ba5d0 | |||
| 56f3a668f2 | |||
| 75f29f0dd9 | |||
| 345a702501 | |||
| d2baa40fb1 | |||
| 216361c640 | |||
| 904f4b8523 | |||
| 59337a9050 | |||
| 0db49991cb | |||
| 34bf87c410 | |||
| f37ec32981 | |||
| f8a4d2a7b5 | |||
| cefef61d0e | |||
| 3090718ca3 | |||
| 7eb7f49f52 | |||
| c255b79da4 | |||
| 94d0eca0ca | |||
| bfdee6b692 | |||
| 732839e4bd | |||
| 98a0801a32 | |||
| e1f86d7ca0 | |||
| 5c7ceec37f | |||
| 723a3f4225 | |||
| b2d56e39e5 | |||
| 0e447a3a19 | |||
| e937f8710c | |||
| b1fce69153 | |||
| b092c24374 | |||
| 1678461f5a | |||
| 35e3156018 | |||
| 611fb28c58 | |||
| 10abb20117 | |||
| 6e96cff435 | |||
| fcf17d3292 | |||
| 4235440ff4 | |||
| 111dddda18 | |||
| 4b25dd1913 | |||
| 2453a6bff7 | |||
| f596e59bd8 | |||
| cb5127735f | |||
| 993f16a089 | |||
| 90ffb6cc8a | |||
| 17526da856 | |||
| eb597924b2 | |||
| e5b1912bfd | |||
| f19a91abfc | |||
| 7ff55f0013 | |||
| 17a901e8da | |||
| 1a19622add | |||
| 7cfeaff95b | |||
| a355b57623 | |||
| 14f7fc5e53 | |||
| 7f4c2d18c6 | |||
| d1fdfe10ea | |||
| 4546ebba98 | |||
| dd600c0b0c | |||
| 0a89784116 | |||
| cb66555a9c | |||
| 1a5aaf3fea | |||
| cff2d79c42 | |||
| ea85d5efc8 | |||
| c3f78f8bce | |||
| d54e14f1d9 | |||
| b7fcb13c33 | |||
| e1032b6a31 | |||
| ca46d11123 | |||
| f9bd19179f | |||
| ae8fc773cb | |||
| 8662211492 | |||
| 892283dc89 | |||
| 76b8f073e4 | |||
| fe7eb4cbe1 | |||
| 8c4c53339c | |||
| ef66c56c2b | |||
| e512d08439 | |||
| c6304add4a | |||
| 90d9cdd0e9 | |||
| bf719e0993 | |||
| 1726caa414 | |||
| 25af7e6cce | |||
| cbbbb4e9a5 | |||
| 450317d8fe | |||
| d62407cfbb | |||
| 09325aed89 | |||
| e0ce86b180 | |||
| 2c206e9ce5 | |||
| 80e4a2aa79 | |||
| 3da6af95bd | |||
| 62beb3a7ae | |||
| a7ec1485a7 | |||
| 992956db8e | |||
| 6fe0d75b38 | |||
| c379b9221a | |||
| effce1df15 | |||
| d57477aefa | |||
| 972d8602c2 | |||
| b3e627fb80 | |||
| 760aa3b2ac | |||
| d3c59b2f33 | |||
| 2161956315 | |||
| 46d8247697 | |||
| 8d88dbec33 | |||
| 254c8f9a49 | |||
| b2de6f3190 | |||
| 61189fbe73 | |||
| 4fff3370bd | |||
| 022a558dd1 | |||
| 5988683cd9 | |||
| a9421cb93f | |||
| ef373e1632 | |||
| a8e4e6b4ae | |||
| 216719670d | |||
| 25472fc1e3 | |||
| 8341430bc9 | |||
| f808c35c34 | |||
| f1a841ba14 | |||
| a1507aa7ab | |||
| f2a5967c12 | |||
| b3f1a10000 | |||
| dab1504b6c | |||
| a211facf79 | |||
| b8a931d0f5 | |||
| 858857a921 | |||
| 8ec00a5120 | |||
| 59cdf946a7 | |||
| 48c5dbfd4d | |||
| 455e73858e | |||
| 7fc53940fd | |||
| 1f9ae1af2e | |||
| a32cfcc116 | |||
| 3c80325aad | |||
| 5cdffe3510 | |||
| 7b865c3337 | |||
| 5a2744069c | |||
| 4f8a9aca40 | |||
| 346261c1ac | |||
| 3ea9d6744e | |||
| 1e710ebeb6 | |||
| f4c09c29a1 | |||
| 7642adc703 | |||
| 5787a546b5 | |||
| e1c310182f | |||
| 35c52bf9c4 | |||
| a885f9cfdc | |||
| 89e6a48b19 | |||
| f7c1c4fd74 | |||
| 437e8d2996 | |||
| 2d5fad409d | |||
| 0705e52d7d | |||
| b93f034b19 | |||
| 0e94e74d20 | |||
| 9c8fed3010 | |||
| f5a2ea3aa1 | |||
| ddc625f443 | |||
| 7cfd5be6ff | |||
| 4beb704b2b | |||
| 19ec34e613 | |||
| e046ce9e0e | |||
| 28dd0a0471 | |||
| a468234da1 | |||
| 725a788c34 | |||
| bbe06b9e50 | |||
| 9e14cefe65 | |||
| 43e24cc996 | |||
| 4b639452af | |||
| 7707f60bc9 | |||
| db2f5f7646 | |||
| e4c2b2537f | |||
| 2f1b393b3e | |||
| ddb2360cd8 | |||
| 0b6687dc3e | |||
| 34ef0c3a78 | |||
| 5ebeb20dc1 | |||
| b61d8847e1 | |||
| f9f46fce1b | |||
| d5f2c6a4d0 | |||
| e343a29c75 | |||
| 287e7e7e90 | |||
| 9ce243551b | |||
| cd59b55247 | |||
| aefba2046a | |||
| 3bf99ca0ee | |||
| b441fb8059 | |||
| d7c0785610 | |||
| 51b6038c2e | |||
| 1ee2f5d764 | |||
| 660371d686 | |||
| b0672baa9d | |||
| 67167041e7 | |||
| f732c39265 | |||
| d94ba1ace2 | |||
| 8dc837b731 | |||
| 05e9522f18 | |||
| 7e6e2b9c3a | |||
| 89c73fa19c | |||
| dbcf1b39de | |||
| 1e905dd426 | |||
| 087b7fa600 | |||
| 2523234d83 | |||
| 1f06e5c3cf | |||
| 003b067499 | |||
| 972bb2d99f | |||
| bfce8a6371 | |||
| b60c2cdc13 | |||
| a2f89951bb | |||
| 868dd724b3 | |||
| 9bfa0e597d | |||
| 2d1bfad557 | |||
| 696edd438c | |||
| d73f27be25 | |||
| fd8a5d1c83 | |||
| 884cef4c7d | |||
| f9a60d3b3c | |||
| 954270a5ca | |||
| f05907138a | |||
| ca6d3194d4 | |||
| a4adc96394 | |||
| 8d35befade | |||
| 597b8a37f9 | |||
| ca4e7e928b | |||
| 145e2f79e5 | |||
| fbe11fcf9b | |||
| 6aa6c74a34 | |||
| fffbd1925b | |||
| 48c227a2e0 | |||
| 25cd4eec76 | |||
| 72e7c3b83a | |||
| 204580bc52 | |||
| 5f3aff4dbe | |||
| 9d1f9ffc08 | |||
| a4d5a2d66a | |||
| ebb2a3cf75 | |||
| 88dd4390b6 | |||
| 03ade870e4 | |||
| 542d41aef4 | |||
| 68d47e1f1f | |||
| ce72d30139 | |||
| 92c46a66b6 | |||
| df1f5e9390 | |||
| 144d0f13b8 | |||
| d90ad532dc | |||
| 3b15bca356 | |||
| c6424e5250 |
@@ -1,6 +0,0 @@
|
||||
[bumpversion]
|
||||
current_version = 0.0.0
|
||||
commit = True
|
||||
tag = True
|
||||
|
||||
[bumpversion:file:pyproject.toml]
|
||||
@@ -1,30 +0,0 @@
|
||||
"""
|
||||
This file is a template for the .env file.
|
||||
|
||||
Please copy this file to .env and fill in the values.
|
||||
|
||||
For more information about configuration options, please refer to the documentation
|
||||
|
||||
"""
|
||||
|
||||
# Global configs:
|
||||
USE_AZURE=False
|
||||
USE_AZURE_TOKEN_PROVIDER=False
|
||||
MAX_RETRY=10
|
||||
RETRY_WAIT_SECONDS=20
|
||||
|
||||
# LLM API Setting:
|
||||
OPENAI_API_KEY=<your_api_key>
|
||||
CHAT_MODEL=gpt-4-turbo
|
||||
CHAT_MAX_TOKENS=3000
|
||||
CHAT_TEMPERATURE=0.7
|
||||
# CHAT_AZURE_API_BASE=<for_Azure_user>
|
||||
# CHAT_AZURE_API_VERSION=<for_Azure_user>
|
||||
|
||||
EMBEDDING_MODEL=text-embedding-3-small
|
||||
# EMBEDDING_AZURE_API_BASE=<for_Azure_user>
|
||||
# EMBEDDING_AZURE_API_VERSION=<for_Azure_user>
|
||||
|
||||
# Cache Setting (Optional):
|
||||
|
||||
# Senario Configs:
|
||||
@@ -1,2 +0,0 @@
|
||||
github:
|
||||
- MIIC-finance
|
||||
@@ -1,51 +0,0 @@
|
||||
---
|
||||
name: "\U0001F41B Bug Report"
|
||||
about: Submit a bug report to help us improve RD-Agent
|
||||
labels: bug
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Bug Description
|
||||
|
||||
<!-- A clear and concise description of what the bug is. -->
|
||||
|
||||
## To Reproduce
|
||||
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
|
||||
## Expected Behavior
|
||||
|
||||
<!-- A clear and concise description of what you expected to happen. -->
|
||||
|
||||
## Screenshot
|
||||
|
||||
<!-- A screenshot of the error message or anything shouldn't appear-->
|
||||
|
||||
## Environment
|
||||
|
||||
**Note**: Users can run `rdagent collect_info` to get system information and paste it directly here.
|
||||
|
||||
- Name of current operating system:
|
||||
- Processor architecture:
|
||||
- System, version, and hardware information:
|
||||
- Version number of the system:
|
||||
- Python version:
|
||||
- Container ID:
|
||||
- Container Name:
|
||||
- Container Status:
|
||||
- Image ID used by the container:
|
||||
- Image tag used by the container:
|
||||
- Container port mapping:
|
||||
- Container Label:
|
||||
- Startup Commands:
|
||||
- RD-Agent version:
|
||||
- Package version:
|
||||
|
||||
## Additional Notes
|
||||
|
||||
<!-- Add any other information about the problem here. -->
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
name: "\U0001F4D6 Documentation"
|
||||
about: Report an issue related to documentation
|
||||
|
||||
---
|
||||
|
||||
## 📖 Documentation
|
||||
|
||||
<!-- Please specify whether it's tutorial part or API reference part, and describe it.-->
|
||||
@@ -1,25 +0,0 @@
|
||||
---
|
||||
name: "\U0001F31FFeature Request"
|
||||
about: Request for a new RD-Agent feature
|
||||
labels: enhancement
|
||||
|
||||
---
|
||||
|
||||
## 🌟 Feature Description
|
||||
<!-- A clear and concise description of the feature proposal -->
|
||||
|
||||
## Motivation
|
||||
|
||||
1. Application scenario
|
||||
2. Related works (Papers, Github repos etc.):
|
||||
3. Any other relevant and important information:
|
||||
|
||||
<!-- Please describe why the feature is important. -->
|
||||
|
||||
## Alternatives
|
||||
|
||||
<!-- A short description of any alternative solutions or features you've considered. -->
|
||||
|
||||
## Additional Notes
|
||||
|
||||
<!-- Add any other context or screenshots about the feature request here. -->
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
name: "❓Questions & Help"
|
||||
about: Have some questions? We can offer help.
|
||||
labels: question
|
||||
|
||||
---
|
||||
|
||||
## ❓ Questions and Help
|
||||
|
||||
We sincerely suggest you to carefully read the [documentation](http://rdagent.readthedocs.io/). After that, if you still feel puzzled, please describe the question clearly under this issue.
|
||||
@@ -1,36 +0,0 @@
|
||||
<!--- Thank you for submitting a Pull Request! In order to make our work smoother. -->
|
||||
<!--- please make sure your Pull Request meets the following requirements: -->
|
||||
<!--- 1. Provide a general summary of your changes in the Title above; -->
|
||||
<!--- 2. Add appropriate prefixes to titles, such as `build:`, `chore:`, `ci:`, `docs:`, `feat:`, `fix:`, `perf:`, `refactor:`, `revert:`, `style:`, `test:`(Ref: https://www.conventionalcommits.org/). -->
|
||||
<!--- Category: -->
|
||||
<!--- Patch Updates: `fix:` -->
|
||||
<!--- Example: fix(auth): correct login validation issue -->
|
||||
<!--- minor update (introduces new functionality): `feat` -->
|
||||
<!--- Example: feature(parser): add ability to parse arrays -->
|
||||
<!--- major update(destructive update): Include BREAKING CHANGE in the commit message footer, or add `! ` in the commit footer to indicate that there is a destructive update. -->
|
||||
<!--- Example: feat(auth)! : remove support for old authentication method -->
|
||||
<!--- Other updates: `build:`, `chore:`, `ci:`, `docs:`, `perf:`, `refactor:`, `revert:`, `style:`, `test:`. -->
|
||||
|
||||
## Description
|
||||
<!--- Describe your changes in detail -->
|
||||
|
||||
## Motivation and Context
|
||||
<!--- Are there any related issues? If so, please put the link here. -->
|
||||
<!--- Why is this change required? What problem does it solve? -->
|
||||
|
||||
## How Has This Been Tested?
|
||||
<!--- Put an `x` in all the boxes that apply: --->
|
||||
- [ ] Pass the test by running: `pytest qlib/tests/test_all_pipeline.py` under upper directory of `qlib`.
|
||||
- [ ] If you are adding a new feature, test on your own test scripts.
|
||||
|
||||
<!--- **ATTENTION**: If you are adding a new feature, please make sure your codes are **correctly tested**. If our test scripts do not cover your cases, please provide your own test scripts under the `tests` folder and test them. More information about test scripts can be found [here](https://docs.python.org/3/library/unittest.html#basic-example), or you could refer to those we provide under the `tests` folder. -->
|
||||
|
||||
## Screenshots of Test Results (if appropriate):
|
||||
1. Pipeline test:
|
||||
2. Your own tests:
|
||||
|
||||
## Types of changes
|
||||
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
|
||||
- [ ] Fix bugs
|
||||
- [ ] Add new feature
|
||||
- [ ] Update documentation
|
||||
@@ -1,19 +0,0 @@
|
||||
updates:
|
||||
- commit-message:
|
||||
prefix: build(actions)
|
||||
directory: /
|
||||
package-ecosystem: github-actions
|
||||
schedule:
|
||||
interval: weekly
|
||||
- commit-message:
|
||||
prefix: build(requirements)
|
||||
directory: /
|
||||
groups:
|
||||
dev:
|
||||
dependency-type: development
|
||||
prod:
|
||||
dependency-type: production
|
||||
package-ecosystem: pip
|
||||
schedule:
|
||||
interval: weekly
|
||||
version: 2
|
||||
@@ -1,70 +0,0 @@
|
||||
concurrency:
|
||||
cancel-in-progress: true
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
ci:
|
||||
if: ${{ !cancelled() && ! failure() }}
|
||||
needs: dependabot
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
cache: pip
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- run: env | sort
|
||||
- run: make dev
|
||||
- name: lint test docs and build
|
||||
run: make lint docs-gen test-offline # test docs build
|
||||
strategy:
|
||||
matrix:
|
||||
python-version:
|
||||
- '3.10'
|
||||
- '3.11'
|
||||
dependabot:
|
||||
if: ${{ github.actor == 'dependabot[bot]' && startsWith(github.head_ref, 'dependabot/pip/') }}
|
||||
permissions:
|
||||
contents: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.head_ref }}
|
||||
- name: Set up Git
|
||||
run: |
|
||||
git config --global user.name github-actions
|
||||
git config --global user.email github-actions@github.com
|
||||
- name: Set up Python with multiple versions.
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
cache: pip
|
||||
python-version: |
|
||||
3.10
|
||||
3.11
|
||||
- name: Install pipenv using pipx
|
||||
run: pipx install pipenv
|
||||
- name: Generate constraints for all supported Python versions
|
||||
run: |
|
||||
CI= PYTHON_VERSION=3.10 make constraints
|
||||
CI= PYTHON_VERSION=3.11 make constraints
|
||||
- name: Push changes if applicable
|
||||
run: |
|
||||
if [[ -n `git status --porcelain` ]]; then
|
||||
git commit -a -m "build: Update constraints for dependabot."
|
||||
git push
|
||||
fi
|
||||
name: CI
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
@@ -1,22 +0,0 @@
|
||||
concurrency:
|
||||
cancel-in-progress: true
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
lint-title:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check PR Title for Conventional Commit Format
|
||||
run: |
|
||||
if ! echo "${{ github.event.pull_request.title }}" | grep -Pq '^(build|chore|ci|docs|feat|fix|perf|refactor|revert|style|test|Release-As)(\(\w+\))?!?:\s.*'; then
|
||||
echo 'The title does not conform to the Conventional Commit.'
|
||||
echo 'Please refer to "https://www.conventionalcommits.org/"'
|
||||
exit 1
|
||||
fi
|
||||
name: Lint pull request title
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
- reopened
|
||||
- edited
|
||||
@@ -1,17 +0,0 @@
|
||||
concurrency:
|
||||
cancel-in-progress: true
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
jobs:
|
||||
documentation-links:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: readthedocs/actions/preview@v1
|
||||
with:
|
||||
project-slug: RDAgent
|
||||
name: Read the Docs Pull Request Preview
|
||||
on:
|
||||
pull_request_target:
|
||||
types:
|
||||
- opened
|
||||
permissions:
|
||||
pull-requests: write
|
||||
@@ -1,50 +0,0 @@
|
||||
name: Release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
release_and_publish:
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: read
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Release please
|
||||
id: release_please
|
||||
uses: googleapis/release-please-action@v4
|
||||
with:
|
||||
# The current PAT (personal access token) was created on 2024-08-05,
|
||||
# since the maximum validity of PAT is 1 year, you need to change the PAT before 2025-08-05.
|
||||
token: ${{ secrets.PAT }}
|
||||
release-type: simple
|
||||
- uses: actions/checkout@v4
|
||||
if: ${{ steps.release_please.outputs.release_created }}
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Python
|
||||
if: ${{ steps.release_please.outputs.release_created }}
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
cache: pip
|
||||
python-version: '3.10'
|
||||
- name: Install dependencies
|
||||
if: ${{ steps.release_please.outputs.release_created }}
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install setuptools wheel twine # better-exceptions(optional for debug)
|
||||
- run: env | sort
|
||||
if: ${{ steps.release_please.outputs.release_created }}
|
||||
- run: make dev
|
||||
if: ${{ steps.release_please.outputs.release_created }}
|
||||
- run: make build
|
||||
if: ${{ steps.release_please.outputs.release_created }}
|
||||
- name: upload
|
||||
if: ${{ steps.release_please.outputs.release_created }}
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
|
||||
run: |
|
||||
make upload
|
||||
+43
-135
@@ -1,19 +1,13 @@
|
||||
# Custom
|
||||
*.swp
|
||||
.DS_Store
|
||||
Pipfile
|
||||
public
|
||||
release-notes.md
|
||||
# Environment
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
# Python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
@@ -27,146 +21,60 @@ parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
# Virtual environments
|
||||
venv/
|
||||
ENV/
|
||||
env/
|
||||
.venv/
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
# IDE
|
||||
.idea/
|
||||
.vscode/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Unit test / coverage reports
|
||||
# Testing
|
||||
.pytest_cache/
|
||||
.coverage
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
# Logs
|
||||
*.log
|
||||
/log/
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
log/
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
# Cache
|
||||
pickle_cache/
|
||||
prompt_cache.db
|
||||
.cache/
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
# Generated/processed data
|
||||
git_ignore_folder/
|
||||
data_raw/
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
# Build artifacts
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
# Local scripts (generated)
|
||||
convert_1min.py
|
||||
import_1min_qlib.py
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
^env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# all pkl files
|
||||
*.pkl
|
||||
|
||||
# all h5 files
|
||||
# Results (Backtesting, Factors, Runs)
|
||||
results/
|
||||
*.db
|
||||
*.csv
|
||||
*_export.json
|
||||
*.h5
|
||||
|
||||
# all vs-code files
|
||||
.vscode/
|
||||
# Documentation (generated)
|
||||
QWEN.md
|
||||
|
||||
# reports
|
||||
reports/
|
||||
|
||||
# git_ignore_folder
|
||||
git_ignore_folder/
|
||||
|
||||
#cache
|
||||
*cache*/
|
||||
*cache.json
|
||||
|
||||
# DB files
|
||||
*.db
|
||||
|
||||
# Docker
|
||||
factor_template/mlruns/
|
||||
env_tpl
|
||||
mlruns/
|
||||
|
||||
# possible output from coder or runner
|
||||
*.pth
|
||||
*qlib_res.csv
|
||||
|
||||
# shell script
|
||||
*.out
|
||||
*.sh
|
||||
# AI Agent Files (generated by Qwen Code)
|
||||
.qwen/
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
# .readthedocs.yml
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Set the version of Python and other tools you might need
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3.10"
|
||||
|
||||
# Build documentation in the docs/ directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
|
||||
# Build all formats
|
||||
formats: all
|
||||
|
||||
# Optionally set the version of Python and requirements required to build your docs
|
||||
python:
|
||||
install:
|
||||
- requirements: requirements/docs.txt
|
||||
- method: pip
|
||||
path: .
|
||||
@@ -1,2 +0,0 @@
|
||||
[client]
|
||||
showSidebarNavigation = false
|
||||
+14
-61
@@ -1,70 +1,23 @@
|
||||
# Changelog
|
||||
|
||||
## [0.2.0](https://github.com/microsoft/RD-Agent/compare/v0.1.0...v0.2.0) (2024-09-07)
|
||||
All notable changes to Predix will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
### Features
|
||||
## [Unreleased]
|
||||
|
||||
* add collect info ([#233](https://github.com/microsoft/RD-Agent/issues/233)) ([89f4af9](https://github.com/microsoft/RD-Agent/commit/89f4af90fb4d95a0689bf9efc8ffd9326469c0aa))
|
||||
* add cross validation for kaggle scenario ([#236](https://github.com/microsoft/RD-Agent/issues/236)) ([e0b03ba](https://github.com/microsoft/RD-Agent/commit/e0b03ba6b5c3d9aa552b99d470e106d4e348e64d))
|
||||
* add progress status for docker env ([#215](https://github.com/microsoft/RD-Agent/issues/215)) ([538d4ef](https://github.com/microsoft/RD-Agent/commit/538d4ef2e52de795b90d3f75b2e1e877ab85c18d))
|
||||
* Added loop code for Kaggle scene. ([#211](https://github.com/microsoft/RD-Agent/issues/211)) ([975c327](https://github.com/microsoft/RD-Agent/commit/975c32715e51aec6b49537401f5fc59115e04a01))
|
||||
* Demo display effect and usage ([#162](https://github.com/microsoft/RD-Agent/issues/162)) ([8cf122a](https://github.com/microsoft/RD-Agent/commit/8cf122a0155f434fa4477ae7a6d616b5caecd3e0))
|
||||
* piloting of the framework ([#227](https://github.com/microsoft/RD-Agent/issues/227)) ([e9b103e](https://github.com/microsoft/RD-Agent/commit/e9b103e684fdd2b98cd1a89971a3fce2d6e884a1))
|
||||
* support more models for kaggle scenario ([#223](https://github.com/microsoft/RD-Agent/issues/223)) ([e3a9659](https://github.com/microsoft/RD-Agent/commit/e3a96598c0720fe092ec86d7ca8c195c7d6bcc72))
|
||||
* update model_experiment.py to support basic EDA ([#220](https://github.com/microsoft/RD-Agent/issues/220)) ([bf2684c](https://github.com/microsoft/RD-Agent/commit/bf2684c4d55ab8e1048ac0291695475ad53b0cd6))
|
||||
### Changed
|
||||
- Rebranded from RD-Agent to Predix for EUR/USD quantitative trading
|
||||
- Updated project metadata for PredixAI organization
|
||||
|
||||
### Fixed
|
||||
- Removed all Chinese stock references, replaced with EUR/USD 1min FX data
|
||||
- Migrated to 1min EURUSD data (2020-2026)
|
||||
- Injected MultiIndex warning into factor interface prompt
|
||||
|
||||
### Bug Fixes
|
||||
---
|
||||
|
||||
* fix some bugs in llm calling ([#217](https://github.com/microsoft/RD-Agent/issues/217)) ([7b010f8](https://github.com/microsoft/RD-Agent/commit/7b010f8b5940aba65a58f1d78192aa80bcd0e654))
|
||||
* package dependency. ([#234](https://github.com/microsoft/RD-Agent/issues/234)) ([46be295](https://github.com/microsoft/RD-Agent/commit/46be2952952af534fd8d98a656c704c688d7cbdd))
|
||||
* remove useless line ([#177](https://github.com/microsoft/RD-Agent/issues/177)) ([64e9a8e](https://github.com/microsoft/RD-Agent/commit/64e9a8e39a2072a962111db18f5b9565df5b0176))
|
||||
## Historical Changes (from RD-Agent upstream)
|
||||
|
||||
## [0.1.0](https://github.com/microsoft/RD-Agent/compare/v0.0.1...v0.1.0) (2024-08-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add entry for rdagent. ([#187](https://github.com/microsoft/RD-Agent/issues/187)) ([121b6d9](https://github.com/microsoft/RD-Agent/commit/121b6d98de38cd03be30cbee47b40baf39a2b60b))
|
||||
* change ui entry ([#197](https://github.com/microsoft/RD-Agent/issues/197)) ([fa5d335](https://github.com/microsoft/RD-Agent/commit/fa5d3354d22240888f4fc4007d9834f7424632aa))
|
||||
* remove pdfs and enable online pdf readings ([#183](https://github.com/microsoft/RD-Agent/issues/183)) ([18c0501](https://github.com/microsoft/RD-Agent/commit/18c05016a23d694c7b12759cf1322562dcffc56a))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Fix a fail href in readme ([#189](https://github.com/microsoft/RD-Agent/issues/189)) ([1b89218](https://github.com/microsoft/RD-Agent/commit/1b89218f6bc697494f4a1b8a42ad18963002714f))
|
||||
* fix quick start problem ([#191](https://github.com/microsoft/RD-Agent/issues/191)) ([44f61bf](https://github.com/microsoft/RD-Agent/commit/44f61bfa1058a8efb59ca48b7f1417765aeea33e))
|
||||
* update command line in readme.md ([#192](https://github.com/microsoft/RD-Agent/issues/192)) ([9c45d24](https://github.com/microsoft/RD-Agent/commit/9c45d24a192da02f7d9765cb001097da1bc36c61))
|
||||
|
||||
## 0.0.1 (2024-08-08)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add description for scenario experiments. ([#174](https://github.com/microsoft/RD-Agent/issues/174)) ([fbd8c6d](https://github.com/microsoft/RD-Agent/commit/fbd8c6d87e1424c08997103b8e8fbf264858c4ed))
|
||||
* Added QlibFactorFromReportScenario and improved the report-factor loop. ([#161](https://github.com/microsoft/RD-Agent/issues/161)) ([882c79b](https://github.com/microsoft/RD-Agent/commit/882c79bf11583980e646b130f71cfa20201ffc7b))
|
||||
* filter feature which is high correlation to former implemented features ([#145](https://github.com/microsoft/RD-Agent/issues/145)) ([e818326](https://github.com/microsoft/RD-Agent/commit/e818326422740e04a4863f7c3c18744dde2ad98f))
|
||||
* Remove redundant 'key steps' section in frontend scene display. ([#169](https://github.com/microsoft/RD-Agent/issues/169)) ([e767005](https://github.com/microsoft/RD-Agent/commit/e76700513bee29232c93b97414419df330d9be8d))
|
||||
* streamlit webapp demo for different scenarios ([#135](https://github.com/microsoft/RD-Agent/issues/135)) ([d8da7db](https://github.com/microsoft/RD-Agent/commit/d8da7db865e6653fc4740efee9a843b69bd79699))
|
||||
* Uploaded Documentation, Updated Prompts & Some Code for model demo ([#144](https://github.com/microsoft/RD-Agent/issues/144)) ([529f935](https://github.com/microsoft/RD-Agent/commit/529f935aa98623f0dc1dda29eecee3ef738dd446))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Add framework handling for task coding failure. ([#176](https://github.com/microsoft/RD-Agent/issues/176)) ([5e14fa5](https://github.com/microsoft/RD-Agent/commit/5e14fa54a9dd30a94aebe2643b8c9a3b85517a11))
|
||||
* Comprehensive update to factor extraction. ([#143](https://github.com/microsoft/RD-Agent/issues/143)) ([b5ea040](https://github.com/microsoft/RD-Agent/commit/b5ea04019fd5fa15c0f8b9a7e4f18f490f7057d4))
|
||||
* first round app folder cleaning ([#166](https://github.com/microsoft/RD-Agent/issues/166)) ([6a5a750](https://github.com/microsoft/RD-Agent/commit/6a5a75021912927deb5e8e4c7ad3ec4b51bfc788))
|
||||
* fix pickle problem ([#140](https://github.com/microsoft/RD-Agent/issues/140)) ([7ee4258](https://github.com/microsoft/RD-Agent/commit/7ee42587b60d94417f34332cee395cf210dc8a0e))
|
||||
* fix release CI ([#165](https://github.com/microsoft/RD-Agent/issues/165)) ([85d6a5e](https://github.com/microsoft/RD-Agent/commit/85d6a5ed91113fda34ae079b23c89aa24acd2cb2))
|
||||
* fix release CI error ([#160](https://github.com/microsoft/RD-Agent/issues/160)) ([1c9f8ef](https://github.com/microsoft/RD-Agent/commit/1c9f8ef287961731944acc9008496b4dddeddca7))
|
||||
* fix several bugs in data mining scenario ([#147](https://github.com/microsoft/RD-Agent/issues/147)) ([b233380](https://github.com/microsoft/RD-Agent/commit/b233380e2c66fb030db39424f0f040c86e37f5c4))
|
||||
* fix some small bugs in report-factor loop ([#152](https://github.com/microsoft/RD-Agent/issues/152)) ([a79f9f9](https://github.com/microsoft/RD-Agent/commit/a79f9f93406aff6305a76e6a6abd3852642e4c62))
|
||||
* fix_release_ci_error ([#150](https://github.com/microsoft/RD-Agent/issues/150)) ([4f82e99](https://github.com/microsoft/RD-Agent/commit/4f82e9960a2638af9d831581185ddd3bac5711fc))
|
||||
* Fixed some bugs introduced during refactoring. ([#167](https://github.com/microsoft/RD-Agent/issues/167)) ([f8f1445](https://github.com/microsoft/RD-Agent/commit/f8f1445283fb89aefeb2918243c35a219a51a56c))
|
||||
* optimize some prompts in factor loop. ([#158](https://github.com/microsoft/RD-Agent/issues/158)) ([c2c1330](https://github.com/microsoft/RD-Agent/commit/c2c13300b9ad315a663ec2d0eada414e56c6f54f))
|
||||
|
||||
|
||||
### Miscellaneous Chores
|
||||
|
||||
* release 0.0.1 ([1feacd3](https://github.com/microsoft/RD-Agent/commit/1feacd39b21193de11e9bbecf880ddf96d7c261c))
|
||||
For earlier changes inherited from the RD-Agent project, see the [upstream changelog](https://github.com/microsoft/RD-Agent/blob/main/CHANGELOG.md).
|
||||
|
||||
+68
-6
@@ -1,9 +1,71 @@
|
||||
# Microsoft Open Source Code of Conduct
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
## Our Pledge
|
||||
|
||||
Resources:
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
|
||||
- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
|
||||
- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
nico@predix.io.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# Contributing to Predix
|
||||
|
||||
We welcome contributions and suggestions to improve Predix. Whether it's solving an issue, addressing a bug, enhancing documentation, or even correcting a typo, every contribution is valuable and helps improve the project.
|
||||
|
||||
## Getting Started
|
||||
|
||||
To get started, you can explore the issues list or search for `TODO:` comments in the codebase by running:
|
||||
```sh
|
||||
grep -r "TODO:"
|
||||
```
|
||||
|
||||
## How to Contribute
|
||||
|
||||
1. **Fork the Repository**: Create a fork of the repository on GitHub.
|
||||
2. **Clone the Repository**: Clone your forked repository to your local machine.
|
||||
```sh
|
||||
git clone https://github.com/your-username/predix.git
|
||||
```
|
||||
3. **Create a Branch**: Create a new branch for your changes.
|
||||
```sh
|
||||
git checkout -b feature/your-feature-name
|
||||
```
|
||||
4. **Make Changes**: Make your changes to the codebase.
|
||||
5. **Commit Changes**: Commit your changes with a descriptive commit message.
|
||||
```sh
|
||||
git commit -m "Description of your changes"
|
||||
```
|
||||
6. **Push Changes**: Push your changes to your forked repository.
|
||||
```sh
|
||||
git push origin feature/your-feature-name
|
||||
```
|
||||
7. **Ensure CI Passes**: Make sure your code passes the automatic CI checks on GitHub.
|
||||
8. **Create a Pull Request**: Create a pull request from your forked repository to the main repository.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Please adhere to the [Code of Conduct](CODE_OF_CONDUCT.md) in all your interactions with the project.
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
If you encounter any issues or have suggestions for improvements, please open an issue on GitHub.
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Ensure your code follows the project's coding standards.
|
||||
- Write clear and concise commit messages.
|
||||
- Update documentation as needed.
|
||||
- Test your changes thoroughly before submitting a pull request.
|
||||
|
||||
Thank you for contributing to Predix!
|
||||
@@ -1,21 +1,21 @@
|
||||
MIT License
|
||||
MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation.
|
||||
Copyright (c) 2025 Predix Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -19,7 +19,7 @@ CONSTRAINTS_FILE := constraints/$(PYTHON_VERSION).txt
|
||||
PUBLIC_DIR := $(shell [ "$$READTHEDOCS" = "True" ] && echo "$$READTHEDOCS_OUTPUT/html" || echo "public")
|
||||
|
||||
# URL and Path of changelog source code.
|
||||
CHANGELOG_URL := $(shell echo $${CI_PAGES_URL:-https://microsoft.github.io/rdagent}/_sources/changelog.md.txt)
|
||||
CHANGELOG_URL := $(shell echo $${CI_PAGES_URL:-https://predixai.github.io/predix}/_sources/changelog.md.txt)
|
||||
CHANGELOG_PATH := docs/changelog.md
|
||||
|
||||
########################################################################################
|
||||
@@ -67,7 +67,9 @@ init-qlib-env:
|
||||
@source $$(conda info --base)/etc/profile.d/conda.sh && conda activate qlibRDAgent && which pip && pip install pyqlib && pip install ruamel-yaml==0.17.21 && pip install torch==2.1.1 && pip install catboost==0.24.3 && conda deactivate
|
||||
|
||||
dev:
|
||||
$(PIPRUN) pip install -U pip setuptools wheel
|
||||
$(PIPRUN) pip install -e .[docs,lint,package,test] -c $(CONSTRAINTS_FILE)
|
||||
$(PIPRUN) pip install -U kaggle
|
||||
if [ "$(CI)" != "true" ] && command -v pre-commit > /dev/null 2>&1; then pre-commit install --hook-type pre-push; fi
|
||||
|
||||
# Generate constraints for current Python version.
|
||||
@@ -81,23 +83,23 @@ constraints: deepclean
|
||||
|
||||
# Check lint with black.
|
||||
black:
|
||||
$(PIPRUN) python -m black --check --diff . --extend-exclude test/scripts --extend-exclude git_ignore_folder -l 120
|
||||
$(PIPRUN) python -m black --check --diff . --extend-exclude "(test/scripts|test/notebook/testfiles|git_ignore_folder|web)" -l 120
|
||||
|
||||
# Check lint with isort.
|
||||
isort:
|
||||
$(PIPRUN) python -m isort --check . -s git_ignore_folder -s test/scripts
|
||||
$(PIPRUN) python -m isort --check . -s git_ignore_folder -s test/scripts -s test/notebook/testfiles -s web
|
||||
|
||||
# Check lint with mypy.
|
||||
# First deal with the core folder, and then gradually increase the scope of detection,
|
||||
# and eventually realize the detection of the complete project.
|
||||
mypy:
|
||||
$(PIPRUN) python -m mypy rdagent/core # --exclude rdagent/scripts,git_ignore_folder
|
||||
$(PIPRUN) python -m mypy rdagent/core
|
||||
|
||||
# Check lint with ruff.
|
||||
# First deal with the core folder, and then gradually increase the scope of detection,
|
||||
# and eventually realize the detection of the complete project.
|
||||
ruff:
|
||||
$(PIPRUN) ruff check rdagent/core --ignore FBT001,FBT002 # --exclude rdagent/scripts,git_ignore_folder
|
||||
$(PIPRUN) ruff check rdagent/core --ignore FBT001,FBT002,I001,E501 # --exclude rdagent/scripts,git_ignore_folder
|
||||
|
||||
# Check lint with toml-sort.
|
||||
toml-sort:
|
||||
@@ -118,11 +120,11 @@ pre-commit:
|
||||
|
||||
# Auto lint with black.
|
||||
auto-black:
|
||||
$(PIPRUN) python -m black . --extend-exclude test/scripts --extend-exclude git_ignore_folder -l 120
|
||||
$(PIPRUN) python -m black . --extend-exclude "(test/scripts|test/notebook/testfiles|git_ignore_folder|.venv|web)" -l 120
|
||||
|
||||
# Auto lint with isort.
|
||||
auto-isort:
|
||||
$(PIPRUN) python -m isort . -s git_ignore_folder -s test/scripts
|
||||
$(PIPRUN) python -m isort . -s git_ignore_folder -s test/scripts -s test/notebook/testfiles -s .venv -s web
|
||||
|
||||
# Auto lint with toml-sort.
|
||||
auto-toml-sort:
|
||||
|
||||
@@ -0,0 +1,528 @@
|
||||
# Predix - QWEN.md Context File
|
||||
|
||||
## Project Overview
|
||||
|
||||
**Predix** is an autonomous AI-powered quantitative trading agent for EUR/USD forex markets. Built on the RD-Agent framework, it automates the full research and development cycle for trading strategies.
|
||||
|
||||
### Core Purpose
|
||||
- Generate trading factors (signals) autonomously using LLMs
|
||||
- Backtest and validate factors on 1-minute EUR/USD data
|
||||
- Optimize portfolios using modern portfolio theory
|
||||
- Target: 1-3% monthly returns with Sharpe > 2.0
|
||||
|
||||
### Key Technologies
|
||||
- **Python 3.10/3.11** - Primary language
|
||||
- **PyTorch** - Deep learning models
|
||||
- **Qlib** - Backtesting engine
|
||||
- **LLM (Qwen3.5-35B)** - Factor generation via local llama.cpp
|
||||
- **Flask** - Web dashboard API
|
||||
- **SQLite** - Results database
|
||||
- **Rich/Typer** - CLI interface
|
||||
|
||||
### Architecture
|
||||
|
||||
```
|
||||
Predix/
|
||||
├── rdagent/ # Core agent framework
|
||||
│ ├── app/
|
||||
│ │ └── cli.py # Main CLI entry point (rdagent command)
|
||||
│ ├── components/
|
||||
│ │ ├── backtesting/ # Backtest engine, metrics, database
|
||||
│ │ ├── coder/
|
||||
│ │ │ └── factor_coder/ # Factor generation & EURUSD-specific modules
|
||||
│ │ └── ...
|
||||
│ └── scenarios/
|
||||
│ └── qlib/ # Qlib integration for FX trading
|
||||
├── results/ # Backtest results (NOT in git)
|
||||
│ ├── backtests/ # Individual factor backtests (JSON/CSV)
|
||||
│ ├── db/ # SQLite database
|
||||
│ ├── factors/ # Factor analysis
|
||||
│ ├── runs/ # Run results & risk reports
|
||||
│ └── logs/ # Backtest logs
|
||||
├── web/ # Dashboard frontend
|
||||
│ ├── dashboard_api.py # Flask API backend
|
||||
│ └── dashboard.html # Web UI
|
||||
├── .env # Environment config (API keys, etc.)
|
||||
├── data_config.yaml # EURUSD data configuration
|
||||
└── requirements.txt # Python dependencies
|
||||
```
|
||||
|
||||
## Building and Running
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
# Clone repository
|
||||
git clone https://github.com/PredixAI/predix
|
||||
cd predix
|
||||
|
||||
# Create conda environment
|
||||
conda create -n predix python=3.10
|
||||
conda activate predix
|
||||
|
||||
# Install in editable mode
|
||||
pip install -e .[test,lint]
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
||||
1. **Create `.env` file:**
|
||||
```bash
|
||||
# Local LLM (llama.cpp)
|
||||
OPENAI_API_KEY=local
|
||||
OPENAI_API_BASE=http://localhost:8081/v1
|
||||
CHAT_MODEL=qwen3.5-35b
|
||||
|
||||
# Embedding (Ollama)
|
||||
LITELLM_PROXY_API_KEY=local
|
||||
LITELLM_PROXY_API_BASE=http://localhost:11434/v1
|
||||
EMBEDDING_MODEL=nomic-embed-text
|
||||
|
||||
# Paths
|
||||
QLIB_DATA_DIR=~/.qlib/qlib_data/eurusd_1min_data
|
||||
```
|
||||
|
||||
2. **Start LLM server (llama.cpp):**
|
||||
```bash
|
||||
~/llama.cpp/build/bin/llama-server \
|
||||
--model ~/models/qwen3.5/Qwen3.5-35B-A3B-Q3_K_M.gguf \
|
||||
--n-gpu-layers 36 \
|
||||
--ctx-size 80000 \
|
||||
--port 8081
|
||||
```
|
||||
|
||||
### Running the Trading Loop
|
||||
|
||||
```bash
|
||||
# Start trading loop (24/7)
|
||||
./start_loop.sh
|
||||
|
||||
# Or single run
|
||||
rdagent fin_quant
|
||||
|
||||
# With dashboard
|
||||
rdagent fin_quant --with-dashboard
|
||||
|
||||
# With CLI dashboard
|
||||
rdagent fin_quant --cli-dashboard
|
||||
```
|
||||
|
||||
### Running the Dashboard
|
||||
|
||||
```bash
|
||||
# Web dashboard (runs with fin_quant --with-dashboard)
|
||||
# Access at: http://localhost:5000/dashboard.html
|
||||
|
||||
# Or standalone
|
||||
python web/dashboard_api.py
|
||||
```
|
||||
|
||||
### Testing
|
||||
|
||||
```bash
|
||||
# Run all tests
|
||||
pytest test/
|
||||
|
||||
# Run with coverage
|
||||
pytest --cov=rdagent --cov-report=html
|
||||
|
||||
# Test backtesting module
|
||||
python rdagent/components/backtesting/backtest_engine.py
|
||||
python rdagent/components/backtesting/results_db.py
|
||||
python rdagent/components/backtesting/risk_management.py
|
||||
```
|
||||
|
||||
### Code Quality
|
||||
|
||||
```bash
|
||||
# Linting
|
||||
ruff check rdagent/
|
||||
|
||||
# Type checking
|
||||
mypy rdagent/
|
||||
|
||||
# Format
|
||||
black rdagent/
|
||||
|
||||
# Pre-commit (install first)
|
||||
pre-commit install
|
||||
pre-commit run --all-files
|
||||
```
|
||||
|
||||
## Development Conventions
|
||||
|
||||
### Language Policy
|
||||
|
||||
**ALL code comments and documentation MUST be in English.**
|
||||
|
||||
❌ **Wrong (German):**
|
||||
```python
|
||||
# Inspiriert von: TradingAgents
|
||||
# Berechnet den Sharpe Ratio
|
||||
# Achtung: Division durch Null möglich!
|
||||
# Hinweis: Diese Funktion ist experimentell
|
||||
```
|
||||
|
||||
✅ **Correct (English):**
|
||||
```python
|
||||
# Inspired by: TradingAgents
|
||||
# Calculates the Sharpe ratio
|
||||
# Warning: Division by zero possible!
|
||||
# Note: This function is experimental
|
||||
```
|
||||
|
||||
**Rationale:**
|
||||
- International collaboration
|
||||
- Better searchability
|
||||
- Professional codebase
|
||||
- Consistent with commit messages (also English-only)
|
||||
|
||||
**Enforcement:**
|
||||
- All new code must have English comments
|
||||
- Existing German comments should be translated when modified
|
||||
- PRs with German comments will be rejected
|
||||
|
||||
### Code Style
|
||||
|
||||
- **Line length:** 120 characters (configured in pyproject.toml)
|
||||
- **Type hints:** Required for all public functions
|
||||
- **Docstrings:** Google style for public APIs
|
||||
- **Imports:** Sorted automatically with isort
|
||||
|
||||
### Testing Practices
|
||||
- Unit tests in `test/` directory
|
||||
- Test files named `test_*.py`
|
||||
- Use pytest fixtures for common setup
|
||||
- Mock external APIs (LLM, yfinance)
|
||||
- Minimum 80% coverage target
|
||||
|
||||
### Commit Conventions
|
||||
```bash
|
||||
git commit --author="TPTBusiness <tpt.requests@pm.me>" -m "type: description"
|
||||
|
||||
# Types:
|
||||
# - feat: New feature
|
||||
# - fix: Bug fix
|
||||
# - docs: Documentation
|
||||
# - style: Formatting
|
||||
# - refactor: Code restructuring
|
||||
# - test: Tests
|
||||
# - chore: Maintenance
|
||||
```
|
||||
|
||||
### Module Structure
|
||||
```python
|
||||
"""
|
||||
Module Name - Brief description
|
||||
|
||||
Longer description if needed.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from typing import Dict, List, Optional
|
||||
from datetime import datetime
|
||||
|
||||
class ClassName:
|
||||
"""Class docstring."""
|
||||
|
||||
def __init__(self, param: type) -> None:
|
||||
"""Initialize."""
|
||||
pass
|
||||
|
||||
def method(self, param: type) -> ReturnType:
|
||||
"""
|
||||
Method docstring.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
param : type
|
||||
Description
|
||||
|
||||
Returns
|
||||
-------
|
||||
ReturnType
|
||||
Description
|
||||
"""
|
||||
pass
|
||||
```
|
||||
|
||||
### Backtesting Module Usage
|
||||
|
||||
```python
|
||||
from rdagent.components.backtesting import (
|
||||
FactorBacktester,
|
||||
ResultsDatabase,
|
||||
PortfolioOptimizer,
|
||||
AdvancedRiskManager
|
||||
)
|
||||
|
||||
# Run backtest
|
||||
backtester = FactorBacktester()
|
||||
metrics = backtester.run_backtest(
|
||||
factor_values=factor_series,
|
||||
forward_returns=forward_returns,
|
||||
factor_name="MyFactor"
|
||||
)
|
||||
|
||||
# Save to database
|
||||
db = ResultsDatabase()
|
||||
db.add_backtest("MyFactor", metrics)
|
||||
|
||||
# Query top factors
|
||||
top = db.get_top_factors('sharpe_ratio', limit=20)
|
||||
|
||||
# Portfolio optimization
|
||||
optimizer = PortfolioOptimizer()
|
||||
weights = optimizer.mean_variance(expected_returns, cov_matrix)
|
||||
|
||||
# Risk management
|
||||
risk_manager = AdvancedRiskManager()
|
||||
report = risk_manager.generate_risk_report(returns, weights)
|
||||
```
|
||||
|
||||
### Key Metrics
|
||||
|
||||
| Metric | Target | Minimum |
|
||||
|--------|--------|---------|
|
||||
| IC (Information Coefficient) | > 0.05 | > 0.02 |
|
||||
| Sharpe Ratio | > 2.0 | > 1.0 |
|
||||
| Max Drawdown | < 15% | < 25% |
|
||||
| Win Rate | > 55% | > 45% |
|
||||
| Annualized Return | > 10% | > 5% |
|
||||
|
||||
### Important Files
|
||||
|
||||
- `rdagent/app/cli.py` - Main CLI entry point
|
||||
- `rdagent/components/backtesting/` - Backtest engine
|
||||
- `rdagent/components/coder/factor_coder/` - Factor generation
|
||||
- `results/README.md` - Results documentation
|
||||
- `data_config.yaml` - EURUSD configuration
|
||||
- `web/dashboard_api.py` - Dashboard API
|
||||
- `requirements.txt` - Dependencies
|
||||
|
||||
### External Dependencies
|
||||
|
||||
- **llama.cpp** - Local LLM inference (Qwen3.5-35B)
|
||||
- **Ollama** - Embedding models
|
||||
- **Qlib** - Backtesting engine
|
||||
- **yfinance** - Live market data
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **LLM Connection Errors:** Ensure llama.cpp server is running on port 8081
|
||||
2. **Embedding Errors:** Check Ollama is running with nomic-embed-text loaded
|
||||
3. **Database Lock:** Close all connections before running multiple processes
|
||||
4. **Memory Issues:** Reduce batch size or context length for LLM
|
||||
|
||||
### Project Status
|
||||
|
||||
- ✅ Factor Generation (110+ factors created)
|
||||
- ✅ Backtesting Engine (IC, Sharpe, Drawdown)
|
||||
- ✅ Results Database (SQLite with queries)
|
||||
- ✅ Risk Management (Correlation, Portfolio Optimization)
|
||||
- ✅ Dashboards (Web + CLI)
|
||||
- ⏳ Live Trading (Paper trading pending)
|
||||
|
||||
### Next Steps
|
||||
|
||||
1. Backtest all 110 factors
|
||||
2. Select top 20 by IC/Sharpe
|
||||
3. Portfolio optimization
|
||||
4. 4 weeks paper trading
|
||||
5. Live trading with small capital
|
||||
|
||||
---
|
||||
|
||||
## Git Commit Guidelines
|
||||
|
||||
### Language Policy
|
||||
|
||||
**ALL commit messages MUST be in English.**
|
||||
|
||||
❌ **Wrong (German):**
|
||||
```bash
|
||||
git commit -m "feat: Neue Funktion hinzugefügt"
|
||||
git commit -m "fix: Fehler behoben"
|
||||
git commit -m "chore: QWEN.md zu .gitignore hinzugefügt"
|
||||
```
|
||||
|
||||
✅ **Correct (English):**
|
||||
```bash
|
||||
git commit -m "feat: Add new feature"
|
||||
git commit -m "fix: Fix bug"
|
||||
git commit -m "chore: Add QWEN.md to .gitignore"
|
||||
```
|
||||
|
||||
### Pre-Commit Checklist
|
||||
|
||||
**BEFORE every commit, you MUST:**
|
||||
|
||||
1. **Run `git status`** and verify:
|
||||
- Only intended files are staged
|
||||
- No generated files (.qwen/, results/, *.db, etc.)
|
||||
- No sensitive data (.env, API keys, etc.)
|
||||
|
||||
2. **Check .gitignore** is working:
|
||||
```bash
|
||||
git status
|
||||
# Verify .qwen/, results/, *.db are NOT shown
|
||||
```
|
||||
|
||||
3. **Review staged changes:**
|
||||
```bash
|
||||
git diff --staged
|
||||
# Review what will be committed
|
||||
```
|
||||
|
||||
4. **Run tests** (if applicable):
|
||||
```bash
|
||||
pytest test/backtesting/ -v
|
||||
# Ensure all tests pass
|
||||
```
|
||||
|
||||
### Commit Message Format
|
||||
|
||||
Use [Conventional Commits](https://www.conventionalcommits.org/):
|
||||
|
||||
```
|
||||
<type>: <description in English>
|
||||
|
||||
[optional body]
|
||||
```
|
||||
|
||||
**Types:**
|
||||
- `feat:` - New feature
|
||||
- `fix:` - Bug fix
|
||||
- `test:` - Tests
|
||||
- `docs:` - Documentation
|
||||
- `chore:` - Maintenance
|
||||
- `style:` - Formatting
|
||||
- `refactor:` - Code restructuring
|
||||
|
||||
**Examples:**
|
||||
```bash
|
||||
feat: Add backtesting tests with 98% coverage
|
||||
fix: Remove .qwen/ from Git tracking
|
||||
test: Add unit tests for ResultsDatabase
|
||||
docs: Update QWEN.md with commit guidelines
|
||||
chore: Add pytest to requirements.txt
|
||||
```
|
||||
|
||||
### Protected Files (NEVER commit)
|
||||
|
||||
These files/directories MUST NEVER be committed:
|
||||
|
||||
```
|
||||
.qwen/ # AI agent files (generated)
|
||||
results/ # Backtest results (sensitive data)
|
||||
*.db # SQLite databases
|
||||
.env # Environment variables (API keys!)
|
||||
git_ignore_folder/ # Generated data
|
||||
*.log # Log files
|
||||
```
|
||||
|
||||
If you accidentally commit any of these:
|
||||
|
||||
```bash
|
||||
# Remove from last commit (keeps files locally)
|
||||
git reset HEAD~1
|
||||
|
||||
# Or remove from tracking
|
||||
git rm -r --cached .qwen/
|
||||
git commit -m "chore: Remove .qwen/ from tracking"
|
||||
```
|
||||
|
||||
### Fixing Past Commits
|
||||
|
||||
**To fix the last 3-5 commits:**
|
||||
|
||||
```bash
|
||||
# For last 5 commits
|
||||
git rebase -i HEAD~5
|
||||
|
||||
# In the editor, change 'pick' to 'reword' for commits to rename
|
||||
# Save and close
|
||||
# Write new English message for each commit
|
||||
```
|
||||
|
||||
**To fix older commits (advanced):**
|
||||
|
||||
```bash
|
||||
# Find the commit hash
|
||||
git log --oneline
|
||||
|
||||
# Start rebase from that commit
|
||||
git rebase -i <commit-hash>^
|
||||
|
||||
# Follow same process as above
|
||||
```
|
||||
|
||||
**Current German commits to fix (as of April 2026):**
|
||||
```
|
||||
73140b68 test: Backtesting Tests mit 98.77% Coverage
|
||||
→ test: Add backtesting tests with 98.77% coverage
|
||||
|
||||
5148d17d chore: QWEN.md zu .gitignore hinzugefügt
|
||||
→ chore: Add QWEN.md to .gitignore
|
||||
|
||||
df93e162 feat: Intelligent Embedding Chunking statt Kürzung
|
||||
→ feat: Intelligent embedding chunking instead of truncation
|
||||
|
||||
01aa183a fix: CLI Dashboard in separatem Terminal-Fenster
|
||||
→ fix: CLI dashboard in separate terminal window
|
||||
|
||||
df356978 feat: predix.py Wrapper für Dashboard-Support
|
||||
→ feat: predix.py wrapper for dashboard support
|
||||
|
||||
89d01f5d feat: Beautiful CLI Dashboard + korrigierter Start-Befehl
|
||||
→ feat: Beautiful CLI dashboard + corrected start command
|
||||
|
||||
48e4f44e feat: Auto-Start Dashboard für fin_quant
|
||||
→ feat: Auto-start dashboard for fin_quant
|
||||
|
||||
59122a19 feat: Dashboard + Live-Daten Integration (Phase 4)
|
||||
→ feat: Dashboard + live data integration (Phase 4)
|
||||
|
||||
a0f414ed feat: EURUSD Trading-Verbesserungen (Phase 2 & 3)
|
||||
→ feat: EURUSD trading improvements (Phase 2 & 3)
|
||||
|
||||
e8b962b5 feat: EURUSD Trading-Verbesserungen implementiert (Phase 1)
|
||||
→ feat: Implement EURUSD trading improvements (Phase 1)
|
||||
```
|
||||
|
||||
**⚠️ Warning:** Rewriting history changes commit hashes. If you've already pushed:
|
||||
|
||||
```bash
|
||||
# After rebasing locally
|
||||
git push --force-with-lease origin master
|
||||
|
||||
# Tell team members to re-clone:
|
||||
git clone <repo-url>
|
||||
```
|
||||
|
||||
### Push Policy
|
||||
|
||||
**BEFORE pushing:**
|
||||
|
||||
1. Verify commit messages are in English
|
||||
2. Verify no protected files are included
|
||||
3. Run tests one final time
|
||||
|
||||
```bash
|
||||
git status
|
||||
git log -3 --oneline # Verify last 3 commits
|
||||
pytest test/backtesting/ -v # Quick test
|
||||
git push origin master
|
||||
```
|
||||
|
||||
### Enforcement
|
||||
|
||||
- All PRs will be rejected if commit messages are not in English
|
||||
- Protected files in commits will be rejected
|
||||
- Tests must pass before merging
|
||||
|
||||
**Remember:** Consistent English commit messages ensure:
|
||||
- International collaboration
|
||||
- Better searchability
|
||||
- Professional project history
|
||||
@@ -1,250 +1,327 @@
|
||||
<h2 align="center">
|
||||
<img src="docs/_static/logo.png" alt="RA-Agent logo" style="width:70%; ">
|
||||
# Predix
|
||||
|
||||
<a href="https://rdagent.azurewebsites.net">🖥️ Live Demo</a> | <a href="https://rdagent.azurewebsites.net/factor_loop">🎥 Demo Video</a> | <a href="https://rdagent.readthedocs.io/en/latest/index.html">📖 Documentation</a>
|
||||
</h2>
|
||||
<h4 align="center">
|
||||
<strong>AI-powered Quantitative Trading Agent for EUR/USD Forex</strong>
|
||||
</h4>
|
||||
|
||||
[](https://github.com/microsoft/RD-Agent/actions/workflows/ci.yml)
|
||||
[](https://github.com/microsoft/RD-Agent/actions/workflows/github-code-scanning/codeql)
|
||||
[](https://github.com/microsoft/RD-Agent/actions/workflows/dependabot/dependabot-updates)
|
||||
[](https://github.com/microsoft/RD-Agent/actions/workflows/pr.yml)
|
||||
[](https://github.com/microsoft/RD-Agent/actions/workflows/readthedocs-preview.yml)
|
||||
[](https://github.com/microsoft/RD-Agent/actions/workflows/release.yml)
|
||||
[](https://pypi.org/project/rdagent/#files)
|
||||
[](https://pypi.org/project/rdagent/)
|
||||
[](https://pypi.org/project/rdagent/)
|
||||
[](https://github.com/microsoft/RD-Agent/releases)
|
||||
[](https://github.com/microsoft/RD-Agent/blob/main/LICENSE)
|
||||
[](https://github.com/pre-commit/pre-commit)
|
||||
[](http://mypy-lang.org/)
|
||||
[](https://github.com/astral-sh/ruff)
|
||||
<p align="center">
|
||||
<a href="#installation">Installation</a> •
|
||||
<a href="#quick-start">Quick Start</a> •
|
||||
<a href="#configuration">Configuration</a> •
|
||||
<a href="#features">Features</a>
|
||||
</p>
|
||||
|
||||
# 📰 News
|
||||
| 🗞️ News | 📝 Description |
|
||||
| -- | ------ |
|
||||
| First release | **RDAgent** is released on Github |
|
||||
<p align="center">
|
||||
<a href="https://github.com/PredixAI/predix/blob/main/LICENSE"><img src="https://img.shields.io/github/license/PredixAI/predix" alt="License"></a>
|
||||
<a href="https://pypi.org/project/predix/"><img src="https://img.shields.io/pypi/v/predix" alt="PyPI"></a>
|
||||
<a href="https://github.com/PredixAI/predix/actions/workflows/ci.yml"><img src="https://github.com/PredixAI/predix/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
||||
<a href="https://github.com/astral-sh/ruff"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Ruff"></a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
# 🌟 Introduction
|
||||
<div align="center">
|
||||
<img src="docs/_static/scen.png" alt="Our focused scenario" style="width:80%; ">
|
||||
</div>
|
||||
## Overview
|
||||
|
||||
RDAgent aims to automate the most critical and valuable aspects of the industrial R&D process, and we begin with focusing on the data-driven scenarios to streamline the development of models and data.
|
||||
Methodologically, we have identified a framework with two key components: 'R' for proposing new ideas and 'D' for implementing them.
|
||||
We believe that the automatic evolution of R&D will lead to solutions of significant industrial value.
|
||||
**Predix** is an autonomous AI agent for quantitative trading strategies in the EUR/USD forex market. Built on a multi-agent framework, Predix automates the full research and development cycle:
|
||||
|
||||
- 📊 **Data Analysis** – Automatically analyzes market patterns and microstructure
|
||||
- 💡 **Strategy Discovery** – Proposes novel trading factors and signals
|
||||
- 🧠 **Model Evolution** – Iteratively improves predictive models
|
||||
- 📈 **Backtesting** – Validates strategies on historical 1-minute data
|
||||
|
||||
<!-- Tag Cloud -->
|
||||
R&D is a very general scenario. The advent of RDAgent can be your
|
||||
- 💰 **Automatic Quant Factory** [(🎥Demo Video)](https://rdagent.azurewebsites.net/factor_loop)
|
||||
- 🤖 **Data Mining Agent:** Iteratively proposing data [(🎥Demo Video)](https://rdagent.azurewebsites.net/dmm) & models [(🎥Demo Video)](https://rdagent.azurewebsites.net/model_loop) and implementing them by gaining knowledge from data.
|
||||
- 🦾 **Research Copilot:** Auto read research papers [(🎥Demo Video)](https://rdagent.azurewebsites.net/report_model) / financial reports [(🎥Demo Video)](https://rdagent.azurewebsites.net/report_factor) and implement model structures or building datasets.
|
||||
- ...
|
||||
Predix is optimized for **1-minute EUR/USD FX data** (2020–2026) and uses Qlib as the underlying backtesting engine.
|
||||
|
||||
You can click the links above to view the demo. We're continuously adding more methods and scenarios to the project to enhance your R&D processes and boost productivity.
|
||||
## Acknowledgments
|
||||
|
||||
Additionally, you can take a closer look at the examples in our **[🖥️ Live Demo](https://rdagent.azurewebsites.net/)**.
|
||||
This project draws inspiration from various open-source projects in the AI trading and multi-agent systems space. We thank all the authors for their innovative work that helped shape our understanding of these patterns.
|
||||
|
||||
<div align="center">
|
||||
<a href="https://rdagent.azurewebsites.net/" target="_blank">
|
||||
<img src="docs/_static/demo.png" alt="Watch the demo" width="80%">
|
||||
</a>
|
||||
</div>
|
||||
Special thanks to:
|
||||
|
||||
- **[Microsoft RD-Agent](https://github.com/microsoft/RD-Agent)** (MIT License) - Foundation for our autonomous R&D agent framework. We extend our gratitude to the RD-Agent team for their excellent foundational work.
|
||||
|
||||
# ⚡ Quick start
|
||||
- **[TradingAgents](https://github.com/TradingAgents/TradingAgents)** (Apache 2.0 License) - Inspiration for our multi-agent debate system, reflection mechanism, and memory management modules.
|
||||
|
||||
You can try above demos by running the following command:
|
||||
- **[ai-hedge-fund](https://github.com/virattt/ai-hedge-fund)** - Inspiration for macro analysis (Stanley Druckenmiller agent), risk management concepts, and market regime detection.
|
||||
|
||||
### 🐳 Docker installation.
|
||||
Users must ensure Docker is installed before attempting most scenarios. Please refer to the [official 🐳Docker page](https://docs.docker.com/engine/install/) for installation instructions.
|
||||
All code in Predix is originally written and implemented independently. Predix extends these frameworks with EUR/USD forex-specific features, 1-minute backtesting capabilities, comprehensive risk management, and trading dashboards.
|
||||
|
||||
### 🐍 Create a Conda Environment
|
||||
- Create a new conda environment with Python (3.10 and 3.11 are well-tested in our CI):
|
||||
```sh
|
||||
conda create -n rdagent python=3.10
|
||||
```
|
||||
- Activate the environment:
|
||||
```sh
|
||||
conda activate rdagent
|
||||
```
|
||||
---
|
||||
|
||||
### 🛠️ Install the RDAgent
|
||||
- You can directly install the RDAgent package from PyPI:
|
||||
```sh
|
||||
pip install rdagent
|
||||
```
|
||||
## Installation
|
||||
|
||||
### ⚙️ Configuration
|
||||
- You have to config your GPT model in the `.env`
|
||||
```bash
|
||||
cat << EOF > .env
|
||||
OPENAI_API_KEY=<your_api_key>
|
||||
# EMBEDDING_MODEL=text-embedding-3-small
|
||||
CHAT_MODEL=gpt-4-turbo
|
||||
EOF
|
||||
```
|
||||
### Prerequisites
|
||||
|
||||
### 🚀 Run the Application
|
||||
- **Python 3.10 or 3.11**
|
||||
- **Docker** (required for sandboxed code execution)
|
||||
- **Linux** (officially supported; macOS/Windows may work with adjustments)
|
||||
|
||||
The **[🖥️ Live Demo](https://rdagent.azurewebsites.net/)** is implemented by the following commands(each item represents one demo, you can select the one you prefer):
|
||||
|
||||
- Run the **Automated Quantitative Trading & Iterative Factors Evolution**: Qlib self-loop factor proposal and implementation application
|
||||
```sh
|
||||
rdagent fin_factor
|
||||
```
|
||||
|
||||
- Run the **Automated Quantitative Trading & Iterative Model Evolution**: Qlib self-loop model proposal and implementation application
|
||||
```sh
|
||||
rdagent fin_model
|
||||
```
|
||||
|
||||
- Run the **Automated Medical Prediction Model Evolution**: Medical self-loop model proposal and implementation application
|
||||
```sh
|
||||
rdagent med_model
|
||||
```
|
||||
|
||||
- Run the **Automated Quantitative Trading & Factors Extraction from Financial Reports**: Run the Qlib factor extraction and implementation application based on financial reports
|
||||
```sh
|
||||
# 1. Generally, you can run this scenario using the following command:
|
||||
rdagent fin_factor_report --report_folder=<Your financial reports folder path>
|
||||
|
||||
# 2. Specifically, you need to prepare some financial reports first. You can follow this concrete example:
|
||||
wget https://github.com/SunsetWolf/rdagent_resource/releases/download/reports/all_reports.zip
|
||||
unzip all_reports.zip -d git_ignore_folder/reports
|
||||
rdagent fin_factor_report --report_folder=git_ignore_folder/reports
|
||||
```
|
||||
|
||||
- Run the **Automated Model Research & Development Copilot**: model extraction and implementation application
|
||||
```sh
|
||||
# 1. Generally, you can run your own papers/reports with the following command:
|
||||
rdagent general_model <Your paper URL>
|
||||
|
||||
# 2. Specifically, you can do it like this. For more details and additional paper examples, use `rdagent general_model -h`:
|
||||
rdagent general_model "https://arxiv.org/pdf/2210.09789"
|
||||
```
|
||||
|
||||
### 🖥️ Monitor the Application Results
|
||||
- You can serve our demo app to monitor the RD loop by running the following command:
|
||||
```sh
|
||||
rdagent ui --port 80 --log_dir <your log folder like "log/">
|
||||
```
|
||||
|
||||
# 🏭 Scenarios
|
||||
|
||||
We have applied RD-Agent to multiple valuable data-driven industrial scenarios.
|
||||
|
||||
|
||||
## 🎯 Goal: Agent for Data-driven R&D
|
||||
|
||||
In this project, we are aiming to build an Agent to automate Data-Driven R\&D that can
|
||||
+ 📄 Read real-world material (reports, papers, etc.) and **extract** key formulas, descriptions of interested **features** and **models**, which are the key components of data-driven R&D .
|
||||
+ 🛠️ **Implement** the extracted formulas (e.g., features, factors, and models) in runnable codes.
|
||||
+ Due to the limited ability of LLM in implementing at once, build an evolving process for the agent to improve performance by learning from feedback and knowledge.
|
||||
+ 💡 Propose **new ideas** based on current knowledge and observations.
|
||||
|
||||
<!--  -->
|
||||
|
||||
## 📈 Scenarios/Demos
|
||||
|
||||
In the two key areas of data-driven scenarios, model implementation and data building, our system aims to serve two main roles: 🦾Copilot and 🤖Agent.
|
||||
- The 🦾Copilot follows human instructions to automate repetitive tasks.
|
||||
- The 🤖Agent, being more autonomous, actively proposes ideas for better results in the future.
|
||||
|
||||
The supported scenarios are listed below:
|
||||
|
||||
| Scenario/Target | Model Implementation | Data Building |
|
||||
| -- | -- | -- |
|
||||
| **💹 Finance** | 🤖 [Iteratively Proposing Ideas & Evolving](https://rdagent.azurewebsites.net/model_loop) | 🤖 [Iteratively Proposing Ideas & Evolving](https://rdagent.azurewebsites.net/factor_loop) <br/> 🦾 [Auto reports reading & implementation](https://rdagent.azurewebsites.net/report_factor) |
|
||||
| **🩺 Medical** | 🤖 [Iteratively Proposing Ideas & Evolving](https://rdagent.azurewebsites.net/dmm) | - |
|
||||
| **🏭 General** | 🦾 [Auto paper reading & implementation](https://rdagent.azurewebsites.net/report_model) | - |
|
||||
|
||||
Different scenarios vary in entrance and configuration. Please check the detailed setup tutorial in the scenarios documents.
|
||||
|
||||
Here is a gallery of successful explorations. You can download the source code and view the execution trace using the command below:
|
||||
### Quick Install
|
||||
|
||||
```bash
|
||||
rdagent ui --port 80 --log_dir gallary/
|
||||
# Install from PyPI
|
||||
pip install predix
|
||||
|
||||
# Or install from source
|
||||
git clone https://github.com/PredixAI/predix
|
||||
cd predix
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
Please refer to **[📖readthedocs_scen](https://rdagent.readthedocs.io/en/latest/scens/catalog.html)** for more details of the scenarios.
|
||||
### Development Setup
|
||||
|
||||
# ⚙️ Framework
|
||||
```bash
|
||||
# Create conda environment
|
||||
conda create -n predix python=3.10
|
||||
conda activate predix
|
||||
|
||||
<div align="center">
|
||||
<img src="docs/_static/Framework-RDAgent.png" alt="Framework-RDAgent" width="85%">
|
||||
</div>
|
||||
# Install in editable mode with dev dependencies
|
||||
make dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Automating the R&D process in data science is a highly valuable yet underexplored area in industry. We propose a framework to push the boundaries of this important research field.
|
||||
## Quick Start
|
||||
|
||||
The research questions within this framework can be divided into three main categories:
|
||||
| Research Area | Paper/Work List |
|
||||
|--------------------|-----------------|
|
||||
| **Benchmark the R&D abilities** | [Benchmark](#benchmark) |
|
||||
| **Idea proposal:** Explore new ideas or refine existing ones | [Research](#research) |
|
||||
| **Ability to realize ideas:** Implement and execute ideas | [Development](#development) |
|
||||
### 1. Health Check
|
||||
|
||||
We believe that the key to delivering high-quality solutions lies in the ability to evolve R&D capabilities. Agents should learn like human experts, continuously improving their R&D skills.
|
||||
Verify your environment is properly configured:
|
||||
|
||||
More documents can be found in the **[📖 readthedocs](https://rdagent.readthedocs.io/)**.
|
||||
```bash
|
||||
rdagent health_check --no-check-env
|
||||
```
|
||||
|
||||
# 📃 Paper/Work list
|
||||
### 2. Configure LLM Backend
|
||||
|
||||
## 📊 Benchmark
|
||||
- [Towards Data-Centric Automatic R&D](https://arxiv.org/abs/2404.11276)
|
||||
```BibTeX
|
||||
@misc{chen2024datacentric,
|
||||
title={Towards Data-Centric Automatic R&D},
|
||||
author={Haotian Chen and Xinjie Shen and Zeqi Ye and Wenjun Feng and Haoxue Wang and Xiao Yang and Xu Yang and Weiqing Liu and Jiang Bian},
|
||||
year={2024},
|
||||
eprint={2404.11276},
|
||||
archivePrefix={arXiv},
|
||||
primaryClass={cs.AI}
|
||||
Create a `.env` file in your project root:
|
||||
|
||||
```bash
|
||||
# Example: OpenAI configuration
|
||||
cat << EOF > .env
|
||||
CHAT_MODEL=gpt-4o
|
||||
EMBEDDING_MODEL=text-embedding-3-small
|
||||
OPENAI_API_BASE=https://api.openai.com/v1
|
||||
OPENAI_API_KEY=your-api-key-here
|
||||
EOF
|
||||
```
|
||||
|
||||
**Alternative providers:**
|
||||
|
||||
- **Azure OpenAI**: Set `AZURE_API_KEY`, `AZURE_API_BASE`, `AZURE_API_VERSION`
|
||||
- **DeepSeek**: Use `CHAT_MODEL=deepseek/deepseek-chat` with `DEEPSEEK_API_KEY`
|
||||
- **SiliconFlow (embedding)**: Use `EMBEDDING_MODEL=litellm_proxy/BAAI/bge-m3`
|
||||
|
||||
### 3. Run Quantitative Trading Loop
|
||||
|
||||
```bash
|
||||
# Full factor & model co-evolution
|
||||
rdagent fin_quant
|
||||
|
||||
# Factor-only evolution
|
||||
rdagent fin_factor
|
||||
|
||||
# Model-only evolution
|
||||
rdagent fin_model
|
||||
```
|
||||
|
||||
### 4. Monitor Results
|
||||
|
||||
```bash
|
||||
# Start the UI dashboard
|
||||
rdagent ui --port 19899 --log-dir log/ --data-science
|
||||
```
|
||||
|
||||
Then open `http://127.0.0.1:19899` in your browser.
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
### Data Configuration
|
||||
|
||||
Edit [`data_config.yaml`](data_config.yaml) to customize:
|
||||
|
||||
```yaml
|
||||
instrument: EURUSD
|
||||
frequency: 1min
|
||||
data_path: ~/.qlib/qlib_data/eurusd_1min_data
|
||||
|
||||
# Walk-forward split
|
||||
train_start: "2022-03-14"
|
||||
train_end: "2024-06-30"
|
||||
valid_start: "2024-07-01"
|
||||
valid_end: "2024-12-31"
|
||||
test_start: "2025-01-01"
|
||||
test_end: "2026-03-20"
|
||||
|
||||
# Market context for LLM prompts
|
||||
market_context:
|
||||
spread_bps: 1.5
|
||||
target_arr: 9.62 # Target annual return (%)
|
||||
max_drawdown: 20 # Max drawdown (%)
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
| Variable | Description | Example |
|
||||
|----------|-------------|---------|
|
||||
| `CHAT_MODEL` | LLM for reasoning | `gpt-4o`, `deepseek-chat` |
|
||||
| `EMBEDDING_MODEL` | Embedding model | `text-embedding-3-small` |
|
||||
| `OPENAI_API_KEY` | API key for OpenAI | `sk-...` |
|
||||
| `DEEPSEEK_API_KEY` | API key for DeepSeek | `sk-...` |
|
||||
| `DS_LOCAL_DATA_PATH` | Local data directory | `./data` |
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
### 🔄 Iterative Factor Evolution
|
||||
|
||||
Predix continuously proposes, implements, and validates new alpha factors:
|
||||
|
||||
- Learns from backtest feedback
|
||||
- Avoids overfitting through walk-forward validation
|
||||
- Discovers non-obvious patterns in order flow, volatility, and session dynamics
|
||||
|
||||
### 🧠 Model Architecture Search
|
||||
|
||||
Automatically explores and refines predictive models:
|
||||
|
||||
- Linear baselines (LightGBM, XGBoost)
|
||||
- Deep learning (LSTM, Transformer, Temporal CNN)
|
||||
- Ensemble methods
|
||||
|
||||
### 📚 Knowledge Base
|
||||
|
||||
Built-in knowledge accumulation across loops:
|
||||
|
||||
- Successful factors are archived
|
||||
- Failed attempts inform future proposals
|
||||
- Cross-loop learning improves robustness
|
||||
|
||||
### 🖥️ Interactive UI
|
||||
|
||||
Real-time dashboard for monitoring:
|
||||
|
||||
- Factor performance metrics
|
||||
- Model architecture evolution
|
||||
- Cumulative returns and drawdowns
|
||||
- Code diffs and implementation history
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
predix/
|
||||
├── rdagent/ # Core agent framework
|
||||
│ ├── app/ # CLI and scenario apps
|
||||
│ ├── components/ # Reusable agent components
|
||||
│ ├── core/ # Core abstractions
|
||||
│ ├── scenarios/ # Domain-specific scenarios
|
||||
│ └── utils/ # Utilities
|
||||
├── constraints/ # Constraint definitions
|
||||
├── docs/ # Documentation
|
||||
├── web/ # Web UI frontend
|
||||
├── data_config.yaml # Data configuration
|
||||
├── pyproject.toml # Project metadata
|
||||
└── requirements.txt # Dependencies
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Data Setup
|
||||
|
||||
Predix uses 1-minute EUR/USD data. To prepare your dataset:
|
||||
|
||||
```bash
|
||||
# Run the data setup script (if provided)
|
||||
./setup_predix_eurusd.sh
|
||||
|
||||
# Or manually place data in:
|
||||
# ~/.qlib/qlib_data/eurusd_1min_data/
|
||||
```
|
||||
|
||||
Expected data columns: `$open`, `$close`, `$high`, `$low`, `$volume`
|
||||
|
||||
---
|
||||
|
||||
## CLI Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `rdagent fin_quant` | Full factor & model co-evolution |
|
||||
| `rdagent fin_factor` | Factor-only evolution |
|
||||
| `rdagent fin_model` | Model-only evolution |
|
||||
| `rdagent fin_factor_report --report-folder=<path>` | Extract factors from financial reports |
|
||||
| `rdagent general_model <paper-url>` | Extract model from research paper |
|
||||
| `rdagent data_science --competition <name>` | Kaggle/data science competition mode |
|
||||
| `rdagent ui --port 19899 --log-dir <path>` | Start monitoring dashboard |
|
||||
| `rdagent health_check` | Validate environment setup |
|
||||
|
||||
---
|
||||
|
||||
## Requirements
|
||||
|
||||
Core dependencies (see [`requirements.txt`](requirements.txt) for full list):
|
||||
|
||||
- **LLM**: `openai`, `litellm`
|
||||
- **Data**: `pandas`, `numpy`, `pyarrow`
|
||||
- **ML**: `scikit-learn`, `lightgbm`, `xgboost`
|
||||
- **Backtesting**: `qlib` (via Docker)
|
||||
- **UI**: `streamlit`, `plotly`, `flask`
|
||||
|
||||
---
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the **MIT License** – see the [`LICENSE`](LICENSE) file for details.
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are welcome! Please:
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
||||
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
||||
4. Push to the branch (`git push origin feature/amazing-feature`)
|
||||
5. Open a Pull Request
|
||||
|
||||
For major changes, please open an issue first to discuss your approach.
|
||||
|
||||
---
|
||||
|
||||
## Citation
|
||||
|
||||
If you use Predix in your research, please cite the underlying framework:
|
||||
|
||||
```bibtex
|
||||
@misc{yang2025rdagentllmagentframeworkautonomous,
|
||||
title={R&D-Agent: An LLM-Agent Framework Towards Autonomous Data Science},
|
||||
author={Yang, Xu and Yang, Xiao and Fang, Shikai and Zhang, Yifei and Wang, Jian and Xian, Bowen and Li, Qizheng and Li, Jingyuan and Xu, Minrui and Li, Yuante and others},
|
||||
year={2025},
|
||||
eprint={2505.14738},
|
||||
archivePrefix={arXiv},
|
||||
primaryClass={cs.AI}
|
||||
}
|
||||
```
|
||||

|
||||
|
||||
## 🔍 Research
|
||||
---
|
||||
|
||||
In a data mining expert's daily research and development process, they propose a hypothesis (e.g., a model structure like RNN can capture patterns in time-series data), design experiments (e.g., finance data contains time-series and we can verify the hypothesis in this scenario), implement the experiment as code (e.g., Pytorch model structure), and then execute the code to get feedback (e.g., metrics, loss curve, etc.). The experts learn from the feedback and improve in the next iteration.
|
||||
## Support
|
||||
|
||||
Based on the principles above, we have established a basic method framework that continuously proposes hypotheses, verifies them, and gets feedback from the real-world practice. This is the first scientific research automation framework that supports linking with real-world verification.
|
||||
- **Issues**: [GitHub Issues](https://github.com/PredixAI/predix/issues)
|
||||
- **Documentation**: [Read the Docs](https://rdagent.readthedocs.io/)
|
||||
|
||||
For more detail, please refer to our **[🖥️ Live Demo page](https://rdagent.azurewebsites.net)**.
|
||||
---
|
||||
|
||||
## 🛠️ Development
|
||||
## Disclaimer
|
||||
|
||||
- [Collaborative Evolving Strategy for Automatic Data-Centric Development](https://arxiv.org/abs/2407.18690)
|
||||
```BibTeX
|
||||
@misc{yang2024collaborative,
|
||||
title={Collaborative Evolving Strategy for Automatic Data-Centric Development},
|
||||
author={Xu Yang and Haotian Chen and Wenjun Feng and Haoxue Wang and Zeqi Ye and Xinjie Shen and Xiao Yang and Shizhao Sun and Weiqing Liu and Jiang Bian},
|
||||
year={2024},
|
||||
eprint={2407.18690},
|
||||
archivePrefix={arXiv},
|
||||
primaryClass={cs.AI}
|
||||
}
|
||||
```
|
||||

|
||||
Predix is provided "as is" for **research and educational purposes only**. It is **not** intended for:
|
||||
|
||||
- Live trading or financial advice
|
||||
- Production use without thorough testing
|
||||
- Replacement of qualified financial professionals
|
||||
|
||||
# 🤝 Contributing
|
||||
|
||||
## 📝 Guidelines
|
||||
This project welcomes contributions and suggestions.
|
||||
Contributing to this project is straightforward and rewarding. Whether it's solving an issue, addressing a bug, enhancing documentation, or even correcting a typo, every contribution is valuable and helps improve RDAgent.
|
||||
|
||||
To get started, you can explore the issues list, or search for `TODO:` comments in the codebase by running the command `grep -r "TODO:"`.
|
||||
|
||||
<img src="https://img.shields.io/github/contributors-anon/microsoft/RD-Agent"/>
|
||||
|
||||
<a href="https://github.com/microsoft/RD-Agent/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=microsoft/RD-Agent&max=100&columns=15" />
|
||||
</a>
|
||||
|
||||
Before we released RD-Agent as an open-source project on GitHub, it was an internal project within our group. Unfortunately, the internal commit history was not preserved when we removed some confidential code. As a result, some contributions from our group members, including Haotian Chen, Wenjun Feng, Haoxue Wang, Zeqi Ye, Xinjie Shen, and Jinhui Li, were not included in the public commits.
|
||||
|
||||
# ⚖️ Legal disclaimer
|
||||
<p style="line-height: 1; font-style: italic;">The RD-agent is provided “as is”, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. The RD-agent is aimed to facilitate research and development process in the financial industry and not ready-to-use for any financial investment or advice. Users shall independently assess and test the risks of the RD-agent in a specific use scenario, ensure the responsible use of AI technology, including but not limited to developing and integrating risk mitigation measures, and comply with all applicable laws and regulations in all applicable jurisdictions. The RD-agent does not provide financial opinions or reflect the opinions of Microsoft, nor is it designed to replace the role of qualified financial professionals in formulating, assessing, and approving finance products. The inputs and outputs of the RD-agent belong to the users and users shall assume all liability under any theory of liability, whether in contract, torts, regulatory, negligence, products liability, or otherwise, associated with use of the RD-agent and any inputs and outputs thereof.</p>
|
||||
Users assume all liability and should comply with applicable laws and regulations in their jurisdiction. Past performance does not guarantee future results.
|
||||
|
||||
+8
-29
@@ -1,41 +1,20 @@
|
||||
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.9 BLOCK -->
|
||||
# Security Policy
|
||||
|
||||
## Security
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) and [Xamarin](https://github.com/xamarin).
|
||||
|
||||
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below.
|
||||
|
||||
## Reporting Security Issues
|
||||
We take the security of Predix seriously. If you believe you have found a security vulnerability, please report it to us as described below.
|
||||
|
||||
**Please do not report security vulnerabilities through public GitHub issues.**
|
||||
|
||||
Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report).
|
||||
Instead, please report them via email to:
|
||||
- **Email**: nico@predix.io
|
||||
|
||||
If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp).
|
||||
|
||||
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
|
||||
|
||||
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
|
||||
|
||||
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
|
||||
* Full paths of source file(s) related to the manifestation of the issue
|
||||
* The location of the affected source code (tag/branch/commit or direct URL)
|
||||
* Any special configuration required to reproduce the issue
|
||||
* Step-by-step instructions to reproduce the issue
|
||||
* Proof-of-concept or exploit code (if possible)
|
||||
* Impact of the issue, including how an attacker might exploit the issue
|
||||
|
||||
This information will help us triage your report more quickly.
|
||||
|
||||
If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs.
|
||||
You should receive a response within 48 hours. If for some reason you do not, please follow up via email to ensure we received your original message.
|
||||
|
||||
## Preferred Languages
|
||||
|
||||
We prefer all communications to be in English.
|
||||
|
||||
## Policy
|
||||
## Security Updates
|
||||
|
||||
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd).
|
||||
|
||||
<!-- END MICROSOFT SECURITY.MD BLOCK -->
|
||||
Security updates will be released as patch versions. Please ensure you are using the latest version of Predix to benefit from security fixes.
|
||||
|
||||
+25
-25
@@ -1,25 +1,25 @@
|
||||
# TODO: The maintainer of this repo has not yet edited this file
|
||||
|
||||
**REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project?
|
||||
|
||||
- **No CSS support:** Fill out this template with information about how to file issues and get help.
|
||||
- **Yes CSS support:** Fill out an intake form at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). CSS will work with/help you to determine next steps.
|
||||
- **Not sure?** Fill out an intake as though the answer were "Yes". CSS will help you decide.
|
||||
|
||||
*Then remove this first heading from this SUPPORT.MD file before publishing your repo.*
|
||||
|
||||
# Support
|
||||
|
||||
## How to file issues and get help
|
||||
|
||||
This project uses GitHub Issues to track bugs and feature requests. Please search the existing
|
||||
issues before filing new issues to avoid duplicates. For new issues, file your bug or
|
||||
feature request as a new Issue.
|
||||
|
||||
For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE
|
||||
FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER
|
||||
CHANNEL. WHERE WILL YOU HELP PEOPLE?**.
|
||||
|
||||
## Microsoft Support Policy
|
||||
|
||||
Support for this **PROJECT or PRODUCT** is limited to the resources listed above.
|
||||
# Support
|
||||
|
||||
## How to file issues and get help
|
||||
|
||||
This project uses GitHub Issues to track bugs and feature requests. Please search the existing
|
||||
issues before filing new issues to avoid duplicates. For new issues, file your bug or
|
||||
feature request as a new Issue.
|
||||
|
||||
- **Issues**: [https://github.com/PredixAI/predix/issues](https://github.com/PredixAI/predix/issues)
|
||||
|
||||
For help and questions about using this project, please reach out via:
|
||||
|
||||
- **Email**: nico@predix.io
|
||||
- **GitHub Discussions**: [https://github.com/PredixAI/predix/discussions](https://github.com/PredixAI/predix/discussions)
|
||||
|
||||
## Community Support
|
||||
|
||||
We encourage users to help each other through GitHub Discussions or by contributing
|
||||
answers to issues. If you find a solution to a problem, please consider sharing it
|
||||
publicly to help others.
|
||||
|
||||
## Support Policy
|
||||
|
||||
Support is provided on a best-effort basis by the maintainers and community.
|
||||
For critical issues or commercial support needs, please contact the maintainers directly.
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
We encourage to set the TODOs in code. But some TODOs are more global.
|
||||
So we place it here.
|
||||
# TODOs
|
||||
|
||||
This file tracks global TODOs for the Predix project. Individual TODOs in code should be addressed in their respective modules.
|
||||
|
||||
- [ ] Aligning the naming of files in components & scenarios.
|
||||
- We would like to have the same logic for naming convention in components(reusable components for all scenarios) and scenarios (componets for specific scenario).
|
||||
- But now we have following mismatch
|
||||
- `coder` in `components` & `developer` in `components`
|
||||
- [ ] The name of the folders mismatch with the content in them.
|
||||
- Why are scenarios in experiments?
|
||||
## Current Global TODOs
|
||||
|
||||
- [ ] Align naming conventions for files in `components/` and `scenarios/` directories
|
||||
- Current mismatch: `coder` in `components/` vs `developer` in `scenarios/`
|
||||
- [ ] Clean up folder naming inconsistencies
|
||||
- Investigate why some scenario-related code is in `experiments/` folders
|
||||
|
||||
## Completed
|
||||
|
||||
- [x] Rebrand from RD-Agent to Predix for EUR/USD focus
|
||||
- [x] Remove Microsoft-specific references
|
||||
- [x] Update documentation for PredixAI organization
|
||||
|
||||
Executable
+91
@@ -0,0 +1,91 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Liest data_config.yaml und schreibt alle Werte in:
|
||||
- .env (Zeiträume, Pfade)
|
||||
- generate.py (Qlib Datengenerierung)
|
||||
"""
|
||||
import yaml
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
CONFIG = Path(__file__).parent / "data_config.yaml"
|
||||
ENV = Path(__file__).parent / ".env"
|
||||
GENERATE = Path("/home/nico/miniconda3/envs/rdagent/lib/python3.10/site-packages/rdagent/scenarios/qlib/experiment/factor_data_template/generate.py")
|
||||
|
||||
with open(CONFIG) as f:
|
||||
cfg = yaml.safe_load(f)
|
||||
|
||||
# --- .env updaten ---
|
||||
env_text = ENV.read_text()
|
||||
|
||||
replacements = {
|
||||
r"QLIB_DATA_DIR=.*": f"QLIB_DATA_DIR={cfg['data_path'].replace('~', str(Path.home()))}",
|
||||
r"QLIB_FREQ=.*": f"QLIB_FREQ={cfg['frequency']}",
|
||||
r"QLIB_FACTOR_TRAIN_START=.*": f"QLIB_FACTOR_TRAIN_START={cfg['train_start']}",
|
||||
r"QLIB_FACTOR_TRAIN_END=.*": f"QLIB_FACTOR_TRAIN_END={cfg['train_end']}",
|
||||
r"QLIB_FACTOR_VALID_START=.*": f"QLIB_FACTOR_VALID_START={cfg['valid_start']}",
|
||||
r"QLIB_FACTOR_VALID_END=.*": f"QLIB_FACTOR_VALID_END={cfg['valid_end']}",
|
||||
r"QLIB_FACTOR_TEST_START=.*": f"QLIB_FACTOR_TEST_START={cfg['test_start']}",
|
||||
r"QLIB_FACTOR_TEST_END=.*": f"QLIB_FACTOR_TEST_END={cfg['test_end']}",
|
||||
r"QLIB_MODEL_TRAIN_START=.*": f"QLIB_MODEL_TRAIN_START={cfg['train_start']}",
|
||||
r"QLIB_MODEL_TRAIN_END=.*": f"QLIB_MODEL_TRAIN_END={cfg['train_end']}",
|
||||
r"QLIB_MODEL_VALID_START=.*": f"QLIB_MODEL_VALID_START={cfg['valid_start']}",
|
||||
r"QLIB_MODEL_VALID_END=.*": f"QLIB_MODEL_VALID_END={cfg['valid_end']}",
|
||||
r"QLIB_MODEL_TEST_START=.*": f"QLIB_MODEL_TEST_START={cfg['test_start']}",
|
||||
r"QLIB_MODEL_TEST_END=.*": f"QLIB_MODEL_TEST_END={cfg['test_end']}",
|
||||
r"QLIB_QUANT_TRAIN_START=.*": f"QLIB_QUANT_TRAIN_START={cfg['train_start']}",
|
||||
r"QLIB_QUANT_TRAIN_END=.*": f"QLIB_QUANT_TRAIN_END={cfg['train_end']}",
|
||||
r"QLIB_QUANT_VALID_START=.*": f"QLIB_QUANT_VALID_START={cfg['valid_start']}",
|
||||
r"QLIB_QUANT_VALID_END=.*": f"QLIB_QUANT_VALID_END={cfg['valid_end']}",
|
||||
r"QLIB_QUANT_TEST_START=.*": f"QLIB_QUANT_TEST_START={cfg['test_start']}",
|
||||
r"QLIB_QUANT_TEST_END=.*": f"QLIB_QUANT_TEST_END={cfg['test_end']}",
|
||||
}
|
||||
|
||||
for pattern, replacement in replacements.items():
|
||||
env_text = re.sub(pattern, replacement, env_text)
|
||||
|
||||
ENV.write_text(env_text)
|
||||
print("✓ .env aktualisiert")
|
||||
|
||||
# --- generate.py updaten ---
|
||||
data_path = cfg['data_path']
|
||||
freq = cfg['frequency']
|
||||
train_start = cfg['train_start']
|
||||
test_end = cfg['test_end']
|
||||
valid_start = cfg['valid_start']
|
||||
cols = str(cfg['columns'])
|
||||
|
||||
generate_text = f'''import qlib
|
||||
import pandas as pd
|
||||
|
||||
qlib.init(provider_uri="{data_path}", freq="{freq}")
|
||||
|
||||
from qlib.data import D
|
||||
|
||||
instruments = D.instruments(market="all")
|
||||
fields = {cols}
|
||||
|
||||
data = (
|
||||
D.features(instruments, fields, freq="{freq}")
|
||||
.swaplevel()
|
||||
.sort_index()
|
||||
.loc["{train_start}":]
|
||||
.sort_index()
|
||||
)
|
||||
data.to_hdf("./daily_pv_all.h5", key="data")
|
||||
|
||||
data_debug = (
|
||||
D.features(instruments, fields, start_time="{valid_start}", end_time="{test_end}", freq="{freq}")
|
||||
.swaplevel()
|
||||
.sort_index()
|
||||
)
|
||||
data_debug.to_hdf("./daily_pv_debug.h5", key="data")
|
||||
'''
|
||||
|
||||
GENERATE.write_text(generate_text)
|
||||
print("✓ generate.py aktualisiert")
|
||||
print(f"\nKonfiguration angewendet:")
|
||||
print(f" Instrument: {cfg['instrument']}")
|
||||
print(f" Frequenz: {cfg['frequency']}")
|
||||
print(f" Train: {cfg['train_start']} → {cfg['train_end']}")
|
||||
print(f" Test: {cfg['test_start']} → {cfg['test_end']}")
|
||||
+3
-264
@@ -1,266 +1,5 @@
|
||||
aiohttp==3.9.1
|
||||
aiosignal==1.3.1
|
||||
alabaster==0.7.13
|
||||
annotated-types==0.6.0
|
||||
anyio==4.2.0
|
||||
appdirs==1.4.4
|
||||
argon2-cffi==23.1.0
|
||||
argon2-cffi-bindings==21.2.0
|
||||
arrow==1.3.0
|
||||
asttokens==2.4.1
|
||||
async-lru==2.0.4
|
||||
async-timeout==4.0.3
|
||||
attrs==23.2.0
|
||||
autodoc-pydantic==2.0.1
|
||||
azure-ai-formrecognizer==3.3.2
|
||||
azure-common==1.1.28
|
||||
azure-core==1.29.6
|
||||
azure-identity==1.17.1
|
||||
Babel==2.14.0
|
||||
beautifulsoup4==4.12.2
|
||||
black==23.12.1
|
||||
bleach==6.1.0
|
||||
blosc2==2.7.1
|
||||
build==1.0.3
|
||||
certifi==2023.11.17
|
||||
cffi==1.16.0
|
||||
charset-normalizer==3.3.2
|
||||
click==8.1.7
|
||||
colorama==0.4.6
|
||||
comm==0.2.2
|
||||
contourpy==1.2.1
|
||||
coverage==7.4.0
|
||||
cryptography==41.0.7
|
||||
cycler==0.12.1
|
||||
Cython==3.0.7
|
||||
dataclasses-json==0.6.3
|
||||
debugpy==1.8.2
|
||||
decorator==5.1.1
|
||||
defusedxml==0.7.1
|
||||
dill==0.3.8
|
||||
distro==1.9.0
|
||||
docker==7.1.0
|
||||
docutils==0.20.1
|
||||
exceptiongroup==1.2.0
|
||||
executing==2.0.1
|
||||
fastjsonschema==2.20.0
|
||||
feedparser==6.0.11
|
||||
filelock==3.13.1
|
||||
fire==0.5.0
|
||||
fonttools==4.53.1
|
||||
fqdn==1.5.1
|
||||
frozenlist==1.4.1
|
||||
fsspec==2023.12.2
|
||||
furo==2023.9.10
|
||||
fuzzywuzzy==0.18.0
|
||||
git-changelog==2.4.0
|
||||
greenlet==3.0.3
|
||||
h11==0.14.0
|
||||
httpcore==1.0.2
|
||||
httpx==0.26.0
|
||||
idna==3.6
|
||||
imagesize==1.4.1
|
||||
importlib-metadata==7.0.1
|
||||
iniconfig==2.0.0
|
||||
ipykernel==6.29.5
|
||||
ipython==8.26.0
|
||||
ipywidgets==8.1.3
|
||||
isodate==0.6.1
|
||||
isoduration==20.11.0
|
||||
isort==5.13.2
|
||||
jaraco.classes==3.3.0
|
||||
jedi==0.19.1
|
||||
jeepney==0.8.0
|
||||
Jinja2==3.1.2
|
||||
joblib==1.4.2
|
||||
json5==0.9.25
|
||||
jsonpatch==1.33
|
||||
jsonpointer==2.4
|
||||
jsonschema==4.23.0
|
||||
jsonschema-specifications==2023.12.1
|
||||
jupyter==1.0.0
|
||||
jupyter-console==6.6.3
|
||||
jupyter-events==0.10.0
|
||||
jupyter-lsp==2.2.5
|
||||
jupyter_client==8.6.2
|
||||
jupyter_core==5.7.2
|
||||
jupyter_server==2.14.2
|
||||
jupyter_server_terminals==0.5.3
|
||||
jupyterlab==4.2.4
|
||||
jupyterlab_pygments==0.3.0
|
||||
jupyterlab_server==2.27.3
|
||||
jupyterlab_widgets==3.0.11
|
||||
keyring==24.3.0
|
||||
kiwisolver==1.4.5
|
||||
langchain==0.0.353
|
||||
langchain-community==0.0.7
|
||||
langchain-core==0.1.4
|
||||
langsmith==0.0.75
|
||||
Levenshtein==0.25.1
|
||||
livereload==2.6.3
|
||||
loguru==0.7.2
|
||||
loguru-mypy==0.0.4
|
||||
lxml==5.0.0
|
||||
markdown-it-py==3.0.0
|
||||
MarkupSafe==2.1.3
|
||||
marshmallow==3.20.1
|
||||
matplotlib==3.9.1
|
||||
matplotlib-inline==0.1.7
|
||||
mdit-py-plugins==0.4.0
|
||||
mdurl==0.1.2
|
||||
mistune==3.0.2
|
||||
more-itertools==10.1.0
|
||||
mpmath==1.3.0
|
||||
msal==1.30.0
|
||||
msal-extensions==1.2.0
|
||||
msgpack==1.0.8
|
||||
msrest==0.7.1
|
||||
multidict==6.0.4
|
||||
mypy==1.10.0
|
||||
mypy-extensions==1.0.0
|
||||
myst-parser==2.0.0
|
||||
nbclient==0.10.0
|
||||
nbconvert==7.16.4
|
||||
nbformat==5.10.4
|
||||
ndindex==1.8
|
||||
nest-asyncio==1.6.0
|
||||
networkx==3.2.1
|
||||
nh3==0.2.15
|
||||
notebook==7.2.1
|
||||
notebook_shim==0.2.4
|
||||
numexpr==2.10.1
|
||||
numpy==1.26.2
|
||||
nvidia-cublas-cu12==12.1.3.1
|
||||
nvidia-cuda-cupti-cu12==12.1.105
|
||||
nvidia-cuda-nvrtc-cu12==12.1.105
|
||||
nvidia-cuda-runtime-cu12==12.1.105
|
||||
nvidia-cudnn-cu12==8.9.2.26
|
||||
nvidia-cufft-cu12==11.0.2.54
|
||||
nvidia-curand-cu12==10.3.2.106
|
||||
nvidia-cusolver-cu12==11.4.5.107
|
||||
nvidia-cusparse-cu12==12.1.0.106
|
||||
nvidia-nccl-cu12==2.18.1
|
||||
nvidia-nvjitlink-cu12==12.3.101
|
||||
nvidia-nvtx-cu12==12.1.105
|
||||
oauthlib==3.2.2
|
||||
openai==1.6.1
|
||||
overrides==7.7.0
|
||||
packaging==23.2
|
||||
pandarallel==1.6.5
|
||||
pandas==2.1.4
|
||||
pandocfilters==1.5.1
|
||||
parso==0.8.4
|
||||
pathspec==0.12.1
|
||||
patsy==0.5.6
|
||||
pexpect==4.9.0
|
||||
dill==0.3.9
|
||||
pillow==10.4.0
|
||||
pkginfo==1.9.6
|
||||
platformdirs==4.1.0
|
||||
pluggy==1.3.0
|
||||
portalocker==2.10.1
|
||||
prometheus_client==0.20.0
|
||||
prompt_toolkit==3.0.47
|
||||
psutil==6.0.0
|
||||
ptyprocess==0.7.0
|
||||
pure_eval==0.2.3
|
||||
py-cpuinfo==9.0.0
|
||||
pycparser==2.21
|
||||
pydantic==2.5.3
|
||||
pydantic-settings==2.1.0
|
||||
pydantic_core==2.14.6
|
||||
Pygments==2.17.2
|
||||
PyJWT==2.8.0
|
||||
PyMuPDF==1.24.9
|
||||
PyMuPDFb==1.24.9
|
||||
pyparsing==3.1.2
|
||||
pypdf==3.17.4
|
||||
pyproject_hooks==1.0.0
|
||||
pytest==7.4.4
|
||||
python-dateutil==2.8.2
|
||||
python-dotenv==1.0.0
|
||||
python-json-logger==2.0.7
|
||||
python-Levenshtein==0.25.1
|
||||
pytz==2023.3.post1
|
||||
PyYAML==6.0.1
|
||||
pyzmq==26.0.3
|
||||
qtconsole==5.5.2
|
||||
QtPy==2.4.1
|
||||
rapidfuzz==3.9.5
|
||||
readme-renderer==42.0
|
||||
referencing==0.35.1
|
||||
regex==2024.7.24
|
||||
requests==2.31.0
|
||||
requests-oauthlib==1.3.1
|
||||
requests-toolbelt==1.0.0
|
||||
rfc3339-validator==0.1.4
|
||||
rfc3986==2.0.0
|
||||
rfc3986-validator==0.1.1
|
||||
rich==13.7.0
|
||||
rpds-py==0.19.1
|
||||
ruamel.yaml==0.18.5
|
||||
ruamel.yaml.clib==0.2.8
|
||||
ruff==0.4.5
|
||||
scikit-learn==1.5.1
|
||||
scipy==1.11.4
|
||||
SecretStorage==3.3.3
|
||||
semver==3.0.2
|
||||
Send2Trash==1.8.3
|
||||
setuptools-scm==8.0.4
|
||||
sgmllib3k==1.0.0
|
||||
shellingham==1.5.4
|
||||
six==1.16.0
|
||||
sniffio==1.3.0
|
||||
snowballstemmer==2.2.0
|
||||
soupsieve==2.5
|
||||
Sphinx==7.2.6
|
||||
sphinx-autobuild==2021.3.14
|
||||
sphinx-basic-ng==1.0.0b2
|
||||
sphinx-click==5.1.0
|
||||
sphinx-togglebutton==0.3.2
|
||||
sphinxcontrib-applehelp==1.0.7
|
||||
sphinxcontrib-devhelp==1.0.5
|
||||
sphinxcontrib-htmlhelp==2.0.4
|
||||
sphinxcontrib-jsmath==1.0.1
|
||||
sphinxcontrib-qthelp==1.0.6
|
||||
sphinxcontrib-serializinghtml==1.1.9
|
||||
SQLAlchemy==2.0.24
|
||||
stack-data==0.6.3
|
||||
statsmodels==0.14.2
|
||||
sympy==1.12
|
||||
tables==3.9.2
|
||||
tabulate==0.9.0
|
||||
tenacity==8.2.3
|
||||
termcolor==2.4.0
|
||||
terminado==0.18.1
|
||||
threadpoolctl==3.5.0
|
||||
tiktoken==0.7.0
|
||||
tinycss2==1.3.0
|
||||
toml-sort==0.23.1
|
||||
tomli==2.0.1
|
||||
tomlkit==0.12.3
|
||||
torch==2.1.2
|
||||
torch_geometric==2.5.3
|
||||
tornado==6.4
|
||||
tqdm==4.66.1
|
||||
traitlets==5.14.3
|
||||
tree-sitter==0.22.3
|
||||
tree-sitter-python==0.21.0
|
||||
triton==2.1.0
|
||||
twine==4.0.2
|
||||
typer==0.9.0
|
||||
types-psutil==6.0.0.20240621
|
||||
types-python-dateutil==2.9.0.20240316
|
||||
types-PyYAML==6.0.12.20240724
|
||||
types-tqdm==4.66.0.20240417
|
||||
typing-inspect==0.9.0
|
||||
typing_extensions==4.9.0
|
||||
tzdata==2023.4
|
||||
uri-template==1.3.0
|
||||
urllib3==2.1.0
|
||||
wcwidth==0.2.13
|
||||
webcolors==24.6.0
|
||||
webencodings==0.5.1
|
||||
websocket-client==1.8.0
|
||||
widgetsnbextension==4.0.11
|
||||
yarl==1.9.4
|
||||
zipp==3.17.0
|
||||
psutil==6.1.0
|
||||
scipy==1.14.1
|
||||
|
||||
+3
-261
@@ -1,263 +1,5 @@
|
||||
aiohttp==3.9.1
|
||||
aiosignal==1.3.1
|
||||
alabaster==0.7.13
|
||||
annotated-types==0.6.0
|
||||
anyio==4.2.0
|
||||
appdirs==1.4.4
|
||||
argon2-cffi==23.1.0
|
||||
argon2-cffi-bindings==21.2.0
|
||||
arrow==1.3.0
|
||||
asttokens==2.4.1
|
||||
async-lru==2.0.4
|
||||
attrs==23.2.0
|
||||
autodoc-pydantic==2.0.1
|
||||
azure-ai-formrecognizer==3.3.2
|
||||
azure-common==1.1.28
|
||||
azure-core==1.29.6
|
||||
azure-identity==1.17.1
|
||||
Babel==2.14.0
|
||||
beautifulsoup4==4.12.2
|
||||
black==23.12.1
|
||||
bleach==6.1.0
|
||||
blosc2==2.7.1
|
||||
build==1.0.3
|
||||
certifi==2023.11.17
|
||||
cffi==1.16.0
|
||||
charset-normalizer==3.3.2
|
||||
click==8.1.7
|
||||
colorama==0.4.6
|
||||
comm==0.2.2
|
||||
contourpy==1.2.1
|
||||
coverage==7.4.0
|
||||
cryptography==41.0.7
|
||||
cycler==0.12.1
|
||||
Cython==3.0.7
|
||||
dataclasses-json==0.6.3
|
||||
debugpy==1.8.2
|
||||
decorator==5.1.1
|
||||
defusedxml==0.7.1
|
||||
dill==0.3.8
|
||||
distro==1.9.0
|
||||
docker==7.1.0
|
||||
docutils==0.20.1
|
||||
executing==2.0.1
|
||||
fastjsonschema==2.20.0
|
||||
feedparser==6.0.11
|
||||
filelock==3.13.1
|
||||
fire==0.5.0
|
||||
fonttools==4.53.1
|
||||
fqdn==1.5.1
|
||||
frozenlist==1.4.1
|
||||
fsspec==2023.12.2
|
||||
furo==2023.9.10
|
||||
fuzzywuzzy==0.18.0
|
||||
git-changelog==2.4.0
|
||||
greenlet==3.0.3
|
||||
h11==0.14.0
|
||||
httpcore==1.0.2
|
||||
httpx==0.26.0
|
||||
idna==3.6
|
||||
imagesize==1.4.1
|
||||
importlib-metadata==7.0.1
|
||||
iniconfig==2.0.0
|
||||
ipykernel==6.29.5
|
||||
ipython==8.26.0
|
||||
ipywidgets==8.1.3
|
||||
isodate==0.6.1
|
||||
isoduration==20.11.0
|
||||
isort==5.13.2
|
||||
jaraco.classes==3.3.0
|
||||
jedi==0.19.1
|
||||
jeepney==0.8.0
|
||||
Jinja2==3.1.2
|
||||
joblib==1.4.2
|
||||
json5==0.9.25
|
||||
jsonpatch==1.33
|
||||
jsonpointer==2.4
|
||||
jsonschema==4.23.0
|
||||
jsonschema-specifications==2023.12.1
|
||||
jupyter==1.0.0
|
||||
jupyter-console==6.6.3
|
||||
jupyter-events==0.10.0
|
||||
jupyter-lsp==2.2.5
|
||||
jupyter_client==8.6.2
|
||||
jupyter_core==5.7.2
|
||||
jupyter_server==2.14.2
|
||||
jupyter_server_terminals==0.5.3
|
||||
jupyterlab==4.2.4
|
||||
jupyterlab_pygments==0.3.0
|
||||
jupyterlab_server==2.27.3
|
||||
jupyterlab_widgets==3.0.11
|
||||
keyring==24.3.0
|
||||
kiwisolver==1.4.5
|
||||
langchain==0.0.353
|
||||
langchain-community==0.0.7
|
||||
langchain-core==0.1.4
|
||||
langsmith==0.0.75
|
||||
Levenshtein==0.25.1
|
||||
livereload==2.6.3
|
||||
loguru==0.7.2
|
||||
loguru-mypy==0.0.4
|
||||
lxml==5.0.0
|
||||
markdown-it-py==3.0.0
|
||||
MarkupSafe==2.1.3
|
||||
marshmallow==3.20.1
|
||||
matplotlib==3.9.1
|
||||
matplotlib-inline==0.1.7
|
||||
mdit-py-plugins==0.4.0
|
||||
mdurl==0.1.2
|
||||
mistune==3.0.2
|
||||
more-itertools==10.1.0
|
||||
mpmath==1.3.0
|
||||
msal==1.30.0
|
||||
msal-extensions==1.2.0
|
||||
msgpack==1.0.8
|
||||
msrest==0.7.1
|
||||
multidict==6.0.4
|
||||
mypy==1.10.0
|
||||
mypy-extensions==1.0.0
|
||||
myst-parser==2.0.0
|
||||
nbclient==0.10.0
|
||||
nbconvert==7.16.4
|
||||
nbformat==5.10.4
|
||||
ndindex==1.8
|
||||
nest-asyncio==1.6.0
|
||||
networkx==3.2.1
|
||||
nh3==0.2.15
|
||||
notebook==7.2.1
|
||||
notebook_shim==0.2.4
|
||||
numexpr==2.10.1
|
||||
numpy==1.26.2
|
||||
nvidia-cublas-cu12==12.1.3.1
|
||||
nvidia-cuda-cupti-cu12==12.1.105
|
||||
nvidia-cuda-nvrtc-cu12==12.1.105
|
||||
nvidia-cuda-runtime-cu12==12.1.105
|
||||
nvidia-cudnn-cu12==8.9.2.26
|
||||
nvidia-cufft-cu12==11.0.2.54
|
||||
nvidia-curand-cu12==10.3.2.106
|
||||
nvidia-cusolver-cu12==11.4.5.107
|
||||
nvidia-cusparse-cu12==12.1.0.106
|
||||
nvidia-nccl-cu12==2.18.1
|
||||
nvidia-nvjitlink-cu12==12.3.101
|
||||
nvidia-nvtx-cu12==12.1.105
|
||||
oauthlib==3.2.2
|
||||
openai==1.6.1
|
||||
overrides==7.7.0
|
||||
packaging==23.2
|
||||
pandarallel==1.6.5
|
||||
pandas==2.1.4
|
||||
pandocfilters==1.5.1
|
||||
parso==0.8.4
|
||||
pathspec==0.12.1
|
||||
patsy==0.5.6
|
||||
pexpect==4.9.0
|
||||
dill==0.3.9
|
||||
pillow==10.4.0
|
||||
pkginfo==1.9.6
|
||||
platformdirs==4.1.0
|
||||
pluggy==1.3.0
|
||||
portalocker==2.10.1
|
||||
prometheus_client==0.20.0
|
||||
prompt_toolkit==3.0.47
|
||||
psutil==6.0.0
|
||||
ptyprocess==0.7.0
|
||||
pure_eval==0.2.3
|
||||
py-cpuinfo==9.0.0
|
||||
pycparser==2.21
|
||||
pydantic==2.5.3
|
||||
pydantic-settings==2.1.0
|
||||
pydantic_core==2.14.6
|
||||
Pygments==2.17.2
|
||||
PyJWT==2.9.0
|
||||
PyMuPDF==1.24.9
|
||||
PyMuPDFb==1.24.9
|
||||
pyparsing==3.1.2
|
||||
pypdf==3.17.4
|
||||
pyproject_hooks==1.0.0
|
||||
pytest==7.4.4
|
||||
python-dateutil==2.8.2
|
||||
python-dotenv==1.0.0
|
||||
python-json-logger==2.0.7
|
||||
python-Levenshtein==0.25.1
|
||||
pytz==2023.3.post1
|
||||
PyYAML==6.0.1
|
||||
pyzmq==26.0.3
|
||||
qtconsole==5.5.2
|
||||
QtPy==2.4.1
|
||||
rapidfuzz==3.9.5
|
||||
readme-renderer==42.0
|
||||
referencing==0.35.1
|
||||
regex==2024.7.24
|
||||
requests==2.31.0
|
||||
requests-oauthlib==1.3.1
|
||||
requests-toolbelt==1.0.0
|
||||
rfc3339-validator==0.1.4
|
||||
rfc3986==2.0.0
|
||||
rfc3986-validator==0.1.1
|
||||
rich==13.7.0
|
||||
rpds-py==0.19.1
|
||||
ruamel.yaml==0.18.5
|
||||
ruamel.yaml.clib==0.2.8
|
||||
ruff==0.4.5
|
||||
scikit-learn==1.5.1
|
||||
scipy==1.11.4
|
||||
SecretStorage==3.3.3
|
||||
semver==3.0.2
|
||||
Send2Trash==1.8.3
|
||||
setuptools-scm==8.0.4
|
||||
sgmllib3k==1.0.0
|
||||
shellingham==1.5.4
|
||||
six==1.16.0
|
||||
sniffio==1.3.0
|
||||
snowballstemmer==2.2.0
|
||||
soupsieve==2.5
|
||||
Sphinx==7.2.6
|
||||
sphinx-autobuild==2021.3.14
|
||||
sphinx-basic-ng==1.0.0b2
|
||||
sphinx-click==5.1.0
|
||||
sphinx-togglebutton==0.3.2
|
||||
sphinxcontrib-applehelp==1.0.7
|
||||
sphinxcontrib-devhelp==1.0.5
|
||||
sphinxcontrib-htmlhelp==2.0.4
|
||||
sphinxcontrib-jsmath==1.0.1
|
||||
sphinxcontrib-qthelp==1.0.6
|
||||
sphinxcontrib-serializinghtml==1.1.9
|
||||
SQLAlchemy==2.0.24
|
||||
stack-data==0.6.3
|
||||
statsmodels==0.14.2
|
||||
sympy==1.12
|
||||
tables==3.9.2
|
||||
tabulate==0.9.0
|
||||
tenacity==8.2.3
|
||||
termcolor==2.4.0
|
||||
terminado==0.18.1
|
||||
threadpoolctl==3.5.0
|
||||
tiktoken==0.7.0
|
||||
tinycss2==1.3.0
|
||||
toml-sort==0.23.1
|
||||
tomlkit==0.12.3
|
||||
torch==2.1.2
|
||||
torch_geometric==2.5.3
|
||||
tornado==6.4
|
||||
tqdm==4.66.1
|
||||
traitlets==5.14.3
|
||||
tree-sitter==0.22.3
|
||||
tree-sitter-python==0.21.0
|
||||
triton==2.1.0
|
||||
twine==4.0.2
|
||||
typer==0.9.0
|
||||
types-psutil==6.0.0.20240621
|
||||
types-python-dateutil==2.9.0.20240316
|
||||
types-PyYAML==6.0.12.20240724
|
||||
types-tqdm==4.66.0.20240417
|
||||
typing-inspect==0.9.0
|
||||
typing_extensions==4.9.0
|
||||
tzdata==2023.4
|
||||
uri-template==1.3.0
|
||||
urllib3==2.1.0
|
||||
wcwidth==0.2.13
|
||||
webcolors==24.6.0
|
||||
webencodings==0.5.1
|
||||
websocket-client==1.8.0
|
||||
widgetsnbextension==4.0.11
|
||||
yarl==1.9.4
|
||||
zipp==3.17.0
|
||||
psutil==6.1.0
|
||||
scipy==1.14.1
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# ============================================================
|
||||
# Predix Data Configuration
|
||||
# Ändere hier Instrument, Frequenz und Zeiträume
|
||||
# Alle anderen Komponenten lesen aus dieser Datei
|
||||
# ============================================================
|
||||
|
||||
instrument: EURUSD
|
||||
frequency: 1min # 1min, 5min, 15min, 1h, 1d
|
||||
data_path: ~/.qlib/qlib_data/eurusd_1min_data
|
||||
|
||||
# Verfügbare Spalten (keine $factor Spalte!)
|
||||
columns:
|
||||
- $open
|
||||
- $close
|
||||
- $high
|
||||
- $low
|
||||
- $volume
|
||||
|
||||
# Walk-Forward Split
|
||||
train_start: "2022-03-14"
|
||||
train_end: "2024-06-30"
|
||||
valid_start: "2024-07-01"
|
||||
valid_end: "2024-12-31"
|
||||
test_start: "2025-01-01"
|
||||
test_end: "2026-03-20"
|
||||
|
||||
# Markt-Kontext für LLM Prompts
|
||||
market_context:
|
||||
spread_bps: 1.5
|
||||
sessions:
|
||||
asian: "00:00-08:00 UTC"
|
||||
london: "08:00-16:00 UTC"
|
||||
ny: "13:00-21:00 UTC"
|
||||
overlap: "13:00-16:00 UTC"
|
||||
target_arr: 9.62 # % ARR zu schlagen
|
||||
max_drawdown: 20 # % maximaler Drawdown
|
||||
|
||||
# Lookback Referenz (in Bars)
|
||||
lookback:
|
||||
1h: 4
|
||||
2h: 8
|
||||
4h: 16
|
||||
8h: 32
|
||||
1d: 96
|
||||
Vendored
+332
@@ -0,0 +1,332 @@
|
||||
{
|
||||
"alpha053_15": {
|
||||
"description": "Reversal class factor, negative delta of a ratio involving close, low, and high prices over 15 days.",
|
||||
"formulation": "-1 times Deltaleft(frac{(text{close} - text{low}) - (text{high} - text{close})}{text{close} - text{low}}, 15right)",
|
||||
"variables": {
|
||||
"Delta(x, d)": "Change in 'x' over 'd' days.",
|
||||
"text{close}": "Closing price of the stock.",
|
||||
"text{low}": "Lowest price of the stock for the day.",
|
||||
"text{high}": "Highest price of the stock for the day."
|
||||
},
|
||||
"Category": "Volume&Price",
|
||||
"Difficulty": "Easy",
|
||||
"gt_code": "import pandas as pd\ndata_pv = pd.read_hdf('daily_pv.h5')\nnew_df= data_pv.reset_index()\n# Calculate Alpha053\nnew_df['ratio'] = (new_df['$close'] - new_df['$low'] - (new_df['$high'] - new_df['$close'])) / (new_df['$close'] - new_df['$low'])\n# the change of ratio in new_df over the 15 days\nnew_df['result']=-new_df['ratio'].diff(15)\n# transfer the result to series\nresult=pd.DataFrame(new_df['result']).set_index(data_pv.index)\nresult=result['result']\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"liquidity_imbalance": {
|
||||
"description": "liquidity_imbalance=std(minute trading liquidity_imbalance)/mean(minute trading liquidity_imbalance).",
|
||||
"formulation": "liquidity_imbalance = frac{text{std}(text{minute trading liquidity_imbalance})}{text{mean}(text{minute liquidity_imbalance})}",
|
||||
"variables": {
|
||||
"std(minute liquidity_imbalance)": "Standard deviation of trading liquidity_imbalance for each minute of the trading day.",
|
||||
"mean(minute liquidity_imbalance)": "Mean of trading liquidity_imbalance for each minute of the trading day.",
|
||||
"liquidity_imbalance": "(bid_size-ask_size)/(bid_size+ask_size), we use something like bidV for the size"
|
||||
},
|
||||
"Category": "High-Frequency",
|
||||
"Difficulty": "Medium",
|
||||
"gt_code": "import pandas as pd\ndata_hf = pd.read_hdf('high_freq.h5')\nsample_df= data_hf.reset_index()\n# Convert 'datetime' column to datetime and extract date for grouping\nsample_df['date'] = sample_df['datetime'].dt.date\nsample_df['liquidity_imbalance']=(sample_df['bidV']-sample_df['askV'])/(sample_df['bidV']+sample_df['askV'])\n# Group by instrument and date\ngrouped = sample_df.groupby(['date','instrument'])['liquidity_imbalance']\n# Calculate mean and standard deviation of the volume for each group\nstats = grouped.agg(['mean', 'std'])\n# Calculate Z value for each instrument per day\nstats['liquidity_imbalance'] = stats['std'] / stats['mean']\n# Display the calculated Z values\nresult=stats['liquidity_imbalance']\nresult.index.names = ['datetime','instrument']\n# result = result.swaplevel().sort_index()\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"liquidity_imbalance_2": {
|
||||
"description": "liquidity_imbalance=std(minute trading liquidity_imbalance)/mean(minute trading liquidity_imbalance).",
|
||||
"formulation": "liquidity_imbalance = frac{text{std}(text{minute trading liquidity_imbalance})}{text{mean}(text{minute liquidity_imbalance})}",
|
||||
"variables": {
|
||||
"std(minute liquidity_imbalance)": "Standard deviation of trading liquidity_imbalance for each minute of the trading day.",
|
||||
"mean(minute liquidity_imbalance)": "Mean of trading liquidity_imbalance for each minute of the trading day.",
|
||||
"liquidity_imbalance": "(bid_size-ask_size)/2*(bid_size+ask_size), we use something like bidV for the size"
|
||||
},
|
||||
"Category": "High-Frequency",
|
||||
"Difficulty": "Medium",
|
||||
"gt_code": "import pandas as pd\ndata_hf = pd.read_hdf('high_freq.h5')\nsample_df= data_hf.reset_index()\n# Convert 'datetime' column to datetime and extract date for grouping\nsample_df['date'] = sample_df['datetime'].dt.date\nsample_df['liquidity_imbalance']=(sample_df['bidV']-sample_df['askV'])/((sample_df['bidV']+sample_df['askV'])*2)\n# Group by instrument and date\ngrouped = sample_df.groupby(['date','instrument'])['liquidity_imbalance']\n# Calculate mean and standard deviation of the volume for each group\nstats = grouped.agg(['mean', 'std'])\n# Calculate Z value for each instrument per day\nstats['liquidity_imbalance'] = stats['std'] / stats['mean']\n# Display the calculated Z values\nresult=stats['liquidity_imbalance']\nresult.index.names = ['datetime','instrument']\n# result = result.swaplevel().sort_index()\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"liquidity_imbalance_3": {
|
||||
"description": "liquidity_imbalance=std(minute trading liquidity_imbalance)/mean(minute trading liquidity_imbalance).",
|
||||
"formulation": "liquidity_imbalance = frac{text{std}(text{minute trading liquidity_imbalance})}{text{mean}(text{minute liquidity_imbalance})}",
|
||||
"variables": {
|
||||
"std(minute liquidity_imbalance)": "Standard deviation of trading liquidity_imbalance for each minute of the trading day.",
|
||||
"mean(minute liquidity_imbalance)": "Mean of trading liquidity_imbalance for each minute of the trading day.",
|
||||
"liquidity_imbalance": "(bid_size-ask_size)/3*(bid_size+ask_size), we use something like bidV for the size"
|
||||
},
|
||||
"Category": "High-Frequency",
|
||||
"Difficulty": "Medium",
|
||||
"gt_code": "import pandas as pd\ndata_hf = pd.read_hdf('high_freq.h5')\nsample_df= data_hf.reset_index()\n# Convert 'datetime' column to datetime and extract date for grouping\nsample_df['date'] = sample_df['datetime'].dt.date\nsample_df['liquidity_imbalance']=(sample_df['bidV']-sample_df['askV'])/((sample_df['bidV']+sample_df['askV'])*3)\n# Group by instrument and date\ngrouped = sample_df.groupby(['date','instrument'])['liquidity_imbalance']\n# Calculate mean and standard deviation of the volume for each group\nstats = grouped.agg(['mean', 'std'])\n# Calculate Z value for each instrument per day\nstats['liquidity_imbalance'] = stats['std'] / stats['mean']\n# Display the calculated Z values\nresult=stats['liquidity_imbalance']\nresult.index.names = ['datetime','instrument']\n# result = result.swaplevel().sort_index()\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"micro_price": {
|
||||
"description": "micro_price=std(minute trading micro_price)/mean(minute trading micro_price).",
|
||||
"formulation": "micro_price = frac{text{std}(text{minute trading micro_price})}{text{mean}(text{minute micro_price})}",
|
||||
"variables": {
|
||||
"std(minute micro_price)": "Standard deviation of trading micro_price for each minute of the trading day.",
|
||||
"mean(minute micro_price)": "Mean of trading micro_price for each minute of the trading day.",
|
||||
"micro_price": "((df['bid_price'] * df['ask_size']) + (df['ask_price'] * df['bid_size'])) / (df['bid_size'] + df['ask_size'])"
|
||||
},
|
||||
"Category": "High-Frequency",
|
||||
"Difficulty": "Hard",
|
||||
"gt_code": "import pandas as pd\ndata_hf = pd.read_hdf('high_freq.h5')\nsample_df= data_hf.reset_index()\n# Convert 'datetime' column to datetime and extract date for grouping\nsample_df['date'] = sample_df['datetime'].dt.date\nsample_df['micro_price']=(sample_df['bid']*sample_df['askV']+sample_df['ask']*sample_df['bidV'])/(sample_df['bidV']+sample_df['askV'])\n# Group by instrument and date\ngrouped = sample_df.groupby(['date','instrument'])['micro_price']\n# Calculate mean and standard deviation of the volume for each group\nstats = grouped.agg(['mean', 'std'])\n# Calculate Z value for each instrument per day\nstats['micro_price'] = stats['std'] / stats['mean']\n# Display the calculated Z values\nresult=stats['micro_price']\nresult.index.names = ['datetime','instrument']\n# result = result.swaplevel().sort_index()\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"micro_price_2": {
|
||||
"description": "micro_price_2=std(minute trading micro_price)/mean(minute trading micro_price).",
|
||||
"formulation": "micro_price_2 = frac{text{std}(text{minute trading micro_price})}{text{mean}(text{minute micro_price})}",
|
||||
"variables": {
|
||||
"std(minute micro_price)": "Standard deviation of trading micro_price for each minute of the trading day.",
|
||||
"mean(minute micro_price)": "Mean of trading micro_price for each minute of the trading day.",
|
||||
"micro_price": "((df['bid_price'] * df['ask_size']) + (df['ask_price'] * df['bid_size'])) / 2*(df['bid_size'] + df['ask_size']), we use something like bidV for the size"
|
||||
},
|
||||
"Category": "High-Frequency",
|
||||
"Difficulty": "Hard",
|
||||
"gt_code": "import pandas as pd\ndata_hf = pd.read_hdf('high_freq.h5')\nsample_df= data_hf.reset_index()\n# Convert 'datetime' column to datetime and extract date for grouping\nsample_df['date'] = sample_df['datetime'].dt.date\nsample_df['micro_price']=(sample_df['bid']*sample_df['askV']+sample_df['ask']*sample_df['bidV'])/((sample_df['bidV']+sample_df['askV'])*2)\n# Group by instrument and date\ngrouped = sample_df.groupby(['date','instrument'])['micro_price']\n# Calculate mean and standard deviation of the volume for each group\nstats = grouped.agg(['mean', 'std'])\n# Calculate Z value for each instrument per day\nstats['micro_price'] = stats['std'] / stats['mean']\n# Display the calculated Z values\nresult=stats['micro_price']\nresult.index.names = ['datetime','instrument']\n# result = result.swaplevel().sort_index()\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"micro_price_3": {
|
||||
"description": "micro_price_3=std(minute trading micro_price)/mean(minute trading micro_price).",
|
||||
"formulation": "micro_price_3 = frac{text{std}(text{minute trading micro_price})}{text{mean}(text{minute micro_price})}",
|
||||
"variables": {
|
||||
"std(minute micro_price)": "Standard deviation of trading micro_price for each minute of the trading day.",
|
||||
"mean(minute micro_price)": "Mean of trading micro_price for each minute of the trading day.",
|
||||
"micro_price": "((df['bid_price'] * df['ask_size']) + (df['ask_price'] * df['bid_size'])) / 3*(df['bid_size'] + df['ask_size']), we use something like bidV for the size"
|
||||
},
|
||||
"Category": "High-Frequency",
|
||||
"Difficulty": "Hard",
|
||||
"gt_code": "import pandas as pd\ndata_hf = pd.read_hdf('high_freq.h5')\nsample_df= data_hf.reset_index()\n# Convert 'datetime' column to datetime and extract date for grouping\nsample_df['date'] = sample_df['datetime'].dt.date\nsample_df['micro_price']=(sample_df['bid']*sample_df['askV']+sample_df['ask']*sample_df['bidV'])/((sample_df['bidV']+sample_df['askV'])*3)\n# Group by instrument and date\ngrouped = sample_df.groupby(['date','instrument'])['micro_price']\n# Calculate mean and standard deviation of the volume for each group\nstats = grouped.agg(['mean', 'std'])\n# Calculate Z value for each instrument per day\nstats['micro_price'] = stats['std'] / stats['mean']\n# Display the calculated Z values\nresult=stats['micro_price']\nresult.index.names = ['datetime','instrument']\n# result = result.swaplevel().sort_index()\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"mid_price": {
|
||||
"description": "mid_price=std(minute trading mid_price)/mean(minute trading mid_price).",
|
||||
"formulation": "mid_price = frac{text{std}(text{minute trading mid price})}{text{mean}(text{minute mid price})}",
|
||||
"variables": {
|
||||
"std(minute mid_price)": "Standard deviation of trading mid_price for each minute of the trading day.",
|
||||
"mean(minute mid_price)": "Mean of trading mid_price for each minute of the trading day.",
|
||||
"mid_price": "The average of the bid and ask prices."
|
||||
},
|
||||
"Category": "High-Frequency",
|
||||
"Difficulty": "Easy",
|
||||
"gt_code": "import pandas as pd\ndata_hf = pd.read_hdf('high_freq.h5')\nsample_df= data_hf.reset_index()\n# Convert 'datetime' column to datetime and extract date for grouping\nsample_df['date'] = sample_df['datetime'].dt.date\nsample_df['mid_price']=(sample_df['bid']+sample_df['ask'])/2\n# Group by instrument and date\ngrouped = sample_df.groupby(['date','instrument'])['mid_price']\n# Calculate mean and standard deviation of the volume for each group\nstats = grouped.agg(['mean', 'std'])\nstats['mid_price'] = stats['std'] / stats['mean']\nresult=stats['mid_price']\nresult.index.names = ['datetime','instrument']\n# result = result.swaplevel().sort_index()\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"mid_price_2": {
|
||||
"description": "mid_price=std(minute trading mid_price)/mean(minute trading mid_price).",
|
||||
"formulation": "mid_price = frac{text{std}(text{minute trading mid price})}{text{mean}(text{minute mid price})}",
|
||||
"variables": {
|
||||
"std(minute mid_price)": "Standard deviation of trading mid_price for each minute of the trading day.",
|
||||
"mean(minute mid_price)": "Mean of trading mid_price for each minute of the trading day.",
|
||||
"mid_price_2": "the average of the bid and ask prices plus the the average of the bid and ask size (bidV and askV)."
|
||||
},
|
||||
"Category": "High-Frequency",
|
||||
"Difficulty": "Easy",
|
||||
"gt_code": "import pandas as pd\ndata_hf = pd.read_hdf('high_freq.h5')\nsample_df= data_hf.reset_index()\n# Convert 'datetime' column to datetime and extract date for grouping\nsample_df['date'] = sample_df['datetime'].dt.date\nsample_df['mid_price']=(sample_df['bid']+sample_df['ask'])/2+(sample_df['bidV']+sample_df['askV'])/2\n# Group by instrument and date\ngrouped = sample_df.groupby(['date','instrument'])['mid_price']\n# Calculate mean and standard deviation of the volume for each group\nstats = grouped.agg(['mean', 'std'])\nstats['mid_price'] = stats['std'] / stats['mean']\nresult=stats['mid_price']\nresult.index.names = ['datetime','instrument']\n# result = result.swaplevel().sort_index()\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"mid_price_3": {
|
||||
"description": "mid_price=std(minute trading mid_price)/mean(minute trading mid_price).",
|
||||
"formulation": "mid_price = frac{text{std}(text{minute trading mid price})}{text{mean}(text{minute mid price})}",
|
||||
"variables": {
|
||||
"std(minute mid_price)": "Standard deviation of trading mid_price for each minute of the trading day.",
|
||||
"mean(minute mid_price)": "Mean of trading mid_price for each minute of the trading day.",
|
||||
"mid_price_3": "The coefficient of variation (CV) of the mid-price for each minute of the trading day, calculated as the standard deviation of the mid-price divided by the mean mid-price."
|
||||
},
|
||||
"Category": "High-Frequency",
|
||||
"Difficulty": "Easy",
|
||||
"gt_code": "import pandas as pd\ndata_hf = pd.read_hdf('high_freq.h5')\nsample_df= data_hf.reset_index()\n# Convert 'datetime' column to datetime and extract date for grouping\nsample_df['date'] = sample_df['datetime'].dt.date\nsample_df['mid_price']=(sample_df['bid']+sample_df['ask'])/3\n# Group by instrument and date\ngrouped = sample_df.groupby(['date','instrument'])['mid_price']\n# Calculate mean and standard deviation of the volume for each group\nstats = grouped.agg(['mean', 'std'])\nstats['mid_price'] = stats['std'] / stats['mean']\nresult=stats['mid_price']\nresult.index.names = ['datetime','instrument']\n# result = result.swaplevel().sort_index()\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"PB_ROE": {
|
||||
"description": "Constructed using the ranking difference between PB and ROE, with regression versions of PB and ROE replacing original PB and ROE to obtain reconstructed factor values.",
|
||||
"formulation": "text{rank}(PB_t) - rank(ROE_t)",
|
||||
"variables": {
|
||||
"text{rank}(PB_t)": "Ranking of regression version PB on cross-section at time t.",
|
||||
"text{rank}(ROE_t)": "Ranking of regression version single-quarter ROE on cross-section at time t."
|
||||
},
|
||||
"Category": "Fundamentals",
|
||||
"Difficulty": "Easy",
|
||||
"gt_code": "import pandas as pd\ndata_f = pd.read_hdf('daily_f.h5')\ndata = data_f.reset_index()\n# Calculate the rank of PB and ROE\ndata['PB_rank'] = data.groupby('datetime')['B/P'].rank()\ndata['ROE_rank'] = data.groupby('datetime')['ROE'].rank()\n# Calculate the difference between the ranks\ndata['PB_ROE'] = data['PB_rank'] - data['ROE_rank']\n# set the datetime and instrument as index and drop the original index\nresult=pd.DataFrame(data['PB_ROE']).set_index(data_f.index)\n# transfer the result to series\nresult=result['PB_ROE']\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"PB_ROE_2": {
|
||||
"description": "Constructed using the ranking difference between PB/2 and ROE, with regression versions of PB and ROE replacing original PB and ROE to obtain reconstructed factor values.",
|
||||
"formulation": "text{rank}(PB_t)/2 - rank(ROE_t)",
|
||||
"variables": {
|
||||
"text{rank}(PB_t)": "Ranking of regression version PB on cross-section at time t.",
|
||||
"text{rank}(ROE_t)": "Ranking of regression version single-quarter ROE on cross-section at time t."
|
||||
},
|
||||
"Category": "Fundamentals",
|
||||
"Difficulty": "Easy",
|
||||
"gt_code": "import pandas as pd\ndata_f = pd.read_hdf('daily_f.h5')\ndata = data_f.reset_index()\n# Calculate the rank of PB and ROE\ndata['PB_rank'] = data.groupby('datetime')['B/P'].rank()\ndata['ROE_rank'] = data.groupby('datetime')['ROE'].rank()\n# Calculate the difference between the ranks\ndata['PB_ROE'] = data['PB_rank']/2 - data['ROE_rank']\n# set the datetime and instrument as index and drop the original index\nresult=pd.DataFrame(data['PB_ROE']).set_index(data_f.index)\n# transfer the result to series\nresult=result['PB_ROE']\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"PB_ROE_3": {
|
||||
"description": "Constructed using the ranking difference between PB/3 and ROE, with regression versions of PB and ROE replacing original PB and ROE to obtain reconstructed factor values.",
|
||||
"formulation": "text{rank}(PB_t)/3 - rank(ROE_t)",
|
||||
"variables": {
|
||||
"text{rank}(PB_t)": "Ranking of regression version PB on cross-section at time t.",
|
||||
"text{rank}(ROE_t)": "Ranking of regression version single-quarter ROE on cross-section at time t."
|
||||
},
|
||||
"Category": "Fundamentals",
|
||||
"Difficulty": "Easy",
|
||||
"gt_code": "import pandas as pd\ndata_f = pd.read_hdf('daily_f.h5')\ndata = data_f.reset_index()\n# Calculate the rank of PB and ROE\ndata['PB_rank'] = data.groupby('datetime')['B/P'].rank()\ndata['ROE_rank'] = data.groupby('datetime')['ROE'].rank()\n# Calculate the difference between the ranks\ndata['PB_ROE'] = data['PB_rank']/3 - data['ROE_rank']\n# set the datetime and instrument as index and drop the original index\nresult=pd.DataFrame(data['PB_ROE']).set_index(data_f.index)\n# transfer the result to series\nresult=result['PB_ROE']\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"PB_ROE_movement": {
|
||||
"description": "PB_ROE_movement=five day PB_ROE movement indicator(-1 and 1 or 0).",
|
||||
"formulation": "PB_ROE_movement = 5_day_movement(PB_ROE), PB_ROE = text{rank}(PB_t) - rank(ROE_t)",
|
||||
"variables": {
|
||||
"PB_ROE": "the ranking difference between PB and ROE.",
|
||||
"5_day_PB_ROE_movement": "1 if PB_ROE is higher than the PB_ROE 5 days ago, -1 if PB_ROE is lower than the PB_ROE 5 days ago, 0 if PB_ROE is the same as the PB_ROE 5 days ago.",
|
||||
"text{rank}(PB_t)": "Ranking of regression version PB on cross-section at time t.",
|
||||
"text{rank}(ROE_t)": "Ranking of regression version single-quarter ROE on cross-section at time t."
|
||||
},
|
||||
"Category": "Fundamentals",
|
||||
"Difficulty": "Hard",
|
||||
"gt_code": "import pandas as pd\ndata_f = pd.read_hdf('daily_f.h5')\nsample_df = data_f.reset_index()\n# Calculate the rank of PB and ROE\nsample_df['PB_rank'] = sample_df.groupby('datetime')['B/P'].rank()\nsample_df['ROE_rank'] = sample_df.groupby('datetime')['ROE'].rank()\nsample_df['PB_ROE'] = sample_df['PB_rank'] - sample_df['ROE_rank']\n# Group by instrument and date\nsample_df['PB_ROE_movement'] = sample_df['PB_ROE'].diff(periods=5).apply(lambda x: 1 if x > 0 else (-1 if x < 0 else 0))\n#calculate the mid_price_movement ratio for each day\n# set the datetime and instrument as index and drop the original index\nresult=pd.DataFrame(sample_df['PB_ROE_movement']).set_index(data_f.index)\n# transfer the result to series\nresult=result['PB_ROE_movement']\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"PB_ROE_movement_10": {
|
||||
"description": "PB_ROE_movement=10 days PB_ROE movement indicator(-1 and 1 or 0).",
|
||||
"formulation": "PB_ROE_movement = 10_day_movement(PB_ROE), PB_ROE = text{rank}(PB_t) - rank(ROE_t)",
|
||||
"variables": {
|
||||
"PB_ROE": "the ranking difference between PB and ROE.",
|
||||
"10_day_PB_ROE_movement": "1 if PB_ROE is higher than the PB_ROE 10 days ago, -1 if PB_ROE is lower than the PB_ROE 10 days ago, 0 if PB_ROE is the same as the PB_ROE 10 days ago.",
|
||||
"text{rank}(PB_t)": "Ranking of regression version PB on cross-section at time t.",
|
||||
"text{rank}(ROE_t)": "Ranking of regression version single-quarter ROE on cross-section at time t."
|
||||
},
|
||||
"Category": "Fundamentals",
|
||||
"Difficulty": "Hard",
|
||||
"gt_code": "import pandas as pd\ndata_f = pd.read_hdf('daily_f.h5')\nsample_df = data_f.reset_index()\n# Calculate the rank of PB and ROE\nsample_df['PB_rank'] = sample_df.groupby('datetime')['B/P'].rank()\nsample_df['ROE_rank'] = sample_df.groupby('datetime')['ROE'].rank()\nsample_df['PB_ROE'] = sample_df['PB_rank'] - sample_df['ROE_rank']\n# Group by instrument and date\nsample_df['PB_ROE_movement'] = sample_df['PB_ROE'].diff(periods=10).apply(lambda x: 1 if x > 0 else (-1 if x < 0 else 0))\n#calculate the mid_price_movement ratio for each day\n# set the datetime and instrument as index and drop the original index\nresult=pd.DataFrame(sample_df['PB_ROE_movement']).set_index(data_f.index)\n# transfer the result to series\nresult=result['PB_ROE_movement']\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"PB_ROE_movement_20": {
|
||||
"description": "PB_ROE_movement=20 days PB_ROE movement indicator(-1 and 1 or 0).",
|
||||
"formulation": "PB_ROE_movement = 20_day_movement(PB_ROE), PB_ROE = text{rank}(PB_t) - rank(ROE_t)",
|
||||
"variables": {
|
||||
"PB_ROE": "the ranking difference between PB and ROE.",
|
||||
"20_day_PB_ROE_movement": "1 if PB_ROE is higher than the PB_ROE 20 days ago, -1 if PB_ROE is lower than the PB_ROE 20 days ago, 0 if PB_ROE is the same as the PB_ROE 20 days ago.",
|
||||
"text{rank}(PB_t)": "Ranking of regression version PB on cross-section at time t.",
|
||||
"text{rank}(ROE_t)": "Ranking of regression version single-quarter ROE on cross-section at time t."
|
||||
},
|
||||
"Category": "Fundamentals",
|
||||
"Difficulty": "Hard",
|
||||
"gt_code": "import pandas as pd\ndata_f = pd.read_hdf('daily_f.h5')\nsample_df = data_f.reset_index()\n# Calculate the rank of PB and ROE\nsample_df['PB_rank'] = sample_df.groupby('datetime')['B/P'].rank()\nsample_df['ROE_rank'] = sample_df.groupby('datetime')['ROE'].rank()\nsample_df['PB_ROE'] = sample_df['PB_rank'] - sample_df['ROE_rank']\n# Group by instrument and date\nsample_df['PB_ROE_movement'] = sample_df['PB_ROE'].diff(periods=20).apply(lambda x: 1 if x > 0 else (-1 if x < 0 else 0))\n#calculate the mid_price_movement ratio for each day\n# set the datetime and instrument as index and drop the original index\nresult=pd.DataFrame(sample_df['PB_ROE_movement']).set_index(data_f.index)\n# transfer the result to series\nresult=result['PB_ROE_movement']\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"ROE_movement": {
|
||||
"description": "ROE_movement=five day ROE movement indicator(-1 and 1 or 0).",
|
||||
"formulation": "ROE_movement = 5_day_movement(ROE)",
|
||||
"variables": {
|
||||
"ROE": "ROE in fundamental statistics.",
|
||||
"5_day_ROE_movement": "1 if ROE is higher than the ROE 5 days ago, -1 if ROE is lower than the ROE 5 days ago, 0 if ROE is the same as the ROE 5 days ago."
|
||||
},
|
||||
"Category": "Fundamentals",
|
||||
"Difficulty": "Medium",
|
||||
"gt_code": "import pandas as pd\ndata_f = pd.read_hdf('daily_f.h5')\nsample_df = data_f.reset_index()\n# Group by instrument and date\nsample_df['ROE_movement'] = sample_df['ROE'].diff(periods=5).apply(lambda x: 1 if x > 0 else (-1 if x < 0 else 0))\n#calculate the mid_price_movement ratio for each day\n# set the datetime and instrument as index and drop the original index\nresult=pd.DataFrame(sample_df['ROE_movement']).set_index(data_f.index)\n# transfer the result to series\nresult=result['ROE_movement']\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"ROE_movement_10": {
|
||||
"description": "ROE_movement_10=ten day ROE movement indicator(-1 and 1 or 0).",
|
||||
"formulation": "ROE_movement = 10_day_movement(ROE)",
|
||||
"variables": {
|
||||
"ROE": "ROE in fundamental statistics.",
|
||||
"10_day_ROE_movement": "1 if ROE is higher than the ROE 10 days ago, -1 if ROE is lower than the ROE 10 days ago, 0 if ROE is the same as the ROE 10 days ago."
|
||||
},
|
||||
"Category": "Fundamentals",
|
||||
"Difficulty": "Medium",
|
||||
"gt_code": "import pandas as pd\ndata_f = pd.read_hdf('daily_f.h5')\nsample_df = data_f.reset_index()\n# Group by instrument and date\nsample_df['ROE_movement'] = sample_df['ROE'].diff(periods=10).apply(lambda x: 1 if x > 0 else (-1 if x < 0 else 0))\n#calculate the mid_price_movement ratio for each day\n# set the datetime and instrument as index and drop the original index\nresult=pd.DataFrame(sample_df['ROE_movement']).set_index(data_f.index)\n# transfer the result to series\nresult=result['ROE_movement']\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"ROE_movement_20": {
|
||||
"description": "ROE_movement_20=20 day ROE movement indicator(-1 and 1 or 0).",
|
||||
"formulation": "ROE_movement_20 = 20_day_movement(ROE)",
|
||||
"variables": {
|
||||
"ROE": "ROE in fundamental statistics.",
|
||||
"20_day_ROE_movement": "1 if ROE is higher than the ROE 20 days ago, -1 if ROE is lower than the ROE 20 days ago, 0 if ROE is the same as the ROE 20 days ago."
|
||||
},
|
||||
"Category": "Fundamentals",
|
||||
"Difficulty": "Medium",
|
||||
"gt_code": "import pandas as pd\ndata_f = pd.read_hdf('daily_f.h5')\nsample_df = data_f.reset_index()\n# Group by instrument and date\nsample_df['ROE_movement'] = sample_df['ROE'].diff(periods=20).apply(lambda x: 1 if x > 0 else (-1 if x < 0 else 0))\n#calculate the mid_price_movement ratio for each day\n# set the datetime and instrument as index and drop the original index\nresult=pd.DataFrame(sample_df['ROE_movement']).set_index(data_f.index)\n# transfer the result to series\nresult=result['ROE_movement']\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"alpha_pv_diff": {
|
||||
"description": "alpha_pv_diff is defined as the ratio of the difference between close prices 10 days change and open prices 10 days change to the sum of the highest minus lowest prices plus a small constant.",
|
||||
"formulation": "frac{(text{close_diff10} - text{open_diff10})}{(text{high} - text{low} + 0.001)}",
|
||||
"variables": {
|
||||
"close": "Closing price of the stock",
|
||||
"open": "Opening price of the stock",
|
||||
"high": "Highest price of the stock during the day",
|
||||
"low": "Lowest price of the stock during the day"
|
||||
},
|
||||
"Category": "Volume&Price",
|
||||
"Difficulty": "Medium",
|
||||
"gt_code": "import pandas as pd\ndata_pv = pd.read_hdf('daily_pv.h5')\nnew_df= data_pv.reset_index()\n# Calculate Alpha101\nnew_df['result'] = (new_df['$close'].diff(10) - new_df['$open'].diff(10)) / (new_df['$high'] - new_df['$low'] + 0.001)\n# keep the index of the original dataframe\nresult=pd.DataFrame(new_df['result']).set_index(data_pv.index)\n# transfer the result to series\nresult=result['result']\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"alpha_pv_diff_15": {
|
||||
"description": "alpha_pv_diff is defined as the ratio of the difference between close prices 15 days change and open prices 15 days change to the sum of the highest minus lowest prices plus a small constant.",
|
||||
"formulation": "frac{(text{close_diff15} - text{open_diff15})}{(text{high} - text{low} + 0.001)}",
|
||||
"variables": {
|
||||
"close": "Closing price of the stock",
|
||||
"open": "Opening price of the stock",
|
||||
"high": "Highest price of the stock during the day",
|
||||
"low": "Lowest price of the stock during the day"
|
||||
},
|
||||
"Category": "Volume&Price",
|
||||
"Difficulty": "Medium",
|
||||
"gt_code": "import pandas as pd\ndata_pv = pd.read_hdf('daily_pv.h5')\nnew_df= data_pv.reset_index()\n# Calculate Alpha101\nnew_df['result'] = (new_df['$close'].diff(15) - new_df['$open'].diff(15)) / (new_df['$high'] - new_df['$low'] + 0.001)\n# keep the index of the original dataframe\nresult=pd.DataFrame(new_df['result']).set_index(data_pv.index)\n# transfer the result to series\nresult=result['result']\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"alpha_pv_diff_20": {
|
||||
"description": "alpha_pv_diff is defined as the ratio of the difference between close prices 20 days change and open prices 20 days change to the sum of the highest minus lowest prices plus a small constant.",
|
||||
"formulation": "frac{(text{close_diff20} - text{open_diff20})}{(text{high} - text{low} + 0.001)}",
|
||||
"variables": {
|
||||
"close": "Closing price of the stock",
|
||||
"open": "Opening price of the stock",
|
||||
"high": "Highest price of the stock during the day",
|
||||
"low": "Lowest price of the stock during the day"
|
||||
},
|
||||
"Category": "Volume&Price",
|
||||
"Difficulty": "Medium",
|
||||
"gt_code": "import pandas as pd\ndata_pv = pd.read_hdf('daily_pv.h5')\nnew_df= data_pv.reset_index()\n# Calculate Alpha101\nnew_df['result'] = (new_df['$close'].diff(20) - new_df['$open'].diff(20)) / (new_df['$high'] - new_df['$low'] + 0.001)\n# keep the index of the original dataframe\nresult=pd.DataFrame(new_df['result']).set_index(data_pv.index)\n# transfer the result to series\nresult=result['result']\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"alpha_pv_diff_pct": {
|
||||
"description": "alpha_pv is defined as the ratio of the difference between close prices 10 days change and open prices 10 days change to the sum of the highest prices 10 days change ratio minus lowest prices 10 days change ratio plus a small constant.",
|
||||
"formulation": "frac{(text{close_diff10} - text{open_diff10})}{(text{high_pct10} - text{low_pct10} + 0.001)}",
|
||||
"variables": {
|
||||
"close": "Closing price of the stock",
|
||||
"open": "Opening price of the stock",
|
||||
"high": "Highest price of the stock during the day",
|
||||
"low": "Lowest price of the stock during the day"
|
||||
},
|
||||
"Category": "Volume&Price",
|
||||
"Difficulty": "Hard",
|
||||
"gt_code": "import pandas as pd\ndata_pv = pd.read_hdf('daily_pv.h5')\nnew_df= data_pv.reset_index()\n# Calculate Alpha101\nnew_df['result'] = (new_df['$close'].diff(10) - new_df['$open'].diff(10)) / (new_df['$high'].pct_change(10) - new_df['$low'].pct_change(10) + 0.001)\n# keep the index of the original dataframe\nresult=pd.DataFrame(new_df['result']).set_index(data_pv.index)\n# transfer the result to series\nresult=result['result']\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"alpha_pv_diff_pct_15": {
|
||||
"description": "alpha_pv is defined as the ratio of the difference between close prices 15 days change and open prices 15 days change to the sum of the highest prices 10 days change ratio minus lowest prices 10 days change ratio plus a small constant.",
|
||||
"formulation": "frac{(text{close_diff15} - text{open_diff15})}{(text{high_pct10} - text{low_pct10} + 0.001)}",
|
||||
"variables": {
|
||||
"close": "Closing price of the stock",
|
||||
"open": "Opening price of the stock",
|
||||
"high": "Highest price of the stock during the day",
|
||||
"low": "Lowest price of the stock during the day"
|
||||
},
|
||||
"Category": "Volume&Price",
|
||||
"Difficulty": "Hard",
|
||||
"gt_code": "import pandas as pd\ndata_pv = pd.read_hdf('daily_pv.h5')\nnew_df= data_pv.reset_index()\n# Calculate Alpha101\nnew_df['result'] = (new_df['$close'].diff(15) - new_df['$open'].diff(15)) / (new_df['$high'].pct_change(10) - new_df['$low'].pct_change(10) + 0.001)\n# keep the index of the original dataframe\nresult=pd.DataFrame(new_df['result']).set_index(data_pv.index)\n# transfer the result to series\nresult=result['result']\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"alpha_pv_diff_pct_20": {
|
||||
"description": "alpha_pv is defined as the ratio of the difference between close prices 20 days change and open prices 20 days change to the sum of the highest prices 10 days change ratio minus lowest prices 10 days change ratio plus a small constant.",
|
||||
"formulation": "frac{(text{close_diff20} - text{open_diff20})}{(text{high_pct10} - text{low_pct10} + 0.001)}",
|
||||
"variables": {
|
||||
"close": "Closing price of the stock",
|
||||
"open": "Opening price of the stock",
|
||||
"high": "Highest price of the stock during the day",
|
||||
"low": "Lowest price of the stock during the day"
|
||||
},
|
||||
"Category": "Volume&Price",
|
||||
"Difficulty": "Hard",
|
||||
"gt_code": "import pandas as pd\ndata_pv = pd.read_hdf('daily_pv.h5')\nnew_df= data_pv.reset_index()\n# Calculate Alpha101\nnew_df['result'] = (new_df['$close'].diff(20) - new_df['$open'].diff(20)) / (new_df['$high'].pct_change(10) - new_df['$low'].pct_change(10) + 0.001)\n# keep the index of the original dataframe\nresult=pd.DataFrame(new_df['result']).set_index(data_pv.index)\n# transfer the result to series\nresult=result['result']\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"alpha053": {
|
||||
"description": "Reversal class factor, negative delta of a ratio involving close, low, and high prices over 9 days.",
|
||||
"formulation": "-1 times Deltaleft(frac{(text{close} - text{low}) - (text{high} - text{close})}{text{close} - text{low}}, 9right)",
|
||||
"variables": {
|
||||
"Delta(x, d)": "Change in 'x' over 'd' days.",
|
||||
"text{close}": "Closing price of the stock.",
|
||||
"text{low}": "Lowest price of the stock for the day.",
|
||||
"text{high}": "Highest price of the stock for the day."
|
||||
},
|
||||
"Category": "Volume&Price",
|
||||
"Difficulty": "Easy",
|
||||
"gt_code": "import pandas as pd\ndata_pv = pd.read_hdf('daily_pv.h5')\nnew_df= data_pv.reset_index()\n# Calculate Alpha053\nnew_df['ratio'] = (new_df['$close'] - new_df['$low'] - (new_df['$high'] - new_df['$close'])) / (new_df['$close'] - new_df['$low'])\n# the change of ratio in new_df over the 9 days\nnew_df['result']=-new_df['ratio'].diff(9)\n# transfer the result to series\nresult=pd.DataFrame(new_df['result']).set_index(data_pv.index)\nresult=result['result']\nresult.to_hdf('result.h5', key='data')"
|
||||
},
|
||||
"alpha053_5": {
|
||||
"description": "Reversal class factor, negative delta of a ratio involving close, low, and high prices over 5 days.",
|
||||
"formulation": "-1 times Deltaleft(frac{(text{close} - text{low}) - (text{high} - text{close})}{text{close} - text{low}}, 5right)",
|
||||
"variables": {
|
||||
"Delta(x, d)": "Change in 'x' over 'd' days.",
|
||||
"text{close}": "Closing price of the stock.",
|
||||
"text{low}": "Lowest price of the stock for the day.",
|
||||
"text{high}": "Highest price of the stock for the day."
|
||||
},
|
||||
"Category": "Volume&Price",
|
||||
"Difficulty": "Easy",
|
||||
"gt_code": "import pandas as pd\ndata_pv = pd.read_hdf('daily_pv.h5')\nnew_df= data_pv.reset_index()\n# Calculate Alpha053\nnew_df['ratio'] = (new_df['$close'] - new_df['$low'] - (new_df['$high'] - new_df['$close'])) / (new_df['$close'] - new_df['$low'])\n# the change of ratio in new_df over the 5 days\nnew_df['result']=-new_df['ratio'].diff(5)\n# transfer the result to series\nresult=pd.DataFrame(new_df['result']).set_index(data_pv.index)\nresult=result['result']\nresult.to_hdf('result.h5', key='data')"
|
||||
}
|
||||
}
|
||||
Vendored
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 88 KiB |
+4
-4
@@ -10,9 +10,9 @@ import subprocess
|
||||
|
||||
latest_tag = subprocess.check_output(["git", "describe", "--tags", "--abbrev=0"], text=True).strip()
|
||||
|
||||
project = "RDAgent"
|
||||
copyright = "2024, Microsoft"
|
||||
author = "Microsoft"
|
||||
project = "Predix"
|
||||
copyright = "2025, Predix Team"
|
||||
author = "Predix Team"
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
@@ -66,7 +66,7 @@ html_static_path = ["_static"]
|
||||
html_favicon = "_static/favicon.ico"
|
||||
|
||||
html_theme_options = {
|
||||
"source_repository": "https://github.com/microsoft/RD-Agent",
|
||||
"source_repository": "https://github.com/PredixAI/predix",
|
||||
"source_branch": "main",
|
||||
"source_directory": "docs/",
|
||||
}
|
||||
|
||||
+4
-4
@@ -1,13 +1,13 @@
|
||||
.. RDAgent documentation master file, created by
|
||||
.. Predix documentation master file, created by
|
||||
sphinx-quickstart on Mon Jul 15 04:27:50 2024.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to RDAgent's documentation!
|
||||
Welcome to Predix's documentation!
|
||||
===================================
|
||||
|
||||
.. image:: _static/logo.png
|
||||
:alt: RD-Agent Logo
|
||||
:alt: Predix Logo
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
@@ -23,7 +23,7 @@ Welcome to RDAgent's documentation!
|
||||
api_reference
|
||||
policy
|
||||
|
||||
GitHub <https://github.com/microsoft/RD-Agent>
|
||||
GitHub <https://github.com/PredixAI/predix>
|
||||
|
||||
|
||||
Indices and tables
|
||||
|
||||
@@ -11,13 +11,291 @@ Installation
|
||||
- for dev users: `See development <development.html>`_
|
||||
|
||||
**Install Docker**: RDAgent is designed for research and development, acting like a human researcher and developer. It can write and run code in various environments, primarily using Docker for code execution. This keeps the remaining dependencies simple. Users must ensure Docker is installed before attempting most scenarios. Please refer to the `official 🐳Docker page <https://docs.docker.com/engine/install/>`_ for installation instructions.
|
||||
Ensure the current user can run Docker commands **without using sudo**. You can verify this by executing `docker run hello-world`.
|
||||
|
||||
Configuration
|
||||
=============
|
||||
LiteLLM Backend Configuration (Default)
|
||||
=======================================
|
||||
|
||||
.. note::
|
||||
🔥 **Attention**: We now provide experimental support for **DeepSeek** models! You can use DeepSeek's official API for cost-effective and high-performance inference. See the configuration example below for DeepSeek setup.
|
||||
|
||||
Option 1: Unified API base for both models
|
||||
------------------------------------------
|
||||
|
||||
.. code-block:: Properties
|
||||
|
||||
# Set to any model supported by LiteLLM.
|
||||
CHAT_MODEL=gpt-4o
|
||||
EMBEDDING_MODEL=text-embedding-3-small
|
||||
# Configure unified API base
|
||||
# The backend api_key fully follows the convention of litellm.
|
||||
OPENAI_API_BASE=<your_unified_api_base>
|
||||
OPENAI_API_KEY=<replace_with_your_openai_api_key>
|
||||
|
||||
Option 2: Separate API bases for Chat and Embedding models
|
||||
----------------------------------------------------------
|
||||
|
||||
.. code-block:: Properties
|
||||
|
||||
# Set to any model supported by LiteLLM.
|
||||
|
||||
# CHAT MODEL:
|
||||
CHAT_MODEL=gpt-4o
|
||||
OPENAI_API_BASE=<your_chat_api_base>
|
||||
OPENAI_API_KEY=<replace_with_your_openai_api_key>
|
||||
|
||||
# EMBEDDING MODEL:
|
||||
# TAKE siliconflow as an example, you can use other providers.
|
||||
# Note: embedding requires litellm_proxy prefix
|
||||
EMBEDDING_MODEL=litellm_proxy/BAAI/bge-large-en-v1.5
|
||||
LITELLM_PROXY_API_KEY=<replace_with_your_siliconflow_api_key>
|
||||
LITELLM_PROXY_API_BASE=https://api.siliconflow.cn/v1
|
||||
|
||||
Configuration Example: DeepSeek Setup
|
||||
-------------------------------------
|
||||
|
||||
Many users encounter configuration errors when setting up DeepSeek. Here's a complete working example:
|
||||
|
||||
.. code-block:: Properties
|
||||
|
||||
# CHAT MODEL: Using DeepSeek Official API
|
||||
CHAT_MODEL=deepseek/deepseek-chat
|
||||
DEEPSEEK_API_KEY=<replace_with_your_deepseek_api_key>
|
||||
|
||||
# EMBEDDING MODEL: Using SiliconFlow for embedding since DeepSeek has no embedding model.
|
||||
# Note: embedding requires litellm_proxy prefix
|
||||
EMBEDDING_MODEL=litellm_proxy/BAAI/bge-m3
|
||||
LITELLM_PROXY_API_KEY=<replace_with_your_siliconflow_api_key>
|
||||
LITELLM_PROXY_API_BASE=https://api.siliconflow.cn/v1
|
||||
|
||||
Necessary parameters include:
|
||||
|
||||
- `CHAT_MODEL`: The model name of the chat model.
|
||||
|
||||
- `EMBEDDING_MODEL`: The model name of the embedding model.
|
||||
|
||||
- `OPENAI_API_BASE`: The base URL of the API. If `EMBEDDING_MODEL` does not start with `litellm_proxy/`, this is used for both chat and embedding models; otherwise, it is used for `CHAT_MODEL` only.
|
||||
|
||||
Optional parameters (required if your embedding model is provided by a different provider than `CHAT_MODEL`):
|
||||
|
||||
- `LITELLM_PROXY_API_KEY`: The API key for the embedding model, required if `EMBEDDING_MODEL` starts with `litellm_proxy/`.
|
||||
|
||||
- `LITELLM_PROXY_API_BASE`: The base URL for the embedding model, required if `EMBEDDING_MODEL` starts with `litellm_proxy/`.
|
||||
|
||||
**Note:** If you are using an embedding model from a provider different from the chat model, remember to add the `litellm_proxy/` prefix to the `EMBEDDING_MODEL` name.
|
||||
|
||||
|
||||
The `CHAT_MODEL` and `EMBEDDING_MODEL` parameters will be passed into LiteLLM's completion function.
|
||||
|
||||
Therefore, when utilizing models provided by different providers, first review the interface configuration of LiteLLM. The model names must match those allowed by LiteLLM.
|
||||
|
||||
Additionally, you need to set up the the additional parameters for the respective model provider, and the parameter names must align with those required by LiteLLM.
|
||||
|
||||
For example, if you are using a DeepSeek model, you need to set as follows:
|
||||
|
||||
.. code-block:: Properties
|
||||
|
||||
# For some models LiteLLM requires a prefix to the model name.
|
||||
CHAT_MODEL=deepseek/deepseek-chat
|
||||
DEEPSEEK_API_KEY=<replace_with_your_deepseek_api_key>
|
||||
|
||||
Besides, when you are using reasoning models, the response might include the thought process. For this case, you need to set the following environment variable:
|
||||
|
||||
.. code-block:: Properties
|
||||
|
||||
REASONING_THINK_RM=True
|
||||
|
||||
For more details on LiteLLM requirements, refer to the `official LiteLLM documentation <https://docs.litellm.ai/docs>`_.
|
||||
|
||||
Configuration Example 2: Azure OpenAI Setup
|
||||
-------------------------------------------
|
||||
Here’s a sample configuration specifically for Azure OpenAI, based on the `official LiteLLM documentation <https://docs.litellm.ai/docs>`_:
|
||||
|
||||
If you're using Azure OpenAI, below is a working example using the Python SDK, following the `LiteLLM Azure OpenAI documentation <https://docs.litellm.ai/docs/providers/azure/>`_:
|
||||
|
||||
.. code-block:: Properties
|
||||
|
||||
from litellm import completion
|
||||
import os
|
||||
|
||||
# Set Azure OpenAI environment variables
|
||||
os.environ["AZURE_API_KEY"] = "<your_azure_api_key>"
|
||||
os.environ["AZURE_API_BASE"] = "<your_azure_api_base>"
|
||||
os.environ["AZURE_API_VERSION"] = "<version>"
|
||||
|
||||
# Make a request to your Azure deployment
|
||||
response = completion(
|
||||
"azure/<your_deployment_name>",
|
||||
messages = [{ "content": "Hello, how are you?", "role": "user" }]
|
||||
)
|
||||
|
||||
To align with the Python SDK example above, you can configure the `CHAT_MODEL` based on the `response` model setting and use the corresponding `os.environ` variables by writing them into your local `.env` file as follows:
|
||||
|
||||
.. code-block:: Properties
|
||||
|
||||
cat << EOF > .env
|
||||
# CHAT MODEL: Azure OpenAI via LiteLLM
|
||||
CHAT_MODEL=azure/<your_deployment_name>
|
||||
AZURE_API_BASE=https://<your_azure_base>.openai.azure.com/
|
||||
AZURE_API_KEY=<your_azure_api_key>
|
||||
AZURE_API_VERSION=<version>
|
||||
|
||||
# EMBEDDING MODEL: Using SiliconFlow via litellm_proxy
|
||||
EMBEDDING_MODEL=litellm_proxy/BAAI/bge-large-en-v1.5
|
||||
LITELLM_PROXY_API_KEY=<your_siliconflow_api_key>
|
||||
LITELLM_PROXY_API_BASE=https://api.siliconflow.cn/v1
|
||||
EOF
|
||||
|
||||
This configuration allows you to call Azure OpenAI through LiteLLM while using an external provider (e.g., SiliconFlow) for embeddings.
|
||||
|
||||
If your `Azure OpenAI API Key`` supports `embedding model`, you can refer to the following configuration example.
|
||||
|
||||
.. code-block:: Properties
|
||||
|
||||
cat << EOF > .env
|
||||
EMBEDDING_MODEL=azure/<Model deployment supporting embedding>
|
||||
CHAT_MODEL=azure/<your deployment name>
|
||||
AZURE_API_KEY=<replace_with_your_openai_api_key>
|
||||
AZURE_API_BASE=<your_unified_api_base>
|
||||
AZURE_API_VERSION=<azure api version>
|
||||
|
||||
Execution Environment Configuration
|
||||
===================================
|
||||
|
||||
Coder Environment Configuration (Docker vs. Conda)
|
||||
|
||||
RD-Agent's coders can execute code in different environments. You can control this behavior by setting environment variables in your ``.env`` file. This is useful for switching between a local Conda environment and an isolated Docker container.
|
||||
|
||||
To configure the environment, add the corresponding line to your ``.env`` file based on the scenario you are running.
|
||||
|
||||
**For the Model (Quant) Scenario:**
|
||||
|
||||
The execution environment is determined by the ``MODEL_COSTEER_ENV_TYPE`` variable, which is read from ``rdagent/components/coder/model_coder/conf.py``.
|
||||
|
||||
* **To use Docker** (recommended for isolated execution):
|
||||
|
||||
.. code-block:: properties
|
||||
|
||||
MODEL_COSTEER_ENV_TYPE=docker
|
||||
|
||||
* **To use Conda** (for running in a local Conda environment):
|
||||
|
||||
.. code-block:: properties
|
||||
|
||||
MODEL_COSTEER_ENV_TYPE=conda
|
||||
|
||||
**For the Data Science Scenario:**
|
||||
|
||||
The execution environment is determined by the ``DS_CODER_COSTEER_ENV_TYPE`` variable, which is read from ``rdagent/components/coder/data_science/conf.py``.
|
||||
|
||||
* **To use Docker** (recommended for isolated execution):
|
||||
|
||||
.. code-block:: properties
|
||||
|
||||
DS_CODER_COSTEER_ENV_TYPE=docker
|
||||
|
||||
* **To use Conda** (for running in a local Conda environment):
|
||||
|
||||
.. code-block:: properties
|
||||
|
||||
DS_CODER_COSTEER_ENV_TYPE=conda
|
||||
|
||||
|
||||
Custom Time Segment Configuration (Train / Valid / Test)
|
||||
=========================================================
|
||||
|
||||
RD-Agent now supports user-defined time segments for training, validation,
|
||||
and testing (backtesting). Users can customize these segments via environment
|
||||
variables in the ``.env`` file, depending on the scenario being executed.
|
||||
|
||||
This feature allows greater flexibility when running experiments on different
|
||||
time ranges without modifying code or YAML configurations.
|
||||
|
||||
Fin-Factor Scenario
|
||||
-------------------
|
||||
|
||||
When running the **fin_factor** scenario, you can configure the time segments
|
||||
using the following environment variables. These variables are read by the
|
||||
Factor-related PropSettings and directly affect the execution process.
|
||||
|
||||
Add the following entries to your ``.env`` file as needed:
|
||||
|
||||
.. code-block:: properties
|
||||
|
||||
QLIB_FACTOR_TRAIN_START=<train start date, default is 2008-01-01>
|
||||
QLIB_FACTOR_TRAIN_END=<train end date, default is 2014-12-31>
|
||||
QLIB_FACTOR_VALID_START=<valid start date, default is 2015-01-01>
|
||||
QLIB_FACTOR_VALID_END=<valid end date, default is 2016-12-31>
|
||||
QLIB_FACTOR_TEST_START=<test / backtest start date, default is 2017-01-01>
|
||||
QLIB_FACTOR_TEST_END=<test / backtest end date, default is 2020-12-31>
|
||||
|
||||
Fin-Model Scenario
|
||||
------------------
|
||||
|
||||
When running the **fin_model** scenario, the model training, validation, and
|
||||
testing time segments can be configured independently via the following
|
||||
environment variables:
|
||||
|
||||
.. code-block:: properties
|
||||
|
||||
QLIB_MODEL_TRAIN_START=<train start date, default is 2008-01-01>
|
||||
QLIB_MODEL_TRAIN_END=<train end date, default is 2014-12-31>
|
||||
QLIB_MODEL_VALID_START=<valid start date, default is 2015-01-01>
|
||||
QLIB_MODEL_VALID_END=<valid end date, default is 2016-12-31>
|
||||
QLIB_MODEL_TEST_START=<test / backtest start date, default is 2017-01-01>
|
||||
QLIB_MODEL_TEST_END=<test / backtest end date, default is 2020-12-31>
|
||||
|
||||
These settings are used during model training and evaluation and directly
|
||||
impact the execution workflow.
|
||||
|
||||
Fin-Quant Scenario
|
||||
------------------
|
||||
|
||||
When running the **fin_quant** scenario, RD-Agent supports configuring time
|
||||
segments for factor, model, and quant stages simultaneously.
|
||||
|
||||
**Note:** The ``QLIB_QUANT_*`` variables are only used for front-end UI display
|
||||
purposes and do **not** affect the actual execution process.
|
||||
|
||||
You may configure the following variables in your ``.env`` file:
|
||||
|
||||
.. code-block:: properties
|
||||
|
||||
QLIB_FACTOR_TRAIN_START=<train start date, default is 2008-01-01>
|
||||
QLIB_FACTOR_TRAIN_END=<train end date, default is 2014-12-31>
|
||||
QLIB_FACTOR_VALID_START=<valid start date, default is 2015-01-01>
|
||||
QLIB_FACTOR_VALID_END=<valid end date, default is 2016-12-31>
|
||||
QLIB_FACTOR_TEST_START=<test / backtest start date, default is 2017-01-01>
|
||||
QLIB_FACTOR_TEST_END=<test / backtest end date, default is 2020-12-31>
|
||||
|
||||
QLIB_MODEL_TRAIN_START=<train start date, default is 2008-01-01>
|
||||
QLIB_MODEL_TRAIN_END=<train end date, default is 2014-12-31>
|
||||
QLIB_MODEL_VALID_START=<valid start date, default is 2015-01-01>
|
||||
QLIB_MODEL_VALID_END=<valid end date, default is 2016-12-31>
|
||||
QLIB_MODEL_TEST_START=<test / backtest start date, default is 2017-01-01>
|
||||
QLIB_MODEL_TEST_END=<test / backtest end date, default is 2020-12-31>
|
||||
|
||||
QLIB_QUANT_TRAIN_START=<train start date, default is 2008-01-01>
|
||||
QLIB_QUANT_TRAIN_END=<train end date, default is 2014-12-31>
|
||||
QLIB_QUANT_VALID_START=<valid start date, default is 2015-01-01>
|
||||
QLIB_QUANT_VALID_END=<valid end date, default is 2016-12-31>
|
||||
QLIB_QUANT_TEST_START=<test / backtest start date, default is 2017-01-01>
|
||||
QLIB_QUANT_TEST_END=<test / backtest end date, default is 2020-12-31>
|
||||
|
||||
This setup allows the front-end to display consistent segment information
|
||||
across different stages while keeping execution logic unchanged.
|
||||
|
||||
|
||||
Configuration(deprecated)
|
||||
=========================
|
||||
|
||||
To run the application, please create a `.env` file in the root directory of the project and add environment variables according to your requirements.
|
||||
|
||||
The standard configuration options for the user using the OpenAI API are provided in the `.env.example` file.
|
||||
If you are using this deprecated version, you should set `BACKEND` to `rdagent.oai.backend.DeprecBackend`.
|
||||
|
||||
.. code-block:: Properties
|
||||
|
||||
BACKEND=rdagent.oai.backend.DeprecBackend
|
||||
|
||||
Here are some other configuration options that you can use:
|
||||
|
||||
@@ -38,22 +316,23 @@ Azure OpenAI
|
||||
The following environment variables are standard configuration options for the user using the OpenAI API.
|
||||
|
||||
.. code-block:: Properties
|
||||
|
||||
|
||||
USE_AZURE=True
|
||||
|
||||
OPENAI_API_KEY=<replace_with_your_openai_api_key>
|
||||
|
||||
EMBEDDING_OPENAI_API_KEY=<replace_with_your_azure_openai_api_key>
|
||||
EMBEDDING_AZURE_API_BASE= # The endpoint for the Azure OpenAI API.
|
||||
EMBEDDING_AZURE_API_VERSION= # The version of the Azure OpenAI API.
|
||||
EMBEDDING_MODEL=text-embedding-3-small
|
||||
EMBEDDING_AZURE_API_BASE= # The base URL for the Azure OpenAI API.
|
||||
EMBEDDING_AZURE_API_VERSION = # The version of the Azure OpenAI API.
|
||||
|
||||
CHAT_MODEL=gpt-4-turbo
|
||||
CHAT_AZURE_API_VERSION = # The version of the Azure OpenAI API.
|
||||
CHAT_OPENAI_API_KEY=<replace_with_your_azure_openai_api_key>
|
||||
CHAT_AZURE_API_BASE= # The endpoint for the Azure OpenAI API.
|
||||
CHAT_AZURE_API_VERSION= # The version of the Azure OpenAI API.
|
||||
CHAT_MODEL= # The model name of the Azure OpenAI API.
|
||||
|
||||
Use Azure Token Provider
|
||||
------------------------
|
||||
|
||||
If you are using the Azure token provider, you need to set the `USE_AZURE_TOKEN_PROVIDER` environment variable to `True`. then
|
||||
If you are using the Azure token provider, you need to set the `CHAT_USE_AZURE_TOKEN_PROVIDER` and `EMBEDDING_USE_AZURE_TOKEN_PROVIDER` environment variable to `True`. then
|
||||
use the environment variables provided in the `Azure Configuration section <installation_and_configuration.html#azure-openai>`_.
|
||||
|
||||
|
||||
@@ -80,31 +359,33 @@ Configuration List
|
||||
|
||||
- OpenAI API Setting
|
||||
|
||||
+-----------------------------+--------------------------------------------------+-------------------------+
|
||||
| Configuration Option | Meaning | Default Value |
|
||||
+=============================+==================================================+=========================+
|
||||
| OPENAI_API_KEY | API key for both chat and embedding models | None |
|
||||
+-----------------------------+--------------------------------------------------+-------------------------+
|
||||
| EMBEDDING_OPENAI_API_KEY | Use a different API key for embedding model | None |
|
||||
+-----------------------------+--------------------------------------------------+-------------------------+
|
||||
| CHAT_OPENAI_API_KEY | Set to use a different API key for chat model | None |
|
||||
+-----------------------------+--------------------------------------------------+-------------------------+
|
||||
| EMBEDDING_MODEL | Name of the embedding model | text-embedding-3-small |
|
||||
+-----------------------------+--------------------------------------------------+-------------------------+
|
||||
| CHAT_MODEL | Name of the chat model | gpt-4-turbo |
|
||||
+-----------------------------+--------------------------------------------------+-------------------------+
|
||||
| EMBEDDING_AZURE_API_BASE | Base URL for the Azure OpenAI API | None |
|
||||
+-----------------------------+--------------------------------------------------+-------------------------+
|
||||
| EMBEDDING_AZURE_API_VERSION | Version of the Azure OpenAI API | None |
|
||||
+-----------------------------+--------------------------------------------------+-------------------------+
|
||||
| CHAT_AZURE_API_BASE | Base URL for the Azure OpenAI API | None |
|
||||
+-----------------------------+--------------------------------------------------+-------------------------+
|
||||
| CHAT_AZURE_API_VERSION | Version of the Azure OpenAI API | None |
|
||||
+-----------------------------+--------------------------------------------------+-------------------------+
|
||||
| USE_AZURE | True if you are using Azure OpenAI | False |
|
||||
+-----------------------------+--------------------------------------------------+-------------------------+
|
||||
| USE_AZURE_TOKEN_PROVIDER | True if you are using a Azure Token Provider | False |
|
||||
+-----------------------------+--------------------------------------------------+-------------------------+
|
||||
+-----------------------------------+-----------------------------------------------------------------+-------------------------+
|
||||
| Configuration Option | Meaning | Default Value |
|
||||
+===================================+=================================================================+=========================+
|
||||
| OPENAI_API_KEY | API key for both chat and embedding models | None |
|
||||
+-----------------------------------+-----------------------------------------------------------------+-------------------------+
|
||||
| EMBEDDING_OPENAI_API_KEY | Use a different API key for embedding model | None |
|
||||
+-----------------------------------+-----------------------------------------------------------------+-------------------------+
|
||||
| CHAT_OPENAI_API_KEY | Set to use a different API key for chat model | None |
|
||||
+-----------------------------------+-----------------------------------------------------------------+-------------------------+
|
||||
| EMBEDDING_MODEL | Name of the embedding model | text-embedding-3-small |
|
||||
+-----------------------------------+-----------------------------------------------------------------+-------------------------+
|
||||
| CHAT_MODEL | Name of the chat model | gpt-4-turbo |
|
||||
+-----------------------------------+-----------------------------------------------------------------+-------------------------+
|
||||
| EMBEDDING_AZURE_API_BASE | Base URL for the Azure OpenAI API | None |
|
||||
+-----------------------------------+-----------------------------------------------------------------+-------------------------+
|
||||
| EMBEDDING_AZURE_API_VERSION | Version of the Azure OpenAI API | None |
|
||||
+-----------------------------------+-----------------------------------------------------------------+-------------------------+
|
||||
| CHAT_AZURE_API_BASE | Base URL for the Azure OpenAI API | None |
|
||||
+-----------------------------------+-----------------------------------------------------------------+-------------------------+
|
||||
| CHAT_AZURE_API_VERSION | Version of the Azure OpenAI API | None |
|
||||
+-----------------------------------+-----------------------------------------------------------------+-------------------------+
|
||||
| USE_AZURE | True if you are using Azure OpenAI | False |
|
||||
+-----------------------------------+-----------------------------------------------------------------+-------------------------+
|
||||
| CHAT_USE_AZURE_TOKEN_PROVIDER | True if you are using an Azure Token Provider in chat model | False |
|
||||
+-----------------------------------+-----------------------------------------------------------------+-------------------------+
|
||||
| EMBEDDING_USE_AZURE_TOKEN_PROVIDER| True if you are using an Azure Token Provider in embedding model| False |
|
||||
+-----------------------------------+-----------------------------------------------------------------+-------------------------+
|
||||
|
||||
- Globol Setting
|
||||
|
||||
@@ -138,8 +419,6 @@ Configuration List
|
||||
+------------------------------+--------------------------------------------------+-------------------------+
|
||||
| prompt_cache_path | Path to prompt cache | ./prompt_cache.db |
|
||||
+------------------------------+--------------------------------------------------+-------------------------+
|
||||
| session_cache_folder_location| Path to session cache | ./session_cache_folder |
|
||||
+------------------------------+--------------------------------------------------+-------------------------+
|
||||
| max_past_message_include | Maximum number of past messages to include | 10 |
|
||||
+------------------------------+--------------------------------------------------+-------------------------+
|
||||
|
||||
|
||||
+21
-30
@@ -5,21 +5,12 @@ Benchmark
|
||||
Introduction
|
||||
=============
|
||||
|
||||
|
||||
Benchmarking the capabilities of the R&D is a very important research problem of the research area.
|
||||
|
||||
Currently we are continuously exploring how to benchmark them.
|
||||
|
||||
The current benchmarks are listed in this page
|
||||
|
||||
Benchmarking the capabilities of R&D is a crucial research problem in this area. We are continuously exploring methods to benchmark these capabilities. The current benchmarks are listed on this page.
|
||||
|
||||
Development Capability Benchmarking
|
||||
===================================
|
||||
|
||||
|
||||
Benchmark is used to evaluate the effectiveness of factors with fixed data.
|
||||
|
||||
It mainly includes the following steps:
|
||||
Benchmarking is used to evaluate the effectiveness of factors with fixed data. It mainly includes the following steps:
|
||||
|
||||
1. :ref:`read and prepare the eval_data <data>`
|
||||
|
||||
@@ -27,34 +18,31 @@ It mainly includes the following steps:
|
||||
|
||||
3. :ref:`declare the eval method and pass the arguments <config>`
|
||||
|
||||
4. :ref:`run the eval <run>`
|
||||
4. :ref:`run the eval <run>`
|
||||
|
||||
5. :ref:`save and show the result <show>`
|
||||
5. :ref:`save and show the result <show>`
|
||||
|
||||
Configuration
|
||||
Configuration
|
||||
-------------
|
||||
.. _config:
|
||||
|
||||
.. autopydantic_settings:: rdagent.components.benchmark.conf.BenchmarkSettings
|
||||
|
||||
Example
|
||||
++++++++
|
||||
+++++++
|
||||
.. _example:
|
||||
|
||||
The default value for ``bench_test_round`` is 10, and it will take about 2 hours to run 10 rounds.
|
||||
To modify it from ``10`` to ``2`` you can adjust this by adding environment variables in the .env file as shown below.
|
||||
The default value for ``bench_test_round`` is 10, which takes about 2 hours to run. To modify it from ``10`` to ``2``, adjust the environment variables in the .env file as shown below.
|
||||
|
||||
.. code-block:: Properties
|
||||
|
||||
BENCHMARK_BENCH_TEST_ROUND=1
|
||||
BENCHMARK_BENCH_TEST_ROUND=2
|
||||
|
||||
Data Format
|
||||
-------------
|
||||
.. _data:
|
||||
|
||||
The sample data in ``bench_data_path`` is a dictionary where each key represents a factor name.
|
||||
|
||||
The value associated with each key is factor data containing the following information:
|
||||
The sample data in ``bench_data_path`` is a dictionary where each key represents a factor name. The value associated with each key is factor data containing the following information:
|
||||
|
||||
- **description**: A textual description of the factor.
|
||||
- **formulation**: A LaTeX formula representing the model's formulation.
|
||||
@@ -63,22 +51,24 @@ The value associated with each key is factor data containing the following infor
|
||||
- **Difficulty**: The difficulty level of implementing or understanding the factor.
|
||||
- **gt_code**: A piece of code associated with the factor.
|
||||
|
||||
Here is the example of this data format:
|
||||
Here is an example of this data format:
|
||||
|
||||
.. literalinclude:: ../../rdagent/components/benchmark/example.json
|
||||
:language: json
|
||||
|
||||
Ensure the data is placed in the ``FACTOR_COSTEER_SETTINGS.data_folder_debug``. The data files should be in ``.h5`` or ``.md`` format and must not be stored in any subfolders. LLM-Agents will review the file content and implement the tasks.
|
||||
|
||||
.. TODO: Add a script to automatically generate the data in the `rdagent/app/quant_factor_benchmark/data` folder.
|
||||
|
||||
Run Benchmark
|
||||
-------------
|
||||
.. _run:
|
||||
|
||||
Start benchmark after finishing the :doc:`../installation_and_configuration`.
|
||||
Start the benchmark after completing the :doc:`../installation_and_configuration`.
|
||||
|
||||
.. code-block:: Properties
|
||||
|
||||
python rdagent/app/quant_factor_benchmark/eval.py
|
||||
|
||||
|
||||
dotenv run -- python rdagent/app/benchmark/factor/eval.py
|
||||
|
||||
Once completed, a pkl file will be generated, and its path will be printed on the last line of the console.
|
||||
|
||||
@@ -86,18 +76,16 @@ Show Result
|
||||
-------------
|
||||
.. _show:
|
||||
|
||||
The ``analysis.py`` script is used to read data from pkl and convert it to an image.
|
||||
Modify the python code in ``rdagent/app/quant_factor_benchmark/analysis.py`` to specify the path to the pkl file and the output path for the png file.
|
||||
The ``analysis.py`` script reads data from the pkl file and converts it to an image. Modify the Python code in ``rdagent/app/quant_factor_benchmark/analysis.py`` to specify the path to the pkl file and the output path for the png file.
|
||||
|
||||
.. code-block:: Properties
|
||||
|
||||
python rdagent/app/quant_factor_benchmark/analysis.py
|
||||
dotenv run -- python rdagent/app/benchmark/factor/analysis.py <log/path to.pkl>
|
||||
|
||||
A png file will be saved to the designated path as shown below.
|
||||
|
||||
.. image:: ../_static/benchmark.png
|
||||
|
||||
|
||||
Related Paper
|
||||
-------------
|
||||
|
||||
@@ -116,3 +104,6 @@ Related Paper
|
||||
}
|
||||
|
||||
.. image:: https://github.com/user-attachments/assets/494f55d3-de9e-4e73-ba3d-a787e8f9e841
|
||||
|
||||
To replicate the benchmark detailed in the paper, please consult the factors listed in the following file: `RD2bench.json <../_static/RD2bench.json>`_.
|
||||
Please note use ``only_correct_format=False`` when evaluating the results.
|
||||
|
||||
+19
-19
@@ -13,34 +13,34 @@ In the two key areas of data-driven scenarios, model implementation and data bui
|
||||
The supported scenarios are listed below:
|
||||
|
||||
|
||||
|
||||
.. list-table::
|
||||
.. list-table::
|
||||
:header-rows: 1
|
||||
|
||||
* - Scenario/Target
|
||||
- Model Implementation
|
||||
- Data Building
|
||||
* - 💹 Finance
|
||||
- :ref:`🤖Iteratively Proposing Ideas & Evolving <model_agent_fin>`
|
||||
- :ref:`🦾Auto reports reading & implementation <data_copilot_fin>`
|
||||
|
||||
- :ref:`🥇The First Data-Centric Quant Multi-Agent Framework <quant_agent_fin>`
|
||||
- :ref:`🤖Iteratively Proposing Ideas & Evolving <model_agent_fin>`
|
||||
|
||||
:ref:`🦾Auto reports reading & implementation <data_copilot_fin>`
|
||||
|
||||
:ref:`🤖Iteratively Proposing Ideas & Evolving <data_agent_fin>`
|
||||
* - 🩺 Medical
|
||||
- :ref:`🤖Iteratively Proposing Ideas & Evolving <model_agent_med>`
|
||||
-
|
||||
* - 🏭 General
|
||||
- :ref:`🦾Auto paper reading & implementation <model_copilot_general>`
|
||||
-
|
||||
- :ref:`🦾Auto paper reading & implementation <model_copilot_general>`
|
||||
|
||||
- :ref:`🤖 Data Science <data_science_agent>`
|
||||
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
:caption: Doctree:
|
||||
:hidden:
|
||||
|
||||
data_agent_fin
|
||||
data_copilot_fin
|
||||
model_agent_fin
|
||||
model_agent_med
|
||||
model_copilot_general
|
||||
:maxdepth: 1
|
||||
:caption: Doctree:
|
||||
:hidden:
|
||||
|
||||
quant_agent_fin
|
||||
data_agent_fin
|
||||
data_copilot_fin
|
||||
model_agent_fin
|
||||
model_copilot_general
|
||||
data_science
|
||||
finetune
|
||||
|
||||
@@ -131,8 +131,8 @@ The following environment variables can be set in the `.env` file to customize t
|
||||
:settings-show-field-summary: False
|
||||
:exclude-members: Config
|
||||
|
||||
.. autopydantic_settings:: rdagent.components.coder.factor_coder.config.FactorImplementSettings
|
||||
.. autopydantic_settings:: rdagent.components.coder.factor_coder.config.FactorCoSTEERSettings
|
||||
:settings-show-field-summary: False
|
||||
:members: coder_use_cache, data_folder, data_folder_debug, cache_location, enable_execution_cache, file_based_execution_timeout, select_method, select_threshold, max_loop, knowledge_base_path, new_knowledge_base_path
|
||||
:members: coder_use_cache, data_folder, data_folder_debug, file_based_execution_timeout, select_method, max_loop, knowledge_base_path, new_knowledge_base_path
|
||||
:exclude-members: Config, fail_task_trial_limit, v1_query_former_trace_limit, v1_query_similar_success_limit, v2_query_component_limit, v2_query_error_limit, v2_query_former_trace_limit, v2_error_summary, v2_knowledge_sampler
|
||||
:no-index:
|
||||
|
||||
@@ -27,7 +27,7 @@ And this is where the **Finance Data Copilot** steps in.
|
||||
|
||||
<div style="display: flex; justify-content: center; align-items: center;">
|
||||
<video width="600" controls>
|
||||
<source src="https://rdagent.azurewebsites.net/media/65bb598f1372c1857ccbf09b2acf5d55830911625048c03102291098.mp4" type="video/mp4">
|
||||
<source src="https://rdagent.azurewebsites.net/media/7b14b2bd3d8771da9cf7eb799b6d96729cec3d35c8d4f68060f3e2fd.mp4" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
@@ -125,7 +125,7 @@ You can try our demo by running the following command:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
rdagent fin_factor_report --report_folder=git_ignore_folder/reports
|
||||
rdagent fin_factor_report --report-folder=git_ignore_folder/reports
|
||||
|
||||
- Alternatively, you can store the paths of the reports in `report_result_json_file_path`. The format should be:
|
||||
|
||||
@@ -157,8 +157,8 @@ The following environment variables can be set in the `.env` file to customize t
|
||||
:show-inheritance:
|
||||
:exclude-members: Config
|
||||
|
||||
.. autopydantic_settings:: rdagent.components.coder.factor_coder.config.FactorImplementSettings
|
||||
.. autopydantic_settings:: rdagent.components.coder.factor_coder.config.FactorCoSTEERSettings
|
||||
:settings-show-field-summary: False
|
||||
:members: coder_use_cache, data_folder, data_folder_debug, cache_location, enable_execution_cache, file_based_execution_timeout, select_method, select_threshold, max_loop, knowledge_base_path, new_knowledge_base_path
|
||||
:members: coder_use_cache, data_folder, data_folder_debug, file_based_execution_timeout, select_method, max_loop, knowledge_base_path, new_knowledge_base_path
|
||||
:exclude-members: Config, python_bin, fail_task_trial_limit, v1_query_former_trace_limit, v1_query_similar_success_limit, v2_query_component_limit, v2_query_error_limit, v2_query_former_trace_limit, v2_error_summary, v2_knowledge_sampler
|
||||
:no-index:
|
||||
|
||||
@@ -0,0 +1,566 @@
|
||||
.. _data_science_agent:
|
||||
|
||||
=======================
|
||||
Data Science Agent
|
||||
=======================
|
||||
|
||||
**🤖 Automated Feature Engineering & Model Tuning Evolution**
|
||||
------------------------------------------------------------------------------------------
|
||||
The Data Science Agent is an agent that can automatically perform feature engineering and model tuning. It can be used to solve various data science problems, such as image classification, time series forecasting, and text classification.
|
||||
|
||||
🌟 Introduction
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In this scenario, our automated system proposes hypothesis, choose action, implements code, conducts validation, and utilizes feedback in a continuous, iterative process.
|
||||
|
||||
The goal is to automatically optimize performance metrics within the validation set or Kaggle Leaderboard, ultimately discovering the most efficient features and models through autonomous research and development.
|
||||
|
||||
Here's an enhanced outline of the steps:
|
||||
|
||||
**Step 1 : Hypothesis Generation 🔍**
|
||||
|
||||
- Generate and propose initial hypotheses based on previous experiment analysis and domain expertise, with thorough reasoning and financial justification.
|
||||
|
||||
**Step 2 : Experiment Creation ✨**
|
||||
|
||||
- Transform the hypothesis into a task.
|
||||
- Choose a specific action within feature engineering or model tuning.
|
||||
- Develop, define, and implement a new feature or model, including its name, description, and formulation.
|
||||
|
||||
**Step 3 : Model/Feature Implementation 👨💻**
|
||||
|
||||
- Implement the model code based on the detailed description.
|
||||
- Evolve the model iteratively as a developer would, ensuring accuracy and efficiency.
|
||||
|
||||
**Step 4 : Validation on Test Set or Kaggle 📉**
|
||||
|
||||
- Validate the newly developed model using the test set or Kaggle dataset.
|
||||
- Assess the model's effectiveness and performance based on the validation results.
|
||||
|
||||
**Step 5: Feedback Analysis 🔍**
|
||||
|
||||
- Analyze validation results to assess performance.
|
||||
- Use insights to refine hypotheses and enhance the model.
|
||||
|
||||
**Step 6: Hypothesis Refinement ♻️**
|
||||
|
||||
- Adjust hypotheses based on validation feedback.
|
||||
- Iterate the process to continuously improve the model.
|
||||
|
||||
📖 Data Science Background
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
In the evolving landscape of artificial intelligence, **Data Science** represents a powerful paradigm where machines engage in autonomous exploration, hypothesis testing, and model development across diverse domains — from healthcare and finance to logistics and research.
|
||||
|
||||
The **Data Science** Agent stands as a central engine in this transformation, enabling users to automate the entire machine learning workflow: from hypothesis generation to code implementation, validation, and refinement — all guided by performance feedback.
|
||||
|
||||
By leveraging the **Data Science** Agent, researchers and developers can accelerate experimentation cycles. Whether fine-tuning custom models or competing in high-stakes benchmarks like Kaggle, the Data Science Agent unlocks new frontiers in intelligent, self-directed discovery.
|
||||
|
||||
🧭 Example Guide - Customized dataset
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
🔧 **Set up RD-Agent Environment**
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- Before you start, please make sure you have installed RD-Agent and configured the environment for RD-Agent correctly. If you want to know how to install and configure the RD-Agent, please refer to the `documentation <../installation_and_configuration.html>`_.
|
||||
|
||||
- 🔩 **Setting the Environment variables at .env file**
|
||||
|
||||
- Determine the path where the data will be stored and add it to the ``.env`` file.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
dotenv set DS_LOCAL_DATA_PATH <your local directory>/ds_data
|
||||
dotenv set DS_SCEN rdagent.scenarios.data_science.scen.DataScienceScen
|
||||
|
||||
📥 **Prepare Customized datasets**
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- A data science competition dataset usually consists of two parts: ``competition dataset`` and ``evaluation dataset``. (We provide `a sample <https://github.com/microsoft/RD-Agent/tree/main/rdagent/scenarios/data_science/example>`_ of a customized dataset named: `arf-12-hours-prediction-task as a reference`.)
|
||||
|
||||
- The ``competition dataset`` contains **training data**, **test data**, **description files**, **formatted submission files**, **data sampling codes**.
|
||||
|
||||
- The ``evaluation dataset`` contains **standard answer file**, **data checking codes**, and **Code for calculation of scores**.
|
||||
|
||||
- We use the ``arf-12-hours-prediction-task`` data as a sample to introduce the preparation workflow for the competition dataset.
|
||||
|
||||
- Create a ``ds_data/source_data/arf-12-hours-prediction-task`` folder, which will be used to store your raw dataset.
|
||||
|
||||
- The raw files for the competition ``arf-12-hours-prediction-task`` have two files: ``ARF_12h.csv`` and ``X.npz``.
|
||||
|
||||
- Create a ``ds_data/source_data/arf-12-hours-prediction-task/prepare.py`` file that splits your raw data into **training data**, **test data**, **formatted submission file**, and **standard answer file**. (You will need to write a script based on your raw data.)
|
||||
|
||||
- The following shows the preprocessing code for the raw data of ``arf-12-hours-prediction-task``.
|
||||
|
||||
.. literalinclude:: ../../rdagent/scenarios/data_science/example/source_data/arf-12-hours-prediction-task/prepare.py
|
||||
:language: python
|
||||
:caption: ds_data/source_data/arf-12-hours-prediction-task/prepare.py
|
||||
:linenos:
|
||||
|
||||
- At the end of program execution, the ``ds_data`` folder structure will look like this:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
ds_data
|
||||
├── arf-12-hours-prediction-task
|
||||
│ ├── train
|
||||
│ │ ├── ARF_12h.csv
|
||||
│ │ └── X.npz
|
||||
│ ├── test
|
||||
│ │ ├── ARF_12h.csv
|
||||
│ │ └── X.npz
|
||||
│ └── sample_submission.csv
|
||||
├── eval
|
||||
│ └── arf-12-hours-prediction-task
|
||||
│ └── submission_test.csv
|
||||
└── source_data
|
||||
└── arf-12-hours-prediction-task
|
||||
├── ARF_12h.csv
|
||||
├── prepare.py
|
||||
└── X.npz
|
||||
|
||||
- Create a ``ds_data/arf-12-hours-prediction-task/description.md`` file to describe your competition, Objective, dataset, and other information.
|
||||
|
||||
- The following shows the description file for ``arf-12-hours-prediction-task``
|
||||
|
||||
.. literalinclude:: ../../rdagent/scenarios/data_science/example/arf-12-hours-prediction-task/description.md
|
||||
:language: markdown
|
||||
:caption: ds_data/arf-12-hours-prediction-task/description.md
|
||||
:linenos:
|
||||
|
||||
- Create a ``ds_data/arf-12-hours-prediction-task/sample.py`` file to construct the debugging sample data.
|
||||
|
||||
- The following shows the script for constructing the debugging sample data based on the ``arf-12-hours-prediction-task`` dataset implementation.
|
||||
|
||||
.. literalinclude:: ../../rdagent/scenarios/data_science/example/arf-12-hours-prediction-task/sample.py
|
||||
:language: markdown
|
||||
:caption: ds_data/arf-12-hours-prediction-task/sample.py
|
||||
:linenos:
|
||||
|
||||
- Create a ``ds_data/eval/arf-12-hours-prediction-task/valid.py`` file, which is used to check the validity of the submission files to ensure that their formatting is consistent with the reference file.
|
||||
|
||||
- The following shows a script that checks the validity of a submission based on the ``arf-12-hours-prediction-task`` data.
|
||||
|
||||
.. literalinclude:: ../../rdagent/scenarios/data_science/example/eval/arf-12-hours-prediction-task/valid.py
|
||||
:language: markdown
|
||||
:caption: ds_data/eval/arf-12-hours-prediction-task/valid.py
|
||||
:linenos:
|
||||
|
||||
- Create a ``ds_data/eval/arf-12-hours-prediction-task/grade.py`` file, which is used to calculate the score based on the submission file and the **standard answer file**, and output the result in JSON format.
|
||||
|
||||
- The following shows a grading script based on the ``arf-12-hours-prediction-task`` data implementation.
|
||||
|
||||
.. literalinclude:: ../../rdagent/scenarios/data_science/example/eval/arf-12-hours-prediction-task/grade.py
|
||||
:language: markdown
|
||||
:caption: ds_data/eval/arf-12-hours-prediction-task/grade.py
|
||||
:linenos:
|
||||
|
||||
- At this point, you have created a complete dataset. The correct structure of the dataset should look like this.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
ds_data
|
||||
├── arf-12-hours-prediction-task
|
||||
│ ├── train
|
||||
│ │ ├── ARF_12h.csv
|
||||
│ │ └── X.npz
|
||||
│ ├── test
|
||||
│ │ ├── ARF_12h.csv
|
||||
│ │ └── X.npz
|
||||
│ ├── description.md
|
||||
│ ├── sample_submission.csv
|
||||
│ └── sample.py
|
||||
├── eval
|
||||
│ └── arf-12-hours-prediction-task
|
||||
│ ├── grade.py
|
||||
│ ├── submission_test.csv
|
||||
│ └── valid.py
|
||||
└── source_data
|
||||
└── arf-12-hours-prediction-task
|
||||
├── ARF_12h.csv
|
||||
├── prepare.py
|
||||
└── X.npz
|
||||
|
||||
- The above shows the complete dataset creation workflow, some of the files are not required, in practice you can customize the dataset according to your own needs.
|
||||
|
||||
- If we don't need the test set scores, then we can choose not to generate **formatted submission files** and **standard answer file** in the prepare code, and we don't need to write **data checking codes** and **Code for calculation of scores**.
|
||||
|
||||
- **Data sampling code** can also be created according to the actual need, if you do not provide **data sampling code**, RD-Agent will be handed over to the LLM sampling at runtime.
|
||||
|
||||
- In the default sampling method (``create_debug_data``), the default sampling ratio (parameter: ``min_frac``) is 1%, if 1% of the data is less than 5, then 5 data will be sampled (parameter: ``min_num``), you can adjust the sampling ratio by adjusting these two parameters.
|
||||
|
||||
- If you have customized data sampling code, you need to set ``DS_SAMPLE_DATA_BY_LLM`` to ``False`` (default is True) in the ``.env`` file before running, so that the program will use the customized sampling code when running, and you can just execute this line of code in the command line:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
dotenv set DS_SAMPLE_DATA_BY_LLM False
|
||||
|
||||
- In addition, we provide a data sampling method in `rdagent.scenarios.data_science.debug.data.create_debug_data <https://github.com/microsoft/RD-Agent/blob/main/rdagent/scenarios/data_science/debug/data.py#L605>`_, in this method, the default sampling ratio (parameter: ``min_frac``) is 1%, if 1% of the data is less than 5, then 5 data will be sampled (parameter: ``min_num``), you can use this method by the following two ways.
|
||||
|
||||
- You can set ``DS_SAMPLE_DATA_BY_LLM`` to ``False`` in the ``.env`` file so that when the program runs, it will use the sampling code provided by RD-Agent.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
dotenv set DS_SAMPLE_DATA_BY_LLM False
|
||||
|
||||
- If you think that the parameters in the receipt sampling method provided by RD-Agent are not suitable, you can customize the parameters in the following command and run it, and set ``DS_SAMPLE_DATA_BY_LLM`` to ``False`` in the ``.env`` so that the program will use the sampling data you provided when running.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
python rdagent/app/data_science/debug.py --dataset_path <dataset path> --competition <competiton_name> --min_frac <sampling ratio> --min_num <minimum number of sampling>
|
||||
dotenv set DS_SAMPLE_DATA_BY_LLM False
|
||||
|
||||
- If you don't need the scores from the test set and leave the data sampling to the LLM, or if you use the sampling method provided by the RD-Agent, you only need to prepare a minimal dataset. The structure of the simplest dataset should be as shown below.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
ds_data
|
||||
├── arf-12-hours-prediction-task
|
||||
│ ├── train
|
||||
│ │ ├── ARF_12h.csv
|
||||
│ │ └── X.npz
|
||||
│ ├── test
|
||||
│ │ ├── ARF_12h.csv
|
||||
│ │ └── X.npz
|
||||
│ └── description.md
|
||||
└── source_data
|
||||
└── arf-12-hours-prediction-task
|
||||
├── ARF_12h.csv
|
||||
├── prepare.py
|
||||
└── X.npz
|
||||
|
||||
- We have prepared a dataset based on the above description for your reference. You can download it with the following command.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
wget https://github.com/SunsetWolf/rdagent_resource/releases/download/ds_data/arf-12-hours-prediction-task.zip
|
||||
|
||||
⚙️ **Set up Environment for Customized datasets**
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
dotenv set DS_SCEN rdagent.scenarios.data_science.scen.DataScienceScen
|
||||
dotenv set DS_LOCAL_DATA_PATH <your local directory>/ds_data
|
||||
dotenv set DS_CODER_ON_WHOLE_PIPELINE True
|
||||
|
||||
- 📘 More Environment Variables (Optional)
|
||||
|
||||
- If you want to see all the available environment variables, you can refer to the configuration file for Data Science scenarios:
|
||||
|
||||
.. literalinclude:: ../../rdagent/app/data_science/conf.py
|
||||
:language: python
|
||||
:linenos:
|
||||
|
||||
- These variables allow you to have finer-grained control in Data Science scenarios.
|
||||
|
||||
🚀 **Run the Application**
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- 🌏 You can directly run the application by using the following command:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
rdagent data_science --competition <Competition ID>
|
||||
|
||||
- The following shows the command to run based on the ``arf-12-hours-prediction-task`` data
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
rdagent data_science --competition arf-12-hours-prediction-task
|
||||
|
||||
- More CLI Parameters for `rdagent data_science` command:
|
||||
|
||||
.. automodule:: rdagent.app.data_science.loop
|
||||
:members:
|
||||
:no-index:
|
||||
|
||||
- 📈 Visualize the R&D Process
|
||||
|
||||
- We provide a web UI to visualize the log. You just need to run:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
rdagent ui --port <custom port> --log-dir <your log folder like "log/"> --data_science True
|
||||
|
||||
- Then you can input the log path and visualize the R&D process.
|
||||
|
||||
- 🧪 Scoring the test results
|
||||
|
||||
- Finally, shutdown the program, and get the test set scores with this command.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
dotenv run -- python rdagent/log/mle_summary.py grade <url_to_log>
|
||||
|
||||
Here, <url_to_log> refers to the parent directory of the log folder generated during the run.
|
||||
|
||||
🕹️ Kaggle Agent
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
📖 Background
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
In the landscape of data science competitions, Kaggle serves as the ultimate arena where data enthusiasts harness the power of algorithms to tackle real-world challenges.
|
||||
The Kaggle Agent stands as a pivotal tool, empowering participants to seamlessly integrate cutting-edge models and datasets, transforming raw data into actionable insights.
|
||||
|
||||
By utilizing the **Kaggle Agent**, data scientists can craft innovative solutions that not only uncover hidden patterns but also drive significant advancements in predictive accuracy and model robustness.
|
||||
|
||||
🧭 Example Guide - Kaggle Dataset
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
🛠️ Preparing For The Competition
|
||||
""""""""""""""""""""""""""""""""""
|
||||
|
||||
- 🔨 **Configuring the Kaggle API**
|
||||
|
||||
- Register and login on the `Kaggle <https://www.kaggle.com/>`_ website.
|
||||
- Click on the avatar (usually in the top right corner of the page) -> ``Settings`` -> ``Create New Token``, A file called ``kaggle.json`` will be downloaded.
|
||||
- Move ``kaggle.json`` to ``~/.config/kaggle/``
|
||||
- Modify the permissions of the ``kaggle.json`` file.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
chmod 600 ~/.config/kaggle/kaggle.json
|
||||
|
||||
- For more information about Kaggle API Settings, refer to the `Kaggle API <https://github.com/Kaggle/kaggle-api>`_.
|
||||
|
||||
- 🔩 **Setting the Environment variables at .env file**
|
||||
|
||||
- Determine the path where the data will be stored and add it to the ``.env`` file.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
mkdir -p <your local directory>/ds_data
|
||||
dotenv set KG_LOCAL_DATA_PATH <your local directory>/ds_data
|
||||
|
||||
- 📘 More Environment Variables (Optional)
|
||||
|
||||
- If you want to see all the available environment variables, you can refer to the configuration file for Data Science scenarios:
|
||||
|
||||
.. literalinclude:: ../../rdagent/app/data_science/conf.py
|
||||
:language: python
|
||||
:linenos:
|
||||
|
||||
- These variables allow you to have finer-grained control in Data Science scenarios.
|
||||
|
||||
- 🗳️ **Join the competition**
|
||||
|
||||
- If your Kaggle API account has not joined a competition, you will need to join the competition before running the program.
|
||||
|
||||
- At the bottom of the competition details page, you can find the ``Join the competition`` button, click on it and select ``I Understand and Accept`` to join the competition.
|
||||
|
||||
- In the **Competition List Available** below, you can jump to the competition details page.
|
||||
|
||||
📥 Preparing Competition DataDataset && Set up RD-Agent Environment
|
||||
""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
|
||||
|
||||
- As a subset of data science, kaggle's dataset still follows the data science format. Based on this, the kaggle dataset can be divided into two categories depending on whether or not it is supported by the **MLE-Bench**.
|
||||
|
||||
- What is **MLE-Bench**?
|
||||
|
||||
- **MLE-Bench** is a comprehensive benchmark designed to evaluate the **machine learning engineering** capabilities of AI systems using real-world scenarios. The dataset includes multiple Kaggle competitions. Since Kaggle does not provide reserved test sets for these competitions, the benchmark includes preparation scripts for splitting publicly available training data into new training and test sets, and scoring scripts for each competition to accurately evaluate submission scores.
|
||||
|
||||
- I'm running a competition Is **MLE-Bench** supported?
|
||||
|
||||
- You can see all the competitions supported by **MLE-Bench** `here <https://github.com/openai/mle-bench/tree/main/mlebench/competitions>`_.
|
||||
|
||||
- Prepare datasets for **MLE-Bench** supported competitions.
|
||||
|
||||
- If you agree with the **MLE-Bench** standard, then you don't need to prepare the dataset, you just need to configure your ``.env`` file to automate the download of the dataset.
|
||||
|
||||
- Configure environment variables, add ``DS_IF_USING_MLE_DATA`` to environment variables, and set it to ``True``.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
dotenv set DS_IF_USING_MLE_DATA True
|
||||
|
||||
- Configure environment variables, add ``DS_SAMPLE_DATA_BY_LLM`` to environment variables, and set it to ``True``.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
dotenv set DS_SAMPLE_DATA_BY_LLM True
|
||||
|
||||
- Configure environment variables, add ``DS_SCEN`` to environment variables, and set it to ``rdagent.scenarios.data_science.scen.KaggleScen``.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
dotenv set DS_SCEN rdagent.scenarios.data_science.scen.KaggleScen
|
||||
|
||||
- At this point, you are ready to start running your competition, which will automatically download the data, and the LLM will automatically extract the minimum dataset.
|
||||
|
||||
- After running the program the structure of the ds_data folder should look like this (Using the ``tabular-playground-series-dec-2021`` contest as an example).
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
ds_data
|
||||
├── tabular-playground-series-dec-2021
|
||||
│ ├── description.md
|
||||
│ ├── sample_submission.csv
|
||||
│ ├── test.csv
|
||||
│ └── train.csv
|
||||
└── zip_files
|
||||
└── tabular-playground-series-dec-2021
|
||||
└── tabular-playground-series-dec-2021.zip
|
||||
|
||||
- The ``ds_data/zip_files`` folder contains a zip file of the raw competition data downloaded from kaggle website.
|
||||
|
||||
- At runtime, RD-Agent will automatically build the Docker image specified at `rdagent/scenarios/kaggle/docker/mle_bench_docker/Dockerfile <https://github.com/microsoft/RD-Agent/blob/main/rdagent/scenarios/kaggle/docker/mle_bench_docker/Dockerfile>`_. This image is responsible for downloading the required datasets and grading files for MLE-Bench.
|
||||
|
||||
Note: The first run may take longer than subsequent runs as the Docker image and data are being downloaded and set up for the first time.
|
||||
|
||||
- Prepare datasets for competitions that are not supported by **MLE-Bench**.
|
||||
|
||||
- As a subset of data science, we can follow the format and steps of data science dataset to prepare kaggle dataset. Below we will describe the workflow for preparing a kaggle dataset using the competition ``playground-series-s4e9`` as an example.
|
||||
|
||||
- Create a ``ds_data/source_data/playground-series-s4e9`` folder, which will be used to store your raw dataset.
|
||||
|
||||
- The raw files for the competition ``playground-series-s4e9`` have two files: ``train.csv``, ``test.csv``, ``sample_submission.csv``, and there are two ways to get the raw data:
|
||||
|
||||
- You can find the raw data required for the competition on the `official kaggle website <https://www.kaggle.com/competitions/playground-series-s4e9/data>`_.
|
||||
|
||||
- Or you can use the command line to download the raw data for the competition, the download command is as follows.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
kaggle competitions download -c playground-series-s4e9
|
||||
|
||||
- Create a ``ds_data/source_data/playground-series-s4e9/prepare.py`` file that splits your raw data into **training data**, **test data**, **formatted submission file**, and **standard answer file**. (You will need to write a script based on your raw data.)
|
||||
|
||||
- The following shows the preprocessing code for the raw data of ``playground-series-s4e9``.
|
||||
|
||||
.. literalinclude:: ../../rdagent/scenarios/data_science/example/source_data/playground-series-s4e9/prepare.py
|
||||
:language: python
|
||||
:caption: ds_data/source_data/playground-series-s4e9/prepare.py
|
||||
:linenos:
|
||||
|
||||
- At the end of program execution, the ``ds_data`` folder structure will look like this:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
ds_data
|
||||
├── playground-series-s4e9
|
||||
│ ├── train.csv
|
||||
│ ├── test.csv
|
||||
│ └── sample_submission.csv
|
||||
├── eval
|
||||
│ └── playground-series-s4e9
|
||||
│ └── submission_test.csv
|
||||
└── source_data
|
||||
└── playground-series-s4e9
|
||||
├── prepare.py
|
||||
├── sample_submission.csv
|
||||
├── test.csv
|
||||
└── train.csv
|
||||
|
||||
- Create a ``ds_data/playground-series-s4e9/description.md`` file to describe your competition, dataset description, and other information. We can find the `competition description information <https://www.kaggle.com/competitions/playground-series-s4e9/overview>`_ and the `dataset description information <https://www.kaggle.com/competitions/playground-series-s4e9/data>`_ from the Kaggle website.
|
||||
|
||||
- The following shows the description file for ``playground-series-s4e9``
|
||||
|
||||
.. literalinclude:: ../../rdagent/scenarios/data_science/example/playground-series-s4e9/description.md
|
||||
:language: markdown
|
||||
:caption: ds_data/playground-series-s4e9/description.md
|
||||
:linenos:
|
||||
|
||||
- Create a ``ds_data/eval/playground-series-s4e9/valid.py`` file, which is used to check the validity of the submission files to ensure that their formatting is consistent with the reference file.
|
||||
|
||||
- The following shows a script that checks the validity of a submission based on the ``playground-series-s4e9`` data.
|
||||
|
||||
.. literalinclude:: ../../rdagent/scenarios/data_science/example/eval/playground-series-s4e9/valid.py
|
||||
:language: markdown
|
||||
:caption: ds_data/eval/playground-series-s4e9/valid.py
|
||||
:linenos:
|
||||
|
||||
- Create a ``ds_data/eval/playground-series-s4e9/grade.py`` file, which is used to calculate the score based on the submission file and the **standard answer file**, and output the result in JSON format.
|
||||
|
||||
- The following shows a grading script based on the ``playground-series-s4e9`` data implementation.
|
||||
|
||||
.. literalinclude:: ../../rdagent/scenarios/data_science/example/eval/playground-series-s4e9/grade.py
|
||||
:language: markdown
|
||||
:caption: ds_data/eval/playground-series-s4e9/grade.py
|
||||
:linenos:
|
||||
|
||||
- In this example we don't create a ``ds_data/eval/playground-series-s4e9/sample.py``, we use the sample method provided by RD-Agent by default.
|
||||
|
||||
- At this point, you have created a complete dataset. The correct structure of the dataset should look like this.
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
ds_data
|
||||
├── playground-series-s4e9
|
||||
│ ├── train.csv
|
||||
│ ├── test.csv
|
||||
│ ├── description.md
|
||||
│ └── sample_submission.csv
|
||||
├── eval
|
||||
│ └── playground-series-s4e9
|
||||
│ ├── grade.py
|
||||
│ ├── submission_test.csv
|
||||
│ └── valid.py
|
||||
└── source_data
|
||||
└── playground-series-s4e9
|
||||
├── prepare.py
|
||||
├── sample_submission.csv
|
||||
├── test.csv
|
||||
└── train.csv
|
||||
|
||||
- We have prepared a dataset based on the above description for your reference. You can download it with the following command.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
wget https://github.com/SunsetWolf/rdagent_resource/releases/download/ds_data/playground-series-s4e9.zip
|
||||
|
||||
- Next, we need to configure the environment for the ``playground-series-s4e9`` contest. You can do this by executing the following command at the command line.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
dotenv set DS_IF_USING_MLE_DATA False
|
||||
dotenv set DS_SAMPLE_DATA_BY_LLM False
|
||||
dotenv set DS_SCEN rdagent.scenarios.data_science.scen.KaggleScen
|
||||
|
||||
🚀 **Run the Application**
|
||||
""""""""""""""""""""""""""""""""""""
|
||||
|
||||
- 🌏 You can directly run the application by using the following command:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
rdagent data_science --competition <Competition ID>
|
||||
|
||||
- The following shows the command to run based on the ``playground-series-s4e9`` data
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
rdagent data_science --competition playground-series-s4e9
|
||||
|
||||
- More CLI Parameters for `rdagent data_science` command:
|
||||
|
||||
.. automodule:: rdagent.app.data_science.loop
|
||||
:members:
|
||||
:no-index:
|
||||
|
||||
- 📈 Visualize the R&D Process
|
||||
|
||||
- We provide a web UI to visualize the log. You just need to run:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
rdagent ui --port <custom port> --log-dir <your log folder like "log/"> --data_science True
|
||||
|
||||
- Then you can input the log path and visualize the R&D process.
|
||||
|
||||
- 🧪 Scoring the test results
|
||||
|
||||
- Finally, shutdown the program, and get the test set scores with this command.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
dotenv run -- python rdagent/log/mle_summary.py grade <url_to_log>
|
||||
|
||||
- If you have configured the full output in ``ds_data/eval/playground-series-s4e9/grade.py``, or if you are running a competition that receives **MLE-Bench** support, you can also summarize the scores by running the following command.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
rdagent grade_summary --log-folder=<url_to_log>
|
||||
|
||||
Here, <url_to_log> refers to the parent directory of the log folder generated during the run.
|
||||
@@ -0,0 +1,163 @@
|
||||
.. _finetune_agent:
|
||||
|
||||
=============================
|
||||
Fine-tuning an Existing Model
|
||||
=============================
|
||||
|
||||
## **🎯 Scenario: Continue Training on a Pre-trained Model**
|
||||
|
||||
In this workflow the **Data Science Agent** starts from a *previously trained* model (and its training script), performs additional fine-tuning on new data, and then re-uses the updated weights for subsequent inference runs.
|
||||
|
||||
🚧 Directory Structure
|
||||
|
||||
Your competition folder (here called ``custom_data``) must contain **one extra sub-directory** named ``prev_model`` where you keep the old weights and the code that produced them:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
ds_data
|
||||
└── custom_data
|
||||
├── train.csv
|
||||
├── test.csv
|
||||
├── sample_submission.csv # optional
|
||||
├── description.md # optional
|
||||
├── sample.py # optional
|
||||
└── prev_model # ← NEW
|
||||
├── models/ # previous checkpoints (e.g. *.bin, *.pt, *.ckpt)
|
||||
└── main.py # training/inference scripts you used before
|
||||
|
||||
If your competition provides custom grading/validation scripts, keep them under ``ds_data/eval/custom_data`` exactly as before.
|
||||
|
||||
🔧 Environment Setup
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Add or update the following variables in **.env** (examples shown):
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
# required for all Data-Science runs
|
||||
dotenv set DS_LOCAL_DATA_PATH <your local path>/ds_data
|
||||
|
||||
# optional: choose docker / conda, etc.
|
||||
dotenv set DS_CODER_COSTEER_ENV_TYPE docker
|
||||
|
||||
🚀 How It Works at Runtime
|
||||
|
||||
1. **First run**
|
||||
|
||||
* `rdagent` detects `prev_model/models`.
|
||||
* It loads the latest checkpoint and prepare the fine-tuning based on code found under `prev_model/*.py` (or your own pipeline if you override it).
|
||||
* Fine-tuned weights are written to `./workspace_input/models`.
|
||||
|
||||
2. **Subsequent runs**
|
||||
|
||||
* When you execute `python ./workspace_input/main.py`, the script first looks for a checkpoint in `./workspace_input/models`.
|
||||
* If found, it **skips fine-tuning** and goes straight to prediction / submission generation.
|
||||
|
||||
⏰ Managing Timeouts
|
||||
|
||||
|
||||
By default:
|
||||
|
||||
* **Debug loop**: 1 hour (``DS_DEBUG_TIMEOUT=3600`` seconds)
|
||||
* **Full run** : 3 hours (``DS_FULL_TIMEOUT=10800`` seconds)
|
||||
|
||||
Override either value in **.env**:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
# give the debug loop 45 min and the full loop 6 h
|
||||
dotenv set DS_DEBUG_TIMEOUT 2700
|
||||
dotenv set DS_FULL_TIMEOUT 21600
|
||||
|
||||
- 🚀 **Run the Application**
|
||||
|
||||
- You can directly run the application by using the following command:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
dotenv run -- python rdagent/app/finetune/data_science/loop.py --competition <Competition ID>
|
||||
|
||||
- Then, you can run the test set score corresponding to each round of the loop.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
dotenv run -- python rdagent/log/mle_summary.py grade <url_to_log>
|
||||
|
||||
Here, <url_to_log> refers to the parent directory of the log folder generated during the run.
|
||||
|
||||
- 📥 **Visualize the R&D Process**
|
||||
|
||||
- We provide a web UI to visualize the log. You just need to run:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
streamlit run rdagent/log/ui/dsapp.py
|
||||
|
||||
- Then you can input the log path and visualize the R&D process.
|
||||
|
||||
🔍 MLE-bench Guide: Running ML Engineering via MLE-bench
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
- 📝 **MLE-bench Overview**
|
||||
|
||||
- MLE-bench is a comprehensive benchmark designed to evaluate the ML engineering capabilities of AI systems using real-world scenarios. The dataset comprises 75 Kaggle competitions. Since Kaggle does not provide held-out test sets for these competitions, the benchmark includes preparation scripts that split the publicly available training data into new training and test sets, and grading scripts are provided for each competition to accurately evaluate submission scores.
|
||||
|
||||
- 🔧 **Set up Environment for MLE-bench**
|
||||
|
||||
- Running R&D-Agent on MLE-bench is designed for full automation. There is no need for manual downloads and data preparation. Simply set the environment variable ``DS_IF_USING_MLE_DATA`` to True.
|
||||
|
||||
- At runtime, R&D-Agent will automatically build the Docker image specified at ``rdagent/scenarios/kaggle/docker/mle_bench_docker/Dockerfile``. This image is responsible for downloading the required datasets and grading files for MLE-bench.
|
||||
|
||||
- Note: The first run may take longer than subsequent runs as the Docker image and data are being downloaded and set up for the first time.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
dotenv set DS_LOCAL_DATA_PATH <your local directory>/ds_data
|
||||
dotenv set DS_IF_USING_MLE_DATA True
|
||||
|
||||
- 🔨 **Configuring the Kaggle API**
|
||||
|
||||
- Downloading Kaggle competition data requires the Kaggle API. You can set up the Kaggle API by following these steps:
|
||||
|
||||
- Register and login on the `Kaggle <https://www.kaggle.com/>`_ website.
|
||||
|
||||
- Click on the avatar (usually in the top right corner of the page) -> ``Settings`` -> ``Create New Token``, A file called ``kaggle.json`` will be downloaded.
|
||||
|
||||
- Move ``kaggle.json`` to ``~/.config/kaggle/``
|
||||
|
||||
- Modify the permissions of the ``kaggle.json`` file.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
chmod 600 ~/.config/kaggle/kaggle.json
|
||||
|
||||
- For more information about Kaggle API Settings, refer to the `Kaggle API <https://github.com/Kaggle/kaggle-api>`_.
|
||||
|
||||
|
||||
- 🔩 **Setting the Environment Variables for MLE-bench**
|
||||
|
||||
- In addition to auto-downloading the benchmark data, you must also configure the runtime environment for executing the competition code.
|
||||
- Use the environment variable ``DS_CODER_COSTEER_ENV_TYPE`` to select the execution mode:
|
||||
|
||||
• When set to docker (the default), RD-Agent utilizes the official Kaggle Docker image (``gcr.io/kaggle-gpu-images/python:latest``) to ensure that all required packages are available.
|
||||
• If you prefer to use a custom Docker setup, you can modify the configuration using ``DS_DOCKER_IMAGE`` or ``DS_DOCKERFILE_FOLDER_PATH``.
|
||||
• Alternatively, if your competition work only demands basic libraries, you may set ``DS_CODER_COSTEER_ENV_TYPE`` to conda. In this mode, you must create a local conda environment named “kaggle” and pre-install the necessary packages. RD-Agent will execute the competition code within this “kaggle” conda environment.
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
# Configure the runtime environment: choice between 'docker' (default) or 'conda'
|
||||
dotenv set DS_CODER_COSTEER_ENV_TYPE docker
|
||||
|
||||
- **Additional Guidance**
|
||||
|
||||
- **Combine different LLM Models at R&D Stage**
|
||||
|
||||
- You can combine different LLM models at the R&D stage.
|
||||
|
||||
- By default, when you set environment variable ``CHAT_MODEL``, it covers both R&D stages. When customizing the model for the development stage, you can set:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
# This example sets the model to "o3-mini". For some models, the reasoning effort shoule be set to "None".
|
||||
dotenv set LITELLM_CHAT_MODEL_MAP '{"coding":{"model":"o3-mini","reasoning_effort":"high"},"running":{"model":"o3-mini","reasoning_effort":"high"}}'
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 152 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@@ -24,7 +24,7 @@ And this is where the **Finance Model Agent** steps in.
|
||||
|
||||
<div style="display: flex; justify-content: center; align-items: center;">
|
||||
<video width="600" controls>
|
||||
<source src="https://rdagent.azurewebsites.net/media/65bb598f1372c1857ccbf09b2acf5d55830911625048c03102291098.mp4" type="video/mp4">
|
||||
<source src="https://rdagent.azurewebsites.net/media/d85e8cab1da1cd3501d69ce837452f53a971a24911eae7bfa9237137.mp4" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
.. _model_agent_med:
|
||||
|
||||
=======================
|
||||
Medical Model Agent
|
||||
=======================
|
||||
|
||||
**🤖 Automated Medical Predtion Model Evolution**
|
||||
------------------------------------------------------------------------------------------
|
||||
|
||||
📖 Background
|
||||
~~~~~~~~~~~~~~
|
||||
In this scenario, we consider the problem of risk prediction from patients' ICU monitoring data. We use the a public EHR dataset - MIMIC-III and extract a binary classification task for evaluating the framework.
|
||||
In this task, we aim at predicting the whether the patients will suffer from Acute Respiratory Failure (ARF) based their first 12 hours ICU monitoring data.
|
||||
|
||||
🎥 `Demo <https://rdagent.azurewebsites.net/dmm>`_
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<div style="display: flex; justify-content: center; align-items: center;">
|
||||
<video width="600" controls>
|
||||
<source src="https://rdagent.azurewebsites.net/media/65bb598f1372c1857ccbf09b2acf5d55830911625048c03102291098.mp4" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
|
||||
|
||||
🌟 Introduction
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
In this scenario, our automated system proposes hypothesis, constructs model, implements code, receives back-testing, and uses feedbacks.
|
||||
Hypothesis is iterated in this continuous process.
|
||||
The system aims to automatically optimise performance metrics of medical prediction thereby finding the optimised code through autonomous research and development.
|
||||
|
||||
Here's an enhanced outline of the steps:
|
||||
|
||||
**Step 1 : Hypothesis Generation 🔍**
|
||||
|
||||
- Generate and propose initial hypotheses based on previous experiment analysis and domain expertise, with thorough reasoning and justification.
|
||||
|
||||
**Step 2 : Model Creation ✨**
|
||||
|
||||
- Transform the hypothesis into a model.
|
||||
- Develop, define, and implement a machine learning model, including its name, description, and formulation.
|
||||
|
||||
**Step 3 : Model Implementation 👨💻**
|
||||
|
||||
- Implement the model code based on the detailed description.
|
||||
- Evolve the model iteratively as a developer would, ensuring accuracy and efficiency.
|
||||
|
||||
**Step 4 : Backtesting with MIMIC-III 📉**
|
||||
|
||||
- Conduct backtesting using the newly developed model on the extracted task from MIMIC-III.
|
||||
- Evaluate the model's effectiveness and performance in terms of AUROC score.
|
||||
|
||||
**Step 5 : Feedback Analysis 🔍**
|
||||
|
||||
- Analyze backtest results to assess performance.
|
||||
- Incorporate feedback to refine hypotheses and improve the model.
|
||||
|
||||
**Step 6 :Hypothesis Refinement ♻️**
|
||||
|
||||
- Refine hypotheses based on feedback from backtesting.
|
||||
- Repeat the process to continuously improve the model.
|
||||
|
||||
⚡ Quick Start
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Please refer to the installation part in :doc:`../installation_and_configuration` to prepare your system dependency.
|
||||
|
||||
You can try our demo by running the following command:
|
||||
|
||||
- 🐍 Create a Conda Environment
|
||||
|
||||
- Create a new conda environment with Python (3.10 and 3.11 are well tested in our CI):
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
conda create -n rdagent python=3.10
|
||||
|
||||
- Activate the environment:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
conda activate rdagent
|
||||
|
||||
- 📦 Install the RDAgent
|
||||
|
||||
- You can install the RDAgent package from PyPI:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
pip install rdagent
|
||||
|
||||
- 📦 Request PhysioNet Account
|
||||
|
||||
- Apply for an account at `PhysioNet <https://physionet.org/>`_.
|
||||
- Request access to FIDDLE preprocessed data: `FIDDLE Dataset <https://physionet.org/content/mimic-eicu-fiddle-feature/1.0.0/>`_.
|
||||
- Place your username and password in `.rdagent.app.data_mining.conf`.
|
||||
|
||||
|
||||
- 🚀 Run the Application
|
||||
|
||||
- You can directly run the application by using the following command:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
rdagent med_model
|
||||
|
||||
🛠️ Usage of modules
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. _Env Config:
|
||||
|
||||
- **Env Config**
|
||||
|
||||
The following environment variables can be set in the `.env` file to customize the application's behavior:
|
||||
|
||||
.. autopydantic_settings:: rdagent.app.data_mining.conf.PropSetting
|
||||
:settings-show-field-summary: False
|
||||
:exclude-members: Config
|
||||
@@ -22,7 +22,7 @@ And this is where the **General Model Copilot** steps in.
|
||||
|
||||
<div style="display: flex; justify-content: center; align-items: center;">
|
||||
<video width="600" controls>
|
||||
<source src="https://rdagent.azurewebsites.net/media/65bb598f1372c1857ccbf09b2acf5d55830911625048c03102291098.mp4" type="video/mp4">
|
||||
<source src="https://rdagent.azurewebsites.net/media/b35f904765b05099b0fcddbebe041a04f4d7bde239657e5fc24bf0cc.mp4" type="video/mp4">
|
||||
Your browser does not support the video tag.
|
||||
</video>
|
||||
</div>
|
||||
@@ -96,4 +96,4 @@ You can try our demo by running the following command:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
rdagent general_model --report_file_path=<path_to_pdf_file>
|
||||
rdagent general_model --report-file-path=<path_to_pdf_file>
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
.. _quant_agent_fin:
|
||||
|
||||
=====================
|
||||
Finance Quant Agent
|
||||
=====================
|
||||
|
||||
|
||||
**🥇The First Data-Centric Quant Multi-Agent Framework RD-Agent(Q)**
|
||||
---------------------------------------------------------------------
|
||||
|
||||
R&D-Agent for Quantitative Finance, in short **RD-Agent(Q)**, is the first data-centric, multi-agent framework designed to automate the full-stack research and development of quantitative strategies via coordinated factor-model co-optimization.
|
||||
|
||||
You can learn more details about **RD-Agent(Q)** through the `paper <https://arxiv.org/abs/2505.15155>`_.
|
||||
|
||||
⚡ Quick Start
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
Before you start, please make sure you have installed RD-Agent and configured the environment for RD-Agent correctly. If you want to know how to install and configure the RD-Agent, please refer to the `documentation <../installation_and_configuration.html>`_.
|
||||
|
||||
Then, you can run the framework by running the following command:
|
||||
|
||||
- 🐍 Create a Conda Environment
|
||||
|
||||
- Create a new conda environment with Python (3.10 and 3.11 are well tested in our CI):
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
conda create -n rdagent python=3.10
|
||||
|
||||
- Activate the environment:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
conda activate rdagent
|
||||
|
||||
- 📦 Install the RDAgent
|
||||
|
||||
- You can install the RDAgent package from PyPI:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
pip install rdagent
|
||||
|
||||
- 🚀 Run the Application
|
||||
|
||||
- You can directly run the application by using the following command:
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
rdagent fin_quant
|
||||
|
||||
|
||||
🛠️ Usage of modules
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. _Env Config:
|
||||
|
||||
- **Env Config**
|
||||
|
||||
The following environment variables can be set in the `.env` file to customize the application's behavior:
|
||||
|
||||
.. autopydantic_settings:: rdagent.app.qlib_rd_loop.conf.QuantBasePropSetting
|
||||
:settings-show-field-summary: False
|
||||
:exclude-members: Config
|
||||
|
||||
.. autopydantic_settings:: rdagent.components.coder.factor_coder.config.FactorCoSTEERSettings
|
||||
:settings-show-field-summary: False
|
||||
:members: coder_use_cache, data_folder, data_folder_debug, file_based_execution_timeout, select_method, max_loop, knowledge_base_path, new_knowledge_base_path
|
||||
:exclude-members: Config, fail_task_trial_limit, v1_query_former_trace_limit, v1_query_similar_success_limit, v2_query_component_limit, v2_query_error_limit, v2_query_former_trace_limit, v2_error_summary, v2_knowledge_sampler
|
||||
:no-index:
|
||||
|
||||
- **Qlib Configuration**
|
||||
- The `.yaml` files in both the `model_template` and `factor_template` directories contain some configurations for running the corresponding models or factors within the Qlib framework. Below is an overview of their contents and roles:
|
||||
- **General Settings**:
|
||||
- **provider_uri**: Specifies the local Qlib data path, set to `~/.qlib/qlib_data/cn_data`.
|
||||
- **market**: Configured to `csi300`, representing the CSI 300 index constituents.
|
||||
- **benchmark**: Set to `SH000300`, used for backtesting evaluation.
|
||||
|
||||
- **Data Handling**:
|
||||
- **start_time** and **end_time**: Define the full data range, from `2008-01-01` to `2022-08-01`.
|
||||
- **fit_start_time**: The start date for fitting the model, set to `2008-01-01`.
|
||||
- **fit_end_time**: The end date for fitting the model, set to `2014-12-31`.
|
||||
- **features and labels**: Generated via a nested data loader combining `Alpha158DL` (for engineered features such as `RESI5`, `WVMA5`, `RSQR5`, `KLEN`, etc.) and a `StaticDataLoader` that loads precomputed factor files (`combined_factors_df.parquet`).
|
||||
- **normalization**: The pipeline includes `RobustZScoreNorm` (with clipping) and `Fillna` for inference, and `DropnaLabel` with `CSZScoreNorm` for training.
|
||||
|
||||
- **Training Configuration**:
|
||||
- **Model**: Uses `GeneralPTNN`, a PyTorch-based neural network model.
|
||||
- **Dataset Splits**:
|
||||
- **train**: `2008-01-01` to `2014-12-31`
|
||||
- **valid**: `2015-01-01` to `2016-12-31`
|
||||
- **test**: `2017-01-01` to `2020-08-01`
|
||||
|
||||
- **Default Hyperparameters** (can be overridden by command-line arguments):
|
||||
- **n_epochs**: `100`
|
||||
- **lr**: `2e-4`
|
||||
- **early_stop**: `10`
|
||||
- **batch_size**: `256`
|
||||
- **weight_decay**: `0.0`
|
||||
- **metric**: `loss`
|
||||
- **loss**: `mse`
|
||||
- **n_jobs**: `20`
|
||||
- **GPU**: `0` (uses GPU 0 if available)
|
||||
|
||||
- **Backtesting and Evaluation**:
|
||||
- **strategy**: `TopkDropoutStrategy`, which selects the top 50 stocks and randomly drops 5 to introduce exploration.
|
||||
- **backtest period**: `2017-01-01` to `2020-08-01`
|
||||
- **initial capital**: `100,000,000`
|
||||
- **cost configuration**: Includes open/close costs, minimum transaction costs, and slippage control.
|
||||
|
||||
- **Recording and Analysis**:
|
||||
- **SignalRecord**: Logs predicted signals.
|
||||
- **SigAnaRecord**: Performs signal analysis without long-short separation.
|
||||
- **PortAnaRecord**: Conducts portfolio analysis using the configured strategy and backtest settings.
|
||||
+2
-1
@@ -18,7 +18,7 @@ In `RD-Agent/` folder, run:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
rdagent ui --port <port> --log_dir <log_dir like "log/"> [--debug]
|
||||
rdagent ui --port <port> --log-dir <log_dir like "log/"> [--debug]
|
||||
|
||||
This will start a web app on `http://localhost:<port>`.
|
||||
|
||||
@@ -38,6 +38,7 @@ Use Web App
|
||||
- Qlib Factor
|
||||
- Data Mining
|
||||
- Model from Paper
|
||||
- Kaggle
|
||||
|
||||
3. Click the `Config⚙️` button and input the log path (if you set the log_dir parameter, you can select a log_path in the dropdown list).
|
||||
|
||||
|
||||
@@ -0,0 +1,553 @@
|
||||
import io
|
||||
import json
|
||||
from abc import abstractmethod
|
||||
from typing import Dict, Tuple
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from rdagent.components.coder.factor_coder.config import FACTOR_COSTEER_SETTINGS
|
||||
from rdagent.components.coder.factor_coder.factor import FactorTask
|
||||
from rdagent.core.experiment import Task, Workspace
|
||||
from rdagent.oai.llm_conf import LLM_SETTINGS
|
||||
from rdagent.oai.llm_utils import APIBackend
|
||||
from rdagent.utils.agent.tpl import T
|
||||
|
||||
|
||||
class FactorEvaluator:
|
||||
"""Although the init method is same to Evaluator, but we want to emphasize they are different"""
|
||||
|
||||
def __init__(self, scen=None) -> None:
|
||||
self.scen = scen
|
||||
|
||||
@abstractmethod
|
||||
def evaluate(
|
||||
self,
|
||||
target_task: Task,
|
||||
implementation: Workspace,
|
||||
gt_implementation: Workspace,
|
||||
**kwargs,
|
||||
) -> Tuple[str, object]:
|
||||
"""You can get the dataframe by
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
_, gen_df = implementation.execute()
|
||||
_, gt_df = gt_implementation.execute()
|
||||
|
||||
Returns
|
||||
-------
|
||||
Tuple[str, object]
|
||||
- str: the text-based description of the evaluation result
|
||||
- object: a comparable metric (bool, integer, float ...) None for evaluator with only text-based result
|
||||
|
||||
"""
|
||||
raise NotImplementedError("Please implement the `evaluator` method")
|
||||
|
||||
def _get_df(self, gt_implementation: Workspace, implementation: Workspace):
|
||||
if gt_implementation is not None:
|
||||
_, gt_df = gt_implementation.execute()
|
||||
if isinstance(gt_df, pd.Series):
|
||||
gt_df = gt_df.to_frame("gt_factor")
|
||||
if isinstance(gt_df, pd.DataFrame):
|
||||
gt_df = gt_df.sort_index()
|
||||
else:
|
||||
gt_df = None
|
||||
|
||||
_, gen_df = implementation.execute()
|
||||
if isinstance(gen_df, pd.Series):
|
||||
gen_df = gen_df.to_frame("source_factor")
|
||||
if isinstance(gen_df, pd.DataFrame):
|
||||
gen_df = gen_df.sort_index()
|
||||
return gt_df, gen_df
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__class__.__name__
|
||||
|
||||
|
||||
class FactorCodeEvaluator(FactorEvaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
target_task: FactorTask,
|
||||
implementation: Workspace,
|
||||
execution_feedback: str,
|
||||
value_feedback: str = "",
|
||||
gt_implementation: Workspace = None,
|
||||
**kwargs,
|
||||
):
|
||||
factor_information = target_task.get_task_information()
|
||||
code = implementation.all_codes
|
||||
|
||||
system_prompt = T(".prompts:evaluator_code_feedback_v1_system").r(
|
||||
scenario=(
|
||||
self.scen.get_scenario_all_desc(
|
||||
target_task,
|
||||
filtered_tag="feature",
|
||||
simple_background=FACTOR_COSTEER_SETTINGS.simple_background,
|
||||
)
|
||||
if self.scen is not None
|
||||
else "No scenario description."
|
||||
)
|
||||
)
|
||||
|
||||
execution_feedback_to_render = execution_feedback
|
||||
for _ in range(10): # 10 times to split the content is enough
|
||||
user_prompt = T(".prompts:evaluator_code_feedback_v1_user").r(
|
||||
factor_information=factor_information,
|
||||
code=code,
|
||||
execution_feedback=execution_feedback_to_render,
|
||||
value_feedback=value_feedback,
|
||||
gt_code=gt_implementation.code if gt_implementation else None,
|
||||
)
|
||||
if (
|
||||
APIBackend().build_messages_and_calculate_token(
|
||||
user_prompt=user_prompt,
|
||||
system_prompt=system_prompt,
|
||||
)
|
||||
> APIBackend().chat_token_limit
|
||||
):
|
||||
execution_feedback_to_render = execution_feedback_to_render[len(execution_feedback_to_render) // 2 :]
|
||||
else:
|
||||
break
|
||||
critic_response = APIBackend().build_messages_and_create_chat_completion(
|
||||
user_prompt=user_prompt,
|
||||
system_prompt=system_prompt,
|
||||
json_mode=False,
|
||||
)
|
||||
|
||||
return critic_response, None
|
||||
|
||||
|
||||
class FactorInfEvaluator(FactorEvaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
implementation: Workspace,
|
||||
gt_implementation: Workspace,
|
||||
) -> Tuple[str, object]:
|
||||
_, gen_df = self._get_df(gt_implementation, implementation)
|
||||
if gen_df is None:
|
||||
return (
|
||||
"The source dataframe is None. Please check the implementation.",
|
||||
False,
|
||||
)
|
||||
INF_count = gen_df.isin([float("inf"), -float("inf")]).sum().sum()
|
||||
if INF_count == 0:
|
||||
return "The source dataframe does not have any infinite values.", True
|
||||
else:
|
||||
return (
|
||||
f"The source dataframe has {INF_count} infinite values. Please check the implementation.",
|
||||
False,
|
||||
)
|
||||
|
||||
|
||||
class FactorSingleColumnEvaluator(FactorEvaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
implementation: Workspace,
|
||||
gt_implementation: Workspace,
|
||||
) -> Tuple[str, object]:
|
||||
_, gen_df = self._get_df(gt_implementation, implementation)
|
||||
if gen_df is None:
|
||||
return (
|
||||
"The source dataframe is None. Please check the implementation.",
|
||||
False,
|
||||
)
|
||||
if len(gen_df.columns) == 1:
|
||||
return "The source dataframe has only one column which is correct.", True
|
||||
else:
|
||||
return (
|
||||
"The source dataframe has more than one column. Please check the implementation. We only evaluate the first column.",
|
||||
False,
|
||||
)
|
||||
|
||||
|
||||
class FactorOutputFormatEvaluator(FactorEvaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
implementation: Workspace,
|
||||
gt_implementation: Workspace,
|
||||
) -> Tuple[str, object]:
|
||||
gt_df, gen_df = self._get_df(gt_implementation, implementation)
|
||||
if gen_df is None:
|
||||
return (
|
||||
"The source dataframe is None. Skip the evaluation of the output format.",
|
||||
False,
|
||||
)
|
||||
buffer = io.StringIO()
|
||||
gen_df.info(buf=buffer)
|
||||
gen_df_info_str = f"The user is currently working on a feature related task.\nThe output dataframe info is:\n{buffer.getvalue()}"
|
||||
system_prompt = T(".prompts:evaluator_output_format_system").r(
|
||||
scenario=(
|
||||
self.scen.get_scenario_all_desc(implementation.target_task, filtered_tag="feature")
|
||||
if self.scen is not None
|
||||
else "No scenario description."
|
||||
)
|
||||
)
|
||||
|
||||
# TODO: with retry_context(retry_n=3, except_list=[KeyError]):
|
||||
max_attempts = 3
|
||||
attempts = 0
|
||||
final_evaluation_dict = None
|
||||
|
||||
while attempts < max_attempts:
|
||||
try:
|
||||
api = APIBackend() if attempts == 0 else APIBackend(use_chat_cache=False)
|
||||
resp = api.build_messages_and_create_chat_completion(
|
||||
user_prompt=gen_df_info_str,
|
||||
system_prompt=system_prompt,
|
||||
json_mode=True,
|
||||
json_target_type=Dict[str, str | bool | int],
|
||||
)
|
||||
resp_dict = json.loads(resp)
|
||||
resp_dict["output_format_decision"] = str(resp_dict["output_format_decision"]).lower() in ["true", "1"]
|
||||
|
||||
return (
|
||||
str(resp_dict["output_format_feedback"]),
|
||||
resp_dict["output_format_decision"],
|
||||
)
|
||||
except (KeyError, json.JSONDecodeError) as e:
|
||||
attempts += 1
|
||||
if attempts >= max_attempts:
|
||||
raise KeyError(
|
||||
"Wrong JSON Response or missing 'output_format_decision' or 'output_format_feedback' key after multiple attempts."
|
||||
) from e
|
||||
|
||||
return "Failed to evaluate output format after multiple attempts.", False
|
||||
|
||||
|
||||
class FactorDatetimeDailyEvaluator(FactorEvaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
implementation: Workspace,
|
||||
gt_implementation: Workspace,
|
||||
) -> Tuple[str | object]:
|
||||
_, gen_df = self._get_df(gt_implementation, implementation)
|
||||
if gen_df is None:
|
||||
return "The source dataframe is None. Skip the evaluation of the datetime format.", False
|
||||
|
||||
if "datetime" not in gen_df.index.names:
|
||||
return "The source dataframe does not have a datetime index. Please check the implementation.", False
|
||||
|
||||
try:
|
||||
pd.to_datetime(gen_df.index.get_level_values("datetime"))
|
||||
except Exception:
|
||||
return (
|
||||
f"The source dataframe has a datetime index but it is not in the correct format (maybe a regular string or other objects). Please check the implementation.\n The head of the output dataframe is: \n{gen_df.head()}",
|
||||
False,
|
||||
)
|
||||
|
||||
time_diff = pd.to_datetime(gen_df.index.get_level_values("datetime")).to_series().diff().dropna()
|
||||
min_diff = time_diff.min()
|
||||
if min_diff <= pd.Timedelta(minutes=1):
|
||||
return (
|
||||
"The generated dataframe is not daily. The implementation is definitely wrong. Please check the implementation.",
|
||||
False,
|
||||
)
|
||||
if min_diff <= pd.Timedelta(minutes=30):
|
||||
return "The generated dataframe is intraday (1min bars). This is correct for EURUSD.", True
|
||||
return "The generated dataframe is daily.", True
|
||||
|
||||
|
||||
class FactorRowCountEvaluator(FactorEvaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
implementation: Workspace,
|
||||
gt_implementation: Workspace,
|
||||
) -> Tuple[str, object]:
|
||||
gt_df, gen_df = self._get_df(gt_implementation, implementation)
|
||||
if gen_df is None:
|
||||
return (
|
||||
"The source dataframe is None. Please check the implementation.",
|
||||
False,
|
||||
)
|
||||
ratio = min(len(gen_df), len(gt_df)) / max(len(gen_df), len(gt_df))
|
||||
return (
|
||||
(
|
||||
f"The ratio of rows count in the source dataframe to the ground truth dataframe is {ratio:.2f}. "
|
||||
+ "Please verify the implementation. "
|
||||
if ratio <= 0.99
|
||||
else ""
|
||||
),
|
||||
ratio,
|
||||
)
|
||||
|
||||
|
||||
class FactorIndexEvaluator(FactorEvaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
implementation: Workspace,
|
||||
gt_implementation: Workspace,
|
||||
) -> Tuple[str, object]:
|
||||
gt_df, gen_df = self._get_df(gt_implementation, implementation)
|
||||
if gen_df is None:
|
||||
return (
|
||||
"The source dataframe is None. Please check the implementation.",
|
||||
False,
|
||||
)
|
||||
gen_index_set, gt_index_set = set(gen_df.index), set(gt_df.index)
|
||||
similarity = len(gen_index_set.intersection(gt_index_set)) / len(gen_index_set.union(gt_index_set))
|
||||
return (
|
||||
(
|
||||
f"The source dataframe and the ground truth dataframe have different index with a similarity of {similarity:.2%}. The similarity is calculated by the number of shared indices divided by the union indices. "
|
||||
+ "Please check the implementation."
|
||||
if similarity <= 0.99
|
||||
else ""
|
||||
),
|
||||
similarity,
|
||||
)
|
||||
|
||||
|
||||
class FactorMissingValuesEvaluator(FactorEvaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
implementation: Workspace,
|
||||
gt_implementation: Workspace,
|
||||
) -> Tuple[str, object]:
|
||||
gt_df, gen_df = self._get_df(gt_implementation, implementation)
|
||||
if gen_df is None:
|
||||
return (
|
||||
"The source dataframe is None. Please check the implementation.",
|
||||
False,
|
||||
)
|
||||
if gen_df.isna().sum().sum() == gt_df.isna().sum().sum():
|
||||
return "Both dataframes have the same missing values.", True
|
||||
else:
|
||||
return (
|
||||
f"The dataframes do not have the same missing values. The source dataframe has {gen_df.isna().sum().sum()} missing values, while the ground truth dataframe has {gt_df.isna().sum().sum()} missing values. Please check the implementation.",
|
||||
False,
|
||||
)
|
||||
|
||||
|
||||
class FactorEqualValueRatioEvaluator(FactorEvaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
implementation: Workspace,
|
||||
gt_implementation: Workspace,
|
||||
) -> Tuple[str, object]:
|
||||
gt_df, gen_df = self._get_df(gt_implementation, implementation)
|
||||
if gen_df is None:
|
||||
return (
|
||||
"The source dataframe is None. Please check the implementation.",
|
||||
-1,
|
||||
)
|
||||
try:
|
||||
close_values = gen_df.sub(gt_df).abs().lt(1e-6)
|
||||
result_int = close_values.astype(int)
|
||||
pos_num = result_int.sum().sum()
|
||||
acc_rate = pos_num / close_values.size
|
||||
except:
|
||||
close_values = gen_df
|
||||
if close_values.all().iloc[0]:
|
||||
return (
|
||||
"All values in the dataframes are equal within the tolerance of 1e-6.",
|
||||
acc_rate,
|
||||
)
|
||||
else:
|
||||
return (
|
||||
"Some values differ by more than the tolerance of 1e-6. Check for rounding errors or differences in the calculation methods.",
|
||||
acc_rate,
|
||||
)
|
||||
|
||||
|
||||
class FactorCorrelationEvaluator(FactorEvaluator):
|
||||
def __init__(self, hard_check: bool, *args, **kwargs) -> None:
|
||||
super().__init__(*args, **kwargs)
|
||||
self.hard_check = hard_check
|
||||
|
||||
def evaluate(
|
||||
self,
|
||||
implementation: Workspace,
|
||||
gt_implementation: Workspace,
|
||||
) -> Tuple[str, object]:
|
||||
gt_df, gen_df = self._get_df(gt_implementation, implementation)
|
||||
if gen_df is None:
|
||||
return (
|
||||
"The source dataframe is None. Please check the implementation.",
|
||||
False,
|
||||
)
|
||||
concat_df = pd.concat([gen_df, gt_df], axis=1)
|
||||
concat_df.columns = ["source", "gt"]
|
||||
ic = concat_df.groupby("datetime").apply(lambda df: df["source"].corr(df["gt"])).dropna().mean()
|
||||
ric = (
|
||||
concat_df.groupby("datetime")
|
||||
.apply(lambda df: df["source"].corr(df["gt"], method="spearman"))
|
||||
.dropna()
|
||||
.mean()
|
||||
)
|
||||
|
||||
if self.hard_check:
|
||||
if ic > 0.99 and ric > 0.99:
|
||||
return (
|
||||
f"The dataframes are highly correlated. The ic is {ic:.6f} and the rankic is {ric:.6f}.",
|
||||
True,
|
||||
)
|
||||
else:
|
||||
return (
|
||||
f"The dataframes are not sufficiently high correlated. The ic is {ic:.6f} and the rankic is {ric:.6f}. Investigate the factors that might be causing the discrepancies and ensure that the logic of the factor calculation is consistent.",
|
||||
False,
|
||||
)
|
||||
else:
|
||||
return f"The ic is ({ic:.6f}) and the rankic is ({ric:.6f}).", ic
|
||||
|
||||
|
||||
class FactorValueEvaluator(FactorEvaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
implementation: Workspace,
|
||||
gt_implementation: Workspace,
|
||||
version: int = 1, # 1 for qlib factors and 2 for kaggle factors
|
||||
**kwargs,
|
||||
) -> Tuple:
|
||||
conclusions = []
|
||||
|
||||
# Initialize result variables
|
||||
row_result = 0
|
||||
index_result = 0
|
||||
output_format_result = None
|
||||
equal_value_ratio_result = 0
|
||||
high_correlation_result = False
|
||||
row_result = None
|
||||
|
||||
# Check if both dataframe has only one columns Mute this since factor task might generate more than one columns now
|
||||
if version == 1:
|
||||
feedback_str, _ = FactorSingleColumnEvaluator(self.scen).evaluate(implementation, gt_implementation)
|
||||
conclusions.append(feedback_str)
|
||||
elif version == 2:
|
||||
input_shape = self.scen.input_shape
|
||||
_, gen_df = self._get_df(gt_implementation, implementation)
|
||||
if gen_df.shape[-1] > input_shape[-1]:
|
||||
conclusions.append(
|
||||
"Output dataframe has more columns than input feature which is not acceptable in feature processing tasks. Please check the implementation to avoid generating too many columns. Consider this implementation as a failure."
|
||||
)
|
||||
|
||||
feedback_str, inf_evaluate_res = FactorInfEvaluator(self.scen).evaluate(implementation, gt_implementation)
|
||||
conclusions.append(feedback_str)
|
||||
|
||||
# Check if the index of the dataframe is ("datetime", "instrument")
|
||||
feedback_str, _ = FactorOutputFormatEvaluator(self.scen).evaluate(implementation, gt_implementation)
|
||||
conclusions.append(feedback_str)
|
||||
if version == 1:
|
||||
feedback_str, daily_check_result = FactorDatetimeDailyEvaluator(self.scen).evaluate(
|
||||
implementation, gt_implementation
|
||||
)
|
||||
conclusions.append(feedback_str)
|
||||
else:
|
||||
daily_check_result = None
|
||||
|
||||
# Check dataframe format
|
||||
if gt_implementation is not None:
|
||||
feedback_str, row_result = FactorRowCountEvaluator(self.scen).evaluate(implementation, gt_implementation)
|
||||
conclusions.append(feedback_str)
|
||||
|
||||
feedback_str, index_result = FactorIndexEvaluator(self.scen).evaluate(implementation, gt_implementation)
|
||||
conclusions.append(feedback_str)
|
||||
|
||||
feedback_str, output_format_result = FactorMissingValuesEvaluator(self.scen).evaluate(
|
||||
implementation, gt_implementation
|
||||
)
|
||||
conclusions.append(feedback_str)
|
||||
|
||||
feedback_str, equal_value_ratio_result = FactorEqualValueRatioEvaluator(self.scen).evaluate(
|
||||
implementation, gt_implementation
|
||||
)
|
||||
conclusions.append(feedback_str)
|
||||
|
||||
if index_result > 0.99:
|
||||
feedback_str, high_correlation_result = FactorCorrelationEvaluator(
|
||||
hard_check=True, scen=self.scen
|
||||
).evaluate(implementation, gt_implementation)
|
||||
else:
|
||||
high_correlation_result = False
|
||||
feedback_str = "The source dataframe and the ground truth dataframe have different index. Give up comparing the values and correlation because it's useless"
|
||||
conclusions.append(feedback_str)
|
||||
|
||||
# Combine all conclusions into a single string
|
||||
conclusion_str = "\n".join(conclusions)
|
||||
|
||||
if gt_implementation is not None and (equal_value_ratio_result > 0.99) or high_correlation_result:
|
||||
decision_from_value_check = True
|
||||
elif (
|
||||
row_result is not None
|
||||
and row_result <= 0.99
|
||||
or output_format_result is False
|
||||
or daily_check_result is False
|
||||
or inf_evaluate_res is False
|
||||
):
|
||||
decision_from_value_check = False
|
||||
else:
|
||||
decision_from_value_check = None
|
||||
return conclusion_str, decision_from_value_check
|
||||
|
||||
|
||||
class FactorFinalDecisionEvaluator(FactorEvaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
target_task: FactorTask,
|
||||
execution_feedback: str,
|
||||
value_feedback: str,
|
||||
code_feedback: str,
|
||||
**kwargs,
|
||||
) -> Tuple:
|
||||
system_prompt = T(".prompts:evaluator_final_decision_v1_system").r(
|
||||
scenario=(
|
||||
self.scen.get_scenario_all_desc(target_task, filtered_tag="feature")
|
||||
if self.scen is not None
|
||||
else "No scenario description."
|
||||
)
|
||||
)
|
||||
execution_feedback_to_render = execution_feedback
|
||||
|
||||
for _ in range(10): # 10 times to split the content is enough
|
||||
user_prompt = T(".prompts:evaluator_final_decision_v1_user").r(
|
||||
factor_information=target_task.get_task_information(),
|
||||
execution_feedback=execution_feedback_to_render,
|
||||
code_feedback=code_feedback,
|
||||
value_feedback=(
|
||||
value_feedback
|
||||
if value_feedback is not None
|
||||
else "No Ground Truth Value provided, so no evaluation on value is performed."
|
||||
),
|
||||
)
|
||||
if (
|
||||
APIBackend().build_messages_and_calculate_token(
|
||||
user_prompt=user_prompt,
|
||||
system_prompt=system_prompt,
|
||||
)
|
||||
> APIBackend().chat_token_limit
|
||||
):
|
||||
execution_feedback_to_render = execution_feedback_to_render[len(execution_feedback_to_render) // 2 :]
|
||||
else:
|
||||
break
|
||||
|
||||
# TODO: with retry_context(retry_n=3, except_list=[KeyError]):
|
||||
final_evaluation_dict = None
|
||||
attempts = 0
|
||||
max_attempts = 3
|
||||
|
||||
while attempts < max_attempts:
|
||||
try:
|
||||
api = APIBackend() if attempts == 0 else APIBackend(use_chat_cache=False)
|
||||
final_evaluation_dict = json.loads(
|
||||
api.build_messages_and_create_chat_completion(
|
||||
user_prompt=user_prompt,
|
||||
system_prompt=system_prompt,
|
||||
json_mode=True,
|
||||
seed=attempts, # in case of useless retrying when cache enabled.
|
||||
json_target_type=Dict[str, str | bool | int],
|
||||
),
|
||||
)
|
||||
final_decision = final_evaluation_dict["final_decision"]
|
||||
final_feedback = final_evaluation_dict["final_feedback"]
|
||||
|
||||
final_decision = str(final_decision).lower() in ["true", "1"]
|
||||
return final_decision, final_feedback
|
||||
|
||||
except json.JSONDecodeError as e:
|
||||
raise ValueError("Failed to decode JSON response from API.") from e
|
||||
except KeyError as e:
|
||||
attempts += 1
|
||||
if attempts >= max_attempts:
|
||||
raise KeyError(
|
||||
"Response from API is missing 'final_decision' or 'final_feedback' key after multiple attempts."
|
||||
) from e
|
||||
|
||||
return None, None
|
||||
Executable
+42
@@ -0,0 +1,42 @@
|
||||
# How to read files.
|
||||
For example, if you want to read `filename.h5`
|
||||
```Python
|
||||
import pandas as pd
|
||||
df = pd.read_hdf("filename.h5", key="data")
|
||||
```
|
||||
NOTE: **key is always "data" for all hdf5 files **.
|
||||
|
||||
# Here is a short description about the data
|
||||
| Filename | Description |
|
||||
| -------------- | -----------------------------------------------------------------|
|
||||
| "daily_pv.h5" | EURUSD 1-minute OHLCV intraday data (2020-2026). |
|
||||
|
||||
# For different data, We have some basic knowledge for them
|
||||
|
||||
## EURUSD 1min intraday data
|
||||
$open: open price of EURUSD at the start of the 1min bar.
|
||||
$close: close price of EURUSD at the end of the 1min bar.
|
||||
$high: highest price of EURUSD during the 1min bar.
|
||||
$low: lowest price of EURUSD during the 1min bar.
|
||||
$volume: traded volume during the 1min bar (tick volume for FX).
|
||||
|
||||
**IMPORTANT: There is NO $factor column. Use only $open, $close, $high, $low, $volume.**
|
||||
|
||||
## Market sessions (UTC)
|
||||
- Asian session: 00:00 - 08:00 (mean reversion tendencies)
|
||||
- London session: 08:00 - 16:00 (trending, momentum works)
|
||||
- NY session: 13:00 - 21:00 (high volatility)
|
||||
- London-NY overlap: 13:00 - 16:00 (highest volume)
|
||||
|
||||
## Lookback reference for 1min data
|
||||
- 4 bars = 4 minutes
|
||||
- 8 bars = 8 minutes
|
||||
- 16 bars = 16 minutes
|
||||
- 32 bars = 32 minutes
|
||||
- 96 bars = 1.6 hours
|
||||
- 1440 bars = 1 day (24 hours)
|
||||
|
||||
## Data range
|
||||
- Start: 2020-01-01 17:00:00 UTC
|
||||
- End: 2026-03-20 15:58:00 UTC
|
||||
- Total bars: ~2.26 million
|
||||
@@ -0,0 +1,132 @@
|
||||
import json
|
||||
from typing import List, Tuple
|
||||
|
||||
from rdagent.components.coder.factor_coder.factor import FactorExperiment, FactorTask
|
||||
from rdagent.components.proposal import FactorHypothesis2Experiment, FactorHypothesisGen
|
||||
from rdagent.core.proposal import Hypothesis, Scenario, Trace
|
||||
from rdagent.scenarios.qlib.experiment.factor_experiment import QlibFactorExperiment
|
||||
from rdagent.scenarios.qlib.experiment.model_experiment import QlibModelExperiment
|
||||
from rdagent.scenarios.qlib.experiment.quant_experiment import QlibQuantScenario
|
||||
from rdagent.utils.agent.tpl import T
|
||||
|
||||
QlibFactorHypothesis = Hypothesis
|
||||
|
||||
|
||||
class QlibFactorHypothesisGen(FactorHypothesisGen):
|
||||
def __init__(self, scen: Scenario) -> Tuple[dict, bool]:
|
||||
super().__init__(scen)
|
||||
|
||||
def prepare_context(self, trace: Trace) -> Tuple[dict, bool]:
|
||||
hypothesis_and_feedback = (
|
||||
T("scenarios.qlib.prompts:hypothesis_and_feedback").r(
|
||||
trace=trace,
|
||||
)
|
||||
if len(trace.hist) > 0
|
||||
else "No previous hypothesis and feedback available since it's the first round."
|
||||
)
|
||||
last_hypothesis_and_feedback = (
|
||||
T("scenarios.qlib.prompts:last_hypothesis_and_feedback").r(
|
||||
experiment=trace.hist[-1][0], feedback=trace.hist[-1][1]
|
||||
)
|
||||
if len(trace.hist) > 0
|
||||
else "No previous hypothesis and feedback available since it's the first round."
|
||||
)
|
||||
|
||||
context_dict = {
|
||||
"hypothesis_and_feedback": hypothesis_and_feedback,
|
||||
"last_hypothesis_and_feedback": last_hypothesis_and_feedback,
|
||||
"RAG": (
|
||||
"Try EURUSD-specific FX factors: momentum (4-32 bars), mean reversion, ATR volatility, volume spikes, session-based signals. Use only $open $close $high $low $volume columns. No $factor column exists."
|
||||
if len(trace.hist) < 15
|
||||
else "Now, you need to try factors that can achieve high IC (e.g., machine learning-based factors)."
|
||||
),
|
||||
"hypothesis_output_format": T("scenarios.qlib.prompts:factor_hypothesis_output_format").r(),
|
||||
"hypothesis_specification": T("scenarios.qlib.prompts:factor_hypothesis_specification").r(),
|
||||
}
|
||||
return context_dict, True
|
||||
|
||||
def convert_response(self, response: str) -> Hypothesis:
|
||||
response_dict = json.loads(response)
|
||||
hypothesis = QlibFactorHypothesis(
|
||||
hypothesis=response_dict.get("hypothesis"),
|
||||
reason=response_dict.get("reason"),
|
||||
concise_reason=response_dict.get("concise_reason"),
|
||||
concise_observation=response_dict.get("concise_observation"),
|
||||
concise_justification=response_dict.get("concise_justification"),
|
||||
concise_knowledge=response_dict.get("concise_knowledge"),
|
||||
)
|
||||
return hypothesis
|
||||
|
||||
|
||||
class QlibFactorHypothesis2Experiment(FactorHypothesis2Experiment):
|
||||
def prepare_context(self, hypothesis: Hypothesis, trace: Trace) -> Tuple[dict | bool]:
|
||||
if isinstance(trace.scen, QlibQuantScenario):
|
||||
scenario = trace.scen.get_scenario_all_desc(action="factor")
|
||||
else:
|
||||
scenario = trace.scen.get_scenario_all_desc()
|
||||
|
||||
experiment_output_format = T("scenarios.qlib.prompts:factor_experiment_output_format").r()
|
||||
|
||||
if len(trace.hist) == 0:
|
||||
hypothesis_and_feedback = "No previous hypothesis and feedback available since it's the first round."
|
||||
else:
|
||||
specific_trace = Trace(trace.scen)
|
||||
for i in range(len(trace.hist) - 1, -1, -1):
|
||||
if not hasattr(trace.hist[i][0].hypothesis, "action") or trace.hist[i][0].hypothesis.action == "factor":
|
||||
specific_trace.hist.insert(0, trace.hist[i])
|
||||
if len(specific_trace.hist) > 0:
|
||||
specific_trace.hist.reverse()
|
||||
hypothesis_and_feedback = T("scenarios.qlib.prompts:hypothesis_and_feedback").r(
|
||||
trace=specific_trace,
|
||||
)
|
||||
else:
|
||||
hypothesis_and_feedback = "No previous hypothesis and feedback available."
|
||||
|
||||
return {
|
||||
"target_hypothesis": str(hypothesis),
|
||||
"scenario": scenario,
|
||||
"hypothesis_and_feedback": hypothesis_and_feedback,
|
||||
"experiment_output_format": experiment_output_format,
|
||||
"target_list": [],
|
||||
"RAG": None,
|
||||
}, True
|
||||
|
||||
def convert_response(self, response: str, hypothesis: Hypothesis, trace: Trace) -> FactorExperiment:
|
||||
response_dict = json.loads(response)
|
||||
tasks = []
|
||||
|
||||
for factor_name in response_dict:
|
||||
description = response_dict[factor_name]["description"]
|
||||
formulation = response_dict[factor_name]["formulation"]
|
||||
variables = response_dict[factor_name]["variables"]
|
||||
tasks.append(
|
||||
FactorTask(
|
||||
factor_name=factor_name,
|
||||
factor_description=description,
|
||||
factor_formulation=formulation,
|
||||
variables=variables,
|
||||
)
|
||||
)
|
||||
|
||||
exp = QlibFactorExperiment(tasks, hypothesis=hypothesis)
|
||||
exp.based_experiments = [QlibFactorExperiment(sub_tasks=[])] + [
|
||||
t[0] for t in trace.hist if t[1] and isinstance(t[0], FactorExperiment)
|
||||
]
|
||||
|
||||
unique_tasks = []
|
||||
for task in tasks:
|
||||
duplicate = False
|
||||
for based_exp in exp.based_experiments:
|
||||
if isinstance(based_exp, QlibModelExperiment):
|
||||
continue
|
||||
for sub_task in based_exp.sub_tasks:
|
||||
if task.factor_name == sub_task.factor_name:
|
||||
duplicate = True
|
||||
break
|
||||
if duplicate:
|
||||
break
|
||||
if not duplicate:
|
||||
unique_tasks.append(task)
|
||||
|
||||
exp.tasks = unique_tasks
|
||||
return exp
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Qlib läuft in rdagent4qlib environment
|
||||
result = subprocess.run(
|
||||
["/home/nico/miniconda3/envs/rdagent4qlib/bin/python3", "-c", """
|
||||
import qlib
|
||||
from qlib.data import D
|
||||
qlib.init(provider_uri="~/.qlib/qlib_data/eurusd_1min_data")
|
||||
fields = ["$open", "$close", "$high", "$low", "$volume"]
|
||||
data = (D.features(["EURUSD"], fields, start_time="2022-03-14", end_time="2026-03-20", freq="1min")
|
||||
.swaplevel().sort_index())
|
||||
data.to_hdf("./daily_pv_all.h5", key="data")
|
||||
data_debug = (D.features(["EURUSD"], fields, start_time="2024-01-01", end_time="2026-03-20", freq="1min")
|
||||
.swaplevel().sort_index())
|
||||
data_debug.to_hdf("./daily_pv_debug.h5", key="data")
|
||||
print(f"Done: {data.shape[0]} rows")
|
||||
"""],
|
||||
capture_output=False
|
||||
)
|
||||
@@ -0,0 +1,257 @@
|
||||
qlib_quant_background: |-
|
||||
Quantitative investment is a data-driven approach to asset management that relies on mathematical models, statistical techniques, and computational methods to analyze financial markets and make investment decisions. Two essential components of this approach are factors and models.
|
||||
|
||||
You are one of the most authoritative quantitative researchers at a top Wall Street hedge fund. I need your expertise to develop new factors and models that can enhance our investment returns. Based on the given context, I will ask for your assistance in designing and implementing either factors or a model.
|
||||
|
||||
{% if runtime_environment is not none %}
|
||||
====== Runtime Environment ======
|
||||
You have following environment to run the code:
|
||||
{{ runtime_environment }}
|
||||
{% endif %}
|
||||
|
||||
qlib_factor_background: |-
|
||||
The factor is a characteristic or variable used in quant investment that can help explain the returns and risks of a portfolio or a single asset. Factors are used by investors to identify and exploit sources of excess returns, and they are central to many quantitative investment strategies.
|
||||
Each number in the factor represents a physics value to an instrument on a day.
|
||||
User will train a model to predict the next several days return based on the factor values of the previous days.
|
||||
The factor is defined in the following parts:
|
||||
1. Name: The name of the factor.
|
||||
2. Description: The description of the factor.
|
||||
3. Formulation: The formulation of the factor.
|
||||
4. Variables: The variables or functions used in the formulation of the factor.
|
||||
The factor might not provide all the parts of the information above since some might not be applicable.
|
||||
Please specifically give all the hyperparameter in the factors like the window size, look back period, and so on. One factor should statically defines one output with a static source data. For example, last 10 days momentum and last 20 days momentum should be two different factors.
|
||||
|
||||
{% if runtime_environment is not none %}
|
||||
====== Runtime Environment ======
|
||||
You have following environment to run the code:
|
||||
{{ runtime_environment }}
|
||||
{% endif %}
|
||||
|
||||
qlib_factor_interface: |-
|
||||
Your python code should follow the interface to better interact with the user's system.
|
||||
CRITICAL DATA FORMAT: The HDF5 file has a MultiIndex with levels ['datetime', 'instrument']. The instrument is an INDEX LEVEL, NOT a column. Never use df['instrument']. Always use df.index.get_level_values('instrument') or df.groupby(level='instrument'). For rolling calculations use df['$close'].unstack(level='instrument'), apply rolling, then .stack() to restore MultiIndex.
|
||||
Your python code should contain the following part: the import part, the function part, and the main part. You should write a main function name: "calculate_{function_name}" and call this function in "if __name__ == __main__" part. Don't write any try-except block in your python code. The user will catch the exception message and provide the feedback to you.
|
||||
User will write your python code into a python file and execute the file directly with "python {your_file_name}.py". You should calculate the factor values and save the result into a HDF5(H5) file named "result.h5" in the same directory as your python file. The result file is a HDF5(H5) file containing a pandas dataframe. The index of the dataframe is the "datetime" and "instrument", and the single column name is the factor name,and the value is the factor value. The result file should be saved in the same directory as your python file.
|
||||
|
||||
qlib_factor_strategy: |-
|
||||
Ensure that for every step of data processing, the data format (including indexes) is clearly explained through comments.
|
||||
Each transformation or calculation should be accompanied by a detailed description of how the data is structured, especially focusing on key aspects like whether the data has multi-level indexing, how to access specific columns or index levels, and any operations that affect the data shape (e.g., `reset_index()`, `groupby()`, `merge()`).
|
||||
This step-by-step explanation will ensure clarity and accuracy in data handling. For example:
|
||||
1. **Start with multi-level index**:
|
||||
```python
|
||||
# The initial DataFrame has a multi-level index with 'datetime' and 'instrument'.
|
||||
# To access the 'datetime' index, use df.index.get_level_values('datetime').
|
||||
datetime_values = df.index.get_level_values('datetime')
|
||||
```
|
||||
|
||||
2. **Reset the index if necessary**:
|
||||
```python
|
||||
# Resetting the index to move 'datetime' and 'instrument' from the index to columns.
|
||||
# This operation flattens the multi-index structure.
|
||||
df = df.reset_index()
|
||||
```
|
||||
|
||||
3. **Perform groupby operations**:
|
||||
```python
|
||||
# Grouping by 'datetime' and 'instrument' to aggregate the data.
|
||||
# After groupby, the result will maintain 'datetime' and 'instrument' as a multi-level index.
|
||||
df_grouped = df.groupby(['datetime', 'instrument']).sum()
|
||||
```
|
||||
|
||||
4. **Ensure consistent datetime formats**:
|
||||
```python
|
||||
# Before merging, ensure that the 'datetime' column in both DataFrames is of the same format.
|
||||
# Convert to datetime format if necessary.
|
||||
df['datetime'] = pd.to_datetime(df['datetime'])
|
||||
other_df['datetime'] = pd.to_datetime(other_df['datetime'])
|
||||
```
|
||||
|
||||
5. **Merge operations**:
|
||||
```python
|
||||
# When merging DataFrames, ensure you are merging on both 'datetime' and 'instrument'.
|
||||
# If these are part of the index, reset the index before merging.
|
||||
merged_df = pd.merge(df, other_df, on=['datetime', 'instrument'], how='inner')
|
||||
```
|
||||
|
||||
qlib_factor_output_format: |-
|
||||
Your output should be a pandas dataframe similar to the following example information:
|
||||
<class 'pandas.core.frame.DataFrame'>
|
||||
MultiIndex: 2261923 entries, (Timestamp('2020-01-01 17:00:00'), 'EURUSD') to (Timestamp('2026-03-20 15:58:00'), 'EURUSD')
|
||||
Data columns (total 1 columns):
|
||||
# Column Non-Null Count Dtype
|
||||
--- ------ -------------- -----
|
||||
0 your factor name 2261923 non-null float64
|
||||
dtypes: float64(1)
|
||||
memory usage: <ignore>
|
||||
Notice: The non-null count is OK to be different to the total number of entries since some instruments may not have the factor value on some days.
|
||||
One possible format of `result.h5` may be like following:
|
||||
datetime instrument
|
||||
2020-01-01 EURUSD 1.094240
|
||||
2020-01-02 EURUSD 1.094280
|
||||
2020-01-03 EURUSD 1.095920
|
||||
...
|
||||
2026-03-20 EURUSD 1.083150
|
||||
|
||||
qlib_factor_simulator: |-
|
||||
The factors will be sent into Qlib to train a model to predict the next several days return based on the factor values of the previous days.
|
||||
Qlib is an AI-oriented quantitative investment platform that aims to realize the potential, empower research, and create value using AI technologies in quantitative investment, from exploring ideas to implementing productions. Qlib supports diverse machine learning modeling paradigms. including supervised learning, market dynamics modeling, and RL.
|
||||
User will use Qlib to automatically do the following things:
|
||||
1. generate a new factor table based on the factor values.
|
||||
2. train a model like LightGBM, CatBoost, LSTM or simple PyTorch model to predict the next several days return based on the factor values.
|
||||
3. build a portfolio based on the predicted return based on a strategy.
|
||||
4. evaluate the portfolio's performance including the return, sharpe ratio, max drawdown, and so on.
|
||||
|
||||
qlib_factor_rich_style_description : |-
|
||||
### R&D Agent-Qlib: Automated Quantitative Trading & Iterative Factors Evolution Demo
|
||||
|
||||
#### [Overview](#_summary)
|
||||
|
||||
The demo showcases the iterative process of hypothesis generation, knowledge construction, and decision-making. It highlights how financial factors evolve through continuous feedback and refinement.
|
||||
|
||||
#### [Automated R&D](#_rdloops)
|
||||
|
||||
- **[R (Research)](#_research)**
|
||||
- Iterative development of ideas and hypotheses.
|
||||
- Continuous learning and knowledge construction.
|
||||
|
||||
- **[D (Development)](#_development)**
|
||||
- Progressive implementation and code generation of factors.
|
||||
- Automated testing and validation of financial factors.
|
||||
|
||||
#### [Objective](#_summary)
|
||||
|
||||
To demonstrate the dynamic evolution of financial factors through the Qlib platform, emphasizing how each iteration enhances the accuracy and reliability of the resulting financial factors.
|
||||
|
||||
qlib_factor_from_report_rich_style_description : |-
|
||||
### R&D Agent-Qlib: Automated Quantitative Trading & Factor Extraction from Financial Reports Demo
|
||||
|
||||
#### [Overview](#_summary)
|
||||
|
||||
This demo showcases the process of extracting factors from financial research reports, implementing these factors, and analyzing their performance through Qlib backtest, continually expanding and refining the factor library.
|
||||
|
||||
#### [Automated R&D](#_rdloops)
|
||||
|
||||
- **[R (Research)](#_research)**
|
||||
- Iterative development of ideas and hypotheses from financial reports.
|
||||
- Continuous learning and knowledge construction.
|
||||
|
||||
- **[D (Development)](#_development)**
|
||||
- Progressive factor extraction and code generation.
|
||||
- Automated implementation and testing of financial factors.
|
||||
|
||||
#### [Objective](#_summary)
|
||||
|
||||
<table border="1" style="width:100%; border-collapse: collapse;">
|
||||
<tr>
|
||||
<td>💡 <strong>Innovation </strong></td>
|
||||
<td>Tool to quickly extract and test factors from research reports.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>⚡ <strong>Efficiency </strong></td>
|
||||
<td>Rapid identification of valuable factors from numerous reports.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>🗃️ <strong>Outputs </strong></td>
|
||||
<td>Expand and refine the factor library to support further research.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
qlib_factor_experiment_setting: |-
|
||||
| Dataset 📊 | Model 🤖 | Factors 🌟 | Data Split 🧮 |
|
||||
|---------|----------|---------------|-------------------------------------------------|
|
||||
| EURUSD | LGBModel | Alpha158 Plus | Train: 2022-01-01 to 2024-06-30 <br> Valid: 2024-07-01 to 2024-12-31 <br> Test : 2025-01-01 to 2026-03-20 |
|
||||
|
||||
|
||||
qlib_model_background: |-
|
||||
The model is a machine learning or deep learning structure used in quantitative investment to predict the returns and risks of a portfolio or a single asset. Models are employed by investors to generate forecasts based on historical data and identified factors, which are central to many quantitative investment strategies.
|
||||
Each model takes the factors as input and predicts the future returns. Usually, the bigger the model is, the better the performance would be.
|
||||
The model is defined in the following parts:
|
||||
1. Name: The name of the model.
|
||||
2. Description: The description of the model.
|
||||
3. Architecture: The detailed architecture of the model, such as neural network layers or tree structures.
|
||||
4. Hyperparameters: The hyperparameters used in the model.
|
||||
5. Training_hyperparameters: The hyperparameters used during the training process.
|
||||
6. ModelType: The type of the model, "Tabular" for tabular model and "TimeSeries" for time series model.
|
||||
The model should provide clear and detailed documentation of its architecture and hyperparameters. One model should statically define one output with a fixed architecture and hyperparameters.
|
||||
|
||||
{% if runtime_environment is not none %}
|
||||
====== Runtime Environment ======
|
||||
You have following environment to run the code:
|
||||
{{ runtime_environment }}
|
||||
{% endif %}
|
||||
|
||||
qlib_model_interface: |-
|
||||
Your python code should follow the interface to better interact with the user's system.
|
||||
You code should contain several parts:
|
||||
1. The import part: import the necessary libraries.
|
||||
2. A class which is a sub-class of pytorch.nn.Module. This class should should have a init function and a forward function which inputs a tensor and outputs a tensor.
|
||||
3. Set a variable called "model_cls" to the class you defined.
|
||||
|
||||
The user will save your code into a python file called "model.py". Then the user imports model_cls in file "model.py" after setting the cwd into the directory:
|
||||
```python
|
||||
from model import model_cls
|
||||
```
|
||||
So your python code should follow the pattern:
|
||||
```python
|
||||
class XXXModel(torch.nn.Module):
|
||||
...
|
||||
model_cls = XXXModel
|
||||
```
|
||||
|
||||
The model can be configured as either "Tabular" for tabular models or "TimeSeries" for time series models. For a tabular model, the input shape is (batch_size, num_features), while for a time series model, the input shape is (batch_size, num_timesteps, num_features). In both cases, the output shape of the model should be (batch_size, 1).
|
||||
`num_features` will be directly set for the model based on the input data shape.
|
||||
User will initialize the tabular model with the following code:
|
||||
```python
|
||||
model = model_cls(num_features=num_features)
|
||||
```
|
||||
User will initialize the time series model with the following code:
|
||||
```python
|
||||
model = model_cls(num_features=num_features, num_timesteps=num_timesteps)
|
||||
```
|
||||
No other parameters will be passed to the model so give other parameters a default value or just make them static.
|
||||
|
||||
Don't write any try-except block in your python code. The user will catch the exception message and provide the feedback to you. Also, don't write main function in your python code. The user will call the forward method in the model_cls to get the output tensor.
|
||||
|
||||
Please notice that your model should only use current features as input. The user will provide the input tensor to the model's forward function.
|
||||
|
||||
|
||||
qlib_model_output_format: |-
|
||||
Your output should be a tensor with shape (batch_size, 1).
|
||||
The output tensor should be saved in a file named "output.pth" in the same directory as your python file.
|
||||
The user will evaluate the shape of the output tensor so the tensor read from "output.pth" should be 8 numbers.
|
||||
|
||||
qlib_model_simulator: |-
|
||||
The models will be sent into Qlib to train and evaluate their performance in predicting future returns. Hypothesis is improved upon checking the feedback on the results.
|
||||
Qlib is an AI-oriented quantitative investment platform that aims to realize the potential, empower research, and create value using AI technologies in quantitative investment, from exploring ideas to implementing productions. Qlib supports diverse machine learning modeling paradigms, including supervised learning, market dynamics modeling, and reinforcement learning (RL).
|
||||
User will use Qlib to automatically perform the following tasks:
|
||||
1. Generate a baseline factor table.
|
||||
2. Train the model defined in your class Net to predict the next several days' returns based on the factor values.
|
||||
3. Build a portfolio based on the predicted returns using a specific strategy.
|
||||
4. Evaluate the portfolio's performance, including metrics such as return, IC, max drawdown, and others.
|
||||
5. Iterate on growing the hypothesis to enable model improvements based on performance evaluations and feedback.
|
||||
|
||||
qlib_model_rich_style_description: |-
|
||||
### Qlib Model Evolving Automatic R&D Demo
|
||||
|
||||
#### [Overview](#_summary)
|
||||
|
||||
The demo showcases the iterative process of hypothesis generation, knowledge construction, and decision-making in model construction in quantitative finance. It highlights how models evolve through continuous feedback and refinement.
|
||||
|
||||
#### [Automated R&D](#_rdloops)
|
||||
|
||||
- **[R (Research)](#_research)**
|
||||
- Iteration of ideas and hypotheses.
|
||||
- Continuous learning and knowledge construction.
|
||||
|
||||
- **[D (Development)](#_development)**
|
||||
- Evolving code generation and model refinement.
|
||||
- Automated implementation and testing of models.
|
||||
|
||||
#### [Objective](#_summary)
|
||||
|
||||
To demonstrate the dynamic evolution of models through the Qlib platform, emphasizing how each iteration enhances the accuracy and reliability of the resulting models.
|
||||
|
||||
qlib_model_experiment_setting: |-
|
||||
| Dataset 📊 | Model 🤖 | Factors 🌟 | Data Split 🧮 |
|
||||
|---------|----------|---------------|-------------------------------------------------|
|
||||
| EURUSD | RDAgent-dev | 20 factors (Alpha158) | Train: 2022-01-01 to 2024-06-30 <br> Valid: 2024-07-01 to 2024-12-31 <br> Test : 2025-01-01 to 2026-03-20 |
|
||||
@@ -0,0 +1,23 @@
|
||||
hypothesis_generation:
|
||||
system: |-
|
||||
You are an expert in FX and quantitative trading, specialized in EURUSD intraday strategies.
|
||||
Your task is to generate a well-reasoned hypothesis for new alpha factors based on EURUSD 1min OHLCV data.
|
||||
|
||||
Key market knowledge:
|
||||
- EURUSD trades 24h with three main sessions: Asian (00:00-08:00 UTC), London (08:00-16:00 UTC), NY (13:00-21:00 UTC)
|
||||
- London-NY overlap (13:00-16:00 UTC) has highest volume and momentum
|
||||
- Asian session shows mean reversion tendencies
|
||||
- Spread costs approximately 1.5 bps per trade — avoid overtrading
|
||||
- No overnight gap risk like stocks, but weekend gaps exist
|
||||
- Volume spikes signal news events (NFP, ECB, Fed)
|
||||
|
||||
Please ensure your response is in JSON format as shown below:
|
||||
{
|
||||
"hypothesis": "A clear and concise hypothesis based on the provided information.",
|
||||
"reason": "A detailed explanation supporting the generated hypothesis.",
|
||||
}
|
||||
user: |-
|
||||
The following are the financial factors and their descriptions:
|
||||
{{ factor_descriptions }}
|
||||
The report content is as follows:
|
||||
{{ report_content }}
|
||||
@@ -0,0 +1,312 @@
|
||||
hypothesis_and_feedback: |-
|
||||
=========================================================
|
||||
{% for experiment, feedback in trace.hist %}
|
||||
# Trial {{ loop.index }}:
|
||||
## Hypothesis
|
||||
{{ experiment.hypothesis }}
|
||||
## Specific task:
|
||||
{% for task in experiment.sub_tasks %}
|
||||
{% if task is not none and task.get_task_brief_information is defined %}
|
||||
{{ task.get_task_brief_information() }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
## Backtest Analysis and Feedback:
|
||||
{% if experiment.result is not none %}
|
||||
Backtest Result: {{ experiment.result.loc[["IC", "1day.excess_return_without_cost.annualized_return", "1day.excess_return_without_cost.max_drawdown"]] }}
|
||||
{% endif %}
|
||||
Observation: {{ feedback.observations }}
|
||||
Hypothesis Evaluation: {{ feedback.hypothesis_evaluation }}
|
||||
Decision (Whether the hypothesis was successful): {{ feedback.decision }}
|
||||
=========================================================
|
||||
{% endfor %}
|
||||
|
||||
last_hypothesis_and_feedback: |-
|
||||
## Hypothesis
|
||||
{{ experiment.hypothesis }}
|
||||
## Specific task:
|
||||
{% for task in experiment.sub_tasks %}
|
||||
{% if task is not none and task.get_task_brief_information is defined %}
|
||||
{{ task.get_task_brief_information() }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
## Backtest Analysis and Feedback:
|
||||
{% if experiment.result is not none %}
|
||||
Backtest Result: {{ experiment.result.loc[["IC", "1day.excess_return_without_cost.annualized_return", "1day.excess_return_without_cost.max_drawdown"]] }}
|
||||
{% endif %}
|
||||
Training Log:
|
||||
Here, you need to focus on analyzing whether there are any issues with the training. If any problems are identified, you must correct them in the next iteration and clearly describe how the changes will be made in the hypothesis.
|
||||
{{ experiment.stdout }}
|
||||
Observation: {{ feedback.observations }}
|
||||
Evaluation: {{ feedback.hypothesis_evaluation }}
|
||||
Decision (Whether this experiment is SOTA): {{ feedback.decision }}
|
||||
New Hypothesis (Given in feedback stage, just for reference, and can be accepted or rejected in the next round): {{ feedback.new_hypothesis }}
|
||||
Reasoning (Justification for the new hypothesis): {{ feedback.reason }}
|
||||
|
||||
sota_hypothesis_and_feedback: |-
|
||||
## Hypothesis
|
||||
{{ experiment.hypothesis }}
|
||||
## Specific task:
|
||||
{% for task in experiment.sub_tasks %}
|
||||
{% if task is not none and task.get_task_brief_information is defined %}
|
||||
{{ task.get_task_brief_information() }}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
## Backtest Analysis and Feedback:
|
||||
{% if experiment.result is not none %}
|
||||
Backtest Result: {{ experiment.result.loc[["IC", "1day.excess_return_without_cost.annualized_return", "1day.excess_return_without_cost.max_drawdown"]] }}
|
||||
{% endif %}
|
||||
Training Log: {{ experiment.stdout }}
|
||||
Observation: {{ feedback.observations }}
|
||||
Evaluation: {{ feedback.hypothesis_evaluation }}
|
||||
Decision (Whether this experiment is SOTA): {{ feedback.decision }}
|
||||
|
||||
hypothesis_output_format: |-
|
||||
The output should follow JSON format. The schema is as follows:
|
||||
{
|
||||
"hypothesis": "An exact, testable, and innovative statement derived from previous experimental trace analysis. Avoid overly general ideas and ensure precision. The hypothesis should clearly specify the exact approach and expected improvement in performance in two or three sentences.",
|
||||
"reason": "Provide a clear, logical explanation for why this hypothesis was proposed, grounded in evidence (e.g., trace history, domain principles). Reason should be short with no more than two sentences.",
|
||||
}
|
||||
|
||||
factor_hypothesis_output_format: |-
|
||||
The output should follow JSON format. The schema is as follows:
|
||||
{
|
||||
"hypothesis": "The new hypothesis generated based on the information provided. Limit in two or three sentences.",
|
||||
"reason": "The reason why you generate this hypothesis. It should be comprehensive and logical. It should cover the other keys below and extend them. Limit in two or three sentences.",
|
||||
}
|
||||
|
||||
hypothesis_output_format_with_action: |-
|
||||
The output should follow JSON format. The schema is as follows:
|
||||
{
|
||||
"action": "If `hypothesis_specification` provides the action you need to take, please follow "hypothesis_specification" to choose the action. Otherwise, based on previous experimental results, suggest the action you believe is most appropriate at the moment. It should be one of [`factor`, `model`].",
|
||||
"hypothesis": "The new hypothesis generated based on the information provided,should be a string.",
|
||||
"reason": "The reason why you generate this hypothesis. It should be comprehensive and logical. It should cover the other keys below and extend them. Limit in two or three sentences.",
|
||||
}
|
||||
|
||||
model_hypothesis_specification: |-
|
||||
1. First, observe and analyze the overall experimental progression in `hypothesis_and_feedback`. Analyze where the previous model designs were inadequate — whether it was due to parameter settings, architectural flaws, or a lack of novelty (proposing entirely new concepts is highly encouraged as long as they demonstrate effectiveness).
|
||||
2. Second, `last_hypothesis_and_feedback` and `sota_hypothesis_and_feedback` are key references you should pay close attention to. You can choose to optimize based on either of them or generate new ideas to form hypotheses and experiments.
|
||||
3. If there is no prior experiment or result available at the beginning, you can start by implementing a simple and small architecture.
|
||||
4. If a series of attempts fail to achieve SOTA, consider exploring entirely new directions; at this point, it is acceptable to return to simple architectures.
|
||||
5. Focus exclusively on the architecture of PyTorch models. Each hypothesis should specifically address architectural decisions, such as layer configurations, activation functions, regularization methods, and overall model structure. DO NOT do any feature-specific processing. Instead, you can propose innovative transformations on the input time-series data to enhance model training effectiveness.
|
||||
6. Avoid including aspects unrelated to architecture, such as input features or optimization strategies.
|
||||
7. Sometimes, when training performance is poor, adjusting hyperparameters can also be an effective strategy for improvement.
|
||||
8. Use standard libraries for baseline models, but also explore custom architecture designs to investigate novel structures. After sufficient trials with traditional models, aim for innovation comparable to top-tier AI conferences (NeurIPS, ICLR, ICML, SIGKDD, etc.) in time series modeling.
|
||||
|
||||
factor_hypothesis_specification: |-
|
||||
You are developing alpha factors for EURUSD intraday trading using 1-MINUTE OHLCV bars.
|
||||
|
||||
**Market Context:**
|
||||
- EURUSD trades 24h with three sessions: Asian (00:00-08:00 UTC), London (08:00-16:00 UTC), NY (13:00-21:00 UTC)
|
||||
- London-NY overlap (13:00-16:00 UTC) has highest volume and trending behavior
|
||||
- Asian session shows mean reversion tendencies
|
||||
- Spread cost ~1.5 bps per trade — avoid high-turnover factors
|
||||
- No $factor column exists — use only $open, $close, $high, $low, $volume
|
||||
- Each "instrument" is EURUSD, each "day" has 96 bars (24h * 60min = 1440 minutes / 15min bars was wrong, correct is 1440 1min bars)
|
||||
- Bar interpretation: 4 bars = 4 minutes, 16 bars = 16 minutes, 96 bars = 1.6 hours
|
||||
|
||||
**Factor Generation Rules:**
|
||||
1. **3-5 Factors per Generation** — cover different signal types per round
|
||||
2. **FX-Specific Signals First:**
|
||||
- Momentum: price change over last N bars (N=4,8,16,32 = 1h,2h,4h,8h)
|
||||
- Mean Reversion: deviation from rolling mean, Bollinger Band position
|
||||
- Volatility: ATR, realized vol, high-low range normalized
|
||||
- Volume: volume spike ratio, volume trend
|
||||
- Session: time-of-day encoded signals (London open, NY open)
|
||||
3. **Gradual Complexity:**
|
||||
- Rounds 1-5: single indicators (RSI, momentum, ATR)
|
||||
- Rounds 6-15: combined signals (momentum + volume filter)
|
||||
- Rounds 15+: ML-based factors (LSTM embeddings, XGBoost residuals)
|
||||
4. **Avoid:**
|
||||
- Factors requiring $factor column
|
||||
- Daily-frequency assumptions (no overnight gaps in logic)
|
||||
- Factors with >100 bar lookback without justification
|
||||
5. No matter how many factors you plan to generate, only reply with one set of hypothesis and reason.
|
||||
|
||||
factor_experiment_output_format: |-
|
||||
The output should follow JSON format. The schema is as follows:
|
||||
{
|
||||
"factor name 1": {
|
||||
"description": "description of factor 1, start with its type, e.g. [Momentum Factor]",
|
||||
"formulation": "latex formulation of factor 1",
|
||||
"variables": {
|
||||
"variable or function name 1": "description of variable or function 1",
|
||||
"variable or function name 2": "description of variable or function 2"
|
||||
}
|
||||
},
|
||||
"factor name 2": {
|
||||
"description": "description of factor 2, start with its type, e.g. [Machine Learning based Factor]",
|
||||
"formulation": "latex formulation of factor 2",
|
||||
"variables": {
|
||||
"variable or function name 1": "description of variable or function 1",
|
||||
"variable or function name 2": "description of variable or function 2"
|
||||
}
|
||||
}
|
||||
# Don't add ellipsis (...) or any filler text that might cause JSON parsing errors here!
|
||||
}
|
||||
|
||||
model_experiment_output_format: |-
|
||||
So far please only design one model to test the hypothesis!
|
||||
The output should follow JSON format. The schema is as follows (value in training_hyperparameters is a basic setting for reference, you CAN CHANGE depends on the previous training log):
|
||||
{
|
||||
"model_name (The name of the model)": {
|
||||
"description": "A detailed description of the model",
|
||||
"formulation": "A LaTeX formula representing the model's formulation",
|
||||
"architecture": "A detailed description of the model's architecture, e.g., neural network layers or tree structures",
|
||||
"variables": {
|
||||
"\\hat{y}_u": "The predicted output for node u",
|
||||
"variable_name_2": "Description of variable 2",
|
||||
"variable_name_3": "Description of variable 3"
|
||||
},
|
||||
"hyperparameters": {
|
||||
"hyperparameter_name_1": "value of hyperparameter 1",
|
||||
"hyperparameter_name_2": "value of hyperparameter 2",
|
||||
"hyperparameter_name_3": "value of hyperparameter 3"
|
||||
},
|
||||
"training_hyperparameters" { # All values are for reference; you can set them yourself
|
||||
"n_epochs": "100",
|
||||
"lr": "1e-3",
|
||||
"early_stop": 10,
|
||||
"batch_size": 256,
|
||||
"weight_decay": 1e-4,
|
||||
}
|
||||
"model_type": "Tabular or TimeSeries" # Should be one of "Tabular" or "TimeSeries"
|
||||
},
|
||||
}
|
||||
|
||||
factor_feedback_generation:
|
||||
system: |-
|
||||
You are a professional FX quantitative analyst specializing in EURUSD intraday strategies.
|
||||
The task is described in the following scenario:
|
||||
|
||||
{{ scenario }}
|
||||
|
||||
You will receive a hypothesis, multiple tasks with their factors, their results, and the SOTA result.
|
||||
Your feedback should specify whether the current result supports or refutes the hypothesis, compare it with previous SOTA results, and suggest FX-specific improvements.
|
||||
|
||||
**FX-specific evaluation criteria:**
|
||||
- IC > 0.02 is meaningful for 1min EURUSD data
|
||||
- Annualized return target: >9.62% (current SOTA to beat)
|
||||
- Spread cost ~1.5 bps per trade — penalize high-turnover factors
|
||||
- Factors using $factor column are INVALID — only $open $close $high $low $volume allowed
|
||||
- Session-aware factors (London/NY) tend to outperform session-agnostic ones
|
||||
- Mean reversion works in Asian session, momentum in London-NY overlap
|
||||
|
||||
Please understand the following operation logic:
|
||||
1. Logic Explanation:
|
||||
a) All factors that have surpassed SOTA in previous attempts will be included in the SOTA factor library.
|
||||
b) New experiments will generate new factors, combined with the SOTA library factors.
|
||||
c) These combined factors will be backtested and compared against current SOTA.
|
||||
2. Development Directions:
|
||||
a) New Direction: Propose a new FX-specific factor (session filter, volatility regime, volume spike).
|
||||
b) Optimization: Refine lookback windows (4/8/16/32 bars), add ADX filter, adjust for spread costs.
|
||||
3. Final Goal: Beat 9.62% ARR on EURUSD 1min with controlled drawdown (<20%).
|
||||
|
||||
When judging results:
|
||||
1. Any small improvement in annualized return → set Replace Best Result as yes.
|
||||
2. If IC < 0 consistently → factor has no predictive power, change direction entirely.
|
||||
3. High turnover with low return → add volume or volatility filter to reduce trade frequency.
|
||||
|
||||
Respond in JSON format:
|
||||
{
|
||||
"Observations": "Your overall observations here",
|
||||
"Feedback for Hypothesis": "Observations related to the hypothesis",
|
||||
"New Hypothesis": "Your new FX-specific hypothesis here",
|
||||
"Reasoning": "Reasoning for the new hypothesis",
|
||||
"Replace Best Result": "yes or no"
|
||||
}
|
||||
user: |-
|
||||
Target hypothesis:
|
||||
{{ hypothesis_text }}
|
||||
Tasks and Factors:
|
||||
{% for task in task_details %}
|
||||
- {{ task.factor_name }}: {{ task.factor_description }}
|
||||
- Factor Formulation: {{ task.factor_formulation }}
|
||||
- Variables: {{ task.variables }}
|
||||
- Factor Implementation: {{ task.factor_implementation }}
|
||||
{% if task.factor_implementation == "False" %}
|
||||
**Note: This factor was not implemented in the current experiment. Only the hypothesis for implemented factors can be verified.**
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
Combined Results:
|
||||
{{ combined_result }}
|
||||
|
||||
Analyze the combined result in the context of its ability to:
|
||||
1. Support or refute the hypothesis.
|
||||
2. Show improvement or deterioration compared to the SOTA experiment.
|
||||
|
||||
Note: Only factors with 'Factor Implementation' as True are implemented and tested in this experiment. If 'Factor Implementation' is False, the hypothesis for that factor cannot be verified in this run.
|
||||
|
||||
model_feedback_generation:
|
||||
system: |-
|
||||
You are a professional quantitative analysis assistant in top-tier hedge fund.
|
||||
|
||||
The task is described in the following scenario:
|
||||
{{ scenario }}
|
||||
|
||||
You will receive a quantitative model hypothesis, its specific task description, and it market backtest result.
|
||||
Your feedback should specify whether the current result supports or refutes the hypothesis, compare it with previous SOTA results, examine the model's training logs to analyze whether there are issues with hyperparameter settings, and suggest improvements or new directions.
|
||||
|
||||
Please provide detailed and constructive feedback.
|
||||
Example JSON Structure for Result Analysis:
|
||||
{
|
||||
"Observations": "First analyze the model's training logs to determine whether there are any issues with its parameter settings. Then clearly summarize the current results and the SOTA results with exact scores and any notable patterns. Limit your summary to no more than three concise, data-focused sentences.",
|
||||
"Feedback for Hypothesis": "Explicitly confirm or refute the hypothesis based on specific data points or performance trends. Limit to two sentences.",
|
||||
"New Hypothesis": "Propose a revised hypothesis, considering observed patterns and limitations in the current one. Limit to no more than two sentences.",
|
||||
"Reasoning": "Explain the rationale for the new hypothesis using specific trends or performance shifts. Be concise but technically complete. Limit to two sentences.",
|
||||
"Decision": <true or false>,
|
||||
}
|
||||
|
||||
|
||||
user: |-
|
||||
{% if sota_hypothesis %}
|
||||
# SOTA Round Information:
|
||||
Hypothesis: {{ sota_hypothesis.hypothesis }}
|
||||
Specific Task: {{ sota_task }}
|
||||
Code Implementation: {{ sota_code }}
|
||||
Result: {{ sota_result }}
|
||||
{% else %}
|
||||
# This is the first round. No previous information available. As long as the performance is not too negative (eg.ICIR is greater than 0), treat it as successful. Do not set the threshold too high.
|
||||
{% endif %}
|
||||
|
||||
# Current Round Information:
|
||||
Hypothesis: {{ hypothesis.hypothesis }}
|
||||
Why propose this hypothesis: {{ hypothesis.reason }}
|
||||
Specific Task: {{ exp.sub_tasks[0].get_task_information() }}
|
||||
Code Implementation: {{ exp.sub_workspace_list[0].file_dict.get("model.py") }}
|
||||
Training Log: {{ exp.stdout }}
|
||||
Result: {{ exp_result }}
|
||||
|
||||
# When judging the results:
|
||||
1. **Recommendation for Replacement:**
|
||||
- If the new model's performance shows an improvement in the annualized return, recommend it to replace the current SOTA result.
|
||||
- Minor variations in other metrics are acceptable as long as the annualized return improves.
|
||||
2. Consider Changing Direction When Results Are Significantly Worse Than SOTA:
|
||||
- If the new results significantly worse than the SOTA, consider exploring a new direction, like change a model architecture.
|
||||
|
||||
action_gen:
|
||||
system: |-
|
||||
Quantitative investment is a data-driven approach to asset management that relies on mathematical models, statistical techniques, and computational methods to analyze financial markets and make investment decisions. Two essential components of this approach are factors and models.
|
||||
|
||||
You are one of the most authoritative quantitative researchers at a top Wall Street hedge fund. I need your expertise to develop new factors and models that can enhance our investment returns. Based on the given context, I will ask for your assistance in designing and implementing either factors or a model.
|
||||
|
||||
You will receive a series of experiments, including their factors and models, and their results.
|
||||
Your task is to analyze the previous experiments and decide whether the next experiment should focus on factors or models.
|
||||
|
||||
Example JSON Structure for your return:
|
||||
{
|
||||
"action": "factor" or "model", # You must choose one of the two
|
||||
}
|
||||
|
||||
user: |-
|
||||
{% if hypothesis_and_feedback|length == 0 %}
|
||||
It is the first round of hypothesis generation. The user has no hypothesis on this scenario yet.
|
||||
{% else %}
|
||||
The former hypothesis and the corresponding feedbacks are as follows:
|
||||
{{ hypothesis_and_feedback }}
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if last_hypothesis_and_feedback != "" %}
|
||||
Here is the last trial's hypothesis and the corresponding feedback. The main feedback includes a new hypothesis for your reference only. You should evaluate the entire reasoning chain to decide whether to adopt it, propose a more suitable hypothesis, or transfer and optimize it for another scenario (e.g., factor/model), since transfers are generally encouraged:
|
||||
{{ last_hypothesis_and_feedback }}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Predix CLI Wrapper - Startet fin_quant mit Dashboard-Unterstützung
|
||||
|
||||
Verwendung:
|
||||
python predix.py fin_quant # Normal
|
||||
python predix.py fin_quant -d # Web Dashboard
|
||||
python predix.py fin_quant -c # CLI Dashboard
|
||||
python predix.py fin_quant -d -c # Beide
|
||||
python predix.py fin_quant --help # Hilfe
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
# Parent-Directory zum Path hinzufügen
|
||||
sys.path.insert(0, str(Path(__file__).parent))
|
||||
|
||||
# Environment laden
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv(".env")
|
||||
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
from rich.console import Console
|
||||
|
||||
console = Console()
|
||||
|
||||
def start_web_dashboard(port=5000):
|
||||
"""Starte Web Dashboard."""
|
||||
console.print(f"\n[bold green]🚀 Starting Web Dashboard on http://localhost:{port}...[/bold green]")
|
||||
console.print(f" [cyan]Open: http://localhost:{port}/dashboard.html[/cyan]\n")
|
||||
subprocess.run(
|
||||
["python", "web/dashboard_api.py"],
|
||||
cwd=str(Path(__file__).parent),
|
||||
env={**os.environ, "FLASK_ENV": "development"}
|
||||
)
|
||||
|
||||
def start_cli_dashboard():
|
||||
"""Starte CLI Dashboard."""
|
||||
from rdagent.log.ui.predix_dashboard import run_dashboard
|
||||
run_dashboard(log_path="fin_quant.log", refresh_interval=3)
|
||||
|
||||
def fin_quant(path=None, step_n=None, loop_n=None, all_duration=None, checkout=True):
|
||||
"""Starte fin_quant."""
|
||||
from rdagent.app.qlib_rd_loop.quant import main
|
||||
main(path=path, step_n=step_n, loop_n=loop_n, all_duration=all_duration, checkout=checkout)
|
||||
|
||||
def start_cli_dashboard_standalone():
|
||||
"""
|
||||
Startet CLI Dashboard in einem SEPARATEN Terminal-Fenster.
|
||||
"""
|
||||
import subprocess
|
||||
|
||||
# Dashboard Script in neuem Terminal starten
|
||||
dashboard_script = Path(__file__).parent / "rdagent" / "log" / "ui" / "predix_dashboard.py"
|
||||
|
||||
# Versuche verschiedene Terminal-Emulatoren
|
||||
terminal_commands = [
|
||||
["gnome-terminal", "--", "python", str(dashboard_script)],
|
||||
["konsole", "-e", "python", str(dashboard_script)],
|
||||
["xterm", "-e", "python", str(dashboard_script)],
|
||||
["tilix", "-e", "python", str(dashboard_script)],
|
||||
]
|
||||
|
||||
for cmd in terminal_commands:
|
||||
try:
|
||||
subprocess.Popen(cmd, start_new_session=True)
|
||||
console.print(f"[bold green]✓ Dashboard in neuem Terminal-Fenster gestartet[/bold green]")
|
||||
return True
|
||||
except FileNotFoundError:
|
||||
continue
|
||||
|
||||
console.print("[yellow]⚠ Kein unterstütztes Terminal gefunden. Verwende Web Dashboard (-d) statt CLI.[/yellow]")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Predix EURUSD Trading",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="""
|
||||
Examples:
|
||||
python predix.py fin_quant # Normal starten
|
||||
python predix.py fin_quant -d # Web Dashboard (empfohlen!)
|
||||
python predix.py fin_quant -c # CLI Dashboard (separates Terminal)
|
||||
python predix.py fin_quant -d -c # Beide Dashboards
|
||||
python predix.py fin_quant --dashboard-port 5001 # Custom Port
|
||||
"""
|
||||
)
|
||||
|
||||
subparsers = parser.add_subparsers(dest='command', help='Commands')
|
||||
|
||||
# fin_quant command
|
||||
fq_parser = subparsers.add_parser('fin_quant', help='Start EURUSD quantitative trading loop')
|
||||
fq_parser.add_argument('--path', type=str, default=None, help='Path')
|
||||
fq_parser.add_argument('--step-n', type=int, default=None, help='Number of steps')
|
||||
fq_parser.add_argument('--loop-n', type=int, default=None, help='Number of loops')
|
||||
fq_parser.add_argument('--all-duration', type=str, default=None, help='Duration')
|
||||
fq_parser.add_argument('--checkout', action='store_true', default=True, help='Checkout')
|
||||
fq_parser.add_argument('--no-checkout', action='store_false', dest='checkout', help='No checkout')
|
||||
fq_parser.add_argument('-d', '--with-dashboard', action='store_true', help='Start web dashboard')
|
||||
fq_parser.add_argument('-c', '--cli-dashboard', action='store_true', help='Start CLI dashboard in new terminal')
|
||||
fq_parser.add_argument('--dashboard-port', type=int, default=5000, help='Dashboard port')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command == 'fin_quant':
|
||||
# Start Web Dashboard wenn gewünscht
|
||||
if args.with_dashboard:
|
||||
dashboard_thread = threading.Thread(target=start_web_dashboard, args=(args.dashboard_port,), daemon=True)
|
||||
dashboard_thread.start()
|
||||
time.sleep(2)
|
||||
console.print(f"[bold green]✓ Web Dashboard gestartet: http://localhost:{args.dashboard_port}/dashboard.html[/bold green]")
|
||||
|
||||
# Start CLI Dashboard in SEPARATEM Terminal wenn gewünscht
|
||||
if args.cli_dashboard:
|
||||
start_cli_dashboard_standalone()
|
||||
time.sleep(1)
|
||||
|
||||
# Fin Quant starten
|
||||
console.print("\n[bold cyan]Starting fin_quant...[/bold cyan]\n")
|
||||
fin_quant(
|
||||
path=args.path,
|
||||
step_n=args.step_n,
|
||||
loop_n=args.loop_n,
|
||||
all_duration=args.all_duration,
|
||||
checkout=args.checkout
|
||||
)
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
+21
-8
@@ -7,7 +7,7 @@ requires = [
|
||||
|
||||
[project]
|
||||
authors = [
|
||||
{email = "xuyang1@microsoft.com", name = "MSRA-MIIC"},
|
||||
{email = "nico@predix.io", name = "Predix Team"},
|
||||
]
|
||||
classifiers = [
|
||||
"Development Status :: 3 - Alpha",
|
||||
@@ -16,18 +16,20 @@ classifiers = [
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
]
|
||||
description = "Research & Development Agent"
|
||||
description = "Predix - AI-gestützter Quantitative Trading Agent für EUR/USD"
|
||||
dynamic = [
|
||||
"dependencies",
|
||||
"optional-dependencies",
|
||||
"version",
|
||||
]
|
||||
keywords = [
|
||||
"Quantitative Trading",
|
||||
"Autonomous Agents",
|
||||
"Large Language Models",
|
||||
"Research and Development",
|
||||
"EUR/USD",
|
||||
"Forex",
|
||||
]
|
||||
name = "rdagent"
|
||||
name = "predix"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
@@ -35,8 +37,8 @@ requires-python = ">=3.10"
|
||||
rdagent = "rdagent.app.cli:app"
|
||||
|
||||
[project.urls]
|
||||
homepage = "https://github.com/microsoft/RD-Agent/"
|
||||
issue = "https://github.com/microsoft/RD-Agent/issues"
|
||||
homepage = "https://github.com/PredixAI/predix/"
|
||||
issue = "https://github.com/PredixAI/predix/issues"
|
||||
|
||||
[tool.coverage.report]
|
||||
fail_under = 80
|
||||
@@ -61,13 +63,23 @@ explicit_package_bases = true
|
||||
warn_return_any = true
|
||||
warn_unused_ignores = true
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
ignore_missing_imports = true
|
||||
module = "llama"
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
addopts = "-l -s --durations=0"
|
||||
log_cli = true
|
||||
log_cli_level = "info"
|
||||
log_date_format = "%Y-%m-%d %H:%M:%S"
|
||||
log_format = "%(asctime)s %(levelname)s %(message)s"
|
||||
markers = [
|
||||
"offline: tests that do not require external API calls",
|
||||
]
|
||||
minversion = "6.0"
|
||||
norecursedirs = [
|
||||
"workspace",
|
||||
]
|
||||
|
||||
[tool.ruff]
|
||||
fix = true
|
||||
@@ -77,10 +89,10 @@ src = ["rdagent"]
|
||||
[tool.ruff.lint]
|
||||
ignore = [
|
||||
# https://docs.astral.sh/ruff/rules/#pydocstyle-d
|
||||
"ANN101",
|
||||
"ANN401",
|
||||
"D",
|
||||
"ERA001",
|
||||
"EXE002",
|
||||
"FIX",
|
||||
"INP001",
|
||||
"PGH",
|
||||
@@ -88,7 +100,7 @@ ignore = [
|
||||
"S101",
|
||||
"S301",
|
||||
"T20",
|
||||
"TCH003",
|
||||
"TC003",
|
||||
"TD",
|
||||
]
|
||||
select = ["ALL"]
|
||||
@@ -108,6 +120,7 @@ docs = {file = ["requirements/docs.txt"]}
|
||||
lint = {file = ["requirements/lint.txt"]}
|
||||
package = {file = ["requirements/package.txt"]}
|
||||
test = {file = ["requirements/test.txt"]}
|
||||
torch = {file = ["requirements/torch.txt"]} # some agent algorithms need torch. pip install rdagent[torch]
|
||||
|
||||
[tool.setuptools_scm]
|
||||
local_scheme = "no-local-version"
|
||||
|
||||
@@ -2,7 +2,9 @@ import json
|
||||
import pickle
|
||||
from pathlib import Path
|
||||
|
||||
import fire
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import seaborn as sns
|
||||
|
||||
@@ -11,9 +13,10 @@ from rdagent.components.benchmark.eval_method import FactorImplementEval
|
||||
|
||||
|
||||
class BenchmarkAnalyzer:
|
||||
def __init__(self, settings):
|
||||
def __init__(self, settings, only_correct_format=False):
|
||||
self.settings = settings
|
||||
self.index_map = self.load_index_map()
|
||||
self.only_correct_format = only_correct_format
|
||||
|
||||
def load_index_map(self):
|
||||
index_map = {}
|
||||
@@ -42,7 +45,24 @@ class BenchmarkAnalyzer:
|
||||
final_res[experiment] = processed_data.iloc[-1, :]
|
||||
return final_res
|
||||
|
||||
def reformat_succ_rate(self, display_df):
|
||||
def reformat_index(self, display_df):
|
||||
"""
|
||||
reform the results from
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
success rate
|
||||
High_Beta_Factor 0.2
|
||||
|
||||
to
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
success rate
|
||||
Category Difficulty Factor
|
||||
量价 Hard High_Beta_Factor 0.2
|
||||
|
||||
"""
|
||||
new_idx = []
|
||||
display_df = display_df[display_df.index.isin(self.index_map.keys())]
|
||||
for idx in display_df.index:
|
||||
@@ -63,12 +83,12 @@ class BenchmarkAnalyzer:
|
||||
for i in x:
|
||||
order_v.append(
|
||||
{
|
||||
"avg. Run successful rate": 0,
|
||||
"avg. Format successful rate": 1,
|
||||
"avg. Correlation (value only)": 2,
|
||||
"max. Correlation": 3,
|
||||
"max. accuracy": 4,
|
||||
"avg. accuracy": 5,
|
||||
"Avg Run SR": 0,
|
||||
"Avg Format SR": 1,
|
||||
"Avg Correlation": 2,
|
||||
"Max Correlation": 3,
|
||||
"Max Accuracy": 4,
|
||||
"Avg Accuracy": 5,
|
||||
}.get(i, i),
|
||||
)
|
||||
return order_v
|
||||
@@ -76,11 +96,9 @@ class BenchmarkAnalyzer:
|
||||
def analyze_data(self, sum_df):
|
||||
index = [
|
||||
"FactorSingleColumnEvaluator",
|
||||
"FactorOutputFormatEvaluator",
|
||||
"FactorRowCountEvaluator",
|
||||
"FactorIndexEvaluator",
|
||||
"FactorMissingValuesEvaluator",
|
||||
"FactorEqualValueCountEvaluator",
|
||||
"FactorEqualValueRatioEvaluator",
|
||||
"FactorCorrelationEvaluator",
|
||||
"run factor error",
|
||||
]
|
||||
@@ -91,49 +109,59 @@ class BenchmarkAnalyzer:
|
||||
succ_rate = ~run_error
|
||||
succ_rate = succ_rate.mean(axis=0).to_frame("success rate")
|
||||
|
||||
succ_rate_f = self.reformat_succ_rate(succ_rate)
|
||||
succ_rate_f
|
||||
succ_rate_f = self.reformat_index(succ_rate)
|
||||
|
||||
sum_df_clean["FactorRowCountEvaluator"]
|
||||
# if it rasis Error when running the evaluator, we will get NaN
|
||||
# Running failures are reguarded to zero score.
|
||||
format_issue = sum_df_clean[["FactorRowCountEvaluator", "FactorIndexEvaluator"]].apply(
|
||||
lambda x: np.mean(x.fillna(0.0)), axis=1
|
||||
)
|
||||
format_succ_rate = format_issue.unstack().T.mean(axis=0).to_frame("success rate")
|
||||
format_succ_rate_f = self.reformat_index(format_succ_rate)
|
||||
|
||||
format_issue = sum_df_clean["FactorRowCountEvaluator"] & sum_df_clean["FactorIndexEvaluator"]
|
||||
eval_series = format_issue.unstack()
|
||||
succ_rate = eval_series.T.fillna(False).astype(bool) # false indicate failure
|
||||
format_succ_rate = succ_rate.mean(axis=0).to_frame("success rate")
|
||||
format_succ_rate_f = self.reformat_succ_rate(format_succ_rate)
|
||||
corr = sum_df_clean["FactorCorrelationEvaluator"].fillna(0.0)
|
||||
if self.only_correct_format:
|
||||
corr = corr.loc[format_issue == 1.0]
|
||||
|
||||
corr = sum_df_clean["FactorCorrelationEvaluator"] * format_issue
|
||||
corr = corr.unstack().T.mean(axis=0).to_frame("corr(only success)")
|
||||
corr_res = self.reformat_succ_rate(corr)
|
||||
corr_max = sum_df_clean["FactorCorrelationEvaluator"] * format_issue
|
||||
corr_res = corr.unstack().T.mean(axis=0).to_frame("corr(only success)")
|
||||
corr_res = self.reformat_index(corr_res)
|
||||
|
||||
corr_max = corr_max.unstack().T.max(axis=0).to_frame("corr(only success)")
|
||||
corr_max_res = self.reformat_succ_rate(corr_max)
|
||||
corr_max = corr.unstack().T.max(axis=0).to_frame("corr(only success)")
|
||||
corr_max_res = self.reformat_index(corr_max)
|
||||
|
||||
value_max = sum_df_clean["FactorMissingValuesEvaluator"] * format_issue
|
||||
value_max = sum_df_clean["FactorEqualValueRatioEvaluator"]
|
||||
value_max = value_max.unstack().T.max(axis=0).to_frame("max_value")
|
||||
value_max_res = self.reformat_succ_rate(value_max)
|
||||
value_max_res = self.reformat_index(value_max)
|
||||
|
||||
value_avg = (
|
||||
(sum_df_clean["FactorMissingValuesEvaluator"] * format_issue).unstack().T.mean(axis=0).to_frame("avg_value")
|
||||
(sum_df_clean["FactorEqualValueRatioEvaluator"] * format_issue)
|
||||
.unstack()
|
||||
.T.mean(axis=0)
|
||||
.to_frame("avg_value")
|
||||
)
|
||||
value_avg_res = self.reformat_succ_rate(value_avg)
|
||||
value_avg_res = self.reformat_index(value_avg)
|
||||
|
||||
result_all = pd.concat(
|
||||
{
|
||||
"avg. Correlation (value only)": corr_res.iloc[:, 0],
|
||||
"avg. Format successful rate": format_succ_rate_f.iloc[:, 0],
|
||||
"avg. Run successful rate": succ_rate_f.iloc[:, 0],
|
||||
"max. Correlation": corr_max_res.iloc[:, 0],
|
||||
"max. accuracy": value_max_res.iloc[:, 0],
|
||||
"avg. accuracy": value_avg_res.iloc[:, 0],
|
||||
"Avg Correlation": corr_res.iloc[:, 0],
|
||||
"Avg Format SR": format_succ_rate_f.iloc[:, 0],
|
||||
"Avg Run SR": succ_rate_f.iloc[:, 0],
|
||||
"Max Correlation": corr_max_res.iloc[:, 0],
|
||||
"Max Accuracy": value_max_res.iloc[:, 0],
|
||||
"Avg Accuracy": value_avg_res.iloc[:, 0],
|
||||
},
|
||||
axis=1,
|
||||
)
|
||||
|
||||
df = result_all.sort_index(axis=1, key=self.result_all_key_order)
|
||||
df = result_all.sort_index(axis=1, key=self.result_all_key_order).sort_index(axis=0)
|
||||
print(df)
|
||||
|
||||
print()
|
||||
print(df.groupby("Category").mean())
|
||||
|
||||
print()
|
||||
print(df.mean())
|
||||
|
||||
# Calculate the mean of each column
|
||||
mean_values = df.fillna(0.0).mean()
|
||||
mean_df = pd.DataFrame(mean_values).T
|
||||
@@ -159,25 +187,39 @@ class Plotter:
|
||||
plt.rc("figure", titlesize=font_size)
|
||||
|
||||
@staticmethod
|
||||
def plot_data(data, file_name):
|
||||
plt.figure(figsize=(10, 6))
|
||||
sns.barplot(x="index", y="b", hue="a", data=data)
|
||||
plt.xlabel("Method")
|
||||
def plot_data(data, file_name, title):
|
||||
plt.figure(figsize=(10, 10))
|
||||
plt.ylabel("Value")
|
||||
plt.title("Comparison of Different Methods")
|
||||
colors = ["#3274A1", "#E1812C", "#3A923A", "#C03D3E"]
|
||||
plt.bar(data["a"], data["b"], color=colors, capsize=5)
|
||||
for idx, row in data.iterrows():
|
||||
plt.text(idx, row["b"] + 0.01, f"{row['b']:.2f}", ha="center", va="bottom")
|
||||
plt.suptitle(title, y=0.98)
|
||||
plt.xticks(rotation=45)
|
||||
plt.ylim(0, 1)
|
||||
plt.tight_layout()
|
||||
plt.savefig(file_name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
def main(
|
||||
path="git_ignore_folder/eval_results/res_promptV220240724-060037.pkl",
|
||||
round=1,
|
||||
title="Comparison of Different Methods",
|
||||
only_correct_format=False,
|
||||
):
|
||||
settings = BenchmarkSettings()
|
||||
benchmark = BenchmarkAnalyzer(settings)
|
||||
benchmark = BenchmarkAnalyzer(settings, only_correct_format=only_correct_format)
|
||||
results = {
|
||||
"1 round experiment": "git_ignore_folder/eval_results/res_promptV220240724-060037.pkl",
|
||||
f"{round} round experiment": path,
|
||||
}
|
||||
final_results = benchmark.process_results(results)
|
||||
final_results_df = pd.DataFrame(final_results)
|
||||
|
||||
Plotter.change_fs(20)
|
||||
plot_data = final_results_df.drop(["max. accuracy", "avg. accuracy"], axis=0).T
|
||||
plot_data = final_results_df.drop(["Max Accuracy", "Avg Accuracy"], axis=0).T
|
||||
plot_data = plot_data.reset_index().melt("index", var_name="a", value_name="b")
|
||||
Plotter.plot_data(plot_data, "rdagent/app/quant_factor_benchmark/comparison_plot.png")
|
||||
Plotter.plot_data(plot_data, "./comparison_plot.png", title)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
fire.Fire(main)
|
||||
|
||||
@@ -1,16 +1,9 @@
|
||||
import os
|
||||
import pickle
|
||||
import time
|
||||
from pathlib import Path
|
||||
from pprint import pprint
|
||||
|
||||
from rdagent.app.qlib_rd_loop.conf import FACTOR_PROP_SETTING
|
||||
from rdagent.components.benchmark.conf import BenchmarkSettings
|
||||
from rdagent.components.benchmark.eval_method import FactorImplementEval
|
||||
from rdagent.core.scenario import Scenario
|
||||
from rdagent.core.utils import import_class
|
||||
from rdagent.log import rdagent_logger as logger
|
||||
from rdagent.scenarios.qlib.experiment.factor_experiment import QlibFactorScenario
|
||||
from rdagent.scenarios.qlib.factor_experiment_loader.json_loader import (
|
||||
FactorTestCaseLoaderFromJsonFile,
|
||||
)
|
||||
@@ -25,7 +18,7 @@ if __name__ == "__main__":
|
||||
# 3.declare the method to be tested and pass the arguments.
|
||||
|
||||
scen: Scenario = import_class(FACTOR_PROP_SETTING.scen)()
|
||||
generate_method = import_class(bs.bench_method_cls)(scen=scen)
|
||||
generate_method = import_class(bs.bench_method_cls)(scen=scen, **bs.bench_method_extra_kwargs)
|
||||
# 4.declare the eval method and pass the arguments.
|
||||
eval_method = FactorImplementEval(
|
||||
method=generate_method,
|
||||
@@ -36,7 +29,7 @@ if __name__ == "__main__":
|
||||
)
|
||||
|
||||
# 5.run the eval
|
||||
res = eval_method.eval()
|
||||
res = eval_method.eval(eval_method.develop())
|
||||
|
||||
# 6.save the result
|
||||
logger.log_object(res)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from pathlib import Path
|
||||
|
||||
from rdagent.components.coder.model_coder.CoSTEER import ModelCoSTEER
|
||||
from rdagent.components.coder.model_coder import ModelCoSTEER
|
||||
from rdagent.components.loader.task_loader import ModelTaskLoaderJson, ModelWsLoader
|
||||
from rdagent.scenarios.qlib.experiment.model_experiment import (
|
||||
QlibModelExperiment,
|
||||
@@ -13,7 +13,7 @@ if __name__ == "__main__":
|
||||
from rdagent.components.coder.model_coder.benchmark.eval import ModelImpValEval
|
||||
from rdagent.components.coder.model_coder.one_shot import ModelCodeWriter
|
||||
|
||||
bench_folder = DIRNAME.parent.parent / "components" / "coder" / "model_coder" / "benchmark"
|
||||
bench_folder = DIRNAME.parent.parent.parent / "components" / "coder" / "model_coder" / "benchmark"
|
||||
mtl = ModelTaskLoaderJson(str(bench_folder / "model_dict.json"))
|
||||
|
||||
task_l = mtl.load()
|
||||
|
||||
+219
-18
@@ -1,32 +1,59 @@
|
||||
"""
|
||||
CLI entrance for all rdagent application.
|
||||
|
||||
This will
|
||||
This will
|
||||
- make rdagent a nice entry and
|
||||
- autoamtically load dotenv
|
||||
"""
|
||||
import subprocess
|
||||
from importlib.resources import path as rpath
|
||||
|
||||
import fire
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from rdagent.app.data_mining.model import main as med_model
|
||||
load_dotenv(".env")
|
||||
# 1) Make sure it is at the beginning of the script so that it will load dotenv before initializing BaseSettings.
|
||||
# 2) The ".env" argument is necessary to make sure it loads `.env` from the current directory.
|
||||
|
||||
import subprocess
|
||||
from importlib.resources import path as rpath
|
||||
from typing import Optional
|
||||
|
||||
import typer
|
||||
from rich.console import Console
|
||||
from typing_extensions import Annotated
|
||||
|
||||
from rdagent.app.data_science.loop import main as data_science
|
||||
from rdagent.app.finetune.llm.loop import main as llm_finetune
|
||||
from rdagent.app.general_model.general_model import (
|
||||
extract_models_and_implement as general_model,
|
||||
)
|
||||
from rdagent.app.qlib_rd_loop.factor import main as fin_factor
|
||||
from rdagent.app.qlib_rd_loop.factor_from_report import main as fin_factor_report
|
||||
from rdagent.app.qlib_rd_loop.model import main as fin_model
|
||||
from rdagent.app.qlib_rd_loop.quant import main as fin_quant
|
||||
from rdagent.app.utils.health_check import health_check
|
||||
from rdagent.app.utils.info import collect_info
|
||||
from rdagent.log.mle_summary import grade_summary as grade_summary
|
||||
|
||||
load_dotenv()
|
||||
app = typer.Typer()
|
||||
|
||||
CheckoutOption = Annotated[bool, typer.Option("--checkout/--no-checkout", "-c/-C")]
|
||||
CheckEnvOption = Annotated[bool, typer.Option("--check-env/--no-check-env", "-e/-E")]
|
||||
CheckDockerOption = Annotated[bool, typer.Option("--check-docker/--no-check-docker", "-d/-D")]
|
||||
CheckPortsOption = Annotated[bool, typer.Option("--check-ports/--no-check-ports", "-p/-P")]
|
||||
|
||||
|
||||
def ui(port=80, log_dir="", debug=False):
|
||||
def ui(port=19899, log_dir="", debug: bool = False, data_science: bool = False):
|
||||
"""
|
||||
start web app to show the log traces.
|
||||
"""
|
||||
if data_science:
|
||||
with rpath("rdagent.log.ui", "dsapp.py") as app_path:
|
||||
cmds = ["streamlit", "run", app_path, f"--server.port={port}"]
|
||||
subprocess.run(cmds)
|
||||
return
|
||||
with rpath("rdagent.log.ui", "app.py") as app_path:
|
||||
cmds = ["streamlit", "run", app_path, f"--server.port={port}"]
|
||||
if log_dir or debug:
|
||||
@@ -38,15 +65,189 @@ def ui(port=80, log_dir="", debug=False):
|
||||
subprocess.run(cmds)
|
||||
|
||||
|
||||
def app():
|
||||
fire.Fire(
|
||||
{
|
||||
"fin_factor": fin_factor,
|
||||
"fin_factor_report": fin_factor_report,
|
||||
"fin_model": fin_model,
|
||||
"med_model": med_model,
|
||||
"general_model": general_model,
|
||||
"ui": ui,
|
||||
"collect_info": collect_info,
|
||||
}
|
||||
def server_ui(port=19899):
|
||||
"""
|
||||
start the Flask log server in real time
|
||||
"""
|
||||
from rdagent.log.server.app import main as log_server_main
|
||||
|
||||
log_server_main(port=port)
|
||||
|
||||
|
||||
def ds_user_interact(port=19900):
|
||||
"""
|
||||
start web app to show the log traces in real time
|
||||
"""
|
||||
commands = ["streamlit", "run", "rdagent/log/ui/ds_user_interact.py", f"--server.port={port}"]
|
||||
subprocess.run(commands)
|
||||
|
||||
|
||||
@app.command(name="fin_factor")
|
||||
def fin_factor_cli(
|
||||
path: Optional[str] = None,
|
||||
step_n: Optional[int] = None,
|
||||
loop_n: Optional[int] = None,
|
||||
all_duration: Optional[str] = None,
|
||||
checkout: CheckoutOption = True,
|
||||
):
|
||||
fin_factor(path=path, step_n=step_n, loop_n=loop_n, all_duration=all_duration, checkout=checkout)
|
||||
|
||||
|
||||
@app.command(name="fin_model")
|
||||
def fin_model_cli(
|
||||
path: Optional[str] = None,
|
||||
step_n: Optional[int] = None,
|
||||
loop_n: Optional[int] = None,
|
||||
all_duration: Optional[str] = None,
|
||||
checkout: CheckoutOption = True,
|
||||
):
|
||||
fin_model(path=path, step_n=step_n, loop_n=loop_n, all_duration=all_duration, checkout=checkout)
|
||||
|
||||
|
||||
@app.command(name="fin_quant")
|
||||
def fin_quant_cli(
|
||||
path: Optional[str] = None,
|
||||
step_n: Optional[int] = None,
|
||||
loop_n: Optional[int] = None,
|
||||
all_duration: Optional[str] = None,
|
||||
checkout: CheckoutOption = True,
|
||||
with_dashboard: bool = typer.Option(False, "--with-dashboard/-d", help="Start web dashboard automatically"),
|
||||
with_cli_dashboard: bool = typer.Option(False, "--cli-dashboard/-c", help="Show beautiful CLI dashboard"),
|
||||
dashboard_port: int = typer.Option(5000, "--dashboard-port", help="Dashboard port"),
|
||||
):
|
||||
"""
|
||||
Start EURUSD quantitative trading loop.
|
||||
|
||||
Options:
|
||||
--with-dashboard/-d: Start web dashboard at http://localhost:5000
|
||||
--cli-dashboard/-c: Show beautiful terminal UI with live stats
|
||||
|
||||
Examples:
|
||||
rdagent fin_quant
|
||||
rdagent fin_quant -d # Web dashboard
|
||||
rdagent fin_quant -c # CLI dashboard
|
||||
rdagent fin_quant -d -c # Both dashboards
|
||||
"""
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
|
||||
# Start Web Dashboard wenn gewünscht
|
||||
if with_dashboard:
|
||||
def start_web_dashboard():
|
||||
console = Console()
|
||||
console.print(f"\n[bold green]🚀 Starting Web Dashboard on http://localhost:{dashboard_port}...[/bold green]")
|
||||
console.print(f" [cyan]Open: http://localhost:{dashboard_port}/dashboard.html[/cyan]\n")
|
||||
subprocess.run(
|
||||
["python", "web/dashboard_api.py"],
|
||||
cwd=str(Path(__file__).parent.parent.parent),
|
||||
env={**os.environ, "FLASK_ENV": "development"}
|
||||
)
|
||||
|
||||
dashboard_thread = threading.Thread(target=start_web_dashboard, daemon=True)
|
||||
dashboard_thread.start()
|
||||
time.sleep(2)
|
||||
|
||||
# Start CLI Dashboard wenn gewünscht
|
||||
if with_cli_dashboard:
|
||||
def start_cli_dash():
|
||||
from rdagent.log.ui.predix_dashboard import run_dashboard
|
||||
run_dashboard(log_path="fin_quant.log", refresh_interval=3)
|
||||
|
||||
cli_thread = threading.Thread(target=start_cli_dash, daemon=True)
|
||||
cli_thread.start()
|
||||
time.sleep(1)
|
||||
|
||||
# Fin Quant starten
|
||||
fin_quant(path=path, step_n=step_n, loop_n=loop_n, all_duration=all_duration, checkout=checkout)
|
||||
|
||||
|
||||
@app.command(name="fin_factor_report")
|
||||
def fin_factor_report_cli(
|
||||
report_folder: Optional[str] = None,
|
||||
path: Optional[str] = None,
|
||||
all_duration: Optional[str] = None,
|
||||
checkout: CheckoutOption = True,
|
||||
):
|
||||
fin_factor_report(report_folder=report_folder, path=path, all_duration=all_duration, checkout=checkout)
|
||||
|
||||
|
||||
@app.command(name="general_model")
|
||||
def general_model_cli(report_file_path: str):
|
||||
general_model(report_file_path)
|
||||
|
||||
|
||||
@app.command(name="data_science")
|
||||
def data_science_cli(
|
||||
path: Optional[str] = None,
|
||||
checkout: CheckoutOption = True,
|
||||
step_n: Optional[int] = None,
|
||||
loop_n: Optional[int] = None,
|
||||
timeout: Optional[str] = None,
|
||||
competition: Optional[str] = None,
|
||||
):
|
||||
data_science(
|
||||
path=path,
|
||||
checkout=checkout,
|
||||
step_n=step_n,
|
||||
loop_n=loop_n,
|
||||
timeout=timeout,
|
||||
competition=competition,
|
||||
)
|
||||
|
||||
|
||||
@app.command(name="llm_finetune")
|
||||
def llm_finetune_cli(
|
||||
path: Optional[str] = None,
|
||||
checkout: CheckoutOption = True,
|
||||
benchmark: Optional[str] = None,
|
||||
benchmark_description: Optional[str] = None,
|
||||
dataset: Optional[str] = None,
|
||||
base_model: Optional[str] = None,
|
||||
upper_data_size_limit: Optional[int] = None,
|
||||
step_n: Optional[int] = None,
|
||||
loop_n: Optional[int] = None,
|
||||
timeout: Optional[str] = None,
|
||||
):
|
||||
llm_finetune(
|
||||
path=path,
|
||||
checkout=checkout,
|
||||
benchmark=benchmark,
|
||||
benchmark_description=benchmark_description,
|
||||
dataset=dataset,
|
||||
base_model=base_model,
|
||||
upper_data_size_limit=upper_data_size_limit,
|
||||
step_n=step_n,
|
||||
loop_n=loop_n,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
|
||||
@app.command(name="grade_summary")
|
||||
def grade_summary_cli(log_folder: str):
|
||||
grade_summary(log_folder)
|
||||
|
||||
|
||||
app.command(name="ui")(ui)
|
||||
app.command(name="server_ui")(server_ui)
|
||||
|
||||
|
||||
@app.command(name="health_check")
|
||||
def health_check_cli(
|
||||
check_env: CheckEnvOption = True,
|
||||
check_docker: CheckDockerOption = True,
|
||||
check_ports: CheckPortsOption = True,
|
||||
):
|
||||
health_check(check_env=check_env, check_docker=check_docker, check_ports=check_ports)
|
||||
|
||||
|
||||
@app.command(name="collect_info")
|
||||
def collect_info_cli():
|
||||
collect_info()
|
||||
|
||||
|
||||
app.command(name="ds_user_interact")(ds_user_interact)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app()
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
from pathlib import Path
|
||||
|
||||
from pydantic_settings import BaseSettings
|
||||
|
||||
from rdagent.components.workflow.conf import BasePropSetting
|
||||
|
||||
|
||||
class PropSetting(BasePropSetting):
|
||||
class Config:
|
||||
env_prefix = "DM_"
|
||||
"""Use `DM_` as prefix for environment variables"""
|
||||
protected_namespaces = ()
|
||||
"""Add 'model_' to the protected namespaces"""
|
||||
|
||||
# 1) overriding the default
|
||||
scen: str = "rdagent.scenarios.data_mining.experiment.model_experiment.DMModelScenario"
|
||||
"""Scenario class for data mining model"""
|
||||
|
||||
hypothesis_gen: str = "rdagent.scenarios.data_mining.proposal.model_proposal.DMModelHypothesisGen"
|
||||
"""Hypothesis generation class"""
|
||||
|
||||
hypothesis2experiment: str = "rdagent.scenarios.data_mining.proposal.model_proposal.DMModelHypothesis2Experiment"
|
||||
"""Hypothesis to experiment class"""
|
||||
|
||||
coder: str = "rdagent.scenarios.data_mining.developer.model_coder.DMModelCoSTEER"
|
||||
"""Coder class"""
|
||||
|
||||
runner: str = "rdagent.scenarios.data_mining.developer.model_runner.DMModelRunner"
|
||||
"""Runner class"""
|
||||
|
||||
summarizer: str = "rdagent.scenarios.data_mining.developer.feedback.DMModelHypothesisExperiment2Feedback"
|
||||
"""Summarizer class"""
|
||||
|
||||
evolving_n: int = 10
|
||||
"""Number of evolutions"""
|
||||
|
||||
evolving_n: int = 10
|
||||
|
||||
# 2) Extra config for the scenario
|
||||
# physionet account
|
||||
# NOTE: You should apply the account in https://physionet.org/
|
||||
username: str = ""
|
||||
"""Physionet account username"""
|
||||
|
||||
password: str = ""
|
||||
"""Physionet account password"""
|
||||
|
||||
|
||||
PROP_SETTING = PropSetting()
|
||||
@@ -1,31 +0,0 @@
|
||||
import fire
|
||||
|
||||
from rdagent.app.data_mining.conf import PROP_SETTING
|
||||
from rdagent.components.workflow.rd_loop import RDLoop
|
||||
from rdagent.core.exception import ModelEmptyError
|
||||
|
||||
|
||||
class ModelRDLoop(RDLoop):
|
||||
skip_loop_error = (ModelEmptyError,)
|
||||
|
||||
|
||||
def main(path=None, step_n=None):
|
||||
"""
|
||||
Auto R&D Evolving loop for models in a medical scenario.
|
||||
|
||||
You can continue running session by
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
dotenv run -- python rdagent/app/data_mining/model.py $LOG_PATH/__session__/1/0_propose --step_n 1 # `step_n` is a optional paramter
|
||||
|
||||
"""
|
||||
if path is None:
|
||||
model_loop = ModelRDLoop(PROP_SETTING)
|
||||
else:
|
||||
model_loop = ModelRDLoop.load(path)
|
||||
model_loop.run(step_n=step_n)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
fire.Fire(main)
|
||||
@@ -0,0 +1,206 @@
|
||||
from pathlib import Path
|
||||
from typing import Literal
|
||||
|
||||
from pydantic_settings import SettingsConfigDict
|
||||
|
||||
from rdagent.app.kaggle.conf import KaggleBasePropSetting
|
||||
|
||||
|
||||
class DataScienceBasePropSetting(KaggleBasePropSetting):
|
||||
# TODO: Kaggle Setting should be the subclass of DataScience
|
||||
model_config = SettingsConfigDict(env_prefix="DS_", protected_namespaces=())
|
||||
|
||||
# Main components
|
||||
## Scen
|
||||
scen: str = "rdagent.scenarios.data_science.scen.KaggleScen"
|
||||
"""
|
||||
Scenario class for data science tasks.
|
||||
- For Kaggle competitions, use: "rdagent.scenarios.data_science.scen.KaggleScen"
|
||||
- For custom data science scenarios, use: "rdagent.scenarios.data_science.scen.DataScienceScen"
|
||||
"""
|
||||
|
||||
planner: str = "rdagent.scenarios.data_science.proposal.exp_gen.planner.DSExpPlannerHandCraft"
|
||||
hypothesis_gen: str = "rdagent.scenarios.data_science.proposal.exp_gen.router.ParallelMultiTraceExpGen"
|
||||
interactor: str = "rdagent.components.interactor.SkipInteractor"
|
||||
trace_scheduler: str = "rdagent.scenarios.data_science.proposal.exp_gen.trace_scheduler.RoundRobinScheduler"
|
||||
"""Hypothesis generation class"""
|
||||
|
||||
summarizer: str = "rdagent.scenarios.data_science.dev.feedback.DSExperiment2Feedback"
|
||||
summarizer_init_kwargs: dict = {
|
||||
"version": "exp_feedback",
|
||||
}
|
||||
## Workflow Related
|
||||
consecutive_errors: int = 5
|
||||
|
||||
## Coding Related
|
||||
coding_fail_reanalyze_threshold: int = 3
|
||||
|
||||
debug_recommend_timeout: int = 600
|
||||
"""The recommend time limit for running on debugging data"""
|
||||
debug_timeout: int = 600
|
||||
"""The timeout limit for running on debugging data"""
|
||||
full_recommend_timeout: int = 3600
|
||||
"""The recommend time limit for running on full data"""
|
||||
full_timeout: int = 3600
|
||||
"""The timeout limit for running on full data"""
|
||||
|
||||
#### model dump
|
||||
enable_model_dump: bool = False
|
||||
enable_doc_dev: bool = False
|
||||
model_dump_check_level: Literal["medium", "high"] = "medium"
|
||||
|
||||
#### MCP documentation search integration
|
||||
enable_mcp_documentation_search: bool = False
|
||||
"""Enable MCP documentation search for error resolution. Requires MCP_ENABLED=true and MCP_CONTEXT7_ENABLED=true in environment."""
|
||||
|
||||
### specific feature
|
||||
|
||||
### notebook integration
|
||||
enable_notebook_conversion: bool = False
|
||||
|
||||
#### enable specification
|
||||
spec_enabled: bool = True
|
||||
|
||||
#### proposal related
|
||||
# proposal_version: str = "v2" deprecated
|
||||
|
||||
coder_on_whole_pipeline: bool = True
|
||||
max_trace_hist: int = 3
|
||||
|
||||
coder_max_loop: int = 10
|
||||
runner_max_loop: int = 3
|
||||
|
||||
sample_data_by_LLM: bool = True
|
||||
use_raw_description: bool = False
|
||||
show_nan_columns: bool = False
|
||||
|
||||
### knowledge base
|
||||
enable_knowledge_base: bool = False
|
||||
knowledge_base_version: str = "v1"
|
||||
knowledge_base_path: str | None = None
|
||||
idea_pool_json_path: str | None = None
|
||||
|
||||
### archive log folder after each loop
|
||||
enable_log_archive: bool = True
|
||||
log_archive_path: str | None = None
|
||||
log_archive_temp_path: str | None = (
|
||||
None # This is to store the mid tar file since writing the tar file is preferred in local storage then copy to target storage
|
||||
)
|
||||
|
||||
#### Evaluation on Test related
|
||||
eval_sub_dir: str = "eval" # TODO: fixme, this is not a good name
|
||||
"""We'll use f"{DS_RD_SETTING.local_data_path}/{DS_RD_SETTING.eval_sub_dir}/{competition}"
|
||||
to find the scriipt to evaluate the submission on test"""
|
||||
|
||||
"""---below are the settings for multi-trace---"""
|
||||
|
||||
### multi-trace related
|
||||
max_trace_num: int = 1
|
||||
"""The maximum number of traces to grow before merging"""
|
||||
|
||||
scheduler_temperature: float = 1.0
|
||||
"""The temperature for the trace scheduler for softmax calculation, used in ProbabilisticScheduler"""
|
||||
|
||||
# PUCT exploration constant for MCTSScheduler (ignored by other schedulers)
|
||||
scheduler_c_puct: float = 1.0
|
||||
"""Exploration constant used by MCTSScheduler (PUCT)."""
|
||||
|
||||
enable_score_reward: bool = False
|
||||
"""Enable using score-based reward for trace selection in multi-trace scheduling."""
|
||||
|
||||
#### multi-trace:checkpoint selector
|
||||
selector_name: str = "rdagent.scenarios.data_science.proposal.exp_gen.select.expand.LatestCKPSelector"
|
||||
"""The name of the selector to use"""
|
||||
sota_count_window: int = 5
|
||||
"""The number of trials to consider for SOTA count"""
|
||||
sota_count_threshold: int = 1
|
||||
"""The threshold for SOTA count"""
|
||||
|
||||
#### multi-trace: SOTA experiment selector
|
||||
sota_exp_selector_name: str = "rdagent.scenarios.data_science.proposal.exp_gen.select.submit.GlobalSOTASelector"
|
||||
"""The name of the SOTA experiment selector to use"""
|
||||
|
||||
### multi-trace:inject optimals for multi-trace
|
||||
# inject diverse when start a new sub-trace
|
||||
enable_inject_diverse: bool = False
|
||||
|
||||
# inject diverse from other traces when start a new sub-trace
|
||||
enable_cross_trace_diversity: bool = True
|
||||
"""Enable cross-trace diversity injection when starting a new sub-trace.
|
||||
This is different from `enable_inject_diverse` which is for non-parallel cases."""
|
||||
|
||||
diversity_injection_strategy: str = (
|
||||
"rdagent.scenarios.data_science.proposal.exp_gen.diversity_strategy.InjectUntilSOTAGainedStrategy"
|
||||
)
|
||||
"""The strategy to use for injecting diversity context."""
|
||||
|
||||
# enable different version of DSExpGen for multi-trace
|
||||
enable_multi_version_exp_gen: bool = False
|
||||
exp_gen_version_list: str = "v3,v2"
|
||||
|
||||
#### multi-trace: time for final multi-trace merge
|
||||
merge_hours: float = 0
|
||||
"""The time for merge"""
|
||||
|
||||
#### multi-trace: max SOTA-retrieved number, used in AutoSOTAexpSelector
|
||||
# constrains the number of SOTA experiments to retrieve, otherwise too many SOTA experiments to retrieve will cause the exceed of the context window of LLM
|
||||
max_sota_retrieved_num: int = 10
|
||||
"""The maximum number of SOTA experiments to retrieve in a LLM call"""
|
||||
|
||||
#### enable draft before first sota experiment
|
||||
enable_draft_before_first_sota: bool = False
|
||||
enable_planner: bool = False
|
||||
|
||||
model_architecture_suggestion_time_percent: float = 0.75
|
||||
allow_longer_timeout: bool = False
|
||||
coder_enable_llm_decide_longer_timeout: bool = False
|
||||
runner_enable_llm_decide_longer_timeout: bool = False
|
||||
coder_longer_timeout_multiplier_upper: int = 3
|
||||
runner_longer_timeout_multiplier_upper: int = 2
|
||||
coder_timeout_increase_stage: float = 0.3
|
||||
runner_timeout_increase_stage: float = 0.3
|
||||
runner_timeout_increase_stage_patience: int = 2
|
||||
"""Number of failures tolerated before escalating to next timeout level (stage width). Every 'patience' failures, timeout increases by 'runner_timeout_increase_stage'"""
|
||||
show_hard_limit: bool = True
|
||||
|
||||
#### enable runner code change summary
|
||||
runner_enable_code_change_summary: bool = True
|
||||
|
||||
### Proposal workflow related
|
||||
|
||||
#### Hypothesis Generate related
|
||||
enable_simple_hypothesis: bool = False
|
||||
"""If true, generate simple hypothesis, no more than 2 sentences each."""
|
||||
|
||||
enable_generate_unique_hypothesis: bool = False
|
||||
"""Enable generate unique hypothesis. If True, generate unique hypothesis for each component. If False, generate unique hypothesis for each component."""
|
||||
|
||||
enable_research_rag: bool = False
|
||||
"""Enable research RAG for hypothesis generation."""
|
||||
|
||||
#### hypothesis critique and rewrite
|
||||
enable_hypo_critique_rewrite: bool = False
|
||||
"""Enable hypothesis critique and rewrite stages for improving hypothesis quality"""
|
||||
enable_scale_check: bool = False
|
||||
|
||||
##### select related
|
||||
ratio_merge_or_ensemble: int = 70
|
||||
"""The ratio of merge or ensemble to be considered as a valid solution"""
|
||||
llm_select_hypothesis: bool = False
|
||||
"""Whether to use LLM to select hypothesis. If True, use LLM selection; if False, use the existing ranking method."""
|
||||
|
||||
#### Task Generate related
|
||||
fix_seed_and_data_split: bool = False
|
||||
|
||||
ensemble_time_upper_bound: bool = False
|
||||
|
||||
user_interaction_wait_seconds: int = 6000 # seconds to wait for user interaction
|
||||
user_interaction_mid_folder: Path = Path.cwd() / "git_ignore_folder" / "RD-Agent_user_interaction"
|
||||
|
||||
|
||||
DS_RD_SETTING = DataScienceBasePropSetting()
|
||||
|
||||
# enable_cross_trace_diversity and llm_select_hypothesis should not be true at the same time
|
||||
assert not (
|
||||
DS_RD_SETTING.enable_cross_trace_diversity and DS_RD_SETTING.llm_select_hypothesis
|
||||
), "enable_cross_trace_diversity and llm_select_hypothesis cannot be true at the same time"
|
||||
@@ -0,0 +1,6 @@
|
||||
import fire
|
||||
|
||||
from rdagent.scenarios.data_science.debug.data import create_debug_data
|
||||
|
||||
if __name__ == "__main__":
|
||||
fire.Fire(create_debug_data)
|
||||
@@ -0,0 +1,83 @@
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
import fire
|
||||
|
||||
from rdagent.app.data_science.conf import DS_RD_SETTING
|
||||
from rdagent.core.utils import import_class
|
||||
from rdagent.log import rdagent_logger as logger
|
||||
from rdagent.scenarios.data_science.loop import DataScienceRDLoop
|
||||
|
||||
|
||||
def main(
|
||||
path: Optional[str] = None,
|
||||
checkout: bool = True,
|
||||
checkout_path: Optional[str] = None,
|
||||
step_n: Optional[int] = None,
|
||||
loop_n: Optional[int] = None,
|
||||
timeout: Optional[str] = None,
|
||||
competition="bms-molecular-translation",
|
||||
replace_timer=True,
|
||||
exp_gen_cls: Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path :
|
||||
A path like `$LOG_PATH/__session__/1/0_propose`. This indicates that we restore the state after finishing step 0 in loop 1.
|
||||
checkout :
|
||||
Used to control the log session path. Boolean type, default is True.
|
||||
- If True, the new loop will use the existing folder and clear logs for sessions after the one corresponding to the given path.
|
||||
- If False, the new loop will use the existing folder but keep the logs for sessions after the one corresponding to the given path.
|
||||
checkout_path:
|
||||
If a checkout_path (or a str like Path) is provided, the new loop will be saved to that path, leaving the original path unchanged.
|
||||
step_n :
|
||||
Number of steps to run; if None, the process will run indefinitely until an error or KeyboardInterrupt occurs.
|
||||
loop_n :
|
||||
Number of loops to run; if None, the process will run indefinitely until an error or KeyboardInterrupt occurs.
|
||||
- If the current loop is incomplete, it will be counted as the first loop for completion.
|
||||
- If both step_n and loop_n are provided, the process will stop as soon as either condition is met.
|
||||
timeout :
|
||||
Maximum duration to run the loop. Accepts a string format recognized by the internal timer.
|
||||
- If None, the loop will run until completion, error, or KeyboardInterrupt.
|
||||
competition :
|
||||
Competition name.
|
||||
replace_timer :
|
||||
If a session is loaded, determines whether to replace the timer with session.timer.
|
||||
exp_gen_cls :
|
||||
When there are different stages, the exp_gen can be replaced with the new proposal.
|
||||
|
||||
|
||||
Auto R&D Evolving loop for models in a Kaggle scenario.
|
||||
You can continue running a session by using the command:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
dotenv run -- python rdagent/app/data_science/loop.py [--competition titanic] $LOG_PATH/__session__/1/0_propose --step_n 1 # `step_n` is an optional parameter
|
||||
rdagent kaggle --competition playground-series-s4e8 # This command is recommended.
|
||||
"""
|
||||
if not checkout_path is None:
|
||||
checkout = Path(checkout_path)
|
||||
|
||||
if competition is not None:
|
||||
DS_RD_SETTING.competition = competition
|
||||
|
||||
if not DS_RD_SETTING.competition:
|
||||
logger.error("Please specify competition name.")
|
||||
|
||||
if path is None:
|
||||
kaggle_loop = DataScienceRDLoop(DS_RD_SETTING)
|
||||
else:
|
||||
kaggle_loop: DataScienceRDLoop = DataScienceRDLoop.load(path, checkout=checkout, replace_timer=replace_timer)
|
||||
|
||||
# replace exp_gen if we have new class
|
||||
if exp_gen_cls is not None:
|
||||
kaggle_loop.exp_gen = import_class(exp_gen_cls)(kaggle_loop.exp_gen.scen)
|
||||
|
||||
asyncio.run(kaggle_loop.run(step_n=step_n, loop_n=loop_n, all_duration=timeout))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
fire.Fire(main)
|
||||
@@ -0,0 +1,40 @@
|
||||
import os
|
||||
|
||||
from pydantic_settings import SettingsConfigDict
|
||||
|
||||
from rdagent.app.data_science.conf import DS_RD_SETTING
|
||||
from rdagent.core.conf import RD_AGENT_SETTINGS, ExtendedBaseSettings
|
||||
|
||||
|
||||
class DSFinetuneScen(ExtendedBaseSettings):
|
||||
model_config = SettingsConfigDict(env_prefix="FT_", protected_namespaces=())
|
||||
scen: str = "rdagent.app.finetune.data_science.scen.DSFinetuneScen"
|
||||
"""
|
||||
Scenario class for data science tasks.
|
||||
- For Kaggle competitions, use: "rdagent.scenarios.data_science.scen.KaggleScen"
|
||||
- For custom data science scenarios, use: "rdagent.scenarios.data_science.scen.DataScienceScen"
|
||||
- For LLM finetune scenarios, use: "rdagent.app.finetune.llm.scen.LLMFinetuneScen"
|
||||
- For Data science finetune scenarios, use: "rdagent.app.finetune.data_science.scen.DSFinetuneScen"
|
||||
"""
|
||||
|
||||
debug_timeout: int = 3600
|
||||
"""The timeout limit for running on debugging data"""
|
||||
full_timeout: int = 10800
|
||||
"""The timeout limit for running on full data"""
|
||||
|
||||
coder_on_whole_pipeline: bool = True
|
||||
enable_model_dump: bool = True
|
||||
app_tpl: str = "app/finetune/data_science/tpl"
|
||||
|
||||
|
||||
def update_settings(competition: str):
|
||||
"""
|
||||
Update the RD_AGENT_SETTINGS with the values from DS_FINETUNE_SETTINGS.
|
||||
"""
|
||||
DS_FINETUNE_SETTINGS = DSFinetuneScen()
|
||||
RD_AGENT_SETTINGS.app_tpl = DS_FINETUNE_SETTINGS.app_tpl
|
||||
os.environ["DS_CODER_COSTEER_EXTRA_EVALUATOR"] = '["rdagent.app.finetune.share.eval.PrevModelLoadEvaluator"]'
|
||||
for field_name, new_value in DS_FINETUNE_SETTINGS.model_dump().items():
|
||||
if hasattr(DS_RD_SETTING, field_name):
|
||||
setattr(DS_RD_SETTING, field_name, new_value)
|
||||
DS_RD_SETTING.competition = competition
|
||||
@@ -0,0 +1,40 @@
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
|
||||
import fire
|
||||
|
||||
from rdagent.app.data_science.conf import DS_RD_SETTING
|
||||
from rdagent.app.finetune.data_science.conf import update_settings
|
||||
from rdagent.core.utils import import_class
|
||||
from rdagent.log import rdagent_logger as logger
|
||||
from rdagent.scenarios.data_science.loop import DataScienceRDLoop
|
||||
|
||||
|
||||
def main(
|
||||
model: str | None = None,
|
||||
competition: str | None = None,
|
||||
):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
competition :
|
||||
Competition name.
|
||||
|
||||
Auto R&D Evolving loop for models finetune.
|
||||
You can continue running a session by using the command:
|
||||
.. code-block:: bash
|
||||
dotenv run -- python rdagent/app/finetune/data_science/loop.py --competition aerial-cactus-identification
|
||||
"""
|
||||
if not competition:
|
||||
raise Exception("Please specify competition name.")
|
||||
|
||||
model_folder = Path(DS_RD_SETTING.local_data_path) / competition / "prev_model"
|
||||
if not model_folder.exists():
|
||||
raise Exception(f"Please put the model path to {model_folder}.")
|
||||
update_settings(competition)
|
||||
rd_loop: DataScienceRDLoop = DataScienceRDLoop(DS_RD_SETTING)
|
||||
asyncio.run(rd_loop.run())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
fire.Fire(main)
|
||||
@@ -0,0 +1,20 @@
|
||||
from pathlib import Path
|
||||
|
||||
from rdagent.app.data_science.conf import DS_RD_SETTING
|
||||
from rdagent.core.scenario import Scenario
|
||||
from rdagent.log import rdagent_logger as logger
|
||||
from rdagent.scenarios.data_science.scen import DataScienceScen
|
||||
from rdagent.scenarios.data_science.scen.utils import describe_data_folder_v2
|
||||
from rdagent.utils.agent.tpl import T
|
||||
|
||||
|
||||
class DSFinetuneScen(DataScienceScen):
|
||||
"""DSFinetuneScen Scenario"""
|
||||
|
||||
def _get_data_folder_description(self) -> str:
|
||||
folder_desc = describe_data_folder_v2(
|
||||
Path(DS_RD_SETTING.local_data_path) / self.competition,
|
||||
show_nan_columns=DS_RD_SETTING.show_nan_columns,
|
||||
max_length=20000, # more context for model script
|
||||
)
|
||||
return folder_desc
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
pipeline_coder:
|
||||
system: |-
|
||||
{% include "rdagent.components.coder.data_science.pipeline.prompts:pipeline_coder.system" %}
|
||||
NOTE: Ensure that base model form `{% include "scenarios.data_science.share:scen.input_path" %}prev_model` is correctly loaded, you are supposed to finetune the base model.
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
task_gen:
|
||||
system: |-
|
||||
{% include "rdagent.scenarios.data_science.proposal.exp_gen.prompts_v2:task_gen.system" %}
|
||||
NOTE: You MUST load base model form `{% include "scenarios.data_science.share:scen.input_path" %}prev_model`. Your main goal is to finetune it.
|
||||
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
# LLM Fine-tuning (FT) 场景运行指南
|
||||
|
||||
本文档介绍如何运行 RD-Agent 的 LLM Fine-tuning 场景。
|
||||
|
||||
## 简介
|
||||
|
||||
FT 场景用于自动化优化大语言模型在特定 benchmark 上的表现。系统会自动:
|
||||
1. 生成数据处理和训练代码
|
||||
2. 执行模型微调
|
||||
3. 在目标 benchmark 上评估模型性能
|
||||
4. 根据反馈迭代改进
|
||||
|
||||
## 支持的 Benchmark
|
||||
|
||||
| 类别 | Benchmark | 数据集 | 描述 |
|
||||
|------|-----------|--------|------|
|
||||
| Math | `aime24`, `aime25` | `deepscaler` | AIME 数学竞赛 |
|
||||
| Patent | `panorama_par4pc` | `panorama-par4pc` | 专利现有技术检索 |
|
||||
| Patent | `panorama_pi4pc` | `panorama-pi4pc` | 专利段落识别 |
|
||||
| Patent | `panorama_noc4pc` | `panorama-noc4pc` | 专利新颖性分类 |
|
||||
| Chemistry | `chemcotbench_mol_und` | `chemcot-mol_und` | 分子理解 |
|
||||
| Chemistry | `chemcotbench_mol_edit` | `chemcot-mol_edit` | 分子编辑 |
|
||||
| Chemistry | `chemcotbench_mol_opt` | `chemcot-mol_opt` | 分子优化 |
|
||||
| Chemistry | `chemcotbench_reaction` | `chemcot-rxn` | 化学反应预测 |
|
||||
|
||||
> 数据集配置位于 `rdagent/scenarios/finetune/datasets/__init__.py` 的 `DATASETS` 字典中。
|
||||
|
||||
>运行时agent会查看所有数据集,根据target benchmark和scenario选出与之相关的。
|
||||
|
||||
## 环境配置
|
||||
|
||||
### 1. 运行环境
|
||||
|
||||
确保已安装 `rdagent` 主运行环境,其他需要的运行环境会自动创建
|
||||
|
||||
> 在 `.env` 配置文件中通过设置 `FT_Coder_CoSTEER_env_type = conda/docker` 来配置
|
||||
|
||||
### 2. .env 配置文件
|
||||
|
||||
在项目根目录创建 `.env` 文件,参考以下模板:
|
||||
|
||||
```bash
|
||||
# ========== API Configuration ==========
|
||||
BACKEND=rdagent.oai.backend.LiteLLMAPIBackend
|
||||
CHAT_MODEL=gpt-5.2
|
||||
CHAT_TEMPERATURE=1
|
||||
CHAT_STREAM=True
|
||||
OPENAI_API_KEY=sk-xxx
|
||||
OPENAI_API_BASE=http://your-api-endpoint
|
||||
|
||||
EMBEDDING_MODEL=text-embedding-ada-002
|
||||
EMBEDDING_USE_AZURE=True
|
||||
|
||||
# ========== Global Configs ==========
|
||||
MAX_RETRY=12000
|
||||
RETRY_WAIT_SECONDS=5
|
||||
MULTI_PROC_N=16
|
||||
STEP_SEMAPHORE=1
|
||||
|
||||
# ========== Cache Settings ==========
|
||||
DUMP_CHAT_CACHE=False
|
||||
USE_CHAT_CACHE=False
|
||||
DUMP_EMBEDDING_CACHE=True
|
||||
USE_EMBEDDING_CACHE=True
|
||||
LOG_LLM_CHAT_CONTENT=True
|
||||
|
||||
CHAT_FREQUENCY_PENALTY=0.1
|
||||
CHAT_PRESENCE_PENALTY=0.0
|
||||
|
||||
# ========== FT Scenario Specific ==========
|
||||
FT_FILE_PATH=/path/to/your/finetune/workspace
|
||||
|
||||
# Environment type: docker or conda
|
||||
# Set to "conda" when Docker is unavailable
|
||||
FT_Coder_CoSTEER_env_type=conda
|
||||
|
||||
# Docker settings (only used when env_type=docker)
|
||||
FT_DOCKER_ENABLE_CACHE=True
|
||||
FT_UPDATE_LLAMA_FACTORY=False
|
||||
|
||||
# Data processing API concurrency (adjust based on target API capacity)
|
||||
FT_API_MAX_WORKERS=1000
|
||||
|
||||
# Data processing Model
|
||||
FT_STRONG_MODELS='["gpt-5", "gpt-5.1"]'
|
||||
FT_WEAK_MODELS='["gpt-4o-mini"]'
|
||||
|
||||
# Benchmark and target (can be overridden in script)
|
||||
FT_TARGET_BENCHMARK=aime25
|
||||
FT_USER_TARGET_SCENARIO="I need to enhance the model's performance on math reasoning tasks."
|
||||
|
||||
# Timeout settings
|
||||
FT_DATA_PROCESSING_TIMEOUT=28800
|
||||
|
||||
# Judge settings (optional)
|
||||
# FT_JUDGE_MODEL=gpt-5.1
|
||||
# FT_JUDGE_RETRY=10
|
||||
|
||||
REASONING_THINK_RM=True
|
||||
|
||||
# ========== Logging ==========
|
||||
LOG_FORMAT_CONSOLE="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | <cyan>{process}</cyan> | {name}:{function}:{line} - {message}"
|
||||
|
||||
# ========== HuggingFace ==========
|
||||
HF_TOKEN=hf_xxx
|
||||
```
|
||||
|
||||
## 运行方法
|
||||
|
||||
### 基本命令
|
||||
|
||||
```bash
|
||||
# 激活 conda 环境
|
||||
conda activate rdagent
|
||||
|
||||
# 运行 FT 场景
|
||||
dotenv run -- python rdagent/app/finetune/llm/loop.py --base-model <MODEL>
|
||||
```
|
||||
|
||||
### 命令行参数
|
||||
|
||||
| 参数 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `--base-model` | 基础模型名称(必需,其他都可以不填) | `Qwen/Qwen2.5-7B-Instruct` |
|
||||
| `--benchmark` | 目标 benchmark | `aime25` |
|
||||
| `--benchmark-description` | Benchmark 描述 | - |
|
||||
| `--dataset` | 指定数据集 | - |
|
||||
| `--step-n` | 步数限制 | `10` |
|
||||
| `--loop-n` | 循环次数限制 | `5` |
|
||||
| `--timeout` | 总时间限制 | - |
|
||||
|
||||
### 运行示例
|
||||
|
||||
```bash
|
||||
# 在 AIME25 上微调 Qwen2.5-7B
|
||||
dotenv run -- python rdagent/app/finetune/llm/loop.py \
|
||||
--base-model Qwen/Qwen2.5-7B-Instruct
|
||||
|
||||
# 指定 GPU 运行
|
||||
CUDA_VISIBLE_DEVICES=0,1 dotenv run -- python rdagent/app/finetune/llm/loop.py \
|
||||
--base-model Qwen/Qwen2.5-7B-Instruct
|
||||
|
||||
# 限制循环次数
|
||||
dotenv run -- python rdagent/app/finetune/llm/loop.py \
|
||||
--base-model Qwen/Qwen2.5-7B-Instruct \
|
||||
--loop-n 3
|
||||
```
|
||||
|
||||
### 多任务并行运行
|
||||
|
||||
创建 `tasks.json` 配置文件:
|
||||
```json
|
||||
{
|
||||
"tasks": [
|
||||
{"model": "Qwen/Qwen2.5-7B-Instruct", "benchmark": "aime25", "gpus": "0,1"},
|
||||
{"model": "Qwen/Qwen2.5-7B-Instruct", "benchmark": "gsm8k", "gpus": "2,3"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
使用 `run_ft_deploy.sh` 脚本运行:
|
||||
```bash
|
||||
./run_ft_deploy.sh tasks.json # 正常运行
|
||||
./run_ft_deploy.sh tasks.json --dry-run # 仅预览配置
|
||||
./run_ft_deploy.sh tasks.json --no-sync # 禁用 blob 同步
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>run_ft_deploy.sh 脚本参考</summary>
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# 多任务并行部署脚本(简化版)
|
||||
|
||||
RDAGENT_DIR="$HOME/RD-Agent"
|
||||
ENV_TEMPLATE=".env.ft"
|
||||
STAGGER_DELAY=60
|
||||
|
||||
cd "$RDAGENT_DIR"
|
||||
source ~/miniconda3/etc/profile.d/conda.sh
|
||||
conda activate rdagent
|
||||
|
||||
CONFIG_FILE="${1:-tasks.json}"
|
||||
NUM_TASKS=$(jq '.tasks | length' "$CONFIG_FILE")
|
||||
|
||||
for ((i=0; i<NUM_TASKS; i++)); do
|
||||
model=$(jq -r ".tasks[$i].model" "$CONFIG_FILE")
|
||||
benchmark=$(jq -r ".tasks[$i].benchmark" "$CONFIG_FILE")
|
||||
gpus=$(jq -r ".tasks[$i].gpus" "$CONFIG_FILE")
|
||||
|
||||
# 更新 .env 中的 benchmark
|
||||
cp "$ENV_TEMPLATE" .env
|
||||
sed -i "s|^FT_TARGET_BENCHMARK=.*|FT_TARGET_BENCHMARK=$benchmark|" .env
|
||||
|
||||
CUDA_VISIBLE_DEVICES=$gpus \
|
||||
dotenv run -- python rdagent/app/finetune/llm/loop.py --base-model "$model" &
|
||||
|
||||
# 首个任务等待环境创建,后续任务错开启动
|
||||
[[ $i -eq 0 ]] && sleep 120 || sleep $STAGGER_DELAY
|
||||
done
|
||||
|
||||
wait
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## Blob 日志同步
|
||||
|
||||
使用 Azure Blob 在多台机器间同步日志文件。
|
||||
|
||||
### 1. 生成 SAS Token
|
||||
|
||||
```bash
|
||||
# 首先登录 Azure CLI
|
||||
az login
|
||||
|
||||
# 生成 Token(默认有效期 7 天)
|
||||
bash rdagent/utils/blob/gen_token.sh
|
||||
|
||||
# 或指定过期时间
|
||||
bash rdagent/utils/blob/gen_token.sh 2025-01-31T00:00Z
|
||||
```
|
||||
|
||||
Token 会保存到 `git_ignore_folder/.az_sas_token`。
|
||||
|
||||
### 2. 同步日志
|
||||
|
||||
同步路径:`log/` ↔ `blob://epeastus/rdagent/FinetuneAgenticLLM/FT_qizheng/logs`
|
||||
|
||||
```bash
|
||||
# 上传本地日志到 Blob
|
||||
bash rdagent/utils/blob/azsync.sh up
|
||||
|
||||
# 从 Blob 下载日志到本地
|
||||
bash rdagent/utils/blob/azsync.sh down
|
||||
```
|
||||
|
||||
> 如需修改远程路径,编辑 `rdagent/utils/blob/azsync.sh` 中的 `REMOTE_PATH` 变量。
|
||||
|
||||
## 日志查看
|
||||
|
||||
运行日志保存在 `log/` 目录下:
|
||||
|
||||
```
|
||||
log/
|
||||
└── 2025-01-01_12-00-00-123456/
|
||||
├── Loop_0/
|
||||
│ ├── direct_exp_gen/ # 假设生成
|
||||
│ ├── coding/ # 代码生成
|
||||
│ ├── running/ # 训练执行
|
||||
│ └── feedback/ # 反馈总结
|
||||
└── Loop_1/
|
||||
└── ...
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
from pathlib import Path
|
||||
|
||||
from pydantic_settings import SettingsConfigDict
|
||||
|
||||
from rdagent.core.conf import ExtendedBaseSettings
|
||||
|
||||
|
||||
class LLMFinetunePropSetting(ExtendedBaseSettings):
|
||||
"""LLM Fine-tune dedicated property settings.
|
||||
|
||||
- Adjust timeouts and template
|
||||
- Use FT_ env prefix for overrides
|
||||
"""
|
||||
|
||||
model_config = SettingsConfigDict(env_prefix="FT_", protected_namespaces=())
|
||||
|
||||
# Main Components
|
||||
scen: str = "rdagent.scenarios.finetune.scen.scenario.LLMFinetuneScen"
|
||||
"""Scenario class for LLM fine-tuning tasks."""
|
||||
|
||||
hypothesis_gen: str = "rdagent.scenarios.finetune.proposal.proposal.LLMFinetuneExpGen"
|
||||
"""Hypothesis generation class for LLM fine-tuning tasks."""
|
||||
|
||||
coder: str = "rdagent.components.coder.finetune.LLMFinetuneCoSTEER"
|
||||
"""Code generator.
|
||||
Function: Generate LLM fine-tuning code based on experiment design.
|
||||
"""
|
||||
|
||||
runner: str = "rdagent.scenarios.finetune.train.runner.LLMFinetuneRunner" # TODO
|
||||
"""Code runner.
|
||||
Function: Execute LLM fine-tuning code in a Docker environment.
|
||||
"""
|
||||
|
||||
summarizer: str = "rdagent.scenarios.finetune.dev.feedback.FTExperiment2Feedback"
|
||||
"""Result summarizer - To be implemented.
|
||||
Function: Analyze fine-tuning results and generate feedback, including performance metrics and error analysis.
|
||||
"""
|
||||
|
||||
# Timeouts (longer for LLM training, all for Docker container timeout)
|
||||
full_timeout: int = 360000
|
||||
"""Full training timeout in seconds (default 100 hours, env: FT_FULL_TIMEOUT). Used in running stage for complete model training."""
|
||||
data_processing_timeout: int = 3600
|
||||
"""Data processing script timeout in seconds (default 1 hour, env: FT_DATA_PROCESSING_TIMEOUT). Used for full data processing in running stage."""
|
||||
debug_data_processing_timeout: int = 1200
|
||||
"""Debug data processing timeout in seconds (default 20 minutes, env: FT_DEBUG_DATA_PROCESSING_TIMEOUT). Used for --debug mode in coding stage."""
|
||||
micro_batch_timeout: int = 1800
|
||||
"""Micro-batch test timeout in seconds (default 30 minutes, env: FT_MICRO_BATCH_TIMEOUT)."""
|
||||
|
||||
# Pipeline behavior
|
||||
coder_on_whole_pipeline: bool = True
|
||||
app_tpl: str = "scenarios/finetune"
|
||||
|
||||
# Benchmark evaluation (always enabled as part of evaluation pipeline)
|
||||
|
||||
benchmark_timeout: int = 0
|
||||
"""Benchmark evaluation timeout in seconds. 0 means no timeout."""
|
||||
|
||||
# Judge API configuration (for llmjudge benchmarks like AIME)
|
||||
judge_model: str = "gpt-5.1"
|
||||
"""LLM judge model name for evaluation"""
|
||||
|
||||
judge_api_key: str | None = None
|
||||
"""API key for judge model (if None, will try to use from environment)"""
|
||||
|
||||
judge_api_base: str | None = None
|
||||
"""API base URL for judge model (if None, will use default)"""
|
||||
|
||||
judge_retry: int = 10
|
||||
"""Number of retries for LLM judge API calls (env: FT_JUDGE_RETRY)"""
|
||||
|
||||
benchmark_limit: int | None = None
|
||||
"""Limit number of samples for benchmark evaluation (None for full evaluation). Use for quick testing and debugging."""
|
||||
|
||||
benchmark_num_runs: int = 1
|
||||
"""Number of times to run each sample (for computing average or pass@k). Set >1 for multiple runs."""
|
||||
|
||||
benchmark_pass_k: list[int] | None = None
|
||||
"""Pass@k parameter list for code generation tasks (e.g., [1, 5, 10]). None to disable."""
|
||||
|
||||
# Data paths and processing
|
||||
file_path: Path = Path.cwd() / "git_ignore_folder" / "finetune_files"
|
||||
show_nan_columns: bool = False
|
||||
sample_data_by_LLM: bool = True
|
||||
|
||||
# LLM-specific fields
|
||||
user_target_scenario: str | None = None
|
||||
target_benchmark: str | None = None
|
||||
"""Benchmark dataset to evaluate on. Supported: aime25, aime24, mmlu, gsm8k, math, etc."""
|
||||
benchmark_description: str | None = None
|
||||
base_model: str | None = None
|
||||
dataset: str | None = None
|
||||
upper_data_size_limit: int = 2000
|
||||
|
||||
# Data processing LLM models (for API calls in data processing scripts)
|
||||
strong_models: list[str] = ["gpt-5", "gpt-5.1"]
|
||||
"""Strong models for complex tasks (CoT generation, reasoning) - supports list (env: FT_STRONG_MODELS)"""
|
||||
|
||||
weak_models: list[str] = ["gpt-4o-mini", "o4-mini", "gpt-5-mini"]
|
||||
"""Weak models for simple tasks (filtering, format conversion) - supports list (env: FT_WEAK_MODELS)"""
|
||||
|
||||
embedding_models: list[str] = ["text-embedding-3-small", "text-embedding-3-large"]
|
||||
|
||||
# Docker settings
|
||||
docker_enable_cache: bool = False
|
||||
"""Enable Docker cache for training (set via FT_DOCKER_ENABLE_CACHE)"""
|
||||
|
||||
# data sample count
|
||||
data_sample_count: int = 3
|
||||
|
||||
# API concurrency for data processing
|
||||
api_max_workers: int = 1000
|
||||
"""Max concurrent workers for LLM API calls in data processing scripts (env: FT_API_MAX_WORKERS)"""
|
||||
|
||||
# Coder settings
|
||||
coder_max_loop: int = 10
|
||||
|
||||
# CoT format settings
|
||||
force_think_token: bool = False
|
||||
"""Force <think> token wrapping for CoT training data (env: FT_FORCE_THINK_TOKEN).
|
||||
When True: Data must be wrapped in <think>...</think> format, benchmark uses extract-non-reasoning-content postprocessor.
|
||||
When False: CoT reasoning required but format is flexible, no postprocessor needed."""
|
||||
|
||||
|
||||
# Global setting instance for LLM finetuning scenario
|
||||
FT_RD_SETTING = LLMFinetunePropSetting()
|
||||
@@ -0,0 +1,131 @@
|
||||
# FT Job Runner
|
||||
|
||||
批量并行运行多个 LLM 微调任务的脚本。
|
||||
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
# 1. 准备环境配置
|
||||
cp .env.template .env
|
||||
# 编辑 .env,填入 API key 等配置
|
||||
|
||||
# 2. 准备任务配置
|
||||
cp tasks.json.example tasks.json
|
||||
# 编辑 tasks.json,定义要运行的任务
|
||||
|
||||
# 3. 运行
|
||||
./run_ft_job.sh
|
||||
```
|
||||
|
||||
## 用法
|
||||
|
||||
```bash
|
||||
./run_ft_job.sh [tasks.json]
|
||||
```
|
||||
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| `tasks.json` | 任务配置文件路径(可选,默认使用同目录下的 `tasks.json`) |
|
||||
| `-h, --help` | 显示帮助信息 |
|
||||
|
||||
### 示例
|
||||
|
||||
```bash
|
||||
# 使用默认配置
|
||||
./run_ft_job.sh
|
||||
|
||||
# 指定自定义配置文件
|
||||
./run_ft_job.sh /path/to/my_tasks.json
|
||||
```
|
||||
|
||||
## 配置文件
|
||||
|
||||
### tasks.json
|
||||
|
||||
定义要并行运行的任务列表:
|
||||
|
||||
```json
|
||||
{
|
||||
"tasks": [
|
||||
{
|
||||
"model": "Qwen/Qwen3-8B",
|
||||
"benchmark": "aime25",
|
||||
"gpus": "0,1"
|
||||
},
|
||||
{
|
||||
"model": "Qwen/Qwen3-8B",
|
||||
"benchmark": "gsm8k",
|
||||
"gpus": "2,3",
|
||||
"scenario": "自定义优化目标"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 必填 | 默认值 | 说明 |
|
||||
|------|:----:|--------|------|
|
||||
| `model` | ✅ | - | HuggingFace 模型路径 |
|
||||
| `benchmark` | ✅ | - | 评估基准(如 `aime25`, `gsm8k`) |
|
||||
| `gpus` | ❌ | `"0"` | 使用的 GPU 编号 |
|
||||
| `scenario` | ❌ | `"Improve model performance on {benchmark}"` | 优化目标描述 |
|
||||
|
||||
### .env
|
||||
|
||||
环境配置文件,包含 API 密钥、模型设置等。从 `.env.template` 复制并修改:
|
||||
|
||||
```bash
|
||||
cp .env.template .env
|
||||
```
|
||||
|
||||
主要配置项:
|
||||
|
||||
| 配置 | 说明 |
|
||||
|------|------|
|
||||
| `OPENAI_API_KEY` | OpenAI API 密钥 |
|
||||
| `OPENAI_API_BASE` | API 地址 |
|
||||
| `FT_Coder_CoSTEER_env_type` | 环境类型:`docker` 或 `conda` |
|
||||
| `HF_TOKEN` | HuggingFace Token |
|
||||
|
||||
## 输出
|
||||
|
||||
运行后会在 `log/` 目录下创建 job 文件夹:
|
||||
|
||||
```
|
||||
log/2025-12-23/
|
||||
├── aime25_Qwen3-8B.log # 任务日志
|
||||
├── gsm8k_Qwen3-8B.log
|
||||
└── aime25_Qwen3-8B/ # 任务 trace(Loop 数据)
|
||||
├── Loop_0/
|
||||
└── ...
|
||||
```
|
||||
|
||||
## 监控
|
||||
|
||||
### 命令行
|
||||
|
||||
```bash
|
||||
# 查看所有任务日志
|
||||
tail -f log/2025-12-23/*.log
|
||||
|
||||
# 查看特定任务
|
||||
tail -f log/2025-12-23/aime25_Qwen3-8B.log
|
||||
```
|
||||
|
||||
### Web UI
|
||||
|
||||
```bash
|
||||
streamlit run rdagent/app/finetune/llm/ui/app.py
|
||||
```
|
||||
|
||||
在 UI 中选择 Job Folder 为对应的日志目录即可查看运行状态。
|
||||
|
||||
## 依赖
|
||||
|
||||
- `jq`:JSON 解析工具
|
||||
- `conda` 环境:`rdagent`
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. 任务启动间隔默认为 60 秒(`STAGGER_DELAY`),避免同时启动造成资源竞争
|
||||
2. 确保指定的 GPU 编号不冲突
|
||||
3. 如果同一天多次运行,会自动创建 `log/2025-12-23_1/`、`log/2025-12-23_2/` 等目录
|
||||
Executable
+186
@@ -0,0 +1,186 @@
|
||||
#!/bin/bash
|
||||
# Run multiple FT tasks in parallel under a single job directory
|
||||
#
|
||||
# Usage: ./run_ft_job.sh [tasks.json]
|
||||
#
|
||||
# Config format (tasks.json):
|
||||
# {
|
||||
# "tasks": [
|
||||
# {"model": "Qwen/Qwen3-8B", "benchmark": "aime25", "gpus": "0,1"},
|
||||
# {"model": "Qwen/Qwen3-8B", "benchmark": "gsm8k", "gpus": "2,3"}
|
||||
# ]
|
||||
# }
|
||||
|
||||
set -e
|
||||
|
||||
# ========== CONFIG ==========
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
RDAGENT_DIR="$(cd "$SCRIPT_DIR/../../../../.." && pwd)"
|
||||
ENV_FILE="$SCRIPT_DIR/.env"
|
||||
SCENARIOS_FILE="$SCRIPT_DIR/scenarios.json"
|
||||
STAGGER_DELAY=60
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 [tasks.json]"
|
||||
echo "Run multiple FT tasks under a single job directory."
|
||||
echo "UI: streamlit run rdagent/app/finetune/llm/ui/app.py"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# ========== PARSE ARGS ==========
|
||||
CONFIG_FILE=""
|
||||
|
||||
for arg in "$@"; do
|
||||
case $arg in
|
||||
-h|--help) usage ;;
|
||||
*) [[ -z "$CONFIG_FILE" ]] && CONFIG_FILE="$arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -z "$CONFIG_FILE" ]] && CONFIG_FILE="$SCRIPT_DIR/tasks.json"
|
||||
[[ ! -f "$CONFIG_FILE" ]] && echo "Error: Config not found: $CONFIG_FILE" && exit 1
|
||||
|
||||
# Check .env file
|
||||
if [[ ! -f "$ENV_FILE" ]]; then
|
||||
echo "Error: .env not found at $ENV_FILE"
|
||||
echo "Please create it from template: cp $SCRIPT_DIR/.env.template $ENV_FILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check jq
|
||||
command -v jq &>/dev/null || { echo "Error: jq required"; exit 1; }
|
||||
|
||||
# ========== SETUP ==========
|
||||
# Get log and workspace base paths from environment or use defaults
|
||||
# Default to project-relative paths; can be overridden by environment variables
|
||||
FT_LOG_BASE="${FT_LOG_BASE:-$RDAGENT_DIR/log}"
|
||||
FT_WORKSPACE_BASE="${FT_WORKSPACE_BASE:-$RDAGENT_DIR/git_ignore_folder/RD-Agent_workspace}"
|
||||
|
||||
JOB_ID=$(date +%Y-%m-%d_%H-%M)
|
||||
JOB_DIR="$FT_LOG_BASE/$JOB_ID"
|
||||
if [[ -d "$JOB_DIR" ]]; then
|
||||
i=1; while [[ -d "${JOB_DIR}_$i" ]]; do ((i++)); done
|
||||
JOB_ID="${JOB_ID}_$i"; JOB_DIR="${JOB_DIR}_$i"
|
||||
fi
|
||||
mkdir -p "$JOB_DIR"
|
||||
|
||||
cd "$RDAGENT_DIR"
|
||||
|
||||
NUM_TASKS=$(jq '.tasks | length' "$CONFIG_FILE")
|
||||
|
||||
echo "=============================================="
|
||||
echo "FT Job: $JOB_ID"
|
||||
echo "=============================================="
|
||||
echo "Config: $CONFIG_FILE"
|
||||
echo "Tasks: $NUM_TASKS"
|
||||
echo "Log: $JOB_DIR"
|
||||
echo "Workspace: $FT_WORKSPACE_BASE/$JOB_ID"
|
||||
echo ""
|
||||
|
||||
# Setup tmux session
|
||||
TMUX_SESSION="rdagent"
|
||||
tmux kill-session -t "$TMUX_SESSION" 2>/dev/null || true
|
||||
tmux new-session -d -s "$TMUX_SESSION" -n "main"
|
||||
echo "Tmux session created: $TMUX_SESSION"
|
||||
echo ""
|
||||
|
||||
for ((i=0; i<NUM_TASKS; i++)); do
|
||||
model=$(jq -r ".tasks[$i].model" "$CONFIG_FILE")
|
||||
benchmark=$(jq -r ".tasks[$i].benchmark" "$CONFIG_FILE")
|
||||
gpus=$(jq -r ".tasks[$i].gpus // \"0\"" "$CONFIG_FILE")
|
||||
port=$(jq -r ".tasks[$i].port // empty" "$CONFIG_FILE")
|
||||
task_timeout=$(jq -r ".tasks[$i].timeout // \"12h\"" "$CONFIG_FILE")
|
||||
|
||||
# Load benchmark_description: tasks.json -> scenarios.json
|
||||
benchmark_desc=$(jq -r ".tasks[$i].benchmark_description // empty" "$CONFIG_FILE")
|
||||
if [[ -z "$benchmark_desc" ]]; then
|
||||
benchmark_desc=$(jq -r ".[\"$benchmark\"].benchmark_description // empty" "$SCENARIOS_FILE")
|
||||
fi
|
||||
# Note: Special characters in benchmark_desc are handled by writing to env file
|
||||
model_name=$(basename "$model")
|
||||
task_name="${benchmark}_${model_name}"
|
||||
trace_path="$JOB_DIR/$task_name"
|
||||
|
||||
port_info=""
|
||||
[[ -n "$port" ]] && port_info=", port=$port"
|
||||
echo "Task $i: $task_name (model=$model, benchmark=$benchmark, gpus=$gpus$port_info)"
|
||||
|
||||
# Run task in tmux window with script -c for output capture
|
||||
task_workspace="$FT_WORKSPACE_BASE/$JOB_ID/$task_name"
|
||||
mkdir -p "$task_workspace"
|
||||
LOG_FILE="$JOB_DIR/${task_name}.log"
|
||||
|
||||
# Write task-specific env file (avoids command-line escaping issues with special chars)
|
||||
TASK_ENV_FILE="$task_workspace/.task_env"
|
||||
cat > "$TASK_ENV_FILE" << EOF
|
||||
CUDA_VISIBLE_DEVICES='$gpus'
|
||||
LOG_TRACE_PATH='$trace_path'
|
||||
WORKSPACE_PATH='$task_workspace'
|
||||
FT_TARGET_BENCHMARK='$benchmark'
|
||||
EOF
|
||||
# Escape shell special characters for double-quoted string: \ " ` $
|
||||
if [[ -n "$benchmark_desc" ]]; then
|
||||
escaped_desc="$benchmark_desc"
|
||||
escaped_desc="${escaped_desc//\\/\\\\}" # \ -> \\
|
||||
escaped_desc="${escaped_desc//\"/\\\"}" # " -> \"
|
||||
escaped_desc="${escaped_desc//\`/\\\`}" # ` -> \`
|
||||
escaped_desc="${escaped_desc//\$/\\\$}" # $ -> \$
|
||||
echo "FT_BENCHMARK_DESCRIPTION=\"$escaped_desc\"" >> "$TASK_ENV_FILE"
|
||||
fi
|
||||
[[ -n "$port" ]] && echo "OPENAI_API_BASE='http://localhost:$port'" >> "$TASK_ENV_FILE"
|
||||
|
||||
# Create tmux window for this task and get its full target (e.g., rdagent:1.0)
|
||||
# Use "session:" format to ensure window is created in the correct session
|
||||
WIN_TARGET=$(tmux new-window -t "$TMUX_SESSION:" -n "$benchmark" -P)
|
||||
|
||||
# Build the command with environment setup (env vars loaded from file)
|
||||
timeout_arg=""
|
||||
[[ -n "$task_timeout" ]] && timeout_arg="--timeout $task_timeout"
|
||||
|
||||
TASK_CMD="source ~/miniconda3/etc/profile.d/conda.sh && conda activate qz_rdagent"
|
||||
TASK_CMD="$TASK_CMD && set -a && source '$ENV_FILE' && source '$TASK_ENV_FILE' && set +a"
|
||||
TASK_CMD="$TASK_CMD && cd '$RDAGENT_DIR'"
|
||||
TASK_CMD="$TASK_CMD && python rdagent/app/finetune/llm/loop.py --base-model '$model' $timeout_arg"
|
||||
|
||||
# Run with script -c to capture terminal output (using full target for reliability)
|
||||
tmux send-keys -t "$WIN_TARGET" "script -q '$LOG_FILE' -c \"$TASK_CMD\"" Enter
|
||||
|
||||
echo " Window: $benchmark"
|
||||
echo ""
|
||||
|
||||
# Stagger starts
|
||||
if [[ $i -eq 0 ]]; then
|
||||
# First task: wait for initialization
|
||||
# Get FT_FILE_PATH from .env or use default
|
||||
FT_FILE_PATH=$(grep -E "^FT_FILE_PATH=" "$ENV_FILE" | cut -d= -f2 | tr -d '"' || echo "")
|
||||
[[ -z "$FT_FILE_PATH" ]] && FT_FILE_PATH="$RDAGENT_DIR/git_ignore_folder/finetune"
|
||||
DATASET_INFO="$FT_FILE_PATH/datasets/dataset_info.json"
|
||||
|
||||
echo " Waiting for scenario initialization (dataset_info.json)..."
|
||||
while [[ ! -f "$DATASET_INFO" ]]; do
|
||||
sleep 5
|
||||
done
|
||||
echo " Scenario initialized!"
|
||||
|
||||
echo " Waiting for llm_finetune conda env..."
|
||||
while ! conda run -n llm_finetune python -c "import requests" 2>/dev/null; do
|
||||
sleep 10
|
||||
done
|
||||
|
||||
echo " Waiting for opencompass conda env..."
|
||||
while ! conda run -n opencompass python -c "import opencompass" 2>/dev/null; do
|
||||
sleep 10
|
||||
done
|
||||
echo " Environment ready!"
|
||||
elif [[ $i -lt $((NUM_TASKS - 1)) ]]; then
|
||||
sleep $STAGGER_DELAY
|
||||
fi
|
||||
done
|
||||
|
||||
echo "=============================================="
|
||||
echo "All tasks started in tmux session: $TMUX_SESSION"
|
||||
echo " - Attach: tmux attach -t $TMUX_SESSION"
|
||||
echo " - List: tmux list-windows -t $TMUX_SESSION"
|
||||
echo " - Select: tmux select-window -t $TMUX_SESSION:{window_name}"
|
||||
echo "Monitor: tail -f $JOB_DIR/*.log"
|
||||
echo "UI: streamlit run rdagent/app/finetune/llm/ui/app.py (Job Folder: $JOB_DIR)"
|
||||
@@ -0,0 +1,128 @@
|
||||
{
|
||||
"_comment": "Benchmark scenarios for FT tasks. Used by run_ft_job.sh and UI.",
|
||||
|
||||
"aime24": {
|
||||
"category": "math",
|
||||
"scenario": "Improve the model's ability to solve advanced competition math problems through multi-step reasoning, including number theory, combinatorics, geometry, and algebraic manipulation, with answers expressed as integers from 0 to 999.",
|
||||
"benchmark_description": "AIME 2024 (American Invitational Mathematics Examination) - Advanced high school math competition problems requiring creative problem-solving. Each answer is an integer 0-999. Topics include number theory, algebra, geometry, trigonometry, probability, and combinatorics. Problems require multi-step reasoning and often have elegant solutions. Expected Output Format: Put final answer within \\boxed{}, e.g., \\boxed{42}."
|
||||
},
|
||||
"aime25": {
|
||||
"category": "math",
|
||||
"scenario": "Improve the model's ability to solve advanced competition math problems through multi-step reasoning, including number theory, combinatorics, geometry, and algebraic manipulation, with answers expressed as integers from 0 to 999.",
|
||||
"benchmark_description": "AIME 2025 (American Invitational Mathematics Examination) - Advanced high school math competition problems requiring creative problem-solving. Each answer is an integer 0-999. Topics include number theory, algebra, geometry, trigonometry, probability, and combinatorics. Problems require multi-step reasoning and often have elegant solutions. Expected Output Format: Put final answer within \\boxed{}, e.g., \\boxed{42}."
|
||||
},
|
||||
"panorama": {
|
||||
"category": "patent",
|
||||
"scenario": "Improve the model's patent examination capabilities including prior art retrieval, paragraph identification, and novelty/obviousness classification based on USPTO standards.",
|
||||
"benchmark_description": "PANORAMA tests patent examination capabilities based on real USPTO Office Actions. Tasks include: retrieving relevant prior art patents, identifying specific paragraphs in prior art that relate to claims, and classifying claims as allowable, lacking novelty (102), or obvious (103). Requires understanding patent law, technical document analysis, and legal reasoning. Expected Output Format: Return JSON with task-specific format (see subtask descriptions)."
|
||||
},
|
||||
"panorama_par4pc": {
|
||||
"category": "patent",
|
||||
"scenario": "Improve the model's ability to retrieve relevant prior art patents given a patent claim, by understanding claim scope, identifying technical similarities, and ranking patents by relevance for rejection analysis.",
|
||||
"benchmark_description": "PAR4PC (Prior Art Retrieval for Patent Claims) - Given a patent claim, retrieve the most relevant prior art patents from a candidate pool. Requires understanding claim scope, identifying technical similarities, and ranking patents by relevance for potential 35 USC 102/103 rejections. Expected Output Format: Return JSON: {\"answer\": \"A\"} for single patent or {\"answer\": [\"A\", \"C\"]} for multiple patents (codes A-H)."
|
||||
},
|
||||
"panorama_pi4pc": {
|
||||
"category": "patent",
|
||||
"scenario": "Improve the model's ability to identify specific paragraphs in prior art patents that are most relevant for evaluating a claim's novelty and obviousness through element-by-element analysis.",
|
||||
"benchmark_description": "PI4PC (Paragraph Identification for Patent Claims) - Given a patent claim and cited prior art patent, identify specific paragraphs most relevant for evaluating novelty and obviousness. Requires detailed technical reading, element-by-element claim analysis, and understanding how prior art teachings map to claim limitations. Expected Output Format: Return JSON: {\"answer\": \"<paragraph_id>\"}."
|
||||
},
|
||||
"panorama_noc4pc": {
|
||||
"category": "patent",
|
||||
"scenario": "Improve the model's ability to classify patent claims as allowable, anticipated, or obvious by applying patent law standards to analyze claim limitations against prior art.",
|
||||
"benchmark_description": "NOC4PC (Novelty/Obviousness Classification) - Classify patent claims as ALLOW (patentable), 102 (anticipated/lacks novelty), or 103 (obvious). Requires applying patent law standards: 102 when single reference discloses all elements, 103 when combination of references with motivation makes claim obvious to skilled artisan. Expected Output Format: Return JSON: {\"code\": \"ALLOW\"} or {\"code\": \"102\"} or {\"code\": \"103\"}."
|
||||
},
|
||||
"panorama_par4pc_cot": {
|
||||
"category": "patent",
|
||||
"scenario": "Improve the model's ability to retrieve relevant prior art patents while providing explicit chain-of-thought reasoning explaining which claim elements each patent teaches and how it supports a rejection.",
|
||||
"benchmark_description": "PAR4PC with chain-of-thought - Retrieve relevant prior art while providing explicit reasoning. Explain why each retrieved patent is relevant: which claim elements it teaches, what technical problems it addresses, and how it could support a rejection. Expected Output Format: Provide reasoning first, then return JSON: {\"answer\": \"A\"} or {\"answer\": [\"A\", \"C\"]}."
|
||||
},
|
||||
"panorama_pi4pc_cot": {
|
||||
"category": "patent",
|
||||
"scenario": "Improve the model's ability to identify relevant prior art paragraphs while providing element-by-element mapping showing how specific paragraph teachings correspond to claim limitations.",
|
||||
"benchmark_description": "PI4PC with chain-of-thought - Identify relevant prior art paragraphs while explaining the technical connections. Provide element-by-element mapping showing how specific paragraph teachings correspond to claim limitations. Expected Output Format: Provide reasoning first, then return JSON: {\"answer\": \"<paragraph_id>\"}."
|
||||
},
|
||||
"panorama_noc4pc_cot": {
|
||||
"category": "patent",
|
||||
"scenario": "Improve the model's ability to classify patent claims with examiner-style rationale, explaining how references anticipate limitations or how combinations with motivation render claims obvious.",
|
||||
"benchmark_description": "NOC4PC with chain-of-thought - Classify claims with examiner-style rationale. For 102: explain how reference anticipates each limitation. For 103: identify references, explain motivation to combine, and show how combination renders claim obvious. Use proper USPTO citation format. Expected Output Format: Return JSON: {\"reason\": \"<Office Action analysis>\", \"code\": \"ALLOW\"|\"102\"|\"103\"}."
|
||||
},
|
||||
|
||||
"chemcotbench": {
|
||||
"category": "chemistry",
|
||||
"scenario": "Improve the model's chemical reasoning capabilities on molecular structures including understanding molecular features, editing molecules, optimizing properties, and predicting reaction outcomes.",
|
||||
"benchmark_description": "ChemCoTBench tests step-wise chemical reasoning on SMILES molecular structures. Tasks include molecule understanding (identify functional groups, ring systems), molecule editing (add/delete/substitute groups while maintaining validity), molecule optimization (modify for desired properties), and reaction prediction (products, mechanisms, conditions). Contains subtasks with different output requirements. Expected Output Format: Return JSON: {\"output\": \"<answer>\"} where answer format depends on subtask - SMILES string for molecular tasks, numeric count for counting tasks, or Yes/No for equivalence tasks."
|
||||
},
|
||||
"chemcotbench_mol_und": {
|
||||
"category": "chemistry",
|
||||
"scenario": "Improve the model's ability to analyze molecular structures and identify structural features including functional groups (hydroxyl, carboxyl, amine), ring systems (aromatic, aliphatic), and molecular scaffolds.",
|
||||
"benchmark_description": "Molecule Understanding - Analyze SMILES strings for structural features. Subtasks: (1) fg_count/ring_count: return integer count, (2) equivalence/ring_system_scaffold: return Yes or No, (3) Murcko_scaffold: return SMILES string. Requires parsing SMILES notation and applying organic chemistry knowledge. Expected Output Format: Return JSON: {\"output\": \"<answer>\"} where answer is integer/Yes/No/SMILES depending on subtask."
|
||||
},
|
||||
"chemcotbench_mol_edit": {
|
||||
"category": "chemistry",
|
||||
"scenario": "Improve the model's ability to perform precise structural modifications on molecules (add, delete, substitute functional groups) while maintaining chemical validity and molecule integrity.",
|
||||
"benchmark_description": "Molecule Editing - Perform structural modifications on SMILES. Subtasks: add (add functional group), delete (remove group), sub (substitute group). Output must be valid SMILES representing chemically feasible molecules. Expected Output Format: Return JSON: {\"output\": \"<valid SMILES>\"}. SMILES validity is verified using RDKit."
|
||||
},
|
||||
"chemcotbench_mol_opt": {
|
||||
"category": "chemistry",
|
||||
"scenario": "Improve the model's ability to modify molecular structures to achieve target properties such as improved solubility, drug-likeness, or binding affinity to specific biological targets.",
|
||||
"benchmark_description": "Molecule Optimization - Modify structures to achieve target properties. Subtasks: drd/gsk/jnk (binding affinity to DRD2/GSK3β/JNK3 targets), logp (lipophilicity), qed (drug-likeness), solubility. Requires understanding structure-property relationships. Expected Output Format: Return JSON: {\"output\": \"<optimized SMILES>\"}."
|
||||
},
|
||||
"chemcotbench_reaction": {
|
||||
"category": "chemistry",
|
||||
"scenario": "Improve the model's ability to predict chemical reaction outcomes including forward synthesis, retrosynthesis, mechanism selection, and reaction conditions based on functional group transformations.",
|
||||
"benchmark_description": "Reaction Prediction - Predict reaction outcomes. Subtasks: fs (forward synthesis: reactants→products), retro (retrosynthesis: products→reactants), rcr (reaction condition recommendation), nepp (named reaction prediction), mechsel (mechanism selection). Requires understanding reaction types and functional group transformations. Expected Output Format: Return JSON: {\"output\": \"<SMILES or text answer>\"}."
|
||||
},
|
||||
|
||||
"tablebench_data_analysis": {
|
||||
"category": "table_qa",
|
||||
"scenario": "Improve the model's ability to analyze tabular data for complex questions including trend identification, correlation analysis, statistical computation, and data-driven forecasting.",
|
||||
"benchmark_description": "Table Data Analysis - Analyze tabular data to answer complex questions. Subtask types with different evaluation: (1) CorrelationAnalysis/TrendForecasting/StatisticalAnalysis: numeric answers with ±10% relative error tolerance, (2) ImpactAnalysis: exact match required, (3) Other analysis types: evaluated using ROUGE-L. Requires reading tables accurately and applying analytical reasoning. Expected Output Format: End response with \"Final Answer: <value>\"."
|
||||
},
|
||||
"tablebench_fact_checking": {
|
||||
"category": "table_qa",
|
||||
"scenario": "Improve the model's ability to verify factual claims against tabular data through accurate data extraction, implicit relationship understanding, and multi-hop reasoning across table cells.",
|
||||
"benchmark_description": "Table Fact Checking - Answer table-based factual questions accurately. Questions may ask for specific information (numbers, names, dates) or verification (Yes/No, True/False). Uses Exact Match evaluation. Expected Output Format: End response with \"Final Answer: <value>\" where value is the precise answer to the question."
|
||||
},
|
||||
"tablebench_numerical_reasoning": {
|
||||
"category": "table_qa",
|
||||
"scenario": "Improve the model's ability to perform mathematical operations on table data including arithmetic, aggregations (sum, average, count), comparisons, percentages, and multi-step calculations.",
|
||||
"benchmark_description": "Table Numerical Reasoning - Perform mathematical operations on table data: arithmetic (sum, difference, product), aggregations (average, count, max/min), comparisons, percentages, and multi-step calculations. Requires accurate number extraction and correct mathematical computation. Expected Output Format: End response with \"Final Answer: <numeric value>\"."
|
||||
},
|
||||
"tablebench_visualization": {
|
||||
"category": "table_qa",
|
||||
"scenario": "Improve the model's ability to generate Python code that creates appropriate visualizations (bar, line, pie, scatter charts) from tabular data with correct chart type selection and data mapping.",
|
||||
"benchmark_description": "Table Visualization - Generate Python code to create appropriate visualizations from tabular data: bar charts, line charts, pie charts, scatter plots. Select correct chart type for data, map columns correctly to axes, and produce executable matplotlib/pandas code. Expected Output Format: Return Python code in ```python code block using matplotlib/pandas. Code will be executed to verify correctness."
|
||||
},
|
||||
"tablebench_gen": {
|
||||
"category": "table_qa",
|
||||
"scenario": "Improve the model's overall table question answering capabilities across fact checking, numerical reasoning, data analysis, and visualization by understanding table structure and generating accurate answers.",
|
||||
"benchmark_description": "TableBench General - Comprehensive table QA covering fact checking, numerical reasoning, data analysis, and visualization. Questions require understanding table structure, extracting relevant data, performing reasoning or computation, and generating accurate answers or code. Expected Output Format: End response with \"Final Answer: <answer>\"."
|
||||
},
|
||||
|
||||
"FinanceIQ_gen": {
|
||||
"category": "finance",
|
||||
"scenario": "Improve the model's financial domain knowledge and reasoning capabilities across Chinese financial certification exams including CPA, banking, securities, fund, futures, insurance, tax, and actuarial qualifications through multiple-choice question answering.",
|
||||
"benchmark_description": "FinanceIQ tests financial domain knowledge through multiple-choice questions (A/B/C/D). Covers 10 Chinese financial certification exams: CPA (注册会计师), banking qualification, securities qualification, fund qualification, futures qualification, insurance qualification (CICE), tax advisor, economist, financial planner, and actuary. Uses LLM Judge for evaluation with 5-shot in-context learning. Evaluation metric: accuracy."
|
||||
},
|
||||
|
||||
"bioprobench_gen": {
|
||||
"category": "biology",
|
||||
"scenario": "Improve the model's ability to generate complete, detailed experimental protocol steps from research context, including specific reagent concentrations, temperatures, incubation times, and equipment settings.",
|
||||
"benchmark_description": "Protocol Generation - Generate complete experimental protocol steps given research context and objectives. Output detailed, actionable instructions: specify reagent concentrations, temperatures, incubation times, equipment settings. Protocols must be scientifically valid and reproducible. Expected Output Format: Wrap protocol steps in [ANSWER_START]Step 1: ... Step 2: ...[ANSWER_END]. Evaluated using BLEU, ROUGE, and step matching metrics."
|
||||
},
|
||||
"bioprobench_ord": {
|
||||
"category": "biology",
|
||||
"scenario": "Improve the model's ability to arrange shuffled experimental steps in correct sequence. Output MUST be a valid Python list format: [ANSWER_START][0, 2, 1, 3][ANSWER_END]. Use brackets and commas, NOT space-separated indices.",
|
||||
"benchmark_description": "Step Ordering - Arrange shuffled experimental procedure steps in correct logical and temporal sequence. Requires understanding procedural dependencies: which steps must precede others, timing constraints, and scientific logic of experimental workflows. CRITICAL OUTPUT FORMAT: Answer MUST be a valid Python list with brackets and commas, e.g., [ANSWER_START][2, 0, 1, 3][ANSWER_END]. NOT space-separated (0 2 1 3 is WRONG), NOT without brackets (0, 2, 1, 3 is WRONG). Evaluated using Exact Match and Kendall's Tau."
|
||||
},
|
||||
"bioprobench_err": {
|
||||
"category": "biology",
|
||||
"scenario": "Improve the model's ability to identify errors in biological protocol text. CRITICAL SEMANTICS: True = step is CORRECT (no errors), False = step HAS ERRORS. This matches the benchmark prompt: 'If you find anything wrong, answer False.' Output format: [ANSWER_START]True or False[ANSWER_END].",
|
||||
"benchmark_description": "Error Correction - Identify errors in biological protocol text including incorrect temperatures, concentrations, reagents, or procedural mistakes. CRITICAL: The benchmark expects True if the protocol step is CORRECT (no errors), and False if it HAS ERRORS. This follows the prompt: 'If you find anything wrong, answer False.' Do NOT invert this logic. Expected Output Format: [ANSWER_START]True[ANSWER_END] for correct steps, [ANSWER_START]False[ANSWER_END] for erroneous steps."
|
||||
},
|
||||
"bioprobench_pqa": {
|
||||
"category": "biology",
|
||||
"scenario": "Improve the model's ability to extract specific factual information from experimental protocols including temperatures, concentrations, incubation times, reagent quantities, and procedural details.",
|
||||
"benchmark_description": "Protocol QA - Extract specific factual information from experimental protocols: temperatures, concentrations, incubation times, reagent quantities, equipment specifications, and procedural details. Requires careful reading and accurate information extraction from technical text. Expected Output Format: Return [ANSWER_START]<answer text> & <confidence 0-100>%[ANSWER_END], e.g., [ANSWER_START]Option A & 95%[ANSWER_END]. Evaluated using accuracy and Brier Score."
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"tasks": [
|
||||
{
|
||||
"model": "Qwen/Qwen3-8B",
|
||||
"benchmark": "aime25",
|
||||
"gpus": "0,1"
|
||||
},
|
||||
{
|
||||
"model": "Qwen/Qwen3-8B",
|
||||
"benchmark": "gsm8k",
|
||||
"gpus": "2,3"
|
||||
},
|
||||
{
|
||||
"model": "meta-llama/Llama-3-8B",
|
||||
"benchmark": "aime25",
|
||||
"gpus": "4,5",
|
||||
"scenario": "Improve AIME 2025 math reasoning with custom approach"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
"""
|
||||
LLM Fine-tuning Entry Point
|
||||
|
||||
Standard RDLoop entry point for LLM fine-tuning, consistent with data science implementation.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from typing import Optional, cast
|
||||
|
||||
import fire
|
||||
|
||||
from rdagent.app.finetune.llm.conf import FT_RD_SETTING
|
||||
from rdagent.log import rdagent_logger as logger
|
||||
from rdagent.scenarios.finetune.loop import LLMFinetuneRDLoop
|
||||
|
||||
|
||||
def main(
|
||||
path: Optional[str] = None,
|
||||
checkout: bool = True,
|
||||
user_target_scenario: Optional[str] = None,
|
||||
benchmark: Optional[str] = None,
|
||||
benchmark_description: Optional[str] = None,
|
||||
dataset: Optional[str] = None,
|
||||
base_model: Optional[str] = None,
|
||||
upper_data_size_limit: Optional[int] = None,
|
||||
step_n: Optional[int] = None,
|
||||
loop_n: Optional[int] = None,
|
||||
timeout: Optional[str] = None,
|
||||
):
|
||||
"""
|
||||
LLM fine-tuning entry point
|
||||
|
||||
Parameters
|
||||
----------
|
||||
path :
|
||||
A path like `$LOG_PATH/__session__/1/0_propose`. This indicates that we restore the state after finishing step 0 in loop 1.
|
||||
checkout :
|
||||
Used to control the log session path. Boolean type, default is True.
|
||||
- If True, the new loop will use the existing folder and clear logs for sessions after the one corresponding to the given path.
|
||||
- If False, the new loop will use the existing folder but keep the logs for sessions after the one corresponding to the given path.
|
||||
dataset : str
|
||||
Dataset name for fine-tuning (e.g., 'shibing624/alpaca-zh')
|
||||
base_model : str, optional
|
||||
Model name for fine-tuning (e.g., 'Qwen/Qwen2.5-1.5B-Instruct').
|
||||
If not provided, auto-selects optimal model based on hardware and dataset.
|
||||
step_n : int, optional
|
||||
Number of steps to run; if None, runs indefinitely until completion or error
|
||||
loop_n : int, optional
|
||||
Number of loops to run; if None, runs indefinitely until completion or error
|
||||
timeout : str, optional
|
||||
Maximum duration for the entire process
|
||||
|
||||
Examples:
|
||||
.. code-block:: bash
|
||||
dotenv run -- python rdagent/app/finetune/llm/loop.py --dataset shibing624/alpaca-zh --base-model Qwen/Qwen2.5-1.5B-Instruct
|
||||
dotenv run -- python rdagent/app/finetune/llm/loop.py --dataset shibing624/alpaca-zh # TODO: not enabled yet
|
||||
"""
|
||||
|
||||
if user_target_scenario:
|
||||
FT_RD_SETTING.user_target_scenario = user_target_scenario
|
||||
assert (
|
||||
FT_RD_SETTING.user_target_scenario is None
|
||||
), "user_target_scenario is not yet supported, please specify via benchmark and benchmark_description"
|
||||
if upper_data_size_limit:
|
||||
FT_RD_SETTING.upper_data_size_limit = upper_data_size_limit
|
||||
logger.info(f"Set upper_data_size_limit to {FT_RD_SETTING.upper_data_size_limit}")
|
||||
if benchmark and benchmark_description:
|
||||
FT_RD_SETTING.target_benchmark = benchmark
|
||||
FT_RD_SETTING.benchmark_description = benchmark_description
|
||||
assert FT_RD_SETTING.user_target_scenario or (
|
||||
FT_RD_SETTING.target_benchmark and FT_RD_SETTING.benchmark_description
|
||||
), "Either user_target_scenario or target_benchmark must be specified for LLM fine-tuning."
|
||||
|
||||
# Update configuration with provided parameters
|
||||
if dataset:
|
||||
FT_RD_SETTING.dataset = dataset
|
||||
if base_model:
|
||||
FT_RD_SETTING.base_model = base_model
|
||||
|
||||
# Create and run LLM fine-tuning loop
|
||||
data_set_target = FT_RD_SETTING.dataset if FT_RD_SETTING.dataset else "auto generated dataset"
|
||||
model_target = FT_RD_SETTING.base_model if FT_RD_SETTING.base_model else "auto selected model"
|
||||
|
||||
# Temporary assertion until auto-selection is implemented
|
||||
assert (
|
||||
FT_RD_SETTING.base_model is not None
|
||||
), "Base model auto selection not yet supported, please specify via --base-model"
|
||||
|
||||
logger.info(f"Starting LLM fine-tuning on dataset='{data_set_target}' with model='{model_target}'")
|
||||
|
||||
if path is None:
|
||||
loop = LLMFinetuneRDLoop(FT_RD_SETTING)
|
||||
else:
|
||||
loop = cast(LLMFinetuneRDLoop, LLMFinetuneRDLoop.load(str(path), checkout=checkout))
|
||||
|
||||
asyncio.run(loop.run(step_n=step_n, loop_n=loop_n, all_duration=timeout))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
fire.Fire(main)
|
||||
@@ -0,0 +1 @@
|
||||
# FT (Fine-tune) scenario UI
|
||||
@@ -0,0 +1,207 @@
|
||||
"""
|
||||
FT (Fine-tune) Timeline Viewer
|
||||
Hierarchical view: Session > Loop > Stage > EvoLoop > Events
|
||||
|
||||
Run:
|
||||
streamlit run rdagent/app/finetune/llm/ui/app.py
|
||||
"""
|
||||
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
import streamlit as st
|
||||
from streamlit import session_state as state
|
||||
|
||||
from rdagent.app.finetune.llm.ui.benchmarks import get_core_metric_score
|
||||
from rdagent.app.finetune.llm.ui.components import render_session, render_summary
|
||||
from rdagent.app.finetune.llm.ui.config import ALWAYS_VISIBLE_TYPES, OPTIONAL_TYPES
|
||||
from rdagent.app.finetune.llm.ui.data_loader import (
|
||||
get_summary,
|
||||
get_valid_sessions,
|
||||
load_ft_session,
|
||||
)
|
||||
from rdagent.app.finetune.llm.ui.ft_summary import render_job_summary
|
||||
|
||||
DEFAULT_LOG_BASE = "log/"
|
||||
|
||||
|
||||
def get_job_options(base_path: Path) -> list[str]:
|
||||
"""
|
||||
Scan directory and return job options list.
|
||||
- "." means standalone tasks in root directory
|
||||
- Others are job directory names
|
||||
"""
|
||||
options = []
|
||||
has_root_tasks = False
|
||||
job_dirs = []
|
||||
|
||||
if not base_path.exists():
|
||||
return options
|
||||
|
||||
for d in base_path.iterdir():
|
||||
if not d.is_dir():
|
||||
continue
|
||||
# Check if standalone task (has __session__ directly)
|
||||
if (d / "__session__").exists():
|
||||
has_root_tasks = True
|
||||
# Check if job directory (subdirs have __session__)
|
||||
else:
|
||||
try:
|
||||
if any((sub / "__session__").exists() for sub in d.iterdir() if sub.is_dir()):
|
||||
job_dirs.append(d.name)
|
||||
except PermissionError:
|
||||
pass
|
||||
|
||||
# Sort job dirs by name descending (newest first, since names are date-based)
|
||||
job_dirs.sort(reverse=True)
|
||||
|
||||
# Add job dirs first, then root tasks at the end
|
||||
options.extend(job_dirs)
|
||||
if has_root_tasks:
|
||||
options.append(". (Current)")
|
||||
|
||||
return options
|
||||
|
||||
|
||||
def main():
|
||||
st.set_page_config(layout="wide", page_title="FT Timeline", page_icon="🔬")
|
||||
|
||||
# ========== Sidebar ==========
|
||||
with st.sidebar:
|
||||
# View mode selection
|
||||
view_mode = st.radio("View Mode", ["Job Summary", "Single Task"], horizontal=True)
|
||||
|
||||
st.divider()
|
||||
|
||||
default_log = os.environ.get("FT_LOG_PATH", DEFAULT_LOG_BASE)
|
||||
job_folder = default_log # Initialize for both modes
|
||||
selected_types = ALWAYS_VISIBLE_TYPES.copy() # Initialize for both modes
|
||||
is_root_job = False # Track if viewing root tasks
|
||||
|
||||
if view_mode == "Job Summary":
|
||||
# Job Summary mode
|
||||
st.header("Job")
|
||||
base_folder = st.text_input("Base Folder", value=default_log, key="base_folder_input")
|
||||
base_path = Path(base_folder)
|
||||
|
||||
job_options = get_job_options(base_path)
|
||||
if job_options:
|
||||
selected_job = st.selectbox("Select Job", job_options, key="job_select")
|
||||
if selected_job.startswith("."):
|
||||
job_folder = base_folder
|
||||
is_root_job = True
|
||||
else:
|
||||
job_folder = str(base_path / selected_job)
|
||||
# Save to session_state for Single Task mode
|
||||
state.selected_job_folder = job_folder
|
||||
else:
|
||||
st.warning("No jobs found in this directory")
|
||||
job_folder = base_folder
|
||||
|
||||
if st.button("Refresh", type="primary", key="refresh_job"):
|
||||
st.rerun()
|
||||
else:
|
||||
# Single Task mode
|
||||
st.header("Session")
|
||||
# Use job_folder from Job Summary mode if available
|
||||
default_path = getattr(state, "selected_job_folder", default_log)
|
||||
log_folder = st.text_input("Log Folder", value=default_path)
|
||||
log_path = Path(log_folder)
|
||||
|
||||
sessions = get_valid_sessions(log_path)
|
||||
if not sessions:
|
||||
st.warning("No valid sessions found")
|
||||
return
|
||||
|
||||
selected_session = st.selectbox("Session", sessions)
|
||||
|
||||
if st.button("Load", type="primary") or "session" not in state:
|
||||
with st.spinner("Loading..."):
|
||||
state.session = load_ft_session(log_path / selected_session)
|
||||
state.session_name = selected_session
|
||||
|
||||
st.divider()
|
||||
|
||||
# Optional type toggles
|
||||
st.subheader("Show More")
|
||||
selected_types = ALWAYS_VISIBLE_TYPES.copy()
|
||||
for event_type, (label, default) in OPTIONAL_TYPES.items():
|
||||
if st.toggle(label, value=default, key=f"toggle_{event_type}"):
|
||||
selected_types.append(event_type)
|
||||
|
||||
st.divider()
|
||||
|
||||
# Display options
|
||||
st.subheader("Display Options")
|
||||
state.render_markdown = st.toggle("Render Prompts", value=False, key="render_markdown_toggle")
|
||||
|
||||
st.divider()
|
||||
|
||||
# Summary in sidebar
|
||||
if "session" in state:
|
||||
summary = get_summary(state.session)
|
||||
st.subheader("Summary")
|
||||
st.metric("Loops", summary.get("loop_count", 0))
|
||||
st.metric("LLM Calls", summary.get("llm_call_count", 0))
|
||||
success = summary.get("docker_success", 0)
|
||||
fail = summary.get("docker_fail", 0)
|
||||
st.metric("Docker", f"{success}✓ / {fail}✗")
|
||||
|
||||
# ========== Main Content ==========
|
||||
if view_mode == "Job Summary":
|
||||
st.title("📊 FT Job Summary")
|
||||
job_path = Path(job_folder)
|
||||
if job_path.exists():
|
||||
render_job_summary(job_path, is_root=is_root_job)
|
||||
else:
|
||||
st.warning(f"Job folder not found: {job_folder}")
|
||||
return
|
||||
|
||||
# Single Task mode
|
||||
st.title("🔬 FT Timeline Viewer")
|
||||
|
||||
if "session" not in state:
|
||||
st.info("Select a session and click **Load** to view")
|
||||
return
|
||||
|
||||
session = state.session
|
||||
summary = get_summary(session)
|
||||
|
||||
# Global info header (Base Model, Datasets, Benchmark) - compact style
|
||||
scenario_event = next((e for e in session.init_events if e.type == "scenario"), None)
|
||||
dataset_event = next((e for e in session.init_events if e.type == "dataset_selection"), None)
|
||||
|
||||
if scenario_event or dataset_event:
|
||||
if scenario_event and hasattr(scenario_event.content, "base_model"):
|
||||
st.markdown(f"🧠 **Model:** `{scenario_event.content.base_model}`")
|
||||
if dataset_event:
|
||||
selected = (
|
||||
dataset_event.content.get("selected_datasets", []) if isinstance(dataset_event.content, dict) else []
|
||||
)
|
||||
if selected:
|
||||
st.markdown(f"📂 **Datasets:** `{', '.join(selected)}`")
|
||||
if scenario_event and hasattr(scenario_event.content, "target_benchmark"):
|
||||
st.markdown(f"🎯 **Benchmark:** `{scenario_event.content.target_benchmark}`")
|
||||
# Display baseline benchmark score
|
||||
if scenario_event and hasattr(scenario_event.content, "baseline_benchmark_score"):
|
||||
baseline = scenario_event.content.baseline_benchmark_score
|
||||
if baseline and isinstance(baseline, dict):
|
||||
benchmark_name = getattr(scenario_event.content, "target_benchmark", "")
|
||||
accuracy_summary = baseline.get("accuracy_summary", {})
|
||||
if accuracy_summary:
|
||||
result = get_core_metric_score(benchmark_name, accuracy_summary)
|
||||
if result:
|
||||
metric_name, score, _ = result
|
||||
st.markdown(f"📊 **Baseline:** `{metric_name} = {score:.1f}`")
|
||||
|
||||
# Summary bar
|
||||
render_summary(summary)
|
||||
|
||||
st.divider()
|
||||
|
||||
# Hierarchical view
|
||||
render_session(session, selected_types)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,70 @@
|
||||
"""Benchmark processors for core metric extraction.
|
||||
|
||||
Each benchmark has its own processor that knows how to extract
|
||||
the core metric name and value from accuracy_summary data.
|
||||
"""
|
||||
|
||||
from .bioprobench import BioProBenchProcessor
|
||||
from .chemcotbench import ChemCotBenchProcessor
|
||||
from .financeiq import FinanceIQProcessor
|
||||
from .panorama import PanoramaProcessor
|
||||
from .tablebench import TableBenchProcessor
|
||||
|
||||
PROCESSORS = [
|
||||
FinanceIQProcessor,
|
||||
PanoramaProcessor,
|
||||
ChemCotBenchProcessor,
|
||||
TableBenchProcessor,
|
||||
BioProBenchProcessor,
|
||||
]
|
||||
|
||||
|
||||
def get_core_metric_score(benchmark_name: str, accuracy_summary: dict) -> tuple[str, float, bool] | None:
|
||||
"""Get core metric name, score, and direction for a benchmark.
|
||||
|
||||
Args:
|
||||
benchmark_name: The benchmark name (e.g., "FinanceIQ", "panorama_par4pc")
|
||||
accuracy_summary: {dataset_name: {metric: value, ...}, ...}
|
||||
|
||||
Returns:
|
||||
(metric_name, value, higher_is_better) or None
|
||||
- metric_name: includes "(average)" suffix if multiple datasets are averaged
|
||||
- value: the score
|
||||
- higher_is_better: True if higher values are better (use ↑), False otherwise (use ↓)
|
||||
"""
|
||||
for processor in PROCESSORS:
|
||||
if processor.match(benchmark_name):
|
||||
return processor.get_core_metric(accuracy_summary)
|
||||
|
||||
# Default fallback: use first numeric value with "accuracy" label
|
||||
scores = []
|
||||
for ds, metrics in accuracy_summary.items():
|
||||
if not isinstance(metrics, dict):
|
||||
continue
|
||||
if "accuracy" in metrics:
|
||||
scores.append(float(metrics["accuracy"]))
|
||||
else:
|
||||
for v in metrics.values():
|
||||
if isinstance(v, (int, float)):
|
||||
scores.append(float(v))
|
||||
break
|
||||
|
||||
if not scores:
|
||||
return None
|
||||
|
||||
avg = sum(scores) / len(scores)
|
||||
if len(scores) == 1:
|
||||
return ("accuracy", avg, True) # higher is better
|
||||
else:
|
||||
return ("accuracy (average)", avg, True) # higher is better
|
||||
|
||||
|
||||
__all__ = [
|
||||
"get_core_metric_score",
|
||||
"PROCESSORS",
|
||||
"FinanceIQProcessor",
|
||||
"PanoramaProcessor",
|
||||
"ChemCotBenchProcessor",
|
||||
"TableBenchProcessor",
|
||||
"BioProBenchProcessor",
|
||||
]
|
||||
@@ -0,0 +1,53 @@
|
||||
"""Base class for benchmark core metric extraction."""
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
|
||||
class BenchmarkProcessor(ABC):
|
||||
"""Base class for benchmark core metric extraction."""
|
||||
|
||||
# Metrics where higher values are better (default assumption)
|
||||
# Override in subclass if needed
|
||||
HIGHER_IS_BETTER: set[str] = {
|
||||
"accuracy",
|
||||
"exact_match",
|
||||
"f1",
|
||||
"f1_score",
|
||||
"macro_f1",
|
||||
"correct_rate",
|
||||
"success_rate",
|
||||
"gold_hit_rate",
|
||||
"score",
|
||||
"scaffold_hard",
|
||||
"kendall_tau",
|
||||
"ROUGE-L",
|
||||
}
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def match(cls, benchmark_name: str) -> bool:
|
||||
"""Check if this processor handles the given benchmark."""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def get_core_metric(cls, accuracy_summary: dict) -> tuple[str, float, bool] | None:
|
||||
"""Extract core metric name, value, and direction from accuracy_summary.
|
||||
|
||||
Args:
|
||||
accuracy_summary: {dataset_name: {metric: value, ...}, ...}
|
||||
|
||||
Returns:
|
||||
(metric_name, value, higher_is_better) or None
|
||||
- metric_name: includes "(average)" suffix if multiple datasets
|
||||
- value: the score
|
||||
- higher_is_better: True if higher values are better, False otherwise
|
||||
"""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def is_higher_better(cls, metric_name: str) -> bool:
|
||||
"""Check if higher values are better for this metric."""
|
||||
# Remove (average) suffix for checking
|
||||
base_metric = metric_name.replace(" (average)", "").strip()
|
||||
return base_metric.lower() in {m.lower() for m in cls.HIGHER_IS_BETTER}
|
||||
@@ -0,0 +1,60 @@
|
||||
"""BioProBench benchmark processor."""
|
||||
|
||||
from .base import BenchmarkProcessor
|
||||
|
||||
|
||||
class BioProBenchProcessor(BenchmarkProcessor):
|
||||
"""BioProBench: Biology protocol benchmark with different task types."""
|
||||
|
||||
CORE_METRICS = {
|
||||
"pqa": "accuracy",
|
||||
"ord": "kendall_tau",
|
||||
"err": "f1",
|
||||
"gen": "ROUGE-L",
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def match(cls, benchmark_name: str) -> bool:
|
||||
return "bioprobench" in benchmark_name.lower()
|
||||
|
||||
@classmethod
|
||||
def get_core_metric(cls, accuracy_summary: dict) -> tuple[str, float, bool] | None:
|
||||
scores = []
|
||||
metrics_used = []
|
||||
|
||||
for ds, metrics in accuracy_summary.items():
|
||||
if not isinstance(metrics, dict):
|
||||
continue
|
||||
ds_lower = ds.lower()
|
||||
# Find matching core metric
|
||||
core_metric = "accuracy" # fallback
|
||||
for pattern, metric in cls.CORE_METRICS.items():
|
||||
if pattern in ds_lower:
|
||||
core_metric = metric
|
||||
break
|
||||
|
||||
if core_metric in metrics:
|
||||
scores.append(float(metrics[core_metric]))
|
||||
metrics_used.append(core_metric)
|
||||
elif core_metric.lower() in [k.lower() for k in metrics.keys()]:
|
||||
# Case-insensitive fallback for metrics like "ROUGE-L"
|
||||
for k, v in metrics.items():
|
||||
if k.lower() == core_metric.lower():
|
||||
scores.append(float(v))
|
||||
metrics_used.append(core_metric)
|
||||
break
|
||||
|
||||
if not scores:
|
||||
return None
|
||||
|
||||
avg = sum(scores) / len(scores)
|
||||
unique = list(set(metrics_used))
|
||||
|
||||
if len(scores) == 1:
|
||||
metric_name = unique[0]
|
||||
elif len(unique) == 1:
|
||||
metric_name = f"{unique[0]} (average)"
|
||||
else:
|
||||
metric_name = "mixed (average)"
|
||||
|
||||
return (metric_name, avg, cls.is_higher_better(metric_name))
|
||||
@@ -0,0 +1,105 @@
|
||||
"""ChemCotBench benchmark processor."""
|
||||
|
||||
from .base import BenchmarkProcessor
|
||||
|
||||
|
||||
class ChemCotBenchProcessor(BenchmarkProcessor):
|
||||
"""ChemCotBench: Chemistry reasoning with various subtasks.
|
||||
|
||||
All metrics are 0-100 percentages, enabling unified averaging within each subset.
|
||||
"""
|
||||
|
||||
# Define core metric field names for each task
|
||||
CORE_METRICS = {
|
||||
# Molecular understanding
|
||||
"mol_und_fg_count": "accuracy",
|
||||
"mol_und_ring_count": "accuracy",
|
||||
"mol_und_murcko_scaffold": "scaffold_hard", # Exact match rate (0-100%)
|
||||
"mol_und_ring_system_scaffold": "score", # "Yes" ratio (0-100%)
|
||||
"mol_und_equivalence": "accuracy",
|
||||
# Molecular editing
|
||||
"mol_edit_add": "correct_rate",
|
||||
"mol_edit_delete": "correct_rate",
|
||||
"mol_edit_sub": "correct_rate",
|
||||
# Molecular optimization (prefix match)
|
||||
"mol_opt_": "success_rate",
|
||||
# Reaction tasks - unified to exact_match
|
||||
"reaction_fs": "exact_match",
|
||||
"reaction_retro": "exact_match",
|
||||
"reaction_nepp": "exact_match",
|
||||
"reaction_rcr": "exact_match",
|
||||
"reaction_mechsel": "exact_match", # Will fallback to accuracy if exact_match not found
|
||||
}
|
||||
|
||||
# Metric groups: unified display names for each subset
|
||||
METRIC_GROUPS = {
|
||||
"mol_und": "accuracy", # mol_und subset displays as accuracy
|
||||
"mol_edit": "correct_rate",
|
||||
"mol_opt": "success_rate",
|
||||
"reaction": "exact_match", # reaction subset displays as exact_match
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def match(cls, benchmark_name: str) -> bool:
|
||||
return "chemcot" in benchmark_name.lower()
|
||||
|
||||
@classmethod
|
||||
def get_core_metric(cls, accuracy_summary: dict) -> tuple[str, float, bool] | None:
|
||||
scores = []
|
||||
group_detected = None
|
||||
|
||||
for ds, metrics in accuracy_summary.items():
|
||||
if not isinstance(metrics, dict):
|
||||
continue
|
||||
ds_lower = ds.lower()
|
||||
|
||||
# Detect subset type
|
||||
for group in cls.METRIC_GROUPS:
|
||||
if group in ds_lower:
|
||||
group_detected = group
|
||||
break
|
||||
|
||||
# Find matching core metric
|
||||
core_metric = "accuracy" # fallback
|
||||
for pattern, metric in cls.CORE_METRICS.items():
|
||||
# Prefix match for patterns ending with _
|
||||
if pattern.endswith("_"):
|
||||
if pattern in ds_lower:
|
||||
core_metric = metric
|
||||
break
|
||||
else:
|
||||
if pattern in ds_lower:
|
||||
core_metric = metric
|
||||
break
|
||||
|
||||
# Try to get metric value with fallback support
|
||||
value = None
|
||||
if core_metric in metrics:
|
||||
value = float(metrics[core_metric])
|
||||
elif core_metric == "exact_match" and "accuracy" in metrics:
|
||||
# reaction_mechsel fallback: exact_match -> accuracy
|
||||
value = float(metrics["accuracy"])
|
||||
|
||||
if value is not None:
|
||||
scores.append(value)
|
||||
|
||||
if not scores:
|
||||
return None
|
||||
|
||||
avg = sum(scores) / len(scores)
|
||||
|
||||
# Use unified metric name for the detected subset
|
||||
if group_detected and group_detected in cls.METRIC_GROUPS:
|
||||
unified_name = cls.METRIC_GROUPS[group_detected]
|
||||
if len(scores) == 1:
|
||||
metric_name = unified_name
|
||||
else:
|
||||
metric_name = f"{unified_name} (average)"
|
||||
else:
|
||||
# Fallback for unknown subsets
|
||||
if len(scores) == 1:
|
||||
metric_name = "accuracy"
|
||||
else:
|
||||
metric_name = "accuracy (average)"
|
||||
|
||||
return (metric_name, avg, cls.is_higher_better(metric_name))
|
||||
@@ -0,0 +1,29 @@
|
||||
"""FinanceIQ benchmark processor."""
|
||||
|
||||
from .base import BenchmarkProcessor
|
||||
|
||||
|
||||
class FinanceIQProcessor(BenchmarkProcessor):
|
||||
"""FinanceIQ: 10 exam subjects, all use accuracy."""
|
||||
|
||||
@classmethod
|
||||
def match(cls, benchmark_name: str) -> bool:
|
||||
return "financeiq" in benchmark_name.lower()
|
||||
|
||||
@classmethod
|
||||
def get_core_metric(cls, accuracy_summary: dict) -> tuple[str, float, bool] | None:
|
||||
scores = []
|
||||
for ds, metrics in accuracy_summary.items():
|
||||
if not isinstance(metrics, dict):
|
||||
continue
|
||||
if "accuracy" in metrics:
|
||||
scores.append(float(metrics["accuracy"]))
|
||||
|
||||
if not scores:
|
||||
return None
|
||||
|
||||
avg = sum(scores) / len(scores)
|
||||
if len(scores) == 1:
|
||||
return ("accuracy", avg, True) # higher is better
|
||||
else:
|
||||
return ("accuracy (average)", avg, True) # higher is better
|
||||
@@ -0,0 +1,52 @@
|
||||
"""Panorama benchmark processor."""
|
||||
|
||||
from .base import BenchmarkProcessor
|
||||
|
||||
|
||||
class PanoramaProcessor(BenchmarkProcessor):
|
||||
"""Panorama: Different sub-datasets use different metrics."""
|
||||
|
||||
CORE_METRICS = {
|
||||
"par4pc": "macro_f1",
|
||||
"pi4pc": "gold_hit_rate",
|
||||
"noc4pc": "macro_f1",
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def match(cls, benchmark_name: str) -> bool:
|
||||
return "panorama" in benchmark_name.lower()
|
||||
|
||||
@classmethod
|
||||
def get_core_metric(cls, accuracy_summary: dict) -> tuple[str, float, bool] | None:
|
||||
scores = []
|
||||
metrics_used = []
|
||||
|
||||
for ds, metrics in accuracy_summary.items():
|
||||
if not isinstance(metrics, dict):
|
||||
continue
|
||||
ds_lower = ds.lower()
|
||||
# Find matching core metric
|
||||
core_metric = "accuracy" # fallback
|
||||
for pattern, metric in cls.CORE_METRICS.items():
|
||||
if pattern in ds_lower:
|
||||
core_metric = metric
|
||||
break
|
||||
|
||||
if core_metric in metrics:
|
||||
scores.append(float(metrics[core_metric]))
|
||||
metrics_used.append(core_metric)
|
||||
|
||||
if not scores:
|
||||
return None
|
||||
|
||||
avg = sum(scores) / len(scores)
|
||||
unique = list(set(metrics_used))
|
||||
|
||||
if len(scores) == 1:
|
||||
metric_name = unique[0]
|
||||
elif len(unique) == 1:
|
||||
metric_name = f"{unique[0]} (average)"
|
||||
else:
|
||||
metric_name = "mixed (average)"
|
||||
|
||||
return (metric_name, avg, cls.is_higher_better(metric_name))
|
||||
@@ -0,0 +1,60 @@
|
||||
"""TableBench benchmark processor."""
|
||||
|
||||
from .base import BenchmarkProcessor
|
||||
|
||||
|
||||
class TableBenchProcessor(BenchmarkProcessor):
|
||||
"""TableBench: Table QA with different subtasks."""
|
||||
|
||||
CORE_METRICS = {
|
||||
"fact": "accuracy",
|
||||
"numerical": "accuracy",
|
||||
"analysis": "accuracy",
|
||||
"visualization": "Pass@1", # TableBench visualization uses Pass@1 as core metric
|
||||
}
|
||||
|
||||
# TableBench-specific metrics where higher is better
|
||||
HIGHER_IS_BETTER = BenchmarkProcessor.HIGHER_IS_BETTER | {
|
||||
"Pass@1",
|
||||
"ECR@1",
|
||||
"Parse@1",
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def match(cls, benchmark_name: str) -> bool:
|
||||
return "tablebench" in benchmark_name.lower()
|
||||
|
||||
@classmethod
|
||||
def get_core_metric(cls, accuracy_summary: dict) -> tuple[str, float, bool] | None:
|
||||
scores = []
|
||||
metrics_used = []
|
||||
|
||||
for ds, metrics in accuracy_summary.items():
|
||||
if not isinstance(metrics, dict):
|
||||
continue
|
||||
ds_lower = ds.lower()
|
||||
# Find matching core metric
|
||||
core_metric = "accuracy" # fallback
|
||||
for pattern, metric in cls.CORE_METRICS.items():
|
||||
if pattern in ds_lower:
|
||||
core_metric = metric
|
||||
break
|
||||
|
||||
if core_metric in metrics:
|
||||
scores.append(float(metrics[core_metric]))
|
||||
metrics_used.append(core_metric)
|
||||
|
||||
if not scores:
|
||||
return None
|
||||
|
||||
avg = sum(scores) / len(scores)
|
||||
unique = list(set(metrics_used))
|
||||
|
||||
if len(scores) == 1:
|
||||
metric_name = unique[0]
|
||||
elif len(unique) == 1:
|
||||
metric_name = f"{unique[0]} (average)"
|
||||
else:
|
||||
metric_name = "mixed (average)"
|
||||
|
||||
return (metric_name, avg, cls.is_higher_better(metric_name))
|
||||
@@ -0,0 +1,721 @@
|
||||
"""
|
||||
FT UI Components - Hierarchical Event Renderers
|
||||
"""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import plotly.graph_objects as go
|
||||
import streamlit as st
|
||||
|
||||
from rdagent.app.finetune.llm.ui.benchmarks import get_core_metric_score
|
||||
from rdagent.app.finetune.llm.ui.config import ICONS
|
||||
from rdagent.app.finetune.llm.ui.data_loader import Event, EvoLoop, Loop, Session
|
||||
|
||||
|
||||
def convert_latex_for_streamlit(text: str) -> str:
|
||||
"""Convert LaTeX syntax to Streamlit-compatible format.
|
||||
|
||||
Streamlit uses $...$ and $$...$$ for LaTeX rendering.
|
||||
This converts \(...\) and \[...\] to the Streamlit format.
|
||||
"""
|
||||
if not text:
|
||||
return text
|
||||
# Convert \(...\) to $...$
|
||||
text = text.replace(r"\(", "$").replace(r"\)", "$")
|
||||
# Convert \[...\] to $$...$$
|
||||
text = text.replace(r"\[", "$$").replace(r"\]", "$$")
|
||||
return text
|
||||
|
||||
|
||||
def format_duration(seconds: float | None) -> str:
|
||||
if seconds is None:
|
||||
return ""
|
||||
if seconds < 60:
|
||||
return f"{seconds:.1f}s"
|
||||
minutes = int(seconds // 60)
|
||||
secs = seconds % 60
|
||||
return f"{minutes}m {secs:.0f}s"
|
||||
|
||||
|
||||
def render_session(session: Session, show_types: list[str]) -> None:
|
||||
"""Render full session with hierarchy"""
|
||||
# Init events (before any loop)
|
||||
if session.init_events:
|
||||
filtered = [e for e in session.init_events if e.type in show_types]
|
||||
if filtered:
|
||||
with st.expander("🚀 **Initialization**", expanded=False):
|
||||
for event in filtered:
|
||||
render_event(event)
|
||||
|
||||
# Loops
|
||||
for loop_id in sorted(session.loops.keys()):
|
||||
loop = session.loops[loop_id]
|
||||
render_loop(loop, show_types)
|
||||
|
||||
|
||||
def render_loop(loop: Loop, show_types: list[str]) -> None:
|
||||
"""Render a single loop with lazy loading"""
|
||||
# 1. Coding stage results
|
||||
evo_results = []
|
||||
for evo in loop.coding.values():
|
||||
if evo.success is True:
|
||||
evo_results.append("✓")
|
||||
elif evo.success is False:
|
||||
evo_results.append("✗")
|
||||
coding_str = f"💻{''.join(evo_results)}" if evo_results else ""
|
||||
|
||||
# 2. Running stage results
|
||||
runner_success = None
|
||||
benchmark_score = None
|
||||
for event in loop.runner:
|
||||
# Docker (Full Train) result - check exit_code, not LLM evaluation
|
||||
if event.type == "docker_exec" and "Full Train" in event.title and event.success is not None:
|
||||
runner_success = event.success
|
||||
# Benchmark score - use core metric from processor
|
||||
if event.type == "feedback" and "Benchmark Result" in event.title:
|
||||
content = event.content
|
||||
if isinstance(content, dict):
|
||||
benchmark_name = content.get("benchmark_name", "")
|
||||
accuracy_summary = content.get("accuracy_summary", {})
|
||||
if isinstance(accuracy_summary, dict) and accuracy_summary:
|
||||
result = get_core_metric_score(benchmark_name, accuracy_summary)
|
||||
if result is not None:
|
||||
_, benchmark_score, _ = result
|
||||
|
||||
# 3. Get feedback decision for benchmark score coloring
|
||||
feedback_decision = None
|
||||
for event in loop.feedback:
|
||||
if event.type == "feedback" and "Feedback:" in event.title:
|
||||
feedback_decision = event.success
|
||||
break
|
||||
|
||||
# 4. Build title string (only show existing stages)
|
||||
parts = []
|
||||
if coding_str:
|
||||
parts.append(coding_str)
|
||||
if runner_success is not None:
|
||||
runner_str = "🏃✓" if runner_success else "🏃✗"
|
||||
parts.append(runner_str)
|
||||
# Show benchmark score with emoji based on feedback decision
|
||||
if benchmark_score is not None:
|
||||
if feedback_decision is True:
|
||||
parts.append(f"✅📊{benchmark_score:.2f}")
|
||||
elif feedback_decision is False:
|
||||
parts.append(f"❌📊{benchmark_score:.2f}")
|
||||
else:
|
||||
parts.append(f"📊{benchmark_score:.2f}")
|
||||
|
||||
result_str = " ".join(parts) if parts else ""
|
||||
|
||||
loop_key = f"loop_{loop.loop_id}_loaded"
|
||||
with st.expander(f"🔄 **Loop {loop.loop_id}** {result_str}", expanded=False):
|
||||
if not st.session_state.get(loop_key, False):
|
||||
# Lazy load: show button first
|
||||
if st.button("📥 Load Content", key=f"load_{loop.loop_id}"):
|
||||
st.session_state[loop_key] = True
|
||||
st.rerun()
|
||||
else:
|
||||
# Render actual content
|
||||
_render_loop_content(loop, show_types)
|
||||
|
||||
|
||||
def _render_loop_content(loop: Loop, show_types: list[str]) -> None:
|
||||
"""Render loop content (called after lazy load)"""
|
||||
# Exp Gen
|
||||
if loop.exp_gen:
|
||||
filtered = [e for e in loop.exp_gen if e.type in show_types]
|
||||
if filtered:
|
||||
st.markdown("#### 🧪 Experiment Generation")
|
||||
for event in filtered:
|
||||
render_event(event)
|
||||
|
||||
# Coding (Evo Loops)
|
||||
if loop.coding:
|
||||
st.markdown("#### 💻 Coding")
|
||||
for evo_id in sorted(loop.coding.keys()):
|
||||
evo = loop.coding[evo_id]
|
||||
render_evo_loop(evo, show_types)
|
||||
|
||||
# Runner
|
||||
if loop.runner:
|
||||
filtered = [e for e in loop.runner if e.type in show_types]
|
||||
if filtered:
|
||||
st.markdown("#### 🏃 Running(Full Train)")
|
||||
for event in filtered:
|
||||
render_event(event)
|
||||
|
||||
# Feedback
|
||||
if loop.feedback:
|
||||
filtered = [e for e in loop.feedback if e.type in show_types]
|
||||
if filtered:
|
||||
st.markdown("#### 📊 Feedback")
|
||||
for event in filtered:
|
||||
render_event(event)
|
||||
|
||||
|
||||
def render_evo_loop(evo: EvoLoop, show_types: list[str]) -> None:
|
||||
"""Render evolution loop"""
|
||||
filtered = [e for e in evo.events if e.type in show_types]
|
||||
if not filtered:
|
||||
return
|
||||
|
||||
status = "🟢" if evo.success else "🔴" if evo.success is False else "⚪"
|
||||
with st.expander(f"{status} Evo {evo.evo_id}", expanded=False):
|
||||
for event in filtered:
|
||||
render_event(event)
|
||||
|
||||
|
||||
def render_event(event: Event) -> None:
|
||||
"""Render a single event"""
|
||||
icon = ICONS.get(event.type, "📌")
|
||||
duration_str = f" ({format_duration(event.duration)})" if event.duration else ""
|
||||
|
||||
status = ""
|
||||
if event.success is True:
|
||||
status = "🟢 "
|
||||
elif event.success is False:
|
||||
status = "🔴 "
|
||||
|
||||
title = f"{event.time_str} {icon} {status}{event.title}{duration_str}"
|
||||
|
||||
renderers = {
|
||||
"scenario": render_scenario,
|
||||
"llm_call": render_llm_call,
|
||||
"template": render_template,
|
||||
"experiment": render_experiment,
|
||||
"code": render_code,
|
||||
"docker_exec": render_docker_exec,
|
||||
"evaluator": render_docker_exec, # Reuse docker_exec renderer for evaluator feedback
|
||||
"feedback": render_feedback,
|
||||
"token": render_token,
|
||||
"time": render_time_info,
|
||||
"settings": render_settings,
|
||||
"hypothesis": render_hypothesis,
|
||||
"dataset_selection": render_dataset_selection,
|
||||
}
|
||||
|
||||
renderer = renderers.get(event.type, render_generic)
|
||||
with st.expander(title, expanded=False):
|
||||
# Pass event.title to docker_exec/evaluator renderers for context-aware labels
|
||||
if event.type in ("docker_exec", "evaluator"):
|
||||
renderer(event.content, event.title)
|
||||
else:
|
||||
renderer(event.content)
|
||||
|
||||
|
||||
def render_scenario(content: Any) -> None:
|
||||
"""Render scenario details (main info shown in page header, this shows extras)."""
|
||||
import json
|
||||
|
||||
# 1. User target scenario
|
||||
if hasattr(content, "user_target_scenario") and content.user_target_scenario:
|
||||
st.markdown(f"**Target Scenario:** {content.user_target_scenario}")
|
||||
|
||||
# 2. Benchmark description
|
||||
if hasattr(content, "benchmark_description") and content.benchmark_description:
|
||||
st.markdown(f"**Benchmark Description:** {content.benchmark_description}")
|
||||
|
||||
# 3. Full timeout
|
||||
if hasattr(content, "real_full_timeout"):
|
||||
try:
|
||||
timeout_hours = content.real_full_timeout() / 60 / 60
|
||||
st.markdown(f"**Full Train Timeout:** {timeout_hours:.2f} hours")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 4. Device info - formatted nicely
|
||||
if hasattr(content, "device_info") and content.device_info:
|
||||
device = content.device_info
|
||||
# Parse string to dict if needed
|
||||
if isinstance(device, str):
|
||||
try:
|
||||
device = json.loads(device)
|
||||
except json.JSONDecodeError:
|
||||
st.markdown(f"**Device:** `{device}`")
|
||||
device = None
|
||||
if isinstance(device, dict):
|
||||
parts = []
|
||||
# Runtime info
|
||||
runtime = device.get("runtime", {})
|
||||
if runtime.get("python_version"):
|
||||
parts.append(f"🐍 Python `{runtime['python_version'].split()[0]}`")
|
||||
if runtime.get("os"):
|
||||
parts.append(f"💻 {runtime['os']}")
|
||||
# GPU info
|
||||
gpu_info = device.get("gpu", {})
|
||||
gpus = gpu_info.get("gpus", [])
|
||||
if gpus:
|
||||
gpu_name = gpus[0].get("name", "Unknown")
|
||||
gpu_mem_gb = gpus[0].get("memory_total_gb", 0)
|
||||
if len(gpus) > 1:
|
||||
parts.append(f"🎮 {len(gpus)}x {gpu_name} ({gpu_mem_gb}GB)")
|
||||
else:
|
||||
parts.append(f"🎮 {gpu_name} ({gpu_mem_gb}GB)")
|
||||
if parts:
|
||||
st.markdown(" · ".join(parts))
|
||||
|
||||
# 5. Model info (detailed specs)
|
||||
if hasattr(content, "model_info") and content.model_info:
|
||||
model_info = content.model_info
|
||||
if isinstance(model_info, dict) and model_info:
|
||||
with st.expander("Model Info", expanded=False):
|
||||
# Show key specs in a readable format
|
||||
if "specs" in model_info and model_info["specs"]:
|
||||
st.markdown("**Specs:**")
|
||||
st.code(model_info["specs"], language="text", wrap_lines=True)
|
||||
# Show other fields
|
||||
other_info = {k: v for k, v in model_info.items() if k != "specs" and v}
|
||||
if other_info:
|
||||
st.json(other_info)
|
||||
|
||||
# 6. Memory report (estimation based on hardware and model)
|
||||
if hasattr(content, "memory_report") and content.memory_report:
|
||||
with st.expander("Memory Estimation", expanded=False):
|
||||
st.code(content.memory_report, language="text", wrap_lines=True)
|
||||
|
||||
|
||||
def render_dataset_selection(content: Any) -> None:
|
||||
if not isinstance(content, dict):
|
||||
st.json(content) if content else st.info("No content")
|
||||
return
|
||||
|
||||
selected = content.get("selected_datasets", [])
|
||||
total = content.get("total_datasets", 0)
|
||||
reasoning = content.get("reasoning", "")
|
||||
|
||||
if selected:
|
||||
st.markdown(f"**Selected ({len(selected)}/{total}):** " + ", ".join(f"`{ds}`" for ds in selected))
|
||||
|
||||
if reasoning:
|
||||
with st.expander("Selection Reasoning", expanded=True):
|
||||
st.markdown(reasoning)
|
||||
|
||||
|
||||
def render_hypothesis(content: Any) -> None:
|
||||
"""Render hypothesis content (Base Model shown in page header, not here)."""
|
||||
if hasattr(content, "hypothesis") and content.hypothesis:
|
||||
st.markdown("**Hypothesis:**")
|
||||
st.markdown(content.hypothesis)
|
||||
if hasattr(content, "reason") and content.reason:
|
||||
with st.expander("Reason", expanded=False):
|
||||
st.markdown(content.reason)
|
||||
|
||||
|
||||
def render_settings(content: Any) -> None:
|
||||
if isinstance(content, dict):
|
||||
st.json(content)
|
||||
else:
|
||||
st.code(str(content), wrap_lines=True)
|
||||
|
||||
|
||||
def render_llm_call(content: Any) -> None:
|
||||
if not isinstance(content, dict):
|
||||
st.json(content) if content else st.info("No content")
|
||||
return
|
||||
|
||||
if content.get("start") and content.get("end"):
|
||||
duration = (content["end"] - content["start"]).total_seconds()
|
||||
st.caption(f"Duration: {format_duration(duration)}")
|
||||
|
||||
# Check if markdown rendering is enabled
|
||||
render_md = st.session_state.get("render_markdown_toggle", False)
|
||||
|
||||
system = content.get("system", "")
|
||||
if system:
|
||||
with st.expander("System Prompt", expanded=False):
|
||||
if render_md:
|
||||
st.markdown(system)
|
||||
else:
|
||||
st.code(system, language="text", line_numbers=True, wrap_lines=True)
|
||||
|
||||
user = content.get("user", "")
|
||||
if user:
|
||||
with st.expander("User Prompt", expanded=False):
|
||||
if render_md:
|
||||
st.markdown(user)
|
||||
else:
|
||||
st.code(user, language="text", line_numbers=True, wrap_lines=True)
|
||||
|
||||
resp = content.get("resp", "")
|
||||
if resp:
|
||||
st.markdown("**Response:**")
|
||||
if render_md:
|
||||
st.markdown(resp)
|
||||
elif resp.strip().startswith("{") or resp.strip().startswith("["):
|
||||
st.code(resp, language="json", line_numbers=True, wrap_lines=True)
|
||||
elif resp.strip().startswith("```"):
|
||||
st.markdown(resp)
|
||||
else:
|
||||
st.code(resp, language="text", line_numbers=True, wrap_lines=True)
|
||||
|
||||
|
||||
def render_template(content: Any) -> None:
|
||||
if not isinstance(content, dict):
|
||||
st.json(content) if content else st.info("No content")
|
||||
return
|
||||
|
||||
uri = content.get("uri", "")
|
||||
st.caption(f"URI: `{uri}`")
|
||||
|
||||
context = content.get("context", {})
|
||||
if context:
|
||||
with st.expander("Context Variables", expanded=False):
|
||||
st.json(context)
|
||||
|
||||
template = content.get("template", "")
|
||||
if template:
|
||||
with st.expander("Template", expanded=False):
|
||||
st.code(template, language="text", line_numbers=True, wrap_lines=True)
|
||||
|
||||
rendered = content.get("rendered", "")
|
||||
if rendered:
|
||||
with st.expander("Rendered", expanded=True):
|
||||
st.code(rendered, language="text", line_numbers=True, wrap_lines=True)
|
||||
|
||||
|
||||
def render_experiment(content: Any) -> None:
|
||||
"""Render experiment tasks (Base Model and Datasets shown in page header, not here)."""
|
||||
if isinstance(content, list):
|
||||
for i, task in enumerate(content):
|
||||
if len(content) > 1:
|
||||
st.markdown(f"**Task {i}**")
|
||||
|
||||
if hasattr(task, "description") and task.description:
|
||||
st.markdown("**Description:**")
|
||||
st.markdown(task.description)
|
||||
else:
|
||||
st.json(content) if content else st.info("No content")
|
||||
|
||||
|
||||
def render_code(content: Any) -> None:
|
||||
if not isinstance(content, list):
|
||||
st.info("No code available")
|
||||
return
|
||||
|
||||
for i, ws in enumerate(content):
|
||||
if not hasattr(ws, "file_dict") or not ws.file_dict:
|
||||
continue
|
||||
|
||||
if len(content) > 1:
|
||||
st.markdown(f"**Workspace {i}**")
|
||||
|
||||
for filename, code in ws.file_dict.items():
|
||||
lang = "yaml" if filename.endswith((".yaml", ".yml")) else "python"
|
||||
with st.expander(filename, expanded=False):
|
||||
st.code(code, language=lang, line_numbers=True, wrap_lines=True)
|
||||
|
||||
|
||||
def _extract_evaluator_name(title: str) -> str:
|
||||
"""Extract evaluator name from event title like 'Eval (Data Processing) ✓'."""
|
||||
match = re.search(r"\(([^)]+)\)", title)
|
||||
return match.group(1) if match else ""
|
||||
|
||||
|
||||
def _render_single_feedback(fb: Any, evaluator_name: str = "") -> None:
|
||||
"""Render a single CoSTEERSingleFeedback object.
|
||||
|
||||
Structure:
|
||||
- execution: LLM-generated execution summary (what happened, success/failure reason)
|
||||
- raw_execution: Raw script stdout/stderr output
|
||||
- return_checking: LLM-generated data quality assessment
|
||||
- code: LLM-generated code improvement suggestions
|
||||
"""
|
||||
decision = getattr(fb, "final_decision", None)
|
||||
if decision is True:
|
||||
st.success("Execution: PASS")
|
||||
elif decision is False:
|
||||
st.error("Execution: FAIL")
|
||||
|
||||
# 1. Execution Summary (LLM-generated)
|
||||
execution = getattr(fb, "execution", "")
|
||||
if execution:
|
||||
label = f"{evaluator_name} Summary" if evaluator_name else "Execution Summary"
|
||||
with st.expander(label, expanded=True):
|
||||
st.code(execution, language="text", line_numbers=True, wrap_lines=True)
|
||||
|
||||
# 2. Raw Execution Log (script stdout)
|
||||
raw_execution = getattr(fb, "raw_execution", "")
|
||||
if raw_execution:
|
||||
with st.expander("Raw Output (stdout)", expanded=False):
|
||||
st.code(raw_execution, language="text", line_numbers=True, wrap_lines=True)
|
||||
|
||||
# 3. Data Quality Check (LLM-generated)
|
||||
return_checking = getattr(fb, "return_checking", "")
|
||||
if return_checking:
|
||||
with st.expander("Data Quality Check", expanded=False):
|
||||
st.code(return_checking, language="text", line_numbers=True, wrap_lines=True)
|
||||
|
||||
# 4. Code Improvement Suggestions (LLM-generated, often very long)
|
||||
code_fb = getattr(fb, "code", "")
|
||||
if code_fb:
|
||||
with st.expander("Code Improvement Suggestions", expanded=False):
|
||||
# Use markdown rendering if content contains markdown formatting
|
||||
if "**" in code_fb or "```" in code_fb or "- " in code_fb:
|
||||
st.markdown(code_fb)
|
||||
else:
|
||||
st.code(code_fb, language="text", line_numbers=True, wrap_lines=True)
|
||||
|
||||
|
||||
def render_docker_exec(content: Any, event_title: str = "") -> None:
|
||||
# Extract evaluator name from event title for context-aware labels
|
||||
evaluator_name = _extract_evaluator_name(event_title)
|
||||
|
||||
# Docker run raw output (dict with exit_code/stdout)
|
||||
if isinstance(content, dict) and ("exit_code" in content or "stdout" in content or "success" in content):
|
||||
# Show workspace ID if available (only the UUID part)
|
||||
workspace_path = content.get("workspace_path")
|
||||
if workspace_path:
|
||||
workspace_id = Path(workspace_path).name
|
||||
st.caption(f"📁 `{workspace_id}`")
|
||||
|
||||
exit_code = content.get("exit_code")
|
||||
success = content.get("success")
|
||||
if exit_code is not None:
|
||||
if exit_code == 0:
|
||||
st.success(f"Exit code: {exit_code}")
|
||||
else:
|
||||
st.error(f"Exit code: {exit_code}")
|
||||
elif success is not None:
|
||||
if success:
|
||||
st.success("Execution: PASS")
|
||||
else:
|
||||
st.error("Execution: FAIL")
|
||||
|
||||
stdout = content.get("stdout", "")
|
||||
if stdout:
|
||||
label = f"{evaluator_name} Output" if evaluator_name else "Execution Output"
|
||||
with st.expander(label, expanded=True):
|
||||
st.code(stdout, language="text", line_numbers=True, wrap_lines=True)
|
||||
return
|
||||
|
||||
# CoSTEERMultiFeedback (has feedback_list)
|
||||
if hasattr(content, "feedback_list"):
|
||||
for i, fb in enumerate(content.feedback_list):
|
||||
if len(content.feedback_list) > 1:
|
||||
st.markdown(f"**Feedback {i}**")
|
||||
_render_single_feedback(fb, evaluator_name)
|
||||
return
|
||||
|
||||
# Single CoSTEERSingleFeedback (has final_decision)
|
||||
if hasattr(content, "final_decision"):
|
||||
_render_single_feedback(content, evaluator_name)
|
||||
return
|
||||
|
||||
# FTExperiment (runner result)
|
||||
if hasattr(content, "sub_workspace_list"):
|
||||
for ws in content.sub_workspace_list:
|
||||
if not hasattr(ws, "running_info") or ws.running_info is None:
|
||||
continue
|
||||
|
||||
info = ws.running_info
|
||||
running_time = getattr(info, "running_time", None)
|
||||
if running_time:
|
||||
st.metric("Running Time", f"{running_time:.1f}s")
|
||||
|
||||
stdout = getattr(info, "stdout", "")
|
||||
if stdout:
|
||||
with st.expander("Full Train Log", expanded=True):
|
||||
st.code(stdout, language="text", line_numbers=True, wrap_lines=True)
|
||||
|
||||
result = getattr(info, "result", {})
|
||||
if result:
|
||||
render_training_result(result)
|
||||
return
|
||||
|
||||
st.json(content) if content else st.info("No content")
|
||||
|
||||
|
||||
def render_feedback(content: Any) -> None:
|
||||
# Handle benchmark result (dict with accuracy_summary)
|
||||
if isinstance(content, dict) and "accuracy_summary" in content:
|
||||
render_benchmark_result(content)
|
||||
return
|
||||
|
||||
col1, col2, col3 = st.columns(3)
|
||||
with col1:
|
||||
decision = getattr(content, "decision", None)
|
||||
if decision is not None:
|
||||
st.metric("Decision", "Accept" if decision else "Reject")
|
||||
with col2:
|
||||
acceptable = getattr(content, "acceptable", None)
|
||||
if acceptable is not None:
|
||||
st.metric("Acceptable", "Yes" if acceptable else "No")
|
||||
with col3:
|
||||
error_type = getattr(content, "observations", None)
|
||||
if error_type:
|
||||
st.metric("Error Type", error_type)
|
||||
|
||||
# FT scenario only uses code_change_summary (observations, hypothesis_evaluation,
|
||||
# new_hypothesis, eda_improvement are DS scenario specific)
|
||||
fields = [
|
||||
("code_change_summary", "Code Change Summary"),
|
||||
]
|
||||
|
||||
for attr, label in fields:
|
||||
value = getattr(content, attr, None)
|
||||
if value:
|
||||
with st.expander(label, expanded=False):
|
||||
st.markdown(value)
|
||||
|
||||
reason = getattr(content, "reason", None)
|
||||
if reason:
|
||||
with st.expander("Reason (Full Details)", expanded=True):
|
||||
st.code(reason, language="text", line_numbers=True, wrap_lines=True)
|
||||
|
||||
exception = getattr(content, "exception", None)
|
||||
if exception:
|
||||
st.error(f"Exception: {exception}")
|
||||
|
||||
|
||||
def render_token(content: Any) -> None:
|
||||
if isinstance(content, dict):
|
||||
col1, col2, col3 = st.columns(3)
|
||||
with col1:
|
||||
st.metric("Prompt", content.get("prompt_tokens", 0))
|
||||
with col2:
|
||||
st.metric("Completion", content.get("completion_tokens", 0))
|
||||
with col3:
|
||||
st.metric("Total", content.get("total_tokens", 0))
|
||||
else:
|
||||
st.json(content) if content else st.info("No content")
|
||||
|
||||
|
||||
def render_time_info(content: Any) -> None:
|
||||
if isinstance(content, dict):
|
||||
for k, v in content.items():
|
||||
st.metric(k, f"{v:.1f}s" if isinstance(v, (int, float)) else str(v))
|
||||
else:
|
||||
st.json(content) if content else st.info("No content")
|
||||
|
||||
|
||||
def render_generic(content: Any) -> None:
|
||||
if hasattr(content, "__dict__"):
|
||||
st.json(vars(content))
|
||||
elif content:
|
||||
st.json(content)
|
||||
else:
|
||||
st.info("No content")
|
||||
|
||||
|
||||
def render_training_result(result: dict) -> None:
|
||||
training_metrics = result.get("training_metrics", {})
|
||||
loss_history = training_metrics.get("loss_history", {})
|
||||
|
||||
# loss_history is Dict[str, List[Dict]] with "train" and "eval" keys
|
||||
train_history = loss_history.get("train", []) if isinstance(loss_history, dict) else []
|
||||
if train_history:
|
||||
fig = go.Figure()
|
||||
steps = [entry.get("step", i) for i, entry in enumerate(train_history)]
|
||||
losses = [entry.get("loss", 0) for entry in train_history]
|
||||
fig.add_trace(go.Scatter(x=steps, y=losses, mode="lines+markers", name="Loss"))
|
||||
fig.update_layout(title="Training Loss", xaxis_title="Step", yaxis_title="Loss", height=300)
|
||||
st.plotly_chart(fig, use_container_width=True)
|
||||
|
||||
col1, col2 = st.columns(2)
|
||||
initial_loss = training_metrics.get("initial_loss")
|
||||
final_loss = training_metrics.get("final_loss")
|
||||
if initial_loss:
|
||||
col1.metric("Initial Loss", f"{initial_loss:.4f}")
|
||||
if final_loss:
|
||||
col2.metric("Final Loss", f"{final_loss:.4f}")
|
||||
|
||||
# Validation benchmark ([:100]) - used for SOTA judgment
|
||||
benchmark = result.get("benchmark", {})
|
||||
if benchmark:
|
||||
st.markdown("**Validation Benchmark**")
|
||||
# Detect format: old format has "accuracy_summary" at top level,
|
||||
# new format has benchmark names as keys with nested accuracy_summary
|
||||
if "accuracy_summary" in benchmark:
|
||||
# Old format: {accuracy_summary: {...}, error_samples: [...]}
|
||||
accuracy_summary = benchmark.get("accuracy_summary", {})
|
||||
if accuracy_summary:
|
||||
rows = [{"dataset": ds, **metrics} for ds, metrics in accuracy_summary.items()]
|
||||
st.dataframe(rows)
|
||||
else:
|
||||
# New format: {bm_name: {accuracy_summary: {...}}, ...}
|
||||
for bm_name, bm_result in benchmark.items():
|
||||
if isinstance(bm_result, dict) and "accuracy_summary" in bm_result:
|
||||
st.markdown(f"*{bm_name}:*")
|
||||
accuracy_summary = bm_result.get("accuracy_summary", {})
|
||||
if accuracy_summary:
|
||||
rows = [{"dataset": ds, **metrics} for ds, metrics in accuracy_summary.items()]
|
||||
st.dataframe(rows)
|
||||
|
||||
# Test benchmark ([100:200]) - frontend display only, not visible to agent
|
||||
benchmark_test = result.get("benchmark_test", {})
|
||||
if benchmark_test and benchmark_test != benchmark: # Avoid duplicate display for small datasets
|
||||
st.markdown("**Test Benchmark**")
|
||||
if "accuracy_summary" in benchmark_test:
|
||||
accuracy_summary = benchmark_test.get("accuracy_summary", {})
|
||||
if accuracy_summary:
|
||||
rows = [{"dataset": ds, **metrics} for ds, metrics in accuracy_summary.items()]
|
||||
st.dataframe(rows)
|
||||
else:
|
||||
for bm_name, bm_result in benchmark_test.items():
|
||||
if isinstance(bm_result, dict) and "accuracy_summary" in bm_result:
|
||||
st.markdown(f"*{bm_name}:*")
|
||||
accuracy_summary = bm_result.get("accuracy_summary", {})
|
||||
if accuracy_summary:
|
||||
rows = [{"dataset": ds, **metrics} for ds, metrics in accuracy_summary.items()]
|
||||
st.dataframe(rows)
|
||||
|
||||
|
||||
def render_benchmark_result(content: dict) -> None:
|
||||
"""Render benchmark evaluation result"""
|
||||
import pandas as pd
|
||||
|
||||
benchmark_name = content.get("benchmark_name", "Unknown")
|
||||
st.markdown(f"**Benchmark: {benchmark_name}**")
|
||||
|
||||
# Accuracy summary table
|
||||
# accuracy_summary is a dict: {dataset_name: {metric: value, ...}, ...}
|
||||
accuracy_summary = content.get("accuracy_summary", {})
|
||||
if accuracy_summary and isinstance(accuracy_summary, dict):
|
||||
st.markdown("**Accuracy Summary:**")
|
||||
# Convert dict {dataset: {metric: value}} to list of dicts for dataframe
|
||||
rows = []
|
||||
for ds, metrics in accuracy_summary.items():
|
||||
row = {"dataset": ds, **metrics}
|
||||
rows.append(row)
|
||||
|
||||
# Create DataFrame and reorder columns
|
||||
df = pd.DataFrame(rows)
|
||||
cols = ["dataset"] + [c for c in df.columns if c != "dataset"]
|
||||
df = df[cols]
|
||||
st.dataframe(df)
|
||||
|
||||
# Error samples
|
||||
error_samples = content.get("error_samples", [])
|
||||
if error_samples:
|
||||
with st.expander(f"Error Samples ({len(error_samples)})", expanded=False):
|
||||
for i, sample in enumerate(error_samples):
|
||||
with st.expander(f"Sample {i+1} (Gold: {sample.get('gold', 'N/A')})", expanded=False):
|
||||
st.markdown(
|
||||
'<div style="font-size: 0.85em;">',
|
||||
unsafe_allow_html=True,
|
||||
)
|
||||
st.markdown("**Question:**")
|
||||
st.markdown(convert_latex_for_streamlit(sample.get("question", "N/A")))
|
||||
st.markdown("---")
|
||||
st.markdown(f"**Gold:** `{sample.get('gold', 'N/A')}`")
|
||||
st.markdown("---")
|
||||
st.markdown("**Model Output:**")
|
||||
st.markdown(convert_latex_for_streamlit(sample.get("model_output", "N/A")))
|
||||
st.markdown("</div>", unsafe_allow_html=True)
|
||||
|
||||
|
||||
def render_summary(summary: dict) -> None:
|
||||
col1, col2, col3, col4 = st.columns(4)
|
||||
with col1:
|
||||
st.metric("Loops", summary.get("loop_count", 0))
|
||||
with col2:
|
||||
st.metric("LLM Calls", summary.get("llm_call_count", 0))
|
||||
with col3:
|
||||
llm_time = summary.get("llm_total_time", 0)
|
||||
st.metric("LLM Time", format_duration(llm_time))
|
||||
with col4:
|
||||
success = summary.get("docker_success", 0)
|
||||
fail = summary.get("docker_fail", 0)
|
||||
st.metric("Executions", f"{success}✓ / {fail}✗")
|
||||
@@ -0,0 +1,69 @@
|
||||
"""
|
||||
FT UI Configuration Constants
|
||||
|
||||
Centralized configuration for FT Timeline Viewer.
|
||||
"""
|
||||
|
||||
from typing import Literal
|
||||
|
||||
# Event type definition
|
||||
EventType = Literal[
|
||||
"scenario",
|
||||
"llm_call",
|
||||
"template",
|
||||
"experiment",
|
||||
"code",
|
||||
"docker_exec",
|
||||
"evaluator", # Evaluator feedback (separate from docker_exec)
|
||||
"feedback",
|
||||
"token",
|
||||
"time",
|
||||
"settings",
|
||||
"hypothesis",
|
||||
"dataset_selection",
|
||||
]
|
||||
|
||||
# Event type icons
|
||||
ICONS = {
|
||||
"scenario": "🎯",
|
||||
"llm_call": "💬",
|
||||
"template": "📋",
|
||||
"experiment": "🧪",
|
||||
"code": "📄",
|
||||
"docker_exec": "🐳",
|
||||
"evaluator": "📝", # Evaluator feedback icon
|
||||
"feedback": "📊",
|
||||
"token": "🔢",
|
||||
"time": "⏱️",
|
||||
"settings": "⚙️",
|
||||
"hypothesis": "💡",
|
||||
"dataset_selection": "📂",
|
||||
}
|
||||
|
||||
# Evaluator configuration mapping (name, default_stage)
|
||||
EVALUATOR_CONFIG = {
|
||||
"FTDataEvaluator": ("Data Processing", "coding"),
|
||||
"FTCoderEvaluator": ("Micro-batch Test", "coding"),
|
||||
"FTRunnerEvaluator": ("Full Train", "runner"),
|
||||
}
|
||||
|
||||
# Always visible event types
|
||||
ALWAYS_VISIBLE_TYPES = [
|
||||
"scenario",
|
||||
"dataset_selection",
|
||||
"hypothesis",
|
||||
"llm_call",
|
||||
"experiment",
|
||||
"code",
|
||||
"docker_exec",
|
||||
"evaluator",
|
||||
"feedback",
|
||||
]
|
||||
|
||||
# Optional event types with toggle config (label, default_enabled)
|
||||
OPTIONAL_TYPES = {
|
||||
"template": ("📋 Template", False),
|
||||
"token": ("🔢 Token", False),
|
||||
"time": ("⏱️ Time", False),
|
||||
"settings": ("⚙️ Settings", False),
|
||||
}
|
||||
@@ -0,0 +1,455 @@
|
||||
"""
|
||||
FT UI Data Loader
|
||||
Load pkl logs and convert to hierarchical timeline structure
|
||||
"""
|
||||
|
||||
import re
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import streamlit as st
|
||||
|
||||
from rdagent.app.finetune.llm.ui.config import EVALUATOR_CONFIG, EventType
|
||||
from rdagent.log.storage import FileStorage
|
||||
|
||||
|
||||
@dataclass
|
||||
class Event:
|
||||
"""Timeline event"""
|
||||
|
||||
type: EventType
|
||||
timestamp: datetime
|
||||
tag: str
|
||||
title: str
|
||||
content: Any
|
||||
loop_id: int | None = None
|
||||
evo_id: int | None = None
|
||||
stage: str = ""
|
||||
duration: float | None = None
|
||||
success: bool | None = None
|
||||
|
||||
@property
|
||||
def time_str(self) -> str:
|
||||
return self.timestamp.strftime("%H:%M:%S")
|
||||
|
||||
|
||||
@dataclass
|
||||
class EvoLoop:
|
||||
"""Evolution loop containing events"""
|
||||
|
||||
evo_id: int
|
||||
events: list[Event] = field(default_factory=list)
|
||||
success: bool | None = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class Loop:
|
||||
"""Main loop containing stages"""
|
||||
|
||||
loop_id: int
|
||||
exp_gen: list[Event] = field(default_factory=list)
|
||||
coding: dict[int, EvoLoop] = field(default_factory=dict) # evo_id -> EvoLoop
|
||||
runner: list[Event] = field(default_factory=list)
|
||||
feedback: list[Event] = field(default_factory=list)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Session:
|
||||
"""Session containing init events and loops"""
|
||||
|
||||
init_events: list[Event] = field(default_factory=list)
|
||||
loops: dict[int, Loop] = field(default_factory=dict) # loop_id -> Loop
|
||||
|
||||
|
||||
def extract_loop_id(tag: str) -> int | None:
|
||||
match = re.search(r"Loop_(\d+)", tag)
|
||||
return int(match.group(1)) if match else None
|
||||
|
||||
|
||||
def extract_evo_id(tag: str) -> int | None:
|
||||
match = re.search(r"evo_loop_(\d+)", tag)
|
||||
return int(match.group(1)) if match else None
|
||||
|
||||
|
||||
def extract_stage(tag: str) -> str:
|
||||
if "direct_exp_gen" in tag:
|
||||
return "exp_gen"
|
||||
if "coding" in tag:
|
||||
return "coding"
|
||||
if "running" in tag: # Note: tag uses "running", not "runner"
|
||||
return "runner"
|
||||
if "feedback" in tag:
|
||||
return "feedback"
|
||||
return ""
|
||||
|
||||
|
||||
def get_valid_sessions(log_folder: Path) -> list[str]:
|
||||
if not log_folder.exists():
|
||||
return []
|
||||
sessions = []
|
||||
for d in log_folder.iterdir():
|
||||
if d.is_dir() and d.joinpath("__session__").exists():
|
||||
sessions.append(d.name)
|
||||
return sorted(sessions, reverse=True)
|
||||
|
||||
|
||||
def parse_event(tag: str, content: Any, timestamp: datetime) -> Event | None:
|
||||
loop_id = extract_loop_id(tag)
|
||||
evo_id = extract_evo_id(tag)
|
||||
stage = extract_stage(tag)
|
||||
|
||||
# Scenario
|
||||
if tag == "scenario":
|
||||
model = getattr(content, "base_model", "Unknown")
|
||||
return Event(type="scenario", timestamp=timestamp, tag=tag, title=f"Scenario: {model}", content=content)
|
||||
|
||||
# Dataset selection
|
||||
if "dataset_selection" in tag:
|
||||
selected = content.get("selected_datasets", []) if isinstance(content, dict) else []
|
||||
total = content.get("total_datasets", 0) if isinstance(content, dict) else 0
|
||||
return Event(
|
||||
type="dataset_selection",
|
||||
timestamp=timestamp,
|
||||
tag=tag,
|
||||
title=f"Dataset Selection: {len(selected)}/{total}",
|
||||
content=content,
|
||||
)
|
||||
|
||||
# Settings
|
||||
if "SETTINGS" in tag:
|
||||
name = tag.replace("_SETTINGS", "").replace("SETTINGS", "")
|
||||
return Event(type="settings", timestamp=timestamp, tag=tag, title=f"Settings: {name}", content=content)
|
||||
|
||||
# Hypothesis
|
||||
if tag == "hypothesis" or (loop_id is not None and "hypothesis" in tag):
|
||||
return Event(
|
||||
type="hypothesis",
|
||||
timestamp=timestamp,
|
||||
tag=tag,
|
||||
title="Hypothesis",
|
||||
content=content,
|
||||
loop_id=loop_id,
|
||||
stage="exp_gen",
|
||||
)
|
||||
|
||||
# LLM Call
|
||||
if "debug_llm" in tag:
|
||||
if isinstance(content, dict) and ("user" in content or "system" in content):
|
||||
duration = None
|
||||
if content.get("start") and content.get("end"):
|
||||
duration = (content["end"] - content["start"]).total_seconds()
|
||||
return Event(
|
||||
type="llm_call",
|
||||
timestamp=timestamp,
|
||||
tag=tag,
|
||||
title="LLM Call",
|
||||
content=content,
|
||||
loop_id=loop_id,
|
||||
evo_id=evo_id,
|
||||
stage=stage,
|
||||
duration=duration,
|
||||
)
|
||||
|
||||
# Template
|
||||
if "debug_tpl" in tag:
|
||||
if isinstance(content, dict) and "uri" in content:
|
||||
uri = content.get("uri", "")
|
||||
tpl_name = uri.split(":")[-1] if ":" in uri else uri
|
||||
return Event(
|
||||
type="template",
|
||||
timestamp=timestamp,
|
||||
tag=tag,
|
||||
title=f"Template: {tpl_name}",
|
||||
content=content,
|
||||
loop_id=loop_id,
|
||||
evo_id=evo_id,
|
||||
stage=stage,
|
||||
)
|
||||
|
||||
# Experiment generation
|
||||
if "experiment generation" in tag:
|
||||
task_count = len(content) if isinstance(content, list) else 1
|
||||
return Event(
|
||||
type="experiment",
|
||||
timestamp=timestamp,
|
||||
tag=tag,
|
||||
title=f"Experiment ({task_count} task)",
|
||||
content=content,
|
||||
loop_id=loop_id,
|
||||
stage=stage,
|
||||
)
|
||||
|
||||
# Evolving code
|
||||
if "evolving code" in tag:
|
||||
file_count = 0
|
||||
if isinstance(content, list):
|
||||
for ws in content:
|
||||
if hasattr(ws, "file_dict"):
|
||||
file_count += len(ws.file_dict)
|
||||
return Event(
|
||||
type="code",
|
||||
timestamp=timestamp,
|
||||
tag=tag,
|
||||
title=f"Code ({file_count} files)",
|
||||
content=content,
|
||||
loop_id=loop_id,
|
||||
evo_id=evo_id,
|
||||
stage=stage or "coding",
|
||||
)
|
||||
|
||||
# Benchmark execution (Docker or Conda) - must check before generic docker_run/conda_run
|
||||
if "docker_run.Benchmark" in tag or "conda_run.Benchmark" in tag:
|
||||
benchmark_name = content.get("benchmark_name", "Unknown") if isinstance(content, dict) else "Unknown"
|
||||
exit_code = content.get("exit_code") if isinstance(content, dict) else None
|
||||
success = exit_code == 0 if exit_code is not None else None
|
||||
env_type = "Docker" if "docker_run" in tag else "Conda"
|
||||
return Event(
|
||||
type="docker_exec",
|
||||
timestamp=timestamp,
|
||||
tag=tag,
|
||||
title=f"Benchmark ({benchmark_name}) [{env_type}] {'✓' if success else '✗' if success is False else ''}",
|
||||
content=content,
|
||||
loop_id=loop_id,
|
||||
stage="runner",
|
||||
success=success,
|
||||
)
|
||||
|
||||
# Environment run (Docker or Conda, raw execution logged before LLM evaluation)
|
||||
if "docker_run." in tag or "conda_run." in tag:
|
||||
is_docker = "docker_run." in tag
|
||||
tag_prefix = "docker_run." if is_docker else "conda_run."
|
||||
class_name = tag.split(tag_prefix)[-1].split(".")[0]
|
||||
|
||||
# FTWorkspace unified logging - determine type from entry command
|
||||
if class_name == "FTWorkspace":
|
||||
entry = content.get("entry", "") if isinstance(content, dict) else ""
|
||||
if "llamafactory-cli train" in entry:
|
||||
# Distinguish by yaml file name: debug_train.yaml for micro-batch, train.yaml for full training
|
||||
if "debug_train.yaml" in entry:
|
||||
evaluator_name, default_stage = "Micro-batch Test", "coding"
|
||||
else:
|
||||
evaluator_name, default_stage = "Full Train", "runner"
|
||||
elif "process_data" in entry.lower():
|
||||
evaluator_name, default_stage = "Data Processing", "coding"
|
||||
elif entry.startswith("rm "):
|
||||
evaluator_name, default_stage = "Cleanup", "runner"
|
||||
else:
|
||||
evaluator_name, default_stage = "Env Run", "coding"
|
||||
else:
|
||||
evaluator_name, default_stage = EVALUATOR_CONFIG.get(class_name, (class_name, "coding"))
|
||||
|
||||
exit_code = content.get("exit_code") if isinstance(content, dict) else None
|
||||
success = exit_code == 0 if exit_code is not None else content.get("success")
|
||||
env_label = "Docker" if is_docker else "Conda"
|
||||
title = f"{env_label} ({evaluator_name}) {'✓' if success else '✗' if success is False else ''}"
|
||||
return Event(
|
||||
type="docker_exec",
|
||||
timestamp=timestamp,
|
||||
tag=tag,
|
||||
title=title,
|
||||
content=content,
|
||||
loop_id=loop_id,
|
||||
evo_id=evo_id,
|
||||
stage=stage or default_stage,
|
||||
success=success,
|
||||
)
|
||||
|
||||
# Docker execution (individual evaluator feedback, logged after LLM evaluation)
|
||||
if "docker_exec." in tag:
|
||||
class_name = tag.split("docker_exec.")[-1].split(".")[0]
|
||||
evaluator_name, default_stage = EVALUATOR_CONFIG.get(class_name, (class_name, "coding"))
|
||||
success = getattr(content, "final_decision", None)
|
||||
title = f"Eval ({evaluator_name}) {'✓' if success else '✗' if success is False else '?'}"
|
||||
return Event(
|
||||
type="docker_exec",
|
||||
timestamp=timestamp,
|
||||
tag=tag,
|
||||
title=title,
|
||||
content=content,
|
||||
loop_id=loop_id,
|
||||
evo_id=evo_id,
|
||||
stage=stage or default_stage,
|
||||
success=success,
|
||||
)
|
||||
|
||||
# Evaluator feedback (logged from FT evaluators with final_decision)
|
||||
if "evaluator_feedback." in tag:
|
||||
class_name = tag.split("evaluator_feedback.")[-1].split(".")[0]
|
||||
evaluator_name, default_stage = EVALUATOR_CONFIG.get(class_name, (class_name, "coding"))
|
||||
success = getattr(content, "final_decision", None)
|
||||
title = f"Eval ({evaluator_name}) {'✓' if success else '✗' if success is False else '?'}"
|
||||
return Event(
|
||||
type="evaluator", # Use dedicated evaluator type with 📝 icon
|
||||
timestamp=timestamp,
|
||||
tag=tag,
|
||||
title=title,
|
||||
content=content,
|
||||
loop_id=loop_id,
|
||||
evo_id=evo_id,
|
||||
stage=stage or default_stage,
|
||||
success=success,
|
||||
)
|
||||
|
||||
# Final feedback
|
||||
if "feedback.feedback" in tag or (tag.endswith(".feedback") and "evo_loop" not in tag):
|
||||
decision = getattr(content, "decision", None)
|
||||
return Event(
|
||||
type="feedback",
|
||||
timestamp=timestamp,
|
||||
tag=tag,
|
||||
title=f"Feedback: {'Accept' if decision else 'Reject'}",
|
||||
content=content,
|
||||
loop_id=loop_id,
|
||||
stage="feedback",
|
||||
success=decision,
|
||||
)
|
||||
|
||||
# Benchmark result (supports benchmark_result, benchmark_result.validation, benchmark_result.test)
|
||||
if "benchmark_result" in tag:
|
||||
benchmark_name = content.get("benchmark_name", "Unknown") if isinstance(content, dict) else "Unknown"
|
||||
accuracy = content.get("accuracy_summary", {}) if isinstance(content, dict) else {}
|
||||
# Extract split from tag or content
|
||||
split = content.get("split", "") if isinstance(content, dict) else ""
|
||||
if not split and "." in tag:
|
||||
split = tag.split(".")[-1] # e.g., "validation" or "test" from "benchmark_result.validation"
|
||||
split_label = f" [{split.title()}]" if split and split != "default" else ""
|
||||
return Event(
|
||||
type="feedback",
|
||||
timestamp=timestamp,
|
||||
tag=tag,
|
||||
title=f"Benchmark Result{split_label} ({benchmark_name}: {len(accuracy)} datasets)",
|
||||
content=content,
|
||||
loop_id=loop_id,
|
||||
stage="runner",
|
||||
)
|
||||
|
||||
# Runner result
|
||||
if "runner result" in tag:
|
||||
return Event(
|
||||
type="docker_exec",
|
||||
timestamp=timestamp,
|
||||
tag=tag,
|
||||
title="Full Train",
|
||||
content=content,
|
||||
loop_id=loop_id,
|
||||
stage="runner",
|
||||
)
|
||||
|
||||
# Token cost
|
||||
if "token_cost" in tag:
|
||||
if isinstance(content, dict):
|
||||
total = content.get("total_tokens", 0)
|
||||
return Event(
|
||||
type="token",
|
||||
timestamp=timestamp,
|
||||
tag=tag,
|
||||
title=f"Token: {total}",
|
||||
content=content,
|
||||
loop_id=loop_id,
|
||||
evo_id=evo_id,
|
||||
stage=stage,
|
||||
)
|
||||
|
||||
# Time info
|
||||
if "time_info" in tag:
|
||||
return Event(
|
||||
type="time", timestamp=timestamp, tag=tag, title="Time Info", content=content, loop_id=loop_id, stage=stage
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@st.cache_data(ttl=300, hash_funcs={Path: str})
|
||||
def load_ft_session(log_path: Path) -> Session:
|
||||
"""Load events into hierarchical session structure"""
|
||||
session = Session()
|
||||
storage = FileStorage(log_path)
|
||||
|
||||
events = []
|
||||
for msg in storage.iter_msg():
|
||||
if not msg.tag:
|
||||
continue
|
||||
event = parse_event(msg.tag, msg.content, msg.timestamp)
|
||||
if event:
|
||||
events.append(event)
|
||||
|
||||
# Sort by timestamp
|
||||
events.sort(key=lambda e: e.timestamp)
|
||||
|
||||
# Organize into hierarchy
|
||||
for event in events:
|
||||
if event.loop_id is None:
|
||||
session.init_events.append(event)
|
||||
continue
|
||||
|
||||
# Ensure loop exists
|
||||
if event.loop_id not in session.loops:
|
||||
session.loops[event.loop_id] = Loop(loop_id=event.loop_id)
|
||||
loop = session.loops[event.loop_id]
|
||||
|
||||
# Place event in appropriate stage
|
||||
if event.stage == "exp_gen":
|
||||
loop.exp_gen.append(event)
|
||||
elif event.stage == "coding":
|
||||
if event.evo_id is not None:
|
||||
if event.evo_id not in loop.coding:
|
||||
loop.coding[event.evo_id] = EvoLoop(evo_id=event.evo_id)
|
||||
evo = loop.coding[event.evo_id]
|
||||
evo.events.append(event)
|
||||
# Use evaluator feedback (final_decision) for evo success, fallback to docker_exec
|
||||
if event.type in ("evaluator", "docker_exec") and event.success is not None:
|
||||
if evo.success is None:
|
||||
evo.success = event.success
|
||||
else:
|
||||
evo.success = evo.success and event.success # AND logic: all evaluators must pass
|
||||
else:
|
||||
# Coding events without evo_id go to evo 0
|
||||
if 0 not in loop.coding:
|
||||
loop.coding[0] = EvoLoop(evo_id=0)
|
||||
loop.coding[0].events.append(event)
|
||||
elif event.stage == "runner":
|
||||
loop.runner.append(event)
|
||||
elif event.stage == "feedback":
|
||||
loop.feedback.append(event)
|
||||
else:
|
||||
# Unknown stage - put in exp_gen
|
||||
loop.exp_gen.append(event)
|
||||
|
||||
return session
|
||||
|
||||
|
||||
def get_summary(session: Session) -> dict:
|
||||
"""Get summary statistics"""
|
||||
llm_calls = []
|
||||
docker_execs = []
|
||||
|
||||
# Collect from init
|
||||
for e in session.init_events:
|
||||
if e.type == "llm_call":
|
||||
llm_calls.append(e)
|
||||
elif e.type == "docker_exec":
|
||||
docker_execs.append(e)
|
||||
|
||||
# Collect from loops
|
||||
for loop in session.loops.values():
|
||||
for e in loop.exp_gen + loop.runner + loop.feedback:
|
||||
if e.type == "llm_call":
|
||||
llm_calls.append(e)
|
||||
elif e.type == "docker_exec":
|
||||
docker_execs.append(e)
|
||||
for evo in loop.coding.values():
|
||||
for e in evo.events:
|
||||
if e.type == "llm_call":
|
||||
llm_calls.append(e)
|
||||
elif e.type == "docker_exec":
|
||||
docker_execs.append(e)
|
||||
|
||||
return {
|
||||
"loop_count": len(session.loops),
|
||||
"llm_call_count": len(llm_calls),
|
||||
"llm_total_time": sum(e.duration or 0 for e in llm_calls),
|
||||
"docker_success": sum(1 for e in docker_execs if e.success is True),
|
||||
"docker_fail": sum(1 for e in docker_execs if e.success is False),
|
||||
}
|
||||
@@ -0,0 +1,587 @@
|
||||
"""
|
||||
FT Job Summary View
|
||||
Display summary table for all tasks in a job directory
|
||||
"""
|
||||
|
||||
import pickle
|
||||
from pathlib import Path
|
||||
|
||||
import pandas as pd
|
||||
import streamlit as st
|
||||
from pandas.io.formats.style import Styler
|
||||
|
||||
from rdagent.app.finetune.llm.ui.benchmarks import get_core_metric_score
|
||||
|
||||
|
||||
def is_valid_task(task_path: Path) -> bool:
|
||||
"""Check if directory is a valid FT task (has __session__ subdirectory)"""
|
||||
return task_path.is_dir() and (task_path / "__session__").exists()
|
||||
|
||||
|
||||
def get_loop_dirs(task_path: Path) -> list[Path]:
|
||||
"""Get sorted list of Loop directories"""
|
||||
loops = [d for d in task_path.iterdir() if d.is_dir() and d.name.startswith("Loop_")]
|
||||
return sorted(loops, key=lambda d: int(d.name.split("_")[1]))
|
||||
|
||||
|
||||
def extract_benchmark_score(loop_path: Path, split: str = "") -> tuple[str, float, bool] | None:
|
||||
"""Extract benchmark score, metric name, and direction from loop directory.
|
||||
|
||||
Args:
|
||||
loop_path: Path to loop directory
|
||||
split: Filter by split type ("validation", "test", or "" for any)
|
||||
|
||||
Returns:
|
||||
(metric_name, score, higher_is_better) or None
|
||||
- metric_name includes "(average)" suffix if multiple datasets are averaged
|
||||
- higher_is_better: True if higher values are better
|
||||
"""
|
||||
for pkl_file in loop_path.rglob("**/benchmark_result*/**/*.pkl"):
|
||||
try:
|
||||
with open(pkl_file, "rb") as f:
|
||||
content = pickle.load(f)
|
||||
if isinstance(content, dict):
|
||||
# Check split filter
|
||||
content_split = content.get("split", "")
|
||||
if split and content_split != split:
|
||||
continue
|
||||
|
||||
benchmark_name = content.get("benchmark_name", "")
|
||||
accuracy_summary = content.get("accuracy_summary", {})
|
||||
if isinstance(accuracy_summary, dict) and accuracy_summary:
|
||||
result = get_core_metric_score(benchmark_name, accuracy_summary)
|
||||
if result is not None:
|
||||
return result
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def extract_benchmark_scores(loop_path: Path) -> dict[str, tuple[str, float, bool] | None]:
|
||||
"""Extract both validation and test benchmark scores from loop directory.
|
||||
|
||||
Returns:
|
||||
Dict with keys "validation" and "test", each containing
|
||||
(metric_name, score, higher_is_better) or None
|
||||
"""
|
||||
return {
|
||||
"validation": extract_benchmark_score(loop_path, split="validation"),
|
||||
"test": extract_benchmark_score(loop_path, split="test"),
|
||||
}
|
||||
|
||||
|
||||
def extract_baseline_score(task_path: Path) -> tuple[str, float] | None:
|
||||
"""Extract baseline benchmark score from scenario object (legacy, validation only).
|
||||
|
||||
Returns:
|
||||
(metric_name, score) or None
|
||||
"""
|
||||
scenario_dir = task_path / "scenario"
|
||||
if not scenario_dir.exists():
|
||||
return None
|
||||
|
||||
for pkl_file in scenario_dir.rglob("*.pkl"):
|
||||
try:
|
||||
with open(pkl_file, "rb") as f:
|
||||
scenario = pickle.load(f)
|
||||
baseline_score = getattr(scenario, "baseline_benchmark_score", None)
|
||||
if baseline_score and isinstance(baseline_score, dict):
|
||||
benchmark_name = getattr(scenario, "target_benchmark", "")
|
||||
accuracy_summary = baseline_score.get("accuracy_summary", {})
|
||||
if isinstance(accuracy_summary, dict) and accuracy_summary:
|
||||
result = get_core_metric_score(benchmark_name, accuracy_summary)
|
||||
if result is not None:
|
||||
metric_name, score, _ = result
|
||||
return metric_name, score
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def extract_baseline_scores(task_path: Path) -> dict[str, tuple[str, float, bool] | None]:
|
||||
"""Extract both validation and test baseline benchmark scores from scenario.
|
||||
|
||||
Returns:
|
||||
{"validation": (metric_name, score, higher_is_better) or None,
|
||||
"test": (metric_name, score, higher_is_better) or None}
|
||||
"""
|
||||
scenario_dir = task_path / "scenario"
|
||||
if not scenario_dir.exists():
|
||||
return {"validation": None, "test": None}
|
||||
|
||||
for pkl_file in scenario_dir.rglob("*.pkl"):
|
||||
try:
|
||||
with open(pkl_file, "rb") as f:
|
||||
scenario = pickle.load(f)
|
||||
|
||||
benchmark_name = getattr(scenario, "target_benchmark", "")
|
||||
result = {"validation": None, "test": None}
|
||||
|
||||
# Validation score
|
||||
baseline_val = getattr(scenario, "baseline_benchmark_score", None)
|
||||
if baseline_val and isinstance(baseline_val, dict):
|
||||
accuracy_summary = baseline_val.get("accuracy_summary", {})
|
||||
if isinstance(accuracy_summary, dict) and accuracy_summary:
|
||||
core = get_core_metric_score(benchmark_name, accuracy_summary)
|
||||
if core:
|
||||
result["validation"] = core
|
||||
|
||||
# Test score (new format only)
|
||||
baseline_test = getattr(scenario, "baseline_benchmark_score_test", None)
|
||||
if baseline_test and isinstance(baseline_test, dict):
|
||||
accuracy_summary = baseline_test.get("accuracy_summary", {})
|
||||
if isinstance(accuracy_summary, dict) and accuracy_summary:
|
||||
core = get_core_metric_score(benchmark_name, accuracy_summary)
|
||||
if core:
|
||||
result["test"] = core
|
||||
|
||||
return result
|
||||
except Exception:
|
||||
pass
|
||||
return {"validation": None, "test": None}
|
||||
|
||||
|
||||
def get_loop_status(
|
||||
task_path: Path, loop_id: int
|
||||
) -> tuple[str, float | None, float | None, str | None, bool | None, bool]:
|
||||
"""
|
||||
Get loop status, validation score, test score, metric name with direction arrow, feedback decision, and direction.
|
||||
Returns: (status_str, val_score_or_none, test_score_or_none, metric_display_or_none, feedback_decision, higher_is_better)
|
||||
Status: 'C'=Coding, 'R'=Running, 'X'=Failed, score_str=Success
|
||||
metric_display: metric name with direction arrow (e.g., "accuracy ↑")
|
||||
feedback_decision: True=accepted, False=rejected, None=no feedback
|
||||
higher_is_better: True if higher values are better for this metric
|
||||
"""
|
||||
loop_path = task_path / f"Loop_{loop_id}"
|
||||
if not loop_path.exists():
|
||||
return "-", None, None, None, None, True
|
||||
|
||||
# Check for benchmark results first (highest priority - means completed)
|
||||
scores = extract_benchmark_scores(loop_path)
|
||||
val_result = scores.get("validation")
|
||||
test_result = scores.get("test")
|
||||
|
||||
# Fallback to old format (no split) if no validation/test found
|
||||
if val_result is None and test_result is None:
|
||||
legacy_result = extract_benchmark_score(loop_path, split="")
|
||||
if legacy_result is not None:
|
||||
val_result = legacy_result # Treat legacy as validation
|
||||
|
||||
# Get feedback decision (used for both score coloring and fallback status)
|
||||
feedback_decision = None
|
||||
feedback_files = list(loop_path.rglob("**/feedback/**/*.pkl"))
|
||||
for f in feedback_files:
|
||||
try:
|
||||
with open(f, "rb") as fp:
|
||||
content = pickle.load(fp)
|
||||
decision = getattr(content, "decision", None)
|
||||
if decision is not None:
|
||||
feedback_decision = decision
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if val_result is not None:
|
||||
metric_name, val_score, higher_is_better = val_result
|
||||
test_score = test_result[1] if test_result else None
|
||||
arrow = "↑" if higher_is_better else "↓"
|
||||
metric_display = f"{metric_name} {arrow}"
|
||||
# Format: "val/test" or just "val" if no test
|
||||
if test_score is not None:
|
||||
status_str = f"{val_score:.2f}/{test_score:.2f}"
|
||||
else:
|
||||
status_str = f"{val_score:.2f}"
|
||||
return status_str, val_score, test_score, metric_display, feedback_decision, higher_is_better
|
||||
|
||||
# Check feedback stage (no benchmark result, use feedback decision directly)
|
||||
if feedback_decision is not None:
|
||||
return ("OK" if feedback_decision else "X"), None, None, None, feedback_decision, True
|
||||
|
||||
# Check running stage
|
||||
running_files = list(loop_path.rglob("**/running/**/*.pkl"))
|
||||
if running_files:
|
||||
return "R", None, None, None, None, True
|
||||
|
||||
# Check coding stage
|
||||
coding_files = list(loop_path.rglob("**/coding/**/*.pkl"))
|
||||
if coding_files:
|
||||
return "C", None, None, None, None, True
|
||||
|
||||
# Has directory but no recognized files
|
||||
return "?", None, None, None, None, True
|
||||
|
||||
|
||||
def get_max_loops(job_path: Path) -> int:
|
||||
"""Get maximum number of loops across all tasks"""
|
||||
max_loops = 0
|
||||
for task_dir in job_path.iterdir():
|
||||
if is_valid_task(task_dir):
|
||||
loops = get_loop_dirs(task_dir)
|
||||
max_loops = max(max_loops, len(loops))
|
||||
return max_loops
|
||||
|
||||
|
||||
def get_job_summary_df(job_path: Path) -> tuple[pd.DataFrame, pd.DataFrame]:
|
||||
"""Generate summary DataFrame and decision DataFrame for all tasks in job
|
||||
|
||||
Each loop column shows "val/test" format when both scores are available.
|
||||
Best columns show the best validation and test scores separately.
|
||||
|
||||
Returns:
|
||||
(df, decisions_df): df is display data, decisions_df has same structure
|
||||
but values are True/False/None for feedback decision
|
||||
"""
|
||||
if not job_path.exists():
|
||||
return pd.DataFrame(), pd.DataFrame()
|
||||
|
||||
tasks = [d for d in sorted(job_path.iterdir(), reverse=True) if is_valid_task(d)]
|
||||
if not tasks:
|
||||
return pd.DataFrame(), pd.DataFrame()
|
||||
|
||||
max_loops = get_max_loops(job_path)
|
||||
if max_loops == 0:
|
||||
max_loops = 10 # Default display columns
|
||||
|
||||
data = []
|
||||
decisions_data = []
|
||||
for task_path in tasks:
|
||||
row = {"Task": task_path.name}
|
||||
decision_row = {"Task": task_path.name}
|
||||
best_val_score = None
|
||||
best_test_score = None
|
||||
best_metric = None
|
||||
best_higher_is_better = True # Default to higher is better
|
||||
|
||||
# Extract baseline scores (validation and test) from scenario
|
||||
baseline_scores = extract_baseline_scores(task_path)
|
||||
val_baseline = baseline_scores.get("validation")
|
||||
test_baseline = baseline_scores.get("test")
|
||||
if val_baseline and test_baseline:
|
||||
row["Baseline"] = f"{val_baseline[1]:.2f}/{test_baseline[1]:.2f}"
|
||||
elif val_baseline:
|
||||
row["Baseline"] = f"{val_baseline[1]:.2f}"
|
||||
else:
|
||||
row["Baseline"] = "-"
|
||||
decision_row["Baseline"] = None
|
||||
|
||||
for i in range(max_loops):
|
||||
status, val_score, test_score, metric_name, feedback_decision, higher_is_better = get_loop_status(
|
||||
task_path, i
|
||||
)
|
||||
row[f"L{i}"] = status
|
||||
decision_row[f"L{i}"] = feedback_decision
|
||||
if val_score is not None:
|
||||
# Use higher_is_better to determine if this score is better
|
||||
if best_val_score is None:
|
||||
best_val_score = val_score
|
||||
best_higher_is_better = higher_is_better
|
||||
best_metric = metric_name
|
||||
elif (higher_is_better and val_score > best_val_score) or (
|
||||
not higher_is_better and val_score < best_val_score
|
||||
):
|
||||
best_val_score = val_score
|
||||
best_higher_is_better = higher_is_better
|
||||
best_metric = metric_name
|
||||
if test_score is not None:
|
||||
# Use same direction as validation score for consistency
|
||||
if best_test_score is None:
|
||||
best_test_score = test_score
|
||||
elif (best_higher_is_better and test_score > best_test_score) or (
|
||||
not best_higher_is_better and test_score < best_test_score
|
||||
):
|
||||
best_test_score = test_score
|
||||
|
||||
# Show best validation and test scores
|
||||
if best_val_score is not None and best_test_score is not None:
|
||||
row["Best"] = f"{best_val_score:.2f}/{best_test_score:.2f}"
|
||||
elif best_val_score is not None:
|
||||
row["Best"] = f"{best_val_score:.2f}"
|
||||
else:
|
||||
row["Best"] = "-"
|
||||
row["Metric"] = best_metric if best_metric else "-"
|
||||
decision_row["Metric"] = None
|
||||
decision_row["Best"] = None
|
||||
data.append(row)
|
||||
decisions_data.append(decision_row)
|
||||
|
||||
# Ensure column order: Task, Metric, Baseline, L0, L1, ..., Best
|
||||
df = pd.DataFrame(data)
|
||||
decisions_df = pd.DataFrame(decisions_data)
|
||||
if not df.empty:
|
||||
loop_cols = [c for c in df.columns if c.startswith("L")]
|
||||
cols = ["Task", "Metric", "Baseline"] + sorted(loop_cols, key=lambda x: int(x[1:])) + ["Best"]
|
||||
df = df[cols]
|
||||
decisions_df = decisions_df[cols]
|
||||
return df, decisions_df
|
||||
|
||||
|
||||
def style_status_cell(val: str, decision: bool | None = None) -> str:
|
||||
"""Style cell based on status value and feedback decision
|
||||
|
||||
Args:
|
||||
val: The cell value
|
||||
decision: True=accepted (green), False=rejected (red), None=no feedback (gray)
|
||||
"""
|
||||
if val == "-":
|
||||
return "color: #888"
|
||||
if val == "C":
|
||||
return "color: #f0ad4e; font-weight: bold" # Orange for coding
|
||||
if val == "R":
|
||||
return "color: #5bc0de; font-weight: bold" # Blue for running
|
||||
if val == "X":
|
||||
return "color: #d9534f; font-weight: bold" # Red for failed
|
||||
if val == "OK":
|
||||
return "color: #5cb85c; font-weight: bold" # Green for success
|
||||
if val == "?":
|
||||
return "color: #888"
|
||||
|
||||
# Check if it's a numeric score (with optional "/" separator)
|
||||
is_numeric = False
|
||||
try:
|
||||
float(val)
|
||||
is_numeric = True
|
||||
except ValueError:
|
||||
if "/" in val:
|
||||
parts = val.split("/")
|
||||
try:
|
||||
float(parts[0])
|
||||
is_numeric = True
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if is_numeric:
|
||||
# Use decision for coloring (use == instead of is for numpy.bool_ compatibility)
|
||||
if decision == True:
|
||||
return "color: #5cb85c; font-weight: bold" # Green for accepted
|
||||
elif decision == False:
|
||||
return "color: #d9534f; font-weight: bold" # Red for rejected
|
||||
else:
|
||||
return "color: #888" # Gray for no feedback
|
||||
|
||||
return ""
|
||||
|
||||
|
||||
def style_df_with_decisions(df: pd.DataFrame, decisions_df: pd.DataFrame) -> Styler:
|
||||
"""Apply styling to dataframe based on decision data
|
||||
|
||||
Args:
|
||||
df: Display dataframe
|
||||
decisions_df: DataFrame with same shape, containing True/False/None values
|
||||
"""
|
||||
|
||||
def apply_styles(row_idx: int, col: str) -> str:
|
||||
val = df.iloc[row_idx][col]
|
||||
decision = decisions_df.iloc[row_idx][col] if col in decisions_df.columns else None
|
||||
return style_status_cell(str(val), decision)
|
||||
|
||||
# Build style matrix
|
||||
styles = pd.DataFrame("", index=df.index, columns=df.columns)
|
||||
for row_idx in range(len(df)):
|
||||
for col in df.columns:
|
||||
styles.iloc[row_idx][col] = apply_styles(row_idx, col)
|
||||
|
||||
return df.style.apply(lambda _: styles, axis=None)
|
||||
|
||||
|
||||
def render_job_summary(job_path: Path, is_root: bool = False) -> None:
|
||||
"""Render job summary UI"""
|
||||
title = "Standalone Tasks" if is_root else f"Job: {job_path.name}"
|
||||
st.subheader(title)
|
||||
|
||||
df, decisions_df = get_job_summary_df(job_path)
|
||||
if df.empty:
|
||||
st.warning("No valid tasks found in this job directory")
|
||||
return
|
||||
|
||||
# Display legend
|
||||
st.markdown(
|
||||
"**Legend:** "
|
||||
"<span style='color:#f0ad4e'>C</span>=Coding, "
|
||||
"<span style='color:#5bc0de'>R</span>=Running, "
|
||||
"<span style='color:#5cb85c'>Score</span>=Accepted, "
|
||||
"<span style='color:#d9534f'>Score/X</span>=Rejected/Failed, "
|
||||
"<span style='color:#888'>Score</span>=No feedback",
|
||||
unsafe_allow_html=True,
|
||||
)
|
||||
|
||||
# Style and display dataframe
|
||||
styled_df = style_df_with_decisions(df, decisions_df)
|
||||
st.dataframe(styled_df, use_container_width=True, hide_index=True)
|
||||
|
||||
# Summary stats
|
||||
col1, col2, col3 = st.columns(3)
|
||||
with col1:
|
||||
st.metric("Tasks", len(df))
|
||||
with col2:
|
||||
# Count tasks with any score
|
||||
tasks_with_score = df["Best"].apply(lambda x: x != "-").sum()
|
||||
st.metric("With Score", tasks_with_score)
|
||||
with col3:
|
||||
# Count tasks with at least one improved loop (decision=True)
|
||||
loop_cols = [c for c in decisions_df.columns if c.startswith("L")]
|
||||
tasks_improved = decisions_df[loop_cols].apply(lambda row: any(v is True for v in row), axis=1).sum()
|
||||
st.metric("Improved", tasks_improved)
|
||||
|
||||
# Detailed scores table
|
||||
render_task_detail_selector(job_path)
|
||||
|
||||
|
||||
def extract_full_benchmark(loop_path: Path, split: str = "") -> dict | None:
|
||||
"""Extract full accuracy_summary from loop directory.
|
||||
|
||||
Args:
|
||||
loop_path: Path to loop directory
|
||||
split: Filter by split type ("validation", "test", or "" for any)
|
||||
|
||||
Returns:
|
||||
accuracy_summary dict {dataset: {metric: value, ...}, ...} or None
|
||||
"""
|
||||
for pkl_file in loop_path.rglob("**/benchmark_result*/**/*.pkl"):
|
||||
try:
|
||||
with open(pkl_file, "rb") as f:
|
||||
content = pickle.load(f)
|
||||
if isinstance(content, dict):
|
||||
# Check split filter
|
||||
content_split = content.get("split", "")
|
||||
if split and content_split != split:
|
||||
continue
|
||||
|
||||
accuracy_summary = content.get("accuracy_summary", {})
|
||||
if isinstance(accuracy_summary, dict) and accuracy_summary:
|
||||
return accuracy_summary
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def extract_baseline_full_benchmark(task_path: Path, split: str = "validation") -> dict | None:
|
||||
"""Extract full accuracy_summary from baseline scenario.
|
||||
|
||||
Args:
|
||||
task_path: Path to task directory
|
||||
split: "validation" or "test"
|
||||
|
||||
Returns:
|
||||
accuracy_summary dict or None
|
||||
"""
|
||||
scenario_dir = task_path / "scenario"
|
||||
if not scenario_dir.exists():
|
||||
return None
|
||||
|
||||
for pkl_file in scenario_dir.rglob("*.pkl"):
|
||||
try:
|
||||
with open(pkl_file, "rb") as f:
|
||||
scenario = pickle.load(f)
|
||||
|
||||
if split == "validation":
|
||||
baseline = getattr(scenario, "baseline_benchmark_score", None)
|
||||
else:
|
||||
baseline = getattr(scenario, "baseline_benchmark_score_test", None)
|
||||
|
||||
if baseline and isinstance(baseline, dict):
|
||||
accuracy_summary = baseline.get("accuracy_summary", {})
|
||||
if isinstance(accuracy_summary, dict) and accuracy_summary:
|
||||
return accuracy_summary
|
||||
except Exception:
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def get_task_full_benchmark_df(task_path: Path, split: str) -> pd.DataFrame:
|
||||
"""Generate full benchmark table for a single task and split.
|
||||
|
||||
Returns DataFrame with columns: Dataset, Metric, Baseline, Loop_0, Loop_1, ...
|
||||
Each row is a dataset-metric combination.
|
||||
"""
|
||||
# Collect all sources (Baseline + Loops)
|
||||
sources = ["Baseline"]
|
||||
loop_dirs = sorted(
|
||||
[d for d in task_path.iterdir() if d.is_dir() and d.name.startswith("Loop_")],
|
||||
key=lambda x: int(x.name.split("_")[1]),
|
||||
)
|
||||
sources.extend([d.name for d in loop_dirs])
|
||||
|
||||
# Collect all accuracy_summaries
|
||||
all_summaries = {}
|
||||
|
||||
# Baseline
|
||||
baseline_summary = extract_baseline_full_benchmark(task_path, split)
|
||||
if baseline_summary:
|
||||
all_summaries["Baseline"] = baseline_summary
|
||||
|
||||
# Loops
|
||||
for loop_dir in loop_dirs:
|
||||
loop_summary = extract_full_benchmark(loop_dir, split)
|
||||
if loop_summary:
|
||||
all_summaries[loop_dir.name] = loop_summary
|
||||
|
||||
if not all_summaries:
|
||||
return pd.DataFrame()
|
||||
|
||||
# Collect all dataset-metric combinations
|
||||
all_keys = set()
|
||||
for summary in all_summaries.values():
|
||||
for dataset, metrics in summary.items():
|
||||
if isinstance(metrics, dict):
|
||||
for metric in metrics.keys():
|
||||
all_keys.add((dataset, metric))
|
||||
|
||||
# Sort keys for consistent display
|
||||
all_keys = sorted(all_keys)
|
||||
|
||||
# Build table data
|
||||
data = []
|
||||
for dataset, metric in all_keys:
|
||||
row = {"Dataset": dataset, "Metric": metric}
|
||||
for source in sources:
|
||||
summary = all_summaries.get(source, {})
|
||||
metrics_dict = summary.get(dataset, {})
|
||||
value = metrics_dict.get(metric) if isinstance(metrics_dict, dict) else None
|
||||
if value is not None:
|
||||
row[source] = f"{value:.2f}" if isinstance(value, float) else str(value)
|
||||
else:
|
||||
row[source] = "-"
|
||||
data.append(row)
|
||||
|
||||
df = pd.DataFrame(data)
|
||||
# Ensure column order
|
||||
if not df.empty:
|
||||
cols = ["Dataset", "Metric"] + [s for s in sources if s in df.columns]
|
||||
df = df[cols]
|
||||
return df
|
||||
|
||||
|
||||
def render_task_detail_selector(job_path: Path) -> None:
|
||||
"""Render task selector dropdown and full benchmark tables."""
|
||||
tasks = [d for d in sorted(job_path.iterdir(), reverse=True) if is_valid_task(d)]
|
||||
if not tasks:
|
||||
return
|
||||
|
||||
st.markdown("---")
|
||||
st.subheader("Detailed Benchmark Scores")
|
||||
|
||||
# Task selector dropdown
|
||||
task_names = [t.name for t in tasks]
|
||||
selected_task = st.selectbox("Select Task", options=task_names, index=0, key="task_detail_selector")
|
||||
|
||||
if selected_task:
|
||||
task_path = job_path / selected_task
|
||||
|
||||
# Display Validation and Test tables side by side
|
||||
col1, col2 = st.columns(2)
|
||||
|
||||
with col1:
|
||||
st.markdown("**Validation**")
|
||||
df_val = get_task_full_benchmark_df(task_path, "validation")
|
||||
if not df_val.empty:
|
||||
st.dataframe(df_val, use_container_width=True, hide_index=True)
|
||||
else:
|
||||
st.info("No validation scores")
|
||||
|
||||
with col2:
|
||||
st.markdown("**Test**")
|
||||
df_test = get_task_full_benchmark_df(task_path, "test")
|
||||
if not df_test.empty:
|
||||
st.dataframe(df_test, use_container_width=True, hide_index=True)
|
||||
else:
|
||||
st.info("No test scores")
|
||||
@@ -0,0 +1,52 @@
|
||||
from pathlib import Path
|
||||
|
||||
from rdagent.components.coder.CoSTEER.evaluators import (
|
||||
CoSTEEREvaluator,
|
||||
CoSTEERSingleFeedback,
|
||||
)
|
||||
from rdagent.core.experiment import FBWorkspace, Task
|
||||
from rdagent.core.scenario import Scenario
|
||||
from rdagent.utils.agent.tpl import T
|
||||
from rdagent.utils.agent.workflow import build_cls_from_json_with_retry
|
||||
|
||||
|
||||
class PrevModelLoadEvaluator(CoSTEEREvaluator):
|
||||
"""This evaluator checks whether the code actually loads a model from `prev_model`."""
|
||||
|
||||
def __init__(self, scen: Scenario):
|
||||
super().__init__(scen)
|
||||
|
||||
def evaluate(
|
||||
self, target_task: Task, implementation: FBWorkspace, gt_implementation: FBWorkspace, *args, **kwargs
|
||||
) -> CoSTEERSingleFeedback:
|
||||
data_source_path = T("scenarios.data_science.share:scen.input_path").r()
|
||||
prev_model_dir = Path(data_source_path) / "prev_model"
|
||||
|
||||
# 1) Inspect the code itself for references to prev_model loading
|
||||
code_str = implementation.file_dict["main.py"]
|
||||
code_contain_prev = "prev_model" in code_str
|
||||
print(f"Code references prev_model: {code_contain_prev}")
|
||||
if not code_contain_prev:
|
||||
err = (
|
||||
"No evidence found that your code loads a model from `prev_model`. "
|
||||
"Please check that you are calling the correct load function "
|
||||
f"and pointing it to the `{prev_model_dir}` directory."
|
||||
)
|
||||
return CoSTEERSingleFeedback(
|
||||
execution=err,
|
||||
return_checking=err,
|
||||
code=err,
|
||||
final_decision=False,
|
||||
)
|
||||
|
||||
system_prompt = T(".prompts:prev_model_eval.system").r()
|
||||
user_prompt = T(".prompts:prev_model_eval.user").r(
|
||||
code=implementation.all_codes,
|
||||
)
|
||||
|
||||
csfb = build_cls_from_json_with_retry(
|
||||
CoSTEERSingleFeedback,
|
||||
system_prompt=system_prompt,
|
||||
user_prompt=user_prompt,
|
||||
)
|
||||
return csfb
|
||||
@@ -0,0 +1,23 @@
|
||||
prev_model_eval:
|
||||
system: |-
|
||||
You are a data scientist tasked with evaluating code generation.
|
||||
|
||||
You will receive the following information:
|
||||
- The implemented code
|
||||
|
||||
Focus on these aspects:
|
||||
- Check if the code load the model in the "prev_model/" subfolder.
|
||||
|
||||
Please respond with your feedback in the following JSON format and order
|
||||
```json
|
||||
{
|
||||
"execution": "Describe whether the code executed successfully. Include any errors or issues encountered, and append all error messages and full traceback details without summarizing or omitting any information. ."
|
||||
"return_checking": "Detect whether the model is loaded from 'prev_model/' subfolder and finetune is prepared based on prev model.",
|
||||
"code": "The code has explicity load the model from 'prev_model/' subfolder and prepares finetune based on prev model.",
|
||||
"final_decision": <true or false in boolean type; only return true when ensuring that the code loads the model from 'prev_model/' subfolder and prepares finetune based on prev model.>
|
||||
}
|
||||
```
|
||||
|
||||
user: |-
|
||||
------------ The implemented code ------------
|
||||
{{code}}
|
||||
@@ -1,9 +1,3 @@
|
||||
from dotenv import load_dotenv
|
||||
|
||||
from rdagent.scenarios.general_model.scenario import GeneralModelScenario
|
||||
|
||||
load_dotenv(override=True)
|
||||
|
||||
import fire
|
||||
|
||||
from rdagent.components.coder.model_coder.task_loader import (
|
||||
@@ -13,6 +7,7 @@ from rdagent.components.document_reader.document_reader import (
|
||||
extract_first_page_screenshot_from_pdf,
|
||||
)
|
||||
from rdagent.log import rdagent_logger as logger
|
||||
from rdagent.scenarios.general_model.scenario import GeneralModelScenario
|
||||
from rdagent.scenarios.qlib.developer.model_coder import QlibModelCoSTEER
|
||||
|
||||
|
||||
@@ -35,19 +30,15 @@ def extract_models_and_implement(report_file_path: str) -> None:
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
with logger.tag("init"):
|
||||
scenario = GeneralModelScenario()
|
||||
logger.log_object(scenario, tag="scenario")
|
||||
with logger.tag("r"):
|
||||
# Save Relevant Images
|
||||
img = extract_first_page_screenshot_from_pdf(report_file_path)
|
||||
logger.log_object(img, tag="pdf_image")
|
||||
exp = ModelExperimentLoaderFromPDFfiles().load(report_file_path)
|
||||
logger.log_object(exp, tag="load_experiment")
|
||||
with logger.tag("d"):
|
||||
exp = QlibModelCoSTEER(scenario).develop(exp)
|
||||
logger.log_object(exp, tag="developed_experiment")
|
||||
return exp
|
||||
scenario = GeneralModelScenario()
|
||||
logger.log_object(scenario, tag="scenario")
|
||||
# Save Relevant Images
|
||||
img = extract_first_page_screenshot_from_pdf(report_file_path)
|
||||
logger.log_object(img, tag="pdf_image")
|
||||
exp = ModelExperimentLoaderFromPDFfiles().load(report_file_path)
|
||||
logger.log_object(exp, tag="load_experiment")
|
||||
exp = QlibModelCoSTEER(scenario).develop(exp)
|
||||
logger.log_object(exp, tag="developed_experiment")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
+75
-21
@@ -1,42 +1,96 @@
|
||||
from pathlib import Path
|
||||
from pydantic_settings import SettingsConfigDict
|
||||
|
||||
from pydantic_settings import BaseSettings
|
||||
|
||||
from rdagent.components.workflow.conf import BasePropSetting
|
||||
from rdagent.core.conf import ExtendedBaseSettings
|
||||
|
||||
|
||||
class PropSetting(BasePropSetting):
|
||||
class Config:
|
||||
env_prefix = "KG_"
|
||||
"""Use `KG_` as prefix for environment variables"""
|
||||
protected_namespaces = ()
|
||||
"""Add 'model_' to the protected namespaces"""
|
||||
class KaggleBasePropSetting(ExtendedBaseSettings):
|
||||
model_config = SettingsConfigDict(env_prefix="KG_", protected_namespaces=())
|
||||
|
||||
# 1) overriding the default
|
||||
scen: str = "rdagent.scenarios.kaggle.experiment.model_experiment.KGModelScenario"
|
||||
scen: str = "rdagent.scenarios.kaggle.experiment.scenario.KGScenario"
|
||||
"""Scenario class for data mining model"""
|
||||
|
||||
hypothesis_gen: str = "rdagent.scenarios.kaggle.proposal.model_proposal.KGModelHypothesisGen"
|
||||
hypothesis_gen: str = "rdagent.scenarios.kaggle.proposal.proposal.KGHypothesisGen"
|
||||
"""Hypothesis generation class"""
|
||||
|
||||
hypothesis2experiment: str = "rdagent.scenarios.kaggle.proposal.model_proposal.KGModelHypothesis2Experiment"
|
||||
hypothesis2experiment: str = "rdagent.scenarios.kaggle.proposal.proposal.KGHypothesis2Experiment"
|
||||
"""Hypothesis to experiment class"""
|
||||
|
||||
coder: str = "rdagent.scenarios.kaggle.developer.model_coder.KGModelCoSTEER"
|
||||
"""Coder class"""
|
||||
feature_coder: str = "rdagent.scenarios.kaggle.developer.coder.KGFactorCoSTEER"
|
||||
"""Feature Coder class"""
|
||||
|
||||
runner: str = "rdagent.scenarios.kaggle.developer.model_runner.KGModelRunner"
|
||||
"""Runner class"""
|
||||
model_feature_selection_coder: str = "rdagent.scenarios.kaggle.developer.coder.KGModelFeatureSelectionCoder"
|
||||
"""Model Feature Selection Coder class"""
|
||||
|
||||
summarizer: str = "rdagent.scenarios.kaggle.developer.feedback.KGModelHypothesisExperiment2Feedback"
|
||||
model_coder: str = "rdagent.scenarios.kaggle.developer.coder.KGModelCoSTEER"
|
||||
"""Model Coder class"""
|
||||
|
||||
feature_runner: str = "rdagent.scenarios.kaggle.developer.runner.KGFactorRunner"
|
||||
"""Feature Runner class"""
|
||||
|
||||
model_runner: str = "rdagent.scenarios.kaggle.developer.runner.KGModelRunner"
|
||||
"""Model Runner class"""
|
||||
|
||||
summarizer: str = "rdagent.scenarios.kaggle.developer.feedback.KGExperiment2Feedback"
|
||||
"""Summarizer class"""
|
||||
|
||||
evolving_n: int = 10
|
||||
"""Number of evolutions"""
|
||||
|
||||
evolving_n: int = 10
|
||||
|
||||
competition: str = ""
|
||||
"""Kaggle competition name, e.g., 'sf-crime'"""
|
||||
|
||||
template_path: str = "rdagent/scenarios/kaggle/experiment/templates"
|
||||
"""Kaggle competition base templates path"""
|
||||
|
||||
local_data_path: str = ""
|
||||
"""Folder storing Kaggle competition data"""
|
||||
|
||||
# Evaluation on Test related
|
||||
if_using_mle_data: bool = False
|
||||
auto_submit: bool = False
|
||||
"""Automatically upload and submit each experiment result to Kaggle platform"""
|
||||
|
||||
# Conditionally set the knowledge_base based on the use of graph RAG
|
||||
knowledge_base: str = ""
|
||||
"""Knowledge base class, uses 'KGKnowledgeGraph' when advanced graph-based RAG is enabled, otherwise empty."""
|
||||
if_action_choosing_based_on_UCB: bool = False
|
||||
"""Enable decision mechanism based on UCB algorithm"""
|
||||
|
||||
domain_knowledge_path: str = "/data/userdata/share/kaggle/domain_knowledge"
|
||||
"""Folder storing domain knowledge files in .case format"""
|
||||
|
||||
knowledge_base_path: str = "kg_graph.pkl"
|
||||
"""Advanced version of graph-based RAG"""
|
||||
|
||||
rag_path: str = "git_ignore_folder/kaggle_vector_base.pkl"
|
||||
"""Base version of vector-based RAG"""
|
||||
|
||||
if_using_vector_rag: bool = False
|
||||
"""Enable basic vector-based RAG"""
|
||||
|
||||
if_using_graph_rag: bool = False
|
||||
"""Enable advanced graph-based RAG"""
|
||||
|
||||
mini_case: bool = False
|
||||
"""Enable mini-case study for experiments"""
|
||||
|
||||
time_ratio_limit_to_enable_hyperparameter_tuning: float = 1
|
||||
"""
|
||||
Runner time ratio limit to enable hyperparameter tuning, if not change, hyperparameter tuning is always enabled in the first evolution.
|
||||
"""
|
||||
|
||||
res_time_ratio_limit_to_enable_hyperparameter_tuning: float = 1
|
||||
"""
|
||||
Overall rest time ratio limit to enable hyperparameter tuning, if not change, hyperparameter tuning is always enabled in the first evolution.
|
||||
`1` indicate we enable hyperparameter tuning when we have 100% residual time. (so hyperparameter tuning is always enabled)
|
||||
"""
|
||||
|
||||
only_first_loop_enable_hyperparameter_tuning: bool = True
|
||||
"""Enable hyperparameter tuning feedback only in the first loop of evaluation."""
|
||||
|
||||
only_enable_tuning_in_merge: bool = False
|
||||
"""Enable hyperparameter tuning only in the merge stage"""
|
||||
|
||||
|
||||
PROP_SETTING = PropSetting()
|
||||
KAGGLE_IMPLEMENT_SETTING = KaggleBasePropSetting()
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
import subprocess
|
||||
from typing import Any
|
||||
|
||||
import fire
|
||||
|
||||
from rdagent.app.kaggle.conf import KAGGLE_IMPLEMENT_SETTING
|
||||
from rdagent.components.workflow.conf import BasePropSetting
|
||||
from rdagent.components.workflow.rd_loop import RDLoop
|
||||
from rdagent.core.developer import Developer
|
||||
from rdagent.core.exception import CoderError, FactorEmptyError, ModelEmptyError
|
||||
from rdagent.core.proposal import (
|
||||
Experiment2Feedback,
|
||||
Hypothesis2Experiment,
|
||||
HypothesisGen,
|
||||
)
|
||||
from rdagent.core.scenario import Scenario
|
||||
from rdagent.core.utils import import_class
|
||||
from rdagent.log import rdagent_logger as logger
|
||||
from rdagent.scenarios.kaggle.experiment.scenario import (
|
||||
KG_ACTION_FEATURE_ENGINEERING,
|
||||
KG_ACTION_FEATURE_PROCESSING,
|
||||
KG_ACTION_MODEL_FEATURE_SELECTION,
|
||||
)
|
||||
from rdagent.scenarios.kaggle.experiment.utils import python_files_to_notebook
|
||||
from rdagent.scenarios.kaggle.kaggle_crawler import download_data
|
||||
from rdagent.scenarios.kaggle.proposal.proposal import KGTrace
|
||||
|
||||
|
||||
class KaggleRDLoop(RDLoop):
|
||||
def __init__(self, PROP_SETTING: BasePropSetting):
|
||||
scen: Scenario = import_class(PROP_SETTING.scen)(PROP_SETTING.competition)
|
||||
logger.log_object(scen, tag="scenario")
|
||||
knowledge_base = (
|
||||
import_class(PROP_SETTING.knowledge_base)(PROP_SETTING.knowledge_base_path, scen)
|
||||
if PROP_SETTING.knowledge_base != ""
|
||||
else None
|
||||
)
|
||||
logger.log_object(knowledge_base, tag="knowledge_base")
|
||||
self.hypothesis_gen: HypothesisGen = import_class(PROP_SETTING.hypothesis_gen)(scen)
|
||||
logger.log_object(self.hypothesis_gen, tag="hypothesis generator")
|
||||
self.hypothesis2experiment: Hypothesis2Experiment = import_class(PROP_SETTING.hypothesis2experiment)()
|
||||
logger.log_object(self.hypothesis2experiment, tag="hypothesis2experiment")
|
||||
self.feature_coder: Developer = import_class(PROP_SETTING.feature_coder)(scen)
|
||||
logger.log_object(self.feature_coder, tag="feature coder")
|
||||
self.model_feature_selection_coder: Developer = import_class(PROP_SETTING.model_feature_selection_coder)(scen)
|
||||
logger.log_object(self.model_feature_selection_coder, tag="model feature selection coder")
|
||||
self.model_coder: Developer = import_class(PROP_SETTING.model_coder)(scen)
|
||||
logger.log_object(self.model_coder, tag="model coder")
|
||||
self.feature_runner: Developer = import_class(PROP_SETTING.feature_runner)(scen)
|
||||
logger.log_object(self.feature_runner, tag="feature runner")
|
||||
self.model_runner: Developer = import_class(PROP_SETTING.model_runner)(scen)
|
||||
logger.log_object(self.model_runner, tag="model runner")
|
||||
self.summarizer: Experiment2Feedback = import_class(PROP_SETTING.summarizer)(scen)
|
||||
logger.log_object(self.summarizer, tag="summarizer")
|
||||
self.trace = KGTrace(scen=scen, knowledge_base=knowledge_base)
|
||||
super(RDLoop, self).__init__()
|
||||
|
||||
def coding(self, prev_out: dict[str, Any]):
|
||||
if prev_out["direct_exp_gen"]["propose"].action in [
|
||||
KG_ACTION_FEATURE_ENGINEERING,
|
||||
KG_ACTION_FEATURE_PROCESSING,
|
||||
]:
|
||||
exp = self.feature_coder.develop(prev_out["direct_exp_gen"]["exp_gen"])
|
||||
elif prev_out["direct_exp_gen"]["propose"].action == KG_ACTION_MODEL_FEATURE_SELECTION:
|
||||
exp = self.model_feature_selection_coder.develop(prev_out["direct_exp_gen"]["exp_gen"])
|
||||
else:
|
||||
exp = self.model_coder.develop(prev_out["direct_exp_gen"]["exp_gen"])
|
||||
logger.log_object(exp.sub_workspace_list, tag="coder result")
|
||||
return exp
|
||||
|
||||
def running(self, prev_out: dict[str, Any]):
|
||||
if prev_out["direct_exp_gen"]["propose"].action in [
|
||||
KG_ACTION_FEATURE_ENGINEERING,
|
||||
KG_ACTION_FEATURE_PROCESSING,
|
||||
]:
|
||||
exp = self.feature_runner.develop(prev_out["coding"])
|
||||
else:
|
||||
exp = self.model_runner.develop(prev_out["coding"])
|
||||
logger.log_object(exp, tag="runner result")
|
||||
if KAGGLE_IMPLEMENT_SETTING.competition in [
|
||||
"optiver-realized-volatility-prediction",
|
||||
"covid19-global-forecasting-week-1",
|
||||
]:
|
||||
try:
|
||||
python_files_to_notebook(KAGGLE_IMPLEMENT_SETTING.competition, exp.experiment_workspace.workspace_path)
|
||||
except Exception as e:
|
||||
logger.error(f"Merge python files to one file failed: {e}")
|
||||
if KAGGLE_IMPLEMENT_SETTING.auto_submit:
|
||||
csv_path = exp.experiment_workspace.workspace_path / "submission.csv"
|
||||
try:
|
||||
subprocess.run(
|
||||
[
|
||||
"kaggle",
|
||||
"competitions",
|
||||
"submit",
|
||||
"-f",
|
||||
str(csv_path.absolute()),
|
||||
"-m",
|
||||
str(csv_path.parent.absolute()),
|
||||
KAGGLE_IMPLEMENT_SETTING.competition,
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
logger.error(f"Auto submission failed: \n{e}")
|
||||
except Exception as e:
|
||||
logger.error(f"Other exception when use kaggle api:\n{e}")
|
||||
|
||||
return exp
|
||||
|
||||
skip_loop_error = (ModelEmptyError, FactorEmptyError, CoderError)
|
||||
|
||||
|
||||
def main(path=None, step_n=None, competition=None):
|
||||
"""
|
||||
Auto R&D Evolving loop for models in a kaggle{} scenario.
|
||||
You can continue running session by
|
||||
.. code-block:: bash
|
||||
dotenv run -- python rdagent/app/kaggle/loop.py [--competition titanic] $LOG_PATH/__session__/1/0_propose --step_n 1 # `step_n` is a optional parameter
|
||||
rdagent kaggle --competition playground-series-s4e8 # You are encouraged to use this one.
|
||||
"""
|
||||
if competition:
|
||||
KAGGLE_IMPLEMENT_SETTING.competition = competition
|
||||
download_data(competition=competition, settings=KAGGLE_IMPLEMENT_SETTING)
|
||||
if KAGGLE_IMPLEMENT_SETTING.if_using_graph_rag:
|
||||
KAGGLE_IMPLEMENT_SETTING.knowledge_base = (
|
||||
"rdagent.scenarios.kaggle.knowledge_management.graph.KGKnowledgeGraph"
|
||||
)
|
||||
else:
|
||||
logger.error("Please specify competition name.")
|
||||
if path is None:
|
||||
kaggle_loop = KaggleRDLoop(KAGGLE_IMPLEMENT_SETTING)
|
||||
else:
|
||||
kaggle_loop = KaggleRDLoop.load(path)
|
||||
kaggle_loop.run(step_n=step_n)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
fire.Fire(main)
|
||||
@@ -1,65 +0,0 @@
|
||||
from collections import defaultdict
|
||||
|
||||
import fire
|
||||
|
||||
from rdagent.app.kaggle.conf import PROP_SETTING
|
||||
from rdagent.components.workflow.conf import BasePropSetting
|
||||
from rdagent.components.workflow.rd_loop import RDLoop
|
||||
from rdagent.core.exception import ModelEmptyError
|
||||
from rdagent.core.proposal import (
|
||||
Hypothesis2Experiment,
|
||||
HypothesisExperiment2Feedback,
|
||||
HypothesisGen,
|
||||
Trace,
|
||||
)
|
||||
from rdagent.core.utils import import_class
|
||||
from rdagent.log import rdagent_logger as logger
|
||||
|
||||
|
||||
class ModelRDLoop(RDLoop):
|
||||
def __init__(self, PROP_SETTING: BasePropSetting):
|
||||
with logger.tag("init"):
|
||||
scen: Scenario = import_class(PROP_SETTING.scen)(PROP_SETTING.competition)
|
||||
logger.log_object(scen, tag="scenario")
|
||||
|
||||
self.hypothesis_gen: HypothesisGen = import_class(PROP_SETTING.hypothesis_gen)(scen)
|
||||
logger.log_object(self.hypothesis_gen, tag="hypothesis generator")
|
||||
|
||||
self.hypothesis2experiment: Hypothesis2Experiment = import_class(PROP_SETTING.hypothesis2experiment)()
|
||||
logger.log_object(self.hypothesis2experiment, tag="hypothesis2experiment")
|
||||
|
||||
self.coder: Developer = import_class(PROP_SETTING.coder)(scen)
|
||||
logger.log_object(self.coder, tag="coder")
|
||||
self.runner: Developer = import_class(PROP_SETTING.runner)(scen)
|
||||
logger.log_object(self.runner, tag="runner")
|
||||
|
||||
self.summarizer: HypothesisExperiment2Feedback = import_class(PROP_SETTING.summarizer)(scen)
|
||||
logger.log_object(self.summarizer, tag="summarizer")
|
||||
self.trace = Trace(scen=scen)
|
||||
super(RDLoop, self).__init__()
|
||||
|
||||
skip_loop_error = (ModelEmptyError,)
|
||||
|
||||
|
||||
def main(path=None, step_n=None, competition=None):
|
||||
"""
|
||||
Auto R&D Evolving loop for models in a kaggle{} scenario.
|
||||
|
||||
You can continue running session by
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
dotenv run -- python rdagent/app/kaggle/model.py [--competition titanic] $LOG_PATH/__session__/1/0_propose --step_n 1 # `step_n` is a optional paramter
|
||||
|
||||
"""
|
||||
if competition:
|
||||
PROP_SETTING.competition = competition
|
||||
if path is None:
|
||||
model_loop = ModelRDLoop(PROP_SETTING)
|
||||
else:
|
||||
model_loop = ModelRDLoop.load(path)
|
||||
model_loop.run(step_n=step_n)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
fire.Fire(main)
|
||||
@@ -1,14 +1,12 @@
|
||||
from pydantic_settings import BaseSettings
|
||||
from typing import Optional
|
||||
|
||||
from pydantic_settings import SettingsConfigDict
|
||||
|
||||
from rdagent.components.workflow.conf import BasePropSetting
|
||||
|
||||
|
||||
class ModelBasePropSetting(BasePropSetting):
|
||||
class Config:
|
||||
env_prefix = "QLIB_MODEL_"
|
||||
"""Use `QLIB_MODEL_` as prefix for environment variables"""
|
||||
protected_namespaces = ()
|
||||
"""Add 'model_' to the protected namespaces"""
|
||||
model_config = SettingsConfigDict(env_prefix="QLIB_MODEL_", protected_namespaces=())
|
||||
|
||||
# 1) override base settings
|
||||
scen: str = "rdagent.scenarios.qlib.experiment.model_experiment.QlibModelScenario"
|
||||
@@ -26,19 +24,33 @@ class ModelBasePropSetting(BasePropSetting):
|
||||
runner: str = "rdagent.scenarios.qlib.developer.model_runner.QlibModelRunner"
|
||||
"""Runner class"""
|
||||
|
||||
summarizer: str = "rdagent.scenarios.qlib.developer.feedback.QlibModelHypothesisExperiment2Feedback"
|
||||
summarizer: str = "rdagent.scenarios.qlib.developer.feedback.QlibModelExperiment2Feedback"
|
||||
"""Summarizer class"""
|
||||
|
||||
evolving_n: int = 10
|
||||
"""Number of evolutions"""
|
||||
|
||||
train_start: str = "2008-01-01"
|
||||
"""Start date of the training segment"""
|
||||
|
||||
train_end: str = "2014-12-31"
|
||||
"""End date of the training segment"""
|
||||
|
||||
valid_start: str = "2015-01-01"
|
||||
"""Start date of the validation segment"""
|
||||
|
||||
valid_end: str = "2016-12-31"
|
||||
"""End date of the validation segment"""
|
||||
|
||||
test_start: str = "2017-01-01"
|
||||
"""Start date of the test / backtest segment"""
|
||||
|
||||
test_end: Optional[str] = "2020-08-01"
|
||||
"""End date of the test / backtest segment"""
|
||||
|
||||
|
||||
class FactorBasePropSetting(BasePropSetting):
|
||||
class Config:
|
||||
env_prefix = "QLIB_FACTOR_"
|
||||
"""Use `QLIB_FACTOR_` as prefix for environment variables"""
|
||||
protected_namespaces = ()
|
||||
"""Add 'factor_' to the protected namespaces"""
|
||||
model_config = SettingsConfigDict(env_prefix="QLIB_FACTOR_", protected_namespaces=())
|
||||
|
||||
# 1) override base settings
|
||||
scen: str = "rdagent.scenarios.qlib.experiment.factor_experiment.QlibFactorScenario"
|
||||
@@ -56,12 +68,30 @@ class FactorBasePropSetting(BasePropSetting):
|
||||
runner: str = "rdagent.scenarios.qlib.developer.factor_runner.QlibFactorRunner"
|
||||
"""Runner class"""
|
||||
|
||||
summarizer: str = "rdagent.scenarios.qlib.developer.feedback.QlibFactorHypothesisExperiment2Feedback"
|
||||
summarizer: str = "rdagent.scenarios.qlib.developer.feedback.QlibFactorExperiment2Feedback"
|
||||
"""Summarizer class"""
|
||||
|
||||
evolving_n: int = 10
|
||||
"""Number of evolutions"""
|
||||
|
||||
train_start: str = "2008-01-01"
|
||||
"""Start date of the training segment"""
|
||||
|
||||
train_end: str = "2014-12-31"
|
||||
"""End date of the training segment"""
|
||||
|
||||
valid_start: str = "2015-01-01"
|
||||
"""Start date of the validation segment"""
|
||||
|
||||
valid_end: str = "2016-12-31"
|
||||
"""End date of the validation segment"""
|
||||
|
||||
test_start: str = "2017-01-01"
|
||||
"""Start date of the test / backtest segment"""
|
||||
|
||||
test_end: Optional[str] = "2020-08-01"
|
||||
"""End date of the test / backtest segment"""
|
||||
|
||||
|
||||
class FactorFromReportPropSetting(FactorBasePropSetting):
|
||||
# 1) override the scen attribute
|
||||
@@ -72,10 +102,75 @@ class FactorFromReportPropSetting(FactorBasePropSetting):
|
||||
report_result_json_file_path: str = "git_ignore_folder/report_list.json"
|
||||
"""Path to the JSON file listing research reports for factor extraction"""
|
||||
|
||||
max_factors_per_exp: int = 10000
|
||||
max_factors_per_exp: int = 6
|
||||
"""Maximum number of factors implemented per experiment"""
|
||||
|
||||
report_limit: int = 20
|
||||
"""Maximum number of reports to process"""
|
||||
|
||||
|
||||
class QuantBasePropSetting(BasePropSetting):
|
||||
model_config = SettingsConfigDict(env_prefix="QLIB_QUANT_", protected_namespaces=())
|
||||
|
||||
# 1) override base settings
|
||||
scen: str = "rdagent.scenarios.qlib.experiment.quant_experiment.QlibQuantScenario"
|
||||
"""Scenario class for Qlib Model"""
|
||||
|
||||
quant_hypothesis_gen: str = "rdagent.scenarios.qlib.proposal.quant_proposal.QlibQuantHypothesisGen"
|
||||
"""Hypothesis generation class"""
|
||||
|
||||
model_hypothesis2experiment: str = "rdagent.scenarios.qlib.proposal.model_proposal.QlibModelHypothesis2Experiment"
|
||||
"""Hypothesis to experiment class"""
|
||||
|
||||
model_coder: str = "rdagent.scenarios.qlib.developer.model_coder.QlibModelCoSTEER"
|
||||
"""Coder class"""
|
||||
|
||||
model_runner: str = "rdagent.scenarios.qlib.developer.model_runner.QlibModelRunner"
|
||||
"""Runner class"""
|
||||
|
||||
model_summarizer: str = "rdagent.scenarios.qlib.developer.feedback.QlibModelExperiment2Feedback"
|
||||
"""Summarizer class"""
|
||||
|
||||
factor_hypothesis2experiment: str = (
|
||||
"rdagent.scenarios.qlib.proposal.factor_proposal.QlibFactorHypothesis2Experiment"
|
||||
)
|
||||
"""Hypothesis to experiment class"""
|
||||
|
||||
factor_coder: str = "rdagent.scenarios.qlib.developer.factor_coder.QlibFactorCoSTEER"
|
||||
"""Coder class"""
|
||||
|
||||
factor_runner: str = "rdagent.scenarios.qlib.developer.factor_runner.QlibFactorRunner"
|
||||
"""Runner class"""
|
||||
|
||||
factor_summarizer: str = "rdagent.scenarios.qlib.developer.feedback.QlibFactorExperiment2Feedback"
|
||||
"""Summarizer class"""
|
||||
|
||||
evolving_n: int = 10
|
||||
"""Number of evolutions"""
|
||||
|
||||
action_selection: str = "bandit"
|
||||
"""Action selection strategy: 'bandit' for bandit-based selection, 'llm' for LLM-based selection, 'random' for random selection"""
|
||||
|
||||
train_start: str = "2008-01-01"
|
||||
"""Start date of the training segment"""
|
||||
|
||||
train_end: str = "2014-12-31"
|
||||
"""End date of the training segment"""
|
||||
|
||||
valid_start: str = "2015-01-01"
|
||||
"""Start date of the validation segment"""
|
||||
|
||||
valid_end: str = "2016-12-31"
|
||||
"""End date of the validation segment"""
|
||||
|
||||
test_start: str = "2017-01-01"
|
||||
"""Start date of the test / backtest segment"""
|
||||
|
||||
test_end: Optional[str] = "2020-08-01"
|
||||
"""End date of the test / backtest segment"""
|
||||
|
||||
|
||||
FACTOR_PROP_SETTING = FactorBasePropSetting()
|
||||
FACTOR_FROM_REPORT_PROP_SETTING = FactorFromReportPropSetting()
|
||||
MODEL_PROP_SETTING = ModelBasePropSetting()
|
||||
QUANT_PROP_SETTING = QuantBasePropSetting()
|
||||
|
||||
@@ -2,30 +2,41 @@
|
||||
Factor workflow with session control
|
||||
"""
|
||||
|
||||
from typing import Any
|
||||
import asyncio
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional
|
||||
|
||||
import fire
|
||||
|
||||
from rdagent.app.qlib_rd_loop.conf import FACTOR_PROP_SETTING
|
||||
from rdagent.components.workflow.rd_loop import RDLoop
|
||||
from rdagent.core.exception import FactorEmptyError
|
||||
from rdagent.core.exception import CoderError, FactorEmptyError
|
||||
from rdagent.log import rdagent_logger as logger
|
||||
|
||||
|
||||
class FactorRDLoop(RDLoop):
|
||||
skip_loop_error = (FactorEmptyError,)
|
||||
skip_loop_error = (FactorEmptyError, CoderError)
|
||||
skip_loop_error_stepname = "feedback"
|
||||
|
||||
def running(self, prev_out: dict[str, Any]):
|
||||
with logger.tag("ef"): # evaluate and feedback
|
||||
exp = self.runner.develop(prev_out["coding"])
|
||||
if exp is None:
|
||||
logger.error(f"Factor extraction failed.")
|
||||
raise FactorEmptyError("Factor extraction failed.")
|
||||
logger.log_object(exp, tag="runner result")
|
||||
exp = self.runner.develop(prev_out["coding"])
|
||||
if exp is None:
|
||||
logger.error(f"Factor extraction failed.")
|
||||
raise FactorEmptyError("Factor extraction failed.")
|
||||
logger.log_object(exp, tag="runner result")
|
||||
return exp
|
||||
|
||||
|
||||
def main(path=None, step_n=None):
|
||||
def main(
|
||||
path: Optional[str] = None,
|
||||
step_n: Optional[int] = None,
|
||||
loop_n: Optional[int] = None,
|
||||
all_duration: str | None = None,
|
||||
checkout: bool = True,
|
||||
checkout_path: Optional[str] = None,
|
||||
base_features_path: Optional[str] = None,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
Auto R&D Evolving loop for fintech factors.
|
||||
|
||||
@@ -36,11 +47,19 @@ def main(path=None, step_n=None):
|
||||
dotenv run -- python rdagent/app/qlib_rd_loop/factor.py $LOG_PATH/__session__/1/0_propose --step_n 1 # `step_n` is a optional paramter
|
||||
|
||||
"""
|
||||
if not checkout_path is None:
|
||||
checkout = Path(checkout_path)
|
||||
|
||||
if path is None:
|
||||
model_loop = FactorRDLoop(FACTOR_PROP_SETTING)
|
||||
factor_loop = FactorRDLoop(FACTOR_PROP_SETTING)
|
||||
else:
|
||||
model_loop = FactorRDLoop.load(path)
|
||||
model_loop.run(step_n=step_n)
|
||||
factor_loop = FactorRDLoop.load(path, checkout=checkout)
|
||||
|
||||
factor_loop._init_base_features(base_features_path)
|
||||
if "user_interaction_queues" in kwargs and kwargs["user_interaction_queues"] is not None:
|
||||
factor_loop._set_interactor(*kwargs["user_interaction_queues"])
|
||||
factor_loop._interact_init_params()
|
||||
asyncio.run(factor_loop.run(step_n=step_n, loop_n=loop_n, all_duration=all_duration))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import asyncio
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any, Tuple
|
||||
from typing import Any, Dict, Tuple
|
||||
|
||||
import fire
|
||||
from jinja2 import Environment, StrictUndefined
|
||||
|
||||
from rdagent.app.qlib_rd_loop.conf import FACTOR_FROM_REPORT_PROP_SETTING
|
||||
from rdagent.app.qlib_rd_loop.factor import FactorRDLoop
|
||||
@@ -11,21 +11,17 @@ from rdagent.components.document_reader.document_reader import (
|
||||
extract_first_page_screenshot_from_pdf,
|
||||
load_and_process_pdfs_by_langchain,
|
||||
)
|
||||
from rdagent.components.workflow.rd_loop import RDLoop
|
||||
from rdagent.core.exception import FactorEmptyError
|
||||
from rdagent.core.prompts import Prompts
|
||||
from rdagent.core.proposal import Hypothesis
|
||||
from rdagent.core.conf import RD_AGENT_SETTINGS
|
||||
from rdagent.core.proposal import Hypothesis, HypothesisFeedback
|
||||
from rdagent.log import rdagent_logger as logger
|
||||
from rdagent.oai.llm_utils import APIBackend
|
||||
from rdagent.scenarios.qlib.experiment.factor_experiment import QlibFactorExperiment
|
||||
from rdagent.scenarios.qlib.factor_experiment_loader.pdf_loader import (
|
||||
FactorExperimentLoaderFromPDFfiles,
|
||||
)
|
||||
from rdagent.utils.agent.tpl import T
|
||||
from rdagent.utils.workflow import LoopMeta
|
||||
|
||||
prompts_path = Path(__file__).parent / "prompts.yaml"
|
||||
prompts = Prompts(file_path=prompts_path)
|
||||
|
||||
|
||||
def generate_hypothesis(factor_result: dict, report_content: str) -> str:
|
||||
"""
|
||||
@@ -38,19 +34,16 @@ def generate_hypothesis(factor_result: dict, report_content: str) -> str:
|
||||
Returns:
|
||||
str: The generated hypothesis.
|
||||
"""
|
||||
system_prompt = (
|
||||
Environment(undefined=StrictUndefined).from_string(prompts["hypothesis_generation"]["system"]).render()
|
||||
)
|
||||
user_prompt = (
|
||||
Environment(undefined=StrictUndefined)
|
||||
.from_string(prompts["hypothesis_generation"]["user"])
|
||||
.render(factor_descriptions=json.dumps(factor_result), report_content=report_content)
|
||||
system_prompt = T(".prompts:hypothesis_generation.system").r()
|
||||
user_prompt = T(".prompts:hypothesis_generation.user").r(
|
||||
factor_descriptions=json.dumps(factor_result), report_content=report_content
|
||||
)
|
||||
|
||||
response = APIBackend().build_messages_and_create_chat_completion(
|
||||
user_prompt=user_prompt,
|
||||
system_prompt=system_prompt,
|
||||
json_mode=True,
|
||||
json_target_type=Dict[str, str],
|
||||
)
|
||||
|
||||
response_json = json.loads(response)
|
||||
@@ -65,7 +58,7 @@ def generate_hypothesis(factor_result: dict, report_content: str) -> str:
|
||||
)
|
||||
|
||||
|
||||
def extract_hypothesis_and_exp_from_reports(report_file_path: str) -> Tuple[QlibFactorExperiment, Hypothesis]:
|
||||
def extract_hypothesis_and_exp_from_reports(report_file_path: str) -> QlibFactorExperiment | None:
|
||||
"""
|
||||
Extract hypothesis and experiment details from report files.
|
||||
|
||||
@@ -73,17 +66,15 @@ def extract_hypothesis_and_exp_from_reports(report_file_path: str) -> Tuple[Qlib
|
||||
report_file_path (str): Path to the report file.
|
||||
|
||||
Returns:
|
||||
Tuple[QlibFactorExperiment, Hypothesis]: The extracted experiment and generated hypothesis.
|
||||
QlibFactorExperiment: An instance of QlibFactorExperiment containing the extracted details.
|
||||
None: If no valid experiment is found in the report.
|
||||
"""
|
||||
with logger.tag("extract_factors_and_implement"):
|
||||
with logger.tag("load_factor_tasks"):
|
||||
exp = FactorExperimentLoaderFromPDFfiles().load(report_file_path)
|
||||
if exp is None or exp.sub_tasks == []:
|
||||
return None, None
|
||||
exp = FactorExperimentLoaderFromPDFfiles().load(report_file_path)
|
||||
if exp is None or exp.sub_tasks == []:
|
||||
return None
|
||||
|
||||
with logger.tag("load_pdf_screenshot"):
|
||||
pdf_screenshot = extract_first_page_screenshot_from_pdf(report_file_path)
|
||||
logger.log_object(pdf_screenshot)
|
||||
pdf_screenshot = extract_first_page_screenshot_from_pdf(report_file_path)
|
||||
logger.log_object(pdf_screenshot, tag="load_pdf_screenshot")
|
||||
|
||||
docs_dict = load_and_process_pdfs_by_langchain(report_file_path)
|
||||
|
||||
@@ -99,7 +90,8 @@ def extract_hypothesis_and_exp_from_reports(report_file_path: str) -> Tuple[Qlib
|
||||
|
||||
report_content = "\n".join(docs_dict.values())
|
||||
hypothesis = generate_hypothesis(factor_result, report_content)
|
||||
return exp, hypothesis
|
||||
exp.hypothesis = hypothesis
|
||||
return exp
|
||||
|
||||
|
||||
class FactorReportLoop(FactorRDLoop, metaclass=LoopMeta):
|
||||
@@ -112,41 +104,43 @@ class FactorReportLoop(FactorRDLoop, metaclass=LoopMeta):
|
||||
else:
|
||||
self.judge_pdf_data_items = [i for i in Path(report_folder).rglob("*.pdf")]
|
||||
|
||||
self.pdf_file_index = 0
|
||||
self.valid_pdf_file_count = 0
|
||||
self.current_loop_hypothesis = None
|
||||
self.current_loop_exp = None
|
||||
self.steps = ["propose_hypo_exp", "propose", "exp_gen", "coding", "running", "feedback"]
|
||||
self.loop_n = min(len(self.judge_pdf_data_items), FACTOR_FROM_REPORT_PROP_SETTING.report_limit)
|
||||
self.shift_report = (
|
||||
0 # some reports does not contain viable factor, so we ship some of them to avoid infinite loop
|
||||
)
|
||||
|
||||
def propose_hypo_exp(self, prev_out: dict[str, Any]):
|
||||
with logger.tag("r"):
|
||||
while True:
|
||||
if self.valid_pdf_file_count > 15:
|
||||
break
|
||||
report_file_path = self.judge_pdf_data_items[self.pdf_file_index]
|
||||
logger.info(f"Processing number {self.pdf_file_index} report: {report_file_path}")
|
||||
self.pdf_file_index += 1
|
||||
exp, hypothesis = extract_hypothesis_and_exp_from_reports(str(report_file_path))
|
||||
async def direct_exp_gen(self, prev_out: dict[str, Any]):
|
||||
while True:
|
||||
if self.get_unfinished_loop_cnt(self.loop_idx) < RD_AGENT_SETTINGS.get_max_parallel():
|
||||
report_file_path = self.judge_pdf_data_items[self.loop_idx + self.shift_report]
|
||||
logger.info(f"Processing number {self.loop_idx} report: {report_file_path}")
|
||||
exp = extract_hypothesis_and_exp_from_reports(str(report_file_path))
|
||||
if exp is None:
|
||||
self.shift_report += 1
|
||||
self.loop_n -= 1
|
||||
if self.loop_n < 0: # NOTE: on every step, we self.loop_n -= 1 at first.
|
||||
raise self.LoopTerminationError("Reach stop criterion and stop loop")
|
||||
continue
|
||||
self.valid_pdf_file_count += 1
|
||||
exp.based_experiments = [QlibFactorExperiment(sub_tasks=[])] + [t[1] for t in self.trace.hist if t[2]]
|
||||
exp.based_experiments = [QlibFactorExperiment(sub_tasks=[], hypothesis=exp.hypothesis)] + [
|
||||
t[0] for t in self.trace.hist if t[1]
|
||||
]
|
||||
exp.sub_workspace_list = exp.sub_workspace_list[: FACTOR_FROM_REPORT_PROP_SETTING.max_factors_per_exp]
|
||||
exp.sub_tasks = exp.sub_tasks[: FACTOR_FROM_REPORT_PROP_SETTING.max_factors_per_exp]
|
||||
logger.log_object(hypothesis, tag="hypothesis generation")
|
||||
exp.base_features = self.plan["features"]
|
||||
if exp.based_experiments:
|
||||
exp.based_experiments[-1].base_features = self.plan["features"]
|
||||
logger.log_object(exp.hypothesis, tag="hypothesis generation")
|
||||
logger.log_object(exp.sub_tasks, tag="experiment generation")
|
||||
self.current_loop_hypothesis = hypothesis
|
||||
self.current_loop_exp = exp
|
||||
return None
|
||||
return exp
|
||||
await asyncio.sleep(1)
|
||||
|
||||
def propose(self, prev_out: dict[str, Any]):
|
||||
return self.current_loop_hypothesis
|
||||
|
||||
def exp_gen(self, prev_out: dict[str, Any]):
|
||||
return self.current_loop_exp
|
||||
def coding(self, prev_out: dict[str, Any]):
|
||||
exp = self.coder.develop(prev_out["direct_exp_gen"])
|
||||
logger.log_object(exp.sub_workspace_list, tag="coder result")
|
||||
return exp
|
||||
|
||||
|
||||
def main(report_folder=None, path=None, step_n=None):
|
||||
def main(report_folder=None, path=None, all_duration=None, checkout=True):
|
||||
"""
|
||||
Auto R&D Evolving loop for fintech factors (the factors are extracted from finance reports).
|
||||
|
||||
@@ -158,11 +152,11 @@ def main(report_folder=None, path=None, step_n=None):
|
||||
if path is None and report_folder is None:
|
||||
model_loop = FactorReportLoop()
|
||||
elif path is not None:
|
||||
model_loop = FactorReportLoop.load(path)
|
||||
model_loop = FactorReportLoop.load(path, checkout=checkout)
|
||||
else:
|
||||
model_loop = FactorReportLoop(report_folder=report_folder)
|
||||
|
||||
model_loop.run(step_n=step_n)
|
||||
asyncio.run(model_loop.run(all_duration=all_duration))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user