Overall Statistics
Total Trades
66
Average Win
0.76%
Average Loss
-1.04%
Compounding Annual Return
-51.820%
Drawdown
13.800%
Expectancy
0.118
Net Profit
-5.033%
Sharpe Ratio
-1.323
Probabilistic Sharpe Ratio
21.041%
Loss Rate
35%
Win Rate
65%
Profit-Loss Ratio
0.73
Alpha
0.059
Beta
-0.486
Annual Standard Deviation
0.358
Annual Variance
0.128
Information Ratio
-3.541
Tracking Error
0.443
Treynor Ratio
0.975
Total Fees
$0.00
import clr
clr.AddReference("System")
clr.AddReference("QuantConnect.Algorithm")
clr.AddReference("QuantConnect.Common")

from System import *
from QuantConnect import *
from QuantConnect.Algorithm import *

import numpy as np
import torch
import torch.nn.functional as F

class PytorchNeuralNetworkAlgorithm(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2020, 5, 7)  # Set Start Date
        #self.SetEndDate(2020, 10, 8) # Set End Date
        #self.SetBrokerageModel(BrokerageName.OandaBrokerage)
        self.SetCash(1000000)  # Set Strategy Cash
        
        # add symbol
        spy = self.AddForex("EURUSD", Resolution.Minute, Market.Oanda)
        spy1 = self.AddForex("USDCHF", Resolution.Minute, Market.Oanda)
        gug = self.AddForex("GBPUSD", Resolution.Minute, Market.Oanda)
        spy2 = self.AddForex("EURGBP", Resolution.Minute, Market.Oanda)
        #spy3 = self.AddForex("AUDUSD", Resolution.Minute, Market.Oanda)
        #spy4 = self.AddForex("EURAUD", Resolution.Minute, Market.Oanda)
        #spy5 = self.AddForex("AUDCHF", Resolution.Minute, Market.Oanda)
        #spy6 = self.AddForex("USDJPY", Resolution.Minute, Market.Oanda)
        #spy7 = self.AddForex("GBPJPY", Resolution.Minute, Market.Oanda)
        #spy8 = self.AddForex("AUDJPY", Resolution.Minute, Market.Oanda)
        #spy9 = self.AddForex("GBPAUD", Resolution.Minute, Market.Oanda)
        
        self.symbols = [spy.Symbol, spy1.Symbol, gug.Symbol, spy2.Symbol]#, spy3.Symbol, spy4.Symbol, spy5.Symbol, spy6.Symbol, spy7.Symbol, spy8.Symbol, spy9.Symbol]# using a list can extend to condition for multiple symbols# , 
        
        self.lookback = 1440 # days of historical data (look back)
        
        self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday, DayOfWeek.Tuesday,  DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday), self.TimeRules.Every(timedelta(minutes=480)), self.NetTrain) # train the NNself.TimeRules.AfterMarketOpen("EURUSD", 175), self.NetTrain) 
        self.Schedule.On(self.DateRules.Every(DayOfWeek.Monday, DayOfWeek.Tuesday,  DayOfWeek.Wednesday, DayOfWeek.Thursday, DayOfWeek.Friday), self.TimeRules.Every(timedelta(minutes=480)), self.Trade)# self.TimeRules.AfterMarketOpen("EURUSD", 179), self.Trade)
        
        self.SetWarmUp(self.lookback)
        stockPlot1 = Chart("Trade Plot")
        stockPlot1.AddSeries(Series("guess", SeriesType.Line, 0))
        stockPlot1.AddSeries(Series("actual", SeriesType.Line, 0))
        #stockPlot1.AddSeries(Series("rsiusd", SeriesType.Line, 0))
        #stockPlot1.AddSeries(Series("liquidate", SeriesType.Line, 0))
        #stockPlot1.AddSeries(Series("rsijpy", SeriesType.Line, 0))
        #stockPlot1.AddSeries(Series("rsijpy1", SeriesType.Line, 0))
        #stockPlot1.AddSeries(Series("rsijpy4", SeriesType.Line, 0))
        self.AddChart(stockPlot1)
    def Ondata(self, data):
        if self.IsWarmingUp: return
        
    def NetTrain(self):
        # Daily historical data is used to train the machine learning model
        history = self.History(self.symbols, self.lookback + 1, Resolution.Minute)
        
        # dicts that store prices for training
        self.prices_x = {} 
        self.prices_y = {}
        
        # dicts that store prices for sell and buy
        self.sell_prices = {}
        self.buy_prices = {}
        
        for symbol in self.symbols:
            if not history.empty:
                # x: preditors; y: response
                self.prices_x[symbol] = list(history.loc[symbol.Value]['open'])[:-1]
                self.prices_y[symbol] = list(history.loc[symbol.Value]['open'])[1:]
                
        for symbol in self.symbols:
            # if this symbol has historical data
            if symbol in self.prices_x:
                
                net = Net(n_feature=1, n_hidden=10, n_output=1)     # define the network
                optimizer = torch.optim.SGD(net.parameters(), lr=0.2)
                loss_func = torch.nn.MSELoss()  # this is for regression mean squared loss
                
                for t in range(200):
                    # Get data and do preprocessing
                    x = torch.from_numpy(np.array(self.prices_x[symbol])).float()
                    y = torch.from_numpy(np.array(self.prices_y[symbol])).float()
                    
                    # unsqueeze data (see pytorch doc for details)
                    x = x.unsqueeze(1) 
                    y = y.unsqueeze(1)
                
                    prediction = net(x)     # input x and predict based on x

                    loss = loss_func(prediction, y)     # must be (1. nn output, 2. target)

                    optimizer.zero_grad()   # clear gradients for next train
                    loss.backward()         # backpropagation, compute gradients
                    optimizer.step()        # apply gradients
            
            # Follow the trend    
            self.buy_prices[symbol] = net(y)[-1] + np.std(y.data.numpy())
            self.sell_prices[symbol] = net(y)[-1] - np.std(y.data.numpy())
            
    def Trade(self):
        ''' 
        Enter or exit positions based on relationship of the open price of the current bar and the prices defined by the machine learning model.
        Liquidate if the open price is below the sell price and buy if the open price is above the buy price 
        ''' 
        
        for holding in self.Portfolio.Values:
            
            if self.CurrentSlice[holding.Symbol].Open < self.sell_prices[holding.Symbol] and not holding.Invested:
                self.Plot("Trade Plot", "guess", self.sell_prices[holding.Symbol])
                self.SetHoldings(holding.Symbol, 10 / len(self.symbols))
                
            elif self.CurrentSlice[holding.Symbol].Open > self.buy_prices[holding.Symbol] and not holding.Invested:
                self.SetHoldings(holding.Symbol, -10 / len(self.symbols))
                    
            elif holding.Invested:
                if self.CurrentSlice[holding.Symbol].Open < self.sell_prices[holding.Symbol] and holding.IsShort:
                    self.Liquidate(holding.Symbol)
                if self.CurrentSlice[holding.Symbol].Open > self.buy_prices[holding.Symbol] and holding.IsLong:
                    self.Liquidate(holding.Symbol)
            self.Plot("Trade Plot", "actual", self.CurrentSlice["EURUSD"].Open)
            
# class for Pytorch NN model
class Net(torch.nn.Module):
    def __init__(self, n_feature, n_hidden, n_output):
        super(Net, self).__init__()
        self.hidden = torch.nn.Linear(n_feature, n_hidden)   # hidden layer
        self.predict = torch.nn.Linear(n_hidden, n_output)   # output layer
    
    def forward(self, x):
        x = F.relu(self.hidden(x))      # activation function for hidden layer
        x = self.predict(x)             # linear output
        return x