def timeToClose(self, currentQuote, strategyMaker): """is it time to close (take-profit, save-loss, stop-loss triggered), or update stop-loss-value?""" avgPrice = self.relevantPrice(currentQuote) newStopValue = self.expectedTrailingStopValue(currentQuote) benefRatio = 0.0 lossRatio = 0.0 holdRatio = 0.0 if(newStopValue is not None and self.trailingStopValue is not None): if((self.forBUY and newStopValue < 0.9999*self.trailingStopValue) or (not self.forBUY and 0.9999*newStopValue > self.trailingStopValue)): corelog.critical( "WARNING: stop value calculations are retrograde!" ) import pdb; pdb.set_trace() newStopValue2 = self.expectedTrailingStopValue(currentQuote) # import pdb; pdb.set_trace() if(self.forBUY): delta = avgPrice - self.entryQuote.ask.o benefRatio = 100*delta/self.expGain lossRatio = -100*delta/self.expLoss # whe losing, use lossRatio and make it negative holdRatio = -lossRatio if(delta<0)else benefRatio if(avgPrice < self.saveLoss ): return ('save-loss', 'close', delta, lossRatio) elif(avgPrice > self.takeProfit): return ('take-profit', 'close', delta, benefRatio) elif(self.trailingStopValue is None and newStopValue is not None): return ('trailing-stop', 'trailing-stop', delta, holdRatio) elif(self.trailingStopValue is not None and newStopValue is not None and round(newStopValue,7) != round(self.trailingStopValue,7)): return ('trailing-stop', 'trailing-progress', delta,holdRatio) elif(self.trailingStopValue is not None and self.trailingStopValue>avgPrice): delta = self.trailingStopValue - self.entryQuote.ask.o return ('trailing-stop', 'close', delta, benefRatio) else: delta = self.entryQuote.bid.o - avgPrice benefRatio = 100*delta/self.expGain lossRatio = -100*delta/self.expLoss holdRatio = benefRatio if(delta>0)else -lossRatio if(avgPrice > self.saveLoss ): expLoss = self.saveLoss - self.entryQuote.ask.o return ('save-loss', 'close', delta, lossRatio) elif(avgPrice < self.takeProfit ): return ('take-profit', 'close', delta, benefRatio) elif(newStopValue is not None and self.trailingStopValue is None): return ('trailing-stop', 'trailing-stop', delta, holdRatio) elif(self.trailingStopValue is not None and newStopValue is not None and round(newStopValue,7) != round(self.trailingStopValue,7)): return ('trailing-stop', 'trailing-progress',delta,holdRatio) elif(self.trailingStopValue is not None and self.trailingStopValue < avgPrice): delta = self.entryQuote.bid.o - self.trailingStopValue return ('trailing-stop', 'close', delta, benefRatio) if(self.trailingStopNeedsReplacement): return ('trailing-stop', 'trailing-update', delta, holdRatio) return ('hold', 'hold', delta, holdRatio)
def refresh(self, force=False, raiseX=True): if (self.simulation): return if (force or self.refreshIsDue()): try: whenT = time.time() accountResp = self.api.account.get(self.accountId) self.account = accountResp.get('account', '200') self.accountTime = time.time() except: corelog.critical("issue refreshing account ... skipping ...") if (force or raiseX): raise
def executeTrade(self, looper, pos,wait=1200,noMore=5): """Execute a trade for a position, and a looper. On success, returns a 2-tuple: position object identified after successful trade, trade id When trade-id is None, the position object is the same as originally thought, the trade hasn't happened yet On failure, the 2-tuple is not returned, but None is returned. Some other issue at the broker (or with the order) have occurred""" kwargs = {} kwargs['instrument'] = looper.instrumentName # kwargs['price']=pos.entryQuote.ask.o if(pos.forBUY) else pos.entryQuote.bid.o kwargs['units']=(pos.size if(pos.forBUY) else -pos.size) #kwargs['timeInForce']='GTC' # saveLoss / takeProfit - user minimal minimumTrailingStopDistance to not annoy the broker mtsd = looper.instrument.minimumTrailingStopDistance sl = pos.saveLoss;tp=pos.takeProfit def nicepadding(x,prec): x = str(x) if(x.find(".")>0): x += "00000" return x[0:(x.index(".")+1+prec)] return x kwargs['stopLossOnFill'] = {"price": nicepadding(sl, looper.displayPrecision)} kwargs['takeProfitOnFill'] = {"price": nicepadding(tp, looper.displayPrecision)} corelog.debug(kwargs) response = looper.api.order.market( looper.accountId, **kwargs ) if(not(response.status == 201 or response.status == '201')): corelog.critical( "Position / Trade could not be executed...") corelog.critical(response.body) else: newTrades =[] prevTradeIDs = map(lambda t: t.id, looper.account.trades) while(len(newTrades)==0): time.sleep(wait/1000.0) looper.refreshPositions(self, force=True) newTrades = [ t for t in looper.account.trades if t.id not in prevTradeIDs and t.instrument == looper.instrumentName ] if(len(newTrades)==0): noMore -= 1 if(noMore>0): corelog.info("new trade not executed yet - waiting again...") else: corelog.warning("new trade not executed yet - but continuing...") return pos, None newPos = self.makeFromExistingTrade(pos.entryQuote, looper.account, newTrades[0].id) # if(pos.trailingSpecs is not None): # newPos.calibrateTrailingStopLossDesireForSteppedSpecs(pos.traillingSpecs, return newPos, newTrades[0].id return None
def executeClose(self, looper, pos, size=None, wait=1200, noMore=5): """ when the close executes, this returns a pair (position, trade-id). If the position is actually closed, then the position is returned as None, so what is returned is (None, trade-id). If the close fails, None is returned """ kwargs = {} if (size is not None): fsize = float(size) if (pos.size > fsize): kwargs["units"] = str(size) else: raise ValueError( "Cannot use executeClose to with a size greater than current position: {} is great than current {}" .format(size, pos.size)) response = looper.api.trade.close(looper.accountId, pos.tradeID, **kwargs) if (response.status.code == "200" or response.status.code == 200): corelog.debug("Closing trade {} with {} was successful".format( pos.tradeID, kwargs)) else: corelog.critical("Unable to close trade: {}\n{}".format( response.status, response.body)) return None myTrade = None while (myTrade is None and noMore > 0): time.sleep(wait / 1000.0) looper.refreshPositions(self, force=True) myTrades = [ t for t in looper.account.positions if t.id == pos.tradeID ] if (len(myTrades) > 0): if (float(myTrades[0].currentUnits) < float(pos.size)): myTrade = myTrades[0] else: if (size is None): # the position is not on the account (not open, but gone, closed) and we didn't pass a size, so it is all good. break else: noMore -= 1 if (myTrade is not None): newPosVersion = self.makeFromExistingTrade(pos.entryQuote, looper.account, myTrade.id) return newPosVersion, myTrade.id else: return None, pos.tradeID