saving
This commit is contained in:
@@ -21,7 +21,6 @@ df_leverage_by_exch = pd.DataFrame(data=leverage.LEVERAGE_BY_EXCH)
|
||||
### Database ###
|
||||
# CON: AsyncContextManager | None = None
|
||||
VAL_KEY: valkey.Valkey
|
||||
VK_OUT: str = 'fr_engine_best_fund_rate_output'
|
||||
|
||||
### Logging ###
|
||||
load_dotenv()
|
||||
@@ -36,6 +35,11 @@ REFRESH_MKT_VOLUME_EVERY_SEC: int = 30
|
||||
Mkt_Info_Last_Refresh_TS_ms: int = 0
|
||||
Mkt_Volume_Last_Refresh_TS_ms: int = 0
|
||||
|
||||
### TODO: score by volume, how long since last trade?, volatility, volume by time of day (active or dormant period?), funding rate consistency (% one side last 24hrs and from active close to active open periods). trade cost estimate?, max tradeable notional.
|
||||
### TODO: figure out what is max percent of volume i can trade - TCA kinda? what is ideal slice size?
|
||||
### TODO: Redesign so Algo allocates across the best markets with a waterfall method until at target collateral usage. order waterfall by score above^^
|
||||
### TODO: NG display grid of markets sorted by above score. top left is control panel, top right is graph (goes to mkt you click on from table) (maybe tabs for different graph views/groups, e.g. PnL total or all mkts percent to liquidate, pov by market etc.) middle bottom is markets table (tabs for open orders, open positions, pnl)
|
||||
|
||||
### Funcs - Load Data ###
|
||||
async def get_extended_markets_info() -> pd.DataFrame:
|
||||
r: dict = json.loads(s=requests.get(url='https://api.starknet.extended.exchange/api/v1/info/markets').text)
|
||||
@@ -46,8 +50,12 @@ async def get_extended_markets_info() -> pd.DataFrame:
|
||||
df['daily_volume'] = df['marketStats'].apply(lambda x: x.get('dailyVolume',{})).astype(float)
|
||||
df['min_order_size'] = df['tradingConfig'].apply(lambda x: x.get('minOrderSize',{}))
|
||||
df['min_price'] = df['tradingConfig'].apply(lambda x: x.get('minPriceChange',{}))
|
||||
df['min_notional'] = 0
|
||||
df['min_lot_size'] = df['tradingConfig'].apply(lambda x: x.get('minOrderSizeChange',{}))
|
||||
df['max_leverage'] = df['tradingConfig'].apply(lambda x: x.get('maxLeverage',{}))
|
||||
|
||||
#### TODO: ADD IN LOT SIZE FOR ROUND LOTS (SEE IPYNB)
|
||||
|
||||
print('Extend markets info refreshed successfully')
|
||||
|
||||
return df
|
||||
@@ -63,7 +71,9 @@ async def get_aster_exch_info() -> pd.DataFrame:
|
||||
df = pd.DataFrame(r['symbols'])
|
||||
df['min_order_size'] = df['filters'].apply(lambda x: [f for f in x if f.get('filterType', None) == 'LOT_SIZE'][0]['minQty'] )
|
||||
df['min_price'] = df['filters'].apply(lambda x: [f for f in x if f.get('filterType', None) == 'PRICE_FILTER'][0]['minPrice'] )
|
||||
|
||||
df['min_notional'] = df['filters'].apply(lambda x: [f for f in x if f.get('filterType', None) == 'MIN_NOTIONAL'][0]['notional'] )
|
||||
df['min_lot_size'] = df['filters'].apply(lambda x: [f for f in x if f.get('filterType', None) == 'LOT_SIZE'][0]['stepSize'] )
|
||||
|
||||
fut_acct_ticker_stats: dict = {
|
||||
"url": "/fapi/v3/ticker/24hr",
|
||||
"method": "GET",
|
||||
@@ -71,8 +81,9 @@ async def get_aster_exch_info() -> pd.DataFrame:
|
||||
}
|
||||
r: dict = await aster_auth.post_authenticated_url(fut_acct_ticker_stats) # ty:ignore[invalid-assignment]
|
||||
df_stats = pd.DataFrame(r)
|
||||
df_stats['last_trade_ts_ast'] = df_stats['closeTime']
|
||||
|
||||
df = df.merge(df_stats[['symbol','quoteVolume']].rename({'quoteVolume':'daily_volume'}, axis=1), on='symbol', how='left')
|
||||
df = df.merge(df_stats[['symbol','quoteVolume','last_trade_ts_ast']].rename({'quoteVolume':'daily_volume'}, axis=1), on='symbol', how='left')
|
||||
df['daily_volume'] = df['daily_volume'].astype(float)
|
||||
|
||||
|
||||
@@ -87,8 +98,8 @@ def load_aster_current_fr(df_aster_exch_info: pd.DataFrame) -> pd.DataFrame:
|
||||
df['funding_rate_updated_dt'] = pd.to_datetime(df['funding_rate_updated_ts_ms'], unit='ms')
|
||||
df['funding_rate'] = df['funding_rate'].astype(float)
|
||||
df['time_delta_to_next_funding'] = pd.to_datetime(df['next_funding_ts'], unit='ms') - pd.Timestamp.now()
|
||||
df = df.merge(df_aster_exch_info[['symbol','daily_volume','min_order_size','min_price']], on='symbol', how='left')
|
||||
|
||||
df = df.merge(df_aster_exch_info[['symbol','daily_volume','min_order_size','min_price','min_lot_size','min_notional', 'last_trade_ts_ast']], on='symbol', how='left')
|
||||
|
||||
return df
|
||||
|
||||
def load_extend_current_fr(df_mkt_stats: pd.DataFrame) -> pd.DataFrame:
|
||||
@@ -97,8 +108,8 @@ def load_extend_current_fr(df_mkt_stats: pd.DataFrame) -> pd.DataFrame:
|
||||
df: pd.DataFrame = df[['symbol','funding_rate_updated_ts_ms','funding_rate']]
|
||||
df['funding_rate_updated_dt'] = pd.to_datetime(df['funding_rate_updated_ts_ms'], unit='ms')
|
||||
df['funding_rate'] = df['funding_rate'].astype(float)
|
||||
|
||||
df: pd.DataFrame = df.merge(df_mkt_stats[['name','assetName','status','funding_rate_ts','daily_volume','min_order_size','min_price']].rename({'name':'symbol','funding_rate_ts':'next_funding_ts'}, axis=1), on='symbol', how='left')
|
||||
|
||||
df = df.merge(df_mkt_stats[['name','assetName','status','funding_rate_ts','min_order_size','min_price','min_lot_size','min_notional','daily_volume']].rename({'name':'symbol','funding_rate_ts':'next_funding_ts'}, axis=1), on='symbol', how='left')
|
||||
df: pd.DataFrame = df.loc[df['status']=='ACTIVE',:]
|
||||
df['USDT_Symbol'] = df['assetName'] + 'USDT'
|
||||
|
||||
@@ -130,35 +141,69 @@ async def loop() -> None:
|
||||
df_comb_fr['net_mult'] = df_comb_fr['net_mult'].round(2)
|
||||
df_comb_fr['net_mult_x_net_fr_abs'] = df_comb_fr['net_funding_rate_abs'] * df_comb_fr['net_mult']
|
||||
|
||||
|
||||
df_best_fr_rate: pd.DataFrame = df_comb_fr[['symbol_ext','symbol_ast','daily_volume_ext','daily_volume_ast','funding_rate_ext','funding_rate_ast','min_price_ext','min_price_ast','min_order_size_ext','min_order_size_ast','max_leverage_ext','max_leverage_ast','lh_asset_ext','lh_asset_ast','rh_asset_ext','rh_asset_ast','net_mult_x_net_fr_abs','net_funding_rate_abs','net_funding_rate','next_funding_at_same_time']].sort_values(by='net_mult_x_net_fr_abs', ascending=False).reset_index(drop=True)
|
||||
|
||||
min_daily_volume = 100_000
|
||||
df_best_fr_rate = df_best_fr_rate.loc[ (df_best_fr_rate['daily_volume_ast']>=min_daily_volume) & (df_best_fr_rate['daily_volume_ext']>min_daily_volume) ,:].reset_index(drop=True)
|
||||
|
||||
ASTER = structs.Perpetual_Exchange(
|
||||
mult = int(df_best_fr_rate['max_leverage_ast'][0]),
|
||||
lh_asset = df_best_fr_rate['lh_asset_ast'][0],
|
||||
rh_asset = df_best_fr_rate['rh_asset_ast'][0],
|
||||
symbol_asset_separator = '',
|
||||
initial_funding_rate=float(df_best_fr_rate['funding_rate_ast'][0]),
|
||||
min_price=float(df_best_fr_rate['min_price_ast'][0]),
|
||||
min_order_size=float(df_best_fr_rate['min_order_size_ast'][0]),
|
||||
)
|
||||
EXTEND = structs.Perpetual_Exchange(
|
||||
mult = int(df_best_fr_rate['max_leverage_ext'][0]),
|
||||
lh_asset = df_best_fr_rate['lh_asset_ext'][0],
|
||||
rh_asset = df_best_fr_rate['rh_asset_ext'][0],
|
||||
symbol_asset_separator = '-',
|
||||
initial_funding_rate=float(df_best_fr_rate['funding_rate_ext'][0]),
|
||||
min_price=float(df_best_fr_rate['min_price_ext'][0]),
|
||||
min_order_size=float(df_best_fr_rate['min_order_size_ext'][0]),
|
||||
)
|
||||
|
||||
best_next_funding_pair: dict[str, dict] = {'ASTER': asdict(obj=ASTER), 'EXTEND': asdict(obj=EXTEND)}
|
||||
df_best_fr_rate = df_comb_fr[['symbol_ext','symbol_ast','daily_volume_ext','daily_volume_ast','min_price_ext','min_price_ast','min_order_size_ext','min_order_size_ast','min_lot_size_ext','min_lot_size_ast','min_notional_ext','min_notional_ast','funding_rate_ext','funding_rate_ast','max_leverage_ext','max_leverage_ast','lh_asset_ext','lh_asset_ast','rh_asset_ext','rh_asset_ast','net_mult_x_net_fr_abs','net_funding_rate_abs','net_funding_rate','next_funding_at_same_time','last_trade_ts_ast']].sort_values(by='net_mult_x_net_fr_abs', ascending=False).reset_index(drop=True)
|
||||
|
||||
VAL_KEY.set(name=VK_OUT, value=json.dumps(obj=best_next_funding_pair))
|
||||
# min_daily_volume = 100_000
|
||||
# df_best_fr_rate = df_best_fr_rate.loc[ (df_best_fr_rate['daily_volume_ast']>=min_daily_volume) & (df_best_fr_rate['daily_volume_ext']>min_daily_volume) ,:].reset_index(drop=True)
|
||||
|
||||
last_trade_max_ts = []
|
||||
for index, row in df_best_fr_rate.iterrows():
|
||||
r = json.loads(requests.get(f'https://api.starknet.extended.exchange/api/v1/info/markets/{row['symbol_ext']}/trades').text)
|
||||
max_ts = max([t['T'] for t in r['data']])
|
||||
last_trade_max_ts.append({'symbol_ext':row['symbol_ext'],'last_trade_ts_ext': max_ts})
|
||||
time.sleep(0.01)
|
||||
|
||||
df_best_fr_rate = df_best_fr_rate.merge(pd.DataFrame(last_trade_max_ts), on='symbol_ext', how='left')
|
||||
|
||||
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) ]
|
||||
|
||||
# print(df_best_fr_rate.columns)
|
||||
# print(df_best_fr_rate.iloc[0])
|
||||
|
||||
if len(df_best_fr_rate) < 1:
|
||||
raise ValueError(f'NO BFR RATE: {df_best_fr_rate}')
|
||||
|
||||
try:
|
||||
ASTER = structs.Perpetual_Exchange(
|
||||
mult = int(df_best_fr_rate['max_leverage_ast'].iloc[0]),
|
||||
lh_asset = df_best_fr_rate['lh_asset_ast'].iloc[0],
|
||||
rh_asset = df_best_fr_rate['rh_asset_ast'].iloc[0],
|
||||
symbol_asset_separator = '',
|
||||
initial_funding_rate=float(df_best_fr_rate['funding_rate_ast'].iloc[0]),
|
||||
min_price=float(df_best_fr_rate['min_price_ast'].iloc[0]),
|
||||
min_order_size=float(df_best_fr_rate['min_order_size_ast'].iloc[0]),
|
||||
min_lot_size=float(df_best_fr_rate['min_lot_size_ast'].iloc[0]),
|
||||
min_notional=float(df_best_fr_rate['min_notional_ast'].iloc[0]),
|
||||
)
|
||||
EXTEND = structs.Perpetual_Exchange(
|
||||
mult = int(df_best_fr_rate['max_leverage_ext'].iloc[0]),
|
||||
lh_asset = df_best_fr_rate['lh_asset_ext'].iloc[0],
|
||||
rh_asset = df_best_fr_rate['rh_asset_ext'].iloc[0],
|
||||
symbol_asset_separator = '-',
|
||||
initial_funding_rate=float(df_best_fr_rate['funding_rate_ext'].iloc[0]),
|
||||
min_price=float(df_best_fr_rate['min_price_ext'].iloc[0]),
|
||||
min_order_size=float(df_best_fr_rate['min_order_size_ext'].iloc[0]),
|
||||
min_lot_size=float(df_best_fr_rate['min_lot_size_ext'].iloc[0]),
|
||||
min_notional=float(df_best_fr_rate['min_notional_ext'].iloc[0]),
|
||||
)
|
||||
except Exception as e:
|
||||
logging.critical(f'Failed to build ASTER/EXTEND objs err: {e}; df cols: {df_best_fr_rate.columns}')
|
||||
logging.error(traceback.format_exc())
|
||||
continue
|
||||
best_next_funding_pair: dict[str, dict] = {'ASTER': asdict(obj=ASTER), 'EXTEND': asdict(obj=EXTEND)}
|
||||
VAL_KEY.set(name='fr_engine_best_fund_rate_output', value=json.dumps(obj=best_next_funding_pair))
|
||||
|
||||
master_data = df_comb_fr[
|
||||
['symbol_ast','max_leverage_ast','lh_asset_ast','rh_asset_ast','funding_rate_ast','min_price_ast','min_order_size_ast','min_lot_size_ast','min_notional_ast',
|
||||
'symbol_ext','max_leverage_ext','lh_asset_ext','rh_asset_ext','funding_rate_ext','min_price_ext','min_order_size_ext','min_lot_size_ext','min_notional_ext']
|
||||
].to_json(orient='records')
|
||||
|
||||
VAL_KEY.set(name='fr_engine_best_fund_rate_master', value=str(master_data))
|
||||
print(df_best_fr_rate[['symbol_ext','max_leverage_ext','funding_rate_ast','funding_rate_ext','net_funding_rate','daily_volume_ast']].head(10))
|
||||
logging.info(f'BFR REFRESHED @ {datetime.now()}')
|
||||
time.sleep(LOOP_SLEEP_SEC)
|
||||
continue
|
||||
except valkey.exceptions.ConnectionError as e:
|
||||
|
||||
Reference in New Issue
Block a user