pivot improvement
This commit is contained in:
parent
249ec6f5a7
commit
d870f74d0c
1
.cursorignore
Normal file
1
.cursorignore
Normal file
@ -0,0 +1 @@
|
||||
# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv)
|
@ -1,749 +1,358 @@
|
||||
[
|
||||
{
|
||||
"trade_id": 1,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:46:48.566670+00:00",
|
||||
"exit_time": "2025-05-30T15:47:11.830306+00:00",
|
||||
"entry_price": 2604.21,
|
||||
"exit_price": 2604.4,
|
||||
"size": 0.003576,
|
||||
"gross_pnl": 0.0006794400000001952,
|
||||
"fees": 0.009312994680000002,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T17:25:58.643819+00:00",
|
||||
"exit_time": "2025-05-30T17:26:39.729472+00:00",
|
||||
"entry_price": 2550.7,
|
||||
"exit_price": 2546.59,
|
||||
"size": 0.003724,
|
||||
"gross_pnl": 0.015305639999998781,
|
||||
"fees": 0.00949115398,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.008633554679999806,
|
||||
"duration": "0:00:23.263636",
|
||||
"net_pnl": 0.005814486019998783,
|
||||
"duration": "0:00:41.085653",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
"mexc_executed": true
|
||||
},
|
||||
{
|
||||
"trade_id": 2,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:47:11.830306+00:00",
|
||||
"exit_time": "2025-05-30T15:47:16.736449+00:00",
|
||||
"entry_price": 2604.4,
|
||||
"exit_price": 2605.29,
|
||||
"size": 0.002833,
|
||||
"gross_pnl": -0.0025213699999996394,
|
||||
"fees": 0.007379525885,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T17:26:39.729472+00:00",
|
||||
"exit_time": "2025-05-30T17:26:40.742643+00:00",
|
||||
"entry_price": 2546.59,
|
||||
"exit_price": 2546.58,
|
||||
"size": 0.003456,
|
||||
"gross_pnl": -3.456000000075437e-05,
|
||||
"fees": 0.008800997759999998,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.00990089588499964,
|
||||
"duration": "0:00:04.906143",
|
||||
"net_pnl": -0.008835557760000754,
|
||||
"duration": "0:00:01.013171",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 3,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:47:16.736449+00:00",
|
||||
"exit_time": "2025-05-30T15:47:33.874932+00:00",
|
||||
"entry_price": 2605.29,
|
||||
"exit_price": 2605.1,
|
||||
"size": 0.002799,
|
||||
"gross_pnl": -0.0005318100000001527,
|
||||
"fees": 0.007291940804999999,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T17:26:40.742643+00:00",
|
||||
"exit_time": "2025-05-30T17:26:44.783909+00:00",
|
||||
"entry_price": 2546.58,
|
||||
"exit_price": 2546.69,
|
||||
"size": 0.003155,
|
||||
"gross_pnl": -0.0003470500000004017,
|
||||
"fees": 0.008034633425,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.007823750805000152,
|
||||
"duration": "0:00:17.138483",
|
||||
"net_pnl": -0.008381683425000402,
|
||||
"duration": "0:00:04.041266",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": true
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 4,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:47:33.874932+00:00",
|
||||
"exit_time": "2025-05-30T15:47:36.898270+00:00",
|
||||
"entry_price": 2605.1,
|
||||
"exit_price": 2605.1,
|
||||
"size": 0.003048,
|
||||
"gross_pnl": 0.0,
|
||||
"fees": 0.007940344799999999,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T17:26:44.783909+00:00",
|
||||
"exit_time": "2025-05-30T17:26:56.903098+00:00",
|
||||
"entry_price": 2546.69,
|
||||
"exit_price": 2546.9,
|
||||
"size": 0.003374,
|
||||
"gross_pnl": 0.0007085400000001227,
|
||||
"fees": 0.00859288633,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.007940344799999999,
|
||||
"duration": "0:00:03.023338",
|
||||
"net_pnl": -0.007884346329999877,
|
||||
"duration": "0:00:12.119189",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 5,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:47:36.898270+00:00",
|
||||
"exit_time": "2025-05-30T15:47:37.897486+00:00",
|
||||
"entry_price": 2605.1,
|
||||
"exit_price": 2604.7,
|
||||
"size": 0.003562,
|
||||
"gross_pnl": -0.001424800000000324,
|
||||
"fees": 0.0092786538,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T17:26:56.903098+00:00",
|
||||
"exit_time": "2025-05-30T17:27:03.971971+00:00",
|
||||
"entry_price": 2546.9,
|
||||
"exit_price": 2547.78,
|
||||
"size": 0.003309,
|
||||
"gross_pnl": -0.002911920000000361,
|
||||
"fees": 0.00842914806,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.010703453800000325,
|
||||
"duration": "0:00:00.999216",
|
||||
"net_pnl": -0.011341068060000362,
|
||||
"duration": "0:00:07.068873",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 6,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:47:37.897486+00:00",
|
||||
"exit_time": "2025-05-30T15:47:48.957013+00:00",
|
||||
"entry_price": 2604.7,
|
||||
"exit_price": 2604.8,
|
||||
"size": 0.002685,
|
||||
"gross_pnl": -0.0002685000000009768,
|
||||
"fees": 0.00699375375,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T17:27:03.971971+00:00",
|
||||
"exit_time": "2025-05-30T17:27:24.185714+00:00",
|
||||
"entry_price": 2547.78,
|
||||
"exit_price": 2548.0,
|
||||
"size": 0.003704,
|
||||
"gross_pnl": 0.0008148799999992589,
|
||||
"fees": 0.009437384560000001,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.007262253750000976,
|
||||
"duration": "0:00:11.059527",
|
||||
"net_pnl": -0.008622504560000742,
|
||||
"duration": "0:00:20.213743",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
"mexc_executed": true
|
||||
},
|
||||
{
|
||||
"trade_id": 7,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:47:48.957013+00:00",
|
||||
"exit_time": "2025-05-30T15:47:51.986365+00:00",
|
||||
"entry_price": 2604.8,
|
||||
"exit_price": 2604.3,
|
||||
"size": 0.003647,
|
||||
"gross_pnl": -0.0018235,
|
||||
"fees": 0.00949879385,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T17:27:24.185714+00:00",
|
||||
"exit_time": "2025-05-30T17:27:35.315014+00:00",
|
||||
"entry_price": 2548.0,
|
||||
"exit_price": 2547.67,
|
||||
"size": 0.003304,
|
||||
"gross_pnl": 0.0010903199999997596,
|
||||
"fees": 0.008418046840000002,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.011322293850000002,
|
||||
"duration": "0:00:03.029352",
|
||||
"net_pnl": -0.007327726840000242,
|
||||
"duration": "0:00:11.129300",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 8,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:47:51.986365+00:00",
|
||||
"exit_time": "2025-05-30T15:47:52.946304+00:00",
|
||||
"entry_price": 2604.3,
|
||||
"exit_price": 2604.3,
|
||||
"size": 0.002838,
|
||||
"gross_pnl": 0.0,
|
||||
"fees": 0.0073910034,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T17:27:35.315014+00:00",
|
||||
"exit_time": "2025-05-30T17:27:48.488282+00:00",
|
||||
"entry_price": 2547.67,
|
||||
"exit_price": 2547.5,
|
||||
"size": 0.003442,
|
||||
"gross_pnl": -0.0005851400000002505,
|
||||
"fees": 0.00876878757,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.0073910034,
|
||||
"duration": "0:00:00.959939",
|
||||
"net_pnl": -0.00935392757000025,
|
||||
"duration": "0:00:13.173268",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 9,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:47:52.946304+00:00",
|
||||
"exit_time": "2025-05-30T15:47:54.208771+00:00",
|
||||
"entry_price": 2604.3,
|
||||
"exit_price": 2604.3,
|
||||
"size": 0.003537,
|
||||
"gross_pnl": 0.0,
|
||||
"fees": 0.009211409100000002,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T17:27:48.488282+00:00",
|
||||
"exit_time": "2025-05-30T17:28:09.641167+00:00",
|
||||
"entry_price": 2547.5,
|
||||
"exit_price": 2547.2,
|
||||
"size": 0.003729,
|
||||
"gross_pnl": 0.0011187000000006783,
|
||||
"fees": 0.009499068150000001,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.009211409100000002,
|
||||
"duration": "0:00:01.262467",
|
||||
"net_pnl": -0.008380368149999321,
|
||||
"duration": "0:00:21.152885",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
"mexc_executed": true
|
||||
},
|
||||
{
|
||||
"trade_id": 10,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:47:54.208771+00:00",
|
||||
"exit_time": "2025-05-30T15:47:57.069714+00:00",
|
||||
"entry_price": 2604.3,
|
||||
"exit_price": 2604.39,
|
||||
"size": 0.00349,
|
||||
"gross_pnl": -0.0003140999999989208,
|
||||
"fees": 0.009089164050000001,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T17:28:09.641167+00:00",
|
||||
"exit_time": "2025-05-30T17:29:03.116674+00:00",
|
||||
"entry_price": 2547.2,
|
||||
"exit_price": 2549.4,
|
||||
"size": 0.0034,
|
||||
"gross_pnl": 0.007480000000000927,
|
||||
"fees": 0.00866422,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.00940326404999892,
|
||||
"duration": "0:00:02.860943",
|
||||
"net_pnl": -0.0011842199999990725,
|
||||
"duration": "0:00:53.475507",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
"mexc_executed": true
|
||||
},
|
||||
{
|
||||
"trade_id": 11,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:47:57.069714+00:00",
|
||||
"exit_time": "2025-05-30T15:48:34.556088+00:00",
|
||||
"entry_price": 2604.39,
|
||||
"exit_price": 2605.5,
|
||||
"size": 0.003648,
|
||||
"gross_pnl": 0.004049280000000465,
|
||||
"fees": 0.009502839360000001,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T17:29:03.116674+00:00",
|
||||
"exit_time": "2025-05-30T17:29:10.180571+00:00",
|
||||
"entry_price": 2549.4,
|
||||
"exit_price": 2549.79,
|
||||
"size": 0.003408,
|
||||
"gross_pnl": -0.0013291199999995661,
|
||||
"fees": 0.00868901976,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.005453559359999536,
|
||||
"duration": "0:00:37.486374",
|
||||
"net_pnl": -0.010018139759999566,
|
||||
"duration": "0:00:07.063897",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 12,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:48:34.556088+00:00",
|
||||
"exit_time": "2025-05-30T15:48:36.554840+00:00",
|
||||
"entry_price": 2605.5,
|
||||
"exit_price": 2605.6,
|
||||
"size": 0.002613,
|
||||
"gross_pnl": -0.00026129999999976235,
|
||||
"fees": 0.00680830215,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.007069602149999762,
|
||||
"duration": "0:00:01.998752",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": true
|
||||
},
|
||||
{
|
||||
"trade_id": 13,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:48:36.554840+00:00",
|
||||
"exit_time": "2025-05-30T15:48:37.522249+00:00",
|
||||
"entry_price": 2605.6,
|
||||
"exit_price": 2605.7,
|
||||
"size": 0.003435,
|
||||
"gross_pnl": 0.0003434999999996876,
|
||||
"fees": 0.00895040775,
|
||||
"entry_time": "2025-05-30T17:29:10.180571+00:00",
|
||||
"exit_time": "2025-05-30T17:29:19.404003+00:00",
|
||||
"entry_price": 2549.79,
|
||||
"exit_price": 2548.9,
|
||||
"size": 0.003552,
|
||||
"gross_pnl": -0.003161279999999548,
|
||||
"fees": 0.00905527344,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.008606907750000312,
|
||||
"duration": "0:00:00.967409",
|
||||
"net_pnl": -0.012216553439999549,
|
||||
"duration": "0:00:09.223432",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 14,
|
||||
"trade_id": 13,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:48:37.522249+00:00",
|
||||
"exit_time": "2025-05-30T15:48:39.531230+00:00",
|
||||
"entry_price": 2605.7,
|
||||
"exit_price": 2606.69,
|
||||
"size": 0.003062,
|
||||
"gross_pnl": -0.003031380000000724,
|
||||
"fees": 0.00798016909,
|
||||
"entry_time": "2025-05-30T17:29:19.404003+00:00",
|
||||
"exit_time": "2025-05-30T17:29:40.434581+00:00",
|
||||
"entry_price": 2548.9,
|
||||
"exit_price": 2547.8,
|
||||
"size": 0.003692,
|
||||
"gross_pnl": 0.004061199999999664,
|
||||
"fees": 0.0094085082,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.011011549090000725,
|
||||
"duration": "0:00:02.008981",
|
||||
"net_pnl": -0.005347308200000336,
|
||||
"duration": "0:00:21.030578",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": true
|
||||
},
|
||||
{
|
||||
"trade_id": 14,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T17:29:40.434581+00:00",
|
||||
"exit_time": "2025-05-30T17:29:41.445058+00:00",
|
||||
"entry_price": 2547.8,
|
||||
"exit_price": 2547.8,
|
||||
"size": 0.003729,
|
||||
"gross_pnl": 0.0,
|
||||
"fees": 0.009500746200000002,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.009500746200000002,
|
||||
"duration": "0:00:01.010477",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 15,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:48:39.531230+00:00",
|
||||
"exit_time": "2025-05-30T15:48:47.597191+00:00",
|
||||
"entry_price": 2606.69,
|
||||
"exit_price": 2605.4,
|
||||
"size": 0.003069,
|
||||
"gross_pnl": -0.003959009999999889,
|
||||
"fees": 0.007997952105000001,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T17:29:41.445058+00:00",
|
||||
"exit_time": "2025-05-30T17:29:45.488994+00:00",
|
||||
"entry_price": 2547.8,
|
||||
"exit_price": 2547.88,
|
||||
"size": 0.003215,
|
||||
"gross_pnl": -0.0002571999999997661,
|
||||
"fees": 0.0081913056,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.01195696210499989,
|
||||
"duration": "0:00:08.065961",
|
||||
"net_pnl": -0.008448505599999765,
|
||||
"duration": "0:00:04.043936",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 16,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:48:47.597191+00:00",
|
||||
"exit_time": "2025-05-30T15:48:55.696686+00:00",
|
||||
"entry_price": 2605.4,
|
||||
"exit_price": 2605.0,
|
||||
"size": 0.003267,
|
||||
"gross_pnl": 0.0013068000000002972,
|
||||
"fees": 0.008511188400000001,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T17:29:45.488994+00:00",
|
||||
"exit_time": "2025-05-30T17:30:11.732339+00:00",
|
||||
"entry_price": 2547.88,
|
||||
"exit_price": 2549.3,
|
||||
"size": 0.003189,
|
||||
"gross_pnl": 0.004528380000000232,
|
||||
"fees": 0.00812745351,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.0072043883999997034,
|
||||
"duration": "0:00:08.099495",
|
||||
"net_pnl": -0.0035990735099997685,
|
||||
"duration": "0:00:26.243345",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
"mexc_executed": true
|
||||
},
|
||||
{
|
||||
"trade_id": 17,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:48:55.696686+00:00",
|
||||
"exit_time": "2025-05-30T15:48:56.673544+00:00",
|
||||
"entry_price": 2605.0,
|
||||
"exit_price": 2605.09,
|
||||
"size": 0.003647,
|
||||
"gross_pnl": 0.0003282300000005307,
|
||||
"fees": 0.009500599115000001,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T17:30:11.732339+00:00",
|
||||
"exit_time": "2025-05-30T17:30:25.893383+00:00",
|
||||
"entry_price": 2549.3,
|
||||
"exit_price": 2548.76,
|
||||
"size": 0.003013,
|
||||
"gross_pnl": 0.0016270199999998904,
|
||||
"fees": 0.007680227390000001,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.00917236911499947,
|
||||
"duration": "0:00:00.976858",
|
||||
"net_pnl": -0.00605320739000011,
|
||||
"duration": "0:00:14.161044",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 18,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:48:56.673544+00:00",
|
||||
"exit_time": "2025-05-30T15:48:59.683812+00:00",
|
||||
"entry_price": 2605.09,
|
||||
"exit_price": 2605.2,
|
||||
"size": 0.00307,
|
||||
"gross_pnl": -0.0003376999999989948,
|
||||
"fees": 0.00799779515,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T17:30:25.893383+00:00",
|
||||
"exit_time": "2025-05-30T17:30:40.053758+00:00",
|
||||
"entry_price": 2548.76,
|
||||
"exit_price": 2549.4,
|
||||
"size": 0.002905,
|
||||
"gross_pnl": 0.0018591999999996302,
|
||||
"fees": 0.007405077400000001,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.008335495149998994,
|
||||
"duration": "0:00:03.010268",
|
||||
"net_pnl": -0.005545877400000371,
|
||||
"duration": "0:00:14.160375",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 19,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:48:59.683812+00:00",
|
||||
"exit_time": "2025-05-30T15:49:09.266816+00:00",
|
||||
"entry_price": 2605.2,
|
||||
"exit_price": 2604.77,
|
||||
"size": 0.003379,
|
||||
"gross_pnl": -0.0014529699999994469,
|
||||
"fees": 0.008802244314999999,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T17:30:40.053758+00:00",
|
||||
"exit_time": "2025-05-30T17:30:46.111367+00:00",
|
||||
"entry_price": 2549.4,
|
||||
"exit_price": 2549.8,
|
||||
"size": 0.003726,
|
||||
"gross_pnl": -0.001490400000000339,
|
||||
"fees": 0.0094998096,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.010255214314999445,
|
||||
"duration": "0:00:09.583004",
|
||||
"net_pnl": -0.01099020960000034,
|
||||
"duration": "0:00:06.057609",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": true
|
||||
},
|
||||
{
|
||||
"trade_id": 20,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:49:09.266816+00:00",
|
||||
"exit_time": "2025-05-30T15:49:11.161782+00:00",
|
||||
"entry_price": 2604.77,
|
||||
"exit_price": 2604.31,
|
||||
"size": 0.002557,
|
||||
"gross_pnl": 0.001176220000000093,
|
||||
"fees": 0.00665980878,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T17:30:46.111367+00:00",
|
||||
"exit_time": "2025-05-30T17:30:48.166894+00:00",
|
||||
"entry_price": 2549.8,
|
||||
"exit_price": 2549.21,
|
||||
"size": 0.003652,
|
||||
"gross_pnl": -0.0021546800000005312,
|
||||
"fees": 0.009310792259999999,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.005483588779999907,
|
||||
"duration": "0:00:01.894966",
|
||||
"net_pnl": -0.011465472260000532,
|
||||
"duration": "0:00:02.055527",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 21,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:49:11.161782+00:00",
|
||||
"exit_time": "2025-05-30T15:49:12.298999+00:00",
|
||||
"entry_price": 2604.31,
|
||||
"exit_price": 2603.92,
|
||||
"size": 0.003603,
|
||||
"gross_pnl": -0.0014051699999995412,
|
||||
"fees": 0.009382626344999999,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.01078779634499954,
|
||||
"duration": "0:00:01.137217",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 22,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:49:12.298999+00:00",
|
||||
"exit_time": "2025-05-30T15:49:24.339209+00:00",
|
||||
"entry_price": 2603.92,
|
||||
"exit_price": 2604.03,
|
||||
"size": 0.003234,
|
||||
"gross_pnl": -0.0003557400000004118,
|
||||
"fees": 0.008421255150000001,
|
||||
"entry_time": "2025-05-30T17:30:48.166894+00:00",
|
||||
"exit_time": "2025-05-30T17:31:12.387130+00:00",
|
||||
"entry_price": 2549.21,
|
||||
"exit_price": 2547.77,
|
||||
"size": 0.003313,
|
||||
"gross_pnl": 0.00477072000000018,
|
||||
"fees": 0.008443147370000001,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.008776995150000412,
|
||||
"duration": "0:00:12.040210",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 23,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:49:24.339209+00:00",
|
||||
"exit_time": "2025-05-30T15:49:25.364806+00:00",
|
||||
"entry_price": 2604.03,
|
||||
"exit_price": 2604.0,
|
||||
"size": 0.003211,
|
||||
"gross_pnl": -9.633000000064248e-05,
|
||||
"fees": 0.008361492165000001,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.008457822165000642,
|
||||
"duration": "0:00:01.025597",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 24,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:49:25.364806+00:00",
|
||||
"exit_time": "2025-05-30T15:49:26.274504+00:00",
|
||||
"entry_price": 2604.0,
|
||||
"exit_price": 2604.0,
|
||||
"size": 0.003067,
|
||||
"gross_pnl": 0.0,
|
||||
"fees": 0.007986468,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.007986468,
|
||||
"duration": "0:00:00.909698",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 25,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:49:26.274504+00:00",
|
||||
"exit_time": "2025-05-30T15:49:33.355299+00:00",
|
||||
"entry_price": 2604.0,
|
||||
"exit_price": 2603.61,
|
||||
"size": 0.003566,
|
||||
"gross_pnl": -0.001390739999999546,
|
||||
"fees": 0.00928516863,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.010675908629999547,
|
||||
"duration": "0:00:07.080795",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 26,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:49:33.355299+00:00",
|
||||
"exit_time": "2025-05-30T15:49:36.415411+00:00",
|
||||
"entry_price": 2603.61,
|
||||
"exit_price": 2603.6,
|
||||
"size": 0.00328,
|
||||
"gross_pnl": 3.280000000071595e-05,
|
||||
"fees": 0.0085398244,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.008507024399999284,
|
||||
"duration": "0:00:03.060112",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 27,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:49:36.415411+00:00",
|
||||
"exit_time": "2025-05-30T15:49:38.429512+00:00",
|
||||
"entry_price": 2603.6,
|
||||
"exit_price": 2602.53,
|
||||
"size": 0.00364,
|
||||
"gross_pnl": -0.0038947999999989404,
|
||||
"fees": 0.0094751566,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.013369956599998942,
|
||||
"duration": "0:00:02.014101",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 28,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:49:38.429512+00:00",
|
||||
"exit_time": "2025-05-30T15:49:47.285835+00:00",
|
||||
"entry_price": 2602.53,
|
||||
"exit_price": 2602.56,
|
||||
"size": 0.00365,
|
||||
"gross_pnl": -0.00010949999999907049,
|
||||
"fees": 0.009499289250000001,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.009608789249999071,
|
||||
"duration": "0:00:08.856323",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": true
|
||||
},
|
||||
{
|
||||
"trade_id": 29,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:49:47.285835+00:00",
|
||||
"exit_time": "2025-05-30T15:50:36.918488+00:00",
|
||||
"entry_price": 2602.56,
|
||||
"exit_price": 2605.1,
|
||||
"size": 0.003291,
|
||||
"gross_pnl": 0.008359139999999881,
|
||||
"fees": 0.008569204530000001,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.00021006453000011957,
|
||||
"duration": "0:00:49.632653",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": true
|
||||
},
|
||||
{
|
||||
"trade_id": 30,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:50:36.918488+00:00",
|
||||
"exit_time": "2025-05-30T15:50:48.718534+00:00",
|
||||
"entry_price": 2605.1,
|
||||
"exit_price": 2604.41,
|
||||
"size": 0.003411,
|
||||
"gross_pnl": 0.002353590000000186,
|
||||
"fees": 0.008884819305,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.006531229304999813,
|
||||
"duration": "0:00:11.800046",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 31,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:50:48.718534+00:00",
|
||||
"exit_time": "2025-05-30T15:50:51.034097+00:00",
|
||||
"entry_price": 2604.41,
|
||||
"exit_price": 2603.93,
|
||||
"size": 0.00337,
|
||||
"gross_pnl": -0.0016176000000000614,
|
||||
"fees": 0.008776052900000001,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.010393652900000062,
|
||||
"duration": "0:00:02.315563",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 32,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:50:51.034097+00:00",
|
||||
"exit_time": "2025-05-30T15:50:53.833190+00:00",
|
||||
"entry_price": 2603.93,
|
||||
"exit_price": 2604.2,
|
||||
"size": 0.003184,
|
||||
"gross_pnl": -0.0008596799999999421,
|
||||
"fees": 0.008291342960000002,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.009151022959999942,
|
||||
"duration": "0:00:02.799093",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 33,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:50:53.833190+00:00",
|
||||
"exit_time": "2025-05-30T15:51:12.337656+00:00",
|
||||
"entry_price": 2604.2,
|
||||
"exit_price": 2604.49,
|
||||
"size": 0.003578,
|
||||
"gross_pnl": 0.0010376199999998698,
|
||||
"fees": 0.009318346410000001,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.00828072641000013,
|
||||
"duration": "0:00:18.504466",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 34,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:51:12.337656+00:00",
|
||||
"exit_time": "2025-05-30T15:51:18.768780+00:00",
|
||||
"entry_price": 2604.49,
|
||||
"exit_price": 2604.3,
|
||||
"size": 0.002971,
|
||||
"gross_pnl": 0.0005644899999988111,
|
||||
"fees": 0.007737657545,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.007173167545001189,
|
||||
"duration": "0:00:06.431124",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": true
|
||||
},
|
||||
{
|
||||
"trade_id": 35,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:51:18.768780+00:00",
|
||||
"exit_time": "2025-05-30T15:51:41.211077+00:00",
|
||||
"entry_price": 2604.3,
|
||||
"exit_price": 2603.58,
|
||||
"size": 0.003587,
|
||||
"gross_pnl": -0.0025826400000009135,
|
||||
"fees": 0.00934033278,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.011922972780000913,
|
||||
"duration": "0:00:22.442297",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 36,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:51:41.211077+00:00",
|
||||
"exit_time": "2025-05-30T15:51:44.328012+00:00",
|
||||
"entry_price": 2603.58,
|
||||
"exit_price": 2603.8,
|
||||
"size": 0.00313,
|
||||
"gross_pnl": -0.000688600000000797,
|
||||
"fees": 0.0081495497,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.008838149700000797,
|
||||
"duration": "0:00:03.116935",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 37,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:51:44.328012+00:00",
|
||||
"exit_time": "2025-05-30T15:52:09.755402+00:00",
|
||||
"entry_price": 2603.8,
|
||||
"exit_price": 2605.17,
|
||||
"size": 0.003105,
|
||||
"gross_pnl": 0.004253849999999662,
|
||||
"fees": 0.008086925925,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.0038330759250003385,
|
||||
"duration": "0:00:25.427390",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": true
|
||||
},
|
||||
{
|
||||
"trade_id": 38,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:52:09.755402+00:00",
|
||||
"exit_time": "2025-05-30T15:52:28.457757+00:00",
|
||||
"entry_price": 2605.17,
|
||||
"exit_price": 2604.7,
|
||||
"size": 0.003439,
|
||||
"gross_pnl": 0.0016163300000008758,
|
||||
"fees": 0.008958371465,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.007342041464999125,
|
||||
"duration": "0:00:18.702355",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 39,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:52:28.457757+00:00",
|
||||
"exit_time": "2025-05-30T15:53:03.648655+00:00",
|
||||
"entry_price": 2604.7,
|
||||
"exit_price": 2605.51,
|
||||
"size": 0.003285,
|
||||
"gross_pnl": 0.0026608500000013147,
|
||||
"fees": 0.008557769925,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.005896919924998686,
|
||||
"duration": "0:00:35.190898",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 40,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:53:03.648655+00:00",
|
||||
"exit_time": "2025-05-30T15:53:17.399923+00:00",
|
||||
"entry_price": 2605.51,
|
||||
"exit_price": 2605.18,
|
||||
"size": 0.003646,
|
||||
"gross_pnl": 0.0012031800000013926,
|
||||
"fees": 0.009499087869999999,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.008295907869998606,
|
||||
"duration": "0:00:13.751268",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": true
|
||||
},
|
||||
{
|
||||
"trade_id": 41,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:53:17.399923+00:00",
|
||||
"exit_time": "2025-05-30T15:53:26.556819+00:00",
|
||||
"entry_price": 2605.18,
|
||||
"exit_price": 2605.06,
|
||||
"size": 0.003546,
|
||||
"gross_pnl": -0.000425519999999613,
|
||||
"fees": 0.009237755520000002,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.009663275519999613,
|
||||
"duration": "0:00:09.156896",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 42,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:53:26.556819+00:00",
|
||||
"exit_time": "2025-05-30T15:53:52.936931+00:00",
|
||||
"entry_price": 2605.06,
|
||||
"exit_price": 2601.4,
|
||||
"size": 0.00318,
|
||||
"gross_pnl": 0.011638799999999538,
|
||||
"fees": 0.0082782714,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": 0.0033605285999995377,
|
||||
"duration": "0:00:26.380112",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
},
|
||||
{
|
||||
"trade_id": 43,
|
||||
"side": "LONG",
|
||||
"entry_time": "2025-05-30T15:53:52.936931+00:00",
|
||||
"exit_time": "2025-05-30T15:53:56.578000+00:00",
|
||||
"entry_price": 2601.4,
|
||||
"exit_price": 2600.78,
|
||||
"size": 0.003544,
|
||||
"gross_pnl": -0.002197279999999613,
|
||||
"fees": 0.009218262960000001,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.011415542959999614,
|
||||
"duration": "0:00:03.641069",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": true
|
||||
},
|
||||
{
|
||||
"trade_id": 44,
|
||||
"side": "SHORT",
|
||||
"entry_time": "2025-05-30T15:53:56.578000+00:00",
|
||||
"exit_time": "2025-05-30T15:54:26.021540+00:00",
|
||||
"entry_price": 2600.78,
|
||||
"exit_price": 2601.2,
|
||||
"size": 0.00335,
|
||||
"gross_pnl": -0.0014069999999987205,
|
||||
"fees": 0.0087133165,
|
||||
"fee_type": "taker",
|
||||
"fee_rate": 0.0005,
|
||||
"net_pnl": -0.01012031649999872,
|
||||
"duration": "0:00:29.443540",
|
||||
"net_pnl": -0.0036724273699998197,
|
||||
"duration": "0:00:24.220236",
|
||||
"symbol": "ETH/USDC",
|
||||
"mexc_executed": false
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ from .extrema_trainer import ExtremaTrainer
|
||||
from .trading_action import TradingAction
|
||||
from .negative_case_trainer import NegativeCaseTrainer
|
||||
from .trading_executor import TradingExecutor
|
||||
from training.enhanced_pivot_rl_trainer import EnhancedPivotRLTrainer, create_enhanced_pivot_trainer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -157,12 +158,22 @@ class EnhancedTradingOrchestrator:
|
||||
self.current_positions = {} # symbol -> {'side': 'LONG'|'SHORT'|'FLAT', 'entry_price': float, 'timestamp': datetime}
|
||||
self.last_signals = {} # symbol -> {'action': 'BUY'|'SELL', 'timestamp': datetime, 'confidence': float}
|
||||
|
||||
# Different thresholds for entry vs exit
|
||||
self.entry_threshold = self.config.orchestrator.get('entry_threshold', 0.75) # Higher threshold for entries
|
||||
self.exit_threshold = self.config.orchestrator.get('exit_threshold', 0.35) # Lower threshold for exits
|
||||
# Initialize Enhanced Pivot RL Trainer
|
||||
self.pivot_rl_trainer = create_enhanced_pivot_trainer(
|
||||
data_provider=self.data_provider,
|
||||
orchestrator=self
|
||||
)
|
||||
|
||||
# Get dynamic thresholds from pivot trainer
|
||||
thresholds = self.pivot_rl_trainer.get_current_thresholds()
|
||||
self.entry_threshold = thresholds['entry_threshold'] # Higher threshold for entries
|
||||
self.exit_threshold = thresholds['exit_threshold'] # Lower threshold for exits
|
||||
self.uninvested_threshold = thresholds['uninvested_threshold'] # Stay out threshold
|
||||
|
||||
logger.info(f"Dynamic Pivot-Based Thresholds:")
|
||||
logger.info(f" Entry threshold: {self.entry_threshold:.3f} (more certain)")
|
||||
logger.info(f" Exit threshold: {self.exit_threshold:.3f} (easier to exit)")
|
||||
logger.info(f" Uninvested threshold: {self.uninvested_threshold:.3f} (stay out when uncertain)")
|
||||
|
||||
# Initialize universal data adapter
|
||||
self.universal_adapter = UniversalDataAdapter(self.data_provider)
|
||||
@ -2046,29 +2057,33 @@ class EnhancedTradingOrchestrator:
|
||||
|
||||
def _make_2_action_decision(self, symbol: str, predictions: List[EnhancedPrediction],
|
||||
market_state: MarketState) -> Optional[TradingAction]:
|
||||
"""
|
||||
Make trading decision using strict 2-action system (BUY/SELL only)
|
||||
|
||||
STRICT Logic:
|
||||
- When FLAT: BUY signal -> go LONG, SELL signal -> go SHORT
|
||||
- When LONG: SELL signal -> close LONG immediately (and optionally enter SHORT if no other positions)
|
||||
- When SHORT: BUY signal -> close SHORT immediately (and optionally enter LONG if no other positions)
|
||||
- ALWAYS close opposite positions first before opening new ones
|
||||
"""
|
||||
"""Enhanced 2-action decision making with pivot analysis and CNN predictions"""
|
||||
try:
|
||||
if not predictions:
|
||||
return None
|
||||
|
||||
try:
|
||||
# Get best prediction
|
||||
best_pred = max(predictions, key=lambda p: p.overall_confidence)
|
||||
raw_action = best_pred.overall_action
|
||||
confidence = best_pred.overall_confidence
|
||||
# Get the best prediction
|
||||
best_pred = max(predictions, key=lambda p: p.confidence)
|
||||
confidence = best_pred.confidence
|
||||
raw_action = best_pred.action
|
||||
|
||||
# Get current position for this symbol
|
||||
current_position = self.current_positions.get(symbol, {'side': 'FLAT'})
|
||||
position_side = current_position['side']
|
||||
# Update dynamic thresholds periodically
|
||||
if hasattr(self, '_last_threshold_update'):
|
||||
if (datetime.now() - self._last_threshold_update).total_seconds() > 3600: # Every hour
|
||||
self.update_dynamic_thresholds()
|
||||
self._last_threshold_update = datetime.now()
|
||||
else:
|
||||
self._last_threshold_update = datetime.now()
|
||||
|
||||
# STRICT LOGIC: Determine action type
|
||||
# Check if we should stay uninvested due to low confidence
|
||||
if confidence < self.uninvested_threshold:
|
||||
logger.info(f"[{symbol}] Staying uninvested - confidence {confidence:.3f} below threshold {self.uninvested_threshold:.3f}")
|
||||
return None
|
||||
|
||||
# Get current position
|
||||
position_side = self._get_current_position_side(symbol)
|
||||
|
||||
# Determine if this is entry or exit
|
||||
is_entry = False
|
||||
is_exit = False
|
||||
final_action = raw_action
|
||||
@ -2098,10 +2113,29 @@ class EnhancedTradingOrchestrator:
|
||||
logger.info(f"[{symbol}] SHORT position - SELL signal ignored (already short)")
|
||||
return None
|
||||
|
||||
# Apply appropriate threshold
|
||||
# Apply appropriate threshold with CNN enhancement
|
||||
if is_entry:
|
||||
threshold = self.entry_threshold
|
||||
threshold_type = "ENTRY"
|
||||
|
||||
# For entries, check if CNN predicts favorable pivot
|
||||
if hasattr(self.pivot_rl_trainer, 'williams') and self.pivot_rl_trainer.williams.cnn_model:
|
||||
try:
|
||||
# Get market data for CNN analysis
|
||||
current_price = market_state.prices.get(self.timeframes[0], 0)
|
||||
|
||||
# CNN prediction could lower entry threshold if it predicts favorable pivot
|
||||
# This allows earlier entry before pivot is confirmed
|
||||
cnn_adjustment = self._get_cnn_threshold_adjustment(symbol, raw_action, market_state)
|
||||
adjusted_threshold = max(threshold - cnn_adjustment, threshold * 0.8) # Max 20% reduction
|
||||
|
||||
if cnn_adjustment > 0:
|
||||
logger.info(f"[{symbol}] CNN predicts favorable pivot - adjusted entry threshold: {threshold:.3f} -> {adjusted_threshold:.3f}")
|
||||
threshold = adjusted_threshold
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Error getting CNN threshold adjustment: {e}")
|
||||
|
||||
elif is_exit:
|
||||
threshold = self.exit_threshold
|
||||
threshold_type = "EXIT"
|
||||
@ -2130,7 +2164,8 @@ class EnhancedTradingOrchestrator:
|
||||
'position_before': position_side,
|
||||
'action_type': threshold_type,
|
||||
'threshold_used': threshold,
|
||||
'strict_mode': True,
|
||||
'pivot_enhanced': True,
|
||||
'cnn_integrated': hasattr(self.pivot_rl_trainer, 'williams') and self.pivot_rl_trainer.williams.cnn_model is not None,
|
||||
'timeframe_breakdown': [(tf.timeframe, tf.action, tf.confidence)
|
||||
for tf in best_pred.timeframe_predictions],
|
||||
'market_regime': market_state.market_regime
|
||||
@ -2148,14 +2183,72 @@ class EnhancedTradingOrchestrator:
|
||||
'confidence': confidence
|
||||
}
|
||||
|
||||
logger.info(f"[{symbol}] STRICT {threshold_type} Decision: {final_action} (conf: {confidence:.3f}, threshold: {threshold:.3f})")
|
||||
logger.info(f"[{symbol}] ENHANCED {threshold_type} Decision: {final_action} (conf: {confidence:.3f}, threshold: {threshold:.3f})")
|
||||
|
||||
return action
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error making strict 2-action decision for {symbol}: {e}")
|
||||
logger.error(f"Error making enhanced 2-action decision for {symbol}: {e}")
|
||||
return None
|
||||
|
||||
def _get_cnn_threshold_adjustment(self, symbol: str, action: str, market_state: MarketState) -> float:
|
||||
"""Get threshold adjustment based on CNN pivot predictions"""
|
||||
try:
|
||||
# This would analyze CNN predictions to determine if we should lower entry threshold
|
||||
# For example, if CNN predicts a swing low and we want to BUY, we can be more aggressive
|
||||
|
||||
# Placeholder implementation - in real scenario, this would:
|
||||
# 1. Get recent market data
|
||||
# 2. Run CNN prediction through Williams structure
|
||||
# 3. Check if predicted pivot aligns with our intended action
|
||||
# 4. Return threshold adjustment (0.0 to 0.1 typically)
|
||||
|
||||
# For now, return small adjustment to demonstrate concept
|
||||
if hasattr(self.pivot_rl_trainer.williams, 'cnn_model') and self.pivot_rl_trainer.williams.cnn_model:
|
||||
# CNN is available, could provide small threshold reduction for better entries
|
||||
return 0.05 # 5% threshold reduction when CNN available
|
||||
|
||||
return 0.0
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting CNN threshold adjustment: {e}")
|
||||
return 0.0
|
||||
|
||||
def update_dynamic_thresholds(self):
|
||||
"""Update thresholds based on recent performance"""
|
||||
try:
|
||||
# Update thresholds in pivot trainer
|
||||
self.pivot_rl_trainer.update_thresholds_based_on_performance()
|
||||
|
||||
# Get updated thresholds
|
||||
thresholds = self.pivot_rl_trainer.get_current_thresholds()
|
||||
old_entry = self.entry_threshold
|
||||
old_exit = self.exit_threshold
|
||||
|
||||
self.entry_threshold = thresholds['entry_threshold']
|
||||
self.exit_threshold = thresholds['exit_threshold']
|
||||
self.uninvested_threshold = thresholds['uninvested_threshold']
|
||||
|
||||
# Log changes if significant
|
||||
if abs(old_entry - self.entry_threshold) > 0.01 or abs(old_exit - self.exit_threshold) > 0.01:
|
||||
logger.info(f"Threshold Update - Entry: {old_entry:.3f} -> {self.entry_threshold:.3f}, "
|
||||
f"Exit: {old_exit:.3f} -> {self.exit_threshold:.3f}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating dynamic thresholds: {e}")
|
||||
|
||||
def calculate_enhanced_pivot_reward(self, trade_decision: Dict[str, Any],
|
||||
market_data: pd.DataFrame,
|
||||
trade_outcome: Dict[str, Any]) -> float:
|
||||
"""Calculate reward using the enhanced pivot-based system"""
|
||||
try:
|
||||
return self.pivot_rl_trainer.calculate_pivot_based_reward(
|
||||
trade_decision, market_data, trade_outcome
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating enhanced pivot reward: {e}")
|
||||
return 0.0
|
||||
|
||||
def _update_2_action_position(self, symbol: str, action: TradingAction):
|
||||
"""Update position tracking for strict 2-action system"""
|
||||
try:
|
||||
|
@ -63,3 +63,7 @@ make all dashboard processes run on the server without need of dashboard page to
|
||||
if that does not work I think we can make it simpler and easier to train if we have just 2 model actions buy/sell. we don't need hold signal, as until we have action we hold. And when we are long and we get a sell signal - we close. and enter short on consequtive sell signal. also, we will have different thresholds for entering and exiting. learning to enter when we are more certain
|
||||
this will also help us simplify the training and our codebase to keep it easy to develop.
|
||||
as our models are chained, it does not make sense anymore to train them separately. so remove all modes from main_clean and all referenced code. we use only web mode wherehe flow is: we collect data, calculate indicators and pivot points -> CNN -> RL => orchestrator -> broker/web
|
||||
|
||||
|
||||
# DASH
|
||||
add a row with small charts showing all the data we feed to the models: the 1m 1h 1d and reference (btc) ohlcv on the dashboard
|
320
test_enhanced_pivot_rl_system.py
Normal file
320
test_enhanced_pivot_rl_system.py
Normal file
@ -0,0 +1,320 @@
|
||||
"""
|
||||
Test Enhanced Pivot-Based RL System
|
||||
|
||||
Tests the new system with:
|
||||
- Different thresholds for entry vs exit
|
||||
- Pivot-based rewards
|
||||
- CNN predictions for early pivot detection
|
||||
- Uninvested rewards
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, Any
|
||||
|
||||
# Setup logging
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s [%(levelname)s] %(name)s: %(message)s',
|
||||
stream=sys.stdout
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Add project root to Python path
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from core.data_provider import DataProvider
|
||||
from core.enhanced_orchestrator import EnhancedTradingOrchestrator
|
||||
from training.enhanced_pivot_rl_trainer import EnhancedPivotRLTrainer, create_enhanced_pivot_trainer
|
||||
|
||||
def test_enhanced_pivot_thresholds():
|
||||
"""Test the enhanced pivot-based threshold system"""
|
||||
logger.info("=== Testing Enhanced Pivot-Based Thresholds ===")
|
||||
|
||||
try:
|
||||
# Create components
|
||||
data_provider = DataProvider()
|
||||
orchestrator = EnhancedTradingOrchestrator(
|
||||
data_provider=data_provider,
|
||||
enhanced_rl_training=True
|
||||
)
|
||||
|
||||
# Test threshold initialization
|
||||
thresholds = orchestrator.pivot_rl_trainer.get_current_thresholds()
|
||||
logger.info(f"Initial thresholds:")
|
||||
logger.info(f" Entry: {thresholds['entry_threshold']:.3f}")
|
||||
logger.info(f" Exit: {thresholds['exit_threshold']:.3f}")
|
||||
logger.info(f" Uninvested: {thresholds['uninvested_threshold']:.3f}")
|
||||
|
||||
# Verify entry threshold is higher than exit threshold
|
||||
assert thresholds['entry_threshold'] > thresholds['exit_threshold'], "Entry threshold should be higher than exit"
|
||||
logger.info("✅ Entry threshold correctly higher than exit threshold")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error testing thresholds: {e}")
|
||||
return False
|
||||
|
||||
def test_pivot_reward_calculation():
|
||||
"""Test the pivot-based reward calculation"""
|
||||
logger.info("=== Testing Pivot-Based Reward Calculation ===")
|
||||
|
||||
try:
|
||||
# Create enhanced pivot trainer
|
||||
data_provider = DataProvider()
|
||||
pivot_trainer = create_enhanced_pivot_trainer(data_provider)
|
||||
|
||||
# Create mock trade decision and outcome
|
||||
trade_decision = {
|
||||
'action': 'BUY',
|
||||
'confidence': 0.75,
|
||||
'price': 2500.0,
|
||||
'timestamp': datetime.now()
|
||||
}
|
||||
|
||||
trade_outcome = {
|
||||
'net_pnl': 15.50, # Profitable trade
|
||||
'exit_price': 2515.0,
|
||||
'duration': timedelta(minutes=45)
|
||||
}
|
||||
|
||||
# Create mock market data
|
||||
market_data = pd.DataFrame({
|
||||
'open': np.random.normal(2500, 10, 100),
|
||||
'high': np.random.normal(2510, 10, 100),
|
||||
'low': np.random.normal(2490, 10, 100),
|
||||
'close': np.random.normal(2500, 10, 100),
|
||||
'volume': np.random.normal(1000, 100, 100)
|
||||
})
|
||||
market_data.index = pd.date_range(start=datetime.now() - timedelta(hours=2), periods=100, freq='1min')
|
||||
|
||||
# Calculate reward
|
||||
reward = pivot_trainer.calculate_pivot_based_reward(
|
||||
trade_decision, market_data, trade_outcome
|
||||
)
|
||||
|
||||
logger.info(f"Calculated pivot-based reward: {reward:.3f}")
|
||||
|
||||
# Test should return a reasonable reward for profitable trade
|
||||
assert -15.0 <= reward <= 10.0, f"Reward {reward} outside expected range"
|
||||
logger.info("✅ Pivot-based reward calculation working")
|
||||
|
||||
# Test uninvested reward
|
||||
low_conf_decision = {
|
||||
'action': 'HOLD',
|
||||
'confidence': 0.35, # Below uninvested threshold
|
||||
'price': 2500.0,
|
||||
'timestamp': datetime.now()
|
||||
}
|
||||
|
||||
uninvested_reward = pivot_trainer._calculate_uninvested_rewards(low_conf_decision, 0.35)
|
||||
logger.info(f"Uninvested reward for low confidence: {uninvested_reward:.3f}")
|
||||
|
||||
assert uninvested_reward > 0, "Should get positive reward for staying uninvested with low confidence"
|
||||
logger.info("✅ Uninvested rewards working correctly")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error testing pivot rewards: {e}")
|
||||
return False
|
||||
|
||||
def test_confidence_adjustment():
|
||||
"""Test confidence-based reward adjustments"""
|
||||
logger.info("=== Testing Confidence-Based Adjustments ===")
|
||||
|
||||
try:
|
||||
pivot_trainer = create_enhanced_pivot_trainer()
|
||||
|
||||
# Test overconfidence penalty on loss
|
||||
high_conf_loss = {
|
||||
'action': 'BUY',
|
||||
'confidence': 0.85, # High confidence
|
||||
'price': 2500.0,
|
||||
'timestamp': datetime.now()
|
||||
}
|
||||
|
||||
loss_outcome = {
|
||||
'net_pnl': -25.0, # Loss
|
||||
'exit_price': 2475.0,
|
||||
'duration': timedelta(hours=3)
|
||||
}
|
||||
|
||||
confidence_adjustment = pivot_trainer._calculate_confidence_adjustment(
|
||||
high_conf_loss, loss_outcome
|
||||
)
|
||||
|
||||
logger.info(f"Confidence adjustment for overconfident loss: {confidence_adjustment:.3f}")
|
||||
assert confidence_adjustment < 0, "Should penalize overconfidence on losses"
|
||||
|
||||
# Test underconfidence penalty on win
|
||||
low_conf_win = {
|
||||
'action': 'BUY',
|
||||
'confidence': 0.35, # Low confidence
|
||||
'price': 2500.0,
|
||||
'timestamp': datetime.now()
|
||||
}
|
||||
|
||||
win_outcome = {
|
||||
'net_pnl': 20.0, # Profit
|
||||
'exit_price': 2520.0,
|
||||
'duration': timedelta(minutes=30)
|
||||
}
|
||||
|
||||
confidence_adjustment_2 = pivot_trainer._calculate_confidence_adjustment(
|
||||
low_conf_win, win_outcome
|
||||
)
|
||||
|
||||
logger.info(f"Confidence adjustment for underconfident win: {confidence_adjustment_2:.3f}")
|
||||
# Should be small penalty or zero
|
||||
|
||||
logger.info("✅ Confidence adjustments working correctly")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error testing confidence adjustments: {e}")
|
||||
return False
|
||||
|
||||
def test_dynamic_threshold_updates():
|
||||
"""Test dynamic threshold updating based on performance"""
|
||||
logger.info("=== Testing Dynamic Threshold Updates ===")
|
||||
|
||||
try:
|
||||
pivot_trainer = create_enhanced_pivot_trainer()
|
||||
|
||||
# Get initial thresholds
|
||||
initial_thresholds = pivot_trainer.get_current_thresholds()
|
||||
logger.info(f"Initial thresholds: {initial_thresholds}")
|
||||
|
||||
# Simulate some poor performance (low win rate)
|
||||
for i in range(25):
|
||||
outcome = {
|
||||
'timestamp': datetime.now(),
|
||||
'action': 'BUY',
|
||||
'confidence': 0.6,
|
||||
'net_pnl': -5.0 if i < 20 else 10.0, # 20% win rate
|
||||
'reward': -1.0 if i < 20 else 2.0,
|
||||
'duration': timedelta(hours=2)
|
||||
}
|
||||
pivot_trainer.trade_outcomes.append(outcome)
|
||||
|
||||
# Update thresholds
|
||||
pivot_trainer.update_thresholds_based_on_performance()
|
||||
|
||||
# Get updated thresholds
|
||||
updated_thresholds = pivot_trainer.get_current_thresholds()
|
||||
logger.info(f"Updated thresholds after poor performance: {updated_thresholds}")
|
||||
|
||||
# Entry threshold should increase (more selective) after poor performance
|
||||
assert updated_thresholds['entry_threshold'] >= initial_thresholds['entry_threshold'], \
|
||||
"Entry threshold should increase after poor performance"
|
||||
|
||||
logger.info("✅ Dynamic threshold updates working correctly")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error testing dynamic thresholds: {e}")
|
||||
return False
|
||||
|
||||
def test_cnn_integration():
|
||||
"""Test CNN integration for pivot predictions"""
|
||||
logger.info("=== Testing CNN Integration ===")
|
||||
|
||||
try:
|
||||
data_provider = DataProvider()
|
||||
orchestrator = EnhancedTradingOrchestrator(
|
||||
data_provider=data_provider,
|
||||
enhanced_rl_training=True
|
||||
)
|
||||
|
||||
# Check if Williams structure is initialized with CNN
|
||||
williams = orchestrator.pivot_rl_trainer.williams
|
||||
logger.info(f"Williams CNN enabled: {williams.enable_cnn_feature}")
|
||||
logger.info(f"Williams CNN model available: {williams.cnn_model is not None}")
|
||||
|
||||
# Test CNN threshold adjustment
|
||||
from core.enhanced_orchestrator import MarketState
|
||||
from datetime import datetime
|
||||
|
||||
mock_market_state = MarketState(
|
||||
symbol='ETH/USDT',
|
||||
timestamp=datetime.now(),
|
||||
prices={'1s': 2500.0},
|
||||
features={'1s': np.array([])},
|
||||
volatility=0.02,
|
||||
volume=1000.0,
|
||||
trend_strength=0.5,
|
||||
market_regime='normal',
|
||||
universal_data=None
|
||||
)
|
||||
|
||||
cnn_adjustment = orchestrator._get_cnn_threshold_adjustment(
|
||||
'ETH/USDT', 'BUY', mock_market_state
|
||||
)
|
||||
|
||||
logger.info(f"CNN threshold adjustment: {cnn_adjustment:.3f}")
|
||||
assert 0.0 <= cnn_adjustment <= 0.1, "CNN adjustment should be reasonable"
|
||||
|
||||
logger.info("✅ CNN integration working correctly")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error testing CNN integration: {e}")
|
||||
return False
|
||||
|
||||
def run_all_tests():
|
||||
"""Run all enhanced pivot RL system tests"""
|
||||
logger.info("🚀 Starting Enhanced Pivot RL System Tests")
|
||||
|
||||
tests = [
|
||||
test_enhanced_pivot_thresholds,
|
||||
test_pivot_reward_calculation,
|
||||
test_confidence_adjustment,
|
||||
test_dynamic_threshold_updates,
|
||||
test_cnn_integration
|
||||
]
|
||||
|
||||
passed = 0
|
||||
total = len(tests)
|
||||
|
||||
for test_func in tests:
|
||||
try:
|
||||
if test_func():
|
||||
passed += 1
|
||||
logger.info(f"✅ {test_func.__name__} PASSED")
|
||||
else:
|
||||
logger.error(f"❌ {test_func.__name__} FAILED")
|
||||
except Exception as e:
|
||||
logger.error(f"❌ {test_func.__name__} ERROR: {e}")
|
||||
|
||||
logger.info(f"\n📊 Test Results: {passed}/{total} tests passed")
|
||||
|
||||
if passed == total:
|
||||
logger.info("🎉 All Enhanced Pivot RL System tests PASSED!")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"⚠️ {total - passed} tests FAILED")
|
||||
return False
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = run_all_tests()
|
||||
|
||||
if success:
|
||||
logger.info("\n🔥 Enhanced Pivot RL System is ready for deployment!")
|
||||
logger.info("Key improvements:")
|
||||
logger.info(" ✅ Higher entry threshold than exit threshold")
|
||||
logger.info(" ✅ Pivot-based reward calculation")
|
||||
logger.info(" ✅ CNN predictions for early pivot detection")
|
||||
logger.info(" ✅ Rewards for staying uninvested when uncertain")
|
||||
logger.info(" ✅ Confidence-based reward adjustments")
|
||||
logger.info(" ✅ Dynamic threshold learning from performance")
|
||||
else:
|
||||
logger.error("\n❌ Enhanced Pivot RL System has issues that need fixing")
|
||||
|
||||
sys.exit(0 if success else 1)
|
584
training/enhanced_pivot_rl_trainer.py
Normal file
584
training/enhanced_pivot_rl_trainer.py
Normal file
@ -0,0 +1,584 @@
|
||||
"""
|
||||
Enhanced Pivot-Based RL Trainer
|
||||
|
||||
Integrates Williams Market Structure pivot points with CNN predictions
|
||||
for improved trading decisions and training rewards.
|
||||
|
||||
Key Features:
|
||||
- Train RL model to buy/sell at local pivot points
|
||||
- CNN predicts next pivot to avoid late signals
|
||||
- Different thresholds for entry vs exit
|
||||
- Rewards for staying uninvested when uncertain
|
||||
- Uncertainty-based confidence adjustment
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import time
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from collections import deque, namedtuple
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, List, Optional, Tuple, Any, Union, TYPE_CHECKING
|
||||
import matplotlib.pyplot as plt
|
||||
from pathlib import Path
|
||||
|
||||
from core.config import get_config
|
||||
from core.data_provider import DataProvider
|
||||
from training.williams_market_structure import WilliamsMarketStructure, SwingType, SwingPoint
|
||||
|
||||
# Use TYPE_CHECKING to avoid circular import
|
||||
if TYPE_CHECKING:
|
||||
from core.enhanced_orchestrator import EnhancedTradingOrchestrator
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class PivotReward:
|
||||
"""Reward structure for pivot-based trading decisions"""
|
||||
|
||||
def __init__(self):
|
||||
# Pivot-based reward weights
|
||||
self.pivot_hit_bonus = 2.0 # Bonus for trading at actual pivot points
|
||||
self.pivot_anticipation_bonus = 1.5 # Bonus for trading before pivot (CNN prediction)
|
||||
self.wrong_direction_penalty = -1.0 # Penalty for trading opposite to pivot direction
|
||||
self.late_entry_penalty = -0.5 # Penalty for entering after pivot is confirmed
|
||||
|
||||
# Stay uninvested rewards
|
||||
self.uninvested_reward = 0.1 # Small positive reward for staying out of poor setups
|
||||
self.avoid_false_signal_bonus = 0.5 # Bonus for avoiding false signals
|
||||
|
||||
# Uncertainty penalties
|
||||
self.overconfidence_penalty = -0.3 # Penalty for being overconfident on losses
|
||||
self.underconfidence_penalty = -0.1 # Small penalty for being underconfident on wins
|
||||
|
||||
class EnhancedPivotRLTrainer:
|
||||
"""Enhanced RL trainer focused on Williams pivot points and CNN predictions"""
|
||||
|
||||
def __init__(self,
|
||||
data_provider: DataProvider = None,
|
||||
orchestrator: Optional["EnhancedTradingOrchestrator"] = None):
|
||||
|
||||
self.config = get_config()
|
||||
self.data_provider = data_provider or DataProvider()
|
||||
self.orchestrator = orchestrator
|
||||
|
||||
# Initialize Williams Market Structure with CNN
|
||||
self.williams = WilliamsMarketStructure(
|
||||
swing_strengths=[2, 4, 6, 8, 10], # Multiple strengths for better detection
|
||||
enable_cnn_feature=True,
|
||||
training_data_provider=data_provider
|
||||
)
|
||||
|
||||
# Pivot tracking
|
||||
self.recent_pivots = deque(maxlen=50)
|
||||
self.pivot_predictions = deque(maxlen=20)
|
||||
self.trade_outcomes = deque(maxlen=100)
|
||||
|
||||
# Threshold management - different for entry vs exit
|
||||
self.entry_threshold = 0.65 # Higher threshold for entering positions
|
||||
self.exit_threshold = 0.35 # Lower threshold for exiting positions
|
||||
self.max_uninvested_reward_threshold = 0.60 # Stay out if confidence below this
|
||||
|
||||
# Confidence learning parameters
|
||||
self.confidence_history = deque(maxlen=200)
|
||||
self.mistake_severity_tracker = deque(maxlen=50)
|
||||
|
||||
# Reward calculator
|
||||
self.pivot_reward = PivotReward()
|
||||
|
||||
logger.info("Enhanced Pivot RL Trainer initialized")
|
||||
logger.info(f"Entry threshold: {self.entry_threshold:.2%}")
|
||||
logger.info(f"Exit threshold: {self.exit_threshold:.2%}")
|
||||
logger.info(f"Uninvested reward threshold: {self.max_uninvested_reward_threshold:.2%}")
|
||||
|
||||
def calculate_pivot_based_reward(self,
|
||||
trade_decision: Dict[str, Any],
|
||||
market_data: pd.DataFrame,
|
||||
trade_outcome: Dict[str, Any]) -> float:
|
||||
"""
|
||||
Calculate enhanced reward based on pivot points and CNN predictions
|
||||
|
||||
Args:
|
||||
trade_decision: The trading decision made by the model
|
||||
market_data: Market data context
|
||||
trade_outcome: Actual trade outcome
|
||||
|
||||
Returns:
|
||||
Enhanced reward score
|
||||
"""
|
||||
try:
|
||||
base_pnl = trade_outcome.get('net_pnl', 0.0)
|
||||
confidence = trade_decision.get('confidence', 0.5)
|
||||
action = trade_decision.get('action', 'HOLD')
|
||||
entry_price = trade_decision.get('price', 0.0)
|
||||
exit_price = trade_outcome.get('exit_price', entry_price)
|
||||
duration = trade_outcome.get('duration', timedelta(0))
|
||||
|
||||
# Base PnL reward
|
||||
base_reward = base_pnl / 5.0
|
||||
|
||||
# 1. Pivot Point Analysis Rewards
|
||||
pivot_reward = self._calculate_pivot_rewards(
|
||||
trade_decision, market_data, trade_outcome
|
||||
)
|
||||
|
||||
# 2. CNN Prediction Accuracy Rewards
|
||||
cnn_reward = self._calculate_cnn_prediction_rewards(
|
||||
trade_decision, market_data, trade_outcome
|
||||
)
|
||||
|
||||
# 3. Uninvested Period Rewards
|
||||
uninvested_reward = self._calculate_uninvested_rewards(
|
||||
trade_decision, confidence
|
||||
)
|
||||
|
||||
# 4. Uncertainty-based Confidence Adjustment
|
||||
confidence_adjustment = self._calculate_confidence_adjustment(
|
||||
trade_decision, trade_outcome
|
||||
)
|
||||
|
||||
# 5. Time efficiency with pivot context
|
||||
time_reward = self._calculate_time_efficiency_reward(
|
||||
duration, base_pnl, market_data
|
||||
)
|
||||
|
||||
# Combine all rewards
|
||||
total_reward = (
|
||||
base_reward +
|
||||
pivot_reward +
|
||||
cnn_reward +
|
||||
uninvested_reward +
|
||||
confidence_adjustment +
|
||||
time_reward
|
||||
)
|
||||
|
||||
# Log detailed reward breakdown
|
||||
self._log_reward_breakdown(
|
||||
trade_decision, trade_outcome, {
|
||||
'base': base_reward,
|
||||
'pivot': pivot_reward,
|
||||
'cnn': cnn_reward,
|
||||
'uninvested': uninvested_reward,
|
||||
'confidence': confidence_adjustment,
|
||||
'time': time_reward,
|
||||
'total': total_reward
|
||||
}
|
||||
)
|
||||
|
||||
# Track for learning
|
||||
self._track_reward_outcome(trade_decision, trade_outcome, total_reward)
|
||||
|
||||
return np.clip(total_reward, -15.0, 10.0)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating pivot-based reward: {e}")
|
||||
return 0.0
|
||||
|
||||
def _calculate_pivot_rewards(self,
|
||||
trade_decision: Dict[str, Any],
|
||||
market_data: pd.DataFrame,
|
||||
trade_outcome: Dict[str, Any]) -> float:
|
||||
"""Calculate rewards based on proximity to pivot points"""
|
||||
try:
|
||||
entry_price = trade_decision.get('price', 0.0)
|
||||
action = trade_decision.get('action', 'HOLD')
|
||||
entry_time = trade_decision.get('timestamp', datetime.now())
|
||||
net_pnl = trade_outcome.get('net_pnl', 0.0)
|
||||
|
||||
# Find recent pivot points from Williams analysis
|
||||
ohlcv_array = self._convert_dataframe_to_ohlcv_array(market_data)
|
||||
if ohlcv_array is None or len(ohlcv_array) < 20:
|
||||
return 0.0
|
||||
|
||||
# Get pivot points from Williams structure
|
||||
structure_levels = self.williams.calculate_recursive_pivot_points(ohlcv_array)
|
||||
if not structure_levels or 'level_0' not in structure_levels:
|
||||
return 0.0
|
||||
|
||||
level_0_pivots = structure_levels['level_0'].swing_points
|
||||
if not level_0_pivots:
|
||||
return 0.0
|
||||
|
||||
# Find closest pivot to entry
|
||||
closest_pivot = self._find_closest_pivot(entry_price, entry_time, level_0_pivots)
|
||||
if not closest_pivot:
|
||||
return 0.0
|
||||
|
||||
# Calculate distance to pivot (price and time)
|
||||
price_distance = abs(entry_price - closest_pivot.price) / closest_pivot.price
|
||||
time_distance = abs((entry_time - closest_pivot.timestamp).total_seconds()) / 3600.0 # hours
|
||||
|
||||
pivot_reward = 0.0
|
||||
|
||||
# Reward trading at or near pivot points
|
||||
if price_distance < 0.005: # Within 0.5% of pivot
|
||||
if time_distance < 0.5: # Within 30 minutes
|
||||
pivot_reward += self.pivot_reward.pivot_hit_bonus
|
||||
logger.debug(f"PIVOT HIT BONUS: {self.pivot_reward.pivot_hit_bonus:.2f}")
|
||||
|
||||
# Check if trade direction aligns with pivot
|
||||
if self._trade_aligns_with_pivot(action, closest_pivot, net_pnl):
|
||||
pivot_reward += self.pivot_reward.pivot_anticipation_bonus
|
||||
logger.debug(f"PIVOT DIRECTION BONUS: {self.pivot_reward.pivot_anticipation_bonus:.2f}")
|
||||
else:
|
||||
pivot_reward += self.pivot_reward.wrong_direction_penalty
|
||||
logger.debug(f"WRONG DIRECTION PENALTY: {self.pivot_reward.wrong_direction_penalty:.2f}")
|
||||
|
||||
# Penalty for late entry after pivot confirmation
|
||||
if time_distance > 2.0: # More than 2 hours after pivot
|
||||
pivot_reward += self.pivot_reward.late_entry_penalty
|
||||
logger.debug(f"LATE ENTRY PENALTY: {self.pivot_reward.late_entry_penalty:.2f}")
|
||||
|
||||
return pivot_reward
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating pivot rewards: {e}")
|
||||
return 0.0
|
||||
|
||||
def _calculate_cnn_prediction_rewards(self,
|
||||
trade_decision: Dict[str, Any],
|
||||
market_data: pd.DataFrame,
|
||||
trade_outcome: Dict[str, Any]) -> float:
|
||||
"""Calculate rewards based on CNN pivot predictions"""
|
||||
try:
|
||||
# Check if we have CNN predictions available
|
||||
if not hasattr(self.williams, 'cnn_model') or not self.williams.cnn_model:
|
||||
return 0.0
|
||||
|
||||
action = trade_decision.get('action', 'HOLD')
|
||||
confidence = trade_decision.get('confidence', 0.5)
|
||||
net_pnl = trade_outcome.get('net_pnl', 0.0)
|
||||
|
||||
# Get latest CNN prediction if available
|
||||
# This would be the prediction made before the trade
|
||||
cnn_prediction = self._get_latest_cnn_prediction()
|
||||
if not cnn_prediction:
|
||||
return 0.0
|
||||
|
||||
cnn_reward = 0.0
|
||||
|
||||
# Reward for following CNN predictions that turn out correct
|
||||
predicted_direction = self._interpret_cnn_prediction(cnn_prediction)
|
||||
|
||||
if predicted_direction == action and net_pnl > 0:
|
||||
# CNN prediction was correct and we followed it
|
||||
cnn_reward += 1.0 * confidence # Scale by confidence
|
||||
logger.debug(f"CNN CORRECT FOLLOW: +{1.0 * confidence:.2f}")
|
||||
|
||||
elif predicted_direction != action and net_pnl < 0:
|
||||
# We didn't follow CNN and it was right (we were wrong)
|
||||
cnn_reward -= 0.5
|
||||
logger.debug(f"CNN IGNORE PENALTY: -0.5")
|
||||
|
||||
elif predicted_direction == action and net_pnl < 0:
|
||||
# We followed CNN but it was wrong
|
||||
cnn_reward -= 0.2 # Small penalty, CNN predictions can be wrong
|
||||
logger.debug(f"CNN WRONG FOLLOW: -0.2")
|
||||
|
||||
return cnn_reward
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating CNN prediction rewards: {e}")
|
||||
return 0.0
|
||||
|
||||
def _calculate_uninvested_rewards(self,
|
||||
trade_decision: Dict[str, Any],
|
||||
confidence: float) -> float:
|
||||
"""Calculate rewards for staying uninvested when uncertain"""
|
||||
try:
|
||||
action = trade_decision.get('action', 'HOLD')
|
||||
|
||||
# Reward staying out when confidence is low
|
||||
if action == 'HOLD' and confidence < self.max_uninvested_reward_threshold:
|
||||
uninvested_reward = self.pivot_reward.uninvested_reward
|
||||
|
||||
# Bonus for avoiding very uncertain setups
|
||||
if confidence < 0.4:
|
||||
uninvested_reward += self.pivot_reward.avoid_false_signal_bonus
|
||||
logger.debug(f"AVOID FALSE SIGNAL BONUS: +{self.pivot_reward.avoid_false_signal_bonus:.2f}")
|
||||
|
||||
logger.debug(f"UNINVESTED REWARD: +{uninvested_reward:.2f}")
|
||||
return uninvested_reward
|
||||
|
||||
return 0.0
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating uninvested rewards: {e}")
|
||||
return 0.0
|
||||
|
||||
def _calculate_confidence_adjustment(self,
|
||||
trade_decision: Dict[str, Any],
|
||||
trade_outcome: Dict[str, Any]) -> float:
|
||||
"""Adjust rewards based on confidence vs outcome to reduce overconfidence"""
|
||||
try:
|
||||
confidence = trade_decision.get('confidence', 0.5)
|
||||
net_pnl = trade_outcome.get('net_pnl', 0.0)
|
||||
|
||||
confidence_adjustment = 0.0
|
||||
|
||||
# Track mistake severity
|
||||
mistake_severity = abs(net_pnl) if net_pnl < 0 else 0.0
|
||||
self.mistake_severity_tracker.append(mistake_severity)
|
||||
|
||||
# Penalize overconfidence on losses
|
||||
if net_pnl < 0 and confidence > 0.7:
|
||||
# High confidence but loss - penalize overconfidence
|
||||
overconfidence_factor = (confidence - 0.7) / 0.3 # 0-1 scale
|
||||
severity_factor = min(mistake_severity / 2.0, 1.0) # Scale by loss size
|
||||
|
||||
penalty = self.pivot_reward.overconfidence_penalty * overconfidence_factor * severity_factor
|
||||
confidence_adjustment += penalty
|
||||
|
||||
logger.debug(f"OVERCONFIDENCE PENALTY: {penalty:.2f} (conf: {confidence:.2f}, loss: ${net_pnl:.2f})")
|
||||
|
||||
# Small penalty for underconfidence on wins
|
||||
elif net_pnl > 0 and confidence < 0.4:
|
||||
underconfidence_factor = (0.4 - confidence) / 0.4 # 0-1 scale
|
||||
penalty = self.pivot_reward.underconfidence_penalty * underconfidence_factor
|
||||
confidence_adjustment += penalty
|
||||
|
||||
logger.debug(f"UNDERCONFIDENCE PENALTY: {penalty:.2f} (conf: {confidence:.2f}, profit: ${net_pnl:.2f})")
|
||||
|
||||
# Update confidence learning
|
||||
self._update_confidence_learning(confidence, net_pnl, mistake_severity)
|
||||
|
||||
return confidence_adjustment
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating confidence adjustment: {e}")
|
||||
return 0.0
|
||||
|
||||
def _calculate_time_efficiency_reward(self,
|
||||
duration: timedelta,
|
||||
net_pnl: float,
|
||||
market_data: pd.DataFrame) -> float:
|
||||
"""Calculate time-based rewards considering market context"""
|
||||
try:
|
||||
duration_hours = duration.total_seconds() / 3600.0
|
||||
|
||||
# Quick profitable trades get bonus
|
||||
if net_pnl > 0 and duration_hours < 0.5: # Less than 30 minutes
|
||||
return 0.3
|
||||
|
||||
# Holding losses too long gets penalty
|
||||
elif net_pnl < 0 and duration_hours > 2.0: # More than 2 hours
|
||||
return -0.5
|
||||
|
||||
return 0.0
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating time efficiency reward: {e}")
|
||||
return 0.0
|
||||
|
||||
def update_thresholds_based_on_performance(self):
|
||||
"""Dynamically adjust entry/exit thresholds based on recent performance"""
|
||||
try:
|
||||
if len(self.trade_outcomes) < 20:
|
||||
return
|
||||
|
||||
recent_outcomes = list(self.trade_outcomes)[-20:]
|
||||
|
||||
# Calculate win rate and average PnL
|
||||
wins = sum(1 for outcome in recent_outcomes if outcome['net_pnl'] > 0)
|
||||
win_rate = wins / len(recent_outcomes)
|
||||
avg_pnl = np.mean([outcome['net_pnl'] for outcome in recent_outcomes])
|
||||
|
||||
# Adjust thresholds based on performance
|
||||
if win_rate < 0.4: # Low win rate - be more selective
|
||||
self.entry_threshold = min(self.entry_threshold + 0.02, 0.80)
|
||||
logger.info(f"Low win rate ({win_rate:.2%}) - increased entry threshold to {self.entry_threshold:.2%}")
|
||||
|
||||
elif win_rate > 0.6 and avg_pnl > 0: # High win rate - can be more aggressive
|
||||
self.entry_threshold = max(self.entry_threshold - 0.01, 0.50)
|
||||
logger.info(f"High win rate ({win_rate:.2%}) - decreased entry threshold to {self.entry_threshold:.2%}")
|
||||
|
||||
# Adjust exit threshold based on loss severity
|
||||
avg_loss_severity = np.mean(list(self.mistake_severity_tracker)) if self.mistake_severity_tracker else 0
|
||||
|
||||
if avg_loss_severity > 1.0: # Large average losses
|
||||
self.exit_threshold = max(self.exit_threshold - 0.01, 0.20)
|
||||
logger.info(f"High loss severity - decreased exit threshold to {self.exit_threshold:.2%}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating thresholds: {e}")
|
||||
|
||||
def get_current_thresholds(self) -> Dict[str, float]:
|
||||
"""Get current entry and exit thresholds"""
|
||||
return {
|
||||
'entry_threshold': self.entry_threshold,
|
||||
'exit_threshold': self.exit_threshold,
|
||||
'uninvested_threshold': self.max_uninvested_reward_threshold
|
||||
}
|
||||
|
||||
# Helper methods
|
||||
|
||||
def _convert_dataframe_to_ohlcv_array(self, df: pd.DataFrame) -> Optional[np.ndarray]:
|
||||
"""Convert pandas DataFrame to numpy array for Williams analysis"""
|
||||
try:
|
||||
if df.empty:
|
||||
return None
|
||||
|
||||
# Ensure we have required columns
|
||||
required_cols = ['open', 'high', 'low', 'close', 'volume']
|
||||
if not all(col in df.columns for col in required_cols):
|
||||
return None
|
||||
|
||||
# Convert to numpy array
|
||||
timestamps = df.index.astype(np.int64) // 10**9 # Convert to Unix timestamp
|
||||
ohlcv_array = np.column_stack([
|
||||
timestamps,
|
||||
df['open'].values,
|
||||
df['high'].values,
|
||||
df['low'].values,
|
||||
df['close'].values,
|
||||
df['volume'].values
|
||||
])
|
||||
|
||||
return ohlcv_array.astype(np.float64)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error converting DataFrame to OHLCV array: {e}")
|
||||
return None
|
||||
|
||||
def _find_closest_pivot(self,
|
||||
entry_price: float,
|
||||
entry_time: datetime,
|
||||
pivots: List[SwingPoint]) -> Optional[SwingPoint]:
|
||||
"""Find the closest pivot point to the trade entry"""
|
||||
try:
|
||||
if not pivots:
|
||||
return None
|
||||
|
||||
# Find pivot closest in time and price
|
||||
best_pivot = None
|
||||
best_score = float('inf')
|
||||
|
||||
for pivot in pivots:
|
||||
time_diff = abs((entry_time - pivot.timestamp).total_seconds()) / 3600.0
|
||||
price_diff = abs(entry_price - pivot.price) / pivot.price
|
||||
|
||||
# Combined score (weighted by time and price proximity)
|
||||
score = time_diff * 0.3 + price_diff * 100 # Weight price difference more heavily
|
||||
|
||||
if score < best_score:
|
||||
best_score = score
|
||||
best_pivot = pivot
|
||||
|
||||
return best_pivot
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error finding closest pivot: {e}")
|
||||
return None
|
||||
|
||||
def _trade_aligns_with_pivot(self,
|
||||
action: str,
|
||||
pivot: SwingPoint,
|
||||
net_pnl: float) -> bool:
|
||||
"""Check if trade direction aligns with pivot type and was profitable"""
|
||||
try:
|
||||
if net_pnl <= 0: # Only consider profitable trades as aligned
|
||||
return False
|
||||
|
||||
if action == 'BUY' and pivot.swing_type == SwingType.SWING_LOW:
|
||||
return True # Bought at/near swing low
|
||||
elif action == 'SELL' and pivot.swing_type == SwingType.SWING_HIGH:
|
||||
return True # Sold at/near swing high
|
||||
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking trade alignment: {e}")
|
||||
return False
|
||||
|
||||
def _get_latest_cnn_prediction(self) -> Optional[np.ndarray]:
|
||||
"""Get the latest CNN prediction from Williams structure"""
|
||||
try:
|
||||
# This would access the Williams CNN model's latest prediction
|
||||
# For now, return None if not available
|
||||
if hasattr(self.williams, 'latest_cnn_prediction'):
|
||||
return self.williams.latest_cnn_prediction
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error getting CNN prediction: {e}")
|
||||
return None
|
||||
|
||||
def _interpret_cnn_prediction(self, prediction: np.ndarray) -> str:
|
||||
"""Interpret CNN prediction array to trading action"""
|
||||
try:
|
||||
if len(prediction) < 2:
|
||||
return 'HOLD'
|
||||
|
||||
# Assuming prediction format: [type, price] for level 0
|
||||
predicted_type = prediction[0] # 0 = LOW, 1 = HIGH
|
||||
|
||||
if predicted_type > 0.5:
|
||||
return 'SELL' # Expecting swing high - sell
|
||||
else:
|
||||
return 'BUY' # Expecting swing low - buy
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error interpreting CNN prediction: {e}")
|
||||
return 'HOLD'
|
||||
|
||||
def _update_confidence_learning(self,
|
||||
confidence: float,
|
||||
net_pnl: float,
|
||||
mistake_severity: float):
|
||||
"""Update confidence learning parameters"""
|
||||
try:
|
||||
self.confidence_history.append({
|
||||
'confidence': confidence,
|
||||
'net_pnl': net_pnl,
|
||||
'mistake_severity': mistake_severity,
|
||||
'timestamp': datetime.now()
|
||||
})
|
||||
|
||||
# Periodically update thresholds based on confidence patterns
|
||||
if len(self.confidence_history) % 10 == 0:
|
||||
self.update_thresholds_based_on_performance()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating confidence learning: {e}")
|
||||
|
||||
def _track_reward_outcome(self,
|
||||
trade_decision: Dict[str, Any],
|
||||
trade_outcome: Dict[str, Any],
|
||||
total_reward: float):
|
||||
"""Track reward outcomes for analysis"""
|
||||
try:
|
||||
outcome_record = {
|
||||
'timestamp': datetime.now(),
|
||||
'action': trade_decision.get('action'),
|
||||
'confidence': trade_decision.get('confidence'),
|
||||
'net_pnl': trade_outcome.get('net_pnl'),
|
||||
'reward': total_reward,
|
||||
'duration': trade_outcome.get('duration')
|
||||
}
|
||||
|
||||
self.trade_outcomes.append(outcome_record)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error tracking reward outcome: {e}")
|
||||
|
||||
def _log_reward_breakdown(self,
|
||||
trade_decision: Dict[str, Any],
|
||||
trade_outcome: Dict[str, Any],
|
||||
rewards: Dict[str, float]):
|
||||
"""Log detailed reward breakdown"""
|
||||
try:
|
||||
action = trade_decision.get('action', 'UNKNOWN')
|
||||
confidence = trade_decision.get('confidence', 0.0)
|
||||
net_pnl = trade_outcome.get('net_pnl', 0.0)
|
||||
|
||||
logger.info(f"[REWARD] {action} (conf: {confidence:.2%}) PnL: ${net_pnl:.2f} -> Total: {rewards['total']:.2f}")
|
||||
logger.debug(f" Base: {rewards['base']:.2f}, Pivot: {rewards['pivot']:.2f}, CNN: {rewards['cnn']:.2f}")
|
||||
logger.debug(f" Uninvested: {rewards['uninvested']:.2f}, Confidence: {rewards['confidence']:.2f}, Time: {rewards['time']:.2f}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error logging reward breakdown: {e}")
|
||||
|
||||
def create_enhanced_pivot_trainer(data_provider: DataProvider = None,
|
||||
orchestrator: Optional["EnhancedTradingOrchestrator"] = None) -> EnhancedPivotRLTrainer:
|
||||
"""Factory function to create enhanced pivot trainer"""
|
||||
return EnhancedPivotRLTrainer(data_provider, orchestrator)
|
@ -55,6 +55,7 @@ try:
|
||||
from core.enhanced_orchestrator import EnhancedTradingOrchestrator
|
||||
from core.universal_data_adapter import UniversalDataAdapter
|
||||
from core.unified_data_stream import UnifiedDataStream, TrainingDataPacket, UIDataPacket
|
||||
from training.enhanced_pivot_rl_trainer import EnhancedPivotRLTrainer, create_enhanced_pivot_trainer
|
||||
ENHANCED_RL_AVAILABLE = True
|
||||
logger.info("Enhanced RL training components available")
|
||||
except ImportError as e:
|
||||
@ -361,6 +362,20 @@ class TradingDashboard:
|
||||
self.williams_structure = None
|
||||
logger.warning("Williams Market Structure not available")
|
||||
|
||||
# Initialize Enhanced Pivot RL Trainer for better position management
|
||||
try:
|
||||
self.pivot_rl_trainer = create_enhanced_pivot_trainer(
|
||||
data_provider=self.data_provider,
|
||||
orchestrator=self.orchestrator
|
||||
)
|
||||
logger.info("Enhanced Pivot RL Trainer initialized for better entry/exit decisions")
|
||||
logger.info(f"Entry threshold: {self.pivot_rl_trainer.get_current_thresholds()['entry_threshold']:.1%}")
|
||||
logger.info(f"Exit threshold: {self.pivot_rl_trainer.get_current_thresholds()['exit_threshold']:.1%}")
|
||||
logger.info(f"Uninvested threshold: {self.pivot_rl_trainer.get_current_thresholds()['uninvested_threshold']:.1%}")
|
||||
except Exception as e:
|
||||
self.pivot_rl_trainer = None
|
||||
logger.warning(f"Enhanced Pivot RL Trainer not available: {e}")
|
||||
|
||||
def _to_local_timezone(self, dt: datetime) -> datetime:
|
||||
"""Convert datetime to configured local timezone"""
|
||||
try:
|
||||
@ -4358,7 +4373,47 @@ class TradingDashboard:
|
||||
return False
|
||||
|
||||
def _calculate_rl_reward(self, closed_trade):
|
||||
"""Calculate enhanced reward for RL training with proper penalties for losing trades"""
|
||||
"""Calculate enhanced reward for RL training using pivot-based system"""
|
||||
try:
|
||||
# Extract trade information
|
||||
trade_decision = {
|
||||
'action': closed_trade.get('side', 'HOLD'),
|
||||
'confidence': closed_trade.get('confidence', 0.5),
|
||||
'price': closed_trade.get('entry_price', 0.0),
|
||||
'timestamp': closed_trade.get('entry_time', datetime.now())
|
||||
}
|
||||
|
||||
trade_outcome = {
|
||||
'net_pnl': closed_trade.get('net_pnl', 0),
|
||||
'exit_price': closed_trade.get('exit_price', 0.0),
|
||||
'duration': closed_trade.get('duration', timedelta(0))
|
||||
}
|
||||
|
||||
# Get market data context for pivot analysis
|
||||
symbol = closed_trade.get('symbol', 'ETH/USDT')
|
||||
trade_time = trade_decision['timestamp']
|
||||
market_data = self._get_training_context_data(symbol, trade_time, lookback_minutes=120)
|
||||
|
||||
# Use enhanced pivot-based reward if orchestrator is available
|
||||
if hasattr(self, 'orchestrator') and self.orchestrator and hasattr(self.orchestrator, 'calculate_enhanced_pivot_reward'):
|
||||
enhanced_reward = self.orchestrator.calculate_enhanced_pivot_reward(
|
||||
trade_decision, market_data, trade_outcome
|
||||
)
|
||||
|
||||
# Log the enhanced reward
|
||||
logger.info(f"[ENHANCED_REWARD] Using pivot-based reward: {enhanced_reward:.3f}")
|
||||
return enhanced_reward
|
||||
|
||||
# Fallback to original reward calculation if enhanced system not available
|
||||
logger.warning("[ENHANCED_REWARD] Falling back to original reward calculation")
|
||||
return self._calculate_original_rl_reward(closed_trade)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error calculating enhanced RL reward: {e}")
|
||||
return self._calculate_original_rl_reward(closed_trade)
|
||||
|
||||
def _calculate_original_rl_reward(self, closed_trade):
|
||||
"""Original RL reward calculation as fallback"""
|
||||
try:
|
||||
net_pnl = closed_trade.get('net_pnl', 0)
|
||||
duration = closed_trade.get('duration', timedelta(0))
|
||||
@ -4419,7 +4474,7 @@ class TradingDashboard:
|
||||
return reward
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Error calculating RL reward: {e}")
|
||||
logger.warning(f"Error calculating original RL reward: {e}")
|
||||
return 0.0
|
||||
|
||||
def _execute_rl_training_step(self, training_episode):
|
||||
|
Loading…
x
Reference in New Issue
Block a user