Skip to content

Commit

Permalink
python jit sdk (#11)
Browse files Browse the repository at this point in the history
* feat: jit proxy client, base jitter, jitter shotgun

* fix: jitter event subscriptions

* fix: jitter event subs

* feat: jit sniper, better jit shotgun

* fix: add missing accounts in proxy client ixs

* fix: missing jit sniper return

* feat: examples for sniper & shotgun

* chore: fmt & better logging

* chore: remove unused fn

* chore: fmt imports

* fix: await sniper try fill

* chore: streamline

* merge

* fix: wrong variable in sniper

* fix: missed await

* fix: get rid of keyerrors

* Delete python/sdk/jit_proxy/__pycache__ directory

* Delete python/sdk/jit_proxy/jitter/__pycache__ directory

* fix: gitignore, remove unused files

* chore: black fmt everything

* chore: update readme

* chore: update main README

* chore: update driftpy to 0.7.6
  • Loading branch information
soundsonacid authored Dec 22, 2023
1 parent c472734 commit f32afc1
Show file tree
Hide file tree
Showing 11 changed files with 1,023 additions and 2 deletions.
8 changes: 7 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,10 @@ migrations
ts/sdk/lib/
ts/sdk/src/**/*.js
ts/sdk/src/**/*.js.map
package-lock.json
package-lock.json
python/sdk/examples/.env
python/poetry.lock
__pycache__/
*.pyc
*.pyo
*.pyd
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

The jit proxy is a solana program that aims to make it easier to provide jit liquidity on drift v2. The jit proxy uses an MM's market and maximum position size to automatically create drift perp and spot orders.

For more information on how interact with the Jit Proxy program, see the Readme for the [typescript sdk](ts/sdk/Readme.md)
For more information on how interact with the Jit Proxy program, see the README for the [typescript sdk](ts/sdk/Readme.md) or the [python sdk](python/README.md)
45 changes: 45 additions & 0 deletions python/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Python JIT Proxy SDK

## QuickStart Guide

1. Add ```.env``` file in ```/sdk/examples``` with your ```RPC_URL``` and ```PRIVATE_KEY``` as a byte array
2. Customize ```JitParams``` in either ```shotgun.py``` or ```sniper.py```
3. Run ```python sniper.py``` or ```python shotgun.py``` while in the ```examples``` directory

## Shotgun vs Sniper

The ```JitterShotgun``` will immediately spray transactions when it detects an eligible auction.
It'll try up to 10 times to fulfill the order, retrying if the order doesn't yet cross or the oracle is stale.

The ```JitterSniper``` will wait until it detects an order that has a high chance of crossing the ```JitParams``` and retry up to 3 times on errors. It won't send transactions immediately like the ```JitterShotgun``` does, but will try to determine that the order will cross the bid/ask during the auction before sending a transaction.


## How to set up a ```JitProxyClient``` and ```Jitter```

This example uses the ```JitterShotgun```, but the same logic follows for the ```JitterSniper```.

However, the ```JitterSniper``` also requires a ```SlotSubscriber``` in its constructor.

```
jit_proxy_client = JitProxyClient(
drift_client,
# JIT program ID
Pubkey.from_string("J1TnP8zvVxbtF5KFp5xRmWuvG9McnhzmBd9XGfCyuxFP"),
)
jitter_shotgun = JitterShotgun(drift_client, auction_subscriber, jit_proxy_client)
jit_params = JitParams(
bid=-1_000_000,
ask=1_010_000,
min_position=0,
max_position=2,
price_type=PriceType.Oracle(),
sub_account_id=None,
)
# Add your parameters to the Jitter before subscribing
jitter_shotgun.update_perp_params(0, jit_params)
await jitter_shotgun.subscribe()
```
22 changes: 22 additions & 0 deletions python/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[tool.poetry]
name = "jit_proxy"
version = "0.1.0"
description = "python sdk for drift protocol v2 jit proxy program"
authors = ["Frank <[email protected]>"]
readme = "README.md"
packages = [
{ include = "jit_proxy", from = "sdk" }
]

[tool.poetry.dependencies]
python = "^3.10"
python-dotenv = "^1.0.0"
solana = "^0.30.1"
anchorpy = "^0.17.1"
driftpy = "0.7.6"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

#
76 changes: 76 additions & 0 deletions python/sdk/examples/shotgun.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import asyncio
import os

from dotenv import load_dotenv

from solana.rpc.async_api import AsyncClient
from solders.pubkey import Pubkey

from anchorpy import Wallet

from driftpy.drift_client import DriftClient
from driftpy.account_subscription_config import AccountSubscriptionConfig
from driftpy.auction_subscriber.auction_subscriber import AuctionSubscriber
from driftpy.auction_subscriber.types import AuctionSubscriberConfig
from driftpy.keypair import load_keypair

from jit_proxy.jitter.jitter_shotgun import JitterShotgun
from jit_proxy.jitter.base_jitter import JitParams
from jit_proxy.jit_proxy_client import JitProxyClient, PriceType


async def main():
load_dotenv()
secret = os.getenv("PRIVATE_KEY")
url = os.getenv("RPC_URL")

kp = load_keypair(secret)
wallet = Wallet(kp)

connection = AsyncClient(url)
drift_client = DriftClient(
connection,
wallet,
"mainnet",
account_subscription=AccountSubscriptionConfig("websocket"),
)

auction_subscriber_config = AuctionSubscriberConfig(drift_client)
auction_subscriber = AuctionSubscriber(auction_subscriber_config)

jit_proxy_client = JitProxyClient(
drift_client,
# JIT program ID
Pubkey.from_string("J1TnP8zvVxbtF5KFp5xRmWuvG9McnhzmBd9XGfCyuxFP"),
)

jitter_shotgun = JitterShotgun(drift_client, auction_subscriber, jit_proxy_client)

jit_params = JitParams(
bid=-1_000_000,
ask=1_010_000,
min_position=0,
max_position=2,
price_type=PriceType.Oracle(),
sub_account_id=None,
)

jitter_shotgun.update_spot_params(0, jit_params)
jitter_shotgun.update_perp_params(0, jit_params)

print(f"Added JitParams: {jit_params} to JitterShotgun")

await jitter_shotgun.subscribe()

print("Subscribed to JitterShotgun successfully!")

# quick & dirty way to keep event loop open
try:
while True:
await asyncio.sleep(3600)
except asyncio.CancelledError:
pass


if __name__ == "__main__":
asyncio.run(main())
81 changes: 81 additions & 0 deletions python/sdk/examples/sniper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import asyncio
import os

from dotenv import load_dotenv

from solana.rpc.async_api import AsyncClient
from solders.pubkey import Pubkey

from anchorpy import Wallet

from driftpy.drift_client import DriftClient
from driftpy.account_subscription_config import AccountSubscriptionConfig
from driftpy.auction_subscriber.auction_subscriber import AuctionSubscriber
from driftpy.auction_subscriber.types import AuctionSubscriberConfig
from driftpy.slot.slot_subscriber import SlotSubscriber
from driftpy.keypair import load_keypair

from jit_proxy.jitter.jitter_sniper import JitterSniper
from jit_proxy.jitter.base_jitter import JitParams
from jit_proxy.jit_proxy_client import JitProxyClient, PriceType


async def main():
load_dotenv()
secret = os.getenv("PRIVATE_KEY")
url = os.getenv("RPC_URL")

kp = load_keypair(secret)
wallet = Wallet(kp)

connection = AsyncClient(url)
drift_client = DriftClient(
connection,
wallet,
"mainnet",
account_subscription=AccountSubscriptionConfig("websocket"),
)

auction_subscriber_config = AuctionSubscriberConfig(drift_client)
auction_subscriber = AuctionSubscriber(auction_subscriber_config)

slot_subscriber = SlotSubscriber(drift_client)

jit_proxy_client = JitProxyClient(
drift_client,
# JIT program ID
Pubkey.from_string("J1TnP8zvVxbtF5KFp5xRmWuvG9McnhzmBd9XGfCyuxFP"),
)

jitter_sniper = JitterSniper(
drift_client, slot_subscriber, auction_subscriber, jit_proxy_client
)

jit_params = JitParams(
bid=-1_000_000,
ask=1_010_000,
min_position=0,
max_position=2,
price_type=PriceType.Oracle(),
sub_account_id=None,
)

jitter_sniper.update_spot_params(1, jit_params)
jitter_sniper.update_perp_params(1, jit_params)

print(f"Added JitParams: {jit_params} to JitterSniper")

await jitter_sniper.subscribe()

print("Subscribed to JitterSniper successfully!")

# quick & dirty way to keep event loop open
try:
while True:
await asyncio.sleep(3600)
except asyncio.CancelledError:
pass


if __name__ == "__main__":
asyncio.run(main())
1 change: 1 addition & 0 deletions python/sdk/jit_proxy/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = "0.1.0"
Loading

0 comments on commit f32afc1

Please sign in to comment.