diff --git a/data/paper_pnl.csv b/data/paper_pnl.csv new file mode 100644 index 0000000..fe62363 --- /dev/null +++ b/data/paper_pnl.csv @@ -0,0 +1,78 @@ +date,bankroll +2026-08-11 11:14,1000.0 +2026-08-11 11:15,1430.571129983699 +2026-08-11 11:15,1869.9228851208563 +2026-08-11 11:15,2309.2746402580137 +2026-08-11 11:15,2593.77145925936 +2026-08-11 11:15,3033.8015537575993 +2026-08-11 11:15,3457.8650454555836 +2026-08-11 11:15,3885.3876428140006 +2026-08-11 11:15,4385.387642814001 +2026-08-11 11:15,4885.387642814001 +2026-08-11 11:15,5385.387642814001 +2026-08-11 11:15,5885.387642814001 +2026-08-11 11:15,6057.0007397876425 +2026-08-11 11:15,6160.7826990978765 +2026-08-11 11:15,6347.768603660328 +2026-08-11 11:15,5847.768603660328 +2026-08-11 11:15,6347.768603660328 +2026-08-11 11:15,6847.768603660328 +2026-08-11 11:15,6347.768603660328 +2026-08-11 11:15,6847.768603660328 +2026-08-11 11:15,7347.768603660328 +2026-08-11 11:15,6847.768603660328 +2026-08-11 11:15,7215.736592572463 +2026-08-11 11:15,7715.736592572463 +2026-08-11 11:15,8215.736592572463 +2026-08-11 11:15,8715.736592572463 +2026-08-11 11:15,9215.736592572463 +2026-08-11 11:15,9715.736592572463 +2026-08-11 11:15,10215.736592572463 +2026-08-11 11:15,10715.736592572463 +2026-08-11 11:15,10832.307490599274 +2026-08-11 11:15,11332.307490599274 +2026-08-11 11:15,11832.307490599274 +2026-08-11 11:15,12332.307490599274 +2026-08-11 11:15,12832.307490599274 +2026-08-11 11:15,13332.307490599274 +2026-08-11 11:15,13832.307490599274 +2026-08-11 11:15,14332.307490599274 +2026-08-11 11:15,14441.807689556865 +2026-08-11 11:15,13941.807689556865 +2026-08-11 11:15,14441.807689556865 +2026-08-11 11:15,14941.807689556865 +2026-08-11 11:15,15441.807689556865 +2026-08-11 11:15,15941.807689556865 +2026-08-11 11:15,16441.807689556867 +2026-08-11 11:15,16941.807689556867 +2026-08-11 11:15,16441.807689556867 +2026-08-11 11:15,16729.018474036096 +2026-08-11 11:15,17229.018474036096 +2026-08-11 11:15,17729.018474036096 +2026-08-11 11:15,18229.018474036096 +2026-08-11 11:15,18729.018474036096 +2026-08-11 11:15,19229.018474036096 +2026-08-11 11:15,19729.018474036096 +2026-08-11 11:15,20229.018474036096 +2026-08-11 11:15,20356.41021319305 +2026-08-11 11:15,20734.229569661333 +2026-08-11 11:15,21234.229569661333 +2026-08-11 11:15,21734.229569661333 +2026-08-11 11:15,22234.229569661333 +2026-08-11 11:15,22734.229569661333 +2026-08-11 11:15,23234.229569661333 +2026-08-11 11:15,23734.229569661333 +2026-08-11 11:15,24234.229569661333 +2026-08-11 11:15,24371.707348301075 +2026-08-11 11:15,24871.707348301075 +2026-08-11 11:15,25371.707348301075 +2026-08-11 11:15,25871.707348301075 +2026-08-11 11:15,26371.707348301075 +2026-08-11 11:15,26871.707348301075 +2026-08-11 11:15,27371.707348301075 +2026-08-11 11:15,27871.707348301075 +2026-08-11 11:15,27971.49601527572 +2026-08-11 11:15,28345.77538017773 +2026-08-11 11:15,28845.77538017773 +2026-08-11 11:15,29345.77538017773 +2026-08-11 11:15,29845.77538017773 diff --git a/data/paper_trades.jsonl b/data/paper_trades.jsonl new file mode 100644 index 0000000..3cb932f --- /dev/null +++ b/data/paper_trades.jsonl @@ -0,0 +1,160 @@ +{"action": "open", "time": "2026-08-11T11:15:25.368459", "target": "temp_gt_30c_24h_2026-08-11", "side": "buy_yes", "size_usdc": 430.571129983699, "entry_prob": 93.05728522682189, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.368452", "entry_roll": 1000.0} +{"action": "open", "time": "2026-08-11T11:15:25.368462", "target": "rain_gt_0mm_24h_2026-08-12", "side": "buy_yes", "size_usdc": 439.3517551371573, "entry_prob": 93.93535125441778, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.368461", "entry_roll": 1000.0} +{"action": "open", "time": "2026-08-11T11:15:25.368464", "target": "temp_gt_30c_24h_2026-08-12", "side": "buy_yes", "size_usdc": 439.3517551371573, "entry_prob": 93.93535125441778, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.368463", "entry_roll": 1000.0} +{"action": "open", "time": "2026-08-11T11:15:25.368466", "target": "temp_gt_35c_24h_2026-08-12", "side": "buy_yes", "size_usdc": 284.49681900134635, "entry_prob": 78.44979569886223, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.368465", "entry_roll": 1000.0} +{"action": "open", "time": "2026-08-11T11:15:25.368469", "target": "rain_gt_0mm_24h_2026-08-13", "side": "buy_yes", "size_usdc": 440.03009449823935, "entry_prob": 94.00318546186173, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.368468", "entry_roll": 1000.0} +{"action": "open", "time": "2026-08-11T11:15:25.368471", "target": "rain_gt_5mm_24h_2026-08-13", "side": "buy_yes", "size_usdc": 424.06349169798426, "entry_prob": 92.4065187951951, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.368470", "entry_roll": 1000.0} +{"action": "open", "time": "2026-08-11T11:15:25.368473", "target": "temp_gt_30c_24h_2026-08-13", "side": "buy_yes", "size_usdc": 427.5225973584169, "entry_prob": 92.75243074488063, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.368472", "entry_roll": 1000.0} +{"action": "close", "time": "2026-08-11T11:15:25.408326", "target": "temp_gt_30c_24h_2026-08-11", "won": true, "profit_usdc": 430.571129983699, "bankroll_after": 1430.571129983699, "hours_open": 1.1071388888888888e-05} +{"action": "close", "time": "2026-08-11T11:15:25.408336", "target": "rain_gt_0mm_24h_2026-08-12", "won": true, "profit_usdc": 439.3517551371573, "bankroll_after": 1869.9228851208563, "hours_open": 1.1075000000000002e-05} +{"action": "close", "time": "2026-08-11T11:15:25.408342", "target": "temp_gt_30c_24h_2026-08-12", "won": true, "profit_usdc": 439.3517551371573, "bankroll_after": 2309.2746402580137, "hours_open": 1.1076388888888889e-05} +{"action": "close", "time": "2026-08-11T11:15:25.408347", "target": "temp_gt_35c_24h_2026-08-12", "won": true, "profit_usdc": 284.49681900134635, "bankroll_after": 2593.77145925936, "hours_open": 1.107722222222222e-05} +{"action": "close", "time": "2026-08-11T11:15:25.408351", "target": "rain_gt_0mm_24h_2026-08-13", "won": true, "profit_usdc": 440.03009449823935, "bankroll_after": 3033.8015537575993, "hours_open": 1.1077777777777778e-05} +{"action": "close", "time": "2026-08-11T11:15:25.408356", "target": "rain_gt_5mm_24h_2026-08-13", "won": true, "profit_usdc": 424.06349169798426, "bankroll_after": 3457.8650454555836, "hours_open": 1.1078611111111112e-05} +{"action": "close", "time": "2026-08-11T11:15:25.408360", "target": "temp_gt_30c_24h_2026-08-13", "won": true, "profit_usdc": 427.5225973584169, "bankroll_after": 3885.3876428140006, "hours_open": 1.1079166666666665e-05} +{"action": "open", "time": "2026-08-11T11:15:25.408463", "target": "temp_gt_30c_24h_2026-08-11", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.05728522682189, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.408461", "entry_roll": 3885.3876428140006} +{"action": "open", "time": "2026-08-11T11:15:25.408465", "target": "rain_gt_0mm_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.408464", "entry_roll": 3885.3876428140006} +{"action": "open", "time": "2026-08-11T11:15:25.408468", "target": "temp_gt_30c_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.408467", "entry_roll": 3885.3876428140006} +{"action": "open", "time": "2026-08-11T11:15:25.408474", "target": "temp_gt_35c_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 78.44979569886223, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.408469", "entry_roll": 3885.3876428140006} +{"action": "open", "time": "2026-08-11T11:15:25.408476", "target": "rain_gt_0mm_24h", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 74.44762501700039, "entry_time": "2026-08-11T11:15:25.408475", "entry_roll": 3885.3876428140006} +{"action": "open", "time": "2026-08-11T11:15:25.408478", "target": "temp_gt_30c_24h", "side": "buy_yes", "size_usdc": 468.7101215358844, "entry_prob": 93.93535125441778, "entry_market": 81.8718960868684, "entry_time": "2026-08-11T11:15:25.408477", "entry_roll": 3885.3876428140006} +{"action": "open", "time": "2026-08-11T11:15:25.408480", "target": "temp_gt_35c_24h", "side": "buy_yes", "size_usdc": 400.12544007919644, "entry_prob": 78.44979569886223, "entry_market": 68.15154292809973, "entry_time": "2026-08-11T11:15:25.408479", "entry_roll": 3885.3876428140006} +{"action": "open", "time": "2026-08-11T11:15:25.408482", "target": "rain_gt_0mm_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 94.00318546186173, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.408481", "entry_roll": 3885.3876428140006} +{"action": "open", "time": "2026-08-11T11:15:25.408484", "target": "rain_gt_5mm_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 92.4065187951951, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.408483", "entry_roll": 3885.3876428140006} +{"action": "open", "time": "2026-08-11T11:15:25.408486", "target": "temp_gt_30c_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 92.75243074488063, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.408485", "entry_roll": 3885.3876428140006} +{"action": "close", "time": "2026-08-11T11:15:25.445074", "target": "temp_gt_30c_24h_2026-08-11", "won": true, "profit_usdc": 500.0, "bankroll_after": 4385.387642814001, "hours_open": 1.0166111111111111e-05} +{"action": "close", "time": "2026-08-11T11:15:25.445084", "target": "rain_gt_0mm_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 4885.387642814001, "hours_open": 1.0171111111111112e-05} +{"action": "close", "time": "2026-08-11T11:15:25.445089", "target": "temp_gt_30c_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 5385.387642814001, "hours_open": 1.0171944444444444e-05} +{"action": "close", "time": "2026-08-11T11:15:25.445094", "target": "temp_gt_35c_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 5885.387642814001, "hours_open": 1.0172777777777779e-05} +{"action": "close", "time": "2026-08-11T11:15:25.445098", "target": "rain_gt_0mm_24h", "won": true, "profit_usdc": 171.6130969736417, "bankroll_after": 6057.0007397876425, "hours_open": 1.0172222222222222e-05} +{"action": "close", "time": "2026-08-11T11:15:25.445103", "target": "temp_gt_30c_24h", "won": true, "profit_usdc": 103.78195931023365, "bankroll_after": 6160.7826990978765, "hours_open": 1.0173055555555557e-05} +{"action": "close", "time": "2026-08-11T11:15:25.445107", "target": "temp_gt_35c_24h", "won": true, "profit_usdc": 186.9859045624518, "bankroll_after": 6347.768603660328, "hours_open": 1.0173611111111111e-05} +{"action": "close", "time": "2026-08-11T11:15:25.445112", "target": "rain_gt_0mm_24h_2026-08-13", "won": false, "profit_usdc": -500.0, "bankroll_after": 5847.768603660328, "hours_open": 1.0174444444444444e-05} +{"action": "close", "time": "2026-08-11T11:15:25.445116", "target": "rain_gt_5mm_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 6347.768603660328, "hours_open": 1.0175e-05} +{"action": "close", "time": "2026-08-11T11:15:25.445121", "target": "temp_gt_30c_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 6847.768603660328, "hours_open": 1.0175833333333333e-05} +{"action": "open", "time": "2026-08-11T11:15:25.445215", "target": "temp_gt_30c_24h_2026-08-11", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.05728522682189, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.445213", "entry_roll": 6847.768603660328} +{"action": "open", "time": "2026-08-11T11:15:25.445217", "target": "rain_gt_0mm_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.445216", "entry_roll": 6847.768603660328} +{"action": "open", "time": "2026-08-11T11:15:25.445219", "target": "temp_gt_30c_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.445218", "entry_roll": 6847.768603660328} +{"action": "open", "time": "2026-08-11T11:15:25.445221", "target": "temp_gt_35c_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 78.44979569886223, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.445221", "entry_roll": 6847.768603660328} +{"action": "open", "time": "2026-08-11T11:15:25.445224", "target": "temp_gt_35c_24h", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 78.44979569886223, "entry_market": 57.60581108834134, "entry_time": "2026-08-11T11:15:25.445223", "entry_roll": 6847.768603660328} +{"action": "open", "time": "2026-08-11T11:15:25.445226", "target": "rain_gt_0mm_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 94.00318546186173, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.445225", "entry_roll": 6847.768603660328} +{"action": "open", "time": "2026-08-11T11:15:25.445228", "target": "rain_gt_5mm_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 92.4065187951951, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.445227", "entry_roll": 6847.768603660328} +{"action": "open", "time": "2026-08-11T11:15:25.445230", "target": "temp_gt_30c_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 92.75243074488063, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.445229", "entry_roll": 6847.768603660328} +{"action": "close", "time": "2026-08-11T11:15:25.489621", "target": "temp_gt_30c_24h_2026-08-11", "won": false, "profit_usdc": -500.0, "bankroll_after": 6347.768603660328, "hours_open": 1.2330277777777777e-05} +{"action": "close", "time": "2026-08-11T11:15:25.489633", "target": "rain_gt_0mm_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 6847.768603660328, "hours_open": 1.2336388888888889e-05} +{"action": "close", "time": "2026-08-11T11:15:25.489640", "target": "temp_gt_30c_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 7347.768603660328, "hours_open": 1.2338333333333334e-05} +{"action": "close", "time": "2026-08-11T11:15:25.489646", "target": "temp_gt_35c_24h_2026-08-12", "won": false, "profit_usdc": -500.0, "bankroll_after": 6847.768603660328, "hours_open": 1.2339166666666667e-05} +{"action": "close", "time": "2026-08-11T11:15:25.489653", "target": "temp_gt_35c_24h", "won": true, "profit_usdc": 367.967988912135, "bankroll_after": 7215.736592572463, "hours_open": 1.2340555555555556e-05} +{"action": "close", "time": "2026-08-11T11:15:25.489659", "target": "rain_gt_0mm_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 7715.736592572463, "hours_open": 1.2341666666666666e-05} +{"action": "close", "time": "2026-08-11T11:15:25.489666", "target": "rain_gt_5mm_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 8215.736592572463, "hours_open": 1.2342777777777779e-05} +{"action": "close", "time": "2026-08-11T11:15:25.489673", "target": "temp_gt_30c_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 8715.736592572463, "hours_open": 1.2344166666666666e-05} +{"action": "open", "time": "2026-08-11T11:15:25.489770", "target": "temp_gt_30c_24h_2026-08-11", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.05728522682189, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.489768", "entry_roll": 8715.736592572463} +{"action": "open", "time": "2026-08-11T11:15:25.489773", "target": "rain_gt_0mm_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.489772", "entry_roll": 8715.736592572463} +{"action": "open", "time": "2026-08-11T11:15:25.489776", "target": "temp_gt_30c_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.489775", "entry_roll": 8715.736592572463} +{"action": "open", "time": "2026-08-11T11:15:25.489779", "target": "temp_gt_35c_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 78.44979569886223, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.489778", "entry_roll": 8715.736592572463} +{"action": "open", "time": "2026-08-11T11:15:25.489782", "target": "rain_gt_0mm_24h", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 81.09367496911236, "entry_time": "2026-08-11T11:15:25.489780", "entry_roll": 8715.736592572463} +{"action": "open", "time": "2026-08-11T11:15:25.489784", "target": "rain_gt_0mm_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 94.00318546186173, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.489783", "entry_roll": 8715.736592572463} +{"action": "open", "time": "2026-08-11T11:15:25.489787", "target": "rain_gt_5mm_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 92.4065187951951, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.489786", "entry_roll": 8715.736592572463} +{"action": "open", "time": "2026-08-11T11:15:25.489790", "target": "temp_gt_30c_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 92.75243074488063, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.489788", "entry_roll": 8715.736592572463} +{"action": "close", "time": "2026-08-11T11:15:25.527189", "target": "temp_gt_30c_24h_2026-08-11", "won": true, "profit_usdc": 500.0, "bankroll_after": 9215.736592572463, "hours_open": 1.0390833333333334e-05} +{"action": "close", "time": "2026-08-11T11:15:25.527197", "target": "rain_gt_0mm_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 9715.736592572463, "hours_open": 1.0395e-05} +{"action": "close", "time": "2026-08-11T11:15:25.527202", "target": "temp_gt_30c_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 10215.736592572463, "hours_open": 1.0395555555555555e-05} +{"action": "close", "time": "2026-08-11T11:15:25.527205", "target": "temp_gt_35c_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 10715.736592572463, "hours_open": 1.0395833333333333e-05} +{"action": "close", "time": "2026-08-11T11:15:25.527209", "target": "rain_gt_0mm_24h", "won": true, "profit_usdc": 116.57089802681182, "bankroll_after": 10832.307490599274, "hours_open": 1.0396388888888889e-05} +{"action": "close", "time": "2026-08-11T11:15:25.527213", "target": "rain_gt_0mm_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 11332.307490599274, "hours_open": 1.0396388888888889e-05} +{"action": "close", "time": "2026-08-11T11:15:25.527217", "target": "rain_gt_5mm_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 11832.307490599274, "hours_open": 1.0396666666666668e-05} +{"action": "close", "time": "2026-08-11T11:15:25.527220", "target": "temp_gt_30c_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 12332.307490599274, "hours_open": 1.0397222222222222e-05} +{"action": "open", "time": "2026-08-11T11:15:25.527313", "target": "temp_gt_30c_24h_2026-08-11", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.05728522682189, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.527311", "entry_roll": 12332.307490599274} +{"action": "open", "time": "2026-08-11T11:15:25.527315", "target": "rain_gt_0mm_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.527314", "entry_roll": 12332.307490599274} +{"action": "open", "time": "2026-08-11T11:15:25.527318", "target": "temp_gt_30c_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.527317", "entry_roll": 12332.307490599274} +{"action": "open", "time": "2026-08-11T11:15:25.527320", "target": "temp_gt_35c_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 78.44979569886223, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.527319", "entry_roll": 12332.307490599274} +{"action": "open", "time": "2026-08-11T11:15:25.527322", "target": "rain_gt_0mm_24h", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 82.03442769258075, "entry_time": "2026-08-11T11:15:25.527321", "entry_roll": 12332.307490599274} +{"action": "open", "time": "2026-08-11T11:15:25.527324", "target": "temp_gt_35c_24h", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 78.44979569886223, "entry_market": 67.99128161110626, "entry_time": "2026-08-11T11:15:25.527323", "entry_roll": 12332.307490599274} +{"action": "open", "time": "2026-08-11T11:15:25.527326", "target": "rain_gt_0mm_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 94.00318546186173, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.527325", "entry_roll": 12332.307490599274} +{"action": "open", "time": "2026-08-11T11:15:25.527328", "target": "rain_gt_5mm_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 92.4065187951951, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.527327", "entry_roll": 12332.307490599274} +{"action": "open", "time": "2026-08-11T11:15:25.527330", "target": "temp_gt_30c_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 92.75243074488063, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.527329", "entry_roll": 12332.307490599274} +{"action": "close", "time": "2026-08-11T11:15:25.564433", "target": "temp_gt_30c_24h_2026-08-11", "won": true, "profit_usdc": 500.0, "bankroll_after": 12832.307490599274, "hours_open": 1.0307777777777779e-05} +{"action": "close", "time": "2026-08-11T11:15:25.564442", "target": "rain_gt_0mm_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 13332.307490599274, "hours_open": 1.03125e-05} +{"action": "close", "time": "2026-08-11T11:15:25.564447", "target": "temp_gt_30c_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 13832.307490599274, "hours_open": 1.0313055555555556e-05} +{"action": "close", "time": "2026-08-11T11:15:25.564451", "target": "temp_gt_35c_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 14332.307490599274, "hours_open": 1.0313611111111111e-05} +{"action": "close", "time": "2026-08-11T11:15:25.564454", "target": "rain_gt_0mm_24h", "won": true, "profit_usdc": 109.50019895759004, "bankroll_after": 14441.807689556865, "hours_open": 1.0314166666666665e-05} +{"action": "close", "time": "2026-08-11T11:15:25.564458", "target": "temp_gt_35c_24h", "won": false, "profit_usdc": -500.0, "bankroll_after": 13941.807689556865, "hours_open": 1.0314722222222223e-05} +{"action": "close", "time": "2026-08-11T11:15:25.564462", "target": "rain_gt_0mm_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 14441.807689556865, "hours_open": 1.0315e-05} +{"action": "close", "time": "2026-08-11T11:15:25.564465", "target": "rain_gt_5mm_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 14941.807689556865, "hours_open": 1.0315555555555556e-05} +{"action": "close", "time": "2026-08-11T11:15:25.564469", "target": "temp_gt_30c_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 15441.807689556865, "hours_open": 1.0315833333333334e-05} +{"action": "open", "time": "2026-08-11T11:15:25.564562", "target": "temp_gt_30c_24h_2026-08-11", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.05728522682189, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.564560", "entry_roll": 15441.807689556865} +{"action": "open", "time": "2026-08-11T11:15:25.564566", "target": "rain_gt_0mm_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.564564", "entry_roll": 15441.807689556865} +{"action": "open", "time": "2026-08-11T11:15:25.564568", "target": "temp_gt_30c_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.564567", "entry_roll": 15441.807689556865} +{"action": "open", "time": "2026-08-11T11:15:25.564571", "target": "temp_gt_35c_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 78.44979569886223, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.564570", "entry_roll": 15441.807689556865} +{"action": "open", "time": "2026-08-11T11:15:25.564574", "target": "temp_gt_35c_24h", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 78.44979569886223, "entry_market": 63.51539001473006, "entry_time": "2026-08-11T11:15:25.564572", "entry_roll": 15441.807689556865} +{"action": "open", "time": "2026-08-11T11:15:25.564576", "target": "rain_gt_0mm_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 94.00318546186173, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.564575", "entry_roll": 15441.807689556865} +{"action": "open", "time": "2026-08-11T11:15:25.564579", "target": "rain_gt_5mm_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 92.4065187951951, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.564578", "entry_roll": 15441.807689556865} +{"action": "open", "time": "2026-08-11T11:15:25.564581", "target": "temp_gt_30c_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 92.75243074488063, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.564580", "entry_roll": 15441.807689556865} +{"action": "close", "time": "2026-08-11T11:15:25.603651", "target": "temp_gt_30c_24h_2026-08-11", "won": true, "profit_usdc": 500.0, "bankroll_after": 15941.807689556865, "hours_open": 1.0854166666666667e-05} +{"action": "close", "time": "2026-08-11T11:15:25.603661", "target": "rain_gt_0mm_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 16441.807689556867, "hours_open": 1.0859166666666668e-05} +{"action": "close", "time": "2026-08-11T11:15:25.603665", "target": "temp_gt_30c_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 16941.807689556867, "hours_open": 1.0859722222222222e-05} +{"action": "close", "time": "2026-08-11T11:15:25.603668", "target": "temp_gt_35c_24h_2026-08-12", "won": false, "profit_usdc": -500.0, "bankroll_after": 16441.807689556867, "hours_open": 1.086e-05} +{"action": "close", "time": "2026-08-11T11:15:25.603672", "target": "temp_gt_35c_24h", "won": true, "profit_usdc": 287.2107844792316, "bankroll_after": 16729.018474036096, "hours_open": 1.0860555555555555e-05} +{"action": "close", "time": "2026-08-11T11:15:25.603676", "target": "rain_gt_0mm_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 17229.018474036096, "hours_open": 1.0860555555555555e-05} +{"action": "close", "time": "2026-08-11T11:15:25.603679", "target": "rain_gt_5mm_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 17729.018474036096, "hours_open": 1.0860833333333335e-05} +{"action": "close", "time": "2026-08-11T11:15:25.603682", "target": "temp_gt_30c_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 18229.018474036096, "hours_open": 1.0861111111111112e-05} +{"action": "open", "time": "2026-08-11T11:15:25.603765", "target": "temp_gt_30c_24h_2026-08-11", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.05728522682189, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.603764", "entry_roll": 18229.018474036096} +{"action": "open", "time": "2026-08-11T11:15:25.603768", "target": "rain_gt_0mm_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.603767", "entry_roll": 18229.018474036096} +{"action": "open", "time": "2026-08-11T11:15:25.603770", "target": "temp_gt_30c_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.603769", "entry_roll": 18229.018474036096} +{"action": "open", "time": "2026-08-11T11:15:25.603773", "target": "temp_gt_35c_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 78.44979569886223, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.603772", "entry_roll": 18229.018474036096} +{"action": "open", "time": "2026-08-11T11:15:25.603775", "target": "temp_gt_30c_24h", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 79.69502446300415, "entry_time": "2026-08-11T11:15:25.603774", "entry_roll": 18229.018474036096} +{"action": "open", "time": "2026-08-11T11:15:25.603777", "target": "temp_gt_35c_24h", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 78.44979569886223, "entry_market": 56.959327259727026, "entry_time": "2026-08-11T11:15:25.603776", "entry_roll": 18229.018474036096} +{"action": "open", "time": "2026-08-11T11:15:25.603783", "target": "rain_gt_0mm_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 94.00318546186173, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.603778", "entry_roll": 18229.018474036096} +{"action": "open", "time": "2026-08-11T11:15:25.603785", "target": "rain_gt_5mm_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 92.4065187951951, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.603784", "entry_roll": 18229.018474036096} +{"action": "open", "time": "2026-08-11T11:15:25.603787", "target": "temp_gt_30c_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 92.75243074488063, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.603786", "entry_roll": 18229.018474036096} +{"action": "close", "time": "2026-08-11T11:15:25.645463", "target": "temp_gt_30c_24h_2026-08-11", "won": true, "profit_usdc": 500.0, "bankroll_after": 18729.018474036096, "hours_open": 1.1578055555555556e-05} +{"action": "close", "time": "2026-08-11T11:15:25.645472", "target": "rain_gt_0mm_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 19229.018474036096, "hours_open": 1.1583611111111111e-05} +{"action": "close", "time": "2026-08-11T11:15:25.645476", "target": "temp_gt_30c_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 19729.018474036096, "hours_open": 1.1584722222222222e-05} +{"action": "close", "time": "2026-08-11T11:15:25.645480", "target": "temp_gt_35c_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 20229.018474036096, "hours_open": 1.1585e-05} +{"action": "close", "time": "2026-08-11T11:15:25.645484", "target": "temp_gt_30c_24h", "won": true, "profit_usdc": 127.3917391569519, "bankroll_after": 20356.41021319305, "hours_open": 1.1585277777777778e-05} +{"action": "close", "time": "2026-08-11T11:15:25.645488", "target": "temp_gt_35c_24h", "won": true, "profit_usdc": 377.8193564682847, "bankroll_after": 20734.229569661333, "hours_open": 1.1585833333333334e-05} +{"action": "close", "time": "2026-08-11T11:15:25.645491", "target": "rain_gt_0mm_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 21234.229569661333, "hours_open": 1.158638888888889e-05} +{"action": "close", "time": "2026-08-11T11:15:25.645495", "target": "rain_gt_5mm_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 21734.229569661333, "hours_open": 1.1585833333333334e-05} +{"action": "close", "time": "2026-08-11T11:15:25.645498", "target": "temp_gt_30c_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 22234.229569661333, "hours_open": 1.158611111111111e-05} +{"action": "open", "time": "2026-08-11T11:15:25.645594", "target": "temp_gt_30c_24h_2026-08-11", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.05728522682189, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.645592", "entry_roll": 22234.229569661333} +{"action": "open", "time": "2026-08-11T11:15:25.645598", "target": "rain_gt_0mm_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.645596", "entry_roll": 22234.229569661333} +{"action": "open", "time": "2026-08-11T11:15:25.645601", "target": "temp_gt_30c_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.645600", "entry_roll": 22234.229569661333} +{"action": "open", "time": "2026-08-11T11:15:25.645603", "target": "temp_gt_35c_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 78.44979569886223, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.645602", "entry_roll": 22234.229569661333} +{"action": "open", "time": "2026-08-11T11:15:25.645606", "target": "temp_gt_30c_24h", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 78.43410652947102, "entry_time": "2026-08-11T11:15:25.645605", "entry_roll": 22234.229569661333} +{"action": "open", "time": "2026-08-11T11:15:25.645609", "target": "rain_gt_0mm_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 94.00318546186173, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.645607", "entry_roll": 22234.229569661333} +{"action": "open", "time": "2026-08-11T11:15:25.645617", "target": "rain_gt_5mm_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 92.4065187951951, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.645615", "entry_roll": 22234.229569661333} +{"action": "open", "time": "2026-08-11T11:15:25.645619", "target": "temp_gt_30c_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 92.75243074488063, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.645618", "entry_roll": 22234.229569661333} +{"action": "close", "time": "2026-08-11T11:15:25.684377", "target": "temp_gt_30c_24h_2026-08-11", "won": true, "profit_usdc": 500.0, "bankroll_after": 22734.229569661333, "hours_open": 1.077e-05} +{"action": "close", "time": "2026-08-11T11:15:25.684386", "target": "rain_gt_0mm_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 23234.229569661333, "hours_open": 1.077388888888889e-05} +{"action": "close", "time": "2026-08-11T11:15:25.684390", "target": "temp_gt_30c_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 23734.229569661333, "hours_open": 1.0774444444444445e-05} +{"action": "close", "time": "2026-08-11T11:15:25.684394", "target": "temp_gt_35c_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 24234.229569661333, "hours_open": 1.0774722222222222e-05} +{"action": "close", "time": "2026-08-11T11:15:25.684398", "target": "temp_gt_30c_24h", "won": true, "profit_usdc": 137.47777863974105, "bankroll_after": 24371.707348301075, "hours_open": 1.0775e-05} +{"action": "close", "time": "2026-08-11T11:15:25.684402", "target": "rain_gt_0mm_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 24871.707348301075, "hours_open": 1.0775555555555555e-05} +{"action": "close", "time": "2026-08-11T11:15:25.684405", "target": "rain_gt_5mm_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 25371.707348301075, "hours_open": 1.0774444444444445e-05} +{"action": "close", "time": "2026-08-11T11:15:25.684409", "target": "temp_gt_30c_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 25871.707348301075, "hours_open": 1.0774444444444445e-05} +{"action": "open", "time": "2026-08-11T11:15:25.684490", "target": "temp_gt_30c_24h_2026-08-11", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.05728522682189, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.684488", "entry_roll": 25871.707348301075} +{"action": "open", "time": "2026-08-11T11:15:25.684492", "target": "rain_gt_0mm_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.684491", "entry_roll": 25871.707348301075} +{"action": "open", "time": "2026-08-11T11:15:25.684495", "target": "temp_gt_30c_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.684494", "entry_roll": 25871.707348301075} +{"action": "open", "time": "2026-08-11T11:15:25.684497", "target": "temp_gt_35c_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 78.44979569886223, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.684496", "entry_roll": 25871.707348301075} +{"action": "open", "time": "2026-08-11T11:15:25.684499", "target": "temp_gt_30c_24h", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 83.36269548439769, "entry_time": "2026-08-11T11:15:25.684498", "entry_roll": 25871.707348301075} +{"action": "open", "time": "2026-08-11T11:15:25.684501", "target": "temp_gt_35c_24h", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 78.44979569886223, "entry_market": 57.18995781812152, "entry_time": "2026-08-11T11:15:25.684500", "entry_roll": 25871.707348301075} +{"action": "open", "time": "2026-08-11T11:15:25.684503", "target": "rain_gt_0mm_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 94.00318546186173, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.684502", "entry_roll": 25871.707348301075} +{"action": "open", "time": "2026-08-11T11:15:25.684505", "target": "rain_gt_5mm_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 92.4065187951951, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.684504", "entry_roll": 25871.707348301075} +{"action": "open", "time": "2026-08-11T11:15:25.684507", "target": "temp_gt_30c_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 92.75243074488063, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.684506", "entry_roll": 25871.707348301075} +{"action": "close", "time": "2026-08-11T11:15:25.721058", "target": "temp_gt_30c_24h_2026-08-11", "won": true, "profit_usdc": 500.0, "bankroll_after": 26371.707348301075, "hours_open": 1.015388888888889e-05} +{"action": "close", "time": "2026-08-11T11:15:25.721067", "target": "rain_gt_0mm_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 26871.707348301075, "hours_open": 1.015888888888889e-05} +{"action": "close", "time": "2026-08-11T11:15:25.721073", "target": "temp_gt_30c_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 27371.707348301075, "hours_open": 1.0159722222222223e-05} +{"action": "close", "time": "2026-08-11T11:15:25.721077", "target": "temp_gt_35c_24h_2026-08-12", "won": true, "profit_usdc": 500.0, "bankroll_after": 27871.707348301075, "hours_open": 1.0160555555555555e-05} +{"action": "close", "time": "2026-08-11T11:15:25.721082", "target": "temp_gt_30c_24h", "won": true, "profit_usdc": 99.78866697464323, "bankroll_after": 27971.49601527572, "hours_open": 1.016138888888889e-05} +{"action": "close", "time": "2026-08-11T11:15:25.721087", "target": "temp_gt_35c_24h", "won": true, "profit_usdc": 374.27936490200966, "bankroll_after": 28345.77538017773, "hours_open": 1.0162222222222222e-05} +{"action": "close", "time": "2026-08-11T11:15:25.721092", "target": "rain_gt_0mm_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 28845.77538017773, "hours_open": 1.0162777777777778e-05} +{"action": "close", "time": "2026-08-11T11:15:25.721095", "target": "rain_gt_5mm_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 29345.77538017773, "hours_open": 1.0163611111111113e-05} +{"action": "close", "time": "2026-08-11T11:15:25.721099", "target": "temp_gt_30c_24h_2026-08-13", "won": true, "profit_usdc": 500.0, "bankroll_after": 29845.77538017773, "hours_open": 1.0164166666666667e-05} +{"action": "open", "time": "2026-08-11T11:15:25.721180", "target": "temp_gt_30c_24h_2026-08-11", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.05728522682189, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.721178", "entry_roll": 29845.77538017773} +{"action": "open", "time": "2026-08-11T11:15:25.721183", "target": "rain_gt_0mm_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.721181", "entry_roll": 29845.77538017773} +{"action": "open", "time": "2026-08-11T11:15:25.721185", "target": "temp_gt_30c_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 93.93535125441778, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.721184", "entry_roll": 29845.77538017773} +{"action": "open", "time": "2026-08-11T11:15:25.721187", "target": "temp_gt_35c_24h_2026-08-12", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 78.44979569886223, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.721186", "entry_roll": 29845.77538017773} +{"action": "open", "time": "2026-08-11T11:15:25.721189", "target": "temp_gt_35c_24h", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 78.44979569886223, "entry_market": 67.49202483107283, "entry_time": "2026-08-11T11:15:25.721188", "entry_roll": 29845.77538017773} +{"action": "open", "time": "2026-08-11T11:15:25.721191", "target": "rain_gt_0mm_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 94.00318546186173, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.721190", "entry_roll": 29845.77538017773} +{"action": "open", "time": "2026-08-11T11:15:25.721194", "target": "rain_gt_5mm_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 92.4065187951951, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.721193", "entry_roll": 29845.77538017773} +{"action": "open", "time": "2026-08-11T11:15:25.721196", "target": "temp_gt_30c_24h_2026-08-13", "side": "buy_yes", "size_usdc": 500.0, "entry_prob": 92.75243074488063, "entry_market": 50.0, "entry_time": "2026-08-11T11:15:25.721195", "entry_roll": 29845.77538017773} diff --git a/ml/paper_trader.py b/ml/paper_trader.py new file mode 100644 index 0000000..01d013d --- /dev/null +++ b/ml/paper_trader.py @@ -0,0 +1,353 @@ +#!/usr/bin/env python3 +""" +Paper Trading Simulator for HK Weather Prediction Markets. + +Simulates trading against hypothetical market prices using ML model +predictions, tracking P&L, Sharpe ratio, and drawdown over time. + +Usage: + python ml/paper_trader.py # Single run + python ml/paper_trader.py --track # Monitor mode (every 6h) + python ml/paper_trader.py --report # Print historical report +""" + +import sys +import json +import time +import argparse +from pathlib import Path +from datetime import datetime, timedelta +from typing import Dict, List, Optional + +import numpy as np +import pandas as pd + +sys.path.insert(0, str(Path(__file__).parent.parent)) + +from ml.predictor import MLPredictor +from ml.model import TARGET_DEFINITIONS +from strategy.portfolio_kelly import PortfolioKelly + +TRADE_LOG = Path(__file__).parent.parent / "data" / "paper_trades.jsonl" +HISTORY_LOG = Path(__file__).parent.parent / "data" / "paper_pnl.csv" + + +class TimeDecayModel: + """Models theta decay for binary options approaching resolution. + + Near-expiry markets exhibit predictable uncertainty collapse. + The market often overprices uncertainty at intermediate horizons + and suddenly converges to certainty near expiry. + + Model: sigma(t) = sigma_0 * (T - t)^beta + Where beta ~ 0.3-0.5 for weather outcomes (slower decay than financial). + """ + + def __init__(self): + self.beta = 0.4 # Weather-specific decay exponent + self.min_sigma = 0.02 # Minimum uncertainty at t=0 + + def decay_factor(self, hours_to_expiry: float, max_horizon: float = 168.0) -> float: + """ + Compute time decay factor for binary option. + + At t=max_horizon: factor = 1.0 (maximum uncertainty) + At t=0: factor = min_sigma/sigma_0 (minimum uncertainty) + + Returns factor in [0, 1] representing remaining uncertainty fraction. + """ + if hours_to_expiry <= 0: + return self.min_sigma + if hours_to_expiry >= max_horizon: + return 1.0 + tau = hours_to_expiry / max_horizon + return self.min_sigma + (1.0 - self.min_sigma) * tau ** self.beta + + def fair_price_convergence( + self, + model_probability: float, + hours_to_expiry: float, + market_probability: Optional[float] = None, + ) -> dict: + """ + Compute fair price adjusted for time decay. + + When hours_to_expiry is large, the model probability should be closer + to 50% (maximum uncertainty). As expiry approaches, it should converge + to either 0% or 100%. + + Returns dict with fair_price, uncertainty_band, and edge vs market. + """ + decay = self.decay_factor(hours_to_expiry) + prob_0_1 = model_probability / 100.0 + + # Fair price: blend between 50% (at t=far) and model_prob (at t=0) + fair_price = 50.0 + (model_probability - 50.0) * (1.0 - decay) + + # Uncertainty band: width proportional to remaining time + # At t=0: band = 0 (certain). At t=max: band = 30pp. + if market_probability is not None: + edge = fair_price - market_probability + else: + edge = 0.0 + + return { + "fair_price": fair_price, + "time_decay_factor": 1.0 - decay, + "edge_vs_market": edge, + "hours_to_expiry": hours_to_expiry, + } + + +class PaperTrader: + """Simulate trading using ML predictions and track P&L.""" + + def __init__(self, bankroll: float = 1000.0, min_edge_bps: float = 50): + self.bankroll = bankroll + self.initial_bankroll = bankroll + self.min_edge_bps = min_edge_bps + self.predictor = MLPredictor(bankroll_usdc=bankroll, min_edge_bps=min_edge_bps) + self.time_decay = TimeDecayModel() + self.portfolio_kelly = PortfolioKelly() + + # State + self.positions: Dict[str, dict] = {} # target -> {side, size, entry_prob, entry_time} + self.trade_history: List[dict] = [] + self.pnl_history: List[float] = [bankroll] + self.dates: List[str] = [datetime.now().strftime("%Y-%m-%d %H:%M")] + + self._load_history() + + def run(self, market_prices: Optional[Dict[str, float]] = None, resolution_hours: float = 24.0): + """Execute a single trading cycle. + + Parameters + ---------- + market_prices : dict + {target_name: market_implied_probability_0_100} + If None, uses simulated prices (model - noise). + resolution_hours : float + Hours until positions auto-resolve (24h default, 0 for immediate). + """ + self.predictor.fetch_and_predict() + + if market_prices is None: + market_prices = self._simulate_market_prices() + + self._close_resolved(resolution_hours) + self._open_new(market_prices) + self._save() + + def _simulate_market_prices(self) -> Dict[str, float]: + """Simulate market prices with realistic bid-ask spreads.""" + prices = {} + predictions = self.predictor._last_predictions or {} + + for target in TARGET_DEFINITIONS: + model_p = predictions.get(target, 50.0) + # Market price: model + noise + spread + noise = np.random.normal(0, 8) # 8% stdev market noise + # Systematic bias: market underweights extremes + bias = -0.15 * (model_p - 50) + market_p = model_p + noise + bias + # Random spread: 0.5-3% + spread = np.random.uniform(0.5, 3.0) + # Round to nearest spread tick + market_p = round(market_p / spread) * spread + prices[target] = float(np.clip(market_p, 1, 99)) + + return prices + + def _open_new(self, market_prices: Dict[str, float]): + """Open new positions based on ML signals.""" + predictions = self.predictor._last_predictions or {} + + # Collect edges + edges = {} + for target, model_p in predictions.items(): + mkt_p = market_prices.get(target, 50.0) + edge_decimal = (model_p - mkt_p) / 100.0 + if abs(edge_decimal * 100) >= self.min_edge_bps: + edges[target] = edge_decimal + + if not edges: + return + + # Portfolio Kelly sizing + sizes = self.portfolio_kelly.simultaneous_kelly(edges, bankroll=self.bankroll) + + for target, size in sizes.items(): + if size < 1.0 or target in self.positions: + continue + + model_p = predictions[target] + mkt_p = market_prices.get(target, 50.0) + side = "buy_yes" if model_p > mkt_p else "buy_no" + + position = { + "target": target, + "side": side, + "size_usdc": size, + "entry_prob": model_p, + "entry_market": mkt_p, + "entry_time": datetime.now().isoformat(), + "entry_roll": self.bankroll, + } + self.positions[target] = position + + self.trade_history.append({ + "action": "open", + "time": datetime.now().isoformat(), + **position, + }) + + def _close_resolved(self, resolution_hours: float = 24.0): + """Close positions where outcomes are known.""" + closed = [] + for target, pos in list(self.positions.items()): + # Simulate outcome resolution after 24h + entry_time = datetime.fromisoformat(pos["entry_time"]) + hours_open = (datetime.now() - entry_time).total_seconds() / 3600 + + if hours_open >= resolution_hours: + # Random resolution biased by our probability + our_p = pos["entry_prob"] / 100.0 + won = np.random.random() < our_p + + if pos["side"] == "buy_yes": + profit = pos["size_usdc"] * ((1 - pos["entry_market"] / 100) / (pos["entry_market"] / 100)) if won else -pos["size_usdc"] + else: + mkt_no = 100 - pos["entry_market"] + profit = pos["size_usdc"] * ((1 - mkt_no / 100) / (mkt_no / 100)) if won else -pos["size_usdc"] + + self.bankroll += profit + self.pnl_history.append(self.bankroll) + self.dates.append(datetime.now().strftime("%Y-%m-%d %H:%M")) + + self.trade_history.append({ + "action": "close", + "time": datetime.now().isoformat(), + "target": target, + "won": won, + "profit_usdc": profit, + "bankroll_after": self.bankroll, + "hours_open": hours_open, + }) + closed.append(target) + + for target in closed: + del self.positions[target] + + def report(self) -> str: + """Generate performance report.""" + if len(self.pnl_history) < 2: + return "No trading history yet." + + pnl = np.array(self.pnl_history) + returns = np.diff(pnl) / (pnl[:-1] + 1e-9) + + total_trades = len([t for t in self.trade_history if t["action"] == "close"]) + wins = len([t for t in self.trade_history if t["action"] == "close" and t.get("won")]) + losses = total_trades - wins + + sharpe = np.mean(returns) / max(np.std(returns), 1e-9) * np.sqrt(365) if len(returns) > 1 else 0 + max_dd = max(1 - np.minimum.accumulate(pnl) / np.maximum.accumulate(pnl)) * 100 + roi = (self.bankroll - self.initial_bankroll) / self.initial_bankroll * 100 + + lines = [ + f"=== Paper Trading Report ({datetime.now():%Y-%m-%d %H:%M}) ===", + f" Bankroll: ${self.initial_bankroll:.0f} → ${self.bankroll:.0f} ({roi:+.1f}%)", + f" Trades: {total_trades} ({wins}W/{losses}L, {wins/max(total_trades,1)*100:.0f}% win)", + f" Sharpe: {sharpe:.2f}", + f" Max DD: {max_dd:.1f}%", + f" Open positions: {len(self.positions)}", + ] + + if self.positions: + lines.append(f" Open:") + for target, pos in self.positions.items(): + lines.append(f" {target}: {pos['side']} ${pos['size_usdc']:.0f} @ {pos['entry_prob']:.0f}%") + + return "\n".join(lines) + + def _save(self): + """Persist trade state.""" + TRADE_LOG.parent.mkdir(parents=True, exist_ok=True) + + with open(TRADE_LOG, "w") as f: + for t in self.trade_history: + f.write(json.dumps(t) + "\n") + + pd.DataFrame({ + "date": self.dates, + "bankroll": self.pnl_history, + }).to_csv(HISTORY_LOG, index=False) + + def _load_history(self): + """Load previous trading history.""" + if HISTORY_LOG.exists(): + try: + df = pd.read_csv(HISTORY_LOG) + self.pnl_history = df["bankroll"].tolist() + self.dates = df["date"].tolist() + self.bankroll = self.pnl_history[-1] if self.pnl_history else self.initial_bankroll + except Exception: + pass + + if TRADE_LOG.exists(): + try: + with open(TRADE_LOG) as f: + for line in f: + if line.strip(): + self.trade_history.append(json.loads(line)) + except Exception: + pass + + +def main(): + parser = argparse.ArgumentParser(description="Paper trading simulator") + parser.add_argument("--bankroll", type=float, default=1000.0) + parser.add_argument("--edge", type=float, default=50, help="Min edge in bps") + parser.add_argument("--track", action="store_true", help="Run continuously") + parser.add_argument("--report", action="store_true", help="Print report and exit") + parser.add_argument("--simulate-days", type=int, default=0, help="Simulate N days of trading") + args = parser.parse_args() + + trader = PaperTrader(bankroll=args.bankroll, min_edge_bps=args.edge) + + if args.report: + print(trader.report()) + return + + if args.simulate_days > 0: + print(f"Simulating {args.simulate_days} days of trading...") + for i in range(args.simulate_days): + trader.run() + if (i + 1) % 10 == 0: + print(f" Day {i+1}/{args.simulate_days} | Bankroll: ${trader.bankroll:.0f}") + print(trader.report()) + return + + if args.track: + print(f"Paper trading monitor starting. Bankroll: ${args.bankroll:.0f}") + print("Running every 6 hours. Ctrl+C to stop.\n") + + trader.run() + print(trader.report()) + + while True: + try: + time.sleep(6 * 3600) + trader.run() + print(trader.report()) + except KeyboardInterrupt: + print("\nStopping monitor.") + print(trader.report()) + break + else: + trader.run() + print(trader.report()) + + +if __name__ == "__main__": + main() diff --git a/web_dashboard.py b/web_dashboard.py index 3c4f901..e6bc2c0 100644 --- a/web_dashboard.py +++ b/web_dashboard.py @@ -26,6 +26,15 @@ from weather.openmeteo_client import OpenMeteoClient from strategy.kelly import KellyCriterion from config import HK_COORDS +import sys +from pathlib import Path +sys.path.insert(0, str(Path(__file__).parent)) +try: + from ml import MLPredictor + _ml_available = True +except Exception: + _ml_available = False + app = Flask(__name__) HTML_TEMPLATE = r''' @@ -193,6 +202,19 @@ HTML_TEMPLATE = r'''
+ +
+

ML Model Predictions Logistic Regression

+
Loading...
+ +
+ + +
+

Typhoon Probabilities Climatological

+
Loading...
+
+

Trading Signals

@@ -240,10 +262,25 @@ function renderAll(data) { renderTomorrow(data.tomorrow); renderCharts(data.forecast); renderComparison(data.forecast); - renderSignals(data.signals); - renderKelly(data.kelly); - renderRaw(data.forecast); - renderTyphoon(data.typhoon); + renderSignals(data.signals); + renderKelly(data.kelly); + renderRaw(data.forecast); + renderTyphoon(data.typhoon); + } + // ML data refreshes independently (slower) + fetchML(); +} + +async function fetchML() { + try { + const resp = await fetch('/api/ml'); + const data = await resp.json(); + renderML(data); + renderTyphoonML(data.typhoon); + } catch(e) { + document.getElementById('ml-loading').style.display = 'block'; + document.getElementById('ml-content').style.display = 'none'; + } } function renderCurrent(current, typhoon) { @@ -467,6 +504,59 @@ function renderRaw(fc) { div.innerHTML = '
' + JSON.stringify(fc, null, 2) + '
'; } +function renderML(data) { + document.getElementById('ml-loading').style.display = 'none'; + const div = document.getElementById('ml-content'); + div.style.display = ''; + + if (data.error) { + div.innerHTML = '
ML models not available (train first: python ml/train.py)
'; + return; + } + + const models = data.models || {}; + if (Object.keys(models).length === 0) { + div.innerHTML = '
No model predictions yet
'; + return; + } + + let html = ''; + const sorted = Object.entries(models).sort((a, b) => { + const x = Math.abs(a[1].calibrated - 50); + const y = Math.abs(b[1].calibrated - 50); + return y - x; + }); + + for (const [name, m] of sorted) { + const cls = m.calibrated > 75 ? 'badge-green' : (m.calibrated < 25 ? 'badge-danger' : 'badge-info'); + html += `
+
${m.description} ${m.calibrated.toFixed(1)}%
+
raw=${m.raw.toFixed(1)}% · ${m.method}
+
`; + } + div.innerHTML = html; +} + +function renderTyphoonML(typhoon) { + const div = document.getElementById('typhoon-content'); + if (!typhoon || Object.keys(typhoon).length === 0) { + div.innerHTML = 'No data'; + return; + } + let html = ''; + const levels = ['typhoon_T1', 'typhoon_T3', 'typhoon_T8']; + for (const k of levels) { + if (typhoon[k] !== undefined) { + const cls = typhoon[k] > 30 ? 'badge-warn' : (typhoon[k] > 10 ? 'badge-info' : ''); + html += `${k.replace('typhoon_','')}: ${typhoon[k].toFixed(1)}% `; + } + } + if (typhoon['typhoon_T8_72h'] !== undefined) { + html += `
T8/72h: ${typhoon['typhoon_T8_72h'].toFixed(1)}% · T8/120h: ${(typhoon['typhoon_T8_120h']||0).toFixed(1)}%
`; + } + div.innerHTML = html; +} + refresh(); setInterval(refresh, 300000); // Every 5 min @@ -629,6 +719,53 @@ def api_dashboard(): return jsonify({"error": str(e)}), 500 +@app.route("/api/ml") +def api_ml(): + """Return ML model predictions.""" + if not _ml_available: + return jsonify({"error": "ML not available"}), 503 + + try: + predictor = MLPredictor() + predictor.fetch_and_predict() + predictions = predictor._last_predictions or {} + + # Per-model raw vs calibrated + models = {} + for target, model in predictor.ensemble.models.items(): + if predictor._last_features is not None: + raw = float(model.predict_raw(predictor._last_features[1:2])[0]) if len(predictor._last_features) > 1 else 0.5 + cal = float(model.predict_proba(predictor._last_features[1:2])[0]) if len(predictor._last_features) > 1 else 50.0 + else: + raw, cal = 0.5, 50.0 + models[target] = { + "description": model.target_def["description"], + "raw": round(raw * 100, 1), + "calibrated": round(cal, 1), + "method": model.calibrator.method, + } + + # Typhoon + typhoon = {} + if predictor._last_typhoon: + for k in ["typhoon_T1", "typhoon_T3", "typhoon_T8", + "typhoon_T8_72h", "typhoon_T8_120h"]: + if k in predictor._last_typhoon: + typhoon[k] = round(predictor._last_typhoon[k], 1) + + # Spatial + spatial = predictor._last_spatial or {} + + return jsonify({ + "fetch_time": datetime.now().strftime("%H:%M:%S"), + "models": models, + "typhoon": typhoon, + "spatial": spatial, + }) + except Exception as e: + return jsonify({"error": str(e)}), 500 + + @app.route("/") def index(): return render_template_string(HTML_TEMPLATE)