happydasch / btoandav20

Support for Oanda-V20 API in backtrader
Apache License 2.0
131 stars 52 forks source link

S/L T/P T/S not working #41

Closed codeguru1983 closed 4 years ago

codeguru1983 commented 4 years ago

Hi, I'm back again I've been strugling for 2 days to get this working but I think I'm too stupid for this so I'll ask for a bit of help, I've downloaded the last fork uploaded yesterday, reinstalled everything from scratch. I don't know what I'm doing wrong but I need to setup the trailing stop and stop loss.

I've tried by adding here this method:

if self.datastatus and not self.position and len(self.orderid) < 1:
            if not self.p.usebracket:
                if not self.p.sell:
                    # price = round(self.data0.close[0] * 0.90, 2)
                    price = self.data0.close[0] - 0.005
                    self.order = self.buy(size=self.p.stake,
                                          exectype=self.p.exectype,
                                          price=price,
                                          valid=self.p.valid)
                else:
                    # price = round(self.data0.close[0] * 1.10, 4)
                    price = self.data0.close[0] - 0.05
                    self.order = self.sell(size=self.p.stake,
                                           exectype=self.p.exectype,
                                           price=price,
                                           valid=self.p.valid)

            else:
                print('USING BRACKET')
                price = self.data0.close[0] - 0.05
                self.order, _, _ = self.buy_bracket(size=self.p.stake,
                                                    exectype=bt.Order.Market,
                                                    price=price,
                                                    **stopprice = price - price * 0.001**,
                            **limitprice = price + price * 0.002**,
                                                    valid=self.p.valid)

also for trailing stop I've tried this method which again didn't do anything, in the fxTrade platform there's nothing in S/L T/P T/S columns, and also within the print of the order inside the terminal there's nothing:


            else:
                print('USING BRACKET')
                price = self.data0.close[0] - 0.05
                self.order, _, _ = self.buy_bracket(size=self.p.stake,
                                                    exectype=bt.Order.StopTrail, 
                            trailpercent=0.02,
                                                    valid=self.p.valid)

terminal print


-------------------------------------------------- ORDER BEGIN 2019-12-18 19:45:00.358522
Ref: 3
OrdType: 1
OrdType: Sell
Status: 4
Status: Completed
Size: -125
Price: 143.355
Price Limit: None
TrailAmount: None
TrailPercent: None
ExecType: 0
ExecType: Market
CommInfo: <backtrader.comminfo.CommInfoBase object at 0x7f4be29b4310>
End of Session: 737411.9999999999
Info: AutoOrderedDict()
Broker: None
Alive: False
-------------------------------------------------- ORDER END
-------------------------------------------------- TRADE BEGIN 2019-12-18 19:45:00.358930
ref:1
data:<btoandav20.feeds.oandav20feed.OandaV20Data object at 0x7f4be10c9fd0>
tradeid:0
size:0.0
price:143.337
value:0.0
commission:0.0
pnl:2.125000000003041
pnlcomm:2.125000000003041
justopened:False
isopen:False
isclosed:True
baropen:1
dtopen:737411.7375
barclose:3
dtclose:737411.7388888889
barlen:2
historyon:False
history:[]
status:2

I'm not sure if this is the only place where I need to declare the trailing stop and I don't understand how to add the stop loss for first step. So let's suppose I trade long GBP/JPY, currently the price is 143.337, if it goes up to let's say 143.90 then the trailing stop should go after him and stop at a percentage or a number of pips, preferably the number of pips, in case I'm right then this trailing stop goes with it, change the stop loss right?, if I'm wrong, there's a stop loss in place (which I guess I should declare too through *stopprice = price - price 0.001**....

I don't understand what I'm doing wrong and how to declare a stop price and a trailing stop in the same time, so if the strategy is right will get a profit at a certain level when the price goes up and then drops, if the strategy is wrong then it won't loose more than whichever price - price * 0.001.

Thank you very much again for your support.

happydasch commented 4 years ago

Hi, please provide some example code to get an idea of your issue.

happydasch commented 4 years ago

also take a look at the order creation method:

https://github.com/ftomassetti/backtrader-oandav20/blob/5bc20472c639c4b274c2095f24ea724d6ace5e09/btoandav20/stores/oandav20store.py#L346

here you can find required parameters

codeguru1983 commented 4 years ago

I'm sure this is less an issue and more to do with me being silly to understand and work it out, is the same code from the example you supply. I'm not sure, I just tried to add the stop loss and trailing stop: So if as in my above post I go long and the strategy is wrong say 0.2% ... sell, if the strategy is good then the trailing stop should move up with the price, and after it drops let's say 10 pips sell, let's stick to the GBP_JPY because then I'll figure it out for the other ones, for trailing stop I've tried to set it up with :


if self.datastatus and not self.position and len(self.orderid) < 1:
            if not self.p.usebracket:
                if not self.p.sell:
                    # price = round(self.data0.close[0] * 0.90, 2)
                    price = self.data0.close[0] - 0.005
                    self.order = self.buy(size=self.p.stake,
                                          exectype=self.p.exectype,
                                          price=price,
                                          valid=self.p.valid)
                else:
                    # price = round(self.data0.close[0] * 1.10, 4)
                    price = self.data0.close[0] - 0.05
                    self.order = self.sell(size=self.p.stake,
                                           exectype=self.p.exectype,
                                           price=price,
                                           valid=self.p.valid)

            else:
                print('USING BRACKET')
                price = self.data0.close[0] - 0.05
                self.order, _, _ = self.buy_bracket(size=1, 
                            exectype=bt.Order.StopTrail, 
                                trailpercent=0.02)

below is the case with a basic stop loss & take profit to see if it's adding it to the order but it doesn't, however, what I'm after and I really need is the stop loss & trailing stop.


#!/usr/bin/env python

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import argparse
import datetime

import backtrader as bt
from backtrader.utils import flushfile  # win32 quick stdout flushing

import btoandav20

StoreCls = btoandav20.stores.OandaV20Store
DataCls = btoandav20.feeds.OandaV20Data
# BrokerCls = btoandav20.brokers.OandaV20Broker

# available timeframes for oanda
TIMEFRAMES = [bt.TimeFrame.Names[bt.TimeFrame.Seconds],
         bt.TimeFrame.Names[bt.TimeFrame.Minutes],
         bt.TimeFrame.Names[bt.TimeFrame.Days],
         bt.TimeFrame.Names[bt.TimeFrame.Weeks],
         bt.TimeFrame.Names[bt.TimeFrame.Months]]

class TestStrategy(bt.Strategy):
    params = dict(
        smaperiod=5,
        trade=False,
        stake=10,
        exectype=bt.Order.Market,
        stopafter=0,
        valid=None,
        cancel=0,
        donotcounter=False,
        sell=False,
        usebracket=False,
    )

    def __init__(self):
        # To control operation entries
        self.orderid = list()
        self.order = None

        self.counttostop = 0
        self.datastatus = 0

        # Create SMA on 2nd data
        self.sma = bt.indicators.MovAv.SMA(self.data, period=self.p.smaperiod)

        print('--------------------------------------------------')
        print('Strategy Created')
        print('--------------------------------------------------')

    def notify_data(self, data, status, *args, **kwargs):
        print('*' * 5, 'DATA NOTIF:', data._getstatusname(status), *args)
        if status == data.LIVE:
            self.counttostop = self.p.stopafter
            self.datastatus = 1

    def notify_store(self, msg, *args, **kwargs):
        print('*' * 5, 'STORE NOTIF:', msg)

    def notify_order(self, order):
        if order.status in [order.Completed, order.Cancelled, order.Rejected]:
            self.order = None

        print('-' * 50, 'ORDER BEGIN', datetime.datetime.now())
        print(order)
        print('-' * 50, 'ORDER END')

    def notify_trade(self, trade):
        print('-' * 50, 'TRADE BEGIN', datetime.datetime.now())
        print(trade)
        print('-' * 50, 'TRADE END')

    def prenext(self):
        self.next(frompre=True)

    def next(self, frompre=False):
        txt = list()
        txt.append('Data0')
        txt.append('%04d' % len(self.data0))
        dtfmt = '%Y-%m-%dT%H:%M:%S.%f'
        txt.append('{:f}'.format(self.data.datetime[0]))
        txt.append('%s' % self.data.datetime.datetime(0).strftime(dtfmt))
        txt.append('{:f}'.format(self.data.open[0]))
        txt.append('{:f}'.format(self.data.high[0]))
        txt.append('{:f}'.format(self.data.low[0]))
        txt.append('{:f}'.format(self.data.close[0]))
        txt.append('{:6d}'.format(int(self.data.volume[0])))
        txt.append('{:d}'.format(int(self.data.openinterest[0])))
        txt.append('{:f}'.format(self.sma[0]))
        print(', '.join(txt))

        if len(self.datas) > 1 and len(self.data1):
            txt = list()
            txt.append('Data1')
            txt.append('%04d' % len(self.data1))
            dtfmt = '%Y-%m-%dT%H:%M:%S.%f'
            txt.append('{}'.format(self.data1.datetime[0]))
            txt.append('%s' % self.data1.datetime.datetime(0).strftime(dtfmt))
            txt.append('{}'.format(self.data1.open[0]))
            txt.append('{}'.format(self.data1.high[0]))
            txt.append('{}'.format(self.data1.low[0]))
            txt.append('{}'.format(self.data1.close[0]))
            txt.append('{}'.format(self.data1.volume[0]))
            txt.append('{}'.format(self.data1.openinterest[0]))
            txt.append('{}'.format(float('NaN')))
            print(', '.join(txt))

        if self.counttostop:  # stop after x live lines
            self.counttostop -= 1
            if not self.counttostop:
                self.env.runstop()
                return

        if not self.p.trade:
            return

        if self.datastatus and not self.position and len(self.orderid) < 1:
            if not self.p.usebracket:
                if not self.p.sell:
                    # price = round(self.data0.close[0] * 0.90, 2)
                    price = self.data0.close[0] - 0.005
                    self.order = self.buy(size=self.p.stake,
                                          exectype=self.p.exectype,
                                          price=price,
                                          valid=self.p.valid)
                else:
                    # price = round(self.data0.close[0] * 1.10, 4)
                    price = self.data0.close[0] - 0.05
                    self.order = self.sell(size=self.p.stake,
                                           exectype=self.p.exectype,
                                           price=price,
                                           valid=self.p.valid)

            else:
                print('USING BRACKET')
                price = self.data0.close[0] - 0.05
                self.order, _, _ = self.buy_bracket(size=self.p.stake,
                                                    exectype=bt.Order.Market,
                                                    price=price,
                                                    stopprice = price - price * 0.001,
                            limitprice = price + price * 0.002,
                                                    valid=self.p.valid)

            self.orderid.append(self.order)
        elif self.position and not self.p.donotcounter:
            if self.order is None:
                if not self.p.sell:
                    self.order = self.sell(size=self.p.stake // 2,
                                           exectype=bt.Order.Market,
                                           price=self.data0.close[0])
                else:
                    self.order = self.buy(size=self.p.stake // 2,
                                          exectype=bt.Order.Market,
                                          price=self.data0.close[0])

            self.orderid.append(self.order)

        elif self.order is not None and self.p.cancel:
            if self.datastatus > self.p.cancel:
                self.cancel(self.order)

        if self.datastatus:
            self.datastatus += 1

    def start(self):
        if self.data0.contractdetails is not None:
            print('-- Contract Details:')
            print(self.data0.contractdetails)

        header = ['Datetime', 'Open', 'High', 'Low', 'Close', 'Volume',
                  'OpenInterest', 'SMA']
        print(', '.join(header))

        self.done = False

def runstrategy():
    args = parse_args()

    # Create a cerebro
    cerebro = bt.Cerebro()

    storekwargs = dict(
        token=args.token,
        account=args.account,
        practice=not args.live
    )

    if not args.no_store:
        store = StoreCls(**storekwargs)

    if args.broker:
        if args.no_store:
            broker = BrokerCls(**storekwargs)
        else:
            broker = store.getbroker()

        cerebro.setbroker(broker)

    timeframe = bt.TimeFrame.TFrame(args.timeframe)
    # Manage data1 parameters
    tf1 = args.timeframe1
    tf1 = bt.TimeFrame.TFrame(tf1) if tf1 is not None else timeframe
    cp1 = args.compression1
    cp1 = cp1 if cp1 is not None else args.compression

    if args.resample or args.replay:
        datatf = datatf1 = bt.TimeFrame.Ticks
        datacomp = datacomp1 = 1
    else:
        datatf = timeframe
        datacomp = args.compression
        datatf1 = tf1
        datacomp1 = cp1

    fromdate = None
    if args.fromdate:
        dtformat = '%Y-%m-%d' + ('T%H:%M:%S' * ('T' in args.fromdate))
        fromdate = datetime.datetime.strptime(args.fromdate, dtformat)

    DataFactory = DataCls if args.no_store else store.getdata

    datakwargs = dict(
        timeframe=datatf, compression=datacomp,
        qcheck=args.qcheck,
        historical=args.historical,
        fromdate=fromdate,
        bidask=args.bidask,
        useask=args.useask,
        backfill_start=not args.no_backfill_start,
        backfill=not args.no_backfill,
        tz=args.timezone
    )

    if args.no_store and not args.broker:   # neither store nor broker
        datakwargs.update(storekwargs)  # pass the store args over the data

    data0 = DataFactory(dataname=args.data0, **datakwargs)

    data1 = None
    if args.data1 is not None:
        if args.data1 != args.data0:
            datakwargs['timeframe'] = datatf1
            datakwargs['compression'] = datacomp1
            data1 = DataFactory(dataname=args.data1, **datakwargs)
        else:
            data1 = data0

    rekwargs = dict(
        timeframe=timeframe, compression=args.compression,
        bar2edge=not args.no_bar2edge,
        adjbartime=not args.no_adjbartime,
        rightedge=not args.no_rightedge,
        takelate=not args.no_takelate,
    )

    if args.replay:
        cerebro.replaydata(data0, **rekwargs)

        if data1 is not None:
            rekwargs['timeframe'] = tf1
            rekwargs['compression'] = cp1
            cerebro.replaydata(data1, **rekwargs)

    elif args.resample:
        cerebro.resampledata(data0, **rekwargs)

        if data1 is not None:
            rekwargs['timeframe'] = tf1
            rekwargs['compression'] = cp1
            cerebro.resampledata(data1, **rekwargs)

    else:
        cerebro.adddata(data0)
        if data1 is not None:
            cerebro.adddata(data1)

    if args.valid is None:
        valid = None
    else:
        valid = datetime.timedelta(seconds=args.valid)
    # Add the strategy
    cerebro.addstrategy(TestStrategy,
                        smaperiod=args.smaperiod,
                        trade=args.trade,
                        exectype=bt.Order.ExecType(args.exectype),
                        stake=args.stake,
                        stopafter=args.stopafter,
                        valid=valid,
                        cancel=args.cancel,
                        donotcounter=args.donotcounter,
                        sell=args.sell,
                        usebracket=args.usebracket)

    # Live data ... avoid long data accumulation by switching to "exactbars"
    cerebro.run(exactbars=args.exactbars)
    if args.exactbars < 1:  # plotting is possible
        if args.plot:
            pkwargs = dict(style='line')
            if args.plot is not True:  # evals to True but is not True
                npkwargs = eval('dict(' + args.plot + ')')  # args were passed
                pkwargs.update(npkwargs)

            cerebro.plot(**pkwargs)

def parse_args(pargs=None):
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description='Test Oanda v20 integration')

    parser.add_argument('--exactbars', default=1, type=int,
                        required=False, action='store',
                        help='exactbars level, use 0/-1/-2 to enable plotting')

    parser.add_argument('--stopafter', default=0, type=int,
                        required=False, action='store',
                        help='Stop after x lines of LIVE data')

    parser.add_argument('--no-store',
                        required=False, action='store_true',
                        help='Do not use the store pattern')

    parser.add_argument('--debug',
                        required=False, action='store_true',
                        help='Display all info received from source')

    parser.add_argument('--token', default=None,
                        required=True, action='store',
                        help='Access token to use')

    parser.add_argument('--account', default=None,
                        required=True, action='store',
                        help='Account identifier to use')

    parser.add_argument('--live', default=None,
                        required=False, action='store',
                        help='Go to live server rather than practice')

    parser.add_argument('--qcheck', default=0.5, type=float,
                        required=False, action='store',
                        help=('Timeout for periodic '
                              'notification/resampling/replaying check'))

    parser.add_argument('--data0', default=None,
                        required=True, action='store',
                        help='data 0 into the system')

    parser.add_argument('--data1', default=None,
                        required=False, action='store',
                        help='data 1 into the system')

    parser.add_argument('--timezone', default=None,
                        required=False, action='store',
                        help='timezone to get time output into (pytz names)')

    parser.add_argument('--bidask', default=None,
                        required=False, action='store_true',
                        help='Use bidask ... if False use midpoint')

    parser.add_argument('--useask', default=None,
                        required=False, action='store_true',
                        help='Use the "ask" of bidask prices/streaming')

    parser.add_argument('--no-backfill_start',
                        required=False, action='store_true',
                        help='Disable backfilling at the start')

    parser.add_argument('--no-backfill',
                        required=False, action='store_true',
                        help='Disable backfilling after a disconnection')

    parser.add_argument('--historical',
                        required=False, action='store_true',
                        help='do only historical download')

    parser.add_argument('--fromdate',
                        required=False, action='store',
                        help=('Starting date for historical download '
                              'with format: YYYY-MM-DD[THH:MM:SS]'))

    parser.add_argument('--smaperiod', default=5, type=int,
                        required=False, action='store',
                        help='Period to apply to the Simple Moving Average')

    pgroup = parser.add_mutually_exclusive_group(required=False)

    pgroup.add_argument('--replay',
                        required=False, action='store_true',
                        help='replay to chosen timeframe')

    pgroup.add_argument('--resample',
                        required=False, action='store_true',
                        help='resample to chosen timeframe')

    parser.add_argument('--timeframe', default=TIMEFRAMES[0],
                        choices=TIMEFRAMES,
                        required=False, action='store',
                        help='TimeFrame for Resample/Replay')

    parser.add_argument('--compression', default=5, type=int,
                        required=False, action='store',
                        help='Compression for Resample/Replay')

    parser.add_argument('--timeframe1', default=None,
                        choices=TIMEFRAMES,
                        required=False, action='store',
                        help='TimeFrame for Resample/Replay - Data1')

    parser.add_argument('--compression1', default=None, type=int,
                        required=False, action='store',
                        help='Compression for Resample/Replay - Data1')

    parser.add_argument('--no-takelate',
                        required=False, action='store_true',
                        help=('resample/replay, do not accept late samples'))

    parser.add_argument('--no-bar2edge',
                        required=False, action='store_true',
                        help='no bar2edge for resample/replay')

    parser.add_argument('--no-adjbartime',
                        required=False, action='store_true',
                        help='no adjbartime for resample/replay')

    parser.add_argument('--no-rightedge',
                        required=False, action='store_true',
                        help='no rightedge for resample/replay')

    parser.add_argument('--broker',
                        required=False, action='store_true',
                        help='Use Oanda as broker')

    parser.add_argument('--trade',
                        required=False, action='store_true',
                        help='Do Sample Buy/Sell operations')

    parser.add_argument('--sell',
                        required=False, action='store_true',
                        help='Start by selling')

    parser.add_argument('--usebracket',
                        required=False, action='store_true',
                        help='Test buy_bracket')

    parser.add_argument('--donotcounter',
                        required=False, action='store_true',
                        help='Do not counter the 1st operation')

    parser.add_argument('--exectype', default=bt.Order.ExecTypes[0],
                        choices=bt.Order.ExecTypes,
                        required=False, action='store',
                        help='Execution to Use when opening position')

    parser.add_argument('--stake', default=10, type=int,
                        required=False, action='store',
                        help='Stake to use in buy operations')

    parser.add_argument('--valid', default=None, type=float,
                        required=False, action='store',
                        help='Seconds to keep the order alive (0 means DAY)')

    parser.add_argument('--cancel', default=0, type=int,
                        required=False, action='store',
                        help=('Cancel a buy order after n bars in operation,'
                              ' to be combined with orders like Limit'))

    # Plot options
    parser.add_argument('--plot', '-p', nargs='?', required=False,
                        metavar='kwargs', const=True,
                        help=('Plot the read data applying any kwargs passed\n'
                              '\n'
                              'For example (escape the quotes if needed):\n'
                              '\n'
                              '  --plot style="candle" (to plot candles)\n'))

    if pargs is not None:
        return parser.parse_args(pargs)

    return parser.parse_args()

if __name__ == '__main__':
    runstrategy()

Have you got a working example that I can add to the git you supply directly, like the example you provide but with trailing stop and stop loss, so then I can dismantle it and understand better?

happydasch commented 4 years ago

I went through the code. It seems, I did not add Trailing Stop support. Only SL and TP will work.

happydasch commented 4 years ago

take also a look in: https://github.com/ftomassetti/backtrader-oandav20/issues/4

codeguru1983 commented 4 years ago

Ok, would it be complicated to add? Can you please tell me what I do wrong that it doesn't add the stop loss and take profit at least? Thank you

codeguru1983 commented 4 years ago

How much would cost me if I have to pay you to add me trailing stop?

happydasch commented 4 years ago

for a SL and TP order:

since you are doing a market order the price does not really matter:

price = self.data0.close[0] self.order = self.buy_bracket( price=price, exectype=bt.Order.Market, limitprice=price + price * 0.002, stopprice=price - price * 0.001,)

for a limit order:

price = self.data0.close[0] - 0.2 self.order = self.buy_bracket( price=price, exectype=bt.Order.Limit, limitprice=price + price * 0.002, stopprice=price - price * 0.001,)

happydasch commented 4 years ago

Ok, would it be complicated to add?

It is not that complicated. all is done in this method:

https://github.com/ftomassetti/backtrader-oandav20/blob/5bc20472c639c4b274c2095f24ea724d6ace5e09/btoandav20/stores/oandav20store.py#L346

How much would cost me if I have to pay you to add me trailing stop?

I am not for hire. I will however work next year on this, but I cannot say exactly when I will get to this. Check out this post on backtrader:

https://community.backtrader.com/topic/2201/is-there-any-commercial-support-available/2

If you need this, then maybe you'll find some commercial guy for this.

codeguru1983 commented 4 years ago

I found this bit of code on backtrade website, I suppose is not enough right to work with your oandav? I can see that's trying to access a text file which ofcourse is not suitable...


from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import argparse
import datetime

import backtrader as bt

class St(bt.Strategy):
    params = dict(
        ma=bt.ind.SMA,
        p1=10,
        p2=30,
        stoptype=bt.Order.StopTrail,
        trailamount=0.0,
        trailpercent=0.0,
    )

    def __init__(self):
        ma1, ma2 = self.p.ma(period=self.p.p1), self.p.ma(period=self.p.p2)
        self.crup = bt.ind.CrossUp(ma1, ma2)
        self.order = None

    def next(self):
        if not self.position:
            if self.crup:
                o = self.buy()
                self.order = None
                print('*' * 50)

        elif self.order is None:
            self.order = self.sell(exectype=self.p.stoptype,
                                   trailamount=self.p.trailamount,
                                   trailpercent=self.p.trailpercent)

            if self.p.trailamount:
                tcheck = self.data.close - self.p.trailamount
            else:
                tcheck = self.data.close * (1.0 - self.p.trailpercent)
            print(','.join(
                map(str, [self.datetime.date(), self.data.close[0],
                          self.order.created.price, tcheck])
                )
            )
            print('-' * 10)
        else:
            if self.p.trailamount:
                tcheck = self.data.close - self.p.trailamount
            else:
                tcheck = self.data.close * (1.0 - self.p.trailpercent)
            print(','.join(
                map(str, [self.datetime.date(), self.data.close[0],
                          self.order.created.price, tcheck])
                )
            )

def runstrat(args=None):
    args = parse_args(args)

    cerebro = bt.Cerebro()

    # Data feed kwargs
    kwargs = dict()

    # Parse from/to-date
    dtfmt, tmfmt = '%Y-%m-%d', 'T%H:%M:%S'
    for a, d in ((getattr(args, x), x) for x in ['fromdate', 'todate']):
        if a:
            strpfmt = dtfmt + tmfmt * ('T' in a)
            kwargs[d] = datetime.datetime.strptime(a, strpfmt)

    # Data feed
    data0 = bt.feeds.BacktraderCSVData(dataname=args.data0, **kwargs)
    cerebro.adddata(data0)

    # Broker
    cerebro.broker = bt.brokers.BackBroker(**eval('dict(' + args.broker + ')'))

    # Sizer
    cerebro.addsizer(bt.sizers.FixedSize, **eval('dict(' + args.sizer + ')'))

    # Strategy
    cerebro.addstrategy(St, **eval('dict(' + args.strat + ')'))

    # Execute
    cerebro.run(**eval('dict(' + args.cerebro + ')'))

    if args.plot:  # Plot if requested to
        cerebro.plot(**eval('dict(' + args.plot + ')'))

def parse_args(pargs=None):
    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description=(
            'StopTrail Sample'
        )
    )

    parser.add_argument('--data0', default='../../datas/2005-2006-day-001.txt',
                        required=False, help='Data to read in')

    # Defaults for dates
    parser.add_argument('--fromdate', required=False, default='',
                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')

    parser.add_argument('--todate', required=False, default='',
                        help='Date[time] in YYYY-MM-DD[THH:MM:SS] format')

    parser.add_argument('--cerebro', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--broker', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--sizer', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--strat', required=False, default='',
                        metavar='kwargs', help='kwargs in key=value format')

    parser.add_argument('--plot', required=False, default='',
                        nargs='?', const='{}',
                        metavar='kwargs', help='kwargs in key=value format')

    return parser.parse_args(pargs)

if __name__ == '__main__':
    runstrat()
codeguru1983 commented 4 years ago

Can you at least please let me know why's not working take profit and stop loss? Is the original source coude I've only changed the price = price *..... is anyhting else that needs to be done?

happydasch commented 4 years ago

example.py.zip

These lines are where the order is created:

Bildschirmfoto 2019-12-18 um 21 03 40
happydasch commented 4 years ago

Ok, would it be complicated to add? Can you please tell me what I do wrong that it doesn't add the stop loss and take profit at least? Thank you

I will do a quick change based on the example code from backtrader, send me a email to daniel@vcard24.de then I will provide you a updated version of btoandav20 which you could test with trailing stop.

happydasch commented 4 years ago

also for brackets, they allow only one stopside and one profitside, so for stop loss and trailing stop, this would not work, you'd need to create the orders manually. This is a limitation of backtrader.

for the profitside, in brackets its always a TP order, for the order itself, either MARKET OR LIMIT ORDER, for the stopside its currently a SL order.

When doing it manually, you would enter a trade (for example a LIMIT or a MARKET order) then you would wait until its created. After that you would create a SELL order and a TRAILING STOP order. This should work.

codeguru1983 commented 4 years ago

Ok, at least trailing stop and I'll keep an eye on it in case it goes wrong. In the mean while... I've tried to change into this for sl tp but still not working, even for first entry that I do manually, for the ones that he generates either not working:


if self.datastatus and not self.position and len(self.orderid) < 1:
            if not self.p.usebracket:
                if not self.p.sell:
                    # price = round(self.data0.close[0] * 0.90, 2)
                    price = self.data0.close[0] - 0.005
                    self.order = self.buy(size=self.p.stake,
                                          exectype=self.p.exectype,
                                          price=price,
                                          valid=self.p.valid)
                else:
                    # price = round(self.data0.close[0] * 1.10, 4)
                    price = self.data0.close[0] - 0.05
                    self.order = self.sell(size=self.p.stake,
                                           exectype=self.p.exectype,
                                           price=price,
                                           valid=self.p.valid)

            else:
                print('USING BRACKET')
                price = self.data0.close[0] - 0.05
                self.order, _, _ = self.buy_bracket(size=self.p.stake,
                                                    expectype=bt.Order.Limit,
                                                    price=price,
                                                    limitprice=price + price * 0.002,
                                                    stopprice=price - price * 0.001,
                                                    valid=self.p.valid)

            self.orderid.append(self.order)
        elif self.position and not self.p.donotcounter:
            if self.order is None:
                if not self.p.sell:
                    self.order = self.sell(size=self.p.stake // 2,
                                           exectype=bt.Order.Market,
                                           price=self.data0.close[0])
                else:
                    self.order = self.buy(size=self.p.stake // 2,
                                          exectype=bt.Order.Market,
                                          price=self.data0.close[0])

            self.orderid.append(self.order)

        elif self.order is not None and self.p.cancel:
            if self.datastatus > self.p.cancel:
                self.cancel(self.order)

        if self.datastatus:
            self.datastatus += 1

I really don't know what else to do with it, I'll wait for that trailing stop example to work with it a bit and see if it can be applied. just use the exact same stuff you've published here because I think it's brilliant, only the strategy needs a bit of fine tuning and it should work great. if it can't do stop loss and trailing stop in the same time, let's stick to the trailing stop only and I'll keep the fun of closing the ones that are dangerously low. I think I'm able to close them directly from the desktop app there's no need to stop it from terminal in another way right?

happydasch commented 4 years ago

you can stop it from the oanda app. also the oanda api allows to create both, a trailing stop and a stop loss order, which are filled when the parent order is executed. not really sure, if this can be done together with backtrader. the btoandav20 store could theoretically handle all orders, since it is listening for transactions from oanda and then creating the orders in backtrader.

happydasch commented 4 years ago

newest commit contains trailing stop support for bracket orders.