Skip to content

Commit

Permalink
Dexter v1.2 Release
Browse files Browse the repository at this point in the history
  • Loading branch information
FLOCK4H committed Jan 27, 2025
1 parent a626e23 commit 032fe95
Show file tree
Hide file tree
Showing 12 changed files with 576 additions and 103 deletions.
29 changes: 14 additions & 15 deletions DexAI/trust_factor.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
try:
from settings import *
from .colors import *
except ImportError:
from colors import *
Expand All @@ -18,7 +19,7 @@
os.makedirs(LOG_DIR, exist_ok=True)

logging.basicConfig(
format=f'{cc.LIGHT_CYAN}[DexAI] %(levelname)s - %(message)s{cc.RESET}',
format=f'{cc.LIGHT_CYAN}[DexAI] %(levelname)s | %(message)s{cc.RESET}',
level=logging.INFO,
handlers=[
logging.FileHandler(os.path.join(LOG_DIR, 'dexter.log')),
Expand Down Expand Up @@ -111,7 +112,7 @@ def _format_small_number(self, num):
def _is_successful_mint(self, token_info):
"""
Determine if a mint is successful based on criteria:
0. Sniping price = Price closest to 1 second after the first transaction.
0. Sniping price = Price closest to X second after the first transaction.
1. Highest price at least 150% of sniping price.
2. The highest price was reached after at least 25 swaps.
"""
Expand All @@ -128,15 +129,15 @@ def _is_successful_mint(self, token_info):

# 0: Calculate the sniping price
first_timestamp = float(timestamps[0])
target_timestamp = first_timestamp + 1 # 1 second after the first transaction
target_timestamp = first_timestamp + SNIPING_PRICE_TIME # 1 second after the first transaction

# Find the closest timestamp to the target
closest_index = min(range(len(timestamps)), key=lambda i: abs(float(timestamps[i]) - target_timestamp))
sniping_price = prices[closest_index]

# 1: Check if highest price is at least 150% of the sniping price
peak_price = Decimal(str(token_info["high_price"]))
if peak_price < sniping_price * Decimal("2.5"):
if peak_price < sniping_price * Decimal(f"{SNIPE_PRICE_TO_PEAK_PRICE_RATIO}"):
return False, 0

# 2: Ensure the highest price was reached after at least 100 swaps
Expand All @@ -145,7 +146,7 @@ def _is_successful_mint(self, token_info):
except StopIteration:
return False, 0

if peak_index < 100:
if peak_index < HIGHEST_PRICE_MIN_SWAPS:
return False, 0

# Calculate the profit ratio based on sniping price
Expand Down Expand Up @@ -236,8 +237,6 @@ def analyze_top_creators_sync(self, data):
"total_swaps": total_swaps,
})

logging.info("Incremental analysis complete.")

def process_results_sync(self, show_result=True):
return self.process_results(show_result=show_result)

Expand All @@ -251,8 +250,6 @@ async def analyze_market(self):
if not data_chunk:
break

logging.info(f"Processing chunk starting at offset {offset}...")

self.analyze_top_creators_sync(data_chunk)

del data_chunk
Expand All @@ -263,10 +260,9 @@ async def analyze_market(self):
logging.info("All data processed. Market analysis completed.")

def process_results(self, show_result=True):
logging.info(f"\n{cc.YELLOW}Top Creators (with trust-factor, success ratio, and medians):{cc.RESET}")
leaderboard = {}

logging.info(f"Top creators: {len(self.top_creators)}")
logging.info(f"Creators in the database: {len(self.top_creators)}")

for creator, data in self.top_creators.items():

Expand All @@ -277,7 +273,10 @@ def process_results(self, show_result=True):
total_swaps = data['total_swaps']

# Here is the main condition to set, this makes the algorithm choose best creators, tweak it to your liking
if (mint_count >= 2 and median_peak_mc >= 7500 and total_swaps >= 5) or (mint_count >= 1 and median_peak_mc >= 7000 and total_swaps >= 5): #1
if (
(mint_count >= 2 and median_peak_mc >= MEDIAN_PEAK_MC_ABOVE_2_MINTS and total_swaps >= TOTAL_SWAPS_ABOVE_2_MINTS) or # Owner has more than 2 mints, median peak market cap is at least 7500$, and total swaps are at least 5
(mint_count >= 1 and median_peak_mc >= MEDIAN_PEAK_MC_1_MINT and total_swaps >= TOTAL_SWAPS_1_MINT) # Owner has 1 mint, median peak market cap is at least 7000$, and total swaps are at least 5
):
success_count = 0
unsuccess_count = 0
success_ratios = []
Expand All @@ -294,8 +293,8 @@ def process_results(self, show_result=True):
total_mints = success_count + unsuccess_count
trust_factor = success_count / total_mints

#2 If trust_factor < 0.75, consider creator unsuccessful => exclude
if total_mints == 0 or trust_factor < 0.65:
#2 If trust_factor < X, consider creator unsuccessful => exclude
if total_mints == 0 or trust_factor < TRUST_FACTOR_RATIO:
continue

avg_success_ratio = (sum(success_ratios) / success_count) if success_count > 0 else 0.0
Expand All @@ -311,7 +310,7 @@ def process_results(self, show_result=True):
if (unsuccess_count > 0):
too_close = any(delay < 900 for delay in data['creation_delays'])
if too_close:
logging.info(f"{cc.RED}Creator {creator} identified as a faker. Excluding from leaderboard.{cc.RESET}")
logging.info(f"{cc.YELLOW}Creator {creator} should not be trusted. Excluding from leaderboard.{cc.RESET}")
continue

leaderboard[creator] = {
Expand Down
3 changes: 0 additions & 3 deletions DexLab/.config

This file was deleted.

20 changes: 11 additions & 9 deletions DexLab/common_.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@

try:
from .config_reader import get_api_key, get_private_key, get_wallet, read_config
except ImportError:
from config_reader import get_api_key, get_private_key, get_wallet, read_config
except ImportError:
from .config_reader import get_api_key, get_private_key, get_wallet, read_config
import os

config = read_config(".config")
config = read_config("config/.config")
if not config:
CONFIG_PATH = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "config/.config")
config = read_config(CONFIG_PATH)
print(CONFIG_PATH)
API_KEY = get_api_key(config)
PRIV_KEY = get_private_key(config)
WALLET = get_wallet(config)
gWS_URL = f"" # e.g. helius
WS_URL = f"" # e.g. helius
PUMP_FUN = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"
METAPLEX = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
RPC_URL = f"" # e.g. helius
SPL_TOKEN_PROGRAM_ID = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
SOL_ADDRESS = "So11111111111111111111111111111111111111112"
STAKED_API = "" # e.g. helius
SWAP_URL = "" # e.g. quicknode
STAKED_API = f"https://staked.helius-rpc.com?api-key={API_KEY}" # e.g. helius
WS_URL = f"wss://mainnet.helius-rpc.com/?api-key={API_KEY}" # e.g. helius
RPC_URL = f"https://mainnet.helius-rpc.com/?api-key={API_KEY}" # e.g. helius
7 changes: 3 additions & 4 deletions DexLab/config_reader.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import base64

def read_config(file_path=".config"):
def read_config(file_path="config\\.config"):
config_data = {}
try:
with open(file_path, "r") as config_file:
Expand All @@ -12,7 +10,8 @@ def read_config(file_path=".config"):
key, value = line.split("=", 1)
config_data[key.strip()] = value.strip()
except Exception as e:
raise RuntimeError(f"Failed to read config file: {e}")
print(f"Failed to read config file: {e}")
return None
return config_data

def get_wallet(config_data):
Expand Down
17 changes: 10 additions & 7 deletions DexLab/market.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
import asyncpg
from decimal import Decimal
try:
from .common_ import *
from .colors import *
except ImportError:
from common_ import *
from colors import *
except ImportError:
from .common_ import *
from .colors import *
import logging, time
import asyncio, json, requests, subprocess
import os
import platform, shlex, traceback
from collections import defaultdict

# Change this to your pg_dump path
PG_DUMP_PATH = r"C:\Program Files\PostgreSQL\17\bin\pg_dump.exe"

def get_pg_dump_path():
"""Return the correct pg_dump path based on the platform."""
if platform.system() == "Windows":
return r"C:\Program Files\PostgreSQL\17\bin\pg_dump.exe"
return PG_DUMP_PATH
else:
return "pg_dump"

logging.basicConfig(
format=f'{cc.GREEN}[DexLab] %(levelname)s - %(message)s{cc.RESET}',
format=f'{cc.LIGHT_BLUE}[DexLab] %(levelname)s | %(message)s{cc.RESET}',
level=logging.INFO,
handlers=[
logging.StreamHandler()
Expand Down Expand Up @@ -55,7 +58,7 @@ def __init__(self, session, serializer, stop_event,

async def init_db(self):
self.db_pool = await asyncpg.create_pool(self.db_dsn, min_size=1, max_size=5000, timeout=60)
logging.info("Database connection pool initialized.")
logging.info(f"{cc.WHITE}{cc.BRIGHT}Database connection pool initialized. Dexter is starting...{cc.RESET}")
async with self.db_pool.acquire() as conn:
tables = await conn.fetch(
"SELECT tablename FROM pg_catalog.pg_tables WHERE schemaname='public';"
Expand Down Expand Up @@ -260,7 +263,7 @@ async def update_mint(self, program, mint, data):

if self.count_iter % 100 == 0:
self.count_iter = 0
logging.info(f"Slot delay: {round(time.time() - data['timestamp'], 2)}s")
logging.info(f"{cc.CYAN}Slot delay: {round(time.time() - data['timestamp'], 2)}s{cc.RESET}")

high_price = float(row['high_price'])
low_price = float(row['low_price'])
Expand Down
36 changes: 22 additions & 14 deletions DexLab/pump_swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,25 @@
from borsh_construct import CStruct, U64
from decimal import Decimal
import logging
import asyncio, json
import asyncio, json, sys
from solders.compute_budget import set_compute_unit_price
from aiohttp import ClientSession
import time, requests

try:
from colors import *
from DexLab.common_ import *
except ImportError:
from .colors import *
from .common_ import *
from .utils import usd_to_lamports, lamports_to_tokens, usd_to_microlamports
except ImportError:
from colors import *
from common_ import *
from utils import usd_to_lamports, lamports_to_tokens, usd_to_microlamports

logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logging.basicConfig(
format=f'{cc.LIGHT_CYAN}[Dexter] %(levelname)s - %(message)s{cc.RESET}',
level=logging.INFO,
handlers=[
logging.StreamHandler(sys.stdout)
]
)

BUY_INSTRUCTION_SCHEMA = CStruct(
"amount" / U64,
Expand All @@ -46,7 +50,7 @@ def get_solana_price_usd():
response = requests.get('https://api.coingecko.com/api/v3/simple/price?ids=solana&vs_currencies=usd')
data = response.json()
price = data['solana']['usd']
print(f"{cc.YELLOW}Solana price: {price}")
print(f"{cc.LIGHT_GREEN}Solana price: {price}")
return str(price)
except Exception:
logging.info(f"{cc.RED}Failed to get Solana price from Coingecko{cc.RESET}")
Expand Down Expand Up @@ -191,6 +195,7 @@ async def pump_buy(
token_amount: int = 0,
sim: bool = False,
priority_micro_lamports: int = 0,
slippage: float = 1.3 # MAX: 1.99
):

transaction = Transaction()
Expand All @@ -206,7 +211,6 @@ async def pump_buy(
priority_micro_lamports
)
)
logging.info(f"Added priority fee instructions with {priority_micro_lamports} micro-lamports per CU.")

# ---------------------------------------------------------------------
# 2) Check if associated token account exists. If not, create it.
Expand All @@ -226,7 +230,7 @@ async def pump_buy(
fee_recipient,
token_amount,
# slippage, 1.99x
int(sol_amount * 1.52)
int(sol_amount * slippage)
)
transaction.add(buy_ix)

Expand Down Expand Up @@ -257,8 +261,10 @@ async def pump_buy(

opts = TxOpts(skip_preflight=True, max_retries=0, skip_confirmation=True)
result = await self.async_client.send_raw_transaction(bytes(signed_txn), opts=opts)
logging.info(f"Transaction result: {result}")
return result
result_json = result.to_json()
transaction_id = json.loads(result_json).get('result')
logging.info(f"Transaction result: {transaction_id}")
return transaction_id
except Exception as e:
logging.error(f"Transaction failed: {e}")
raise
Expand Down Expand Up @@ -322,8 +328,10 @@ async def pump_sell(

opts = TxOpts(skip_preflight=True, max_retries=0, skip_confirmation=True)
result = await self.async_client.send_raw_transaction(bytes(signed_txn), opts=opts)
logging.info(f"Sell transaction result: {result}")
return result
result_json = result.to_json()
transaction_id = json.loads(result_json).get('result')
logging.info(f"Transaction result: {transaction_id}")
return transaction_id
except Exception as e:
logging.error(f"Transaction failed: {e}")
raise
Expand Down
Loading

0 comments on commit 032fe95

Please sign in to comment.