Big renaming of Context properties to make them more consistent and appropriate.
This commit is contained in:
parent
b89a072cdd
commit
261848f325
94
Pandas.ipynb
94
Pandas.ipynb
|
@ -2,7 +2,6 @@
|
||||||
"cells": [
|
"cells": [
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
"source": [
|
||||||
"# ⚠ Warning\n",
|
"# ⚠ Warning\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
@ -11,11 +10,11 @@
|
||||||
"[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gl/OpinionatedGeek%2Fmango-explorer/HEAD?filepath=Pandas.ipynb) _🏃♀️ To run this notebook press the ⏩ icon in the toolbar above._\n",
|
"[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gl/OpinionatedGeek%2Fmango-explorer/HEAD?filepath=Pandas.ipynb) _🏃♀️ To run this notebook press the ⏩ icon in the toolbar above._\n",
|
||||||
"\n",
|
"\n",
|
||||||
"[🥭 Mango Markets](https://mango.markets/) support is available at: [Docs](https://docs.mango.markets/) | [Discord](https://discord.gg/67jySBhxrg) | [Twitter](https://twitter.com/mangomarkets) | [Github](https://github.com/blockworks-foundation) | [Email](mailto:hello@blockworks.foundation)"
|
"[🥭 Mango Markets](https://mango.markets/) support is available at: [Docs](https://docs.mango.markets/) | [Discord](https://discord.gg/67jySBhxrg) | [Twitter](https://twitter.com/mangomarkets) | [Github](https://github.com/blockworks-foundation) | [Email](mailto:hello@blockworks.foundation)"
|
||||||
]
|
],
|
||||||
|
"metadata": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
"source": [
|
||||||
"# 🥭 Mango + Pandas 🐼🐼\n",
|
"# 🥭 Mango + Pandas 🐼🐼\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
@ -24,31 +23,28 @@
|
||||||
"The `DataFrame` is then queried for the total assets and liabilities, the Top 10 margin accounts with the most assets and the most liabilities, and then the Top 10 margin accounts closest to liquidation.\n",
|
"The `DataFrame` is then queried for the total assets and liabilities, the Top 10 margin accounts with the most assets and the most liabilities, and then the Top 10 margin accounts closest to liquidation.\n",
|
||||||
"\n",
|
"\n",
|
||||||
"The data remains in the `DataFrame` called `df` so you can easily add your own queries and analyses."
|
"The data remains in the `DataFrame` called `df` so you can easily add your own queries and analyses."
|
||||||
]
|
],
|
||||||
|
"metadata": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
"metadata": {
|
|
||||||
"jupyter": {
|
|
||||||
"source_hidden": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
"source": [
|
||||||
"import logging\n",
|
"import logging\n",
|
||||||
"import mango\n",
|
"import mango\n",
|
||||||
"import pandas as pd\n",
|
"import pandas as pd\n",
|
||||||
"import time\n"
|
"import time\n"
|
||||||
]
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"metadata": {
|
||||||
|
"jupyter": {
|
||||||
|
"source_hidden": true
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
"metadata": {
|
|
||||||
"tags": []
|
|
||||||
},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
"source": [
|
||||||
"start_time = time.time()\n",
|
"start_time = time.time()\n",
|
||||||
"context = mango.Context.default()\n",
|
"context = mango.Context.default()\n",
|
||||||
|
@ -62,7 +58,7 @@
|
||||||
"print(f\"Done. Time taken: {time.time() - start_time}\")\n",
|
"print(f\"Done. Time taken: {time.time() - start_time}\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
"print(\"Loading margin accounts...\")\n",
|
"print(\"Loading margin accounts...\")\n",
|
||||||
"margin_accounts = mango.MarginAccount.load_all_for_group_with_open_orders(context, context.program_id, group)\n",
|
"margin_accounts = mango.MarginAccount.load_all_for_group_with_open_orders(context, context.mango_program_address, group)\n",
|
||||||
"print(f\"Done. {len(margin_accounts)} accounts. Time taken: {time.time() - start_time}\")\n",
|
"print(f\"Done. {len(margin_accounts)} accounts. Time taken: {time.time() - start_time}\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
"print(\"Loading pandas dataframe...\")\n",
|
"print(\"Loading pandas dataframe...\")\n",
|
||||||
|
@ -113,20 +109,22 @@
|
||||||
" }\n",
|
" }\n",
|
||||||
" all_formats.update(sheet_formats)\n",
|
" all_formats.update(sheet_formats)\n",
|
||||||
" return df.style.format(all_formats)\n"
|
" return df.style.format(all_formats)\n"
|
||||||
]
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"metadata": {
|
||||||
|
"tags": []
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
"source": [
|
||||||
"# 🔥 Total Assets + Liabilities"
|
"# 🔥 Total Assets + Liabilities"
|
||||||
]
|
],
|
||||||
|
"metadata": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
"source": [
|
||||||
"print(f\"\"\"\n",
|
"print(f\"\"\"\n",
|
||||||
"Total Assets: ${df['Assets'].sum():>15,.2f}\n",
|
"Total Assets: ${df['Assets'].sum():>15,.2f}\n",
|
||||||
|
@ -135,112 +133,114 @@
|
||||||
"Liquidatable: {len(df[(df[\"Collateral Ratio\"] != 0) & (df[\"Collateral Ratio\"] <= 1.1)]):>15,}\n",
|
"Liquidatable: {len(df[(df[\"Collateral Ratio\"] != 0) & (df[\"Collateral Ratio\"] <= 1.1)]):>15,}\n",
|
||||||
"🥭 Ripe Mangoes: {len(df[(df[\"Collateral Ratio\"] > 1.1) & (df[\"Collateral Ratio\"] < 1.2)]):>15,}\n",
|
"🥭 Ripe Mangoes: {len(df[(df[\"Collateral Ratio\"] > 1.1) & (df[\"Collateral Ratio\"] < 1.2)]):>15,}\n",
|
||||||
"\"\"\")"
|
"\"\"\")"
|
||||||
]
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"metadata": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
"source": [
|
||||||
"# 🔝 Top 10 Greatest Assets\n",
|
"# 🔝 Top 10 Greatest Assets\n",
|
||||||
"\n",
|
"\n",
|
||||||
"The top 10 margin accounts with most assets"
|
"The top 10 margin accounts with most assets"
|
||||||
]
|
],
|
||||||
|
"metadata": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
"source": [
|
||||||
"render_styled(df.sort_values(\"Assets\", ascending=False).head(10))"
|
"render_styled(df.sort_values(\"Assets\", ascending=False).head(10))"
|
||||||
]
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"metadata": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
"source": [
|
||||||
"# 🔝 Top 10 Greatest Liabilities\n",
|
"# 🔝 Top 10 Greatest Liabilities\n",
|
||||||
"\n",
|
"\n",
|
||||||
"The top 10 margin accounts with most liabilities"
|
"The top 10 margin accounts with most liabilities"
|
||||||
]
|
],
|
||||||
|
"metadata": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
"source": [
|
||||||
"render_styled(df.sort_values(\"Liabilities\", ascending=False).head(10))"
|
"render_styled(df.sort_values(\"Liabilities\", ascending=False).head(10))"
|
||||||
]
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"metadata": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
"source": [
|
||||||
"# 🔝 Top 10 Least Collateralised\n",
|
"# 🔝 Top 10 Least Collateralised\n",
|
||||||
"\n",
|
"\n",
|
||||||
"The top 10 least collateralised margin accounts\n",
|
"The top 10 least collateralised margin accounts\n",
|
||||||
"\n",
|
"\n",
|
||||||
"Collect all margin accounts that have a non-zero Collateral Ratio (so have some liabilities). Then sort them from least-collateralised to most-collateralised."
|
"Collect all margin accounts that have a non-zero Collateral Ratio (so have some liabilities). Then sort them from least-collateralised to most-collateralised."
|
||||||
]
|
],
|
||||||
|
"metadata": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
"source": [
|
||||||
"nonzero = df[df[\"Collateral Ratio\"] != 0]\n",
|
"nonzero = df[df[\"Collateral Ratio\"] != 0]\n",
|
||||||
"render_styled(nonzero.sort_values(\"Collateral Ratio\", ascending=True).head(10))"
|
"render_styled(nonzero.sort_values(\"Collateral Ratio\", ascending=True).head(10))"
|
||||||
]
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"metadata": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
"source": [
|
||||||
"# 💧 Liquidatable\n",
|
"# 💧 Liquidatable\n",
|
||||||
"\n",
|
"\n",
|
||||||
"An account is 'liquidatable' when its available collateral falls below the group's maintenance collataeral threshold.\n",
|
"An account is 'liquidatable' when its available collateral falls below the group's maintenance collataeral threshold.\n",
|
||||||
"\n",
|
"\n",
|
||||||
"This code shows all liquidatable margin accounts, sorted by the available collateral (_not_ the collateral ratio)."
|
"This code shows all liquidatable margin accounts, sorted by the available collateral (_not_ the collateral ratio)."
|
||||||
]
|
],
|
||||||
|
"metadata": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
"source": [
|
||||||
"simplified = nonzero.drop([\"Settled Assets\", \"Unsettled Assets\"], axis=1)\n",
|
"simplified = nonzero.drop([\"Settled Assets\", \"Unsettled Assets\"], axis=1)\n",
|
||||||
"liquidatable = simplified[simplified[\"Collateral Ratio\"] < group.maint_coll_ratio].copy()\n",
|
"liquidatable = simplified[simplified[\"Collateral Ratio\"] < group.maint_coll_ratio].copy()\n",
|
||||||
"\n",
|
"\n",
|
||||||
"print(f\"There are {len(liquidatable)} liquidatable accounts.\")\n",
|
"print(f\"There are {len(liquidatable)} liquidatable accounts.\")\n",
|
||||||
"render_styled(liquidatable.sort_values(\"Available Collateral\", ascending=False).head(len(liquidatable)))"
|
"render_styled(liquidatable.sort_values(\"Available Collateral\", ascending=False).head(len(liquidatable)))"
|
||||||
]
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"metadata": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
"source": [
|
||||||
"# 🥭 Ripe Mangoes\n",
|
"# 🥭 Ripe Mangoes\n",
|
||||||
"\n",
|
"\n",
|
||||||
"'Ripe mangoes' are margin accounts that are below the group's initial margin requirements but have not yet fallen below the liquidation threshold.\n",
|
"'Ripe mangoes' are margin accounts that are below the group's initial margin requirements but have not yet fallen below the liquidation threshold.\n",
|
||||||
"\n",
|
"\n",
|
||||||
"This code shows all ripe mangoes, sorted by the available collateral (_not_ the collateral ratio)."
|
"This code shows all ripe mangoes, sorted by the available collateral (_not_ the collateral ratio)."
|
||||||
]
|
],
|
||||||
|
"metadata": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
"source": [
|
||||||
"ripe = simplified[simplified[\"Collateral Ratio\"] < group.init_coll_ratio]\n",
|
"ripe = simplified[simplified[\"Collateral Ratio\"] < group.init_coll_ratio]\n",
|
||||||
"only_ripe = ripe[ripe[\"Collateral Ratio\"] >= group.maint_coll_ratio].copy()\n",
|
"only_ripe = ripe[ripe[\"Collateral Ratio\"] >= group.maint_coll_ratio].copy()\n",
|
||||||
"\n",
|
"\n",
|
||||||
"print(f\"There are {len(only_ripe)} 🥭 ripe mangoes.\")\n",
|
"print(f\"There are {len(only_ripe)} 🥭 ripe mangoes.\")\n",
|
||||||
"render_styled(only_ripe.sort_values(\"Available Collateral\", ascending=False).head(len(only_ripe)))"
|
"render_styled(only_ripe.sort_values(\"Available Collateral\", ascending=False).head(len(only_ripe)))"
|
||||||
]
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"metadata": {}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
"cells": [
|
"cells": [
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
"source": [
|
||||||
"# ⚠ Warning\n",
|
"# ⚠ Warning\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
@ -11,11 +10,11 @@
|
||||||
"[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gl/OpinionatedGeek%2Fmango-explorer/HEAD?filepath=ShowAllMarginAccounts.ipynb) _🏃♀️ To run this notebook press the ⏩ icon in the toolbar above._\n",
|
"[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gl/OpinionatedGeek%2Fmango-explorer/HEAD?filepath=ShowAllMarginAccounts.ipynb) _🏃♀️ To run this notebook press the ⏩ icon in the toolbar above._\n",
|
||||||
"\n",
|
"\n",
|
||||||
"[🥭 Mango Markets](https://mango.markets/) support is available at: [Docs](https://docs.mango.markets/) | [Discord](https://discord.gg/67jySBhxrg) | [Twitter](https://twitter.com/mangomarkets) | [Github](https://github.com/blockworks-foundation) | [Email](mailto:hello@blockworks.foundation)"
|
"[🥭 Mango Markets](https://mango.markets/) support is available at: [Docs](https://docs.mango.markets/) | [Discord](https://discord.gg/67jySBhxrg) | [Twitter](https://twitter.com/mangomarkets) | [Github](https://github.com/blockworks-foundation) | [Email](mailto:hello@blockworks.foundation)"
|
||||||
]
|
],
|
||||||
|
"metadata": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
"source": [
|
||||||
"# 🥭 Show All Margin Accounts\n",
|
"# 🥭 Show All Margin Accounts\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
@ -24,26 +23,21 @@
|
||||||
"It fetches the data from Solana, parses it, and then prints it.\n",
|
"It fetches the data from Solana, parses it, and then prints it.\n",
|
||||||
"\n",
|
"\n",
|
||||||
"Note: this can take a long time to run."
|
"Note: this can take a long time to run."
|
||||||
]
|
],
|
||||||
|
"metadata": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "markdown",
|
"cell_type": "markdown",
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
"source": [
|
||||||
"## How To Use This Page\n",
|
"## How To Use This Page\n",
|
||||||
"\n",
|
"\n",
|
||||||
"Theo code should be runnable as-is. Just click the >> button in the toolbar above, and you should see output appear below the code."
|
"Theo code should be runnable as-is. Just click the >> button in the toolbar above, and you should see output appear below the code."
|
||||||
]
|
],
|
||||||
|
"metadata": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"cell_type": "code",
|
"cell_type": "code",
|
||||||
"execution_count": null,
|
"execution_count": null,
|
||||||
"metadata": {
|
|
||||||
"tags": [
|
|
||||||
"outputPrepend"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
"source": [
|
||||||
"if __name__ == \"__main__\":\n",
|
"if __name__ == \"__main__\":\n",
|
||||||
" import mango\n",
|
" import mango\n",
|
||||||
|
@ -57,7 +51,7 @@
|
||||||
" print(f\"Done loading group. Time taken: {time.time() - start_time}\")\n",
|
" print(f\"Done loading group. Time taken: {time.time() - start_time}\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
" print(\"Loading margin accounts...\")\n",
|
" print(\"Loading margin accounts...\")\n",
|
||||||
" margin_accounts = mango.MarginAccount.load_all_for_group_with_open_orders(context, context.program_id, group)\n",
|
" margin_accounts = mango.MarginAccount.load_all_for_group_with_open_orders(context, context.mango_program_address, group)\n",
|
||||||
" print(f\"Done loading {len(margin_accounts)} account(s). Total time taken: {time.time() - start_time}\")\n",
|
" print(f\"Done loading {len(margin_accounts)} account(s). Total time taken: {time.time() - start_time}\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
" print(*margin_accounts, sep=\"\\n\")\n",
|
" print(*margin_accounts, sep=\"\\n\")\n",
|
||||||
|
@ -67,7 +61,13 @@
|
||||||
" # import cProfile\n",
|
" # import cProfile\n",
|
||||||
" # import pstats\n",
|
" # import pstats\n",
|
||||||
" # cProfile.run(\"show_all_accounts()\", sort=pstats.SortKey.TIME)\n"
|
" # cProfile.run(\"show_all_accounts()\", sort=pstats.SortKey.TIME)\n"
|
||||||
]
|
],
|
||||||
|
"outputs": [],
|
||||||
|
"metadata": {
|
||||||
|
"tags": [
|
||||||
|
"outputPrepend"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
|
|
|
@ -25,7 +25,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
|
||||||
|
|
||||||
context = mango.ContextBuilder.from_command_line_parameters(args)
|
context = mango.ContextBuilder.from_command_line_parameters(args)
|
||||||
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
||||||
group = mango.Group.load(context, context.group_id)
|
group = mango.Group.load(context, context.group_address)
|
||||||
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
||||||
|
|
||||||
market_symbol = args.market.upper()
|
market_symbol = args.market.upper()
|
||||||
|
|
|
@ -33,7 +33,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
|
||||||
|
|
||||||
context = mango.ContextBuilder.from_command_line_parameters(args)
|
context = mango.ContextBuilder.from_command_line_parameters(args)
|
||||||
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
||||||
group = mango.Group.load(context, context.group_id)
|
group = mango.Group.load(context, context.group_address)
|
||||||
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
||||||
|
|
||||||
market_symbol = args.market.upper()
|
market_symbol = args.market.upper()
|
||||||
|
|
|
@ -29,7 +29,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
|
||||||
context = mango.ContextBuilder.from_command_line_parameters(args)
|
context = mango.ContextBuilder.from_command_line_parameters(args)
|
||||||
logging.info(f"Context: {context}")
|
logging.info(f"Context: {context}")
|
||||||
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
||||||
group = mango.Group.load(context, context.group_id)
|
group = mango.Group.load(context, context.group_address)
|
||||||
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
||||||
|
|
||||||
logging.info(f"Wallet address: {wallet.address}")
|
logging.info(f"Wallet address: {wallet.address}")
|
||||||
|
|
|
@ -27,7 +27,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
|
||||||
context = mango.ContextBuilder.from_command_line_parameters(args)
|
context = mango.ContextBuilder.from_command_line_parameters(args)
|
||||||
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
||||||
|
|
||||||
group = mango.Group.load(context, context.group_id)
|
group = mango.Group.load(context, context.group_address)
|
||||||
accounts = mango.Account.load_all_for_owner(context, wallet.address, group)
|
accounts = mango.Account.load_all_for_owner(context, wallet.address, group)
|
||||||
if len(accounts) == 0:
|
if len(accounts) == 0:
|
||||||
raise Exception(f"Could not find any Mango accounts for '{wallet.address}'.")
|
raise Exception(f"Could not find any Mango accounts for '{wallet.address}'.")
|
||||||
|
|
|
@ -42,7 +42,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
|
||||||
|
|
||||||
context = mango.ContextBuilder.from_command_line_parameters(args)
|
context = mango.ContextBuilder.from_command_line_parameters(args)
|
||||||
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
||||||
group = mango.Group.load(context, context.group_id)
|
group = mango.Group.load(context, context.group_address)
|
||||||
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
||||||
|
|
||||||
disposer = mango.DisposePropagator()
|
disposer = mango.DisposePropagator()
|
||||||
|
|
|
@ -21,7 +21,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
|
||||||
context = mango.ContextBuilder.from_command_line_parameters(args)
|
context = mango.ContextBuilder.from_command_line_parameters(args)
|
||||||
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
||||||
|
|
||||||
group = mango.Group.load(context, context.group_id)
|
group = mango.Group.load(context, context.group_address)
|
||||||
|
|
||||||
signers: mango.CombinableInstructions = mango.CombinableInstructions.from_wallet(wallet)
|
signers: mango.CombinableInstructions = mango.CombinableInstructions.from_wallet(wallet)
|
||||||
init = mango.build_create_account_instructions(context, wallet, group)
|
init = mango.build_create_account_instructions(context, wallet, group)
|
||||||
|
|
|
@ -74,7 +74,7 @@ health_check = mango.HealthCheck()
|
||||||
disposer.add_disposable(health_check)
|
disposer.add_disposable(health_check)
|
||||||
|
|
||||||
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
||||||
group = mango.Group.load(context, context.group_id)
|
group = mango.Group.load(context, context.group_address)
|
||||||
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
||||||
|
|
||||||
market_symbol = args.market.upper()
|
market_symbol = args.market.upper()
|
||||||
|
|
|
@ -35,7 +35,7 @@ if account_info is None:
|
||||||
else:
|
else:
|
||||||
account_sols = account_info.lamports / mango.SOL_DECIMAL_DIVISOR
|
account_sols = account_info.lamports / mango.SOL_DECIMAL_DIVISOR
|
||||||
if account_sols < args.minimum_sol_balance:
|
if account_sols < args.minimum_sol_balance:
|
||||||
report = f"Account \"{args.name} [{args.address}]\" on {context.client.cluster} has only {account_sols} SOL, which is below the minimum required balance of {args.minimum_sol_balance} SOL."
|
report = f"Account \"{args.name} [{args.address}]\" on {context.client.cluster_name} has only {account_sols} SOL, which is below the minimum required balance of {args.minimum_sol_balance} SOL."
|
||||||
for notify in args.notify:
|
for notify in args.notify:
|
||||||
notify.send(report)
|
notify.send(report)
|
||||||
print(f"Notification sent: {report}")
|
print(f"Notification sent: {report}")
|
||||||
|
|
|
@ -32,7 +32,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
|
||||||
|
|
||||||
context = mango.ContextBuilder.from_command_line_parameters(args)
|
context = mango.ContextBuilder.from_command_line_parameters(args)
|
||||||
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
||||||
group = mango.Group.load(context, context.group_id)
|
group = mango.Group.load(context, context.group_address)
|
||||||
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
||||||
|
|
||||||
market_symbol = args.market.upper()
|
market_symbol = args.market.upper()
|
||||||
|
|
|
@ -20,7 +20,7 @@ def report_accrued(basket_token: mango.AccountBasketBaseToken):
|
||||||
|
|
||||||
def load_perp_market(context: mango.Context, group: mango.Group, group_basket_market: mango.GroupBasketMarket):
|
def load_perp_market(context: mango.Context, group: mango.Group, group_basket_market: mango.GroupBasketMarket):
|
||||||
perp_market_details = mango.PerpMarketDetails.load(context, group_basket_market.perp_market_info.address, group)
|
perp_market_details = mango.PerpMarketDetails.load(context, group_basket_market.perp_market_info.address, group)
|
||||||
perp_market = mango.PerpMarket(context.program_id, group_basket_market.perp_market_info.address,
|
perp_market = mango.PerpMarket(context.mango_program_address, group_basket_market.perp_market_info.address,
|
||||||
group_basket_market.base_token_info.token,
|
group_basket_market.base_token_info.token,
|
||||||
group_basket_market.quote_token_info.token, perp_market_details)
|
group_basket_market.quote_token_info.token, perp_market_details)
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ if (not args.all) and (args.market is None):
|
||||||
context = mango.ContextBuilder.from_command_line_parameters(args)
|
context = mango.ContextBuilder.from_command_line_parameters(args)
|
||||||
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
||||||
|
|
||||||
group = mango.Group.load(context, context.group_id)
|
group = mango.Group.load(context, context.group_address)
|
||||||
mngo = group.find_token_info_by_symbol("MNGO")
|
mngo = group.find_token_info_by_symbol("MNGO")
|
||||||
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
|
||||||
|
|
||||||
context = mango.ContextBuilder.from_command_line_parameters(args)
|
context = mango.ContextBuilder.from_command_line_parameters(args)
|
||||||
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
||||||
group = mango.Group.load(context, context.group_id)
|
group = mango.Group.load(context, context.group_address)
|
||||||
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
||||||
|
|
||||||
logging.info(f"Context: {context}")
|
logging.info(f"Context: {context}")
|
||||||
|
|
|
@ -29,6 +29,6 @@ if address is None:
|
||||||
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
||||||
address = wallet.address
|
address = wallet.address
|
||||||
|
|
||||||
group = mango.Group.load(context, context.group_id)
|
group = mango.Group.load(context, context.group_address)
|
||||||
mango_accounts = mango.Account.load_all_for_owner(context, address, group)
|
mango_accounts = mango.Account.load_all_for_owner(context, address, group)
|
||||||
print(mango_accounts)
|
print(mango_accounts)
|
||||||
|
|
|
@ -26,7 +26,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
|
||||||
|
|
||||||
context = mango.ContextBuilder.from_command_line_parameters(args)
|
context = mango.ContextBuilder.from_command_line_parameters(args)
|
||||||
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
||||||
group = mango.Group.load(context, context.group_id)
|
group = mango.Group.load(context, context.group_address)
|
||||||
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
||||||
|
|
||||||
market_symbol = args.market.upper()
|
market_symbol = args.market.upper()
|
||||||
|
|
|
@ -25,7 +25,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
|
||||||
|
|
||||||
context = mango.ContextBuilder.from_command_line_parameters(args)
|
context = mango.ContextBuilder.from_command_line_parameters(args)
|
||||||
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
||||||
group = mango.Group.load(context, context.group_id)
|
group = mango.Group.load(context, context.group_address)
|
||||||
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
||||||
|
|
||||||
market_symbol = args.market.upper()
|
market_symbol = args.market.upper()
|
||||||
|
|
|
@ -25,7 +25,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
|
||||||
|
|
||||||
context = mango.ContextBuilder.from_command_line_parameters(args)
|
context = mango.ContextBuilder.from_command_line_parameters(args)
|
||||||
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
||||||
group = mango.Group.load(context, context.group_id)
|
group = mango.Group.load(context, context.group_address)
|
||||||
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
||||||
|
|
||||||
market_symbol = args.market.upper()
|
market_symbol = args.market.upper()
|
||||||
|
|
|
@ -32,7 +32,7 @@ if not isinstance(market, mango.SerumMarket):
|
||||||
raise Exception(f"Market {market_symbol} is not a Serum market: {market}")
|
raise Exception(f"Market {market_symbol} is not a Serum market: {market}")
|
||||||
|
|
||||||
all_open_orders_for_market = mango.OpenOrders.load_for_market_and_owner(
|
all_open_orders_for_market = mango.OpenOrders.load_for_market_and_owner(
|
||||||
context, market.address, wallet.address, context.dex_program_id, market.base.decimals, market.quote.decimals)
|
context, market.address, wallet.address, context.serum_program_address, market.base.decimals, market.quote.decimals)
|
||||||
print(f"Found {len(all_open_orders_for_market)} OpenOrders account(s) for market {market.symbol}.")
|
print(f"Found {len(all_open_orders_for_market)} OpenOrders account(s) for market {market.symbol}.")
|
||||||
for open_orders in all_open_orders_for_market:
|
for open_orders in all_open_orders_for_market:
|
||||||
print(open_orders)
|
print(open_orders)
|
||||||
|
|
|
@ -43,7 +43,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
|
||||||
try:
|
try:
|
||||||
context = mango.ContextBuilder.from_command_line_parameters(args)
|
context = mango.ContextBuilder.from_command_line_parameters(args)
|
||||||
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
||||||
group = mango.Group.load(context, context.group_id)
|
group = mango.Group.load(context, context.group_address)
|
||||||
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
|
||||||
|
|
||||||
market_symbol = args.market.upper()
|
market_symbol = args.market.upper()
|
||||||
|
|
|
@ -55,7 +55,7 @@ def sols_from_lamports(lamports: Decimal) -> Decimal:
|
||||||
def notifier(name: str) -> typing.Callable[[mango.AccountInfo], None]:
|
def notifier(name: str) -> typing.Callable[[mango.AccountInfo], None]:
|
||||||
def notify(account_info: mango.AccountInfo) -> None:
|
def notify(account_info: mango.AccountInfo) -> None:
|
||||||
account_sols = sols_from_lamports(account_info.lamports)
|
account_sols = sols_from_lamports(account_info.lamports)
|
||||||
report = f"Account \"{name} [{account_info.address}]\" on {context.client.cluster} has only {account_sols} SOL, which is below the minimum required balance of {args.minimum_sol_balance} SOL."
|
report = f"Account \"{name} [{account_info.address}]\" on {context.client.cluster_name} has only {account_sols} SOL, which is below the minimum required balance of {args.minimum_sol_balance} SOL."
|
||||||
send_balance_notification(report)
|
send_balance_notification(report)
|
||||||
print(f"Notification sent: {report}")
|
print(f"Notification sent: {report}")
|
||||||
return notify
|
return notify
|
||||||
|
|
|
@ -29,7 +29,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
|
||||||
context = mango.ContextBuilder.from_command_line_parameters(args)
|
context = mango.ContextBuilder.from_command_line_parameters(args)
|
||||||
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
|
||||||
|
|
||||||
group = mango.Group.load(context, context.group_id)
|
group = mango.Group.load(context, context.group_address)
|
||||||
accounts = mango.Account.load_all_for_owner(context, wallet.address, group)
|
accounts = mango.Account.load_all_for_owner(context, wallet.address, group)
|
||||||
if len(accounts) == 0:
|
if len(accounts) == 0:
|
||||||
raise Exception(f"Could not find any margin accounts for '{wallet.address}'.")
|
raise Exception(f"Could not find any margin accounts for '{wallet.address}'.")
|
||||||
|
|
|
@ -28,7 +28,7 @@ while :
|
||||||
do
|
do
|
||||||
cancel-my-orders --name "WSMM ${MARKET} (cancel)" --market $MARKET --log-level ERROR
|
cancel-my-orders --name "WSMM ${MARKET} (cancel)" --market $MARKET --log-level ERROR
|
||||||
|
|
||||||
CURRENT_PRICE=$(fetch-price --provider serum --symbol $ORACLE_MARKET --log-level ERROR --cluster mainnet | cut -d"'" -f 2 | sed 's/,//')
|
CURRENT_PRICE=$(fetch-price --provider serum --symbol $ORACLE_MARKET --log-level ERROR --cluster-name mainnet | cut -d"'" -f 2 | sed 's/,//')
|
||||||
place-order --name "WSMM ${MARKET} (buy)" --market $MARKET --order-type LIMIT \
|
place-order --name "WSMM ${MARKET} (buy)" --market $MARKET --order-type LIMIT \
|
||||||
--log-level ERROR --side BUY --quantity $FIXED_POSITION_SIZE --price $(echo "$CURRENT_PRICE - $FIXED_SPREAD" | bc)
|
--log-level ERROR --side BUY --quantity $FIXED_POSITION_SIZE --price $(echo "$CURRENT_PRICE - $FIXED_SPREAD" | bc)
|
||||||
place-order --name "WSMM ${MARKET} (sell)" --market $MARKET --order-type LIMIT \
|
place-order --name "WSMM ${MARKET} (sell)" --market $MARKET --order-type LIMIT \
|
||||||
|
|
|
@ -206,7 +206,7 @@ class Account(AddressableAccount):
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
results = context.client.get_program_accounts(context.program_id, memcmp_opts=filters)
|
results = context.client.get_program_accounts(context.mango_program_address, memcmp_opts=filters)
|
||||||
accounts = []
|
accounts = []
|
||||||
for account_data in results:
|
for account_data in results:
|
||||||
address = PublicKey(account_data["pubkey"])
|
address = PublicKey(account_data["pubkey"])
|
||||||
|
|
|
@ -134,10 +134,10 @@ UnspecifiedEncoding = "unspecified"
|
||||||
# some common operations better from our point of view.
|
# some common operations better from our point of view.
|
||||||
#
|
#
|
||||||
class CompatibleClient:
|
class CompatibleClient:
|
||||||
def __init__(self, name: str, cluster: str, cluster_url: str, commitment: Commitment, skip_preflight: bool, instruction_reporter: InstructionReporter):
|
def __init__(self, name: str, cluster_name: str, cluster_url: str, commitment: Commitment, skip_preflight: bool, instruction_reporter: InstructionReporter):
|
||||||
self.logger: logging.Logger = logging.getLogger(self.__class__.__name__)
|
self.logger: logging.Logger = logging.getLogger(self.__class__.__name__)
|
||||||
self.name: str = name
|
self.name: str = name
|
||||||
self.cluster: str = cluster
|
self.cluster_name: str = cluster_name
|
||||||
self.cluster_url: str = cluster_url
|
self.cluster_url: str = cluster_url
|
||||||
self.commitment: Commitment = commitment
|
self.commitment: Commitment = commitment
|
||||||
self.skip_preflight: bool = skip_preflight
|
self.skip_preflight: bool = skip_preflight
|
||||||
|
@ -334,7 +334,7 @@ class CompatibleClient:
|
||||||
return self._build_options(commitment, encoding_to_use, data_slice)
|
return self._build_options(commitment, encoding_to_use, data_slice)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"« 𝙲𝚘𝚖𝚙𝚊𝚝𝚒𝚋𝚕𝚎𝙲𝚕𝚒𝚎𝚗𝚝 [{self.cluster}]: {self.cluster_url} »"
|
return f"« 𝙲𝚘𝚖𝚙𝚊𝚝𝚒𝚋𝚕𝚎𝙲𝚕𝚒𝚎𝚗𝚝 [{self.cluster_name}]: {self.cluster_url} »"
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"{self}"
|
return f"{self}"
|
||||||
|
@ -351,12 +351,12 @@ class BetterClient:
|
||||||
8), Decimal(16), Decimal(20), Decimal(30)]
|
8), Decimal(16), Decimal(20), Decimal(30)]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cluster(self) -> str:
|
def cluster_name(self) -> str:
|
||||||
return self.compatible_client.cluster
|
return self.compatible_client.cluster_name
|
||||||
|
|
||||||
@cluster.setter
|
@cluster_name.setter
|
||||||
def cluster(self, value: str) -> None:
|
def cluster_name(self, value: str) -> None:
|
||||||
self.compatible_client.cluster = value
|
self.compatible_client.cluster_name = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cluster_url(self) -> str:
|
def cluster_url(self) -> str:
|
||||||
|
@ -399,8 +399,8 @@ class BetterClient:
|
||||||
self.compatible_client.instruction_reporter = value
|
self.compatible_client.instruction_reporter = value
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_configuration(name: str, cluster: str, cluster_url: str, commitment: Commitment, skip_preflight: bool, instruction_reporter: InstructionReporter) -> "BetterClient":
|
def from_configuration(name: str, cluster_name: str, cluster_url: str, commitment: Commitment, skip_preflight: bool, instruction_reporter: InstructionReporter) -> "BetterClient":
|
||||||
compatible = CompatibleClient(name, cluster, cluster_url, commitment, skip_preflight, instruction_reporter)
|
compatible = CompatibleClient(name, cluster_name, cluster_url, commitment, skip_preflight, instruction_reporter)
|
||||||
return BetterClient(compatible)
|
return BetterClient(compatible)
|
||||||
|
|
||||||
def is_node_healthy(self) -> bool:
|
def is_node_healthy(self) -> bool:
|
||||||
|
@ -480,7 +480,7 @@ class BetterClient:
|
||||||
return all_confirmed
|
return all_confirmed
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"« 𝙱𝚎𝚝𝚝𝚎𝚛𝙲𝚕𝚒𝚎𝚗𝚝 [{self.cluster}]: {self.cluster_url} »"
|
return f"« 𝙱𝚎𝚝𝚝𝚎𝚛𝙲𝚕𝚒𝚎𝚗𝚝 [{self.cluster_name}]: {self.cluster_url} »"
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"{self}"
|
return f"{self}"
|
||||||
|
|
|
@ -40,17 +40,19 @@ _pool_scheduler = ThreadPoolScheduler(multiprocessing.cpu_count())
|
||||||
|
|
||||||
|
|
||||||
class Context:
|
class Context:
|
||||||
def __init__(self, name: str, cluster: str, cluster_url: str, skip_preflight: bool, program_id: PublicKey, dex_program_id: PublicKey,
|
def __init__(self, name: str, cluster_name: str, cluster_url: str, skip_preflight: bool, mango_program_address: PublicKey,
|
||||||
group_name: str, group_id: PublicKey, token_lookup: TokenLookup, market_lookup: MarketLookup):
|
serum_program_address: PublicKey, group_name: str, group_address: PublicKey,
|
||||||
|
token_lookup: TokenLookup, market_lookup: MarketLookup):
|
||||||
self.logger: logging.Logger = logging.getLogger(self.__class__.__name__)
|
self.logger: logging.Logger = logging.getLogger(self.__class__.__name__)
|
||||||
self.name: str = name
|
self.name: str = name
|
||||||
instruction_reporter: InstructionReporter = CompoundInstructionReporter.from_ids(program_id, dex_program_id)
|
instruction_reporter: InstructionReporter = CompoundInstructionReporter.from_addresses(
|
||||||
|
mango_program_address, serum_program_address)
|
||||||
self.client: BetterClient = BetterClient.from_configuration(
|
self.client: BetterClient = BetterClient.from_configuration(
|
||||||
name, cluster, cluster_url, Commitment("processed"), skip_preflight, instruction_reporter)
|
name, cluster_name, cluster_url, Commitment("processed"), skip_preflight, instruction_reporter)
|
||||||
self.program_id: PublicKey = program_id
|
self.mango_program_address: PublicKey = mango_program_address
|
||||||
self.dex_program_id: PublicKey = dex_program_id
|
self.serum_program_address: PublicKey = serum_program_address
|
||||||
self.group_name: str = group_name
|
self.group_name: str = group_name
|
||||||
self.group_id: PublicKey = group_id
|
self.group_address: PublicKey = group_address
|
||||||
self.token_lookup: TokenLookup = token_lookup
|
self.token_lookup: TokenLookup = token_lookup
|
||||||
self.market_lookup: MarketLookup = market_lookup
|
self.market_lookup: MarketLookup = market_lookup
|
||||||
|
|
||||||
|
@ -74,19 +76,19 @@ class Context:
|
||||||
def lookup_group_name(self, group_address: PublicKey) -> str:
|
def lookup_group_name(self, group_address: PublicKey) -> str:
|
||||||
group_address_str = str(group_address)
|
group_address_str = str(group_address)
|
||||||
for group in MangoConstants["groups"]:
|
for group in MangoConstants["groups"]:
|
||||||
if group["cluster"] == self.client.cluster and group["publicKey"] == group_address_str:
|
if group["cluster"] == self.client.cluster_name and group["publicKey"] == group_address_str:
|
||||||
return group["name"]
|
return group["name"]
|
||||||
|
|
||||||
return "« Unknown Group »"
|
return "« Unknown Group »"
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"""« 𝙲𝚘𝚗𝚝𝚎𝚡𝚝 '{self.name}':
|
return f"""« 𝙲𝚘𝚗𝚝𝚎𝚡𝚝 '{self.name}':
|
||||||
Cluster: {self.client.cluster}
|
Cluster Name: {self.client.cluster_name}
|
||||||
Cluster URL: {self.client.cluster_url}
|
Cluster URL: {self.client.cluster_url}
|
||||||
Program ID: {self.program_id}
|
|
||||||
DEX Program ID: {self.dex_program_id}
|
|
||||||
Group Name: {self.group_name}
|
Group Name: {self.group_name}
|
||||||
Group ID: {self.group_id}
|
Group Address: {self.group_address}
|
||||||
|
Mango Program Address: {self.mango_program_address}
|
||||||
|
Serum Program Address: {self.serum_program_address}
|
||||||
»"""
|
»"""
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
|
|
|
@ -43,7 +43,7 @@ from .tokenlookup import TokenLookup, CompoundTokenLookup
|
||||||
# * CLUSTER_URL
|
# * CLUSTER_URL
|
||||||
# * GROUP_NAME
|
# * GROUP_NAME
|
||||||
# * GROUP_ADDRESS
|
# * GROUP_ADDRESS
|
||||||
# * PROGRAM_ADDRESS
|
# * MANGO_PROGRAM_ADDRESS
|
||||||
# * SERUM_PROGRAM_ADDRESS
|
# * SERUM_PROGRAM_ADDRESS
|
||||||
|
|
||||||
|
|
||||||
|
@ -61,13 +61,13 @@ class ContextBuilder:
|
||||||
def add_command_line_parameters(parser: argparse.ArgumentParser, logging_default=logging.INFO) -> None:
|
def add_command_line_parameters(parser: argparse.ArgumentParser, logging_default=logging.INFO) -> None:
|
||||||
parser.add_argument("--name", type=str, default="Mango Explorer",
|
parser.add_argument("--name", type=str, default="Mango Explorer",
|
||||||
help="Name of the program (used in reports and alerts)")
|
help="Name of the program (used in reports and alerts)")
|
||||||
parser.add_argument("--cluster", type=str, default=None, help="Solana RPC cluster name")
|
parser.add_argument("--cluster-name", type=str, default=None, help="Solana RPC cluster name")
|
||||||
parser.add_argument("--cluster-url", type=str, default=None, help="Solana RPC cluster URL")
|
parser.add_argument("--cluster-url", type=str, default=None, help="Solana RPC cluster URL")
|
||||||
parser.add_argument("--skip-preflight", default=False, action="store_true", help="Skip pre-flight checks")
|
|
||||||
parser.add_argument("--program-id", type=PublicKey, default=None, help="Mango program ID/address")
|
|
||||||
parser.add_argument("--dex-program-id", type=PublicKey, default=None, help="DEX program ID/address")
|
|
||||||
parser.add_argument("--group-name", type=str, default=None, help="Mango group name")
|
parser.add_argument("--group-name", type=str, default=None, help="Mango group name")
|
||||||
parser.add_argument("--group-id", type=PublicKey, default=None, help="Mango group ID/address")
|
parser.add_argument("--group-address", type=PublicKey, default=None, help="Mango group address")
|
||||||
|
parser.add_argument("--mango-program-address", type=PublicKey, default=None, help="Mango program address")
|
||||||
|
parser.add_argument("--serum-program-address", type=PublicKey, default=None, help="Serum program address")
|
||||||
|
parser.add_argument("--skip-preflight", default=False, action="store_true", help="Skip pre-flight checks")
|
||||||
|
|
||||||
parser.add_argument("--token-data-file", type=str, default=SplTokenLookup.DefaultDataFilepath,
|
parser.add_argument("--token-data-file", type=str, default=SplTokenLookup.DefaultDataFilepath,
|
||||||
help="data file that contains token symbols, names, mints and decimals (format is same as https://raw.githubusercontent.com/solana-labs/token-list/main/src/tokens/solana.tokenlist.json)")
|
help="data file that contains token symbols, names, mints and decimals (format is same as https://raw.githubusercontent.com/solana-labs/token-list/main/src/tokens/solana.tokenlist.json)")
|
||||||
|
@ -85,25 +85,17 @@ class ContextBuilder:
|
||||||
#
|
#
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_command_line_parameters(args: argparse.Namespace) -> "Context":
|
def from_command_line_parameters(args: argparse.Namespace) -> "Context":
|
||||||
# Here we should have values for all our parameters (because they'll either be specified
|
|
||||||
# on the command-line or will be the default_* value) but we may be in the situation where
|
|
||||||
# a group name is specified but not a group ID, and in that case we want to look up the
|
|
||||||
# group ID.
|
|
||||||
#
|
|
||||||
# In that situation, the group_name will not be default_group_name but the group_id will
|
|
||||||
# still be default_group_id. In that situation we want to override what we were passed
|
|
||||||
# as the group_id.
|
|
||||||
name: typing.Optional[str] = args.name
|
name: typing.Optional[str] = args.name
|
||||||
group_name: typing.Optional[str] = args.group_name
|
cluster_name: typing.Optional[str] = args.cluster_name
|
||||||
cluster: typing.Optional[str] = args.cluster
|
|
||||||
cluster_url: typing.Optional[str] = args.cluster_url
|
cluster_url: typing.Optional[str] = args.cluster_url
|
||||||
|
group_name: typing.Optional[str] = args.group_name
|
||||||
|
group_address: typing.Optional[PublicKey] = args.group_address
|
||||||
|
mango_program_address: typing.Optional[PublicKey] = args.mango_program_address
|
||||||
|
serum_program_address: typing.Optional[PublicKey] = args.serum_program_address
|
||||||
skip_preflight: bool = bool(args.skip_preflight)
|
skip_preflight: bool = bool(args.skip_preflight)
|
||||||
group_id: typing.Optional[PublicKey] = args.group_id
|
|
||||||
program_id: typing.Optional[PublicKey] = args.program_id
|
|
||||||
dex_program_id: typing.Optional[PublicKey] = args.dex_program_id
|
|
||||||
token_filename: str = args.token_data_file
|
token_filename: str = args.token_data_file
|
||||||
|
|
||||||
return ContextBuilder._build(name, cluster, cluster_url, skip_preflight, group_name, group_id, program_id, dex_program_id, token_filename)
|
return ContextBuilder._build(name, cluster_name, cluster_url, skip_preflight, group_name, group_address, mango_program_address, serum_program_address, token_filename)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def default():
|
def default():
|
||||||
|
@ -111,21 +103,21 @@ class ContextBuilder:
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_group_name(context: Context, group_name: str) -> Context:
|
def from_group_name(context: Context, group_name: str) -> Context:
|
||||||
return ContextBuilder._build(context.name, context.client.cluster, context.client.cluster_url,
|
return ContextBuilder._build(context.name, context.client.cluster_name, context.client.cluster_url,
|
||||||
context.client.skip_preflight, group_name, None,
|
context.client.skip_preflight, group_name, None,
|
||||||
None, None, SplTokenLookup.DefaultDataFilepath)
|
None, None, SplTokenLookup.DefaultDataFilepath)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def forced_to_devnet(context: Context) -> Context:
|
def forced_to_devnet(context: Context) -> Context:
|
||||||
cluster: str = "devnet"
|
cluster_name: str = "devnet"
|
||||||
cluster_url: str = MangoConstants["cluster_urls"][cluster]
|
cluster_url: str = MangoConstants["cluster_urls"][cluster_name]
|
||||||
return ContextBuilder._build(context.name, cluster, cluster_url, context.client.skip_preflight, context.group_name, context.group_id, context.program_id, context.dex_program_id, SplTokenLookup.DefaultDataFilepath)
|
return ContextBuilder._build(context.name, cluster_name, cluster_url, context.client.skip_preflight, context.group_name, context.group_address, context.mango_program_address, context.serum_program_address, SplTokenLookup.DefaultDataFilepath)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def forced_to_mainnet_beta(context: Context) -> Context:
|
def forced_to_mainnet_beta(context: Context) -> Context:
|
||||||
cluster: str = "mainnet"
|
cluster_name: str = "mainnet"
|
||||||
cluster_url: str = MangoConstants["cluster_urls"][cluster]
|
cluster_url: str = MangoConstants["cluster_urls"][cluster_name]
|
||||||
return ContextBuilder._build(context.name, cluster, cluster_url, context.client.skip_preflight, context.group_name, context.group_id, context.program_id, context.dex_program_id, SplTokenLookup.DefaultDataFilepath)
|
return ContextBuilder._build(context.name, cluster_name, cluster_url, context.client.skip_preflight, context.group_name, context.group_address, context.mango_program_address, context.serum_program_address, SplTokenLookup.DefaultDataFilepath)
|
||||||
|
|
||||||
# This function is the converse of `add_command_line_parameters()` - it takes
|
# This function is the converse of `add_command_line_parameters()` - it takes
|
||||||
# an argument of parsed command-line parameters and expects to see the ones it added
|
# an argument of parsed command-line parameters and expects to see the ones it added
|
||||||
|
@ -134,7 +126,7 @@ class ContextBuilder:
|
||||||
# It then uses those parameters to create a properly-configured `Context` object.
|
# It then uses those parameters to create a properly-configured `Context` object.
|
||||||
#
|
#
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _build(name: typing.Optional[str], cluster: typing.Optional[str], cluster_url: typing.Optional[str],
|
def _build(name: typing.Optional[str], cluster_name: typing.Optional[str], cluster_url: typing.Optional[str],
|
||||||
skip_preflight: bool, group_name: typing.Optional[str], group_address: typing.Optional[PublicKey],
|
skip_preflight: bool, group_name: typing.Optional[str], group_address: typing.Optional[PublicKey],
|
||||||
program_address: typing.Optional[PublicKey], serum_program_address: typing.Optional[PublicKey],
|
program_address: typing.Optional[PublicKey], serum_program_address: typing.Optional[PublicKey],
|
||||||
token_filename: str) -> "Context":
|
token_filename: str) -> "Context":
|
||||||
|
@ -144,7 +136,7 @@ class ContextBuilder:
|
||||||
return None
|
return None
|
||||||
default_group_data = MangoConstants["groups"][0]
|
default_group_data = MangoConstants["groups"][0]
|
||||||
actual_name: str = name or os.environ.get("NAME") or "Mango Explorer"
|
actual_name: str = name or os.environ.get("NAME") or "Mango Explorer"
|
||||||
actual_cluster: str = cluster or os.environ.get("CLUSTER") or default_group_data["cluster"]
|
actual_cluster: str = cluster_name or os.environ.get("CLUSTER") or default_group_data["cluster"]
|
||||||
actual_cluster_url: str = cluster_url or os.environ.get(
|
actual_cluster_url: str = cluster_url or os.environ.get(
|
||||||
"CLUSTER_URL") or MangoConstants["cluster_urls"][actual_cluster]
|
"CLUSTER_URL") or MangoConstants["cluster_urls"][actual_cluster]
|
||||||
actual_skip_preflight: bool = skip_preflight
|
actual_skip_preflight: bool = skip_preflight
|
||||||
|
@ -156,12 +148,12 @@ class ContextBuilder:
|
||||||
found_group_data = group
|
found_group_data = group
|
||||||
|
|
||||||
if found_group_data is None:
|
if found_group_data is None:
|
||||||
raise Exception(f"Could not find group named '{actual_group_name}' in cluster '{actual_cluster}.")
|
raise Exception(f"Could not find group named '{actual_group_name}' in cluster_name '{actual_cluster}.")
|
||||||
|
|
||||||
actual_group_address: PublicKey = group_address or public_key_or_none(os.environ.get(
|
actual_group_address: PublicKey = group_address or public_key_or_none(os.environ.get(
|
||||||
"GROUP_ADDRESS")) or PublicKey(found_group_data["publicKey"])
|
"GROUP_ADDRESS")) or PublicKey(found_group_data["publicKey"])
|
||||||
actual_program_address: PublicKey = program_address or public_key_or_none(os.environ.get(
|
actual_program_address: PublicKey = program_address or public_key_or_none(os.environ.get(
|
||||||
"PROGRAM_ADDRESS")) or PublicKey(found_group_data["mangoProgramId"])
|
"MANGO_PROGRAM_ADDRESS")) or PublicKey(found_group_data["mangoProgramId"])
|
||||||
actual_serum_program_address: PublicKey = serum_program_address or public_key_or_none(os.environ.get(
|
actual_serum_program_address: PublicKey = serum_program_address or public_key_or_none(os.environ.get(
|
||||||
"SERUM_PROGRAM_ADDRESS")) or PublicKey(found_group_data["serumProgramId"])
|
"SERUM_PROGRAM_ADDRESS")) or PublicKey(found_group_data["serumProgramId"])
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@ class Group(AddressableAccount):
|
||||||
basket_indices: typing.Sequence[bool],
|
basket_indices: typing.Sequence[bool],
|
||||||
basket: typing.Sequence[GroupBasketMarket],
|
basket: typing.Sequence[GroupBasketMarket],
|
||||||
signer_nonce: Decimal, signer_key: PublicKey,
|
signer_nonce: Decimal, signer_key: PublicKey,
|
||||||
admin: PublicKey, dex_program_id: PublicKey, cache: PublicKey, valid_interval: Decimal,
|
admin: PublicKey, serum_program_address: PublicKey, cache: PublicKey, valid_interval: Decimal,
|
||||||
dao_vault: PublicKey, srm_vault: PublicKey, msrm_vault: PublicKey):
|
dao_vault: PublicKey, srm_vault: PublicKey, msrm_vault: PublicKey):
|
||||||
super().__init__(account_info)
|
super().__init__(account_info)
|
||||||
self.version: Version = version
|
self.version: Version = version
|
||||||
|
@ -95,7 +95,7 @@ class Group(AddressableAccount):
|
||||||
self.signer_nonce: Decimal = signer_nonce
|
self.signer_nonce: Decimal = signer_nonce
|
||||||
self.signer_key: PublicKey = signer_key
|
self.signer_key: PublicKey = signer_key
|
||||||
self.admin: PublicKey = admin
|
self.admin: PublicKey = admin
|
||||||
self.dex_program_id: PublicKey = dex_program_id
|
self.serum_program_address: PublicKey = serum_program_address
|
||||||
self.cache: PublicKey = cache
|
self.cache: PublicKey = cache
|
||||||
self.valid_interval: Decimal = valid_interval
|
self.valid_interval: Decimal = valid_interval
|
||||||
self.dao_vault: PublicKey = dao_vault
|
self.dao_vault: PublicKey = dao_vault
|
||||||
|
@ -151,14 +151,14 @@ class Group(AddressableAccount):
|
||||||
signer_nonce: Decimal = layout.signer_nonce
|
signer_nonce: Decimal = layout.signer_nonce
|
||||||
signer_key: PublicKey = layout.signer_key
|
signer_key: PublicKey = layout.signer_key
|
||||||
admin: PublicKey = layout.admin
|
admin: PublicKey = layout.admin
|
||||||
dex_program_id: PublicKey = layout.dex_program_id
|
serum_program_address: PublicKey = layout.serum_program_address
|
||||||
cache: PublicKey = layout.cache
|
cache: PublicKey = layout.cache
|
||||||
valid_interval: Decimal = layout.valid_interval
|
valid_interval: Decimal = layout.valid_interval
|
||||||
dao_vault: PublicKey = layout.dao_vault
|
dao_vault: PublicKey = layout.dao_vault
|
||||||
srm_vault: PublicKey = layout.srm_vault
|
srm_vault: PublicKey = layout.srm_vault
|
||||||
msrm_vault: PublicKey = layout.msrm_vault
|
msrm_vault: PublicKey = layout.msrm_vault
|
||||||
|
|
||||||
return Group(account_info, version, name, meta_data, quote_token_info, in_basket, basket, signer_nonce, signer_key, admin, dex_program_id, cache, valid_interval, dao_vault, srm_vault, msrm_vault)
|
return Group(account_info, version, name, meta_data, quote_token_info, in_basket, basket, signer_nonce, signer_key, admin, serum_program_address, cache, valid_interval, dao_vault, srm_vault, msrm_vault)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse(context: Context, account_info: AccountInfo) -> "Group":
|
def parse(context: Context, account_info: AccountInfo) -> "Group":
|
||||||
|
@ -186,7 +186,7 @@ class Group(AddressableAccount):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load(context: Context, address: typing.Optional[PublicKey] = None) -> "Group":
|
def load(context: Context, address: typing.Optional[PublicKey] = None) -> "Group":
|
||||||
group_address: PublicKey = address or context.group_id
|
group_address: PublicKey = address or context.group_address
|
||||||
account_info = AccountInfo.load(context, group_address)
|
account_info = AccountInfo.load(context, group_address)
|
||||||
if account_info is None:
|
if account_info is None:
|
||||||
raise Exception(f"Group account not found at address '{group_address}'")
|
raise Exception(f"Group account not found at address '{group_address}'")
|
||||||
|
@ -239,7 +239,7 @@ class Group(AddressableAccount):
|
||||||
Name: {self.name}
|
Name: {self.name}
|
||||||
Signer [Nonce: {self.signer_nonce}]: {self.signer_key}
|
Signer [Nonce: {self.signer_nonce}]: {self.signer_key}
|
||||||
Admin: {self.admin}
|
Admin: {self.admin}
|
||||||
DEX Program ID: {self.dex_program_id}
|
DEX Program ID: {self.serum_program_address}
|
||||||
Cache: {self.cache}
|
Cache: {self.cache}
|
||||||
DAO Vault: {self.dao_vault}
|
DAO Vault: {self.dao_vault}
|
||||||
SRM Vault: {self.srm_vault}
|
SRM Vault: {self.srm_vault}
|
||||||
|
|
|
@ -45,20 +45,20 @@ class IdsJsonMarketType(enum.Enum):
|
||||||
|
|
||||||
|
|
||||||
class IdsJsonMarketLookup(MarketLookup):
|
class IdsJsonMarketLookup(MarketLookup):
|
||||||
def __init__(self, cluster: str) -> None:
|
def __init__(self, cluster_name: str) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.cluster: str = cluster
|
self.cluster_name: str = cluster_name
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _from_dict(market_type: IdsJsonMarketType, program_id: PublicKey, group_address: PublicKey, data: typing.Dict, tokens: typing.Sequence[Token], quote_symbol: str) -> Market:
|
def _from_dict(market_type: IdsJsonMarketType, mango_program_address: PublicKey, group_address: PublicKey, data: typing.Dict, tokens: typing.Sequence[Token], quote_symbol: str) -> Market:
|
||||||
base_symbol = data["baseSymbol"]
|
base_symbol = data["baseSymbol"]
|
||||||
base = Token.find_by_symbol(tokens, base_symbol)
|
base = Token.find_by_symbol(tokens, base_symbol)
|
||||||
quote = Token.find_by_symbol(tokens, quote_symbol)
|
quote = Token.find_by_symbol(tokens, quote_symbol)
|
||||||
address = PublicKey(data["publicKey"])
|
address = PublicKey(data["publicKey"])
|
||||||
if market_type == IdsJsonMarketType.PERP:
|
if market_type == IdsJsonMarketType.PERP:
|
||||||
return PerpMarketStub(program_id, address, base, quote, group_address)
|
return PerpMarketStub(mango_program_address, address, base, quote, group_address)
|
||||||
else:
|
else:
|
||||||
return SpotMarketStub(program_id, address, base, quote, group_address)
|
return SpotMarketStub(mango_program_address, address, base, quote, group_address)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _load_tokens(data: typing.Dict) -> typing.Sequence[Token]:
|
def _load_tokens(data: typing.Dict) -> typing.Sequence[Token]:
|
||||||
|
@ -80,51 +80,51 @@ class IdsJsonMarketLookup(MarketLookup):
|
||||||
check_spots = False # Skip spot markets because we're explicitly told it's a perp
|
check_spots = False # Skip spot markets because we're explicitly told it's a perp
|
||||||
|
|
||||||
for group in MangoConstants["groups"]:
|
for group in MangoConstants["groups"]:
|
||||||
if group["cluster"] == self.cluster:
|
if group["cluster"] == self.cluster_name:
|
||||||
group_address: PublicKey = PublicKey(group["publicKey"])
|
group_address: PublicKey = PublicKey(group["publicKey"])
|
||||||
program_id: PublicKey = PublicKey(group["mangoProgramId"])
|
mango_program_address: PublicKey = PublicKey(group["mangoProgramId"])
|
||||||
if check_perps:
|
if check_perps:
|
||||||
for market_data in group["perpMarkets"]:
|
for market_data in group["perpMarkets"]:
|
||||||
if market_data["name"].upper() == symbol.upper():
|
if market_data["name"].upper() == symbol.upper():
|
||||||
tokens = IdsJsonMarketLookup._load_tokens(group["tokens"])
|
tokens = IdsJsonMarketLookup._load_tokens(group["tokens"])
|
||||||
return IdsJsonMarketLookup._from_dict(IdsJsonMarketType.PERP, program_id, group_address, market_data, tokens, group["quoteSymbol"])
|
return IdsJsonMarketLookup._from_dict(IdsJsonMarketType.PERP, mango_program_address, group_address, market_data, tokens, group["quoteSymbol"])
|
||||||
if check_spots:
|
if check_spots:
|
||||||
for market_data in group["spotMarkets"]:
|
for market_data in group["spotMarkets"]:
|
||||||
if market_data["name"].upper() == symbol.upper():
|
if market_data["name"].upper() == symbol.upper():
|
||||||
tokens = IdsJsonMarketLookup._load_tokens(group["tokens"])
|
tokens = IdsJsonMarketLookup._load_tokens(group["tokens"])
|
||||||
return IdsJsonMarketLookup._from_dict(IdsJsonMarketType.SPOT, program_id, group_address, market_data, tokens, group["quoteSymbol"])
|
return IdsJsonMarketLookup._from_dict(IdsJsonMarketType.SPOT, mango_program_address, group_address, market_data, tokens, group["quoteSymbol"])
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def find_by_address(self, address: PublicKey) -> typing.Optional[Market]:
|
def find_by_address(self, address: PublicKey) -> typing.Optional[Market]:
|
||||||
for group in MangoConstants["groups"]:
|
for group in MangoConstants["groups"]:
|
||||||
if group["cluster"] == self.cluster:
|
if group["cluster"] == self.cluster_name:
|
||||||
group_address: PublicKey = PublicKey(group["publicKey"])
|
group_address: PublicKey = PublicKey(group["publicKey"])
|
||||||
program_id: PublicKey = PublicKey(group["mangoProgramId"])
|
mango_program_address: PublicKey = PublicKey(group["mangoProgramId"])
|
||||||
for market_data in group["perpMarkets"]:
|
for market_data in group["perpMarkets"]:
|
||||||
if market_data["key"] == str(address):
|
if market_data["key"] == str(address):
|
||||||
tokens = IdsJsonMarketLookup._load_tokens(group["tokens"])
|
tokens = IdsJsonMarketLookup._load_tokens(group["tokens"])
|
||||||
return IdsJsonMarketLookup._from_dict(IdsJsonMarketType.PERP, program_id, group_address, market_data, tokens, group["quoteSymbol"])
|
return IdsJsonMarketLookup._from_dict(IdsJsonMarketType.PERP, mango_program_address, group_address, market_data, tokens, group["quoteSymbol"])
|
||||||
for market_data in group["spotMarkets"]:
|
for market_data in group["spotMarkets"]:
|
||||||
if market_data["key"] == str(address):
|
if market_data["key"] == str(address):
|
||||||
tokens = IdsJsonMarketLookup._load_tokens(group["tokens"])
|
tokens = IdsJsonMarketLookup._load_tokens(group["tokens"])
|
||||||
return IdsJsonMarketLookup._from_dict(IdsJsonMarketType.SPOT, program_id, group_address, market_data, tokens, group["quoteSymbol"])
|
return IdsJsonMarketLookup._from_dict(IdsJsonMarketType.SPOT, mango_program_address, group_address, market_data, tokens, group["quoteSymbol"])
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def all_markets(self) -> typing.Sequence[Market]:
|
def all_markets(self) -> typing.Sequence[Market]:
|
||||||
markets = []
|
markets = []
|
||||||
for group in MangoConstants["groups"]:
|
for group in MangoConstants["groups"]:
|
||||||
if group["cluster"] == self.cluster:
|
if group["cluster"] == self.cluster_name:
|
||||||
group_address: PublicKey = PublicKey(group["publicKey"])
|
group_address: PublicKey = PublicKey(group["publicKey"])
|
||||||
program_id: PublicKey = PublicKey(group["mangoProgramId"])
|
mango_program_address: PublicKey = PublicKey(group["mangoProgramId"])
|
||||||
for market_data in group["perpMarkets"]:
|
for market_data in group["perpMarkets"]:
|
||||||
tokens = IdsJsonMarketLookup._load_tokens(group["tokens"])
|
tokens = IdsJsonMarketLookup._load_tokens(group["tokens"])
|
||||||
market = IdsJsonMarketLookup._from_dict(
|
market = IdsJsonMarketLookup._from_dict(
|
||||||
IdsJsonMarketType.PERP, program_id, group_address, market_data, tokens, group["quoteSymbol"])
|
IdsJsonMarketType.PERP, mango_program_address, group_address, market_data, tokens, group["quoteSymbol"])
|
||||||
markets = [market]
|
markets = [market]
|
||||||
for market_data in group["spotMarkets"]:
|
for market_data in group["spotMarkets"]:
|
||||||
tokens = IdsJsonMarketLookup._load_tokens(group["tokens"])
|
tokens = IdsJsonMarketLookup._load_tokens(group["tokens"])
|
||||||
market = IdsJsonMarketLookup._from_dict(
|
market = IdsJsonMarketLookup._from_dict(
|
||||||
IdsJsonMarketType.SPOT, program_id, group_address, market_data, tokens, group["quoteSymbol"])
|
IdsJsonMarketType.SPOT, mango_program_address, group_address, market_data, tokens, group["quoteSymbol"])
|
||||||
markets = [market]
|
markets = [market]
|
||||||
|
|
||||||
return markets
|
return markets
|
||||||
|
|
|
@ -29,14 +29,14 @@ from .tokenlookup import TokenLookup
|
||||||
#
|
#
|
||||||
|
|
||||||
class IdsJsonTokenLookup(TokenLookup):
|
class IdsJsonTokenLookup(TokenLookup):
|
||||||
def __init__(self, cluster: str, group_name: str) -> None:
|
def __init__(self, cluster_name: str, group_name: str) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.cluster: str = cluster
|
self.cluster_name: str = cluster_name
|
||||||
self.group_name: str = group_name
|
self.group_name: str = group_name
|
||||||
|
|
||||||
def find_by_symbol(self, symbol: str) -> typing.Optional[Token]:
|
def find_by_symbol(self, symbol: str) -> typing.Optional[Token]:
|
||||||
for group in MangoConstants["groups"]:
|
for group in MangoConstants["groups"]:
|
||||||
if group["cluster"] == self.cluster and group["name"] == self.group_name:
|
if group["cluster"] == self.cluster_name and group["name"] == self.group_name:
|
||||||
for token in group["tokens"]:
|
for token in group["tokens"]:
|
||||||
if token["symbol"] == symbol:
|
if token["symbol"] == symbol:
|
||||||
return Token(token["symbol"], token["symbol"], PublicKey(token["mintKey"]), Decimal(token["decimals"]))
|
return Token(token["symbol"], token["symbol"], PublicKey(token["mintKey"]), Decimal(token["decimals"]))
|
||||||
|
@ -45,7 +45,7 @@ class IdsJsonTokenLookup(TokenLookup):
|
||||||
def find_by_mint(self, mint: PublicKey) -> typing.Optional[Token]:
|
def find_by_mint(self, mint: PublicKey) -> typing.Optional[Token]:
|
||||||
mint_str = str(mint)
|
mint_str = str(mint)
|
||||||
for group in MangoConstants["groups"]:
|
for group in MangoConstants["groups"]:
|
||||||
if group["cluster"] == self.cluster and group["name"] == self.group_name:
|
if group["cluster"] == self.cluster_name and group["name"] == self.group_name:
|
||||||
for token in group["tokens"]:
|
for token in group["tokens"]:
|
||||||
if token["mintKey"] == mint_str:
|
if token["mintKey"] == mint_str:
|
||||||
return Token(token["symbol"], token["symbol"], PublicKey(token["mintKey"]), Decimal(token["decimals"]))
|
return Token(token["symbol"], token["symbol"], PublicKey(token["mintKey"]), Decimal(token["decimals"]))
|
||||||
|
|
|
@ -48,11 +48,11 @@ class InstructionReporter:
|
||||||
# The `SerumInstructionParser` class knows a bit more about Serum instructions.
|
# The `SerumInstructionParser` class knows a bit more about Serum instructions.
|
||||||
#
|
#
|
||||||
class SerumInstructionReporter(InstructionReporter):
|
class SerumInstructionReporter(InstructionReporter):
|
||||||
def __init__(self, dex_program_id: PublicKey):
|
def __init__(self, serum_program_address: PublicKey):
|
||||||
self.dex_program_id: PublicKey = dex_program_id
|
self.serum_program_address: PublicKey = serum_program_address
|
||||||
|
|
||||||
def matches(self, instruction: TransactionInstruction) -> bool:
|
def matches(self, instruction: TransactionInstruction) -> bool:
|
||||||
return instruction.program_id == self.dex_program_id
|
return instruction.program_id == self.serum_program_address
|
||||||
|
|
||||||
def report(self, instruction: TransactionInstruction) -> str:
|
def report(self, instruction: TransactionInstruction) -> str:
|
||||||
initial = layouts.SERUM_INSTRUCTION_VARIANT_FINDER.parse(instruction.data)
|
initial = layouts.SERUM_INSTRUCTION_VARIANT_FINDER.parse(instruction.data)
|
||||||
|
@ -65,11 +65,11 @@ class SerumInstructionReporter(InstructionReporter):
|
||||||
# The `MangoInstructionReporter` class knows a bit more about Mango instructions.
|
# The `MangoInstructionReporter` class knows a bit more about Mango instructions.
|
||||||
#
|
#
|
||||||
class MangoInstructionReporter(InstructionReporter):
|
class MangoInstructionReporter(InstructionReporter):
|
||||||
def __init__(self, program_id: PublicKey):
|
def __init__(self, mango_program_address: PublicKey):
|
||||||
self.program_id: PublicKey = program_id
|
self.mango_program_address: PublicKey = mango_program_address
|
||||||
|
|
||||||
def matches(self, instruction: TransactionInstruction) -> bool:
|
def matches(self, instruction: TransactionInstruction) -> bool:
|
||||||
return instruction.program_id == self.program_id
|
return instruction.program_id == self.mango_program_address
|
||||||
|
|
||||||
def report(self, instruction: TransactionInstruction) -> str:
|
def report(self, instruction: TransactionInstruction) -> str:
|
||||||
initial = layouts.MANGO_INSTRUCTION_VARIANT_FINDER.parse(instruction.data)
|
initial = layouts.MANGO_INSTRUCTION_VARIANT_FINDER.parse(instruction.data)
|
||||||
|
@ -107,8 +107,8 @@ class CompoundInstructionReporter(InstructionReporter):
|
||||||
f"Could not find instruction reporter for instruction {instruction}.")
|
f"Could not find instruction reporter for instruction {instruction}.")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_ids(program_id: PublicKey, dex_program_id: PublicKey) -> InstructionReporter:
|
def from_addresses(mango_program_address: PublicKey, serum_program_address: PublicKey) -> InstructionReporter:
|
||||||
base: InstructionReporter = InstructionReporter()
|
base: InstructionReporter = InstructionReporter()
|
||||||
serum: InstructionReporter = SerumInstructionReporter(dex_program_id)
|
serum: InstructionReporter = SerumInstructionReporter(serum_program_address)
|
||||||
mango: InstructionReporter = MangoInstructionReporter(program_id)
|
mango: InstructionReporter = MangoInstructionReporter(mango_program_address)
|
||||||
return CompoundInstructionReporter([mango, serum, base])
|
return CompoundInstructionReporter([mango, serum, base])
|
||||||
|
|
|
@ -66,11 +66,11 @@ from .wallet import Wallet
|
||||||
# necesary.
|
# necesary.
|
||||||
#
|
#
|
||||||
|
|
||||||
def build_create_solana_account_instructions(context: Context, wallet: Wallet, program_id: PublicKey, size: int, lamports: int = 0) -> CombinableInstructions:
|
def build_create_solana_account_instructions(context: Context, wallet: Wallet, mango_program_address: PublicKey, size: int, lamports: int = 0) -> CombinableInstructions:
|
||||||
minimum_balance = context.client.get_minimum_balance_for_rent_exemption(size)
|
minimum_balance = context.client.get_minimum_balance_for_rent_exemption(size)
|
||||||
account = SolanaAccount()
|
account = SolanaAccount()
|
||||||
create_instruction = create_account(
|
create_instruction = create_account(
|
||||||
CreateAccountParams(wallet.address, account.public_key(), lamports + minimum_balance, size, program_id))
|
CreateAccountParams(wallet.address, account.public_key(), lamports + minimum_balance, size, mango_program_address))
|
||||||
return CombinableInstructions(signers=[account], instructions=[create_instruction])
|
return CombinableInstructions(signers=[account], instructions=[create_instruction])
|
||||||
|
|
||||||
|
|
||||||
|
@ -177,7 +177,7 @@ def build_serum_consume_events_instructions(context: Context, market_address: Pu
|
||||||
AccountMeta(pubkey=pubkey, is_signer=False, is_writable=True)
|
AccountMeta(pubkey=pubkey, is_signer=False, is_writable=True)
|
||||||
for pubkey in [*open_orders_addresses, market_address, event_queue_address]
|
for pubkey in [*open_orders_addresses, market_address, event_queue_address]
|
||||||
],
|
],
|
||||||
program_id=context.dex_program_id,
|
program_id=context.serum_program_address,
|
||||||
data=PYSERUM_INSTRUCTIONS_LAYOUT.build(
|
data=PYSERUM_INSTRUCTIONS_LAYOUT.build(
|
||||||
dict(instruction_type=PySerumInstructionType.CONSUME_EVENTS, args=dict(limit=limit))
|
dict(instruction_type=PySerumInstructionType.CONSUME_EVENTS, args=dict(limit=limit))
|
||||||
),
|
),
|
||||||
|
@ -260,7 +260,7 @@ def build_spot_settle_instructions(context: Context, wallet: Wallet, account: Ac
|
||||||
AccountMeta(is_signer=False, is_writable=True, pubkey=group.cache),
|
AccountMeta(is_signer=False, is_writable=True, pubkey=group.cache),
|
||||||
AccountMeta(is_signer=True, is_writable=False, pubkey=wallet.address),
|
AccountMeta(is_signer=True, is_writable=False, pubkey=wallet.address),
|
||||||
AccountMeta(is_signer=False, is_writable=True, pubkey=account.address),
|
AccountMeta(is_signer=False, is_writable=True, pubkey=account.address),
|
||||||
AccountMeta(is_signer=False, is_writable=False, pubkey=context.dex_program_id),
|
AccountMeta(is_signer=False, is_writable=False, pubkey=context.serum_program_address),
|
||||||
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.public_key()),
|
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.public_key()),
|
||||||
AccountMeta(is_signer=False, is_writable=True, pubkey=open_orders_address),
|
AccountMeta(is_signer=False, is_writable=True, pubkey=open_orders_address),
|
||||||
AccountMeta(is_signer=False, is_writable=False, pubkey=group.signer_key),
|
AccountMeta(is_signer=False, is_writable=False, pubkey=group.signer_key),
|
||||||
|
@ -275,7 +275,7 @@ def build_spot_settle_instructions(context: Context, wallet: Wallet, account: Ac
|
||||||
AccountMeta(is_signer=False, is_writable=False, pubkey=vault_signer),
|
AccountMeta(is_signer=False, is_writable=False, pubkey=vault_signer),
|
||||||
AccountMeta(is_signer=False, is_writable=False, pubkey=TOKEN_PROGRAM_ID)
|
AccountMeta(is_signer=False, is_writable=False, pubkey=TOKEN_PROGRAM_ID)
|
||||||
],
|
],
|
||||||
program_id=context.program_id,
|
program_id=context.mango_program_address,
|
||||||
data=layouts.SETTLE_FUNDS.build(dict())
|
data=layouts.SETTLE_FUNDS.build(dict())
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -352,7 +352,7 @@ def build_cancel_perp_order_instructions(context: Context, wallet: Wallet, accou
|
||||||
AccountMeta(is_signer=False, is_writable=True, pubkey=perp_market_details.bids),
|
AccountMeta(is_signer=False, is_writable=True, pubkey=perp_market_details.bids),
|
||||||
AccountMeta(is_signer=False, is_writable=True, pubkey=perp_market_details.asks)
|
AccountMeta(is_signer=False, is_writable=True, pubkey=perp_market_details.asks)
|
||||||
],
|
],
|
||||||
program_id=context.program_id,
|
program_id=context.mango_program_address,
|
||||||
data=data
|
data=data
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
@ -399,7 +399,7 @@ def build_place_perp_order_instructions(context: Context, wallet: Wallet, group:
|
||||||
*list([AccountMeta(is_signer=False, is_writable=False,
|
*list([AccountMeta(is_signer=False, is_writable=False,
|
||||||
pubkey=oo_address or SYSTEM_PROGRAM_ADDRESS) for oo_address in account.spot_open_orders])
|
pubkey=oo_address or SYSTEM_PROGRAM_ADDRESS) for oo_address in account.spot_open_orders])
|
||||||
],
|
],
|
||||||
program_id=context.program_id,
|
program_id=context.mango_program_address,
|
||||||
data=layouts.PLACE_PERP_ORDER.build(
|
data=layouts.PLACE_PERP_ORDER.build(
|
||||||
{
|
{
|
||||||
"price": native_price,
|
"price": native_price,
|
||||||
|
@ -435,7 +435,7 @@ def build_mango_consume_events_instructions(context: Context, group: Group, perp
|
||||||
*list([AccountMeta(is_signer=False, is_writable=True,
|
*list([AccountMeta(is_signer=False, is_writable=True,
|
||||||
pubkey=account_address) for account_address in account_addresses])
|
pubkey=account_address) for account_address in account_addresses])
|
||||||
],
|
],
|
||||||
program_id=context.program_id,
|
program_id=context.mango_program_address,
|
||||||
data=layouts.CONSUME_EVENTS.build(
|
data=layouts.CONSUME_EVENTS.build(
|
||||||
{
|
{
|
||||||
"limit": limit,
|
"limit": limit,
|
||||||
|
@ -447,7 +447,7 @@ def build_mango_consume_events_instructions(context: Context, group: Group, perp
|
||||||
|
|
||||||
def build_create_account_instructions(context: Context, wallet: Wallet, group: Group) -> CombinableInstructions:
|
def build_create_account_instructions(context: Context, wallet: Wallet, group: Group) -> CombinableInstructions:
|
||||||
create_account_instructions = build_create_solana_account_instructions(
|
create_account_instructions = build_create_solana_account_instructions(
|
||||||
context, wallet, context.program_id, layouts.MANGO_ACCOUNT.sizeof())
|
context, wallet, context.mango_program_address, layouts.MANGO_ACCOUNT.sizeof())
|
||||||
mango_account_address = create_account_instructions.signers[0].public_key()
|
mango_account_address = create_account_instructions.signers[0].public_key()
|
||||||
|
|
||||||
# /// 0. `[]` mango_group_ai - Group that this mango account is for
|
# /// 0. `[]` mango_group_ai - Group that this mango account is for
|
||||||
|
@ -460,7 +460,7 @@ def build_create_account_instructions(context: Context, wallet: Wallet, group: G
|
||||||
AccountMeta(is_signer=False, is_writable=True, pubkey=mango_account_address),
|
AccountMeta(is_signer=False, is_writable=True, pubkey=mango_account_address),
|
||||||
AccountMeta(is_signer=True, is_writable=False, pubkey=wallet.address)
|
AccountMeta(is_signer=True, is_writable=False, pubkey=wallet.address)
|
||||||
],
|
],
|
||||||
program_id=context.program_id,
|
program_id=context.mango_program_address,
|
||||||
data=layouts.INIT_MANGO_ACCOUNT.build({})
|
data=layouts.INIT_MANGO_ACCOUNT.build({})
|
||||||
)
|
)
|
||||||
return create_account_instructions + CombinableInstructions(signers=[], instructions=[init])
|
return create_account_instructions + CombinableInstructions(signers=[], instructions=[init])
|
||||||
|
@ -496,7 +496,7 @@ def build_deposit_instructions(context: Context, wallet: Wallet, group: Group, a
|
||||||
AccountMeta(is_signer=False, is_writable=False, pubkey=TOKEN_PROGRAM_ID),
|
AccountMeta(is_signer=False, is_writable=False, pubkey=TOKEN_PROGRAM_ID),
|
||||||
AccountMeta(is_signer=False, is_writable=True, pubkey=token_account.address)
|
AccountMeta(is_signer=False, is_writable=True, pubkey=token_account.address)
|
||||||
],
|
],
|
||||||
program_id=context.program_id,
|
program_id=context.mango_program_address,
|
||||||
data=layouts.DEPOSIT.build({
|
data=layouts.DEPOSIT.build({
|
||||||
"quantity": value
|
"quantity": value
|
||||||
})
|
})
|
||||||
|
@ -544,7 +544,7 @@ def build_withdraw_instructions(context: Context, wallet: Wallet, group: Group,
|
||||||
*list([AccountMeta(is_signer=False, is_writable=False,
|
*list([AccountMeta(is_signer=False, is_writable=False,
|
||||||
pubkey=oo_address or SYSTEM_PROGRAM_ADDRESS) for oo_address in account.spot_open_orders])
|
pubkey=oo_address or SYSTEM_PROGRAM_ADDRESS) for oo_address in account.spot_open_orders])
|
||||||
],
|
],
|
||||||
program_id=context.program_id,
|
program_id=context.mango_program_address,
|
||||||
data=layouts.WITHDRAW.build({
|
data=layouts.WITHDRAW.build({
|
||||||
"quantity": value,
|
"quantity": value,
|
||||||
"allow_borrow": allow_borrow
|
"allow_borrow": allow_borrow
|
||||||
|
@ -557,7 +557,7 @@ def build_withdraw_instructions(context: Context, wallet: Wallet, group: Group,
|
||||||
def build_spot_openorders_instructions(context: Context, wallet: Wallet, group: Group, account: Account, market: PySerumMarket) -> CombinableInstructions:
|
def build_spot_openorders_instructions(context: Context, wallet: Wallet, group: Group, account: Account, market: PySerumMarket) -> CombinableInstructions:
|
||||||
instructions: CombinableInstructions = CombinableInstructions.empty()
|
instructions: CombinableInstructions = CombinableInstructions.empty()
|
||||||
create_open_orders = build_create_solana_account_instructions(
|
create_open_orders = build_create_solana_account_instructions(
|
||||||
context, wallet, context.dex_program_id, layouts.OPEN_ORDERS.sizeof())
|
context, wallet, context.serum_program_address, layouts.OPEN_ORDERS.sizeof())
|
||||||
instructions += create_open_orders
|
instructions += create_open_orders
|
||||||
|
|
||||||
open_orders_address = create_open_orders.signers[0].public_key()
|
open_orders_address = create_open_orders.signers[0].public_key()
|
||||||
|
@ -567,13 +567,13 @@ def build_spot_openorders_instructions(context: Context, wallet: Wallet, group:
|
||||||
AccountMeta(is_signer=False, is_writable=False, pubkey=group.address),
|
AccountMeta(is_signer=False, is_writable=False, pubkey=group.address),
|
||||||
AccountMeta(is_signer=False, is_writable=True, pubkey=account.address),
|
AccountMeta(is_signer=False, is_writable=True, pubkey=account.address),
|
||||||
AccountMeta(is_signer=True, is_writable=False, pubkey=wallet.address),
|
AccountMeta(is_signer=True, is_writable=False, pubkey=wallet.address),
|
||||||
AccountMeta(is_signer=False, is_writable=False, pubkey=context.dex_program_id),
|
AccountMeta(is_signer=False, is_writable=False, pubkey=context.serum_program_address),
|
||||||
AccountMeta(is_signer=False, is_writable=True, pubkey=open_orders_address),
|
AccountMeta(is_signer=False, is_writable=True, pubkey=open_orders_address),
|
||||||
AccountMeta(is_signer=False, is_writable=False, pubkey=market.state.public_key()),
|
AccountMeta(is_signer=False, is_writable=False, pubkey=market.state.public_key()),
|
||||||
AccountMeta(is_signer=False, is_writable=False, pubkey=group.signer_key),
|
AccountMeta(is_signer=False, is_writable=False, pubkey=group.signer_key),
|
||||||
AccountMeta(is_signer=False, is_writable=False, pubkey=SYSVAR_RENT_PUBKEY)
|
AccountMeta(is_signer=False, is_writable=False, pubkey=SYSVAR_RENT_PUBKEY)
|
||||||
],
|
],
|
||||||
program_id=context.program_id,
|
program_id=context.mango_program_address,
|
||||||
data=layouts.INIT_SPOT_OPEN_ORDERS.build(dict())
|
data=layouts.INIT_SPOT_OPEN_ORDERS.build(dict())
|
||||||
)
|
)
|
||||||
instructions += CombinableInstructions(signers=[], instructions=[initialise_open_orders_instruction])
|
instructions += CombinableInstructions(signers=[], instructions=[initialise_open_orders_instruction])
|
||||||
|
@ -666,7 +666,7 @@ def build_spot_place_order_instructions(context: Context, wallet: Wallet, group:
|
||||||
AccountMeta(is_signer=False, is_writable=True, pubkey=account.address),
|
AccountMeta(is_signer=False, is_writable=True, pubkey=account.address),
|
||||||
AccountMeta(is_signer=True, is_writable=False, pubkey=wallet.address),
|
AccountMeta(is_signer=True, is_writable=False, pubkey=wallet.address),
|
||||||
AccountMeta(is_signer=False, is_writable=False, pubkey=group.cache),
|
AccountMeta(is_signer=False, is_writable=False, pubkey=group.cache),
|
||||||
AccountMeta(is_signer=False, is_writable=False, pubkey=context.dex_program_id),
|
AccountMeta(is_signer=False, is_writable=False, pubkey=context.serum_program_address),
|
||||||
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.public_key()),
|
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.public_key()),
|
||||||
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.bids()),
|
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.bids()),
|
||||||
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.asks()),
|
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.asks()),
|
||||||
|
@ -690,7 +690,7 @@ def build_spot_place_order_instructions(context: Context, wallet: Wallet, group:
|
||||||
pubkey=oo_address or SYSTEM_PROGRAM_ADDRESS) for oo_address in account.spot_open_orders]),
|
pubkey=oo_address or SYSTEM_PROGRAM_ADDRESS) for oo_address in account.spot_open_orders]),
|
||||||
*fee_discount_address_meta
|
*fee_discount_address_meta
|
||||||
],
|
],
|
||||||
program_id=context.program_id,
|
program_id=context.mango_program_address,
|
||||||
data=layouts.PLACE_SPOT_ORDER.build(
|
data=layouts.PLACE_SPOT_ORDER.build(
|
||||||
dict(
|
dict(
|
||||||
side=serum_side,
|
side=serum_side,
|
||||||
|
@ -736,7 +736,7 @@ def build_cancel_spot_order_instructions(context: Context, wallet: Wallet, group
|
||||||
AccountMeta(is_signer=False, is_writable=False, pubkey=group.address),
|
AccountMeta(is_signer=False, is_writable=False, pubkey=group.address),
|
||||||
AccountMeta(is_signer=True, is_writable=False, pubkey=wallet.address),
|
AccountMeta(is_signer=True, is_writable=False, pubkey=wallet.address),
|
||||||
AccountMeta(is_signer=False, is_writable=False, pubkey=account.address),
|
AccountMeta(is_signer=False, is_writable=False, pubkey=account.address),
|
||||||
AccountMeta(is_signer=False, is_writable=False, pubkey=context.dex_program_id),
|
AccountMeta(is_signer=False, is_writable=False, pubkey=context.serum_program_address),
|
||||||
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.public_key()),
|
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.public_key()),
|
||||||
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.bids()),
|
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.bids()),
|
||||||
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.asks()),
|
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.asks()),
|
||||||
|
@ -744,7 +744,7 @@ def build_cancel_spot_order_instructions(context: Context, wallet: Wallet, group
|
||||||
AccountMeta(is_signer=False, is_writable=False, pubkey=group.signer_key),
|
AccountMeta(is_signer=False, is_writable=False, pubkey=group.signer_key),
|
||||||
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.event_queue())
|
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.event_queue())
|
||||||
],
|
],
|
||||||
program_id=context.program_id,
|
program_id=context.mango_program_address,
|
||||||
data=layouts.CANCEL_SPOT_ORDER.build(
|
data=layouts.CANCEL_SPOT_ORDER.build(
|
||||||
{
|
{
|
||||||
"order_id": order.id,
|
"order_id": order.id,
|
||||||
|
@ -815,7 +815,7 @@ def build_redeem_accrued_mango_instructions(context: Context, wallet: Wallet, pe
|
||||||
AccountMeta(is_signer=False, is_writable=False, pubkey=group.signer_key),
|
AccountMeta(is_signer=False, is_writable=False, pubkey=group.signer_key),
|
||||||
AccountMeta(is_signer=False, is_writable=False, pubkey=TOKEN_PROGRAM_ID)
|
AccountMeta(is_signer=False, is_writable=False, pubkey=TOKEN_PROGRAM_ID)
|
||||||
],
|
],
|
||||||
program_id=context.program_id,
|
program_id=context.mango_program_address,
|
||||||
data=layouts.REDEEM_MNGO.build(dict())
|
data=layouts.REDEEM_MNGO.build(dict())
|
||||||
)
|
)
|
||||||
return CombinableInstructions(signers=[], instructions=[redeem_accrued_mango_instruction])
|
return CombinableInstructions(signers=[], instructions=[redeem_accrued_mango_instruction])
|
||||||
|
|
|
@ -450,7 +450,7 @@ PERP_MARKET_INFO = construct.Struct(
|
||||||
# pub signer_nonce: u64,
|
# pub signer_nonce: u64,
|
||||||
# pub signer_key: Pubkey,
|
# pub signer_key: Pubkey,
|
||||||
# pub admin: Pubkey, // Used to add new markets and adjust risk params
|
# pub admin: Pubkey, // Used to add new markets and adjust risk params
|
||||||
# pub dex_program_id: Pubkey, // Consider allowing more
|
# pub serum_program_address: Pubkey, // Consider allowing more
|
||||||
# pub mango_cache: Pubkey,
|
# pub mango_cache: Pubkey,
|
||||||
# pub valid_interval: u64,
|
# pub valid_interval: u64,
|
||||||
#
|
#
|
||||||
|
@ -473,7 +473,7 @@ GROUP = construct.Struct(
|
||||||
"signer_nonce" / DecimalAdapter(),
|
"signer_nonce" / DecimalAdapter(),
|
||||||
"signer_key" / PublicKeyAdapter(),
|
"signer_key" / PublicKeyAdapter(),
|
||||||
"admin" / PublicKeyAdapter(),
|
"admin" / PublicKeyAdapter(),
|
||||||
"dex_program_id" / PublicKeyAdapter(),
|
"serum_program_address" / PublicKeyAdapter(),
|
||||||
"cache" / PublicKeyAdapter(),
|
"cache" / PublicKeyAdapter(),
|
||||||
"valid_interval" / DecimalAdapter(),
|
"valid_interval" / DecimalAdapter(),
|
||||||
"dao_vault" / PublicKeyAdapter(),
|
"dao_vault" / PublicKeyAdapter(),
|
||||||
|
|
|
@ -39,11 +39,10 @@ class InventorySource(enum.Enum):
|
||||||
#
|
#
|
||||||
# This class describes a crypto market. It *must* have an address, a base token and a quote token.
|
# This class describes a crypto market. It *must* have an address, a base token and a quote token.
|
||||||
#
|
#
|
||||||
|
|
||||||
class Market(metaclass=abc.ABCMeta):
|
class Market(metaclass=abc.ABCMeta):
|
||||||
def __init__(self, program_id: PublicKey, address: PublicKey, inventory_source: InventorySource, base: Token, quote: Token, lot_size_converter: LotSizeConverter):
|
def __init__(self, program_address: PublicKey, address: PublicKey, inventory_source: InventorySource, base: Token, quote: Token, lot_size_converter: LotSizeConverter):
|
||||||
self.logger: logging.Logger = logging.getLogger(self.__class__.__name__)
|
self.logger: logging.Logger = logging.getLogger(self.__class__.__name__)
|
||||||
self.program_id: PublicKey = program_id
|
self.program_address: PublicKey = program_address
|
||||||
self.address: PublicKey = address
|
self.address: PublicKey = address
|
||||||
self.inventory_source: InventorySource = inventory_source
|
self.inventory_source: InventorySource = inventory_source
|
||||||
self.base: Token = base
|
self.base: Token = base
|
||||||
|
|
|
@ -76,7 +76,7 @@ def _polling_serum_model_state_builder_factory(context: mango.Context, wallet: m
|
||||||
raise Exception(
|
raise Exception(
|
||||||
f"Could not find token account owned by {wallet.address} for quote token {market.quote}.")
|
f"Could not find token account owned by {wallet.address} for quote token {market.quote}.")
|
||||||
all_open_orders = mango.OpenOrders.load_for_market_and_owner(
|
all_open_orders = mango.OpenOrders.load_for_market_and_owner(
|
||||||
context, market.address, wallet.address, context.dex_program_id, market.base.decimals, market.quote.decimals)
|
context, market.address, wallet.address, context.serum_program_address, market.base.decimals, market.quote.decimals)
|
||||||
if len(all_open_orders) == 0:
|
if len(all_open_orders) == 0:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
f"Could not find serum openorders account owned by {wallet.address} for market {market.symbol}.")
|
f"Could not find serum openorders account owned by {wallet.address} for market {market.symbol}.")
|
||||||
|
|
|
@ -36,14 +36,14 @@ from .version import Version
|
||||||
|
|
||||||
|
|
||||||
class OpenOrders(AddressableAccount):
|
class OpenOrders(AddressableAccount):
|
||||||
def __init__(self, account_info: AccountInfo, version: Version, program_id: PublicKey,
|
def __init__(self, account_info: AccountInfo, version: Version, program_address: PublicKey,
|
||||||
account_flags: AccountFlags, market: PublicKey, owner: PublicKey,
|
account_flags: AccountFlags, market: PublicKey, owner: PublicKey,
|
||||||
base_token_free: Decimal, base_token_total: Decimal, quote_token_free: Decimal,
|
base_token_free: Decimal, base_token_total: Decimal, quote_token_free: Decimal,
|
||||||
quote_token_total: Decimal, placed_orders: typing.Sequence[PlacedOrder],
|
quote_token_total: Decimal, placed_orders: typing.Sequence[PlacedOrder],
|
||||||
referrer_rebate_accrued: Decimal):
|
referrer_rebate_accrued: Decimal):
|
||||||
super().__init__(account_info)
|
super().__init__(account_info)
|
||||||
self.version: Version = version
|
self.version: Version = version
|
||||||
self.program_id: PublicKey = program_id
|
self.program_address: PublicKey = program_address
|
||||||
self.account_flags: AccountFlags = account_flags
|
self.account_flags: AccountFlags = account_flags
|
||||||
self.market: PublicKey = market
|
self.market: PublicKey = market
|
||||||
self.owner: PublicKey = owner
|
self.owner: PublicKey = owner
|
||||||
|
@ -62,7 +62,7 @@ class OpenOrders(AddressableAccount):
|
||||||
def from_layout(layout: layouts.OPEN_ORDERS, account_info: AccountInfo,
|
def from_layout(layout: layouts.OPEN_ORDERS, account_info: AccountInfo,
|
||||||
base_decimals: Decimal, quote_decimals: Decimal) -> "OpenOrders":
|
base_decimals: Decimal, quote_decimals: Decimal) -> "OpenOrders":
|
||||||
account_flags = AccountFlags.from_layout(layout.account_flags)
|
account_flags = AccountFlags.from_layout(layout.account_flags)
|
||||||
program_id = account_info.owner
|
program_address = account_info.owner
|
||||||
|
|
||||||
base_divisor = 10 ** base_decimals
|
base_divisor = 10 ** base_decimals
|
||||||
quote_divisor = 10 ** quote_decimals
|
quote_divisor = 10 ** quote_decimals
|
||||||
|
@ -75,7 +75,7 @@ class OpenOrders(AddressableAccount):
|
||||||
if account_flags.initialized:
|
if account_flags.initialized:
|
||||||
placed_orders = PlacedOrder.build_from_open_orders_data(
|
placed_orders = PlacedOrder.build_from_open_orders_data(
|
||||||
layout.free_slot_bits, layout.is_bid_bits, layout.orders, layout.client_ids)
|
layout.free_slot_bits, layout.is_bid_bits, layout.orders, layout.client_ids)
|
||||||
return OpenOrders(account_info, Version.UNSPECIFIED, program_id, account_flags, layout.market,
|
return OpenOrders(account_info, Version.UNSPECIFIED, program_address, account_flags, layout.market,
|
||||||
layout.owner, base_token_free, base_token_total, quote_token_free,
|
layout.owner, base_token_free, base_token_total, quote_token_free,
|
||||||
quote_token_total, placed_orders, layout.referrer_rebate_accrued)
|
quote_token_total, placed_orders, layout.referrer_rebate_accrued)
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ class OpenOrders(AddressableAccount):
|
||||||
]
|
]
|
||||||
|
|
||||||
results = context.client.get_program_accounts(
|
results = context.client.get_program_accounts(
|
||||||
group.dex_program_id, data_size=layouts.OPEN_ORDERS.sizeof(), memcmp_opts=filters)
|
group.serum_program_address, data_size=layouts.OPEN_ORDERS.sizeof(), memcmp_opts=filters)
|
||||||
account_infos = list(map(lambda pair: AccountInfo._from_response_values(pair[0], pair[1]), [
|
account_infos = list(map(lambda pair: AccountInfo._from_response_values(pair[0], pair[1]), [
|
||||||
(result["account"], PublicKey(result["pubkey"])) for result in results]))
|
(result["account"], PublicKey(result["pubkey"])) for result in results]))
|
||||||
account_infos_by_address = {key: value for key, value in [
|
account_infos_by_address = {key: value for key, value in [
|
||||||
|
@ -113,7 +113,7 @@ class OpenOrders(AddressableAccount):
|
||||||
return OpenOrders.parse(open_orders_account, base_decimals, quote_decimals)
|
return OpenOrders.parse(open_orders_account, base_decimals, quote_decimals)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_for_market_and_owner(context: Context, market: PublicKey, owner: PublicKey, program_id: PublicKey, base_decimals: Decimal, quote_decimals: Decimal):
|
def load_for_market_and_owner(context: Context, market: PublicKey, owner: PublicKey, program_address: PublicKey, base_decimals: Decimal, quote_decimals: Decimal):
|
||||||
filters = [
|
filters = [
|
||||||
MemcmpOpts(
|
MemcmpOpts(
|
||||||
offset=layouts.ACCOUNT_FLAGS.sizeof() + 5,
|
offset=layouts.ACCOUNT_FLAGS.sizeof() + 5,
|
||||||
|
@ -126,7 +126,7 @@ class OpenOrders(AddressableAccount):
|
||||||
]
|
]
|
||||||
|
|
||||||
results = context.client.get_program_accounts(
|
results = context.client.get_program_accounts(
|
||||||
program_id, data_size=layouts.OPEN_ORDERS.sizeof(), memcmp_opts=filters)
|
program_address, data_size=layouts.OPEN_ORDERS.sizeof(), memcmp_opts=filters)
|
||||||
accounts = map(lambda result: AccountInfo._from_response_values(
|
accounts = map(lambda result: AccountInfo._from_response_values(
|
||||||
result["account"], PublicKey(result["pubkey"])), results)
|
result["account"], PublicKey(result["pubkey"])), results)
|
||||||
return list(map(lambda acc: OpenOrders.parse(acc, base_decimals, quote_decimals), accounts))
|
return list(map(lambda acc: OpenOrders.parse(acc, base_decimals, quote_decimals), accounts))
|
||||||
|
@ -136,7 +136,7 @@ class OpenOrders(AddressableAccount):
|
||||||
|
|
||||||
return f"""« OpenOrders [{self.address}]:
|
return f"""« OpenOrders [{self.address}]:
|
||||||
Flags: {self.account_flags}
|
Flags: {self.account_flags}
|
||||||
Program ID: {self.program_id}
|
Program ID: {self.program_address}
|
||||||
Market: {self.market}
|
Market: {self.market}
|
||||||
Owner: {self.owner}
|
Owner: {self.owner}
|
||||||
Base Token: {self.base_token_free:,.8f} of {self.base_token_total:,.8f}
|
Base Token: {self.base_token_free:,.8f} of {self.base_token_total:,.8f}
|
||||||
|
|
|
@ -110,7 +110,7 @@ class PythOracle(Oracle):
|
||||||
|
|
||||||
class PythOracleProvider(OracleProvider):
|
class PythOracleProvider(OracleProvider):
|
||||||
def __init__(self, context: Context) -> None:
|
def __init__(self, context: Context) -> None:
|
||||||
self.address: PublicKey = PYTH_MAINNET_MAPPING_ROOT if context.client.cluster == "mainnet" else PYTH_DEVNET_MAPPING_ROOT
|
self.address: PublicKey = PYTH_MAINNET_MAPPING_ROOT if context.client.cluster_name == "mainnet" else PYTH_DEVNET_MAPPING_ROOT
|
||||||
super().__init__(f"Pyth Oracle Factory [{self.address}]")
|
super().__init__(f"Pyth Oracle Factory [{self.address}]")
|
||||||
self.context: Context = context
|
self.context: Context = context
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@ class SerumOracle(Oracle):
|
||||||
context.client.skip_preflight,
|
context.client.skip_preflight,
|
||||||
context.client.instruction_reporter)
|
context.client.instruction_reporter)
|
||||||
mainnet_serum_market_lookup: SerumMarketLookup = SerumMarketLookup.load(
|
mainnet_serum_market_lookup: SerumMarketLookup = SerumMarketLookup.load(
|
||||||
context.dex_program_id, SplTokenLookup.DefaultDataFilepath)
|
context.serum_program_address, SplTokenLookup.DefaultDataFilepath)
|
||||||
adjusted_market = self.market
|
adjusted_market = self.market
|
||||||
mainnet_adjusted_market: typing.Optional[Market] = mainnet_serum_market_lookup.find_by_symbol(
|
mainnet_adjusted_market: typing.Optional[Market] = mainnet_serum_market_lookup.find_by_symbol(
|
||||||
self.market.symbol)
|
self.market.symbol)
|
||||||
|
@ -111,7 +111,7 @@ class SerumOracleProvider(OracleProvider):
|
||||||
def oracle_for_market(self, context: Context, market: Market) -> typing.Optional[Oracle]:
|
def oracle_for_market(self, context: Context, market: Market) -> typing.Optional[Oracle]:
|
||||||
loaded_market: Market = ensure_market_loaded(context, market)
|
loaded_market: Market = ensure_market_loaded(context, market)
|
||||||
if isinstance(loaded_market, SpotMarket):
|
if isinstance(loaded_market, SpotMarket):
|
||||||
serum_market = SerumMarket(context.dex_program_id, loaded_market.address, loaded_market.base,
|
serum_market = SerumMarket(context.serum_program_address, loaded_market.address, loaded_market.base,
|
||||||
loaded_market.quote, loaded_market.underlying_serum_market)
|
loaded_market.quote, loaded_market.underlying_serum_market)
|
||||||
return SerumOracle(serum_market)
|
return SerumOracle(serum_market)
|
||||||
elif isinstance(loaded_market, SerumMarket):
|
elif isinstance(loaded_market, SerumMarket):
|
||||||
|
|
|
@ -34,8 +34,8 @@ from .token import Token
|
||||||
# This class encapsulates our knowledge of a Mango perps market.
|
# This class encapsulates our knowledge of a Mango perps market.
|
||||||
#
|
#
|
||||||
class PerpMarket(Market):
|
class PerpMarket(Market):
|
||||||
def __init__(self, program_id: PublicKey, address: PublicKey, base: Token, quote: Token, underlying_perp_market: PerpMarketDetails):
|
def __init__(self, mango_program_address: PublicKey, address: PublicKey, base: Token, quote: Token, underlying_perp_market: PerpMarketDetails):
|
||||||
super().__init__(program_id, address, InventorySource.ACCOUNT, base, quote, RaisingLotSizeConverter())
|
super().__init__(mango_program_address, address, InventorySource.ACCOUNT, base, quote, RaisingLotSizeConverter())
|
||||||
self.underlying_perp_market: PerpMarketDetails = underlying_perp_market
|
self.underlying_perp_market: PerpMarketDetails = underlying_perp_market
|
||||||
self.lot_size_converter: LotSizeConverter = LotSizeConverter(
|
self.lot_size_converter: LotSizeConverter = LotSizeConverter(
|
||||||
base, underlying_perp_market.base_lot_size, quote, underlying_perp_market.quote_lot_size)
|
base, underlying_perp_market.base_lot_size, quote, underlying_perp_market.quote_lot_size)
|
||||||
|
@ -81,7 +81,7 @@ class PerpMarket(Market):
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
underlying: str = f"{self.underlying_perp_market}".replace("\n", "\n ")
|
underlying: str = f"{self.underlying_perp_market}".replace("\n", "\n ")
|
||||||
return f"""« 𝙿𝚎𝚛𝚙𝙼𝚊𝚛𝚔𝚎𝚝 {self.symbol} {self.address} [{self.program_id}]
|
return f"""« 𝙿𝚎𝚛𝚙𝙼𝚊𝚛𝚔𝚎𝚝 {self.symbol} {self.address} [{self.program_address}]
|
||||||
{underlying}
|
{underlying}
|
||||||
»"""
|
»"""
|
||||||
|
|
||||||
|
@ -91,18 +91,18 @@ class PerpMarket(Market):
|
||||||
# This class holds information to load a `PerpMarket` object but doesn't automatically load it.
|
# This class holds information to load a `PerpMarket` object but doesn't automatically load it.
|
||||||
#
|
#
|
||||||
class PerpMarketStub(Market):
|
class PerpMarketStub(Market):
|
||||||
def __init__(self, program_id: PublicKey, address: PublicKey, base: Token, quote: Token, group_address: PublicKey):
|
def __init__(self, mango_program_address: PublicKey, address: PublicKey, base: Token, quote: Token, group_address: PublicKey):
|
||||||
super().__init__(program_id, address, InventorySource.ACCOUNT, base, quote, RaisingLotSizeConverter())
|
super().__init__(mango_program_address, address, InventorySource.ACCOUNT, base, quote, RaisingLotSizeConverter())
|
||||||
self.group_address: PublicKey = group_address
|
self.group_address: PublicKey = group_address
|
||||||
|
|
||||||
def load(self, context: Context, group: typing.Optional[Group] = None) -> PerpMarket:
|
def load(self, context: Context, group: typing.Optional[Group] = None) -> PerpMarket:
|
||||||
actual_group: Group = group or Group.load(context, self.group_address)
|
actual_group: Group = group or Group.load(context, self.group_address)
|
||||||
underlying_perp_market: PerpMarketDetails = PerpMarketDetails.load(context, self.address, actual_group)
|
underlying_perp_market: PerpMarketDetails = PerpMarketDetails.load(context, self.address, actual_group)
|
||||||
return PerpMarket(self.program_id, self.address, self.base, self.quote, underlying_perp_market)
|
return PerpMarket(self.program_address, self.address, self.base, self.quote, underlying_perp_market)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def symbol(self) -> str:
|
def symbol(self) -> str:
|
||||||
return f"{self.base.symbol}-PERP"
|
return f"{self.base.symbol}-PERP"
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"« 𝙿𝚎𝚛𝚙𝙼𝚊𝚛𝚔𝚎𝚝𝚂𝚝𝚞𝚋 {self.symbol} {self.address} [{self.program_id}] »"
|
return f"« 𝙿𝚎𝚛𝚙𝙼𝚊𝚛𝚔𝚎𝚝𝚂𝚝𝚞𝚋 {self.symbol} {self.address} [{self.program_address}] »"
|
||||||
|
|
|
@ -34,8 +34,8 @@ from .token import Token
|
||||||
# This class encapsulates our knowledge of a Serum spot market.
|
# This class encapsulates our knowledge of a Serum spot market.
|
||||||
#
|
#
|
||||||
class SerumMarket(Market):
|
class SerumMarket(Market):
|
||||||
def __init__(self, program_id: PublicKey, address: PublicKey, base: Token, quote: Token, underlying_serum_market: PySerumMarket):
|
def __init__(self, serum_program_address: PublicKey, address: PublicKey, base: Token, quote: Token, underlying_serum_market: PySerumMarket):
|
||||||
super().__init__(program_id, address, InventorySource.SPL_TOKENS, base, quote, RaisingLotSizeConverter())
|
super().__init__(serum_program_address, address, InventorySource.SPL_TOKENS, base, quote, RaisingLotSizeConverter())
|
||||||
self.underlying_serum_market: PySerumMarket = underlying_serum_market
|
self.underlying_serum_market: PySerumMarket = underlying_serum_market
|
||||||
self.lot_size_converter: LotSizeConverter = LotSizeConverter(
|
self.lot_size_converter: LotSizeConverter = LotSizeConverter(
|
||||||
base, underlying_serum_market.state.base_lot_size, quote, underlying_serum_market.state.quote_lot_size)
|
base, underlying_serum_market.state.base_lot_size, quote, underlying_serum_market.state.quote_lot_size)
|
||||||
|
@ -54,7 +54,7 @@ class SerumMarket(Market):
|
||||||
return list(map(Order.from_serum_order, itertools.chain(bids_orderbook.orders(), asks_orderbook.orders())))
|
return list(map(Order.from_serum_order, itertools.chain(bids_orderbook.orders(), asks_orderbook.orders())))
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"""« 𝚂𝚎𝚛𝚞𝚖𝙼𝚊𝚛𝚔𝚎𝚝 {self.symbol} {self.address} [{self.program_id}]
|
return f"""« 𝚂𝚎𝚛𝚞𝚖𝙼𝚊𝚛𝚔𝚎𝚝 {self.symbol} {self.address} [{self.program_address}]
|
||||||
Event Queue: {self.underlying_serum_market.state.event_queue()}
|
Event Queue: {self.underlying_serum_market.state.event_queue()}
|
||||||
Request Queue: {self.underlying_serum_market.state.request_queue()}
|
Request Queue: {self.underlying_serum_market.state.request_queue()}
|
||||||
Bids: {self.underlying_serum_market.state.bids()}
|
Bids: {self.underlying_serum_market.state.bids()}
|
||||||
|
@ -69,13 +69,13 @@ class SerumMarket(Market):
|
||||||
# This class holds information to load a `SerumMarket` object but doesn't automatically load it.
|
# This class holds information to load a `SerumMarket` object but doesn't automatically load it.
|
||||||
#
|
#
|
||||||
class SerumMarketStub(Market):
|
class SerumMarketStub(Market):
|
||||||
def __init__(self, program_id: PublicKey, address: PublicKey, base: Token, quote: Token):
|
def __init__(self, serum_program_address: PublicKey, address: PublicKey, base: Token, quote: Token):
|
||||||
super().__init__(program_id, address, InventorySource.SPL_TOKENS, base, quote, RaisingLotSizeConverter())
|
super().__init__(serum_program_address, address, InventorySource.SPL_TOKENS, base, quote, RaisingLotSizeConverter())
|
||||||
|
|
||||||
def load(self, context: Context) -> SerumMarket:
|
def load(self, context: Context) -> SerumMarket:
|
||||||
underlying_serum_market: PySerumMarket = PySerumMarket.load(
|
underlying_serum_market: PySerumMarket = PySerumMarket.load(
|
||||||
context.client.compatible_client, self.address, context.dex_program_id)
|
context.client.compatible_client, self.address, context.serum_program_address)
|
||||||
return SerumMarket(self.program_id, self.address, self.base, self.quote, underlying_serum_market)
|
return SerumMarket(self.program_address, self.address, self.base, self.quote, underlying_serum_market)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"« 𝚂𝚎𝚛𝚞𝚖𝙼𝚊𝚛𝚔𝚎𝚝𝚂𝚝𝚞𝚋 {self.symbol} {self.address} [{self.program_id}] »"
|
return f"« 𝚂𝚎𝚛𝚞𝚖𝙼𝚊𝚛𝚔𝚎𝚝𝚂𝚝𝚞𝚋 {self.symbol} {self.address} [{self.program_address}] »"
|
||||||
|
|
|
@ -56,7 +56,7 @@ class SerumMarketInstructionBuilder(MarketInstructionBuilder):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load(context: Context, wallet: Wallet, serum_market: SerumMarket) -> "SerumMarketInstructionBuilder":
|
def load(context: Context, wallet: Wallet, serum_market: SerumMarket) -> "SerumMarketInstructionBuilder":
|
||||||
raw_market: PySerumMarket = PySerumMarket.load(
|
raw_market: PySerumMarket = PySerumMarket.load(
|
||||||
context.client.compatible_client, serum_market.address, context.dex_program_id)
|
context.client.compatible_client, serum_market.address, context.serum_program_address)
|
||||||
|
|
||||||
fee_discount_token_address: typing.Optional[PublicKey] = None
|
fee_discount_token_address: typing.Optional[PublicKey] = None
|
||||||
srm_token = context.token_lookup.find_by_symbol("SRM")
|
srm_token = context.token_lookup.find_by_symbol("SRM")
|
||||||
|
@ -68,7 +68,7 @@ class SerumMarketInstructionBuilder(MarketInstructionBuilder):
|
||||||
|
|
||||||
open_orders_address: typing.Optional[PublicKey] = None
|
open_orders_address: typing.Optional[PublicKey] = None
|
||||||
all_open_orders = OpenOrders.load_for_market_and_owner(
|
all_open_orders = OpenOrders.load_for_market_and_owner(
|
||||||
context, serum_market.address, wallet.address, context.dex_program_id, serum_market.base.decimals, serum_market.quote.decimals)
|
context, serum_market.address, wallet.address, context.serum_program_address, serum_market.base.decimals, serum_market.quote.decimals)
|
||||||
if len(all_open_orders) > 0:
|
if len(all_open_orders) > 0:
|
||||||
open_orders_address = all_open_orders[0].address
|
open_orders_address = all_open_orders[0].address
|
||||||
|
|
||||||
|
|
|
@ -50,16 +50,16 @@ from .token import Token
|
||||||
# there is a name-value pair for the particular market we're interested in. Also, the
|
# there is a name-value pair for the particular market we're interested in. Also, the
|
||||||
# current file only lists USDC and USDT markets, so that's all we can support this way.
|
# current file only lists USDC and USDT markets, so that's all we can support this way.
|
||||||
class SerumMarketLookup(MarketLookup):
|
class SerumMarketLookup(MarketLookup):
|
||||||
def __init__(self, program_id: PublicKey, token_data: typing.Dict) -> None:
|
def __init__(self, serum_program_address: PublicKey, token_data: typing.Dict) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.program_id: PublicKey = program_id
|
self.serum_program_address: PublicKey = serum_program_address
|
||||||
self.token_data: typing.Dict = token_data
|
self.token_data: typing.Dict = token_data
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load(program_id: PublicKey, token_data_filename: str) -> "SerumMarketLookup":
|
def load(serum_program_address: PublicKey, token_data_filename: str) -> "SerumMarketLookup":
|
||||||
with open(token_data_filename) as json_file:
|
with open(token_data_filename) as json_file:
|
||||||
token_data = json.load(json_file)
|
token_data = json.load(json_file)
|
||||||
return SerumMarketLookup(program_id, token_data)
|
return SerumMarketLookup(serum_program_address, token_data)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _find_data_by_symbol(symbol: str, token_data: typing.Dict) -> typing.Optional[typing.Dict]:
|
def _find_data_by_symbol(symbol: str, token_data: typing.Dict) -> typing.Optional[typing.Dict]:
|
||||||
|
@ -121,7 +121,7 @@ class SerumMarketLookup(MarketLookup):
|
||||||
f"Could not find market with quote token '{quote.symbol}'. Only markets based on USDC or USDT are supported.")
|
f"Could not find market with quote token '{quote.symbol}'. Only markets based on USDC or USDT are supported.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return SerumMarketStub(self.program_id, market_address, base, quote)
|
return SerumMarketStub(self.serum_program_address, market_address, base, quote)
|
||||||
|
|
||||||
def find_by_address(self, address: PublicKey) -> typing.Optional[Market]:
|
def find_by_address(self, address: PublicKey) -> typing.Optional[Market]:
|
||||||
address_string: str = str(address)
|
address_string: str = str(address)
|
||||||
|
@ -138,7 +138,7 @@ class SerumMarketLookup(MarketLookup):
|
||||||
raise Exception("Could not load token data for USDC (which should always be present).")
|
raise Exception("Could not load token data for USDC (which should always be present).")
|
||||||
quote = Token(quote_data["symbol"], quote_data["name"], PublicKey(
|
quote = Token(quote_data["symbol"], quote_data["name"], PublicKey(
|
||||||
quote_data["address"]), Decimal(quote_data["decimals"]))
|
quote_data["address"]), Decimal(quote_data["decimals"]))
|
||||||
return SerumMarketStub(self.program_id, market_address, base, quote)
|
return SerumMarketStub(self.serum_program_address, market_address, base, quote)
|
||||||
if "serumV3Usdt" in token_data["extensions"]:
|
if "serumV3Usdt" in token_data["extensions"]:
|
||||||
if token_data["extensions"]["serumV3Usdt"] == address_string:
|
if token_data["extensions"]["serumV3Usdt"] == address_string:
|
||||||
market_address_string = token_data["extensions"]["serumV3Usdt"]
|
market_address_string = token_data["extensions"]["serumV3Usdt"]
|
||||||
|
@ -150,7 +150,7 @@ class SerumMarketLookup(MarketLookup):
|
||||||
raise Exception("Could not load token data for USDT (which should always be present).")
|
raise Exception("Could not load token data for USDT (which should always be present).")
|
||||||
quote = Token(quote_data["symbol"], quote_data["name"], PublicKey(
|
quote = Token(quote_data["symbol"], quote_data["name"], PublicKey(
|
||||||
quote_data["address"]), Decimal(quote_data["decimals"]))
|
quote_data["address"]), Decimal(quote_data["decimals"]))
|
||||||
return SerumMarketStub(self.program_id, market_address, base, quote)
|
return SerumMarketStub(self.serum_program_address, market_address, base, quote)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def all_markets(self) -> typing.Sequence[Market]:
|
def all_markets(self) -> typing.Sequence[Market]:
|
||||||
|
@ -165,12 +165,12 @@ class SerumMarketLookup(MarketLookup):
|
||||||
market_address = PublicKey(market_address_string)
|
market_address = PublicKey(market_address_string)
|
||||||
base = Token(token_data["symbol"], token_data["name"], PublicKey(
|
base = Token(token_data["symbol"], token_data["name"], PublicKey(
|
||||||
token_data["address"]), Decimal(token_data["decimals"]))
|
token_data["address"]), Decimal(token_data["decimals"]))
|
||||||
all_markets += [SerumMarketStub(self.program_id, market_address, base, usdc)]
|
all_markets += [SerumMarketStub(self.serum_program_address, market_address, base, usdc)]
|
||||||
if "serumV3Usdt" in token_data["extensions"]:
|
if "serumV3Usdt" in token_data["extensions"]:
|
||||||
market_address_string = token_data["extensions"]["serumV3Usdt"]
|
market_address_string = token_data["extensions"]["serumV3Usdt"]
|
||||||
market_address = PublicKey(market_address_string)
|
market_address = PublicKey(market_address_string)
|
||||||
base = Token(token_data["symbol"], token_data["name"], PublicKey(
|
base = Token(token_data["symbol"], token_data["name"], PublicKey(
|
||||||
token_data["address"]), Decimal(token_data["decimals"]))
|
token_data["address"]), Decimal(token_data["decimals"]))
|
||||||
all_markets += [SerumMarketStub(self.program_id, market_address, base, usdt)]
|
all_markets += [SerumMarketStub(self.serum_program_address, market_address, base, usdt)]
|
||||||
|
|
||||||
return all_markets
|
return all_markets
|
||||||
|
|
|
@ -35,8 +35,8 @@ from .token import Token
|
||||||
# This class encapsulates our knowledge of a Serum spot market.
|
# This class encapsulates our knowledge of a Serum spot market.
|
||||||
#
|
#
|
||||||
class SpotMarket(Market):
|
class SpotMarket(Market):
|
||||||
def __init__(self, program_id: PublicKey, address: PublicKey, base: Token, quote: Token, group: Group, underlying_serum_market: PySerumMarket):
|
def __init__(self, serum_program_address: PublicKey, address: PublicKey, base: Token, quote: Token, group: Group, underlying_serum_market: PySerumMarket):
|
||||||
super().__init__(program_id, address, InventorySource.ACCOUNT, base, quote, RaisingLotSizeConverter())
|
super().__init__(serum_program_address, address, InventorySource.ACCOUNT, base, quote, RaisingLotSizeConverter())
|
||||||
self.group: Group = group
|
self.group: Group = group
|
||||||
self.underlying_serum_market: PySerumMarket = underlying_serum_market
|
self.underlying_serum_market: PySerumMarket = underlying_serum_market
|
||||||
self.lot_size_converter: LotSizeConverter = LotSizeConverter(
|
self.lot_size_converter: LotSizeConverter = LotSizeConverter(
|
||||||
|
@ -56,7 +56,7 @@ class SpotMarket(Market):
|
||||||
return list(map(Order.from_serum_order, itertools.chain(bids_orderbook.orders(), asks_orderbook.orders())))
|
return list(map(Order.from_serum_order, itertools.chain(bids_orderbook.orders(), asks_orderbook.orders())))
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"""« 𝚂𝚙𝚘𝚝𝙼𝚊𝚛𝚔𝚎𝚝 {self.symbol} {self.address} [{self.program_id}]
|
return f"""« 𝚂𝚙𝚘𝚝𝙼𝚊𝚛𝚔𝚎𝚝 {self.symbol} {self.address} [{self.program_address}]
|
||||||
Event Queue: {self.underlying_serum_market.state.event_queue()}
|
Event Queue: {self.underlying_serum_market.state.event_queue()}
|
||||||
Request Queue: {self.underlying_serum_market.state.request_queue()}
|
Request Queue: {self.underlying_serum_market.state.request_queue()}
|
||||||
Bids: {self.underlying_serum_market.state.bids()}
|
Bids: {self.underlying_serum_market.state.bids()}
|
||||||
|
@ -73,15 +73,15 @@ class SpotMarket(Market):
|
||||||
|
|
||||||
|
|
||||||
class SpotMarketStub(Market):
|
class SpotMarketStub(Market):
|
||||||
def __init__(self, program_id: PublicKey, address: PublicKey, base: Token, quote: Token, group_address: PublicKey):
|
def __init__(self, serum_program_address: PublicKey, address: PublicKey, base: Token, quote: Token, group_address: PublicKey):
|
||||||
super().__init__(program_id, address, InventorySource.ACCOUNT, base, quote, RaisingLotSizeConverter())
|
super().__init__(serum_program_address, address, InventorySource.ACCOUNT, base, quote, RaisingLotSizeConverter())
|
||||||
self.group_address: PublicKey = group_address
|
self.group_address: PublicKey = group_address
|
||||||
|
|
||||||
def load(self, context: Context, group: typing.Optional[Group]) -> SpotMarket:
|
def load(self, context: Context, group: typing.Optional[Group]) -> SpotMarket:
|
||||||
actual_group: Group = group or Group.load(context, self.group_address)
|
actual_group: Group = group or Group.load(context, self.group_address)
|
||||||
underlying_serum_market: PySerumMarket = PySerumMarket.load(
|
underlying_serum_market: PySerumMarket = PySerumMarket.load(
|
||||||
context.client.compatible_client, self.address, context.dex_program_id)
|
context.client.compatible_client, self.address, context.serum_program_address)
|
||||||
return SpotMarket(self.program_id, self.address, self.base, self.quote, actual_group, underlying_serum_market)
|
return SpotMarket(self.program_address, self.address, self.base, self.quote, actual_group, underlying_serum_market)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"« 𝚂𝚙𝚘𝚝𝙼𝚊𝚛𝚔𝚎𝚝𝚂𝚝𝚞𝚋 {self.symbol} {self.address} [{self.program_id}] »"
|
return f"« 𝚂𝚙𝚘𝚝𝙼𝚊𝚛𝚔𝚎𝚝𝚂𝚝𝚞𝚋 {self.symbol} {self.address} [{self.program_address}] »"
|
||||||
|
|
|
@ -58,7 +58,7 @@ class SpotMarketInstructionBuilder(MarketInstructionBuilder):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load(context: Context, wallet: Wallet, group: Group, account: Account, spot_market: SpotMarket) -> "SpotMarketInstructionBuilder":
|
def load(context: Context, wallet: Wallet, group: Group, account: Account, spot_market: SpotMarket) -> "SpotMarketInstructionBuilder":
|
||||||
raw_market: PySerumMarket = PySerumMarket.load(
|
raw_market: PySerumMarket = PySerumMarket.load(
|
||||||
context.client.compatible_client, spot_market.address, context.dex_program_id)
|
context.client.compatible_client, spot_market.address, context.serum_program_address)
|
||||||
|
|
||||||
fee_discount_token_address: typing.Optional[PublicKey] = None
|
fee_discount_token_address: typing.Optional[PublicKey] = None
|
||||||
srm_token = context.token_lookup.find_by_symbol("SRM")
|
srm_token = context.token_lookup.find_by_symbol("SRM")
|
||||||
|
|
|
@ -220,7 +220,7 @@ def fetch_all_recent_transaction_signatures(context: Context) -> typing.Sequence
|
||||||
before = None
|
before = None
|
||||||
signature_results: typing.List[str] = []
|
signature_results: typing.List[str] = []
|
||||||
while not all_fetched:
|
while not all_fetched:
|
||||||
signatures = context.client.get_confirmed_signatures_for_address2(context.group_id, before=before)
|
signatures = context.client.get_confirmed_signatures_for_address2(context.group_address, before=before)
|
||||||
signature_results += signatures
|
signature_results += signatures
|
||||||
if (len(signatures) == 0):
|
if (len(signatures) == 0):
|
||||||
all_fetched = True
|
all_fetched = True
|
||||||
|
@ -232,7 +232,7 @@ def fetch_all_recent_transaction_signatures(context: Context) -> typing.Sequence
|
||||||
|
|
||||||
def mango_instruction_from_response(context: Context, all_accounts: typing.Sequence[PublicKey], instruction_data: typing.Dict) -> typing.Optional["MangoInstruction"]:
|
def mango_instruction_from_response(context: Context, all_accounts: typing.Sequence[PublicKey], instruction_data: typing.Dict) -> typing.Optional["MangoInstruction"]:
|
||||||
program_account_index = instruction_data["programIdIndex"]
|
program_account_index = instruction_data["programIdIndex"]
|
||||||
if all_accounts[program_account_index] != context.program_id:
|
if all_accounts[program_account_index] != context.mango_program_address:
|
||||||
# It's an instruction, it's just not a Mango one.
|
# It's an instruction, it's just not a Mango one.
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,7 @@ def build_spot_open_orders_watcher(context: Context, manager: WebSocketSubscript
|
||||||
|
|
||||||
def build_serum_open_orders_watcher(context: Context, manager: WebSocketSubscriptionManager, health_check: HealthCheck, serum_market: SerumMarket, wallet: Wallet) -> Watcher[PlacedOrdersContainer]:
|
def build_serum_open_orders_watcher(context: Context, manager: WebSocketSubscriptionManager, health_check: HealthCheck, serum_market: SerumMarket, wallet: Wallet) -> Watcher[PlacedOrdersContainer]:
|
||||||
all_open_orders = OpenOrders.load_for_market_and_owner(
|
all_open_orders = OpenOrders.load_for_market_and_owner(
|
||||||
context, serum_market.address, wallet.address, context.dex_program_id, serum_market.base.decimals, serum_market.quote.decimals)
|
context, serum_market.address, wallet.address, context.serum_program_address, serum_market.base.decimals, serum_market.quote.decimals)
|
||||||
if len(all_open_orders) > 0:
|
if len(all_open_orders) > 0:
|
||||||
initial_serum_open_orders: OpenOrders = all_open_orders[0]
|
initial_serum_open_orders: OpenOrders = all_open_orders[0]
|
||||||
open_orders_address = initial_serum_open_orders.address
|
open_orders_address = initial_serum_open_orders.address
|
||||||
|
|
|
@ -10,7 +10,7 @@ do
|
||||||
cancel-my-orders --name "Random Taker ${MARKET} (cancel)" --market $MARKET --log-level ERROR
|
cancel-my-orders --name "Random Taker ${MARKET} (cancel)" --market $MARKET --log-level ERROR
|
||||||
|
|
||||||
RANDOM_POSITION_SIZE=$(echo "scale=4; ($(echo "$RANDOM % 1000" | bc) / 1000) * $POSITION_SIZE_CEILING" | bc)
|
RANDOM_POSITION_SIZE=$(echo "scale=4; ($(echo "$RANDOM % 1000" | bc) / 1000) * $POSITION_SIZE_CEILING" | bc)
|
||||||
CURRENT_PRICE=$(fetch-price --provider serum --symbol $ORACLE_MARKET --log-level ERROR --cluster mainnet | cut -d"'" -f 2 | sed 's/,//')
|
CURRENT_PRICE=$(fetch-price --provider serum --symbol $ORACLE_MARKET --log-level ERROR --cluster-name mainnet | cut -d"'" -f 2 | sed 's/,//')
|
||||||
place-order --name "Random Taker ${MARKET} (place buy)" --market $MARKET --order-type IOC --log-level ERROR \
|
place-order --name "Random Taker ${MARKET} (place buy)" --market $MARKET --order-type IOC --log-level ERROR \
|
||||||
--side BUY --quantity $RANDOM_POSITION_SIZE --price $(echo "$CURRENT_PRICE + $IMMEDIATE_BUY_ADJUSTMENT" | bc)
|
--side BUY --quantity $RANDOM_POSITION_SIZE --price $(echo "$CURRENT_PRICE + $IMMEDIATE_BUY_ADJUSTMENT" | bc)
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ do
|
||||||
sleep ${PAUSE_FOR}
|
sleep ${PAUSE_FOR}
|
||||||
|
|
||||||
RANDOM_POSITION_SIZE=$(echo "scale=4; ($(echo "$RANDOM % 1000" | bc) / 1000) * $POSITION_SIZE_CEILING" | bc)
|
RANDOM_POSITION_SIZE=$(echo "scale=4; ($(echo "$RANDOM % 1000" | bc) / 1000) * $POSITION_SIZE_CEILING" | bc)
|
||||||
CURRENT_PRICE=$(fetch-price --provider serum --symbol $ORACLE_MARKET --log-level ERROR --cluster mainnet | cut -d"'" -f 2 | sed 's/,//')
|
CURRENT_PRICE=$(fetch-price --provider serum --symbol $ORACLE_MARKET --log-level ERROR --cluster-name mainnet | cut -d"'" -f 2 | sed 's/,//')
|
||||||
place-order --name "Random Taker ${MARKET} (place sell)" --market $MARKET --order-type IOC --log-level ERROR \
|
place-order --name "Random Taker ${MARKET} (place sell)" --market $MARKET --order-type IOC --log-level ERROR \
|
||||||
--side SELL --quantity $RANDOM_POSITION_SIZE --price $(echo "$CURRENT_PRICE - $IMMEDIATE_BUY_ADJUSTMENT" | bc)
|
--side SELL --quantity $RANDOM_POSITION_SIZE --price $(echo "$CURRENT_PRICE - $IMMEDIATE_BUY_ADJUSTMENT" | bc)
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ while :
|
||||||
do
|
do
|
||||||
cancel-my-orders --name "WSMM ${MARKET} (cancel)" --market $MARKET --log-level ERROR
|
cancel-my-orders --name "WSMM ${MARKET} (cancel)" --market $MARKET --log-level ERROR
|
||||||
|
|
||||||
CURRENT_PRICE=$(fetch-price --provider serum --symbol $ORACLE_MARKET --log-level ERROR --cluster mainnet | cut -d"'" -f 2 | sed 's/,//')
|
CURRENT_PRICE=$(fetch-price --provider serum --symbol $ORACLE_MARKET --log-level ERROR --cluster-name mainnet | cut -d"'" -f 2 | sed 's/,//')
|
||||||
place-order --name "WSMM ${MARKET} (buy)" --market $MARKET --order-type LIMIT \
|
place-order --name "WSMM ${MARKET} (buy)" --market $MARKET --order-type LIMIT \
|
||||||
--log-level ERROR --side BUY --quantity $FIXED_POSITION_SIZE --price $(echo "$CURRENT_PRICE - $FIXED_SPREAD" | bc)
|
--log-level ERROR --side BUY --quantity $FIXED_POSITION_SIZE --price $(echo "$CURRENT_PRICE - $FIXED_SPREAD" | bc)
|
||||||
place-order --name "WSMM ${MARKET} (sell)" --market $MARKET --order-type LIMIT \
|
place-order --name "WSMM ${MARKET} (sell)" --market $MARKET --order-type LIMIT \
|
||||||
|
|
|
@ -53,13 +53,13 @@ def fake_token_info() -> mango.TokenInfo:
|
||||||
|
|
||||||
def fake_context() -> mango.Context:
|
def fake_context() -> mango.Context:
|
||||||
context = mango.Context(name="Mango Test",
|
context = mango.Context(name="Mango Test",
|
||||||
cluster="test",
|
cluster_name="test",
|
||||||
cluster_url="http://localhost",
|
cluster_url="http://localhost",
|
||||||
skip_preflight=False,
|
skip_preflight=False,
|
||||||
program_id=fake_seeded_public_key("program ID"),
|
mango_program_address=fake_seeded_public_key("Mango program address"),
|
||||||
dex_program_id=fake_seeded_public_key("DEX program ID"),
|
serum_program_address=fake_seeded_public_key("Serum program address"),
|
||||||
group_name="TEST_GROUP",
|
group_name="TEST_GROUP",
|
||||||
group_id=fake_seeded_public_key("group ID"),
|
group_address=fake_seeded_public_key("group ID"),
|
||||||
token_lookup=mango.NullTokenLookup(),
|
token_lookup=mango.NullTokenLookup(),
|
||||||
market_lookup=mango.NullMarketLookup())
|
market_lookup=mango.NullMarketLookup())
|
||||||
context.client = MockClient()
|
context.client = MockClient()
|
||||||
|
|
|
@ -37,7 +37,7 @@ def test_group_layout():
|
||||||
assert group.signer_nonce == 0
|
assert group.signer_nonce == 0
|
||||||
assert group.signer_key == PublicKey("9GXvznfEep9yEsvH4CQzqNy5GH81FNk1HDeAR8UjefSf")
|
assert group.signer_key == PublicKey("9GXvznfEep9yEsvH4CQzqNy5GH81FNk1HDeAR8UjefSf")
|
||||||
assert group.admin == PublicKey("Cwg1f6m4m3DGwMEbmsbAfDtUToUf5jRdKrJSGD7GfZCB")
|
assert group.admin == PublicKey("Cwg1f6m4m3DGwMEbmsbAfDtUToUf5jRdKrJSGD7GfZCB")
|
||||||
assert group.dex_program_id == PublicKey("DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY")
|
assert group.serum_program_address == PublicKey("DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY")
|
||||||
assert group.cache == PublicKey("PJhM2enPpZH7E9wgw7Sqt8S2p4mr3Bc7SycawQwfY7b")
|
assert group.cache == PublicKey("PJhM2enPpZH7E9wgw7Sqt8S2p4mr3Bc7SycawQwfY7b")
|
||||||
assert group.valid_interval == 5
|
assert group.valid_interval == 5
|
||||||
assert group.dao_vault == PublicKey("14gfuPWjUQnYXpsxs4WgsjafUrJctKkR9AMFH7fjvTgR")
|
assert group.dao_vault == PublicKey("14gfuPWjUQnYXpsxs4WgsjafUrJctKkR9AMFH7fjvTgR")
|
||||||
|
|
|
@ -31,13 +31,13 @@ def mock_group():
|
||||||
signer_nonce = Decimal(1)
|
signer_nonce = Decimal(1)
|
||||||
signer_key = fake_seeded_public_key("signer key")
|
signer_key = fake_seeded_public_key("signer key")
|
||||||
admin_key = fake_seeded_public_key("admin key")
|
admin_key = fake_seeded_public_key("admin key")
|
||||||
dex_program_id = fake_seeded_public_key("DEX program ID")
|
serum_program_address = fake_seeded_public_key("DEX program ID")
|
||||||
cache_key = fake_seeded_public_key("cache key")
|
cache_key = fake_seeded_public_key("cache key")
|
||||||
valid_interval = Decimal(7)
|
valid_interval = Decimal(7)
|
||||||
|
|
||||||
return mango.Group(account_info, mango.Version.V1, name, meta_data, token_infos,
|
return mango.Group(account_info, mango.Version.V1, name, meta_data, token_infos,
|
||||||
spot_markets, perp_markets, oracles, signer_nonce, signer_key,
|
spot_markets, perp_markets, oracles, signer_nonce, signer_key,
|
||||||
admin_key, dex_program_id, cache_key, valid_interval)
|
admin_key, serum_program_address, cache_key, valid_interval)
|
||||||
|
|
||||||
|
|
||||||
def mock_prices(prices: typing.Sequence[str]):
|
def mock_prices(prices: typing.Sequence[str]):
|
||||||
|
@ -53,11 +53,11 @@ def mock_prices(prices: typing.Sequence[str]):
|
||||||
|
|
||||||
def mock_open_orders(base_token_free: Decimal = Decimal(0), base_token_total: Decimal = Decimal(0), quote_token_free: Decimal = Decimal(0), quote_token_total: Decimal = Decimal(0), referrer_rebate_accrued: Decimal = Decimal(0)):
|
def mock_open_orders(base_token_free: Decimal = Decimal(0), base_token_total: Decimal = Decimal(0), quote_token_free: Decimal = Decimal(0), quote_token_total: Decimal = Decimal(0), referrer_rebate_accrued: Decimal = Decimal(0)):
|
||||||
account_info = fake_account_info()
|
account_info = fake_account_info()
|
||||||
program_id = fake_seeded_public_key("program ID")
|
program_address = fake_seeded_public_key("program address")
|
||||||
market = fake_seeded_public_key("market")
|
market = fake_seeded_public_key("market")
|
||||||
owner = fake_seeded_public_key("owner")
|
owner = fake_seeded_public_key("owner")
|
||||||
|
|
||||||
flags = mango.AccountFlags(mango.Version.V1, True, False, True, False, False, False, False, False)
|
flags = mango.AccountFlags(mango.Version.V1, True, False, True, False, False, False, False, False)
|
||||||
return mango.OpenOrders(account_info, mango.Version.V1, program_id, flags, market,
|
return mango.OpenOrders(account_info, mango.Version.V1, program_address, flags, market,
|
||||||
owner, base_token_free, base_token_total, quote_token_free,
|
owner, base_token_free, base_token_total, quote_token_free,
|
||||||
quote_token_total, [], referrer_rebate_accrued)
|
quote_token_total, [], referrer_rebate_accrued)
|
||||||
|
|
|
@ -4,10 +4,10 @@ from solana.publickey import PublicKey
|
||||||
|
|
||||||
|
|
||||||
def context_has_default_values(ctx: mango.Context):
|
def context_has_default_values(ctx: mango.Context):
|
||||||
assert ctx.program_id == PublicKey("mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68")
|
assert ctx.mango_program_address == PublicKey("mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68")
|
||||||
assert ctx.dex_program_id == PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin")
|
assert ctx.serum_program_address == PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin")
|
||||||
assert ctx.group_name == "mainnet.1"
|
assert ctx.group_name == "mainnet.1"
|
||||||
assert ctx.group_id == PublicKey("98pjRuQjK3qA6gXts96PqZT4Ze5QmnCmt3QYjhbUSPue")
|
assert ctx.group_address == PublicKey("98pjRuQjK3qA6gXts96PqZT4Ze5QmnCmt3QYjhbUSPue")
|
||||||
|
|
||||||
|
|
||||||
def test_context_default_exists():
|
def test_context_default_exists():
|
||||||
|
@ -22,21 +22,21 @@ def test_context_default_values():
|
||||||
# def test_new_from_cluster():
|
# def test_new_from_cluster():
|
||||||
# context_has_default_values(mango.ContextBuilder.default())
|
# context_has_default_values(mango.ContextBuilder.default())
|
||||||
# derived = mango.ContextBuilder.default().new_from_cluster("mainnet")
|
# derived = mango.ContextBuilder.default().new_from_cluster("mainnet")
|
||||||
# assert derived.cluster == "mainnet"
|
# assert derived.cluster_name == "mainnet"
|
||||||
# assert derived.cluster_url == "https://solana-api.projectserum.com"
|
# assert derived.cluster_url == "https://solana-api.projectserum.com"
|
||||||
# assert derived.program_id == PublicKey("mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68")
|
# assert derived.mango_program_address == PublicKey("mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68")
|
||||||
# assert derived.dex_program_id == PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin")
|
# assert derived.serum_program_address == PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin")
|
||||||
# assert derived.group_name == "mainnet.0"
|
# assert derived.group_name == "mainnet.0"
|
||||||
# assert derived.group_id == PublicKey("98pjRuQjK3qA6gXts96PqZT4Ze5QmnCmt3QYjhbUSPue")
|
# assert derived.group_address == PublicKey("98pjRuQjK3qA6gXts96PqZT4Ze5QmnCmt3QYjhbUSPue")
|
||||||
# context_has_default_values(mango.ContextBuilder.default())
|
# context_has_default_values(mango.ContextBuilder.default())
|
||||||
|
|
||||||
|
|
||||||
def test_new_from_group_name():
|
def test_new_from_group_name():
|
||||||
context_has_default_values(mango.ContextBuilder.default())
|
context_has_default_values(mango.ContextBuilder.default())
|
||||||
derived = mango.ContextBuilder.from_group_name(mango.ContextBuilder.default(), "mainnet.0")
|
derived = mango.ContextBuilder.from_group_name(mango.ContextBuilder.default(), "mainnet.0")
|
||||||
assert derived.program_id == PublicKey("mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68")
|
assert derived.mango_program_address == PublicKey("mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68")
|
||||||
assert derived.dex_program_id == PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin")
|
assert derived.serum_program_address == PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin")
|
||||||
|
|
||||||
# Should update both of these values on new group name.
|
# Should update both of these values on new group name.
|
||||||
assert derived.group_name == "mainnet.0"
|
assert derived.group_name == "mainnet.0"
|
||||||
assert derived.group_id == PublicKey("4yJ2Vx3kZnmHTNCrHzdoj5nCwriF2kVhfKNvqC6gU8tr")
|
assert derived.group_address == PublicKey("4yJ2Vx3kZnmHTNCrHzdoj5nCwriF2kVhfKNvqC6gU8tr")
|
||||||
|
|
|
@ -15,7 +15,7 @@ def test_construction():
|
||||||
signer_nonce = Decimal(1)
|
signer_nonce = Decimal(1)
|
||||||
signer_key = fake_seeded_public_key("signer key")
|
signer_key = fake_seeded_public_key("signer key")
|
||||||
admin_key = fake_seeded_public_key("admin key")
|
admin_key = fake_seeded_public_key("admin key")
|
||||||
dex_program_id = fake_seeded_public_key("DEX program ID")
|
serum_program_address = fake_seeded_public_key("Serum program ID")
|
||||||
cache_key = fake_seeded_public_key("cache key")
|
cache_key = fake_seeded_public_key("cache key")
|
||||||
valid_interval = Decimal(7)
|
valid_interval = Decimal(7)
|
||||||
dao_vault = fake_seeded_public_key("insurance vault")
|
dao_vault = fake_seeded_public_key("insurance vault")
|
||||||
|
@ -24,7 +24,7 @@ def test_construction():
|
||||||
|
|
||||||
actual = mango.Group(account_info, mango.Version.V1, name, meta_data, shared_quote_token,
|
actual = mango.Group(account_info, mango.Version.V1, name, meta_data, shared_quote_token,
|
||||||
in_basket, basket_markets, signer_nonce, signer_key, admin_key,
|
in_basket, basket_markets, signer_nonce, signer_key, admin_key,
|
||||||
dex_program_id, cache_key, valid_interval, dao_vault, srm_vault, msrm_vault)
|
serum_program_address, cache_key, valid_interval, dao_vault, srm_vault, msrm_vault)
|
||||||
|
|
||||||
assert actual is not None
|
assert actual is not None
|
||||||
assert actual.logger is not None
|
assert actual.logger is not None
|
||||||
|
@ -36,7 +36,7 @@ def test_construction():
|
||||||
assert actual.signer_nonce == signer_nonce
|
assert actual.signer_nonce == signer_nonce
|
||||||
assert actual.signer_key == signer_key
|
assert actual.signer_key == signer_key
|
||||||
assert actual.admin == admin_key
|
assert actual.admin == admin_key
|
||||||
assert actual.dex_program_id == dex_program_id
|
assert actual.serum_program_address == serum_program_address
|
||||||
assert actual.cache == cache_key
|
assert actual.cache == cache_key
|
||||||
assert actual.valid_interval == valid_interval
|
assert actual.valid_interval == valid_interval
|
||||||
assert actual.dao_vault == dao_vault
|
assert actual.dao_vault == dao_vault
|
||||||
|
@ -55,7 +55,7 @@ def test_construction():
|
||||||
# context = fake_context()
|
# context = fake_context()
|
||||||
# ids_json_token_lookup: mango.TokenLookup = mango.IdsJsonTokenLookup("devnet", mango.default_group_name)
|
# ids_json_token_lookup: mango.TokenLookup = mango.IdsJsonTokenLookup("devnet", mango.default_group_name)
|
||||||
# context.token_lookup = ids_json_token_lookup
|
# context.token_lookup = ids_json_token_lookup
|
||||||
# # context.cluster = "devnet" # Needed for devnet token lookups.
|
# # context.cluster_name = "devnet" # Needed for devnet token lookups.
|
||||||
# group = mango.Group.parse(context, group_account_info)
|
# group = mango.Group.parse(context, group_account_info)
|
||||||
|
|
||||||
# assert group.address == group_public_key
|
# assert group.address == group_public_key
|
||||||
|
@ -81,7 +81,7 @@ def test_construction():
|
||||||
# assert group.signer_nonce == 0
|
# assert group.signer_nonce == 0
|
||||||
# assert group.signer_key == PublicKey("HzpyPmuz8C4P6ExinCDndujSAy68MeMNHNzYLpmMAGMS")
|
# assert group.signer_key == PublicKey("HzpyPmuz8C4P6ExinCDndujSAy68MeMNHNzYLpmMAGMS")
|
||||||
# assert group.admin == PublicKey("Cwg1f6m4m3DGwMEbmsbAfDtUToUf5jRdKrJSGD7GfZCB")
|
# assert group.admin == PublicKey("Cwg1f6m4m3DGwMEbmsbAfDtUToUf5jRdKrJSGD7GfZCB")
|
||||||
# assert group.dex_program_id == PublicKey("DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY")
|
# assert group.serum_program_address == PublicKey("DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY")
|
||||||
# assert group.cache == PublicKey("D2zSEAJTJADF46eCm1SrsSP3k4QDfv33nneTpbHow3DM")
|
# assert group.cache == PublicKey("D2zSEAJTJADF46eCm1SrsSP3k4QDfv33nneTpbHow3DM")
|
||||||
# assert group.valid_interval == 5
|
# assert group.valid_interval == 5
|
||||||
# assert group.dao_vault is None
|
# assert group.dao_vault is None
|
||||||
|
|
|
@ -6,12 +6,12 @@ from decimal import Decimal
|
||||||
|
|
||||||
def test_constructor():
|
def test_constructor():
|
||||||
account_info = fake_account_info()
|
account_info = fake_account_info()
|
||||||
program_id = fake_public_key()
|
program_address = fake_public_key()
|
||||||
market = fake_public_key()
|
market = fake_public_key()
|
||||||
owner = fake_public_key()
|
owner = fake_public_key()
|
||||||
|
|
||||||
flags = mango.AccountFlags(mango.Version.V1, True, False, True, False, False, False, False, False)
|
flags = mango.AccountFlags(mango.Version.V1, True, False, True, False, False, False, False, False)
|
||||||
actual = mango.OpenOrders(account_info, mango.Version.V1, program_id, flags, market,
|
actual = mango.OpenOrders(account_info, mango.Version.V1, program_address, flags, market,
|
||||||
owner, Decimal(0), Decimal(0), Decimal(0), Decimal(0), [], Decimal(0))
|
owner, Decimal(0), Decimal(0), Decimal(0), Decimal(0), [], Decimal(0))
|
||||||
assert actual is not None
|
assert actual is not None
|
||||||
assert actual.logger is not None
|
assert actual.logger is not None
|
||||||
|
|
|
@ -5,16 +5,16 @@ from decimal import Decimal
|
||||||
|
|
||||||
|
|
||||||
def test_spot_market_stub_constructor():
|
def test_spot_market_stub_constructor():
|
||||||
program_id = fake_seeded_public_key("program ID")
|
program_address = fake_seeded_public_key("program address")
|
||||||
address = fake_seeded_public_key("spot market address")
|
address = fake_seeded_public_key("spot market address")
|
||||||
base = mango.Token("BASE", "Base Token", fake_seeded_public_key("base token"), Decimal(7))
|
base = mango.Token("BASE", "Base Token", fake_seeded_public_key("base token"), Decimal(7))
|
||||||
quote = mango.Token("QUOTE", "Quote Token", fake_seeded_public_key("quote token"), Decimal(9))
|
quote = mango.Token("QUOTE", "Quote Token", fake_seeded_public_key("quote token"), Decimal(9))
|
||||||
group_address = fake_seeded_public_key("group address")
|
group_address = fake_seeded_public_key("group address")
|
||||||
actual = mango.SpotMarketStub(program_id, address, base, quote, group_address)
|
actual = mango.SpotMarketStub(program_address, address, base, quote, group_address)
|
||||||
assert actual is not None
|
assert actual is not None
|
||||||
assert actual.logger is not None
|
assert actual.logger is not None
|
||||||
assert actual.base == base
|
assert actual.base == base
|
||||||
assert actual.quote == quote
|
assert actual.quote == quote
|
||||||
assert actual.address == address
|
assert actual.address == address
|
||||||
assert actual.program_id == program_id
|
assert actual.program_address == program_address
|
||||||
assert actual.group_address == group_address
|
assert actual.group_address == group_address
|
||||||
|
|
Loading…
Reference in New Issue