diff --git a/extended.ipynb b/extended.ipynb index 2a1270e..c80b600 100644 --- a/extended.ipynb +++ b/extended.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "6c70a8c3", "metadata": {}, "outputs": [], @@ -21,13 +21,13 @@ "import asyncio\n", "import logging\n", "from decimal import Decimal\n", - "\n", + "import modules.extended_auth as extend_auth\n", "\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "id": "ff971ca9", "metadata": {}, "outputs": [], @@ -47,6 +47,16 @@ "ORDER_PRICE = Decimal(\"75000\")" ] }, + { + "cell_type": "code", + "execution_count": 8, + "id": "fc2c6d2b", + "metadata": {}, + "outputs": [], + "source": [ + "client, trading_client = await extend_auth.create_auth_account_and_trading_client()" + ] + }, { "cell_type": "code", "execution_count": null, @@ -54,13 +64,13 @@ "metadata": {}, "outputs": [], "source": [ - "placed_order = await trading_client.place_order(\n", - " market_name=ORDER_MARKET,\n", - " amount_of_synthetic=ORDER_QTY,\n", - " price=ORDER_PRICE,\n", - " side=ORDER_SIDE,\n", - " taker_fee=Decimal(\"0.00025\")\n", - ")" + "# placed_order = await trading_client.place_order(\n", + "# market_name=ORDER_MARKET,\n", + "# amount_of_synthetic=ORDER_QTY,\n", + "# price=ORDER_PRICE,\n", + "# side=ORDER_SIDE,\n", + "# taker_fee=Decimal(\"0.00025\")\n", + "# )" ] }, { diff --git a/main.py b/main.py index b10f595..571b005 100644 --- a/main.py +++ b/main.py @@ -17,10 +17,11 @@ import requests import valkey from sqlalchemy import text from sqlalchemy.ext.asyncio import create_async_engine - +import modules.aster_auth as aster_auth +import modules.extended_auth as extend_auth ### Database ### -CLIENT = None +EXTEND_CLIENT = None CON: AsyncContextManager | None = None VAL_KEY = None @@ -28,9 +29,53 @@ VAL_KEY = None load_dotenv() LOG_FILEPATH: str = os.getenv("LOGS_PATH") + '/Fund_Rate_Algo.log' +### CONSTANTS ### +ASTER_LH_ASSET: str = 'ETH' +ASTER_RH_ASSET: str = 'USDT' +ASTER_TICKER: str = ASTER_LH_ASSET + ASTER_RH_ASSET +EXTEND_LH_ASSET: str = 'ETH' +EXTEND_RH_ASSET: str = 'USD' +EXTEND_TICKER: str = EXTEND_LH_ASSET + '-' + EXTEND_RH_ASSET + +TARGET_OPEN_CASH_POSITION: float = 10 # Each side (alpha and hedge) + +### GLOBALS ### +ASTER_MULT = 150 +EXTEND_MULT = 50 +MAX_TARGET_NOTIONAL = min([ASTER_MULT, EXTEND_MULT]) * TARGET_OPEN_CASH_POSITION + +ASTER_MIN_ORDER_QTY = 0.001 +EXTEND_MIN_ORDER_QTY = 0.01 + +ASTER_AVAIL_COLLATERAL = 0 +ASTER_NOTIONAL_POSITION = 0 +EXTEND_AVAIL_COLLATERAL = 0 +EXTEND_NOTIONAL_POSITION = 0 + +ASTER_OPEN_POSITIONS = [] +EXTEND_OPEN_POSITIONS = [] + +ASTER_OPEN_ORDERS = [] +EXTEND_OPEN_ORDERS = [] + +### FLAGS ### +LIQUIDATE_POS_AND_KILL_ALGO_FLAG: bool = False + +async def aster_remainder_route(): + # Check open orders...cancel replace or new order? + # Check collateral to confirm you have enough money to trade + # if CR, what should be the new price? has it changed? maybe no action needed? how long has it been working? + # if not enough collateral then need to liquidate and kill algo - flip flag + + # if good to order, then create and post order. ADD to LOCAL OPEN ORDERS LIST + + + pass + +async def extend_remainder_route(): + pass async def run_algo(): - try: while True: loop_start = time.time() @@ -48,14 +93,84 @@ async def run_algo(): ASTER_FUND_RATE = float(ASTER_FUND_RATE_DICT.get('funding_rate', 0)) EXTEND_FUND_RATE = float(EXTENDED_FUND_RATE_DICT.get('funding_rate', 0)) + ASTER_FUND_RATE_TIME = float(ASTER_FUND_RATE_DICT.get('next_funding_time_ts_ms', 0)) + EXTEND_FUND_RATE_TIME = float(EXTENDED_FUND_RATE_DICT.get('next_funding_time_ts_ms', 0)) + + ASTER_PAYOUT_DIRECTION_STR = 'LONG PAYS SHORT' if ASTER_FUND_RATE > 0 else 'SHORT PAYS LONG' + EXTEND_PAYOUT_DIRECTION_STR = 'LONG PAYS SHORT' if EXTEND_FUND_RATE > 0 else 'SHORT PAYS LONG' + + FUNDINGS_AT_SAME_TIME_NEXT_HR = ( (ASTER_FUND_RATE_TIME < 60*60*1000) and (EXTEND_FUND_RATE < 60*60*1000) ) + + if ( abs(ASTER_FUND_RATE) > abs(EXTEND_FUND_RATE) ) and FUNDINGS_AT_SAME_TIME_NEXT_HR: + ALPHA_EXCH = 'ASTER' + ALPHA_FUND_RATE = ASTER_FUND_RATE + else: + ALPHA_EXCH = 'EXTEND' + ALPHA_FUND_RATE = EXTEND_FUND_RATE + + if ALPHA_FUND_RATE < 0: + ALPHA_CARRY_SIDE = 'BUY' + ALPHA_TGT_NOTIONAL = MAX_TARGET_NOTIONAL + else: + ALPHA_CARRY_SIDE = 'SELL' + ALPHA_TGT_NOTIONAL = MAX_TARGET_NOTIONAL*-1 + + + def calc_next_net_fund_rate(FUNDINGS_AT_SAME_TIME_NEXT_HR: bool) -> float: + if FUNDINGS_AT_SAME_TIME_NEXT_HR: + return ASTER_FUND_RATE + EXTEND_FUND_RATE + else: + return EXTEND_FUND_RATE + + NEXT_NET_FUNDING_RATE = calc_next_net_fund_rate(FUNDINGS_AT_SAME_TIME_NEXT_HR) + + if ALPHA_EXCH == 'EXTEND': + ASTER_TGT_NOTIONAL = ALPHA_TGT_NOTIONAL*-1 + EXTEND_TGT_NOTIONAL = ALPHA_TGT_NOTIONAL + else: + ASTER_TGT_NOTIONAL = ALPHA_TGT_NOTIONAL + EXTEND_TGT_NOTIONAL = ALPHA_TGT_NOTIONAL*-1 + + ASTER_TGT_TAIL = ASTER_TGT_NOTIONAL - ASTER_NOTIONAL_POSITION + EXTEND_TGT_TAIL = EXTEND_TGT_NOTIONAL - EXTEND_NOTIONAL_POSITION + + ASTER_TGT_TAIL_ORDERABLE = abs(ASTER_TGT_TAIL) >= ASTER_MIN_ORDER_QTY + EXTEND_TGT_TAIL_ORDERABLE = abs(EXTEND_TGT_TAIL) >= EXTEND_MIN_ORDER_QTY print(f''' - ASTER FR: {ASTER_FUND_RATE:.6%} | EXTEND FR: {EXTEND_FUND_RATE:.6%} + {pd.to_datetime(ASTER_FUND_RATE_TIME, unit='ms')} ({(pd.to_datetime(ASTER_FUND_RATE_TIME, unit='ms')-datetime.now()):}) | {pd.to_datetime(EXTEND_FUND_RATE_TIME, unit='ms')} ({(pd.to_datetime(EXTEND_FUND_RATE_TIME, unit='ms')-datetime.now()):}) + ASTER: {ASTER_FUND_RATE:.6%} [{ASTER_FUND_RATE*10_000:.2f}bps] [{ASTER_FUND_RATE*1_000_000:.0f}pips] | EXTEND: {EXTEND_FUND_RATE:.6%} [{EXTEND_FUND_RATE*10_000:.2f}bps] [{EXTEND_FUND_RATE*1_000_000:.0f}pips] + ASTER: {ASTER_PAYOUT_DIRECTION_STR} | EXTEND: {EXTEND_PAYOUT_DIRECTION_STR} + ASTER: [ Available Collateral: {ASTER_AVAIL_COLLATERAL:.4f} ] | EXTEND: [ Available Collateral: {EXTEND_AVAIL_COLLATERAL:.4f} ] + ASTER: [ Notional Position $ : {ASTER_NOTIONAL_POSITION:.4f} ] | EXTEND: [ Notional Position $ : {EXTEND_NOTIONAL_POSITION:.4f} ] + + SAME TIME? : {FUNDINGS_AT_SAME_TIME_NEXT_HR} + NET FUNDING : {NEXT_NET_FUNDING_RATE:.6%} [{NEXT_NET_FUNDING_RATE*10_000:.2f}bps] [{NEXT_NET_FUNDING_RATE*1_000_000:.0f}pips] + ALPHA SIDE : {ALPHA_EXCH} [{ALPHA_CARRY_SIDE}] + + TGT NOTIONAL: $ {MAX_TARGET_NOTIONAL} + + ASTER: {ASTER_NOTIONAL_POSITION:.4f} -> {ASTER_TGT_NOTIONAL:.2f} [ Remain: {ASTER_TGT_TAIL:.4f} ] | EXTEND: {EXTEND_NOTIONAL_POSITION:.4f} -> {EXTEND_TGT_NOTIONAL:.2f} [ Remain: {EXTEND_TGT_TAIL} ] + ASTER: {ASTER_TGT_TAIL:.4f} > {ASTER_MIN_ORDER_QTY:.4f} min [ Order: {ASTER_TGT_TAIL_ORDERABLE} ] | EXTEND: {EXTEND_TGT_TAIL:.4f} > {EXTEND_MIN_ORDER_QTY:.4f} min [ Order: {EXTEND_TGT_TAIL_ORDERABLE} ] + ''') + ### SCAN VALKEY USER FEEDS FOR BALANCE UPDATES ### + # or just to begin hit the rest API before ordering and update bals then + + + ### ROUTES ### + if ASTER_TGT_TAIL_ORDERABLE: + await aster_remainder_route() + + if EXTEND_TGT_TAIL_ORDERABLE: + await extend_remainder_route() + + + + print(f'__________ End ___________ (Algo Engine ms: {(time.time() - loop_start)*1000})') time.sleep(5) - print(f'__________________________ (Algo Engine ms: {(time.time() - loop_start)*1000})') except KeyboardInterrupt: print('...algo stopped') # await cancel_all_orders(CLIENT=CLIENT) @@ -63,17 +178,90 @@ async def run_algo(): logging.critical(f'*** ALGO ENGINE CRASHED: {e}') logging.error(traceback.format_exc()) # await cancel_all_orders(CLIENT=CLIENT) - + +### WALLLET ### +async def get_aster_collateral(): + global ASTER_AVAIL_COLLATERAL + + fut_acct_balances = { + "url": "/fapi/v3/balance", + "method": "GET", + "params": {} + } + r = aster_auth.post_authenticated_url(fut_acct_balances) + ASTER_AVAIL_COLLATERAL = float([d for d in r if d.get('asset')==ASTER_RH_ASSET][0].get('availableBalance')) + +async def get_aster_notional_position(): + global ASTER_NOTIONAL_POSITION + global ASTER_MULT + + fut_acct_positionRisk = { + "url": "/fapi/v3/positionRisk", + "method": "GET", + "params": {} + } + r = aster_auth.post_authenticated_url(fut_acct_positionRisk) + d = [d for d in r if d.get('symbol', None) == ASTER_TICKER][0] + + ASTER_NOTIONAL_POSITION = float(d.get('notional' ,0)) + ASTER_MULT = float(d.get('leverage', ASTER_MULT)) + +async def get_extend_collateral(): + global EXTEND_AVAIL_COLLATERAL + + get_bals = dict(dict(await EXTEND_CLIENT.account.get_balance()).get('data', {})) + EXTEND_AVAIL_COLLATERAL = get_bals.get('available_for_trade', 0) if get_bals.get('collateral_name', None)==EXTEND_RH_ASSET else 0 + +async def get_extend_notional(): + global EXTEND_NOTIONAL_POSITION + global EXTEND_MULT + + get_pos = dict(await EXTEND_CLIENT.account.get_positions()).get('data', {}) + pos_dict = [d for d in get_pos if d.get('market') == EXTEND_TICKER] + if pos_dict: + pos_dict = pos_dict[0] + EXTEND_NOTIONAL_POSITION = pos_dict.get('value', 0) + EXTEND_MULT = pos_dict.get('leverage', EXTEND_MULT) + else: + EXTEND_NOTIONAL_POSITION = 0 + +### EXCHANGE INFO ### +async def get_aster_exch_info(): + global ASTER_MIN_ORDER_QTY + + fut_acct_exchangeInfo = { + "url": "/fapi/v3/exchangeInfo", + "method": "GET", + "params": {} + } + r = aster_auth.post_authenticated_url(fut_acct_exchangeInfo) + s = r['symbols'] + d = [d for d in s if d.get('symbol', None) == 'ETHUSDT'][0] + f = [f for f in d['filters'] if f.get('filterType', None) == 'LOT_SIZE'][0] + ASTER_MIN_ORDER_QTY = float(f['minQty']) + +async def get_extend_exch_info(): + global EXTEND_MIN_ORDER_QTY + + r = await EXTEND_CLIENT.markets_info.get_markets_dict() + EXTEND_MIN_ORDER_QTY = float(r['ETH-USD'].trading_config.min_order_size) + async def main(): - global CLIENT + global EXTEND_CLIENT global VAL_KEY global CON + _, EXTEND_CLIENT = await extend_auth.create_auth_account_and_trading_client() VAL_KEY = valkey.Valkey(host='localhost', port=6379, db=0, decode_responses=True) engine = create_async_engine('mysql+asyncmy://root:pwd@localhost/fund_rate') async with engine.connect() as CON: # await create_executions_orders_table(CON=CON) + await get_aster_collateral() + await get_aster_notional_position() + await get_extend_collateral() + await get_extend_notional() + await run_algo() if __name__ == '__main__': diff --git a/modules/__pycache__/extended_auth.cpython-313.pyc b/modules/__pycache__/extended_auth.cpython-313.pyc index 746f089..140d02b 100644 Binary files a/modules/__pycache__/extended_auth.cpython-313.pyc and b/modules/__pycache__/extended_auth.cpython-313.pyc differ