OKEx Contract Position Management System
Python strategy by FawkesPan.
PythonOther
The original spec has no written description. Read the source below.
Source Python
import time
import math
import threading
import decimal
from uuid import uuid4 as _uuid
def genUUID():
return str(_uuid())
class PositionManager:
def __init__(self, exchange, symbol, contract, getDepth, tickSize=0.01):
self.okex = exchange
self.symbol = symbol
self.contract = contract
self.tickSize = tickSize
self.isFMZ = False
if 'Asks' in getDepth().keys():
self.isFMZ = True
self.okexGetDepth = getDepth
self.orderLock = threading.Lock()
self.orderLocks = {}
self.orders = {}
self.cancels = []
self.completeds = []
self.init()
def init(self):
self.okex.IO("currency", self.symbol.upper())
self.okex.SetContractType(self.contract)
self.order_logs = {
'buySent': 0,
'buyCanceled': 0,
'sellSent': 0,
'sellCanceled': 0
}
def toNearest(self, num):
tickDec = decimal.Decimal(str(self.tickSize))
return float((decimal.Decimal(round(num / self.tickSize, 0)) * tickDec))
def getDepth(self):
depth = self.okexGetDepth()
if self.isFMZ:
depth['asks'] = [[x['Price'], x['Amount']] for x in depth['Asks']]
depth['bids'] = [[x['Price'], x['Amount']] for x in depth['Bids']]
return depth
def getOrderLogs(self):
return self.order_logs
def getBestPrices(self):
bestPrices = {}
depth = self.getDepth()
ask, bid = depth['asks'][0][0], depth['bids'][0][0]
spread = ask - bid
if spread <= self.tickSize*2:
bestPrices['ask'] = ask
bestPrices['bid'] = bid
elif spread <= self.tickSize*6:
bestPrices['ask'] = ask-self.tickSize
bestPrices['bid'] = bid+self.tickSize
elif spread >= self.tickSize*20:
bestPrices['ask'] = ask-self.tickSize*5
bestPrices['bid'] = bid+self.tickSize*5
else:
midPrice = self.toNearest((ask+bid)/2)
bestPrices['ask'] = midPrice+self.tickSize*2
bestPrices['bid'] = midPrice-self.tickSize*2
return {
'ask': self.toNearest(bestPrices['ask']),
'bid': self.toNearest(bestPrices['bid'])
}
def getPositions(self, noRetry=False, maxRetries=10):
if noRetry:
return self.okex.GetPosition()
attempts = 0
while True:
time.sleep(0.4)
positions = self.okex.GetPosition()
attempts+=1
if len(positions) != 0:
return positions
if attempts == maxRetries:
return False
def havePosition(self, type=None, noRetry=True, maxRetries=10):
positions = self.getPositions(noRetry=True)
if not positions:
positions = []
if type == None:
pass
elif type == 'buy':
key = 'buy_amount'
elif type == 'sell':
key = 'sell_amount'
for position in positions:
if type is not None:
if position['Info'][key] != 0:
return positions
else:
return positions
if noRetry:
return False
attempts = 0
while True:
time.sleep(0.4)
positions = self.getPositions(noRetry=True)
if not positions:
positions = []
attempts+=1
for position in positions:
if type is not None:
if position['Info'][key] != 0:
return positions
else:
return positions
if attempts == maxRetries:
return False
def orderDone(self, orderID):
orders = self.okex.GetOrders()
for order in orders:
if order['Info']['order_id'] == orderID:
return order['Info']['price'], order['Info']['amount']-order['Info']['deal_amount']
return True
def getOrder(self, uuid):
try:
self.orders[uuid]
except KeyError:
Log('No order exists %s' % uuid)
return 0
return self.orders[uuid]
def getOrderInfo(self, uuid):
return self.okex.GetOrder(self.getOrder(uuid))
def awaitOrder(self, uuid, timeout=None):
start = time.time()
try:
self.orderLocks[uuid]
except KeyError:
return False
while True:
if self.orderLocks[uuid].acquire(False):
del self.orderLocks[uuid]
return self.getOrder(uuid)
else:
if timeout is not None:
if time.time()-timeout>start:
Log('Waiting for the order to be completed has timed out..')
return
time.sleep(0.001)
def isDone(self, uuid):
if uuid in self.completeds:
return True
return False
def cancel(self, uuid):
self.cancels.append(uuid)
_cancel = self.okex.CancelOrder(self.getOrder(uuid))
self.orderLocks[uuid].acquire()
self.orderLocks[uuid].release()
if _cancel:
Log(uuid, str(self.getOrder(uuid)), 'canceled.')
return True
Log(uuid, str(self.getOrder(uuid)), 'Cancel Failed.', str(self.okex.GetRawJSON()))
return False
def trade(self, type, price, amount, matchPrice=False, noRetry=False, uuid=None):
if type == 'buy' or type == 'closesell':
_trader = self.okex.Buy
_side = 'buy'
__side = 'bid'
else:
_trader = self.okex.Sell
_side = 'sell'
__side = 'ask'
def trader(price, amount):
self.orderLock.acquire()
self.okex.SetDirection(type)
order = _trader(price, amount)
self.orders[uuid] = order
self.orderLock.release()
self.order_logs['%sSent' % _side]+=1
return order
def isBestPrice(currentPrice, depth):
ret = False
if _side == 'buy':
if currentPrice > depth['bids'][1][0]:
ret = True
else:
if currentPrice < depth['asks'][1][0]:
ret = True
return ret
if price is None:
price = self.getBestPrices()[__side]
if matchPrice:
price = -1
order = trader(price, amount)
if noRetry:
self.completeds.append(uuid)
self.orderLocks[uuid].release()
return
if order == False:
if 20016 in str(self.okex.GetRawJSON()):
order = trader(price, amount)
if order == False:
Log('%s Return value: %s', (uuid, str(self.okex.GetRawJSON())))
self.completeds.append(uuid)
self.orderLocks[uuid].release()
return
time.sleep(1)
while True:
if uuid in self.cancels:
Log('%s Stopped trying to place orders.' % uuid)
self.completeds.append(uuid)
self.orderLocks[uuid].release()
return
time.sleep(1)
depth = self.getDepth()
isDone = self.orderDone(order)
if isDone == True:
self.completeds.append(uuid)
self.orderLocks[uuid].release()
return
if isBestPrice(isDone[0], depth):
continue
if not self.okex.CancelOrder(order):
time.sleep(1)
if self.orderDone(order):
self.completeds.append(uuid)
self.orderLocks[uuid].release()
return
self.order_logs['%sCanceled' % _side]+=1
time.sleep(1)
if price != -1:
price = self.getBestPrices()[__side]
if uuid in self.cancels:
Log('%s Stopped trying to place orders.' % uuid)
self.completeds.append(uuid)
self.orderLocks[uuid].release()
return
order = trader(price, isDone[1])
def openLong(self, amount, price=None, matchPrice=False, noRetry=False):
uuid = genUUID()
Log('%s Buy long, whether to use counterparty price %s Block until successful %s' % (uuid, str(matchPrice), str(not noRetry)))
threading.Thread(target=self.trade, kwargs=dict({'type': 'buy',
'price': price,
'amount': amount,
'matchPrice': matchPrice,
'noRetry': noRetry,
'uuid': uuid}), daemon=True).start()
self.orderLocks[uuid] = threading.Lock()
self.orderLocks[uuid].acquire()
return uuid
def openShort(self, amount, price=None, matchPrice=False, noRetry=False):
uuid = genUUID()
Log('%s When selling short, whether to use the counterparty price? %s Block until successful %s' % (uuid, str(matchPrice), str(not noRetry)))
threading.Thread(target=self.trade, kwargs=dict({'type': 'sell',
'price': price,
'amount': amount,
'matchPrice': matchPrice,
'noRetry': noRetry,
'uuid': uuid}), daemon=True).start()
self.orderLocks[uuid] = threading.Lock()
self.orderLocks[uuid].acquire()
return uuid
def coverLong(self, amount, price=None, matchPrice=False, noRetry=False):
uuid = genUUID()
Log('%s When selling long position, whether to use the counterparty price? %s Block until successful %s' % (uuid, str(matchPrice), str(not noRetry)))
threading.Thread(target=self.trade, kwargs=dict({'type': 'closebuy',
'price': price,
'amount': amount,
'matchPrice': matchPrice,
'noRetry': noRetry,
'uuid': uuid}), daemon=True).start()
self.orderLocks[uuid] = threading.Lock()
self.orderLocks[uuid].acquire()
return uuid
def coverShort(self, amount, price=None, matchPrice=False, noRetry=False):
uuid = genUUID()
Log('%s Buy and close whether to use counterparty price %s Block until successful %s' % (uuid, str(matchPrice), str(not noRetry)))
threading.Thread(target=self.trade, kwargs=dict({'type': 'closesell',
'price': price,
'amount': amount,
'matchPrice': matchPrice,
'noRetry': noRetry,
'uuid': uuid}), daemon=True).start()
self.orderLocks[uuid] = threading.Lock()
self.orderLocks[uuid].acquire()
return uuid
ext.okexPositionManager = PositionManager
Published by FawkesPan on the FMZ strategy square · last modified 2019-05-16. Collected in the Quant Trading Vault. Unvalidated research: re-test with your own data and costs before trusting it.
Build this with AgenKit
A spec is not a trading system. Paste this into your coding agent and AgenKit runs a specialist team — architecture, test-first build, risk gates, review — with paper trading on by default.
/agenkit build a production-grade trading bot with a backtest engine, risk controls and paper trading by default from strategies/OKEx-Contract-Position-Management-System.md- npx agenkit install engineering-kit in your project
- Paste the command into your agent and approve each phase
- Get tests, backtest, risk controls and a paper-trading switch
Understand it first
Before you run this, read the lesson behind the idea and how to backtest it honestly. Specs like this one are the survivors of an unknown number of trials; deflate their results accordingly.
Related specs
- Dynamic Balancing Strategy Python VersionPython · Other
- Exchange Wash Trading and Mutual BrushingPython · Other
- Leek Protection Program Tang Anqi Channel Position Balancing StrategyPython · Other
- Paul The Gambler Lévy Gold EditionPython · Other
- Porting OKCoin Leek Harvester Multi Variety Version Annotated Python VersionPython · Other
- Python Example Using asyncio http to Obtain Market DataPython · Other