Example #1
0
 def executeTrailingStop(self, looper, pos, wait=1200, noMore=5):
     """Bring an executed position (with tradeID) to be set with the expected trailing-stop parameters"""
     mtsd = looper.instrument.minimumTrailingStopDistance
     if (pos.tradeID is not None):
         distance = pos.trailingStopDesiredDistance
         if (distance < mtsd):
             distance = mtsd
         # price = (pos.trailingStopValue+distance*(1 if(pos.forBUY) else -1)) if(pos.trailingStopValue is not None) else pos.trailingStopTriggerPrice
         distance = self.nicepadding(distance, looper.displayPrecision)
         # price    = self.nicepadding(price, looper.displayPrecision)
         tslargs = {"tradeID": str(pos.tradeID), "distance": distance}
         corelog.debug(tslargs)
         respTSL = None
         if (pos.trailingStopLossOrderId is None):
             respTSL = looper.api.order.trailing_stop_loss(
                 looper.accountId, **tslargs)
         else:
             #                corelog.debug( "replace order {}", pos.trailingStopLossOrderId)
             respTSL = looper.api.order.trailing_stop_loss_replace(
                 looper.accountId, pos.trailingStopLossOrderId, **tslargs)
         #corelog.debug("status code:{}\nbody:{}",respTSL.status, respTSL.body)
         if (str(respTSL.status) == '201'):
             time.sleep(float(wait) / 1000.0)
             looper.refreshPositions(self, True)
     else:
         raise RuntimeError(
             "cannot call executeTrailingStop on a position that has not been entered/traded"
         )
Example #2
0
def getBacktrackingCandles(loopr,
                           highCount,
                           highSlice,
                           lowSlice,
                           lowAheadOfHigh=True):
    """ get high and low slice candles from OANDA api.
        This will provide the number of highCount high-slice candles and the low-slice candles in between.
        See getCachedBacktrackingCandles for details on other parameters"""
    highKW = {"count": highCount, "price": "BA", "granularity": highSlice}
    lowKW = {"price": "BA", "granularity": lowSlice}

    xoff = 1 if (not lowAheadOfHigh) else 0

    corelog.debug(highKW)
    highCandles = getSortedCandles(loopr, highKW)
    # when not lowAheadOfHigh starts with an empty backtracking list, otherwise starts with single high candle and no low candles
    backtracking = [(highCandles[0], [])] if (not lowAheadOfHigh) else []
    for i in range(len(highCandles) - xoff):
        a = highCandles[i]
        lowKW["fromTime"] = a.time
        if (i + 1 < len(highCandles)):
            b = highCandles[i + 1]
            lowKW["toTime"] = b.time
        corelog.debug(lowKW)
        lowCandles = getSortedCandles(loopr, lowKW)
        hc = highCandles[i + xoff]
        item = (hc, lowCandles)
        backtracking.append(item)

    return backtracking
    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 refreshPositions(self, positionFactory, force=False):
        self.refresh(force)
        freshPositions = []
        for pos in self.findPositionsRaw():
            tradeIDs = (pos.long.tradeIDs if(pos.long.tradeIDs is not None)else pos.short.tradeIDs)
            if(tradeIDs is not None):
                for tradeID in tradeIDs:
                    ppos = positionFactory.makeFromExistingTrade(self.mkCandlestickTemplate(), self.account, tradeID)
                    freshPositions.append(ppos)
            else:
                corelog.debug("(trade less position)")

        # make sure the position array is sorted by openTime
        freshPositions.sort(lambda a,b: cmp(a.entryQuote.time, b.entryQuote.time))
        self.positions = freshPositions
Example #5
0
    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