This commit is contained in:
2026-05-04 18:04:45 +00:00
parent b05f389e49
commit 4eadc32f03
12 changed files with 10709 additions and 2496 deletions

File diff suppressed because one or more lines are too long

View File

@@ -1,12 +1,12 @@
{
"Updated_Timestamp": 1777667398908,
"Updated_Timestamp": 1777828655743,
"Config": {
"Loop_Sleep_Sec": 0.0,
"Max_Order_Over_Notional_Ratio": 1.05,
"Max_Target_Notional": 0.0,
"Min_Time_To_Funding_Minutes": 60,
"Min_Fund_Rate_Pct_To_Trade": 0.0,
"Price_Worsener_Aster": 0,
"Min_Time_To_Funding_Minutes": 57,
"Min_Fund_Rate_Pct_To_Trade": 0.0005,
"Price_Worsener_Aster": 1,
"Price_Worsener_Extend": -1,
"Switch_To_Taker_Seconds": 3,
"Target_Open_Cash_Position": 10

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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:

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

246
main.py
View File

@@ -1,4 +1,4 @@
from orjson import JSONDecodeError
from x10.utils.http import WrappedApiResponse
from x10.perpetual.trading_client.trading_client import PerpetualTradingClient
import asyncio
@@ -9,7 +9,7 @@ import os
import time
import traceback
from datetime import datetime, timezone
from decimal import ROUND_DOWN, ROUND_HALF_UP, Decimal
from decimal import ROUND_DOWN, ROUND_UP, ROUND_HALF_UP, Decimal
from typing import AsyncContextManager
from dataclasses import dataclass, asdict
from typing import Any
@@ -114,10 +114,15 @@ async def get_aster_account_open_symbols() -> list[str]:
"url": "/fapi/v3/positionRisk",
"method": "GET",
"params": {
'symbol': ASTER.symbol,
'symbol':''
}
}
resp: list = await aster_auth.post_authenticated_url(req=fut_acct_positionRisk) # ty:ignore[invalid-assignment]
try:
resp: list = await aster_auth.post_authenticated_url(req=fut_acct_positionRisk) # ty:ignore[invalid-assignment]
except Exception as e:
logging.critical(f'JSONDecodeError trying to get Aster open orders: {e}; resp: {resp}')
await kill_algo()
resp: list = []
ld = [ utils.symbol_to_extend_fmt(x['symbol']) for x in resp if abs(float(x.get('positionAmt', 0))) > 0]
return ld
@@ -144,10 +149,11 @@ async def get_aster_notional_position(resp: list | None = None):
}
try:
resp: list = await aster_auth.post_authenticated_url(req=fut_acct_positionRisk) # ty:ignore[invalid-assignment]
except JSONDecodeError as e:
logging.warning(f'JSONDecodeError trying to get Aster notional: {e}; resp: {resp}')
time.sleep(0.1)
resp: list = await aster_auth.post_authenticated_url(req=fut_acct_positionRisk) # ty:ignore[invalid-assignment]
except Exception as e:
logging.critical(f'JSONDecodeError trying to get Aster notional: {e}; resp: {resp}')
await kill_algo()
resp: list = []
d = [x for x in resp if x.get('symbol', None) == ASTER.symbol][0]
d['timestamp_arrival'] = round(datetime.now().timestamp()*1000)
@@ -168,10 +174,10 @@ async def get_aster_notional_position(resp: list | None = None):
ASTER_NOTIONAL_POSITION = float(d['notional']) - ASTER_UNREALIZED_PNL
else:
ASTER_NOTIONAL_POSITION = float(d['position_amount'])*float(d['entry_price'])
# if not resp: # this can never evaluate
# ASTER.mult = float(d['leverage'])
if d.get('leverage') is not None:
ASTER.mult = int(d['leverage'])
if abs(ASTER_NOTIONAL_POSITION) > ALGO_CONFIG.Config.Max_Target_Notional*ALGO_CONFIG.Config.Max_Order_Over_Notional_Ratio:
logging.info(f'BAD NOTIONAL - ASTER CHANGE: {previous_notional_position} -> {ASTER_NOTIONAL_POSITION}; UR PNL: {ASTER_UNREALIZED_PNL}; MULT: {ASTER.mult}; d: {d}; resp: {resp}')
logging.info(f'BAD NOTIONAL - ASTER CHANGE: {previous_notional_position} -> {ASTER_NOTIONAL_POSITION}; UR PNL: {ASTER_UNREALIZED_PNL}; MULT: {ASTER.mult}; d: {d}; resp: {resp}; max_tgt_notional: {ALGO_CONFIG.Config.Max_Target_Notional}')
await kill_algo()
if ASTER_NOTIONAL_POSITION != previous_notional_position:
logging.info(f'ASTER NOTIONAL CHANGE: {previous_notional_position:.2f} -> {ASTER_NOTIONAL_POSITION:.2f}; UR PNL: {ASTER_UNREALIZED_PNL:.2f}; MULT: {ASTER.mult:.0f}; resp: {bool(resp)}')
@@ -210,13 +216,15 @@ async def get_extend_notional(resp: list | None = None):
pos_dict = [dict(d) for d in resp if dict(d).get('market') == EXTEND.symbol]
if pos_dict:
pos_dict = pos_dict[0]
pos_dict['timestamp_arrival'] = round(datetime.now().timestamp()*1000)
else:
pos_dict = {}
pos_dict['side'] = 'LONG'
pos_dict['value'] = 0.00
pos_dict['unrealised_pnl'] = 0.00
pos_dict['timestamp_arrival'] = round(datetime.now().timestamp()*1000)
logging.info('get_extend_notional - No Positions')
pos_dict['timestamp_arrival'] = round(datetime.now().timestamp()*1000)
else:
pos_dict = [dict(d) for d in resp if dict(d).get('market') == EXTEND.symbol]
if pos_dict:
@@ -225,14 +233,18 @@ async def get_extend_notional(resp: list | None = None):
pos_dict = {}
pos_dict['side'] = 'LONG'
pos_dict['value'] = 0.00
pos_dict['unrealised_pnl'] = 0.00
pos_dict['timestamp_arrival'] = round(datetime.now().timestamp()*1000)
# logging.info('get_extend_notional - No Positions')
pos_dict['timestamp_arrival'] = round(datetime.now().timestamp()*1000)
# pos_dict['timestamp_arrival'] = round(datetime.now().timestamp()*1000)
if previous_notional_obj is not None:
if previous_notional_obj['timestamp_arrival'] > pos_dict['timestamp_arrival']:
# logging.info(f'EXTEND NOTIONAL: prev timestamp ({pd.to_datetime(previous_notional_obj['timestamp_arrival'], unit='ms')}) > new timestamp ({pd.to_datetime(d['timestamp_arrival'], unit='ms')}); skipping')
# logging.info(f'EXTEND NOTIONAL: prev timestamp ({pd.to_datetime(previous_notional_obj['timestamp_arrival'], unit='ms')}) > new timestamp ({pd.to_datetime(pos_dict['timestamp_arrival'], unit='ms')}); skipping')
return
else:
previous_notional_obj = {}
EXTEND_NOTIONAL_OBJ = pos_dict
@@ -252,7 +264,7 @@ async def get_extend_notional(resp: list | None = None):
logging.info(f'BAD NOTIONAL - EXTEND CHANGE: {previous_notional_position} -> {EXTEND_NOTIONAL_POSITION}; UR PNL: {EXTEND_UNREALIZED_PNL}; MULT: {EXTEND.mult}; d: {pos_dict}; resp: {resp}')
await kill_algo()
if EXTEND_NOTIONAL_POSITION != previous_notional_position:
logging.info(f'EXTEND NOTIONAL CHANGE: {previous_notional_position} -> {EXTEND_NOTIONAL_POSITION:.2f}; UR PNL: {EXTEND_UNREALIZED_PNL:.2f}; MULT: {EXTEND.mult}; resp: {bool(resp)}')
logging.info(f'EXTEND NOTIONAL CHANGE: {previous_notional_position} [{previous_notional_obj.get('timestamp_arrival')}] -> {EXTEND_NOTIONAL_POSITION:.2f} [{EXTEND_NOTIONAL_OBJ['timestamp_arrival']}]; UR PNL: {EXTEND_UNREALIZED_PNL:.2f}; MULT: {EXTEND.mult}; resp: {bool(resp)}')
### EXCHANGE INFO ###
async def get_aster_exch_info(symbol_override: str | None = None):
@@ -271,6 +283,7 @@ async def get_aster_exch_info(symbol_override: str | None = None):
d: dict = [d for d in s if d.get('symbol', None) == ASTER.symbol][0]
f: dict = [f for f in d['filters'] if f.get('filterType', None) == 'LOT_SIZE'][0]
q: dict = [f for f in d['filters'] if f.get('filterType', None) == 'PRICE_FILTER'][0]
n: dict = [f for f in d['filters'] if f.get('filterType', None) == 'MIN_NOTIONAL'][0]
min_qty = float(f['minQty'])
min_qty = int(min_qty) if min_qty == int(min_qty) else min_qty
@@ -279,6 +292,7 @@ async def get_aster_exch_info(symbol_override: str | None = None):
min_price = int(min_price) if min_price == int(min_price) else min_price
ASTER.min_order_size = min_qty
ASTER.min_price = min_price
ASTER.min_notional = float(n['notional'])
async def get_extend_exch_info(symbol_override: str | None = None):
global EXTEND
@@ -290,6 +304,7 @@ async def get_extend_exch_info(symbol_override: str | None = None):
EXTEND.min_order_size = float(r[EXTEND.symbol].trading_config.min_order_size)
EXTEND.min_price = float(r[EXTEND.symbol].trading_config.min_price_change)
### CANCEL ORDERS ###
async def aster_cancel_all_orders():
cancel_all_open_orders = {
@@ -367,13 +382,11 @@ async def run_algo():
ASTER_FUND_RATE = ASTER_FUND_RATE * -1
EXTEND_FUND_RATE = EXTEND_FUND_RATE * -1
if ALGO_CONFIG.Overrides.Flatten_Open_Positions or ALGO_CONFIG.Overrides.Flatten_Open_Positions_Opportunistic:
ROUNDING = ROUND_HALF_UP
else:
ROUNDING = ROUND_DOWN
ASTER_FUND_RATE_TIME = float(ASTER_FUND_RATE_DICT.get('next_funding_time_ts_ms', 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(EXTENDED_FUND_RATE_DICT.get('next_funding_time_ts_ms', 0)), 0])
EXTEND_FUND_RATE_TIME = EXTEND_FUND_RATE_TIME+(60*60*1000) if EXTEND_FUND_RATE_TIME < (datetime.now().timestamp()*1000) else EXTEND_FUND_RATE_TIME
ASTER_TICKER_DICT: Any = VAL_KEY.get('fut_ticker_aster')
ASTER_TICKER_DICT: dict = json.loads(s=ASTER_TICKER_DICT) if ASTER_TICKER_DICT is not None else {}
@@ -510,16 +523,39 @@ async def run_algo():
if abs( ASTER_NOTIONAL_POSITION ) > 0.00 or abs( EXTEND_NOTIONAL_POSITION ) > 0.00:
if ALGO_CONFIG.Logging.Print_Summary_Each_Loop:
print(f'Symbol switch [{ASTER.symbol} > {best_symbol_by_exchange_aster.symbol}] - Flattening Positions')
ALGO_CONFIG.Overrides.Flatten_Open_Positions = True
ALGO_CONFIG.Overrides.Flatten_Open_Positions_Opportunistic = True
else:
logging.info('Balances Flattened - Updating to Trade New Symbols:')
logging.info(f' ASTER.symbol -> {best_symbol_by_exchange_aster.symbol}')
logging.info(f' EXTEND.symbol -> {best_symbol_by_exchange_extend.symbol}')
ALGO_CONFIG.Overrides.Flatten_Open_Positions = False
ALGO_CONFIG.Overrides.Flatten_Open_Positions_Opportunistic = False
if Open_Symbols:
logging.info(f'OPEN SYMBOLS TO CLOSE: {Open_Symbols}')
await get_aster_exch_info(symbol_override=Open_Symbols[0])
await get_extend_exch_info(symbol_override=Open_Symbols[0])
master_data = json.loads(s=VAL_KEY.get(name='fr_engine_best_fund_rate_master')) # ty:ignore[invalid-argument-type]
open_symbol_to_work = Open_Symbols[0]
current_pos_master_ast = [d for d in master_data if d.get('symbol_ext') == open_symbol_to_work][0]
ASTER = structs.Perpetual_Exchange(
mult = int(current_pos_master_ast['max_leverage_ast']),
lh_asset = current_pos_master_ast['lh_asset_ast'],
rh_asset = current_pos_master_ast['rh_asset_ast'],
symbol_asset_separator = '',
initial_funding_rate=float(current_pos_master_ast['funding_rate_ast']),
min_price=float(current_pos_master_ast['min_price_ast']),
min_order_size=float(current_pos_master_ast['min_order_size_ast']),
min_lot_size=float(current_pos_master_ast['min_lot_size_ast']),
min_notional=float(current_pos_master_ast['min_notional_ast']),
)
EXTEND = structs.Perpetual_Exchange(
mult = int(current_pos_master_ast['max_leverage_ext']),
lh_asset = current_pos_master_ast['lh_asset_ext'],
rh_asset = current_pos_master_ast['rh_asset_ext'],
symbol_asset_separator = '-',
initial_funding_rate=float(current_pos_master_ast['funding_rate_ext']),
min_price=float(current_pos_master_ast['min_price_ext']),
min_order_size=float(current_pos_master_ast['min_order_size_ext']),
min_lot_size=float(current_pos_master_ast['min_lot_size_ext']),
min_notional=float(current_pos_master_ast['min_notional_ext']),
)
Open_Symbols.pop(0)
await get_aster_notional_position()
await get_extend_notional()
@@ -565,7 +601,13 @@ async def run_algo():
# else:
# logging.info('NET FUNDING = 0.00; NO OPEN ORDERS; Waiting...')
# time.sleep(5)
if ALGO_CONFIG.Overrides.Flatten_Open_Positions or ALGO_CONFIG.Overrides.Flatten_Open_Positions_Opportunistic or ALPHA_TGT_NOTIONAL==0.00:
# ROUNDING = ROUND_UP
ROUNDING = ROUND_HALF_UP
else:
ROUNDING = ROUND_DOWN
if ALPHA_EXCH == 'EXTEND':
ASTER_TGT_NOTIONAL = ALPHA_TGT_NOTIONAL*-1
@@ -603,28 +645,46 @@ async def run_algo():
Expected_Alpha = ( ( ASTER_TOB_PX - EXTEND_TOB_PX ) / (( ASTER_TOB_PX + EXTEND_TOB_PX ) / 2) )
Expected_Alpha_Net_FR = abs(NEXT_NET_FUNDING_RATE) + Expected_Alpha
Expected_Alpha_Net_FR_w_Taker = Expected_Alpha_Net_FR-0.0002
Expected_Alpha_w_Taker = Expected_Alpha-0.0002
Expected_Alpha_Net_FR_w_Taker = Expected_Alpha_Net_FR-0.00025
Expected_Alpha_w_Taker = Expected_Alpha-0.00025
EXTEND_TGT_NOTIONAL = ASTER_NOTIONAL_POSITION * -1
ASTER_TGT_TAIL = ASTER_TGT_NOTIONAL - ( float(ASTER_NOTIONAL_POSITION) + float(ASTER_UNREALIZED_PNL) )
EXTEND_TGT_TAIL = EXTEND_TGT_NOTIONAL - ( float(EXTEND_NOTIONAL_POSITION) + float(EXTEND_UNREALIZED_PNL) )
# EXTEND_TGT_TAIL = EXTEND_TGT_NOTIONAL - ( float(EXTEND_NOTIONAL_POSITION) + float(EXTEND_UNREALIZED_PNL) )
EXTEND_TGT_TAIL = EXTEND_TGT_NOTIONAL - ( float(EXTEND_NOTIONAL_POSITION) )
# EXTEND_TGT_TAIL = float(ASTER_NOTIONAL_POSITION)*-1
min_order_size = ASTER.min_order_size
min_order_size = int(min_order_size) if min_order_size == int(min_order_size) else min_order_size
ASTER_TGT_TAIL_BASE_QTY = Decimal(str(float(ASTER_TGT_TAIL) / float(ASTER_TOB_PX))).quantize(Decimal(str(min_order_size)), rounding=ROUNDING)
if ASTER.min_lot_size:
ASTER_TGT_TAIL_BASE_QTY = float(ASTER_TGT_TAIL_BASE_QTY) - ( float(ASTER_TGT_TAIL_BASE_QTY) % ASTER.min_lot_size )
ASTER_TGT_TAIL_BASE_QTY = Decimal(str(ASTER_TGT_TAIL_BASE_QTY)).quantize(Decimal(str(min_order_size)), rounding=ROUNDING)
min_order_size = EXTEND.min_order_size
min_order_size = int(min_order_size) if min_order_size == int(min_order_size) else min_order_size
EXTEND_TGT_TAIL_BASE_QTY = Decimal(str(float(EXTEND_TGT_TAIL) / float(EXTEND_TOB_PX))).quantize(Decimal(str(min_order_size)), rounding=ROUNDING)
if EXTEND.min_lot_size:
EXTEND_TGT_TAIL_BASE_QTY = float(EXTEND_TGT_TAIL_BASE_QTY) - ( float(EXTEND_TGT_TAIL_BASE_QTY) % EXTEND.min_lot_size )
EXTEND_TGT_TAIL_BASE_QTY = Decimal(str(EXTEND_TGT_TAIL_BASE_QTY)).quantize(Decimal(str(min_order_size)), rounding=ROUNDING)
# MAX_MIN_ORDER_QTY = max([ASTER.min_order_size, EXTEND.min_order_size])
ASTER_TGT_TAIL_ORDERABLE = Decimal(str(abs(ASTER_TGT_TAIL_BASE_QTY))) >= Decimal(str(abs(ASTER.min_order_size)))
EXTEND_TGT_TAIL_ORDERABLE = Decimal(str(abs(EXTEND_TGT_TAIL_BASE_QTY))) >= Decimal(str(abs(EXTEND.min_order_size)))
# ASTER_TGT_TAIL_ORDERABLE = abs(ASTER_TGT_TAIL_BASE_QTY) >= MAX_MIN_ORDER_QTY
# EXTEND_TGT_TAIL_ORDERABLE = abs(EXTEND_TGT_TAIL_BASE_QTY) >= MAX_MIN_ORDER_QTY
ASTER_TGT_TAIL_ORDERABLE = ( Decimal(str(abs(ASTER_TGT_TAIL_BASE_QTY)) ) >= Decimal(str(abs(ASTER.min_order_size))) ) and ( Decimal(str(abs(ASTER_TGT_TAIL))) > Decimal(str(abs(ASTER.min_notional))) )
EXTEND_TGT_TAIL_ORDERABLE = ( Decimal(str(abs(EXTEND_TGT_TAIL_BASE_QTY))) >= Decimal(str(abs(EXTEND.min_order_size))) ) and ( Decimal(str(abs(EXTEND_TGT_TAIL))) > Decimal(str(abs(EXTEND.min_notional))) )
if not ASTER_TGT_TAIL_ORDERABLE:
if abs(ASTER_TGT_TAIL_BASE_QTY) > 0:
if ALGO_CONFIG.Overrides.Flatten_Open_Positions or ALGO_CONFIG.Overrides.Flatten_Open_Positions_Opportunistic or ALPHA_TGT_NOTIONAL == 0.00:
logging.info('* Trying to flatten small Aster balance, was originally not orderable.')
ASTER_TGT_TAIL_ORDERABLE = True
if not EXTEND_TGT_TAIL_ORDERABLE:
if abs(EXTEND_TGT_TAIL_BASE_QTY) > 0:
if ALGO_CONFIG.Overrides.Flatten_Open_Positions or ALGO_CONFIG.Overrides.Flatten_Open_Positions_Opportunistic or ALPHA_TGT_NOTIONAL == 0.00:
logging.info('* Trying to flatten small Extend balance, was originally not orderable.')
EXTEND_TGT_TAIL_ORDERABLE = True
# Hedge_Ratio = abs(( abs( max([abs(float(EXTEND_NOTIONAL_POSITION)), 0.01]) / max([abs(float(ASTER_NOTIONAL_POSITION)), 0.01]) ) - 1 ) * 100)
Hedge_Ratio = abs( ( EXTEND_NOTIONAL_POSITION + ASTER_NOTIONAL_POSITION ) / max([ASTER_NOTIONAL_POSITION, 0.01]) ) * 100
@@ -652,7 +712,7 @@ async def run_algo():
ASTER: {ASTER_TGT_TAIL_BASE_QTY:.4f} > {ASTER.min_order_size:.4f} min [ Order: {ASTER_TGT_TAIL_ORDERABLE} ] | EXTEND: {EXTEND_TGT_TAIL_BASE_QTY:.4f} > {EXTEND.min_order_size:.4f} min [ Order: {EXTEND_TGT_TAIL_ORDERABLE} ]
ALPHA: {ALPHA_RATIO:.8f} ALPHA_RATIO: {Alpha_Nominator:_.6f} / {Alpha_Denominator:_.6f} (Px Diff: {abs(Alpha_Nominator-Alpha_Denominator):.2f}); Expected_Alpha = {Expected_Alpha:.6f} + FR[{NEXT_NET_FUNDING_RATE:.6f}] = * {Expected_Alpha_Net_FR:.6f} *
FEES : TAKER: {0.0002:.2%}; Expected Alpha w Taker = {Expected_Alpha_Net_FR-0.0002:.6f} [w/o FR: {Expected_Alpha_w_Taker:.6f}]
FEES : TAKER: {0.00025:.6%}; Expected Alpha w Taker = {Expected_Alpha_Net_FR_w_Taker:.6f} [w/o FR: {Expected_Alpha_w_Taker:.6f}]
HEDGE: {Hedge_Ratio:.2f}% <= {1:.2f}%: {Currently_Hedged} [{EXTEND_NOTIONAL_POSITION:.2f} / {ASTER_NOTIONAL_POSITION:.2f}]
MKT : Aster: {ASTER.symbol} (best: {best_symbol_by_exchange_aster.symbol}) | Extend: {ASTER.symbol} (best: {best_symbol_by_exchange_extend.symbol})
@@ -674,11 +734,16 @@ async def run_algo():
### ROUTES ###
# Just_Rejected_Or_Expired
# MIN_EXPECTED_ALPHA_TO_TRADE = 0.0001
MIN_EXPECTED_ALPHA_TO_TRADE = abs(NEXT_NET_FUNDING_RATE)*-1
MIN_EXPECTED_ALPHA_TO_TRADE = 0.0000
if ALGO_CONFIG.Overrides.Flatten_Open_Positions_Opportunistic:
exp_alpha = Expected_Alpha_w_Taker
else:
exp_alpha = Expected_Alpha_Net_FR_w_Taker
# MIN_EXPECTED_ALPHA_TO_TRADE = abs(NEXT_NET_FUNDING_RATE)*-1
# MIN_EXPECTED_ALPHA_TO_TRADE = -0.000001
# ALPHA RATIO CHECK
if not( ( Expected_Alpha_Net_FR_w_Taker > MIN_EXPECTED_ALPHA_TO_TRADE ) or ( ASTER_OPEN_ORDERS or EXTEND_OPEN_ORDERS or ALGO_CONFIG.Overrides.Flatten_Open_Positions) ) and Currently_Hedged:
if not( ( exp_alpha > MIN_EXPECTED_ALPHA_TO_TRADE ) or ( ASTER_OPEN_ORDERS or EXTEND_OPEN_ORDERS or ALGO_CONFIG.Overrides.Flatten_Open_Positions) ) and Currently_Hedged:
# if not( ( Expected_Alpha_Net_FR_w_Taker > MIN_EXPECTED_ALPHA_TO_TRADE ) or ( ASTER_OPEN_ORDERS or EXTEND_OPEN_ORDERS or ALGO_CONFIG.Overrides.Flatten_Open_Positions) ) and Currently_Hedged:
if ALGO_CONFIG.Logging.Print_Summary_Each_Loop:
print(f'Alpha Ratio too low ({ALPHA_RATIO:.8f}) and no Open Orders...')
elif ( Expected_Alpha_Net_FR_w_Taker <= MIN_EXPECTED_ALPHA_TO_TRADE ) and ( ASTER_OPEN_ORDERS or EXTEND_OPEN_ORDERS ) and Currently_Hedged and not(ALGO_CONFIG.Overrides.Flatten_Open_Positions):
@@ -732,14 +797,18 @@ async def run_algo():
if ASTER_TGT_TAIL_BASE_QTY == 0.00:
place_order = False
logging.info('ASTER TRYNG TO ORDER 0.00 BASE QTY, SKIPPING')
if place_order:
min_price = ASTER.min_price
min_price = int(min_price) if min_price == int(min_price) else min_price
price: Decimal = Decimal(str(price)).quantize(Decimal(str(min_price)), rounding=ROUND_HALF_UP)
if price == Decimal(str(0.00)).quantize(Decimal(str(min_price)), rounding=ROUND_HALF_UP):
logging.info('ASTER TRYNG TO ORDER with A PRICE OF 0.00, SKIPPING')
continue
if qty >= ASTER.min_order_size:
if qty >= ASTER.min_order_size and (qty*price) > ASTER.min_notional:
reduceOnly = False
else:
reduceOnly = True
@@ -763,7 +832,7 @@ async def run_algo():
order_resp['order_status'] = order_resp['status']
ASTER_OPEN_ORDERS.append(order_resp)
Just_Rejected_Or_Expired = False
utils.send_tg_alert(f'FR_ALGO - ASTER Order ({order_resp['orderId']}). Start_$: {ASTER_NOTIONAL_POSITION:.2f}; Value: {float(ASTER_TGT_TAIL_BASE_QTY)*float(price):.2f}; Price: {float(price):.2f}')
utils.send_tg_alert(f'FR_ALGO - ASTER Order ({order_resp['orderId']}). Start_$: {ASTER_NOTIONAL_POSITION:.4f}; Value: {float(ASTER_TGT_TAIL_BASE_QTY)*float(price):.4f}; Price: {float(price):.4f}')
logging.info(f'ASTER ORDER PLACED SUCCESS: {order_resp}')
print_summary(use_logging=True)
else:
@@ -777,13 +846,22 @@ async def run_algo():
### Add code to flatten small balances
logging.info('ASTER HAS NO TAIL BUT OPEN ORDERS - CANCELLING OPEN ORDERS')
await aster_cancel_all_orders()
time.sleep(0.1)
# if (float(ALPHA_TGT_NOTIONAL) < float(EXTEND_NOTIONAL_POSITION)) and ((float(EXTEND_NOTIONAL_POSITION) + float(EXTEND_TGT_TAIL)) > float(EXTEND_NOTIONAL_POSITION)):
# EXTEND_TGT_TAIL_ORDERABLE= False
# print('ASTER ordering in the wrong directiion - Should be selling, but its buying - skipping')
# elif (float(ALPHA_TGT_NOTIONAL) > float(EXTEND_NOTIONAL_POSITION)) and ((float(EXTEND_NOTIONAL_POSITION) + float(EXTEND_TGT_TAIL)) < float(EXTEND_NOTIONAL_POSITION)):
# EXTEND_TGT_TAIL_ORDERABLE= False
# print('ASTER ordering in the wrong directiion - Should be buying, but its selling - skipping')
# EXTEND
if EXTEND_TGT_TAIL_ORDERABLE and ALGO_CONFIG.Overrides.Allow_Ordering_Extend:
if (EXTEND_TGT_TAIL_ORDERABLE and ALGO_CONFIG.Overrides.Allow_Ordering_Extend):
# if ALGO_CONFIG.Overrides.Allow_Ordering_Extend:
side = OrderSide.BUY if EXTEND_TGT_TAIL_BASE_QTY > 0.00 else OrderSide.SELL
symbol = EXTEND.symbol
qty = Decimal(value=str(abs(EXTEND_TGT_TAIL_BASE_QTY)))
Time_Since_Last_Aster_Fill_ms = ( datetime.now().timestamp()*1000 ) - Last_Aster_Fill_Time_Ts
min_price = EXTEND.min_price
min_price = int(min_price) if min_price == int(min_price) else min_price
@@ -791,10 +869,10 @@ async def run_algo():
post_only = False
price: Decimal = Decimal(value=str(EXTEND_TOB_PX - ( float(min_price)*int(ALGO_CONFIG.Config.Price_Worsener_Extend) ) if side == 'BUY' else EXTEND_TOB_PX + ( float(min_price)*int(ALGO_CONFIG.Config.Price_Worsener_Extend) ) )).quantize(Decimal(str(min_price)), rounding=ROUND_HALF_UP)
else:
post_only = True
# post_only = True
post_only = False
price: Decimal = Decimal(value=str(EXTEND_TOB_PX)).quantize(Decimal(str(min_price)), rounding=ROUND_HALF_UP)
if abs( ( float(EXTEND_TGT_TAIL_BASE_QTY)*float(price) ) + EXTEND_NOTIONAL_POSITION ) > ALGO_CONFIG.Config.Max_Target_Notional*ALGO_CONFIG.Config.Max_Order_Over_Notional_Ratio:
logging.info(f'TRYING TO ORDER OVER MAX NOTIOANL - EXTEND: {EXTEND_NOTIONAL_POSITION:.2f} + {float(EXTEND_TGT_TAIL_BASE_QTY)*float(price):.2f} (qty: {float(EXTEND_TGT_TAIL_BASE_QTY):.2f}; px: {float(price):.2f})')
await kill_algo()
@@ -802,28 +880,19 @@ async def run_algo():
open_order_dict = dict(EXTEND_OPEN_ORDERS[0])
open_order_id = str(open_order_dict['external_id'])
open_order_px = float(open_order_dict['price'])
open_order_filled_qty = float(open_order_dict['filled_qty'])
if qty >= EXTEND.min_order_size:
place_order = True
place_residual_order = False
else:
if int(qty) == 0:
place_order = False
place_residual_order = False
logging.info(f'EXTEND NOT ORDERING DUE TO NOTIONAL QTY == 0; Filled: {float(open_order_filled_qty):.4f}; Residual: {qty:.4f}')
else:
place_order = True
place_residual_order = True
logging.info(f'Ordering RESIDUAL market order for remaining small amount: {qty}')
# if int(qty) == 0:
# place_order = False
# place_residual_order = False
# logging.info(f'EXTEND NOT ORDERING DUE TO NOTIONAL QTY == 0; Filled: {float(open_order_filled_qty):.4f}; Residual: {qty:.4f}')
# else:
# place_order = True
# place_residual_order = False
# logging.info(f'Ordering RESIDUAL market order for remaining small amount: {qty}')
else:
open_order_id = None
open_order_px = 0
place_order = True
if qty >= EXTEND.min_order_size:
place_residual_order = False
else:
place_residual_order = True
if place_order:
price: Decimal = Decimal(str(price)).quantize(Decimal(str(min_price)), rounding=ROUND_HALF_UP)
if round(open_order_px - float(price), len(str(min_price)) - 2 ) == 0.00:
@@ -831,14 +900,13 @@ async def run_algo():
print('EXTEND OPEN ORDER NO PX CHG; SKIPPING')
else:
try:
if place_residual_order:
post_only = False
if abs(float(EXTEND_NOTIONAL_POSITION) + (float(qty)*float(price))) < abs(float(EXTEND_NOTIONAL_POSITION)):
reduce_only = True
else:
# post_only = SEE ABOVE
reduce_only = False
taker_fee = taker_fee=Decimal("0.00000") if post_only else Decimal("0.00025")
# taker_fee = taker_fee=Decimal("0.00000") if post_only else Decimal("0.00025")
taker_fee = Decimal("0.00025")
order_resp: WrappedApiResponse[PlacedOrderModel] = await EXTEND_CLIENT.place_order(
market_name=symbol,
amount_of_synthetic=Decimal(str(qty)),
@@ -846,14 +914,14 @@ async def run_algo():
side=side,
taker_fee=taker_fee,
previous_order_id=open_order_id,
post_only=post_only
# reduce_only=reduce_only
post_only=post_only,
reduce_only=reduce_only
)
except Exception as e:
logging.error(f'EXTEND ORDER PLACEMENT FAILED - RESP: {order_resp}')
logging.error(f'EXTEND ORDER PLACEMENT FAILED: {e}')
logging.error(f'EXTEND ORDER PLACEMENT FAILED - POSTED: market_name:{symbol}, amount_of_synthetic:{qty}, price:{price}, side:{side},taker_fee:{taker_fee}, previous_order_id:{open_order_id}, post_only:{post_only}; reduce_only:{reduce_only}')
logging.error(f'EXTEND ORDER PLACEMENT FAILED - POSTED: market_name:{symbol}, side: {side} amount_of_synthetic:{qty}, price:{price}, side:{side},taker_fee:{taker_fee}, previous_order_id:{open_order_id}, post_only:{post_only}; reduce_only:{reduce_only}')
logging.error(traceback.format_exc())
logging.error(f'EXTEND ORDER PLACEMENT FAILED - RESP: {order_resp}')
order_resp_dict = dict(order_resp)
@@ -888,7 +956,8 @@ async def run_algo():
continue
else:
time.sleep(ALGO_CONFIG.Config.Loop_Sleep_Sec)
# print(f'_____ End No Open Orders _____ (Algo Engine ms: {(time.time() - loop_start)*1000:.2f}); Sleeping for sec: {ALGO_CONFIG.Config.Loop_Sleep_Sec:.0f}')
if ALGO_CONFIG.Logging.Print_Summary_Each_Loop:
print(f'_____ End No Open Orders _____ (Algo Engine ms: {(time.time() - loop_start)*1000:.2f}); Sleeping for sec: {ALGO_CONFIG.Config.Loop_Sleep_Sec:.0f}')
except KeyboardInterrupt:
logging.info('CANCELLING OPEN ORDERS')
@@ -916,23 +985,50 @@ async def main():
VAL_KEY = valkey.Valkey(host='localhost', port=6379, db=0, decode_responses=True)
engine = create_async_engine('mysql+asyncmy://root:pwd@localhost/fund_rate')
best_symbol_by_exchange: dict = json.loads(s=VAL_KEY.get(name='fr_engine_best_fund_rate_output')) # ty:ignore[invalid-argument-type]
ASTER = structs.Perpetual_Exchange(**best_symbol_by_exchange['ASTER'])
EXTEND = structs.Perpetual_Exchange(**best_symbol_by_exchange['EXTEND'])
await set_comb_open_symbols()
best_symbol_by_exchange: dict = json.loads(s=VAL_KEY.get(name='fr_engine_best_fund_rate_output')) # ty:ignore[invalid-argument-type]
if Open_Symbols:
logging.info(f'OPEN SYMBOLS TO CLOSE: {Open_Symbols}')
await get_aster_exch_info(symbol_override=Open_Symbols[0])
await get_extend_exch_info(symbol_override=Open_Symbols[0])
logging.info(f'OPEN SYMBOLS: {Open_Symbols}')
master_data = json.loads(s=VAL_KEY.get(name='fr_engine_best_fund_rate_master')) # ty:ignore[invalid-argument-type]
open_symbol_to_work = Open_Symbols[0]
current_pos_master_ast = [d for d in master_data if d.get('symbol_ext') == open_symbol_to_work][0]
ASTER = structs.Perpetual_Exchange(
mult = int(current_pos_master_ast['max_leverage_ast']),
lh_asset = current_pos_master_ast['lh_asset_ast'],
rh_asset = current_pos_master_ast['rh_asset_ast'],
symbol_asset_separator = '',
initial_funding_rate=float(current_pos_master_ast['funding_rate_ast']),
min_price=float(current_pos_master_ast['min_price_ast']),
min_order_size=float(current_pos_master_ast['min_order_size_ast']),
min_lot_size=float(current_pos_master_ast['min_lot_size_ast']),
min_notional=float(current_pos_master_ast['min_notional_ast']),
)
EXTEND = structs.Perpetual_Exchange(
mult = int(current_pos_master_ast['max_leverage_ext']),
lh_asset = current_pos_master_ast['lh_asset_ext'],
rh_asset = current_pos_master_ast['rh_asset_ext'],
symbol_asset_separator = '-',
initial_funding_rate=float(current_pos_master_ast['funding_rate_ext']),
min_price=float(current_pos_master_ast['min_price_ext']),
min_order_size=float(current_pos_master_ast['min_order_size_ext']),
min_lot_size=float(current_pos_master_ast['min_lot_size_ext']),
min_notional=float(current_pos_master_ast['min_notional_ext']),
)
Open_Symbols.pop(0)
else:
ASTER = structs.Perpetual_Exchange(**best_symbol_by_exchange['ASTER'])
EXTEND = structs.Perpetual_Exchange(**best_symbol_by_exchange['EXTEND'])
# await get_aster_exch_info(symbol_override=Open_Symbols[0])
# await get_extend_exch_info(symbol_override=Open_Symbols[0])
with open('algo_config.json', mode='r', encoding='utf-8') as file:
ALGO_CONFIG = json.load(file)
ALGO_CONFIG = structs.Algo_Config(**ALGO_CONFIG)
ALGO_CONFIG.Config.Max_Target_Notional = float(min([ASTER.mult, EXTEND.mult]) * ALGO_CONFIG.Config.Target_Open_Cash_Position)
# logging.info(f'Initial Algo Config: {ALGO_CONFIG}')
VAL_KEY.set(name='fr_orchestrator_output', value=json.dumps(obj=ALGO_CONFIG.model_dump()))
VAL_KEY.set(name='fr_algo_working_symbol', value=json.dumps(obj={'ASTER': asdict(obj=ASTER), 'EXTEND': asdict(obj=EXTEND)}))

View File

@@ -2,6 +2,7 @@ import requests
from dotenv import load_dotenv
import os
import time
import logging
import threading
from urllib import parse
@@ -90,7 +91,7 @@ async def post_authenticated_url(req: dict) -> list | dict:
if method == 'GET':
res: requests.Response = requests.get(url=full_url, headers=headers)
# print(res.status_code, res.text)
# logging.warning(res.status_code, res.text)
return res.json()
elif method == 'POST':
res: requests.Response = requests.post(url=full_url, headers=headers)

View File

@@ -28,8 +28,8 @@ LEVERAGE_BY_EXCH: list[Asset_Leverage] = [
Asset_Leverage('ASTER', 'SUI' , 'USDT', 75 , 5_416 ), Asset_Leverage('EXTEND', 'SUI' , 'USD', 50, 500_000 ),
Asset_Leverage('ASTER', 'TRUMP', 'USDT', 10 , 60_000 ), Asset_Leverage('EXTEND', 'TRUMP', 'USD', 25, 400_000 ),
Asset_Leverage('ASTER', 'WLFI' , 'USDT', 25 , 104_869), Asset_Leverage('EXTEND', 'WLFI' , 'USD', 10, 250_000 ),
Asset_Leverage('ASTER', 'XAG' , 'USDT', 100, 50_000 ), Asset_Leverage('EXTEND', 'XAG' , 'USD', 10, 1_000_000),
Asset_Leverage('ASTER', 'XAU' , 'USDT', 75 , 2_500 ), Asset_Leverage('EXTEND', 'XAU' , 'USD', 25, 2_000_000),
# Asset_Leverage('ASTER', 'XAG' , 'USDT', 100, 50_000 ), Asset_Leverage('EXTEND', 'XAG' , 'USD', 10, 1_000_000),
# Asset_Leverage('ASTER', 'XAU' , 'USDT', 75 , 2_500 ), Asset_Leverage('EXTEND', 'XAU' , 'USD', 25, 2_000_000),
Asset_Leverage('ASTER', 'XMR' , 'USDT', 50 , 10_000 ), Asset_Leverage('EXTEND', 'XMR' , 'USD', 25, 400_000 ),
Asset_Leverage('ASTER', 'XPT' , 'USDT', 3 , 30_000 ), Asset_Leverage('EXTEND', 'XPT' , 'USD', 5 , 1_000_000),
Asset_Leverage('ASTER', 'XRP' , 'USDT', 100, 40_000 ), Asset_Leverage('EXTEND', 'XRP' , 'USD', 50, 500_000 ),

View File

@@ -169,6 +169,8 @@ class Perpetual_Exchange:
initial_funding_rate: float = 0
min_price: float = 0
min_order_size: float = 0
min_lot_size: float = 0
min_notional: float = 0
# async def update(self):
# await self.Collateral_Updates.update()

619
pnl.ipynb
View File

@@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
"execution_count": 109,
"execution_count": 1,
"id": "44ff5c50",
"metadata": {},
"outputs": [],
@@ -33,7 +33,38 @@
},
{
"cell_type": "code",
"execution_count": 111,
"execution_count": 19,
"id": "d3206fe9",
"metadata": {},
"outputs": [],
"source": [
"start_ts = (round(datetime.now().timestamp()*1000)-(60*60*24*1000))"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "9847869c",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"Timestamp('2026-05-01 22:56:30.744000')"
]
},
"execution_count": 20,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"pd.to_datetime(start_ts, unit='ms')"
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "ca48e11c",
"metadata": {},
"outputs": [
@@ -57,7 +88,7 @@
},
{
"cell_type": "code",
"execution_count": 112,
"execution_count": 22,
"id": "ec8f5d67",
"metadata": {},
"outputs": [],
@@ -82,7 +113,7 @@
},
{
"cell_type": "code",
"execution_count": 113,
"execution_count": 23,
"id": "1cb4869a",
"metadata": {},
"outputs": [],
@@ -108,7 +139,7 @@
},
{
"cell_type": "code",
"execution_count": 119,
"execution_count": 24,
"id": "0ebf54b3",
"metadata": {},
"outputs": [],
@@ -140,7 +171,7 @@
},
{
"cell_type": "code",
"execution_count": 120,
"execution_count": 25,
"metadata": {},
"outputs": [
{
@@ -268,194 +299,168 @@
"type": "float"
}
],
"ref": "ba489ddb-f098-469d-8584-5f1564160359",
"ref": "4fed1e21-7dd2-454b-b923-48d479d7aa72",
"rows": [
[
"0",
"17371700930",
"2026-04-30 08:46:33.100000",
"328652716",
"2026-05-02 01:00:01.450000",
"FILLED",
"BUY",
"0.441",
"551.0",
"0.0",
"2260.01",
"0.9059",
"True",
"2049778331801128960",
"2026-04-30 09:10:12.149000",
"2050627894673694720",
"2026-05-02 17:26:03.721000",
"FILLED",
"SELL",
"0.441",
"0.24869",
"2255.7",
"273.0",
"0.06208",
"0.8957",
"False",
"2260.01",
"2255.7",
"0.441",
"0.441",
"0.9059",
"0.8957",
"551.0",
"273.0",
"ASTER",
"-2.1494000000001763",
"-0.0019070712076496412"
"-2.8466799999999965",
"-0.5101158787853532"
],
[
"1",
"17372582002",
"2026-04-30 13:59:05",
"329066650",
"2026-05-02 17:25:57.700000",
"FILLED",
"SELL",
"0.439",
"0.0",
"2262.5",
"True",
"2049851052585218048",
"2026-04-30 13:59:15.023000",
"277.0",
"0.1007726",
"0.9095",
"False",
"2050627894673694720",
"2026-05-02 17:26:03.721000",
"FILLED",
"BUY",
"0.438",
"0.0",
"2261.7",
"True",
"2261.7",
"2262.5",
"0.438",
"0.439",
"SELL",
"273.0",
"0.06208",
"0.8957",
"False",
"0.8957",
"0.9095",
"273.0",
"277.0",
"EXTEND",
"0.35120000000007984",
"0.0026376288252886937"
"3.659747399999979",
"0.030284701714868006"
],
[
"2",
"17372999630",
"2026-04-30 15:32:12.700000",
"FILLED",
"BUY",
"0.438",
"0.0",
"2274.09",
"True",
"2049881470860271616",
"2026-04-30 16:00:02.415000",
"329183551",
"2026-05-02 21:42:27.900000",
"FILLED",
"SELL",
"0.438",
"0.247886",
"2263.7",
"273.0",
"0.0",
"0.9126",
"True",
"2050694154048565248",
"2026-05-02 21:49:21.186000",
"FILLED",
"BUY",
"273.0",
"0.062319",
"0.9131",
"False",
"2274.09",
"2263.7",
"0.438",
"0.438",
"ASTER",
"-4.798706000000144",
"-0.00456886051123762"
"0.9131",
"0.9126",
"273.0",
"273.0",
"EXTEND",
"-0.1988190000000153",
"-0.0005475851494907951"
],
[
"3",
"17373094474",
"2026-04-30 16:00:02.050000",
"329195229",
"2026-05-02 22:08:49.450000",
"FILLED",
"SELL",
"0.439",
"BUY",
"273.0",
"0.0",
"2264.61",
"0.9136",
"True",
"2049881470860271616",
"2026-04-30 16:00:02.415000",
"2050699055663546368",
"2026-05-02 22:08:49.823000",
"FILLED",
"SELL",
"0.438",
"0.247886",
"2263.7",
"272.0",
"0.062056",
"0.9123",
"False",
"2263.7",
"2264.61",
"0.438",
"0.439",
"EXTEND",
"0.15160400000013574",
"0.002686019554602488"
"0.9136",
"0.9123",
"273.0",
"272.0",
"ASTER",
"-0.41565599999999125",
"-0.005080733627143444"
],
[
"4",
"17373547036",
"2026-04-30 19:06:50.200000",
"FILLED",
"BUY",
"0.442",
"0.0",
"2260.48",
"True",
"2049929626042335232",
"2026-04-30 19:11:23.505000",
"329206619",
"2026-05-02 22:33:28.550000",
"FILLED",
"SELL",
"0.441",
"0.24944",
"2262.5",
"False",
"2260.48",
"2262.5",
"0.442",
"0.441",
"ASTER",
"0.641379999999992",
"-0.0013708496781846692"
"274.0",
"0.0",
"0.9106",
"True",
"2050705268467499008",
"2026-05-02 22:33:52.752000",
"FILLED",
"BUY",
"274.0",
"0.0",
"0.9105",
"True",
"0.9105",
"0.9106",
"274.0",
"274.0",
"EXTEND",
"0.027399999999996982",
"0.00010982976386600805"
],
[
"5",
"17373827670",
"2026-04-30 21:48:38.550000",
"329210143",
"2026-05-02 22:41:58.300000",
"FILLED",
"SELL",
"0.221",
"274.0",
"0.0",
"2257.17",
"0.912",
"True",
"2049969423813185536",
"2026-04-30 21:49:32.034000",
"2050707405608058880",
"2026-05-02 22:42:05.138000",
"FILLED",
"BUY",
"0.22",
"0.124041",
"2255.4",
"False",
"2255.4",
"2257.17",
"0.22",
"0.221",
"EXTEND",
"0.26712899999999595",
"0.0053338049287769895"
],
[
"6",
"17373941409",
"2026-04-30 23:01:21.150000",
"FILLED",
"SELL",
"0.221",
"274.0",
"0.0",
"2253.58",
"0.9117",
"True",
"2049987504752680960",
"2026-04-30 23:03:53.771000",
"FILLED",
"BUY",
"0.221",
"0.0",
"2252.6",
"True",
"2252.6",
"2253.58",
"0.221",
"0.221",
"0.9117",
"0.912",
"274.0",
"274.0",
"EXTEND",
"0.21658000000000402",
"0.0004350528278434247"
"0.08220000000002137",
"0.0003290556103982722"
]
],
"shape": {
"columns": 23,
"rows": 7
"rows": 6
}
},
"text/html": [
@@ -503,227 +508,198 @@
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>17371700930</td>\n",
" <td>2026-04-30 08:46:33.100</td>\n",
" <td>328652716</td>\n",
" <td>2026-05-02 01:00:01.450</td>\n",
" <td>FILLED</td>\n",
" <td>BUY</td>\n",
" <td>0.441</td>\n",
" <td>0.0</td>\n",
" <td>2260.01</td>\n",
" <td>551.0</td>\n",
" <td>0.000000</td>\n",
" <td>0.9059</td>\n",
" <td>True</td>\n",
" <td>2049778331801128960</td>\n",
" <td>2026-04-30 09:10:12.149</td>\n",
" <td>2050627894673694720</td>\n",
" <td>2026-05-02 17:26:03.721</td>\n",
" <td>...</td>\n",
" <td>0.248690</td>\n",
" <td>2255.7</td>\n",
" <td>0.062080</td>\n",
" <td>0.8957</td>\n",
" <td>False</td>\n",
" <td>2260.01</td>\n",
" <td>2255.70</td>\n",
" <td>0.441</td>\n",
" <td>0.441</td>\n",
" <td>0.9059</td>\n",
" <td>0.8957</td>\n",
" <td>551.0</td>\n",
" <td>273.0</td>\n",
" <td>ASTER</td>\n",
" <td>-2.149400</td>\n",
" <td>-0.001907</td>\n",
" <td>-2.846680</td>\n",
" <td>-0.510116</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>17372582002</td>\n",
" <td>2026-04-30 13:59:05.000</td>\n",
" <td>329066650</td>\n",
" <td>2026-05-02 17:25:57.700</td>\n",
" <td>FILLED</td>\n",
" <td>SELL</td>\n",
" <td>0.439</td>\n",
" <td>0.0</td>\n",
" <td>2262.50</td>\n",
" <td>True</td>\n",
" <td>2049851052585218048</td>\n",
" <td>2026-04-30 13:59:15.023</td>\n",
" <td>277.0</td>\n",
" <td>0.100773</td>\n",
" <td>0.9095</td>\n",
" <td>False</td>\n",
" <td>2050627894673694720</td>\n",
" <td>2026-05-02 17:26:03.721</td>\n",
" <td>...</td>\n",
" <td>0.000000</td>\n",
" <td>2261.7</td>\n",
" <td>True</td>\n",
" <td>2261.70</td>\n",
" <td>2262.50</td>\n",
" <td>0.438</td>\n",
" <td>0.439</td>\n",
" <td>0.062080</td>\n",
" <td>0.8957</td>\n",
" <td>False</td>\n",
" <td>0.8957</td>\n",
" <td>0.9095</td>\n",
" <td>273.0</td>\n",
" <td>277.0</td>\n",
" <td>EXTEND</td>\n",
" <td>0.351200</td>\n",
" <td>0.002638</td>\n",
" <td>3.659747</td>\n",
" <td>0.030285</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>17372999630</td>\n",
" <td>2026-04-30 15:32:12.700</td>\n",
" <td>329183551</td>\n",
" <td>2026-05-02 21:42:27.900</td>\n",
" <td>FILLED</td>\n",
" <td>BUY</td>\n",
" <td>0.438</td>\n",
" <td>0.0</td>\n",
" <td>2274.09</td>\n",
" <td>SELL</td>\n",
" <td>273.0</td>\n",
" <td>0.000000</td>\n",
" <td>0.9126</td>\n",
" <td>True</td>\n",
" <td>2049881470860271616</td>\n",
" <td>2026-04-30 16:00:02.415</td>\n",
" <td>2050694154048565248</td>\n",
" <td>2026-05-02 21:49:21.186</td>\n",
" <td>...</td>\n",
" <td>0.247886</td>\n",
" <td>2263.7</td>\n",
" <td>0.062319</td>\n",
" <td>0.9131</td>\n",
" <td>False</td>\n",
" <td>2274.09</td>\n",
" <td>2263.70</td>\n",
" <td>0.438</td>\n",
" <td>0.438</td>\n",
" <td>ASTER</td>\n",
" <td>-4.798706</td>\n",
" <td>-0.004569</td>\n",
" <td>0.9131</td>\n",
" <td>0.9126</td>\n",
" <td>273.0</td>\n",
" <td>273.0</td>\n",
" <td>EXTEND</td>\n",
" <td>-0.198819</td>\n",
" <td>-0.000548</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>17373094474</td>\n",
" <td>2026-04-30 16:00:02.050</td>\n",
" <td>329195229</td>\n",
" <td>2026-05-02 22:08:49.450</td>\n",
" <td>FILLED</td>\n",
" <td>SELL</td>\n",
" <td>0.439</td>\n",
" <td>0.0</td>\n",
" <td>2264.61</td>\n",
" <td>BUY</td>\n",
" <td>273.0</td>\n",
" <td>0.000000</td>\n",
" <td>0.9136</td>\n",
" <td>True</td>\n",
" <td>2049881470860271616</td>\n",
" <td>2026-04-30 16:00:02.415</td>\n",
" <td>2050699055663546368</td>\n",
" <td>2026-05-02 22:08:49.823</td>\n",
" <td>...</td>\n",
" <td>0.247886</td>\n",
" <td>2263.7</td>\n",
" <td>0.062056</td>\n",
" <td>0.9123</td>\n",
" <td>False</td>\n",
" <td>2263.70</td>\n",
" <td>2264.61</td>\n",
" <td>0.438</td>\n",
" <td>0.439</td>\n",
" <td>EXTEND</td>\n",
" <td>0.151604</td>\n",
" <td>0.002686</td>\n",
" <td>0.9136</td>\n",
" <td>0.9123</td>\n",
" <td>273.0</td>\n",
" <td>272.0</td>\n",
" <td>ASTER</td>\n",
" <td>-0.415656</td>\n",
" <td>-0.005081</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>17373547036</td>\n",
" <td>2026-04-30 19:06:50.200</td>\n",
" <td>329206619</td>\n",
" <td>2026-05-02 22:33:28.550</td>\n",
" <td>FILLED</td>\n",
" <td>BUY</td>\n",
" <td>0.442</td>\n",
" <td>0.0</td>\n",
" <td>2260.48</td>\n",
" <td>SELL</td>\n",
" <td>274.0</td>\n",
" <td>0.000000</td>\n",
" <td>0.9106</td>\n",
" <td>True</td>\n",
" <td>2049929626042335232</td>\n",
" <td>2026-04-30 19:11:23.505</td>\n",
" <td>2050705268467499008</td>\n",
" <td>2026-05-02 22:33:52.752</td>\n",
" <td>...</td>\n",
" <td>0.249440</td>\n",
" <td>2262.5</td>\n",
" <td>False</td>\n",
" <td>2260.48</td>\n",
" <td>2262.50</td>\n",
" <td>0.442</td>\n",
" <td>0.441</td>\n",
" <td>ASTER</td>\n",
" <td>0.641380</td>\n",
" <td>-0.001371</td>\n",
" <td>0.000000</td>\n",
" <td>0.9105</td>\n",
" <td>True</td>\n",
" <td>0.9105</td>\n",
" <td>0.9106</td>\n",
" <td>274.0</td>\n",
" <td>274.0</td>\n",
" <td>EXTEND</td>\n",
" <td>0.027400</td>\n",
" <td>0.000110</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>17373827670</td>\n",
" <td>2026-04-30 21:48:38.550</td>\n",
" <td>329210143</td>\n",
" <td>2026-05-02 22:41:58.300</td>\n",
" <td>FILLED</td>\n",
" <td>SELL</td>\n",
" <td>0.221</td>\n",
" <td>0.0</td>\n",
" <td>2257.17</td>\n",
" <td>274.0</td>\n",
" <td>0.000000</td>\n",
" <td>0.9120</td>\n",
" <td>True</td>\n",
" <td>2049969423813185536</td>\n",
" <td>2026-04-30 21:49:32.034</td>\n",
" <td>...</td>\n",
" <td>0.124041</td>\n",
" <td>2255.4</td>\n",
" <td>False</td>\n",
" <td>2255.40</td>\n",
" <td>2257.17</td>\n",
" <td>0.220</td>\n",
" <td>0.221</td>\n",
" <td>EXTEND</td>\n",
" <td>0.267129</td>\n",
" <td>0.005334</td>\n",
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>17373941409</td>\n",
" <td>2026-04-30 23:01:21.150</td>\n",
" <td>FILLED</td>\n",
" <td>SELL</td>\n",
" <td>0.221</td>\n",
" <td>0.0</td>\n",
" <td>2253.58</td>\n",
" <td>True</td>\n",
" <td>2049987504752680960</td>\n",
" <td>2026-04-30 23:03:53.771</td>\n",
" <td>2050707405608058880</td>\n",
" <td>2026-05-02 22:42:05.138</td>\n",
" <td>...</td>\n",
" <td>0.000000</td>\n",
" <td>2252.6</td>\n",
" <td>0.9117</td>\n",
" <td>True</td>\n",
" <td>2252.60</td>\n",
" <td>2253.58</td>\n",
" <td>0.221</td>\n",
" <td>0.221</td>\n",
" <td>0.9117</td>\n",
" <td>0.9120</td>\n",
" <td>274.0</td>\n",
" <td>274.0</td>\n",
" <td>EXTEND</td>\n",
" <td>0.216580</td>\n",
" <td>0.000435</td>\n",
" <td>0.082200</td>\n",
" <td>0.000329</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>7 rows × 23 columns</p>\n",
"<p>6 rows × 23 columns</p>\n",
"</div>"
],
"text/plain": [
" order_id_ast timestamp_ts_ast status_ast side_ast filled_qty_ast \\\n",
"0 17371700930 2026-04-30 08:46:33.100 FILLED BUY 0.441 \n",
"1 17372582002 2026-04-30 13:59:05.000 FILLED SELL 0.439 \n",
"2 17372999630 2026-04-30 15:32:12.700 FILLED BUY 0.438 \n",
"3 17373094474 2026-04-30 16:00:02.050 FILLED SELL 0.439 \n",
"4 17373547036 2026-04-30 19:06:50.200 FILLED BUY 0.442 \n",
"5 17373827670 2026-04-30 21:48:38.550 FILLED SELL 0.221 \n",
"6 17373941409 2026-04-30 23:01:21.150 FILLED SELL 0.221 \n",
"0 328652716 2026-05-02 01:00:01.450 FILLED BUY 551.0 \n",
"1 329066650 2026-05-02 17:25:57.700 FILLED SELL 277.0 \n",
"2 329183551 2026-05-02 21:42:27.900 FILLED SELL 273.0 \n",
"3 329195229 2026-05-02 22:08:49.450 FILLED BUY 273.0 \n",
"4 329206619 2026-05-02 22:33:28.550 FILLED SELL 274.0 \n",
"5 329210143 2026-05-02 22:41:58.300 FILLED SELL 274.0 \n",
"\n",
" payed_fee_ast price_ast is_mkt_maker_ast order_id_ext \\\n",
"0 0.0 2260.01 True 2049778331801128960 \n",
"1 0.0 2262.50 True 2049851052585218048 \n",
"2 0.0 2274.09 True 2049881470860271616 \n",
"3 0.0 2264.61 True 2049881470860271616 \n",
"4 0.0 2260.48 True 2049929626042335232 \n",
"5 0.0 2257.17 True 2049969423813185536 \n",
"6 0.0 2253.58 True 2049987504752680960 \n",
"0 0.000000 0.9059 True 2050627894673694720 \n",
"1 0.100773 0.9095 False 2050627894673694720 \n",
"2 0.000000 0.9126 True 2050694154048565248 \n",
"3 0.000000 0.9136 True 2050699055663546368 \n",
"4 0.000000 0.9106 True 2050705268467499008 \n",
"5 0.000000 0.9120 True 2050707405608058880 \n",
"\n",
" timestamp_ts_ext ... payed_fee_ext price_ext is_mkt_maker_ext \\\n",
"0 2026-04-30 09:10:12.149 ... 0.248690 2255.7 False \n",
"1 2026-04-30 13:59:15.023 ... 0.000000 2261.7 True \n",
"2 2026-04-30 16:00:02.415 ... 0.247886 2263.7 False \n",
"3 2026-04-30 16:00:02.415 ... 0.247886 2263.7 False \n",
"4 2026-04-30 19:11:23.505 ... 0.249440 2262.5 False \n",
"5 2026-04-30 21:49:32.034 ... 0.124041 2255.4 False \n",
"6 2026-04-30 23:03:53.771 ... 0.000000 2252.6 True \n",
"0 2026-05-02 17:26:03.721 ... 0.062080 0.8957 False \n",
"1 2026-05-02 17:26:03.721 ... 0.062080 0.8957 False \n",
"2 2026-05-02 21:49:21.186 ... 0.062319 0.9131 False \n",
"3 2026-05-02 22:08:49.823 ... 0.062056 0.9123 False \n",
"4 2026-05-02 22:33:52.752 ... 0.000000 0.9105 True \n",
"5 2026-05-02 22:42:05.138 ... 0.000000 0.9117 True \n",
"\n",
" buy_price sell_price buy_qty sell_qty buy_side per_trade_pnl \\\n",
"0 2260.01 2255.70 0.441 0.441 ASTER -2.149400 \n",
"1 2261.70 2262.50 0.438 0.439 EXTEND 0.351200 \n",
"2 2274.09 2263.70 0.438 0.438 ASTER -4.798706 \n",
"3 2263.70 2264.61 0.438 0.439 EXTEND 0.151604 \n",
"4 2260.48 2262.50 0.442 0.441 ASTER 0.641380 \n",
"5 2255.40 2257.17 0.220 0.221 EXTEND 0.267129 \n",
"6 2252.60 2253.58 0.221 0.221 EXTEND 0.216580 \n",
"0 0.9059 0.8957 551.0 273.0 ASTER -2.846680 \n",
"1 0.8957 0.9095 273.0 277.0 EXTEND 3.659747 \n",
"2 0.9131 0.9126 273.0 273.0 EXTEND -0.198819 \n",
"3 0.9136 0.9123 273.0 272.0 ASTER -0.415656 \n",
"4 0.9105 0.9106 274.0 274.0 EXTEND 0.027400 \n",
"5 0.9117 0.9120 274.0 274.0 EXTEND 0.082200 \n",
"\n",
" per_trade_pnl_pct \n",
"0 -0.001907 \n",
"1 0.002638 \n",
"2 -0.004569 \n",
"3 0.002686 \n",
"4 -0.001371 \n",
"5 0.005334 \n",
"6 0.000435 \n",
"0 -0.510116 \n",
"1 0.030285 \n",
"2 -0.000548 \n",
"3 -0.005081 \n",
"4 0.000110 \n",
"5 0.000329 \n",
"\n",
"[7 rows x 23 columns]"
"[6 rows x 23 columns]"
]
},
"execution_count": 120,
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
@@ -734,7 +710,7 @@
},
{
"cell_type": "code",
"execution_count": 116,
"execution_count": 18,
"metadata": {},
"outputs": [
{
@@ -760,17 +736,13 @@
"showlegend": false,
"type": "scatter",
"x": [
"2026-04-30T09:10:12.149000",
"2026-04-30T13:59:15.023000",
"2026-04-30T16:00:02.415000",
"2026-04-30T16:00:02.415000",
"2026-04-30T19:11:23.505000",
"2026-04-30T21:49:32.034000",
"2026-04-30T23:03:53.771000"
"2026-05-02T22:08:49.823000",
"2026-05-02T22:33:52.752000",
"2026-05-02T22:42:05.138000"
],
"xaxis": "x",
"y": {
"bdata": "bS+QoPgxAcA3wZaQD3rWP3d6VPzfMRPAQMD4hsJnwz/J3olZL4bkP+wKtDukGNE/gd17uOS4yz8=",
"bdata": "fraYnxua2r+APKTfvg6cP/A1uycPC7U/",
"dtype": "f8"
},
"yaxis": "y"
@@ -1608,16 +1580,41 @@
},
{
"cell_type": "code",
"execution_count": null,
"execution_count": 27,
"id": "1827a1ca",
"metadata": {},
"outputs": [],
"source": [
"j = \"[{'timestamp_arrival': 1777762804677, 'timestamp_msg': 1777762804673, 'timestamp_transaction': 1777762804650, 'event_reason_type': 'ORDER', 'symbol': 'LITUSDT', 'position_amount': 0.0, 'entry_price': 0.0, 'accumulated_realized_pre_fees': 2.0749, 'unrealized_pnl': 0.0, 'margin_type': 'cross', 'isolated_wallet': 0.0, 'position_side': 'BOTH'}]\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "77f27d2f",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "6bd8f38d",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "f1a0e1a1",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "a0380428",
"metadata": {},
"outputs": [],
"source": []