Hi all
I managed to make the backtest work. My logic is that I am using my predictions of close, high and low based on a machine learning model to decide whether I am buying EURUSD, selling or both in a 1 hour timeframe. I want the take profit to be 80% of my predicted high and low respectively. The stop loss I want to be 200% of the difference between open and high and open and low respectively.
I am obviously newbie, so I am not sure if the code does what I want it to do. The returns are negative but my predictions are 0.1% accurate.
I set resolution to be .Hour so I assume onData runs every hour. Is this correct or am I better off with
self.Schedule.On
? If so, how can I say run every hour?
Also there is an OnOrderEvent that I assume runs whenever an order (Market, Limit or Stop) is made. I took this code from another thread in the forum.
Another issue I have is with price. I found in a thread that it is
price = float(data["EURUSD"].Price)
Ideally I would like the price that my order was filled with because I want to set Take Profit and Stop Loss to be based on that.
There are a lot of self.log because I wanted a way to see what I am doing for debugging.
Any help will be appreciated.
Georgios Kourogiorgas
I mean 0.1% error
Halldor Andersen
Hi Georgios
.Welcome to QuantConnect.To ensure that your custom data is in sync with other data in the security universe, create a custom type. For further information, check out the documentation section on importing custom data.
In the attached backtest, I've demonstrated how you can import your custom data using the method I described. The logic in your strategy is the same as before to produce a buy signal. However, you need to add the other leg of the strategy to emit a sell signal. Also, I've demonstrated how to use an average fill price for take-profit and stop-loss order instead of using the current price.The following code snippet in the attached backtest will make sure you get hourly data in OnData():
# Add custom data to security universe self.AddData(predEURUSD, "predEURUSD", Resolution.Hour) # Add EUR/USD currency pair to security universe self.AddForex("EURUSD", Resolution.Hour, Market.Oanda)
The hourly data resolution can be changed to daily data resolution by setting Resolution.Hour to Resolution.Daily. Good luck!
Georgios Kourogiorgas
  Halldor Andersen thank you very much for your help. I didn't know that importing data as custom data is better.
My remaining questions are:
1. How do I set quantity to buy as % of balance? InÂ
self.Buy("EURUSD", 100)
what is the unit of measurement of "100"?
2. How can I have buy and sell orders open at the same time?
3. Is the code for bracket orders working?
Thanks in advance
ÂHalldor Andersen
Hi Georgios.
1. Quantity of 100 means that you are buying 100 Euros in exchange for U.S. dollars. You can use the method CalculateOrderQuantity() to calculate quantity with a specific portfolio value allocation target in mind. For example, to buy Euros for 50% of the value of your portfolio:
quantity = self.CalculateOrderQuantity("EURUSD",0.5) self.Buy("EURUSD", quantity)
You can also use SetHoldings:
self.SetHoldings("EURUSD",0.5)
2. You can submit buy order and sell order simultaneously. However, you will have a net exposure to security, depending on the size of your orders.
3. The following code snippet that you are using in your code will cancel the remaining order if either stop-loss or take-profit is triggered, so the "bracket" orders should work.
def OnOrderEvent(self, orderEvent): order = self.Transactions.GetOrderById(orderEvent.OrderId) if order.Status == OrderStatus.Filled: if order.Type == OrderType.Limit or order.Type == OrderType.Limit: self.Transactions.CancelOpenOrders(order.Symbol) if order.Status == OrderStatus.Canceled: self.Log(str(orderEvent))
Â
Georgios Kourogiorgas
@Halldor Andersen thanks again for your answers. I tried to attach the latest backtest with relatively good results after incorporating your feedback but the app hangs.
However, I think that the orders are executed not in the way I intend. It seems to me that stop loss and take profit are taken into consideration while the order remains unfilled. I want the order to be like in Metatrader, where a position is opened and closed when the stop loss or take profit level is reached or when there is not enough fund due to opposite direction.
Have I got it right?
Georgios Kourogiorgas
Here is the code because I can't make attaching backtest work
import numpy as np import pandas as pd from QuantConnect.Data import SubscriptionDataSource from QuantConnect.Python import PythonData from datetime import date, timedelta, datetime from decimal import Decimal ### <summary> ### Basic template algorithm simply initializes the date range and cash. This is a skeleton ### framework you can use for designing an algorithm. ### </summary> class BasicTemplateAlgorithm(QCAlgorithm): '''Basic template algorithm simply initializes the date range and cash''' def Initialize(self): '''Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.''' self.SetStartDate(2017,1, 10) #Set Start Date self.SetEndDate(2018,12,31) #Set End Date self.SetCash(10000) #Set Strategy Cash # Find more symbols here: http://quantconnect.com/data self.AddForex("EURUSD", Resolution.Hour, Market.Oanda) self.AddData(predEURUSDASK, "predEURUSDASK", Resolution.Hour) self.AddData(predEURUSDBID, "predEURUSDBID", Resolution.Hour) self.SetTimeZone("Etc/GMT0") #self.Schedule.On(self.DateRules.EveryDay("EURUSD"), self.TimeRules.Every("Hour"), self.EveryHour) self.spmulti = self.GetParameter("SPmulti") self.tpmulti = self.GetParameter("TPmulti") self.Debug("Everything loaded") def OnData(self, data): '''OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here. Arguments: data: Slice object keyed by symbol containing the stock data ''' if not data.ContainsKey("predEURUSDASK") or not data.ContainsKey("predEURUSDBID") or not data.ContainsKey("EURUSD"): return quoteBar = data['EURUSD'] self.Log(f"Time: {quoteBar.EndTime}") #The time the period closes self.Log(f"Open: {quoteBar.Open}") # Retrieve High Ask Price from object highASK=data["predEURUSDASK"].Value self.Log(f"High ASK: {highASK}") # User variables highASKOpendiff = highASK- quoteBar.Open self.Log(f"highASKOpendiff: {highASKOpendiff}") BuyTPASK=highASKOpendiff*0.8 self.Log(f"BuyTPASK: {BuyTPASK}") BuySLASK=highASKOpendiff*5 self.Log(f"BuySLASK: {BuySLASK}") # Retrieve Low Bid Price from object lowBID=data["predEURUSDBID"].Value self.Log(f"Low BID: {lowBID}") # User variables OpenLowBIDdiff = quoteBar.Open - lowBID self.Log(f"OpenLowBIDdiff: {OpenLowBIDdiff}") SellTPBID=OpenLowBIDdiff*0.8 self.Log(f"SellTPBID: {SellTPBID}") SellSLBID=OpenLowBIDdiff*5 self.Log(f"SellSLBID: {SellSLBID}") #if sum(highASK.notna()) >=1 & sum(lowBID.notna()) >=1 : if highASKOpendiff > 0.0015: self.Buy("EURUSD", 1000) averageFillPrice = self.Portfolio["EURUSD"].AveragePrice self.LimitOrder("EURUSD", -1000, (averageFillPrice +BuyTPASK)) self.StopMarketOrder("EURUSD", -1000, (averageFillPrice -BuySLASK)) if OpenLowBIDdiff > 0.0015: self.Sell("EURUSD", 1000) averageFillPrice = self.Portfolio["EURUSD"].AveragePrice self.LimitOrder("EURUSD", 1000, (averageFillPrice -SellTPBID)) self.StopMarketOrder("EURUSD", 1000, (averageFillPrice + SellSLBID)) def OnOrderEvent(self, orderEvent): order = self.Transactions.GetOrderById(orderEvent.OrderId) if order.Status == OrderStatus.Filled: if order.Type == OrderType.Limit or order.Type == OrderType.StopMarket: self.Transactions.CancelOpenOrders(order.Symbol) if order.Status == OrderStatus.Canceled: self.Log(str(orderEvent)) def EveryHour(self): pass class predEURUSDASK(PythonData): #"Import custom data from a csv file" def GetSource(self, config, date, isLiveMode): return SubscriptionDataSource("http://sensedrive.science/files/EURUSD1H_CHL_ASK_cat.csv", SubscriptionTransportMedium.RemoteFile) def Reader(self, config, line, date, isLiveMode): if not (line.strip() and line[0].isdigit()): return None index = predEURUSDASK() index.Symbol = config.Symbol try: data = line.split(',') index.Time = datetime.strptime(data[0], "%Y-%m-%d %H:%M:%S") ### Retrieve "predicted" High Ask Price index.Value = Decimal(data[2]) except: return None return index class predEURUSDBID(PythonData): #"Import custom data from a csv file" def GetSource(self, config, date, isLiveMode): return SubscriptionDataSource("http://sensedrive.science/files/EURUSD1H_CHL_BID_cat.csv", SubscriptionTransportMedium.RemoteFile) def Reader(self, config, line, date, isLiveMode): if not (line.strip() and line[0].isdigit()): return None index = predEURUSDBID() index.Symbol = config.Symbol try: data = line.split(',') index.Time = datetime.strptime(data[0], "%Y-%m-%d %H:%M:%S") ### Retrieve "predicted" High Ask Price index.Value = Decimal(data[2]) except: return None return index
Â
Halldor Andersen
Hi Georgios.
It seems that your custom data is no longer available in the cloud.
These two links are not active:
http://sensedrive.science/files/EURUSD1H_CHL_ASK_cat.csv
http://sensedrive.science/files/EURUSD1H_CHL_BID_cat.csv
Lack of data is currently the reason why the algorithm places no trades.
Georgios Kourogiorgas
The material on this website is provided for informational purposes only and does not constitute an offer to sell, a solicitation to buy, or a recommendation or endorsement for any security or strategy, nor does it constitute an offer to provide investment advisory services by QuantConnect. In addition, the material offers no opinion with respect to the suitability of any security or specific investment. QuantConnect makes no guarantees as to the accuracy or completeness of the views expressed in the website. The views are subject to change, and may have become unreliable for various reasons, including changes in market conditions or economic circumstances. All investments involve risk, including loss of principal. You should consult with an investment professional before making any investment decisions.
To unlock posting to the community forums please complete at least 30% of Boot Camp.
You can continue your Boot Camp training progress from the terminal. We hope to see you in the community soon!