Giter VIP home page Giter VIP logo

Comments (21)

happydasch avatar happydasch commented on June 3, 2024 2

ok, will look into this later.

from btoandav20.

happydasch avatar happydasch commented on June 3, 2024

Then your order is still pending. You will receive completed once the order is executed by oanda.

from btoandav20.

booboothefool avatar booboothefool commented on June 3, 2024

I am saying what I am seeing is that the order is completed, as in I see it go through on the Oanda dashboard/TradingView, but I never receive the notification in backtrader.

from btoandav20.

happydasch avatar happydasch commented on June 3, 2024

the order is entered (it appears in oanda) but did it execute? like did it open a position?

from btoandav20.

booboothefool avatar booboothefool commented on June 3, 2024

Yes the orders execute as in there was an open position and I won/lost money, haha.

For example:

self.E1 = self.buy(price=entry_price, size=size, exectype=bt.Order.Stop, transmit=False)
self.SL1 = self.sell(price=stoploss_price, size=size, exectype=bt.Order.Stop, transmit=False, parent=self.E1)
self.TP1 = self.sell(price=TP1_price, size=size, exectype=bt.Order.Limit, transmit=True, parent=self.E1)

E1 is Submitted -> Accepted
E1 is hit and it opens a new position (but no Completed)
TP1 is hit and won money (but no Completed)

from btoandav20.

happydasch avatar happydasch commented on June 3, 2024

could you provide some sample code which shows the problem

from btoandav20.

booboothefool avatar booboothefool commented on June 3, 2024

Goal is to try to move SL2 to breakeven after TP1 is hit. I am logging out every order notification in the beginning and it never seems to print Completed, so order.status == order.Completed logic never executes.

    def notify_order(self, order):
        logs = True
            
        logs and print('{}: Order ref: {} / Type {} / Status {} / ExecType {} / Size {} / Alive {} / Price {} / Position {}'.format(
            self.data.datetime.date(0),
            order.ref,
            'Buy' * order.isbuy() or 'Sell',
            order.getstatusname(),
            order_exectypes[order.exectype],
            order.size,
            order.alive(),
            order.executed.price,
            self.position.size,
        ))

        if order.alive():
            # Submitted, Accepted, Partial
            if order.status in (order.Partial, order.Submitted):
                # otherwise these ^ get stuck sometimes
                if self.E1 and order.ref == self.E1.ref: self.E1 = None
                elif self.E2 and order.ref == self.E2.ref: self.E2 = None
            # pass
        else:
            # Completed, Canceled, Rejected
            if self.E1 and order.ref == self.E1.ref: self.E1 = None
            elif self.SL1 and order.ref == self.SL1.ref: self.SL1 = None
                
            elif self.TP1 and order.ref == self.TP1.ref:
                self.TP1 = None

                if self.params.breakeven:
                    if order.status == order.Completed:
                        logs and print('HIT TP1')
                        if self.SL2 and self.last_entry_price:
                            # if hit TP1, move SL2 to breakeven
                            logs and print('MOVE SL2 TO BREAKEVEN', self.last_entry_price)
                            self.move_stop_loss(self.last_entry_price)
                            self.last_entry_price = None

            elif self.E2 and order.ref == self.E2.ref:
                self.E2 = None

                if self.params.breakeven:
                    if order.status == order.Completed:
                        self.last_entry_price = order.executed.price
                
            elif self.SL2 and order.ref == self.SL2.ref:
                self.SL2 = None

                if self.params.breakeven:
                    if order.status == order.Completed:
                        logs and print('CREATED NEW SL2')
                        # only when the new SL2 gets successfully created, then can we cancel the old one
                        if self.old_SL2:
                            logs and print('CANCEL OLD SL2')
                            self.old_SL2.cancel()
                            self.old_SL2 = None
                
            elif self.TP2 and order.ref == self.TP2.ref:
                self.TP2 = None

                if self.params.breakeven:
                    if order.status == order.Completed:
                        logs and print('HIT TP2')
                        # if SL2 was moved to breakeven after TP1, and therefore it is not of the original transmit group, then we now need to manually cancel it after TP2 is hit
                        if self.SL2 and self.moved_SL2:
                            logs and print('CANCEL NEW SL2')
                            self.SL2.cancel()
                            self.moved_SL2 = False

from btoandav20.

happydasch avatar happydasch commented on June 3, 2024

Goal is to try to move SL2 to breakeven after TP1 is hit. I am logging out every order notification in the beginning and it never seems to get Completed, so order.status == order.Completed logic never executes.

    def notify_order(self, order):
        logs = True
            
        logs and print('{}: Order ref: {} / Type {} / Status {} / ExecType {} / Size {} / Alive {} / Price {} / Position {}'.format(
            self.data.datetime.date(0),
            order.ref,
            'Buy' * order.isbuy() or 'Sell',
            order.getstatusname(),
            order_exectypes[order.exectype],
            order.size,
            order.alive(),
            order.executed.price,
            self.position.size,
        ))

        if order.alive():
            # Submitted, Accepted, Partial
            if order.status in (order.Partial, order.Submitted):
                # otherwise these ^ get stuck sometimes
                if self.E1 and order.ref == self.E1.ref: self.E1 = None
                elif self.E2 and order.ref == self.E2.ref: self.E2 = None
            # pass
        else:
            # Completed, Canceled, Rejected
            if self.E1 and order.ref == self.E1.ref: self.E1 = None
            elif self.SL1 and order.ref == self.SL1.ref: self.SL1 = None
                
            elif self.TP1 and order.ref == self.TP1.ref:
                self.TP1 = None

                if self.params.breakeven:
                    if order.status == order.Completed:
                        logs and print('HIT TP1')
                        if self.SL2 and self.last_entry_price:
                            # if hit TP1, move SL2 to breakeven
                            logs and print('MOVE SL2 TO BREAKEVEN', self.last_entry_price)
                            self.move_stop_loss(self.last_entry_price)
                            self.last_entry_price = None

            elif self.E2 and order.ref == self.E2.ref:
                self.E2 = None

                if self.params.breakeven:
                    if order.status == order.Completed:
                        self.last_entry_price = order.executed.price
                
            elif self.SL2 and order.ref == self.SL2.ref:
                self.SL2 = None

                if self.params.breakeven:
                    if order.status == order.Completed:
                        logs and print('CREATED NEW SL2')
                        # only when the new SL2 gets successfully created, then can we cancel the old one
                        if self.old_SL2:
                            logs and print('CANCEL OLD SL2')
                            self.old_SL2.cancel()
                            self.old_SL2 = None
                
            elif self.TP2 and order.ref == self.TP2.ref:
                self.TP2 = None

                if self.params.breakeven:
                    if order.status == order.Completed:
                        logs and print('HIT TP2')
                        # if SL2 was moved to breakeven after TP1, and therefore it is not of the original transmit group, then we now need to manually cancel it after TP2 is hit
                        if self.SL2 and self.moved_SL2:
                            logs and print('CANCEL NEW SL2')
                            self.SL2.cancel()
                            self.moved_SL2 = False

please post how you create the orders.

from btoandav20.

booboothefool avatar booboothefool commented on June 3, 2024
    def buy_risk(self):
        stop_dist = self.set_stop_dist()
        entry_dist = self.set_entry_dist()

        entry_price = self.last5high[0] + entry_dist
        stoploss_price = self.l[0] - stop_dist

        R = entry_price - stoploss_price
        R_pips_big = R * 10000
        
        TP1_price = entry_price + (1 * R)
        TP2_price = entry_price + (2 * R)
        
        size = self.calculate_risk_size(R_pips_big)
        
        print('SL %.4f / Price %.4f / BuyStop %.4f / R=%.4f %.4f / TP1=%.4f / TP2=%.4f / size %.4f / Stop Dist %.4f' % (
            stoploss_price,
            self.c[0],
            entry_price,
            R, R_pips_big,
            TP1_price,
            TP2_price,
            size,
            stop_dist,
        ))
                        
        self.E1 = self.buy(price=entry_price, size=size, exectype=bt.Order.Stop, transmit=False)
        self.SL1 = self.sell(price=stoploss_price, size=size, exectype=bt.Order.Stop, transmit=False, parent=self.E1)
        self.TP1 = self.sell(price=TP1_price, size=size, exectype=bt.Order.Limit, transmit=True, parent=self.E1)
        
        self.E2 = self.buy(price=entry_price, size=size, exectype=bt.Order.Stop, transmit=False)
        self.SL2 = self.sell(price=stoploss_price, size=size, exectype=bt.Order.Stop, transmit=False, parent=self.E2)
        self.TP2 = self.sell(price=TP2_price, size=size, exectype=bt.Order.Limit, transmit=True, parent=self.E2)

from btoandav20.

happydasch avatar happydasch commented on June 3, 2024

for now you could try if bracket orders will work with your code

from btoandav20.

happydasch avatar happydasch commented on June 3, 2024

I could not really replicate your issue. I entered these values for the orders:

        self.E1 = self.buy(price=self.data.close[0], size=100, exectype=bt.Order.Stop, transmit=False)
        self.SL1 = self.sell(price=self.data.close[0] + 0.0005, size=100, exectype=bt.Order.Stop, transmit=False, parent=self.E1)
        self.TP1 = self.sell(price=self.data.close[0] - 0.0002, size=100, exectype=bt.Order.Limit, transmit=True, parent=self.E1)

but these orders get canceled. you can use the code at the bottom to provide a strategy which replicates your issue.

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

# For datetime objects
import datetime
import pytz

# Import the backtrader platform
import backtrader as bt
import btoandav20 as bto
import backtrader.version as btvers



print(btvers.__version__)



class strategy(bt.Strategy):

    '''Initialization '''
    def __init__(self):
        self.live = True

    def log(self, txt, dt=None):
        dt = dt or self.data.datetime.datetime(0)
        print('%s, %s' % (dt.isoformat(), txt))

    ''' Store notification '''
    def notify_store(self, msg, *args, **kwargs):
        print('-' * 50, 'STORE BEGIN', datetime.datetime.now())
        print(msg)
        print('-' * 50, 'STORE END')

    ''' Order notification '''
    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')

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

        if not trade.isclosed:
            return

        self.log('OPERATION PROFIT, GROSS %.2f, NET %.2f' %
            (trade.pnl, trade.pnlcomm))

    ''' Data notification '''
    def notify_data(self, data, status, *args, **kwargs):
        print('*' * 5, 'DATA NOTIF:', data._getstatusname(status), *args)
        if status == data.LIVE:
            self.live = True
        elif status == data.DELAYED:
            self.live = False

    def next(self):
        self.log('Next {}, {} {} {} {}'.format(len(self.data), self.data.open[0], self.data.high[0], self.data.low[0], self.data.close[0]))
        print(self.position)
        if not self.position and self.live:

            self.E1 = self.buy(price=self.data.close[0], size=100, exectype=bt.Order.Stop, transmit=False)
            self.SL1 = self.sell(price=self.data.close[0] + 0.0005, size=100, exectype=bt.Order.Stop, transmit=False, parent=self.E1)
            self.TP1 = self.sell(price=self.data.close[0] - 0.0002, size=100, exectype=bt.Order.Limit, transmit=True, parent=self.E1)

        elif self.live:
            #self.close()
            pass

# Create a cerebro entity
cerebro = bt.Cerebro(quicknotify=True)

# Prepare oanda STORE
storekwargs = dict(
    token="",
    account="",
    practice=True,
)
print(bto.stores)
store = bto.stores.OandaV20Store(**storekwargs)

# Prepare oanda data
datakwargs = dict(
    timeframe=bt.TimeFrame.Seconds,
    compression=5,
    tz=pytz.timezone('Europe/Berlin'),
    backfill=False,
    backfill_start=False,
)
data = store.getdata(dataname="EUR_USD", **datakwargs)
#data.resample(timeframe=bt.TimeFrame.Seconds, compression=30,rightedge=True,boundoff=1)
cerebro.adddata(data)

# Add broker
cerebro.setbroker(store.getbroker())

# Add strategy
cerebro.addstrategy(strategy)

cerebro.run()

from btoandav20.

happydasch avatar happydasch commented on June 3, 2024

Also please Check if the both stop orders (one with entry price) are really stop orders or if the first one should be a limit order.

from btoandav20.

booboothefool avatar booboothefool commented on June 3, 2024

Hi if you did:

        self.E1 = self.buy(price=self.data.close[0], size=100, exectype=bt.Order.Stop, transmit=False)
        self.SL1 = self.sell(price=self.data.close[0] + 0.0005, size=100, exectype=bt.Order.Stop, transmit=False, parent=self.E1)
        self.TP1 = self.sell(price=self.data.close[0] - 0.0002, size=100, exectype=bt.Order.Limit, transmit=True, parent=self.E1)

It is a Buy position, so SL1 stop loss should be below, TP1 take profit should be above. So maybe just:

        self.E1 = self.buy(price=self.data.close[0] + 0.0005, size=100, exectype=bt.Order.Stop, transmit=False)
        self.SL1 = self.sell(price=self.data.close[0] - 0.0005, size=100, exectype=bt.Order.Stop, transmit=False, parent=self.E1)
        self.TP1 = self.sell(price=self.data.close[0] + 0.0010, size=100, exectype=bt.Order.Limit, transmit=True, parent=self.E1)

And yes, it should be:

  • Entry: Buy Stop (to buy when price goes up and hits the price)
  • Stop Loss: Sell Stop (below)
  • Take Profit: Sell Limit (above)

But from what I have seen, it doesn't matter what the order type is, I never see Completed status when running live even with regular Market order instead of Stop order e.g. self.E1 = self.buy(size=100, exectype=bt.Order.Market, transmit=False).

from btoandav20.

happydasch avatar happydasch commented on June 3, 2024

I am not really sure, how to send this type of orders. When sending your orders, oanda will reject it with CLIENT_ORDER_ID_ALREADY_EXISTS

from btoandav20.

happydasch avatar happydasch commented on June 3, 2024

i committed some changes today, you can check them out. you will get better error codes and descriptions for further investigation. the issue seems to be that either the stores creates the stop order wrong or there is an other issue.

CLIENT_ORDER_ID_ALREADY_EXISTS: The client Order ID specified is already assigned to another pending Order

these are the generated order details:

{'instrument': 'EUR_USD',
 'units': 100,
 'type': 'STOP',
 'price': '1.11979',
 'timeInForce': 'GTC',
 'stopLossOnFill': {'price': '1.11879', 'timeInForce': 'GTC', 'clientExtensions': {'id': '2'}},
 'takeProfitOnFill': {'price': '1.12029', 'timeInForce': 'GTC', 'clientExtensions': {'id': '3'}},
 'clientExtensions': {'id': '1'}}

the clientExtensions id is the id of the order in backtrader.

you can check out https://developer.oanda.com/rest-live-v20/transaction-df/ for the needed details for the type of orders you would like to submit.

from btoandav20.

happydasch avatar happydasch commented on June 3, 2024

Would need a better explanation of what you want to achieve. Will wait for more details.

from btoandav20.

booboothefool avatar booboothefool commented on June 3, 2024

Thanks, I appreciate you exposing the errors! Ok, I will look into this more. Yes, I've been struggling so much with the manual brackets that I just switched to Market orders, and it turns out my algorithm actually performs better...

I heard back from Oanda support. They said what you said:

1) Transaction no.: 40749

Reason for Stop Order Reject: TRAILING_STOP_LOSS_ON_FILL_PRICE_DISTANCE_MINIMUM_NOT_MET
*Please note that Trailing Stop must be at least 5 pips away however you had it at 3 pips away. Hence, it was rejected

2) Transaction no.: 40748, 40747

Reason for Stop Order Reject: CLIENT_ORDER_ID_ALREADY_EXISTS

*Wet cannot create a new Stop order using an order ID that already exists from past orders. Please  use a new order ID. (e.g Transaction #: 40747:  You are trying to create a Stop order with ID 25 but the order ID 25 already exists from past transaction.).

As for more explanation/details, was basically just trying to do the risk management strategy as shown here: https://youtu.be/zhEukjCzXwM?t=747

from btoandav20.

happydasch avatar happydasch commented on June 3, 2024

we could ensure, that the id is unique, so adding some unique key for id, which changes when you restart the script

from btoandav20.

happydasch avatar happydasch commented on June 3, 2024

As for more explanation/details, was basically just trying to do the risk management strategy as shown here: https://youtu.be/zhEukjCzXwM?t=747

for that, you could achieve this by setting limit orders in opposite direction. you would need to cancel the order once you would set a new order when the price reaches new high (or low).

from btoandav20.

happydasch avatar happydasch commented on June 3, 2024

added a unique client id, now you will not get CLIENT_ORDER_ID_ALREADY_EXISTS messages, for the other issues, Stop orders work, will close this issue.

from btoandav20.

einnairo avatar einnairo commented on June 3, 2024

@ booboothefool
I did not fully go through your post here, but today I will open an issue but you can check my code, I think it is something along the lines of what u want to do.

from btoandav20.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.