##### MAKE ORDERS #####
def smartSignals(self):
# in this example the model predicts a moving average. predictedMA is the current value of the moving average the model predicts
predictedMA = talib.MA(self.marketData["Close"],timeperiod=self.modelParamsDict["rollingMeanWindow"]).iloc[-1]
# if price is above current and predicted MA, this means the price is going down. thus, sell all
if (self.price > predictedMA) & (self.price > self.currentPrediction):
self.targetRatio = 0
# else, compute predicted percentual change of the MA and use it to generate the target ratio of exposure
else:
percentualPred = (self.currentPrediction-predictedMA)/predictedMA
# the operation below is mostly arbitrary and can be optimized through backtests
# it converts the predicted percentual change of the MA into a target ratio of exposure
self.targetRatio = min(max(percentualPred*self.exposureMultiplier,0),1)
# if the target ratio is below minPctChange, set it to 0
if self.targetRatio < self.minPctChange:
self.targetRatio = 0
# with targetRatio set, we now place orders to achieve such ratio
self.achieveIdealPortfolio()
def achieveIdealPortfolio(self,saveOrder=True):
# utility functions (basically to avoid minimum notional)
def upThenDown():
self.placeOrder(sell=False,amount=(self.pfValNonUSD*(1-self.cryptoRatio)),saveOrder=saveOrder, refreshPf=True)
self.placeOrder(sell=True,amount=(self.pfValNonUSD*(1-self.targetRatio)),saveOrder=saveOrder)
def downThenUp():
self.placeOrder(sell=True,amount=(self.pfValNonUSD*self.cryptoRatio),saveOrder=saveOrder, refreshPf=True)
self.placeOrder(sell=False,amount=(self.pfValNonUSD*self.targetRatio),saveOrder=saveOrder)
# if the difference between the target ratio and the current ratio is less than minPctChange, do nothing
percentChange = self.targetRatio-self.cryptoRatio
if abs(percentChange)>self.minPctChange:
minNotionalThreshold = 12
minNotionalRatio = minNotionalThreshold/self.pfValUSD
# avoid minimum notional
if abs(percentChange)<minNotionalRatio:
if (self.cryptoRatio>1.2*minNotionalRatio):
downThenUp()
else:
upThenDown()
else:
self.placeOrder(sell=(percentChange<0),amount=abs(self.pfValNonUSD*percentChange),saveOrder=saveOrder)
def placeOrder(self, sell, amount, saveOrder=True, refreshPf=False):
def roundAndSendOrder(self, client, sell, amount):
amountToOrder = math.floor(amount*10000)/10000
print(f"\n-----> {'SELL' if sell else 'BUY'} {amountToOrder} {self.symbol[:-4]} | {round(amountToOrder*self.price,2)} USD | {round((amountToOrder*self.price*100)/self.pfValUSD,2)}% <-----\n")
if sell:
order = client.order_market_sell(
symbol= self.symbol,
quantity = amountToOrder)
else:
order = client.order_market_buy(
symbol= self.symbol,
quantity = amountToOrder)
return order
while True:
try:
client = Client(self.API[0], self.API[1], testnet=True)
# SELL
if sell:
amountCrypto = self.pfValNonUSD*self.cryptoRatio
if amount > amountCrypto:
print("Not enough crypto to sell!")
amount = amountCrypto*0.9999
order = roundAndSendOrder(self, client, sell, amount)
# BUY
else:
amountUSD = self.pfValNonUSD*(1-self.cryptoRatio)
if amount > amountUSD:
print("Not enough USD to buy!")
amount = amountUSD*0.9999
order = roundAndSendOrder(self, client, sell, amount)
client.close_connection()
except Exception as e:
if isinstance(e, socket.error):
print(f"Connection error:\n{e}")
else:
print(f"Ooops there was a problem placing an order:\n{e}")
else:
if refreshPf: self.refreshPortfolio()
if saveOrder: self.saveOrder(order)
break
def saveOrder(self,order):
order['effectivePrice'] = [round(float(order['cummulativeQuoteQty'])/float(order['executedQty']),2)]
order['pfValue'] = self.pfValUSD
order.pop('fills')
orderDF = pd.DataFrame.from_dict(order)
orderDF = orderDF[['symbol','pfValue','orderId','executedQty','cummulativeQuoteQty','effectivePrice','side','status','type','transactTime']].copy()
orderDF["transactTime"] = pd.to_datetime(orderDF["transactTime"],unit="ms")
orderDF.to_csv(f"{self.botFolderPath}/{self.name}_log.csv",mode="a",index=False,header=False)
#-----