From e4bdb3f6a0c48379c4ef3da5b3f736b632ed0d8a Mon Sep 17 00:00:00 2001 From: stevekeyharvey Date: Thu, 7 May 2026 20:59:47 +0000 Subject: [PATCH] update switching fr logic for expected alpha --- algo.ipynb | 23 +++++----- algo_config.json | 2 +- engine_best_funding_rate.py | 4 +- main.py | 61 +++++++++++++------------- ng.py | 75 ++++++++++++++++++++++++++------ nicegui_modules/static/script.js | 17 +++++++- 6 files changed, 122 insertions(+), 60 deletions(-) diff --git a/algo.ipynb b/algo.ipynb index e4bd3e9..cbd4882 100644 --- a/algo.ipynb +++ b/algo.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 7, + "execution_count": 1, "id": "d1eed397", "metadata": {}, "outputs": [], @@ -22,7 +22,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 2, "id": "c6151613", "metadata": {}, "outputs": [], @@ -32,7 +32,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "id": "d83c61e5", "metadata": {}, "outputs": [ @@ -48,6 +48,7 @@ } ], "source": [ + "\n", "config_update = {\n", " # 'Config': {\n", " # 'Price_Worsener_Aster': 0,\n", @@ -57,15 +58,15 @@ " # },\n", " 'Logging': {\n", " 'Log_Summary_Each_Loop': False,\n", - " 'Print_Summary_Each_Loop': True,\n", - " },\n", - " 'Overrides': {\n", - " 'Allow_Ordering_Aster': True,\n", - " 'Allow_Ordering_Extend': True,\n", - " 'Allow_Symbol_Change': True,\n", - " # 'Flatten_Open_Positions': False,\n", - " # 'Flatten_Open_Positions_Opportunistic': False,\n", + " 'Print_Summary_Each_Loop': False,\n", " },\n", + " # 'Overrides': {\n", + " # 'Allow_Ordering_Aster': True,\n", + " # 'Allow_Ordering_Extend': True,\n", + " # 'Allow_Symbol_Change': True,\n", + " # # 'Flatten_Open_Positions': False,\n", + " # # 'Flatten_Open_Positions_Opportunistic': False,\n", + " # },\n", "}\n", "VAL_KEY.publish('fr_orchestrator_input', json.dumps(config_update))" ] diff --git a/algo_config.json b/algo_config.json index d967c42..2bd19e9 100644 --- a/algo_config.json +++ b/algo_config.json @@ -1,5 +1,5 @@ { - "Updated_Timestamp": 1778133411421, + "Updated_Timestamp": 1778178429902, "Config": { "Loop_Sleep_Sec": 0.0, "Max_Order_Over_Notional_Ratio": 1.05, diff --git a/engine_best_funding_rate.py b/engine_best_funding_rate.py index 0401888..630ed15 100644 --- a/engine_best_funding_rate.py +++ b/engine_best_funding_rate.py @@ -204,8 +204,8 @@ async def loop() -> None: df_best_fr_rate['last_trade_ts_dt_ast'] = pd.to_datetime(df_best_fr_rate['last_trade_ts_ast'], unit='ms') df_best_fr_rate['last_trade_ts_dt_ext'] = pd.to_datetime(df_best_fr_rate['last_trade_ts_ext'], unit='ms') - df_best_fr_rate = df_best_fr_rate.loc[( (datetime.now().timestamp()*1000 )-df_best_fr_rate['last_trade_ts_ast']) < (3*60*1000) ] - df_best_fr_rate = df_best_fr_rate.loc[( (datetime.now().timestamp()*1000 )-df_best_fr_rate['last_trade_ts_ext']) < (15*60*1000) ] + df_best_fr_rate = df_best_fr_rate.loc[( (datetime.now().timestamp()*1000 )-df_best_fr_rate['last_trade_ts_ast']) < (5*60*1000) ] # Last traded in 3min + # df_best_fr_rate = df_best_fr_rate.loc[( (datetime.now().timestamp()*1000 )-df_best_fr_rate['last_trade_ts_ext']) < (15*60*1000) ] # Last traded in 15min # print(df_best_fr_rate.columns) # print(df_best_fr_rate.iloc[0]) diff --git a/main.py b/main.py index d8d54b5..2cd0851 100644 --- a/main.py +++ b/main.py @@ -249,33 +249,34 @@ def signal_alpha_over_taker( alpha_hurdle_adj: Decimal = Decimal('0.00'), ) -> Signal: + + if funding_rate_exch == 'ASTER': + if funding_rate_side == 'BUY': + aster_buy_fund_rate_return = abs(funding_rate) + extend_buy_fund_rate_return = abs(funding_rate) * -1 + else: + aster_buy_fund_rate_return = abs(funding_rate) * -1 + extend_buy_fund_rate_return = abs(funding_rate) + else: # funding_rate_exch == 'EXTEND': + if funding_rate_side == 'BUY': + aster_buy_fund_rate_return = abs(funding_rate) * -1 + extend_buy_fund_rate_return = abs(funding_rate) + else: + aster_buy_fund_rate_return = abs(funding_rate) + extend_buy_fund_rate_return = abs(funding_rate) * -1 + + if Config.Overrides.Flatten_Open_Positions_Opportunistic: if Decimal(str(Aster.notional_position)) > 0: - aster_buy_fund_rate_return = abs(funding_rate_switch) * -1 - extend_buy_fund_rate_return = abs(funding_rate_switch) + aster_buy_fund_rate_return = aster_buy_fund_rate_return + extend_buy_fund_rate_return = max([extend_buy_fund_rate_return, abs(funding_rate_switch)]) # funding_rate_exch = 'EXTEND' # funding_rate_side = 'BUY' else: # Decimal(str(Aster.notional_position)) < 0: - aster_buy_fund_rate_return = abs(funding_rate_switch) - extend_buy_fund_rate_return = abs(funding_rate_switch) * -1 + aster_buy_fund_rate_return = max([aster_buy_fund_rate_return, abs(funding_rate_switch)]) + extend_buy_fund_rate_return = extend_buy_fund_rate_return # funding_rate_exch = 'ASTER' # funding_rate_side = 'BUY' - else: - if funding_rate_exch == 'ASTER': - if funding_rate_side == 'BUY': - aster_buy_fund_rate_return = abs(funding_rate) - extend_buy_fund_rate_return = abs(funding_rate) * -1 - else: - aster_buy_fund_rate_return = abs(funding_rate) * -1 - extend_buy_fund_rate_return = abs(funding_rate) - else: # funding_rate_exch == 'EXTEND': - if funding_rate_side == 'BUY': - aster_buy_fund_rate_return = abs(funding_rate) * -1 - extend_buy_fund_rate_return = abs(funding_rate) - else: - aster_buy_fund_rate_return = abs(funding_rate) - extend_buy_fund_rate_return = abs(funding_rate) * -1 - aster_mid_px: Decimal = ( Decimal(str(aster_ticker_dict['best_ask_px'])) + Decimal(str(aster_ticker_dict['best_bid_px'])) ) / 2 extend_mid_px: Decimal = ( Decimal(str(extend_ticker_dict['best_ask_px'])) + Decimal(str(extend_ticker_dict['best_bid_px'])) ) / 2 @@ -288,7 +289,8 @@ def signal_alpha_over_taker( aster_buy_expected_alpha: Decimal = ( aster_buy_ratio_min_taker_hurdle - Decimal(str(Aster.buy_ratio)) ).quantize(Decimal('0.000001'), rounding='ROUND_DOWN') # Decimal Price % Diff (x Qty = Alpha $) extend_buy_expected_alpha: Decimal = ( extend_buy_ratio_min_taker_hurdle - Decimal(str(Extend.buy_ratio)) ).quantize(Decimal('0.000001'), rounding='ROUND_DOWN') # Decimal Price % Diff (x Qty = Alpha $) - + best_expected_alpha = max([aster_buy_expected_alpha, extend_buy_expected_alpha]) + # logging.info(f'aster_buy_ratio_min_taker_hurdle: ( {aster_buy_ratio} - {aster_buy_fund_rate_return} ) - {taker_fee} - {alpha_hurdle_adj} = {aster_buy_ratio_min_taker_hurdle}') # logging.info(f'extend_buy_ratio_min_taker_hurdle: ( {extend_buy_ratio} - {extend_buy_fund_rate_return} ) - {taker_fee} - {alpha_hurdle_adj} = {extend_buy_ratio_min_taker_hurdle}') # logging.info(f'aster_buy_expected_alpha: {aster_buy_ratio_min_taker_hurdle} - {Decimal(str(Aster.buy_ratio))} = {aster_buy_expected_alpha}') @@ -299,7 +301,7 @@ def signal_alpha_over_taker( # aster_buy_expected_alpha : 0.000673878630659394266895260 - 0.0014628375 = -0.0007 # extend_buy_expected_alpha : -0.001173878630659394266895260 - -0.0014628375 = 0.0002 - if aster_buy_expected_alpha > 0: + if ( aster_buy_expected_alpha > 0 ) and ( best_expected_alpha == aster_buy_expected_alpha ): signal: bool = True exchange: str = 'ASTER' side: str = 'BUY' @@ -307,7 +309,7 @@ def signal_alpha_over_taker( expected_alpha: Decimal = aster_buy_expected_alpha model_ratio: Decimal = Decimal(str(Aster.buy_ratio)) current_ratio: Decimal = aster_buy_ratio_min_taker_hurdle - elif extend_buy_expected_alpha > 0: + elif ( extend_buy_expected_alpha > 0 ) and ( best_expected_alpha == extend_buy_expected_alpha ): signal: bool = True exchange: str = 'EXTEND' side: str = 'BUY' @@ -316,7 +318,7 @@ def signal_alpha_over_taker( model_ratio: Decimal = Decimal(str(Extend.buy_ratio)) current_ratio: Decimal = extend_buy_ratio_min_taker_hurdle else: - if max([aster_buy_expected_alpha,extend_buy_expected_alpha]) == aster_buy_expected_alpha: + if best_expected_alpha == aster_buy_expected_alpha: signal: bool = False exchange: str = 'ASTER' side: str = 'BUY' @@ -877,7 +879,7 @@ async def run_algo(): aster_fund_rate = aster_fund_rate * -1 extend_fund_rate = extend_fund_rate * -1 - aster_fund_rate_time = float(aster_fund_rate_dict.get('next_funding_time_ts_ms', 0)) + aster_fund_rate_time = max([float(aster_fund_rate_dict.get('next_funding_time_ts_ms', 0)), 0]) aster_fund_rate_time = aster_fund_rate_time+(60*60*1000) if aster_fund_rate_time < (datetime.now().timestamp()*1000) else aster_fund_rate_time extend_fund_rate_time = max([float(extend_fund_rate_dict.get('next_funding_time_ts_ms', 0)), 0]) @@ -893,7 +895,8 @@ async def run_algo(): if next_funding_at_same_time: net_fr = max([fund_rate_ast, fund_rate_ext]) - min([fund_rate_ast, fund_rate_ext]) fr_best_exch = 'ASTER' if max([abs(fund_rate_ast), abs(fund_rate_ext)]) == abs(fund_rate_ast) else 'EXTEND' - fr_best_side = 'BUY' if net_fr < 0 else 'SELL' + fr_best_rate = fund_rate_ast if max([abs(fund_rate_ast), abs(fund_rate_ext)]) == abs(fund_rate_ast) else fund_rate_ext + fr_best_side = 'BUY' if fr_best_rate < 0 else 'SELL' return net_fr, fr_best_exch, fr_best_side else: fr_best_exch = 'EXTEND' @@ -1261,11 +1264,7 @@ async def main(): VAL_KEY.set(name='fr_orchestrator_output', value=json.dumps(obj=Config.model_dump())) VAL_KEY.set(name='fr_algo_working_symbol', value=json.dumps(obj={'ASTER': asdict(obj=Aster), 'EXTEND': asdict(obj=Extend)})) - Funding_Rates_Min_Remaining_Factor_Pcts = calc_fr_minutes_remaining_factor( - min_start_procedure = 30, - min_to_end_procedure = 7, - factor_exp_pct = 0.50 - ) + Funding_Rates_Min_Remaining_Factor_Pcts = calc_fr_minutes_remaining_factor() Algo_Status = structs.Algo_Status( last_update_ts_ms = int(round(datetime.now().timestamp()*1000, 2)), diff --git a/ng.py b/ng.py index e96ae00..c181e88 100644 --- a/ng.py +++ b/ng.py @@ -1,5 +1,5 @@ import os -from nicegui import ui, app +from nicegui import ui, app, html from sqlalchemy import create_engine # import requests import json @@ -127,7 +127,8 @@ def update_body_scroll(e=None, bool_override=False): async def update_tv(): series_update_aster_tob = json.loads(VALKEY.get('fut_ticker_aster')) # ty:ignore[invalid-argument-type] series_update_extend_tob = json.loads(VALKEY.get('fut_ticker_extended')) # ty:ignore[invalid-argument-type] - series_update_algo_status = json.loads(VALKEY.get('algo_status')) # ty:ignore[invalid-argument-type] + series_update_algo_status = json.loads(VALKEY.get('algo_status')) # ty:ignore[invalid-argument-type] + master_data = json.loads(VALKEY.get(name='fr_engine_best_fund_rate_master')) # ty:ignore[invalid-argument-type] timestamp_aster_tob = round( ( series_update_aster_tob['timestamp_transaction'] / 1000 ) , 2) timestamp_extend_tob = round( ( series_update_extend_tob['timestamp_msg'] / 1000 ) , 2) @@ -172,18 +173,66 @@ async def rt_chart_page(): LOOKBACK = app.storage.user.get('lookback', LOOKBACK) timer = ui.timer(REFRESH_INTERVAL_RT_SEC, update_tv) - with ui.row(): - with ui.column(): - ui.switch('☸︎', value=ALLOW_BODY_SCROLL, on_change=lambda e: update_body_scroll(e)) - with ui.column(): - ui.switch('▶️', value=True).bind_value_to(timer, 'active') - with ui.column().style('position: absolute; right: 20px; font-family: monospace; align-self: center;'): - ui.label('Atwater Trading - Funding Rate') + # ui.query('.q-page').classes('flex flex-col h-screen') + + # with ui.row(): + # with ui.column(): + # ui.switch('☸︎', value=ALLOW_BODY_SCROLL, on_change=lambda e: update_body_scroll(e)) + # with ui.column(): + # ui.switch('▶️', value=True).bind_value_to(timer, 'active') + # with ui.column().style('position: absolute; right: 20px; font-family: monospace; align-self: center;'): + # ui.label('Atwater Trading - Funding Rate') - with ui.grid(columns=16).classes('w-full gap-0 auto-fit'): - with ui.card().tight().classes('w-full col-span-full no-shadow border border-black-200').style('overflow: auto;'): - ui.html('
', sanitize=False).classes('w-full') - ui.run_javascript(f'await create_tv(charts_list={CHARTS}, create_chart_options={CHARTS_OPTIONS});') + with ui.grid(columns=2, rows=2).classes('h-screen w-full flex-grow gap-2 auto-fit '): + aggrid = ui.aggrid({ + 'columnDefs': [ + {'field': 'name', 'editable': True, 'sortable': True}, + {'field': 'age', 'editable': True}, + {'field': 'id'}, + ], + 'rowData': [ + {'id': 0, 'name': 'Alice', 'age': 18}, + {'id': 1, 'name': 'Bob', 'age': 21}, + {'id': 2, 'name': 'Carol', 'age': 20}, + ], + 'rowSelection': {'mode': 'multiRow'}, + 'stopEditingWhenCellsLoseFocus': True, + }).classes('auto-fit flex-grow w-full col-span-2 md:col-span-1') + # with ui.element(tag='div').classes('auto-fit flex-grow w-full').style("height:100%; width: 100%;"): + # with ui.tabs().classes('w-full') as tabs: + # one = ui.tab('One').classes('auto-fit flex-grow w-full').style("height:100%; width: 100%;") + # two = ui.tab('Two').classes('auto-fit flex-grow w-full').style("height:100%; width: 100%;") + # with ui.tab_panels(tabs, value=two).classes('auto-fit flex-grow w-full').style("height:100%; width: 100%;"): + # with ui.tab_panel(one).classes('auto-fit flex-grow w-full').style("height:100%; width: 100%;"): + # ui.label('First tab') + # with ui.tab_panel(two).classes('auto-fit flex-grow w-full').style("height:100%; width: 100%;"): + ui.html('
', sanitize=False).classes('auto-fit flex-grow w-full col-span-2 md:col-span-1') + ui.run_javascript(f'await create_tv(charts_list={CHARTS}, create_chart_options={CHARTS_OPTIONS});') + + with ui.element(tag='div').classes('col-span-2').style("height:100%; width: 100%;"): + with ui.tabs().classes('w-full') as tabs: + one = ui.tab('One') + two = ui.tab('Two') + with ui.tab_panels(tabs, value=two).classes('w-full').style("height:100%; width: 100%;"): + with ui.tab_panel(one): + ui.label('First tab') + with ui.tab_panel(two): + aggrid_2 = ui.aggrid({ + 'columnDefs': [ + {'field': 'name', 'editable': True, 'sortable': True}, + {'field': 'age', 'editable': True}, + {'field': 'id'}, + ], + 'rowData': [ + {'id': 0, 'name': 'Alice', 'age': 18}, + {'id': 1, 'name': 'Bob', 'age': 21}, + {'id': 2, 'name': 'Carol', 'age': 20}, + ], + 'rowSelection': {'mode': 'multiRow'}, + 'stopEditingWhenCellsLoseFocus': True, + }) + + def root(): app.add_static_files(max_cache_age=0, url_path='/static', local_directory=os.path.join(os.path.dirname(__file__), 'nicegui_modules/static')) diff --git a/nicegui_modules/static/script.js b/nicegui_modules/static/script.js index 87d514c..2cbbcff 100644 --- a/nicegui_modules/static/script.js +++ b/nicegui_modules/static/script.js @@ -36,10 +36,12 @@ async function update_tv(data_list, lookback_max_points) { async function create_tv(charts_list, create_chart_options) { + const container = document.getElementById('tv'); + if (create_chart_options.crosshair == 'NORMAL') { create_chart_options.crosshair = { mode: LightweightCharts.CrosshairMode.Normal } }; - window.chart = LightweightCharts.createChart(document.getElementById('tv'), create_chart_options); + window.chart = LightweightCharts.createChart(container, create_chart_options); window.charts_arr = []; charts_list.forEach(function (item, index) { @@ -64,7 +66,18 @@ async function create_tv(charts_list, create_chart_options) { }); window.chart.timeScale().fitContent(); - + + // Handle responsiveness: Resize chart when container size changes + const resizeObserver = new ResizeObserver(entries => {{ + for (let entry of entries) {{ + window.chart.applyOptions({ + width: entry.contentRect.width, + height: entry.contentRect.height + }); + }} + }}); + resizeObserver.observe(container); + console.log("TV Created!") // window.midPriceLine_Config = {