Files
Polymarket/main.py
2026-04-01 17:37:19 +00:00

808 lines
29 KiB
Python

import asyncio
import json
from dataclasses import dataclass
import logging
import math
import os
import time
from datetime import datetime, timezone
from typing import AsyncContextManager
import traceback
import numpy as np
import pandas as pd
import requests
import talib
import valkey
from dotenv import load_dotenv
from py_clob_client.clob_types import (
OrderArgs,
OrderType,
PartialCreateOrderOptions,
PostOrdersArgs,
BalanceAllowanceParams
)
from py_clob_client.order_builder.constants import BUY, SELL
from sqlalchemy import text
from sqlalchemy.ext.asyncio import create_async_engine
from functools import wraps
import modules.api as api
### Custom Order Args ###
@dataclass
class Custom_OrderArgs(OrderArgs):
max_price: float = 0.00
### Database ###
CLIENT = None
CON: AsyncContextManager | None = None
VAL_KEY = None
### Logging ###
load_dotenv()
LOG_FILEPATH: str = os.getenv("LOGS_PATH") + '/Polymarket_5min_Algo.log'
### ALGO CONFIG / CONSTANTS ###
SLOPE_YES_THRESH = 0.01 # In Percent % Chg (e.g. 0.02 == 0.02%)
ENDTIME_BUFFER_SEC = 30 # Stop trading, cancel all open orders and exit positions this many seconds before mkt settles.
TGT_PX_INDEX_DIFF_THRESH = 0.1 # In Percent % Chg (e.g. 0.02 == 0.02%)
DEFAULT_ORDER_SIZE = 10 # In USDe
MIN_ORDER_SIZE = 5 # In USDe
TGT_PROFIT_CENTS = 0.02
CHASE_TO_BUY_CENTS = 0.05
MAX_ALLOWED_POLY_PX = 0.90
### GLOBALS ###
ORDER_LOCK = 0
SLUG_END_TIME = 0
FREE_CASH: float = 0
POLY_BINANCE = {}
POLY_REF = {}
POLY_CLOB = {}
POLY_CLOB_DOWN = {}
USER_TRADES = {}
USER_ORDERS = {}
SLOPE_HIST = []
LOCAL_ACTIVE_ORDERS = []
LOCAL_TOKEN_BALANCES = {}
# LOCAL_ACTIVE_POSITIONS = []
ACTIVE_BALANCES_EXIST = False ### REMOVE
### Decorators ###
def async_timeit(func):
@wraps(func)
async def wrapper(*args, **kwargs):
start_time = time.perf_counter()
try:
return await func(*args, **kwargs)
finally:
end_time = time.perf_counter()
total_time = (end_time - start_time)*1000
print(f"Function '{func.__name__}' executed in {total_time:.4f} ms")
return wrapper
### Database Funcs ###
# @async_timeit
async def create_executions_orders_table(
CON: AsyncContextManager,
engine: str = 'mysql', # mysql | duckdb
) -> None:
if CON is None:
logging.info("NO DB CONNECTION, SKIPPING Create Statements")
else:
if engine == 'mysql':
logging.info('Creating Table if Does Not Exist: executions_orders')
await CON.execute(text("""
CREATE TABLE IF NOT EXISTS executions_orders (
timestamp_sent BIGINT,
token_id VARCHAR(100),
limit_price DOUBLE,
size DOUBLE,
side VARCHAR(8),
order_type VARCHAR(8),
post_only BOOL,
resp_errorMsg VARCHAR(100),
resp_orderID VARCHAR(100),
resp_takingAmount DOUBLE,
resp_makingAmount DOUBLE,
resp_status VARCHAR(20),
resp_success BOOL
);
"""))
await CON.commit()
else:
raise ValueError('Only MySQL engine is implemented')
# @async_timeit
async def insert_executions_orders_table(
timestamp_sent: int,
token_id: str,
limit_price: float,
size: float,
side: str,
order_type: str,
post_only: bool,
resp_errorMsg: str,
resp_orderID: str,
resp_takingAmount: float,
resp_makingAmount: float,
resp_status: str,
resp_success: bool,
CON: AsyncContextManager,
engine: str = 'mysql', # mysql | duckdb
) -> None:
params={
'timestamp_sent': timestamp_sent,
'token_id': token_id,
'limit_price': limit_price,
'size': size,
'side': side,
'order_type': order_type,
'post_only': post_only,
'resp_errorMsg': resp_errorMsg,
'resp_orderID': resp_orderID,
'resp_takingAmount': resp_takingAmount,
'resp_makingAmount': resp_makingAmount,
'resp_status': resp_status,
'resp_success': resp_success,
}
if CON is None:
logging.info("NO DB CONNECTION, SKIPPING Insert Statements")
else:
if engine == 'mysql':
await CON.execute(text("""
INSERT INTO executions_orders
(
timestamp_sent,
token_id,
limit_price,
size,
side,
order_type,
post_only,
resp_errorMsg,
resp_orderID,
resp_takingAmount,
resp_makingAmount,
resp_status,
resp_success
)
VALUES
(
:timestamp_sent,
:token_id,
:limit_price,
:size,
:side,
:order_type,
:post_only,
:resp_errorMsg,
:resp_orderID,
:resp_takingAmount,
:resp_makingAmount,
:resp_status,
:resp_success
)
"""),
parameters=params
)
await CON.commit()
else:
raise ValueError('Only MySQL engine is implemented')
### Functions ###
# @async_timeit
async def slope_decision() -> list[bool, str]:
hist_trades = np.array(POLY_BINANCE.get('hist_trades', []))
if ( np.max(hist_trades[:, 0] )*1000 ) - ( np.min(hist_trades[:, 0])*1000 ) < 5:
logging.info('Max - Min Trade In History is < 5 Seconds Apart')
return False, ''
last_px = POLY_BINANCE['value']
last_px_ts = POLY_BINANCE['timestamp_value']
ts_min_1_sec = last_px_ts - 1000
price_min_1_sec_index = (np.abs(hist_trades[:, 0] - ts_min_1_sec)).argmin()
price_min_1_sec = hist_trades[:, 1][price_min_1_sec_index]
ts_min_5_sec = last_px_ts - 5000
price_min_5_sec_index = (np.abs(hist_trades[:, 0] - ts_min_5_sec)).argmin()
price_min_5_sec = hist_trades[:, 1][price_min_5_sec_index]
slope = (last_px - price_min_1_sec) / price_min_1_sec
slope_5 = (last_px - price_min_5_sec) / price_min_5_sec
SLOPE_HIST.append(slope)
# print(f'Avg Binance: {np.mean(hist_trades[:, 1])}')
# print(f'Len Hist : {len(hist_trades[:, 1])}')
# print(f'First Hist : {pd.to_datetime(np.min(hist_trades[:, 0]), unit='ms')}')
# print(f'Latest Hist: {pd.to_datetime(np.max(hist_trades[:, 0]), unit='ms')}')
print(f'Slope Hist Avg: {np.mean(SLOPE_HIST):.4%}')
print(f'Slope Hist Max: {np.max(SLOPE_HIST):.4%}')
print(f'Slope Hist Std: {np.std(SLOPE_HIST):.4%}')
slope_1_buy = abs(slope) >= ( SLOPE_YES_THRESH / 100)
slope_5_buy = abs(slope_5) >= ( SLOPE_YES_THRESH / 100)
print(f'SLOPE_1: {slope:.4%} == {slope_1_buy}; SLOPE_5: {slope_5:.4%} == {slope_5_buy};')
### DECISION ###
if slope_1_buy and slope_5_buy:
print(f'🤑🤑🤑🤑🤑🤑🤑🤑🤑🤑 Slope: {slope_5:.4%};')
side = 'UP' if slope > 0.00 else 'DOWN'
return True, side
else:
return False, ''
# @async_timeit
async def cancel_all_orders(CLIENT):
logging.info('Attempting to Cancel All Orders')
cxl_resp = CLIENT.cancel_all()
if bool(cxl_resp.get('not_canceled', True)):
logging.warning(f'*** Cancel Request FAILED, trying again and shutting down: {cxl_resp}')
cxl_resp = CLIENT.cancel_all()
raise Exception('*** Cancel Request FAILED')
logging.info(f'Cancel Successful: {cxl_resp}')
# @async_timeit
async def cancel_single_order_by_id(CLIENT, order_id):
global LOCAL_ACTIVE_ORDERS
logging.info(f'Attempting to Cancel Single Order: {order_id}')
cxl_resp = CLIENT.cancel(order_id=order_id)
for idx, o in enumerate(LOCAL_ACTIVE_ORDERS):
if o.get('orderID') == order_id:
if bool(cxl_resp.get('not_canceled', True)):
if cxl_resp.get('not_canceled', {}).get(order_id, None) == "matched orders can't be canceled":
logging.info(f'Cancel request failed b/c already matched: {cxl_resp}')
return False
elif cxl_resp.get('not_canceled', {}).get(order_id, None) == "order can't be found - already canceled or matched":
logging.info(f'Cancel request failed b/c already matched or cancelled: {cxl_resp}')
LOCAL_ACTIVE_ORDERS.pop(idx)
return False
else:
logging.warning(f'*** Cancel Request FAILED, shutting down: {cxl_resp}')
raise Exception('*** Cancel Request FAILED - SHUTDONW')
else:
LOCAL_ACTIVE_ORDERS.pop(idx)
logging.info(f'Cancel Successful: {cxl_resp}')
return False
# @async_timeit
async def flatten_open_positions(CLIENT, token_id_up, token_id_down):
up = await get_balance_by_token_id(CLIENT=CLIENT, token_id=token_id_up)
down = await get_balance_by_token_id(CLIENT=CLIENT, token_id=token_id_down)
### Submit orders to flatten outstanding balances ###
if up > MIN_ORDER_SIZE:
logging.info(f'Flattening Up Position: {up}')
await post_order(
CLIENT = CLIENT,
tick_size = POLY_CLOB['tick_size'],
neg_risk = POLY_CLOB['neg_risk'],
OrderArgs_list = [Custom_OrderArgs(
token_id=token_id_up,
price=float(POLY_CLOB['price'])-0.01,
size=up,
side=SELL,
)]
)
if down > MIN_ORDER_SIZE:
logging.info(f'Flattening Down Position: {down}')
await post_order(
CLIENT = CLIENT,
tick_size = POLY_CLOB['tick_size'],
neg_risk = POLY_CLOB['neg_risk'],
OrderArgs_list = [Custom_OrderArgs(
token_id=token_id_down,
price=float(POLY_CLOB_DOWN['price'])-0.01,
size=down,
side=SELL,
)]
)
# @async_timeit
async def get_balance_by_token_id(CLIENT, token_id):
collateral = CLIENT.get_balance_allowance(
BalanceAllowanceParams(
asset_type='CONDITIONAL',
token_id=token_id,
)
)
return int(collateral['balance']) / 1_000_000
# @async_timeit
async def get_usde_balance(CLIENT):
collateral = CLIENT.get_balance_allowance(
BalanceAllowanceParams(
asset_type='COLLATERAL'
)
)
return int(collateral['balance']) / 1_000_000
@async_timeit
async def check_for_open_positions(CLIENT, token_id_up, token_id_down):
global LOCAL_TOKEN_BALANCES
if token_id_up is None or token_id_down is None:
logging.critical('Token Id is None, Exiting')
raise ValueError('Token Id is None, Exiting')
# return False
up = await get_balance_by_token_id(CLIENT=CLIENT, token_id=token_id_up)
down = await get_balance_by_token_id(CLIENT=CLIENT, token_id=token_id_down)
LOCAL_TOKEN_BALANCES = {
token_id_up: up if up else 0,
token_id_down: down if down else 0,
}
logging.info(f'LOCAL_TOKEN_BALANCES: {LOCAL_TOKEN_BALANCES}')
if ( abs(up) > 0 ) or ( abs(down) > 0 ):
return True
else:
return False
@async_timeit
async def post_order(CLIENT, OrderArgs_list: list[Custom_OrderArgs], tick_size: float | str, neg_risk: bool):
global LOCAL_ACTIVE_ORDERS
global LOCAL_TOKEN_BALANCES
orders = []
for oa in OrderArgs_list:
orders.append(
PostOrdersArgs(
order=CLIENT.create_order(
order_args=oa,
options=PartialCreateOrderOptions(
tick_size=str(tick_size),
neg_risk=neg_risk
),
),
orderType=OrderType.GTC,
postOnly=False,
),
)
### POST
response = CLIENT.post_orders(orders)
for idx, d in enumerate(response):
if d['errorMsg'] == '':
d['token_id'] = OrderArgs_list[idx].token_id
d['price'] = OrderArgs_list[idx].price
d['size'] = OrderArgs_list[idx].size
d['side'] = str(OrderArgs_list[idx].side).upper()
if d['status'].upper() =='MATCHED':
### Order Immediately Matched, Can Put in Offsetting Order Depending on State ###
print('******** ORDER APPEND TO LOCAL - MATCHED ********* ')
LOCAL_ACTIVE_ORDERS.append(d)
if d['status'].upper() == 'CONFIRMED':
current_balance = float(LOCAL_TOKEN_BALANCES.get(d['token_id'], 0.00))
if d['side'] == 'BUY':
size = float(d['size'])
else:
size = float(d['size']) * -1
LOCAL_TOKEN_BALANCES[d['token_id']] = current_balance + size
print('******** TRADE FILLED, BAL UPDATED ********* ')
else:
print('******** ORDER APPEND TO LOCAL - LIVE ********* ')
LOCAL_ACTIVE_ORDERS.append(d)
else:
raise ValueError(f'Order entry failed: {d}')
logging.info(f'Order Posted Resp: {response}')
print(f'Order Posted Resp: {response}')
### Routes ###
async def no_orders_no_positions_route():
global ORDER_LOCK
### Check for Price Bands ###
up_px = float(POLY_CLOB.get('price', 0))
down_px = float(POLY_CLOB_DOWN.get('price', 0))
if (up_px > MAX_ALLOWED_POLY_PX) or (down_px > MAX_ALLOWED_POLY_PX):
logging.info(f'Outside max allowed px: {MAX_ALLOWED_POLY_PX}')
return False
### Check for Index vs. Target Px ###
tgt_px = float(POLY_CLOB.get('target_price', 0))
ref_px = float(POLY_REF.get('value'))
tgt_px_diff_to_index = ( abs( tgt_px - ref_px ) / tgt_px)
if tgt_px_diff_to_index > (TGT_PX_INDEX_DIFF_THRESH / 100):
logging.info(f'Tgt Diff to Index Outside Limit ({TGT_PX_INDEX_DIFF_THRESH}%); Diff {tgt_px_diff_to_index:.4%}; Index: {ref_px:.2f}; Tgt: {tgt_px:.2f}')
return False
### Check Slope ###
slope_bool, slope_side = await slope_decision()
if not slope_bool:
logging.info('Failed Slope Check')
return False
token_id = POLY_CLOB.get('token_id_up', None) if slope_side=='UP' else POLY_CLOB.get('token_id_down', None)
### Order Entry ###
px = float(POLY_CLOB['price'])+0.01
order = Custom_OrderArgs(
token_id=token_id,
price=px,
size=DEFAULT_ORDER_SIZE,
side=BUY,
max_price = px + CHASE_TO_BUY_CENTS
)
### ADD CHECK FOR MKT MOVED AWAY FROM OPPORTNITY ###
if ORDER_LOCK:
logging.info(f'BUY ORDER BLOCKED BY LOCK: {order}')
else:
logging.info(f'Attempting BUY Order {order}')
await post_order(
CLIENT = CLIENT,
tick_size = POLY_CLOB['tick_size'],
neg_risk = POLY_CLOB['neg_risk'],
OrderArgs_list = [order]
)
# ORDER_LOCK = ORDER_LOCK + 1
async def active_orders_no_positions_route():
if len(LOCAL_ACTIVE_ORDERS) > 2:
logging.critical('More than two active orders, shutting down')
await kill_algo()
b_c = 0
s_c = 0
for o in LOCAL_ACTIVE_ORDERS:
if o['side'] == 'BUY':
b_c = b_c + 1
elif o['side'] == 'SELL':
s_c = s_c + 1
if (b_c > 1) or (s_c > 1):
logging.critical(f'More than one active buy or more than one active sell: b_c {b_c}; s_c{s_c}')
await kill_algo()
for o in LOCAL_ACTIVE_ORDERS:
logging.info(f'Working on order ({o['side']}): {o['orderID']}')
if o.get('status').upper() == 'MATCHED':
logging.info('Order is matched, awaiting confirm or kickback')
elif o.get('status').upper() == 'FAILED':
raise ValueError(f'Trade FAILED after matching: {o}')
else:
orig_px = float(o['price'])
orig_size = float(o['size'])
if o['side'] == 'BUY':
if POLY_CLOB['token_id_up'] == o['token_id']:
clob_px = float(POLY_CLOB['price'])
else:
clob_px = float(POLY_CLOB_DOWN['price'])
if clob_px >= orig_px:
logging.info(f"Market px: ({clob_px} is above buy order px: {orig_px:.2f})")
if o.get('max_price', 0) > clob_px:
logging.info(f"Market px: ({clob_px} has moved too far away from original target, cancelling and resetting algo: {o.get('max_price', 0) :.2f})")
order_matched = await cancel_single_order_by_id(CLIENT=CLIENT, order_id=o['orderID'])
if order_matched:
o['status'] = 'MATCHED'
else:
px = orig_px+0.01
await post_order(
CLIENT = CLIENT,
tick_size = POLY_CLOB['tick_size'],
neg_risk = POLY_CLOB['neg_risk'],
OrderArgs_list = [Custom_OrderArgs(
token_id=o['token_id'],
price=px,
size=orig_size,
side=BUY,
max_price=o['max_price']
)]
)
else:
await cancel_single_order_by_id(CLIENT=CLIENT, order_id=o['orderID'])
elif o['side'] == 'SELL':
if POLY_CLOB['token_id_up'] == o['token_id']:
clob_px = float(POLY_CLOB['price'])
else:
clob_px = float(POLY_CLOB_DOWN['price'])
if clob_px <= orig_px:
logging.info(f"Market px: ({clob_px} is below sell order px: {orig_px:.2f})")
order_filled = await cancel_single_order_by_id(CLIENT=CLIENT, order_id=o['orderID'])
if not order_filled:
await post_order(
CLIENT = CLIENT,
tick_size = POLY_CLOB['tick_size'],
neg_risk = POLY_CLOB['neg_risk'],
OrderArgs_list = [Custom_OrderArgs(
token_id=o['token_id'],
price=orig_px-0.01,
size=orig_size,
side=SELL,
max_price = 0.00
)]
)
async def no_orders_active_positions_route():
'''
Succesful Buy, now neeed to take profit and exit
'''
global LOCAL_TOKEN_BALANCES
OrderArgs_list = []
logging.warning(f'LOCAL_TOKEN_BALANCES: {LOCAL_TOKEN_BALANCES}')
for k, v in LOCAL_TOKEN_BALANCES.items():
size = await get_balance_by_token_id(CLIENT=CLIENT, token_id=k)
if size >= MIN_ORDER_SIZE:
if POLY_CLOB['token_id_up'] == k:
clob_px = float(POLY_CLOB['price'])
else:
clob_px = float(POLY_CLOB_DOWN['price'])
OrderArgs_list.append(
Custom_OrderArgs(
token_id=k,
price=clob_px + TGT_PROFIT_CENTS,
size=size,
side='SELL',
)
)
else:
LOCAL_TOKEN_BALANCES[k] = 0.00
logging.info(f'Wants to flatten small amount, skipping: {v}')
if OrderArgs_list:
logging.info(f'Posting orders to close: {OrderArgs_list}')
await post_order(
CLIENT = CLIENT,
tick_size = POLY_CLOB['tick_size'],
neg_risk = POLY_CLOB['neg_risk'],
OrderArgs_list = OrderArgs_list
)
async def active_orders_active_positions_route():
pass
async def kill_algo():
logging.info('Killing algo...')
await cancel_all_orders(CLIENT=CLIENT)
await flatten_open_positions(
CLIENT=CLIENT,
token_id_up = POLY_CLOB.get('token_id_up', None),
token_id_down = POLY_CLOB.get('token_id_down', None),
)
logging.info('...algo killed')
raise Exception('Algo Killed')
async def run_algo():
global POLY_BINANCE
global POLY_REF
global POLY_CLOB
global POLY_CLOB_DOWN
global USER_TRADES
global USER_ORDERS
global SLOPE_HIST
global ACTIVE_BALANCES_EXIST
global LOCAL_ACTIVE_ORDERS
global LOCAL_TOKEN_BALANCES
# global LOCAL_ACTIVE_POSITIONS
print(f'token_id_up: {POLY_CLOB.get('token_id_up', None)}')
print(f'token_id_down: {POLY_CLOB.get('token_id_down', None)}')
POLY_CLOB = json.loads(VAL_KEY.get('poly_5min_btcusd'))
ACTIVE_BALANCES_EXIST = await check_for_open_positions(
CLIENT=CLIENT,
token_id_up=POLY_CLOB.get('token_id_up', None),
token_id_down=POLY_CLOB.get('token_id_down', None),
)
try:
while True:
loop_start = time.time()
print('__________Start___________')
POLY_BINANCE = json.loads(VAL_KEY.get('poly_binance_btcusd'))
POLY_REF = json.loads(VAL_KEY.get('poly_rtds_cl_btcusd'))
POLY_CLOB = json.loads(VAL_KEY.get('poly_5min_btcusd'))
POLY_CLOB_DOWN = json.loads(VAL_KEY.get('poly_5min_btcusd_down'))
USER_TRADES = json.loads(VAL_KEY.get('poly_user_trades'))
USER_ORDERS = VAL_KEY.get('poly_user_orders')
USER_ORDERS = json.loads(USER_ORDERS) if USER_ORDERS is not None else []
### CHANGE METHOD FROM BUY-SELL TO BUY UP - BUY DOWN
### DO THIS TO AVOID DELAY WITH FILL CONFIRMS
### Manage Local vs User Stream Orders ###
print(f'LOCAL_ACTIVE_ORDERS: {LOCAL_ACTIVE_ORDERS}')
for idx, o in enumerate(LOCAL_ACTIVE_ORDERS):
user_order = next((item for item in USER_ORDERS if item["id"] == o['orderID']), None)
user_trade = next( ( item for item in USER_TRADES if ( o['orderID'] == item['taker_order_id'] ) or ( o["orderID"] == json.loads(item['maker_orders'])[0]['order_id'] ) ), None )
print(f'USER TRADE: {user_trade}')
if user_trade is not None:
trade_status = str(user_trade['status']).upper()
logging.info(f'Updated Trade Status: {o['status']} --> {trade_status}; {o['orderID']}')
if trade_status == 'CONFIRMED':
LOCAL_ACTIVE_ORDERS.pop(idx)
token_id = user_trade['asset_id']
current_balance = float(LOCAL_TOKEN_BALANCES.get(token_id, 0.00))
if user_trade['side'] == 'BUY':
size = float(user_trade['size'])
else:
size = float(user_trade['size']) * -1
LOCAL_TOKEN_BALANCES[token_id] = current_balance + size
# px = user_trade['price']
# LOCAL_ACTIVE_POSITIONS.append({
# 'token_id': token_id,
# 'order_id': o['orderID'],
# 'associate_trades': user_order['associate_trades'],
# 'size_matched': user_order['size_matched'],
# 'price': px,
# 'timestamp_value': user_order['timestamp'],
# })
logging.info('Order FILLED!')
elif trade_status == 'MATCHED':
logging.info(f'Order Matched...awaiting confirm: {trade_status}')
else:
logging.info(f'Trade status but not filled: trade= {user_trade}; order={o}')
elif user_order is not None:
order_status = str(user_order['status']).upper()
logging.info(f'Updated Order Status: {o['status']} --> {order_status}; {o['orderID']}')
# LOCAL_ACTIVE_ORDERS[idx]['status'] = order_status
# if order_status == 'MATCHED':
# LOCAL_ACTIVE_ORDERS.pop(idx)
# token_id = user_order['asset_id']
# current_balance = float(LOCAL_TOKEN_BALANCES.get(token_id, 0.00))
# if user_order['side'] == 'BUY':
# size = float(user_order['size_matched'])
# else:
# size = float(user_order['size_matched']) * -1
# LOCAL_TOKEN_BALANCES[token_id] = current_balance + size
# # px = user_order['price']
# # LOCAL_ACTIVE_POSITIONS.append({
# # 'token_id': token_id,
# # 'order_id': o['orderID'],
# # 'associate_trades': user_order['associate_trades'],
# # 'size_matched': user_order['size_matched'],
# # 'price': px,
# # 'timestamp_value': user_order['timestamp'],
# # })
# logging.info('Order FILLED!')
if order_status == 'CANCELED':
LOCAL_ACTIVE_ORDERS.pop(idx)
logging.info('Order Canceled')
else:
logging.info('Order Live or Trade Awaiting Confirm')
### UPDATES CAN COME THRU EITHER ORDER OR TRADE CHANNELS - NEED TO UPDATE TO HANDLE TRADE CHANNLE
token_id_up = POLY_CLOB.get('token_id_up', 0)
token_id_down = POLY_CLOB.get('token_id_down', 0)
if (token_id_up is None) or (token_id_down is None):
print('Missing Token Ids for Market, sleeping 1 sec and retrying...')
time.sleep(1)
ACTIVE_BALANCES_EXIST = {}
continue
else:
if (LOCAL_TOKEN_BALANCES.get(token_id_up) is None):
LOCAL_TOKEN_BALANCES[token_id_up] = 0.00
if (LOCAL_TOKEN_BALANCES.get(token_id_down) is None):
LOCAL_TOKEN_BALANCES[token_id_down] = 0.00
ACTIVE_BALANCES_EXIST = (LOCAL_TOKEN_BALANCES.get(token_id_up) > 0) or (LOCAL_TOKEN_BALANCES.get(token_id_down) > 0)
### Check for Endtime Buffer ###
if ENDTIME_BUFFER_SEC > POLY_CLOB.get('sec_remaining', 0):
if LOCAL_ACTIVE_ORDERS:
print('buffer zone - orders cancel')
await cancel_all_orders(CLIENT=CLIENT)
if ACTIVE_BALANCES_EXIST:
print('buffer zone - flatten positions')
await flatten_open_positions(
CLIENT=CLIENT,
token_id_up = POLY_CLOB.get('token_id_up', None),
token_id_down = POLY_CLOB.get('token_id_down', None),
)
print('buffer zone, sleeping until next session')
time.sleep(1)
continue
### Execution Route ###
if not(LOCAL_ACTIVE_ORDERS) and not(ACTIVE_BALANCES_EXIST): # No Orders, No Positions
print('ROUTE: no_orders_no_positions_route')
await no_orders_no_positions_route()
### Open Orders Route ###
elif LOCAL_ACTIVE_ORDERS and not(ACTIVE_BALANCES_EXIST): # Orders, No Positions
print('ROUTE: active_orders_no_positions_route')
await active_orders_no_positions_route()
### Open Positions Route ###
elif not(LOCAL_ACTIVE_ORDERS) and ACTIVE_BALANCES_EXIST: # No Orders, Positions
print('ROUTE: no_orders_no_positions_route')
await no_orders_active_positions_route()
### Open Orders and Open Positions Route ###
else:
print('ROUTE: active_orders_active_positions_route')
await active_orders_no_positions_route() # Orders and Positions
print(f'__________________________ (Algo Engine ms: {(time.time() - loop_start)*1000})')
time.sleep(1)
except KeyboardInterrupt:
print('...algo stopped')
await cancel_all_orders(CLIENT=CLIENT)
except Exception as e:
logging.critical(f'*** ALGO ENGINE CRASHED: {e}')
logging.error(traceback.format_exc())
await cancel_all_orders(CLIENT=CLIENT)
async def main():
global CLIENT
global VAL_KEY
global CON
CLIENT = api.create_client()
VAL_KEY = valkey.Valkey(host='localhost', port=6379, db=0, decode_responses=True)
engine = create_async_engine('mysql+asyncmy://root:pwd@localhost/polymarket')
async with engine.connect() as CON:
await create_executions_orders_table(CON=CON)
await run_algo()
if __name__ == '__main__':
START_TIME = round(datetime.now().timestamp()*1000)
logging.info(f'Log FilePath: {LOG_FILEPATH}')
logging.basicConfig(
force=True,
filename=LOG_FILEPATH,
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
filemode='w'
)
logging.info(f"STARTED: {START_TIME}")
asyncio.run(main())