예제 #1
0
    def updateTrailingStopValue(self, currentQuote):
        """Based on current trailing stop value, and on trailing stop distance,
           this may change the trailing stop value according to the current-quote.
           Example: trailing stop is at 100, trailing stop distance is 10, on a BUY order, 
           then the stop value should increase as soon the current-quote goes abouve 110.
           
           This is intended to simulate the change in trailing-stop value that the Broker is supposed to maintain. """

        avgPrice = self.relevantPrice(currentQuote)

        if (self.forBUY):
            if (self.trailingStopValue is not None
                    and self.trailingStopValue + self.trailingStopDistance <
                    avgPrice):
                self.trailingStopValue = avgPrice - self.trailingStopDistance
                corelog.info(
                    "updated trailing stop value to %f with distance %f",
                    self.trailingStopValue, self.trailingStopDistance)
        else:
            if (self.trailingStopValue is not None
                    and self.trailingStopValue - self.trailingStopDistance >
                    avgPrice):
                self.trailingStopValue = avgPrice + self.trailingStopDistance
                corelog.info(
                    "updated trailing stop value to %f with distance %f",
                    self.trailingStopValue, self.trailingStopDistance)
예제 #2
0
    def trailSpecShrink(self, entryQuote, currentQuote, trailSpecs):
        if(trailSpecs is None or len(trailSpecs)==0): return trailSpecs  # do nothing when there is nothing to do

        eqt = dateutil.parser.parse(entryQuote.time)
        cqt = dateutil.parser.parse(currentQuote.time)
        deltaT = cqt-eqt
        periods = deltaT.total_seconds() / self.frequency
        if(periods>1):
            periods = round(periods)
            af = self.factor ** periods
            import math
            afTrailSpecs = map(lambda s: (math.ceil(af*s[0]), math.ceil(af*s[1]) ), trailSpecs)
            afTrailSpecs.sort(lambda x,y: cmp(x[0],y[0]))
            # print(afTrailSpecs)
            ntrailSpecs = [ afTrailSpecs[0] ]
            for n in range(len(afTrailSpecs)):
                if(n>0):
                    cc = afTrailSpecs[n]
                    ll = ntrailSpecs[-1]
                    if(ll[0] == cc[0]): # same trigger, choose the bigger distance
                        if(ll[1]< cc[1]):
                            ntrailSpecs[-1] = cc
                    elif(ll[0] > cc[0]): #later trigger is smaller, not possible, do nothing
                        pass
                    elif(ll[0] < cc[0]): 
                        if(ll[1]>cc[1]):
                            ntrailSpecs.append(cc)
            corelog.info("trailSpecs shrunk with factor " + str(af))
            return ntrailSpecs
        else:
            return trailSpecs 
예제 #3
0
    def calibrateTrailingStopLossDesireForSteppedSpecs(self, currentQuote, trailSpecs, mspread, minimumTrailingStopDistance, trailingShrinker = None):
        """ calibrate the Position trailing stop details for the new current quote.
            This may change the trigger price, and desired distance based on current-quote (latest reading from the market)
            and the trailing-stop-specc/steps, also based on current median-spread.
            If a change is deemed necessary, it is made and the property trailingStopNeedsReplacement is set to True.
            This does NOT change the trailingStopValue (it is changed by 'updateTrailingStopValue')

            TrailSpecs and mspread can be passed as None if you just want to use the latest calibrated values for trailSpecs and mspread
            currentquote can be passed as None to calibrate the position at the very beginning."""

        if(currentQuote is None and (trailSpecs is None or mspread is None)):
            raise ValueError("need either curentQuote or trailSpecs + mspread")

        if(mspread is None):
            mspread = self.medianSpread
        else:
            self.medianSpread = mspread

        self.minimumTrailingStopDistance = minimumTrailingStopDistance

        plusMinus = 1.0 if(self.forBUY) else -1.0

        if(currentQuote is None):
            self.trailingStopDesiredDistance = mspread*trailSpecs[0][1]
            self.trailingStopTriggerPrice    = mspread*trailSpecs[0][0]* plusMinus + (self.entryPrice())
            self.trailSpecs               = trailSpecs
            return

        if(trailSpecs is None): trailSpecs = self.trailSpecs
        currentDistance = self.trailingStopDistance
        unitProfit = self.quoteProfit(currentQuote, True)
        if(unitProfit>0.0):
            #print("unitProfit: " + str(unitProfit) + " = " + str(unitProfit/mspread) + " x median-spread")
            if(trailingShrinker is not None):
                trailSpecs = trailingShrinker(self.entryQuote, currentQuote, trailSpecs)

            okSpec = None
            for spec in trailSpecs:
                if(unitProfit>mspread*spec[0] and (currentDistance is None or mspread*spec[1]<currentDistance)):
                    okSpec = spec
                elif(okSpec is not None):
                    break

            if(okSpec is None): return

            newDesiredDistance = round(mspread*okSpec[1],7)
            if(newDesiredDistance<minimumTrailingStopDistance): newDesiredDistance = minimumTrailingStopDistance

            if(newDesiredDistance < self.trailingStopDesiredDistance or self.trailingStopDesiredDistance<=0):
                corelog.info("new desired distance {} spreads = {}".format(okSpec[1], newDesiredDistance))
                self.trailingStopDesiredDistance = newDesiredDistance
                self.trailingStopNeedsReplacement = True
            if(self.trailingStopDistance is not None):
                # then trailing stop is already engage, note a needed replacement only if trailingStopDistance is different than new distance, the moving stop value is taken care by the broker
                self.trailingStopNeedsReplacement = (newDesiredDistance != round(self.trailingStopDistance,7))
                corelog.info("current trailing stop distance:%s, new desired distance:%s, replacement-needed:%s",
                              self.trailingStopDistance, newDesiredDistance, self.trailingStopNeedsReplacement)
            return
예제 #4
0
    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