moving to extravm

This commit is contained in:
2026-05-08 03:02:02 +00:00
parent e4bdb3f6a0
commit f5f43be1a1
4 changed files with 517 additions and 447 deletions

View File

@@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"execution_count": 10,
"id": "d1eed397",
"metadata": {},
"outputs": [],
@@ -22,7 +22,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 11,
"id": "c6151613",
"metadata": {},
"outputs": [],
@@ -2600,88 +2600,6 @@
"Decimal('1.0').quantize()"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1.0"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"x = 1.0\n",
"x"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "70b57870",
"metadata": {},
"outputs": [],
"source": [
"f = ['b','a','z','e']"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['b', 'a', 'z', 'e']"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"f"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "9fd60c6e",
"metadata": {},
"outputs": [],
"source": [
"f.sort()"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "cd8b41de",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"['a', 'b', 'e', 'z']"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"f"
]
},
{
"cell_type": "code",
"execution_count": 46,
@@ -2699,125 +2617,42 @@
{
"cell_type": "code",
"execution_count": null,
"id": "57fac02c",
"metadata": {},
"outputs": [],
"source": [
"### Locked Values 🔒🔑 ###"
]
"source": []
},
{
"cell_type": "code",
"execution_count": 124,
"id": "8c2d003f",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"from sqlalchemy.util.typing import Self\n",
"from typing import Any\n",
"from collections.abc import Sequence, Callable\n",
"\n",
"class Locked_Value(Sequence):\n",
" def __init__(self, initial_value: Any, unlock_func: Callable):\n",
" self._value: Any = initial_value\n",
" self._unlock_func: Callable = unlock_func\n",
" self._is_locked: bool = True\n",
"\n",
" def __repr__(self):\n",
" return str((self._value, self._is_locked, self._unlock_func))\n",
"\n",
" def __len__(self):\n",
" return len((self._value, self._is_locked, self._unlock_func))\n",
"\n",
" def __getitem__(self, index):\n",
" return (self._value, self._is_locked, self._unlock_func)[index]\n",
"\n",
" def __str__(self):\n",
" return str((self._value))\n",
"\n",
" def unlock(self) -> Self:\n",
" if self._unlock_func():\n",
" self._is_locked = False\n",
" return self\n",
"\n",
" @property\n",
" def is_locked(self):\n",
" return self._is_locked\n",
"\n",
" @property\n",
" def value(self):\n",
" return self._value\n",
"\n",
" @value.setter\n",
" def value(self, v):\n",
" if not(self._is_locked):\n",
" self._value = v\n",
" else:\n",
" raise ValueError(f'Failed to set value, item is locked: {str(self.__repr__)}')\n"
]
},
{
"cell_type": "code",
"execution_count": 155,
"id": "6fd6a46c",
"metadata": {},
"outputs": [],
"source": [
"class Current_Previous_Value:\n",
" def __init__(self, value: Any = None, previous_value: Any = None):\n",
" self._value: Any = value\n",
" self._previous_value: Any = previous_value\n",
"\n",
" def __repr__(self):\n",
" return str((self._value, self._previous_value))\n",
"\n",
" def __len__(self):\n",
" return len((self._value, self._previous_value))\n",
"\n",
" def __getitem__(self, index):\n",
" return (self._value, self._previous_value)[index]\n",
"\n",
" def __str__(self):\n",
" return str(self._value)\n",
"\n",
" @property\n",
" def value(self):\n",
" return self._value\n",
"\n",
" @property\n",
" def previous_value(self):\n",
" return self._previous_value\n",
"\n",
" @value.setter\n",
" def value(self, v):\n",
" self._previous_value = self._value\n",
" self._value = v\n"
]
},
{
"cell_type": "code",
"execution_count": 156,
"metadata": {},
"outputs": [],
"source": [
"vo = Current_Previous_Value()"
]
},
{
"cell_type": "code",
"execution_count": 163,
"id": "b557d932",
"metadata": {},
"outputs": [],
"source": [
"vo.value = 1\n",
"vo.value = 2"
"df = pd.DataFrame(json.loads(VAL_KEY.get('fr_engine_best_fund_rate_master'))) # ty:ignore[invalid-argument-type]"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ef1d4ce0",
"id": "d1413506",
"metadata": {},
"outputs": [],
"source": [
"\n",
"\n",
"col_extras = {\n",
" 'symbol_ast': {\n",
" 'editable': False, \n",
" 'sortable': True\n",
" }\n",
"}\n",
"cols = [ {'field': v, **col_extras.get(v, {})} for v in df.columns ]\n",
"rows = df.to_dict(orient='records')"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fcad79c3",
"metadata": {},
"outputs": [],
"source": []
@@ -2825,142 +2660,318 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 131,
"id": "04efe6cc",
"metadata": {},
"outputs": [],
"source": [
"v = 1"
]
},
{
"cell_type": "code",
"execution_count": 132,
"id": "36b56800",
"id": "d6f23c60",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1"
"[{'symbol_ast': 'LITUSDT',\n",
" 'max_leverage_ast': 50,\n",
" 'lh_asset_ast': 'LIT',\n",
" 'rh_asset_ast': 'USDT',\n",
" 'funding_rate_ast': 0.00011009,\n",
" 'min_price_ast': '0.0001000',\n",
" 'min_order_size_ast': '1',\n",
" 'min_lot_size_ast': '1',\n",
" 'min_notional_ast': '5',\n",
" 'buy_ratio_ast': -0.0005956543,\n",
" 'symbol_ext': 'LIT-USD',\n",
" 'max_leverage_ext': 25,\n",
" 'lh_asset_ext': 'LIT',\n",
" 'rh_asset_ext': 'USD',\n",
" 'funding_rate_ext': 1.3e-05,\n",
" 'min_price_ext': '0.0001',\n",
" 'min_order_size_ext': '10',\n",
" 'min_lot_size_ext': '1',\n",
" 'min_notional_ext': 0.0,\n",
" 'buy_ratio_ext': 0.0005956543,\n",
" 'buy_ratio_std': 0.0007698555,\n",
" 'next_funding_at_same_time': True,\n",
" 'id': 0},\n",
" {'symbol_ast': 'HYPEUSDT',\n",
" 'max_leverage_ast': 300,\n",
" 'lh_asset_ast': 'HYPE',\n",
" 'rh_asset_ast': 'USDT',\n",
" 'funding_rate_ast': 5e-05,\n",
" 'min_price_ast': '0.00100',\n",
" 'min_order_size_ast': '0.01',\n",
" 'min_lot_size_ast': '0.01',\n",
" 'min_notional_ast': '5',\n",
" 'buy_ratio_ast': -0.0002001154,\n",
" 'symbol_ext': 'HYPE-USD',\n",
" 'max_leverage_ext': 50,\n",
" 'lh_asset_ext': 'HYPE',\n",
" 'rh_asset_ext': 'USD',\n",
" 'funding_rate_ext': 1.3e-05,\n",
" 'min_price_ext': '0.001',\n",
" 'min_order_size_ext': '0.1',\n",
" 'min_lot_size_ext': '0.01',\n",
" 'min_notional_ext': 0.0,\n",
" 'buy_ratio_ext': 0.0002001154,\n",
" 'buy_ratio_std': 0.0002123501,\n",
" 'next_funding_at_same_time': False,\n",
" 'id': 1},\n",
" {'symbol_ast': 'SOLUSDT',\n",
" 'max_leverage_ast': 100,\n",
" 'lh_asset_ast': 'SOL',\n",
" 'rh_asset_ast': 'USDT',\n",
" 'funding_rate_ast': 0.0001,\n",
" 'min_price_ast': '0.4200',\n",
" 'min_order_size_ast': '0.01',\n",
" 'min_lot_size_ast': '0.01',\n",
" 'min_notional_ast': '5',\n",
" 'buy_ratio_ast': -0.0004271435,\n",
" 'symbol_ext': 'SOL-USD',\n",
" 'max_leverage_ext': 50,\n",
" 'lh_asset_ext': 'SOL',\n",
" 'rh_asset_ext': 'USD',\n",
" 'funding_rate_ext': -1.4e-05,\n",
" 'min_price_ext': '0.01',\n",
" 'min_order_size_ext': '0.1',\n",
" 'min_lot_size_ext': '0.01',\n",
" 'min_notional_ext': 0.0,\n",
" 'buy_ratio_ext': 0.0004271435,\n",
" 'buy_ratio_std': 9.37564e-05,\n",
" 'next_funding_at_same_time': False,\n",
" 'id': 2},\n",
" {'symbol_ast': 'BNBUSDT',\n",
" 'max_leverage_ast': 100,\n",
" 'lh_asset_ast': 'BNB',\n",
" 'rh_asset_ast': 'USDT',\n",
" 'funding_rate_ast': 0.00012242,\n",
" 'min_price_ast': '0.010',\n",
" 'min_order_size_ast': '0.01',\n",
" 'min_lot_size_ast': '0.01',\n",
" 'min_notional_ast': '5',\n",
" 'buy_ratio_ast': -0.0011992245,\n",
" 'symbol_ext': 'BNB-USD',\n",
" 'max_leverage_ext': 50,\n",
" 'lh_asset_ext': 'BNB',\n",
" 'rh_asset_ext': 'USD',\n",
" 'funding_rate_ext': 1.3e-05,\n",
" 'min_price_ext': '0.01',\n",
" 'min_order_size_ext': '0.01',\n",
" 'min_lot_size_ext': '0.001',\n",
" 'min_notional_ext': 0.0,\n",
" 'buy_ratio_ext': 0.0011992245,\n",
" 'buy_ratio_std': 5.93637e-05,\n",
" 'next_funding_at_same_time': False,\n",
" 'id': 3},\n",
" {'symbol_ast': 'XRPUSDT',\n",
" 'max_leverage_ast': 100,\n",
" 'lh_asset_ast': 'XRP',\n",
" 'rh_asset_ast': 'USDT',\n",
" 'funding_rate_ast': 4.038e-05,\n",
" 'min_price_ast': '0.0143',\n",
" 'min_order_size_ast': '0.1',\n",
" 'min_lot_size_ast': '0.1',\n",
" 'min_notional_ast': '5',\n",
" 'buy_ratio_ast': -0.0001086996,\n",
" 'symbol_ext': 'XRP-USD',\n",
" 'max_leverage_ext': 50,\n",
" 'lh_asset_ext': 'XRP',\n",
" 'rh_asset_ext': 'USD',\n",
" 'funding_rate_ext': 1.3e-05,\n",
" 'min_price_ext': '0.0001',\n",
" 'min_order_size_ext': '10',\n",
" 'min_lot_size_ext': '1',\n",
" 'min_notional_ext': 0.0,\n",
" 'buy_ratio_ext': 0.0001086996,\n",
" 'buy_ratio_std': 8.12797e-05,\n",
" 'next_funding_at_same_time': False,\n",
" 'id': 4},\n",
" {'symbol_ast': 'DOGEUSDT',\n",
" 'max_leverage_ast': 75,\n",
" 'lh_asset_ast': 'DOGE',\n",
" 'rh_asset_ast': 'USDT',\n",
" 'funding_rate_ast': -5.33e-06,\n",
" 'min_price_ast': '0.002440',\n",
" 'min_order_size_ast': '1',\n",
" 'min_lot_size_ast': '1',\n",
" 'min_notional_ast': '5',\n",
" 'buy_ratio_ast': 0.0010192971,\n",
" 'symbol_ext': 'DOGE-USD',\n",
" 'max_leverage_ext': 50,\n",
" 'lh_asset_ext': 'DOGE',\n",
" 'rh_asset_ext': 'USD',\n",
" 'funding_rate_ext': 1.3e-05,\n",
" 'min_price_ext': '0.00001',\n",
" 'min_order_size_ext': '100',\n",
" 'min_lot_size_ext': '10',\n",
" 'min_notional_ext': 0.0,\n",
" 'buy_ratio_ext': -0.0010192971,\n",
" 'buy_ratio_std': 8.60126e-05,\n",
" 'next_funding_at_same_time': False,\n",
" 'id': 5},\n",
" {'symbol_ast': 'SUIUSDT',\n",
" 'max_leverage_ast': 75,\n",
" 'lh_asset_ast': 'SUI',\n",
" 'rh_asset_ast': 'USDT',\n",
" 'funding_rate_ast': 0.0001,\n",
" 'min_price_ast': '0.000100',\n",
" 'min_order_size_ast': '0.1',\n",
" 'min_lot_size_ast': '0.1',\n",
" 'min_notional_ast': '5',\n",
" 'buy_ratio_ast': 0.0002862497,\n",
" 'symbol_ext': 'SUI-USD',\n",
" 'max_leverage_ext': 50,\n",
" 'lh_asset_ext': 'SUI',\n",
" 'rh_asset_ext': 'USD',\n",
" 'funding_rate_ext': 1.3e-05,\n",
" 'min_price_ext': '0.0001',\n",
" 'min_order_size_ext': '10',\n",
" 'min_lot_size_ext': '1',\n",
" 'min_notional_ext': 0.0,\n",
" 'buy_ratio_ext': -0.0002862497,\n",
" 'buy_ratio_std': 0.0005447464,\n",
" 'next_funding_at_same_time': False,\n",
" 'id': 6},\n",
" {'symbol_ast': 'BTCUSDT',\n",
" 'max_leverage_ast': 150,\n",
" 'lh_asset_ast': 'BTC',\n",
" 'rh_asset_ast': 'USDT',\n",
" 'funding_rate_ast': -5.216e-05,\n",
" 'min_price_ast': '1',\n",
" 'min_order_size_ast': '0.001',\n",
" 'min_lot_size_ast': '0.001',\n",
" 'min_notional_ast': '5',\n",
" 'buy_ratio_ast': -0.000104008,\n",
" 'symbol_ext': 'BTC-USD',\n",
" 'max_leverage_ext': 50,\n",
" 'lh_asset_ext': 'BTC',\n",
" 'rh_asset_ext': 'USD',\n",
" 'funding_rate_ext': -8e-06,\n",
" 'min_price_ext': '1',\n",
" 'min_order_size_ext': '0.0001',\n",
" 'min_lot_size_ext': '0.00001',\n",
" 'min_notional_ext': 0.0,\n",
" 'buy_ratio_ext': 0.000104008,\n",
" 'buy_ratio_std': 0.0001018788,\n",
" 'next_funding_at_same_time': False,\n",
" 'id': 7},\n",
" {'symbol_ast': 'CHIPUSDT',\n",
" 'max_leverage_ast': 50,\n",
" 'lh_asset_ast': 'CHIP',\n",
" 'rh_asset_ast': 'USDT',\n",
" 'funding_rate_ast': 1.25e-05,\n",
" 'min_price_ast': '0.0000100',\n",
" 'min_order_size_ast': '1',\n",
" 'min_lot_size_ast': '1',\n",
" 'min_notional_ast': '5',\n",
" 'buy_ratio_ast': -0.0007168459,\n",
" 'symbol_ext': 'CHIP-USD',\n",
" 'max_leverage_ext': 5,\n",
" 'lh_asset_ext': 'CHIP',\n",
" 'rh_asset_ext': 'USD',\n",
" 'funding_rate_ext': -4.6e-05,\n",
" 'min_price_ext': '0.000001',\n",
" 'min_order_size_ext': '100',\n",
" 'min_lot_size_ext': '10',\n",
" 'min_notional_ext': 0.0,\n",
" 'buy_ratio_ext': 0.0007168459,\n",
" 'buy_ratio_std': 0.0014437729,\n",
" 'next_funding_at_same_time': True,\n",
" 'id': 8},\n",
" {'symbol_ast': 'ASTERUSDT',\n",
" 'max_leverage_ast': 75,\n",
" 'lh_asset_ast': 'ASTER',\n",
" 'rh_asset_ast': 'USDT',\n",
" 'funding_rate_ast': 5e-05,\n",
" 'min_price_ast': '0.00010',\n",
" 'min_order_size_ast': '0.01',\n",
" 'min_lot_size_ast': '0.01',\n",
" 'min_notional_ast': '5',\n",
" 'buy_ratio_ast': -0.0001497055,\n",
" 'symbol_ext': 'ASTER-USD',\n",
" 'max_leverage_ext': 25,\n",
" 'lh_asset_ext': 'ASTER',\n",
" 'rh_asset_ext': 'USD',\n",
" 'funding_rate_ext': 1.3e-05,\n",
" 'min_price_ext': '0.00001',\n",
" 'min_order_size_ext': '10',\n",
" 'min_lot_size_ext': '1',\n",
" 'min_notional_ext': 0.0,\n",
" 'buy_ratio_ext': 0.0001497055,\n",
" 'buy_ratio_std': 0.0001520848,\n",
" 'next_funding_at_same_time': False,\n",
" 'id': 9},\n",
" {'symbol_ast': 'ETHUSDT',\n",
" 'max_leverage_ast': 150,\n",
" 'lh_asset_ast': 'ETH',\n",
" 'rh_asset_ast': 'USDT',\n",
" 'funding_rate_ast': -2.051e-05,\n",
" 'min_price_ast': '0.01',\n",
" 'min_order_size_ast': '0.001',\n",
" 'min_lot_size_ast': '0.001',\n",
" 'min_notional_ast': '5',\n",
" 'buy_ratio_ast': -8.96077e-05,\n",
" 'symbol_ext': 'ETH-USD',\n",
" 'max_leverage_ext': 50,\n",
" 'lh_asset_ext': 'ETH',\n",
" 'rh_asset_ext': 'USD',\n",
" 'funding_rate_ext': 4e-06,\n",
" 'min_price_ext': '0.1',\n",
" 'min_order_size_ext': '0.01',\n",
" 'min_lot_size_ext': '0.001',\n",
" 'min_notional_ext': 0.0,\n",
" 'buy_ratio_ext': 8.96077e-05,\n",
" 'buy_ratio_std': 8.24728e-05,\n",
" 'next_funding_at_same_time': False,\n",
" 'id': 10},\n",
" {'symbol_ast': 'ZECUSDT',\n",
" 'max_leverage_ast': 75,\n",
" 'lh_asset_ast': 'ZEC',\n",
" 'rh_asset_ast': 'USDT',\n",
" 'funding_rate_ast': -1.06e-06,\n",
" 'min_price_ast': '0.0100',\n",
" 'min_order_size_ast': '0.001',\n",
" 'min_lot_size_ast': '0.001',\n",
" 'min_notional_ast': '5',\n",
" 'buy_ratio_ast': 0.000456781,\n",
" 'symbol_ext': 'ZEC-USD',\n",
" 'max_leverage_ext': 10,\n",
" 'lh_asset_ext': 'ZEC',\n",
" 'rh_asset_ext': 'USD',\n",
" 'funding_rate_ext': 1.3e-05,\n",
" 'min_price_ext': '0.001',\n",
" 'min_order_size_ext': '0.1',\n",
" 'min_lot_size_ext': '0.1',\n",
" 'min_notional_ext': 0.0,\n",
" 'buy_ratio_ext': -0.000456781,\n",
" 'buy_ratio_std': 0.0008927931,\n",
" 'next_funding_at_same_time': True,\n",
" 'id': 11},\n",
" {'symbol_ast': 'WLFIUSDT',\n",
" 'max_leverage_ast': 25,\n",
" 'lh_asset_ast': 'WLFI',\n",
" 'rh_asset_ast': 'USDT',\n",
" 'funding_rate_ast': 5e-05,\n",
" 'min_price_ast': '0.0001000',\n",
" 'min_order_size_ast': '1',\n",
" 'min_lot_size_ast': '1',\n",
" 'min_notional_ast': '5',\n",
" 'buy_ratio_ast': 0.0008577801,\n",
" 'symbol_ext': 'WLFI-USD',\n",
" 'max_leverage_ext': 10,\n",
" 'lh_asset_ext': 'WLFI',\n",
" 'rh_asset_ext': 'USD',\n",
" 'funding_rate_ext': 1.3e-05,\n",
" 'min_price_ext': '0.00001',\n",
" 'min_order_size_ext': '100',\n",
" 'min_lot_size_ext': '10',\n",
" 'min_notional_ext': 0.0,\n",
" 'buy_ratio_ext': -0.0008577801,\n",
" 'buy_ratio_std': 0.0006254649,\n",
" 'next_funding_at_same_time': False,\n",
" 'id': 12}]"
]
},
"execution_count": 132,
"execution_count": 34,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"v"
]
},
{
"cell_type": "code",
"execution_count": 133,
"id": "e24ff466",
"metadata": {},
"outputs": [],
"source": [
"v = 2"
]
},
{
"cell_type": "code",
"execution_count": 134,
"id": "b5ee5afa",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"2"
]
},
"execution_count": 134,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"v"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "74051e1d",
"metadata": {},
"outputs": [],
"source": [
"def test():\n",
" return False"
]
},
{
"cell_type": "code",
"execution_count": 128,
"id": "38ee912d",
"metadata": {},
"outputs": [],
"source": [
"v = Locked_Value('locked', unlock_func=test)"
]
},
{
"cell_type": "code",
"execution_count": 129,
"id": "bb703d87",
"metadata": {},
"outputs": [
{
"ename": "ValueError",
"evalue": "Failed to set value, item is locked: <bound method Locked_Value.__repr__ of ('locked', True, <function test at 0x76725ff485e0>)>",
"output_type": "error",
"traceback": [
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
"\u001b[31mValueError\u001b[39m Traceback (most recent call last)",
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[129]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m v.unlock().value = \u001b[33m'unlocked'\u001b[39m\n",
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[124]\u001b[39m\u001b[32m, line 41\u001b[39m, in \u001b[36mLocked_Value.value\u001b[39m\u001b[34m(self, v)\u001b[39m\n\u001b[32m 37\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m value(self, v):\n\u001b[32m 38\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28;01mnot\u001b[39;00m(self._is_locked):\n\u001b[32m 39\u001b[39m self._value = v\n\u001b[32m 40\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m---> \u001b[39m\u001b[32m41\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m ValueError(f'Failed to set value, item is locked: {str(self.__repr__)}')\n",
"\u001b[31mValueError\u001b[39m: Failed to set value, item is locked: <bound method Locked_Value.__repr__ of ('locked', True, <function test at 0x76725ff485e0>)>"
]
}
],
"source": [
"v.unlock().value = 'unlocked'"
]
},
{
"cell_type": "code",
"execution_count": 123,
"id": "76c44d97",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"('locked', True, <function test at 0x767268132520>)"
]
},
"execution_count": 123,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"v"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
@@ -2973,66 +2984,7 @@
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {},
"outputs": [],
"source": [
"lv = Locked_Value(initial_value=999)\n",
"# lv.unlock()"
]
},
{
"cell_type": "code",
"execution_count": 65,
"id": "76e21865",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"2"
]
},
"execution_count": 65,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(lv)"
]
},
{
"cell_type": "code",
"execution_count": 32,
"id": "10258f5b",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(999, False)"
]
},
"execution_count": 32,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"lv"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9270dd8d",
"id": "93323174",
"metadata": {},
"outputs": [],
"source": []
@@ -3044,29 +2996,6 @@
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "a938b2e0",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "39667bd8",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,

43
main.py
View File

@@ -1,3 +1,20 @@
'''
Atwater Trading: Perpetual Futures Funding Rate
TODO:
- Update switch signal to account for diff in leverage (right now its just pure rate)
- Can do by calculating based on expected alpha in dollar terms and then back over the collateral for percent - be careful fees are correctly accounted for in dollar terms.
- WS Stability
- Make sure every WS has reconnect and can handle failure scenarios
- Create health service (or add to orchestrator) to check that WS outputs are recent and not delayed, kill algo and alert if delayed.
- GUI
- NG basic dashboard to show algo summary, bfr, grid of trades/positions/orders/pnl, graph of ratios, prices, orders, fills, pnl, etc.
- Infra
- Move off AWS to a cheaper option (NYCServers, QuantVPS)
- Shutdown old VPS in NYC
'''
from x10.utils.http import WrappedApiResponse
from x10.perpetual.trading_client.trading_client import PerpetualTradingClient
import asyncio
@@ -28,6 +45,7 @@ import modules.aster_auth as aster_auth
import modules.extended_auth as extend_auth
import modules.structs as structs
### Clients ###
EXTEND_CLIENT: PerpetualTradingClient
CON: AsyncContextManager
@@ -535,6 +553,10 @@ async def handle_order_updates(exch: str, local_open_orders: list[dict], ws_open
if order_update_status in ['CANCELLED','CANCELED']:
logging.info(f'{exch} ORDER CANCELLED: {order_id}')
local_open_orders.pop(idx)
if exch=='ASTER':
Aster.cancel_request_pending = False
else:
Extend.cancel_request_pending = False
# utils.send_tg_alert(f'FR_ALGO - {exch} REJECTED ({order_id})')
elif order_update_status in ['EXPIRED','REJECTED']:
logging.info(f'{exch} ORDER REJECTED or EXPIRED: {order_id}')
@@ -542,9 +564,11 @@ async def handle_order_updates(exch: str, local_open_orders: list[dict], ws_open
if exch=='ASTER':
Aster.just_rejected_count = Aster.just_rejected_count + 1
Config.Config.Price_Worsener_Aster=1
Aster.cancel_request_pending = False
else:
Extend.just_rejected_count = Extend.just_rejected_count + 1
# Config.Config.Price_Worsener_Extend=1
Extend.cancel_request_pending = False
if Aster.just_rejected_count > 1 or Extend.just_rejected_count > 1:
time.sleep(1)
Aster.just_rejected_count = 0
@@ -556,8 +580,10 @@ async def handle_order_updates(exch: str, local_open_orders: list[dict], ws_open
if exch=='ASTER':
await get_aster_notional_position(resp=ws_pos_updates)
Last_Aster_Fill_Time_Ts = datetime.now().timestamp()*1000
Aster.cancel_request_pending = False
else:
await get_extend_notional(resp=ws_pos_updates)
Extend.cancel_request_pending = False
utils.send_tg_alert(f'FR_ALGO - {exch} PARTIALLY FILLED ({order_id})')
elif order_update_status in ['FILLED']:
logging.info(f'{exch} ORDER FILLED: {order_id}')
@@ -567,8 +593,10 @@ async def handle_order_updates(exch: str, local_open_orders: list[dict], ws_open
# await aster_cancel_all_orders()
await get_aster_notional_position(resp=ws_pos_updates)
Last_Aster_Fill_Time_Ts = datetime.now().timestamp()*1000
Aster.cancel_request_pending = False
else:
await extend_cancel_all_orders()
Extend.cancel_request_pending = False
await get_extend_notional()
utils.send_tg_alert(f'FR_ALGO - {exch} FILLED ({order_id})')
else:
@@ -796,6 +824,9 @@ async def get_extend_exch_info(symbol_override: str | None = None):
### CANCEL ORDERS ###
async def aster_cancel_all_orders():
global Aster
global Aster_Open_Orders
cancel_all_open_orders = {
"url": "/fapi/v3/allOpenOrders",
"method": "DELETE",
@@ -803,7 +834,12 @@ async def aster_cancel_all_orders():
'symbol': Aster.symbol,
}
}
r = await aster_auth.post_authenticated_url(cancel_all_open_orders)
r: dict = await aster_auth.post_authenticated_url(cancel_all_open_orders) # ty:ignore[invalid-assignment]
if r.get('code') == 200 and Aster_Open_Orders:
Aster_Open_Orders.pop(0)
Aster.cancel_request_pending = False
else:
Aster.cancel_request_pending = True
logging.info(f'ASTER CANCEL ALL OPEN ORDERS RESP: {r}')
async def extend_cancel_all_orders():
@@ -1082,7 +1118,10 @@ async def run_algo():
if not(aster_target.is_orderable(price=price)) and Aster_Open_Orders:
logging.info('ASTER HAS NO TAIL BUT OPEN ORDERS - CANCELLING OPEN ORDERS; SKIPPING')
skip = True
await aster_cancel_all_orders()
if not Aster.cancel_request_pending:
await aster_cancel_all_orders()
print_summary(use_logging=True)
if aster_target.is_orderable(price=price) and aster_target.base_tail(price=price) == Decimal('0.00'):
logging.info(f'ASTER TRYNG TO ORDER 0.00 BASE QTY, SKIPPING: base_qty: {aster_target.base_tail(price=price)}; {aster_target}')
skip = True

View File

@@ -252,11 +252,11 @@ class Perpetual_Exchange:
# Collateral_Updates: Collateral
# Funding_Rate: Funding_Rate
# Markets: Markets_Details
mult: int
lh_asset: str
rh_asset: str
symbol: str = ''
symbol_asset_separator: str = ''
mult: int
initial_funding_rate: float = 0
fund_rate_at_same_time: bool = False
min_price: float = 0
@@ -264,11 +264,13 @@ class Perpetual_Exchange:
min_lot_size: float = 0
min_notional: float = 0
buy_ratio: float = 0
buy_ratio_std: float = 0
notional_obj: dict = field(default_factory=dict)
notional_position: float = 0
unrealized_pnl: float = 0
buy_ratio_std: float = 0
just_rejected_count: int = 0
cancel_request_pending: bool = False
# async def update(self):
# await self.Collateral_Updates.update()

190
ng.py
View File

@@ -1,7 +1,8 @@
import os
from nicegui import ui, app, html
from sqlalchemy import create_engine
from sqlalchemy import create_engine, text
# import requests
import pandas as pd
import json
# import time
# import re
@@ -9,6 +10,8 @@ import valkey
import asyncio
from datetime import datetime
from dataclasses import dataclass, field
from sqlalchemy.ext.asyncio import create_async_engine
from typing import AsyncContextManager
# from random import random
# from nicegui_modules import data
# from nicegui_modules import ui_components
@@ -16,13 +19,16 @@ from dataclasses import dataclass, field
ALLOW_BODY_SCROLL: bool = True
LOOKBACK: int = 60
LOOKBACK_RT_TV_MAX_POINTS: int = 3000
# REFRESH_INTERVAL_SEC: float = 10
REFRESH_INTERVAL_SEC: float = 10
REFRESH_INTERVAL_RT_SEC: float = 1/10
# CON: AsyncContextManager
# ENGINE = create_async_engine('mysql+asyncmy://root:pwd@localhost/fund_rate')
ENGINE = create_engine('mysql+pymysql://root:pwd@localhost/fund_rate')
VALKEY = valkey.Valkey(host='localhost', port=6379, db=0, decode_responses=True)
VALKEY = valkey.Valkey(host='localhost', port=6379, db=0, decode_responses=True)
CHARTS = [
{
'type': 'AREA',
@@ -110,6 +116,81 @@ CHARTS_OPTIONS = {
}
}
### Data ###
async def get_bfr_master_data() -> pd.DataFrame:
df = pd.DataFrame(json.loads(VALKEY.get('fr_engine_best_fund_rate_master'))) # ty:ignore[invalid-argument-type]
df.reset_index(drop=True)
df['id'] = df.index
return df
async def get_trades_hist() -> pd.DataFrame:
start_ts = (round(datetime.now().timestamp()*1000)-(60*60*24*1000))
### ASTER ###
aster_orders = text(f'''
SELECT *
FROM fr_aster_user_order_trade
WHERE timestamp_arrival > {start_ts}
''')
df_aster_orders = pd.read_sql(aster_orders, con=ENGINE)
df_aster_orders['timestamp_dt'] = pd.to_datetime(df_aster_orders['timestamp_transaction'], unit='ms')
df_aster_orders_fill = df_aster_orders.loc[df_aster_orders['execution_type']=='TRADE',:]
df_aster_orders_fill = df_aster_orders_fill[['timestamp_transaction','order_trade_time_ts','timestamp_dt','order_id','trade_id','client_order_id','order_status','side','last_filled_qty','filled_accumulated_qty','commission','last_filled_price','realized_profit']].reset_index(drop=True)
df_aster_trades = df_aster_orders_fill.groupby('order_id').agg({'timestamp_transaction': 'first','order_trade_time_ts':'last','order_status':'last','side':'last','last_filled_qty':'sum','filled_accumulated_qty':'last','commission':'sum','last_filled_price':'mean','realized_profit':'sum'}).reset_index()
df_aster_trades['is_mkt_maker'] = df_aster_trades['commission'] == 0.00
df_aster_trades['timestamp_ts'] = pd.to_datetime(df_aster_trades['order_trade_time_ts'], unit='ms')
df_aster_trades = df_aster_trades.rename({'order_status':'status','filled_accumulated_qty':'filled_qty','commission':'payed_fee','last_filled_price':'price'}, axis=1)
### EXTEND ###
# Load and Transform Orders
extend_orders = text(f'''
SELECT *
FROM fr_extended_user_order
WHERE timestamp_arrival > {start_ts}
''')
df_extend_orders = pd.read_sql(extend_orders, con=ENGINE)
df_extend_orders['timestamp_dt'] = pd.to_datetime(df_extend_orders['updated_time_ts'], unit='ms')
df_extend_orders_fill = df_extend_orders.loc[df_extend_orders['status'].isin(['FILLED','PARTIALLY_FILLED']),:]
df_extend_orders_fill = df_extend_orders_fill[['created_time_ts','updated_time_ts','timestamp_dt','order_id','external_id','status','side','qty','filled_qty','payed_fee','price','averagePrice']].reset_index(drop=True)
# Trades
df_extend_trades = df_extend_orders_fill.groupby('order_id').agg({'created_time_ts':'first','updated_time_ts':'last','status': 'last','side': 'last', 'filled_qty':'last','payed_fee':'sum','price':'last'}).reset_index()
df_extend_trades['duration_sec_ast'] = ( df_extend_trades['updated_time_ts'] - df_extend_trades['created_time_ts'] ) / 1000
df_extend_trades['is_mkt_maker'] = df_extend_trades['payed_fee'] == 0.00
df_extend_trades['timestamp_ts'] = pd.to_datetime(df_extend_trades['updated_time_ts'], unit='ms')
def tie_trades_together_get_extend_from_aster(row):
row = row.to_frame().T
row.index=[1]
extend_row = df_extend_trades[['order_id','timestamp_ts','status','side','filled_qty','payed_fee','price','is_mkt_maker']].loc[df_extend_trades['timestamp_ts']>row['timestamp_ts'].iloc[0],:].iloc[0]
extend_row = extend_row.to_frame().T
extend_row.index=[1]
return_row = row.merge(extend_row, left_index=True, right_index=True, suffixes=('_ast','_ext'))
return return_row.iloc[0]
df_comb_trades = df_aster_trades[['order_id','timestamp_ts','status','side','filled_qty','payed_fee','price','is_mkt_maker']].apply(tie_trades_together_get_extend_from_aster, axis=1)
df_comb_trades['buy_price'] = df_comb_trades['price_ast'].where(df_comb_trades['side_ast']=='BUY', df_comb_trades['price_ext'])
df_comb_trades['sell_price'] = df_comb_trades['price_ast'].where(df_comb_trades['side_ast']=='SELL', df_comb_trades['price_ext'])
df_comb_trades['buy_qty'] = df_comb_trades['filled_qty_ast'].where(df_comb_trades['side_ast']=='BUY', df_comb_trades['filled_qty_ext'])
df_comb_trades['sell_qty'] = df_comb_trades['filled_qty_ast'].where(df_comb_trades['side_ast']=='SELL', df_comb_trades['filled_qty_ext'])
df_comb_trades['buy_side'] = df_comb_trades['order_id_ast'].where(df_comb_trades['side_ast']=='BUY', df_comb_trades['order_id_ext'])
df_comb_trades['buy_side'] = df_comb_trades['order_id_ast'] == df_comb_trades['buy_side']
df_comb_trades['buy_side'] = df_comb_trades['buy_side'].replace(True, 'ASTER').replace(False,'EXTEND')
df_comb_trades['per_trade_pnl'] = ( ( df_comb_trades['sell_price'] - df_comb_trades['buy_price'] ) * df_comb_trades['sell_qty'] ) - df_comb_trades['payed_fee_ast'] - df_comb_trades['payed_fee_ext']
df_comb_trades['per_trade_pnl_pct'] = ( (df_comb_trades['sell_price']*df_comb_trades['sell_qty']) - (df_comb_trades['buy_price']*df_comb_trades['buy_qty']) ) / (df_comb_trades['buy_price']*df_comb_trades['buy_qty'])
df = df_comb_trades.apply(lambda x: x.dt.strftime('%Y-%m-%d %H:%M:%S.%f') if hasattr(x, 'dt') else x)
df.reset_index(drop=True)
df['id'] = df.index
return df
### Utils ###
def update_body_scroll(e=None, bool_override=False):
if e is None:
@@ -128,7 +209,6 @@ async def update_tv():
series_update_aster_tob = json.loads(VALKEY.get('fut_ticker_aster')) # ty:ignore[invalid-argument-type]
series_update_extend_tob = json.loads(VALKEY.get('fut_ticker_extended')) # ty:ignore[invalid-argument-type]
series_update_algo_status = json.loads(VALKEY.get('algo_status')) # ty:ignore[invalid-argument-type]
master_data = json.loads(VALKEY.get(name='fr_engine_best_fund_rate_master')) # ty:ignore[invalid-argument-type]
timestamp_aster_tob = round( ( series_update_aster_tob['timestamp_transaction'] / 1000 ) , 2)
timestamp_extend_tob = round( ( series_update_extend_tob['timestamp_msg'] / 1000 ) , 2)
@@ -165,13 +245,57 @@ async def update_tv():
ui.run_javascript(f'await update_tv(data_list={data_list}, lookback_max_points={LOOKBACK_RT_TV_MAX_POINTS});')
async def create_bfr_aggrid() -> ui.aggrid:
df = await get_bfr_master_data()
col_extras = {
'symbol_ast': {
'editable': False,
'sortable': True
}
}
cols = [ {'field': v, **col_extras.get(v, {})} for v in df.columns ]
rows = df.to_dict(orient='records')
grid = ui.aggrid(
{
'columnDefs': cols,
'rowData': rows,
'autoSizeStrategy': {
'type': 'fitCellContents',
},
# 'rowSelection': {'mode': 'multiRow'},
# 'stopEditingWhenCellsLoseFocus': True,
}
).classes('auto-fit flex-grow w-full col-span-2 md:col-span-1')
return grid
async def create_pnl_aggrid() -> ui.aggrid:
df = await get_trades_hist()
col_extras = {}
cols = [ {'field': v, **col_extras.get(v, {})} for v in df.columns ]
rows = df.to_dict(orient='records')
grid = ui.aggrid(
{
'columnDefs': cols,
'rowData': rows,
'autoSizeStrategy': {
'type': 'fitCellContents',
},
# 'rowSelection': {'mode': 'multiRow'},
# 'stopEditingWhenCellsLoseFocus': True,
}
).classes('auto-fit flex-grow w-full col-span-2 md:col-span-1')
return grid
### Pages ###
async def rt_chart_page():
global LOOKBACK
LOOKBACK = app.storage.user.get('lookback', LOOKBACK)
timer = ui.timer(REFRESH_INTERVAL_RT_SEC, update_tv)
timer_tv = ui.timer(REFRESH_INTERVAL_RT_SEC, update_tv)
# timer_sql = ui.timer(REFRESH_INTERVAL_SEC)
# ui.query('.q-page').classes('flex flex-col h-screen')
@@ -182,22 +306,11 @@ async def rt_chart_page():
# ui.switch('▶️', value=True).bind_value_to(timer, 'active')
# with ui.column().style('position: absolute; right: 20px; font-family: monospace; align-self: center;'):
# ui.label('Atwater Trading - Funding Rate')
ui.query('.nicegui-content').classes('p-0 w-full')
ui.query('.q-page').classes('flex')
with ui.grid(columns=2, rows=2).classes('h-screen w-full flex-grow gap-2 auto-fit '):
aggrid = ui.aggrid({
'columnDefs': [
{'field': 'name', 'editable': True, 'sortable': True},
{'field': 'age', 'editable': True},
{'field': 'id'},
],
'rowData': [
{'id': 0, 'name': 'Alice', 'age': 18},
{'id': 1, 'name': 'Bob', 'age': 21},
{'id': 2, 'name': 'Carol', 'age': 20},
],
'rowSelection': {'mode': 'multiRow'},
'stopEditingWhenCellsLoseFocus': True,
}).classes('auto-fit flex-grow w-full col-span-2 md:col-span-1')
# aggrid = await create_bfr_aggrid()
# with ui.element(tag='div').classes('auto-fit flex-grow w-full').style("height:100%; width: 100%;"):
# with ui.tabs().classes('w-full') as tabs:
# one = ui.tab('One').classes('auto-fit flex-grow w-full').style("height:100%; width: 100%;")
@@ -210,31 +323,17 @@ async def rt_chart_page():
ui.run_javascript(f'await create_tv(charts_list={CHARTS}, create_chart_options={CHARTS_OPTIONS});')
with ui.element(tag='div').classes('col-span-2').style("height:100%; width: 100%;"):
with ui.tabs().classes('w-full') as tabs:
one = ui.tab('One')
two = ui.tab('Two')
with ui.tab_panels(tabs, value=two).classes('w-full').style("height:100%; width: 100%;"):
with ui.tab_panel(one):
ui.label('First tab')
with ui.tab_panel(two):
aggrid_2 = ui.aggrid({
'columnDefs': [
{'field': 'name', 'editable': True, 'sortable': True},
{'field': 'age', 'editable': True},
{'field': 'id'},
],
'rowData': [
{'id': 0, 'name': 'Alice', 'age': 18},
{'id': 1, 'name': 'Bob', 'age': 21},
{'id': 2, 'name': 'Carol', 'age': 20},
],
'rowSelection': {'mode': 'multiRow'},
'stopEditingWhenCellsLoseFocus': True,
})
with ui.tabs().props('align=justify').classes('justify-start') as tabs:
tab_pnl = ui.tab('PnL').classes('justify-start')
tab_bfr = ui.tab('BFR').classes('justify-start')
with ui.tab_panels(tabs, value=tab_pnl).classes('w-full').style("height:100%; width: 100%;"):
with ui.tab_panel(tab_pnl):
ag_pnl = await create_pnl_aggrid()
with ui.tab_panel(tab_bfr):
ag_bfr = await create_bfr_aggrid()
def root():
async def root():
app.add_static_files(max_cache_age=0, url_path='/static', local_directory=os.path.join(os.path.dirname(__file__), 'nicegui_modules/static'))
ui.add_head_html('''
<meta name="darkreader-lock">
@@ -251,4 +350,5 @@ def root():
'/': rt_chart_page,
}).classes('w-full')
ui.run(root, storage_secret="123ABC", reload=True, dark=True, title='Atwater Trading')
ui.run(root, storage_secret="123ABC", reload=True, dark=True, title='Atwater Trading', port=9090)