saving
This commit is contained in:
182
algo.ipynb
182
algo.ipynb
File diff suppressed because one or more lines are too long
@@ -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
|
||||
|
||||
1258
aster.ipynb
1258
aster.ipynb
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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
1181
extended.ipynb
1181
extended.ipynb
File diff suppressed because one or more lines are too long
246
main.py
246
main.py
@@ -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)}))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 ),
|
||||
|
||||
@@ -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
619
pnl.ipynb
@@ -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": []
|
||||
|
||||
Reference in New Issue
Block a user