Overall Statistics
Total Trades
967
Average Win
1.48%
Average Loss
-1.46%
Compounding Annual Return
11.465%
Drawdown
26.700%
Expectancy
0.207
Net Profit
303.402%
Sharpe Ratio
0.796
Loss Rate
40%
Win Rate
60%
Profit-Loss Ratio
1.01
Alpha
0.057
Beta
0.65
Annual Standard Deviation
0.149
Annual Variance
0.022
Information Ratio
0.215
Tracking Error
0.11
Treynor Ratio
0.183
Total Fees
$7819.56
# Derek M Tishler - 2017
# https://tishlercapital.com/
# Basic TensorFlow Softmax Classification Example
# Based on https://www.tensorflow.org/get_started/mnist/pros

import random
import numpy as np
import pandas as pd

import tensorflow as tf

seed  = 1
random.seed(seed)
np.random.seed(seed)

class Model():

    def __init__(self):

        # len of hitory
        self.eval_lookback  = 252*4 + 1# input batch size will be eval_lookback+n_features-1 
        
        # We will feed in the past n open-to-open price changes
        self.n_features = 15
        
        # How much historical data do we need?
        self.warmup_count   = self.eval_lookback + self.n_features

        # define our tensorflow model/network
        self.network_setup()

    def network_setup(self):
        # Tensorflow Turorial does a great job(with illustrations) so comments left out here mostly: https://www.tensorflow.org/get_started/mnist/pros
        self.sess               = tf.InteractiveSession()

        self.x                  = tf.placeholder(tf.float32, shape=[None, self.n_features])
        self.y_                 = tf.placeholder(tf.float32, shape=[None, 2])

        self.W                  = tf.Variable(tf.zeros([self.n_features,2]))
        self.b                  = tf.Variable(tf.zeros([2]))

        self.y                  = tf.matmul(self.x,self.W) + self.b

        self.y_pred = tf.nn.softmax(self.y)

        self.cross_entropy      = tf.reduce_mean(
            tf.nn.softmax_cross_entropy_with_logits(labels=self.y_, logits=self.y))

        self.train_step         = tf.train.AdamOptimizer(1e-3).minimize(self.cross_entropy)

        # Some handy metric ops
        self.correct_prediction = tf.equal(tf.argmax(self.y,1), tf.argmax(self.y_,1))
        self.accuracy           = tf.reduce_mean(tf.cast(self.correct_prediction, tf.float32))
        #self.logloss            = tf.contrib.losses.log_loss(self.y_pred, self.y_)

        # This is done later vs Tensorflow Tutorial because of AdamOptimizer usage, which needs its own vars to be init'ed
        self.sess.run(tf.global_variables_initializer())

    def preproessing(self):
        
        # Create our input feature dataset and corresponding labels
        all_data = np.append(self.hist_data.open.values.flatten().astype(np.float32), self.current_price)
        features   = []
        labels     = []
        for i in range(self.n_features+1, len(all_data)-1):
            # input is change in priice
            features.append( np.diff(all_data[i-self.n_features-1:i].copy()) )
            # label is change in price from last day in input to the next day
            dp = 100.*(all_data[i+1]-all_data[i])/all_data[i]
            if dp > 0.0:
                dp = 1
            else:
                dp = 0
            labels.append(dp)
        self.features = np.array(features)
        self.labels   = np.array(labels)

        # convert to one hot for tensorflow
        oh = np.zeros((len(labels),2))
        oh[np.arange(len(labels)),labels] = 1.0
        self.labels = oh

    def train(self):
        # Perform  training step(s) and check train accuracy. This is really lame, use a test/train split and measure OOS data for good info about test/validation accuracy.
        for _ in range(1):
            self.train_step.run(session=self.sess, feed_dict={self.x: self.features, self.y_: self.labels})
        self.current_accuracy = self.accuracy.eval(session=self.sess, feed_dict={self.x: self.features, self.y_: self.labels})
        print("\nTrain LogLoss: %0.5f"%self.current_accuracy)
        

    def predict(self):
        # Perform inference
        pred_feat  =  np.append(self.hist_data.open.values.flatten().astype(np.float32), self.current_price)[-self.n_features-1:]
        pred_feat  = np.diff(pred_feat)
        pred_proba = self.y_pred.eval(session=self.sess, feed_dict={self.x: [pred_feat]})
        
        print("Forecast: Long p: %0.3f\tCashh p: %0.3f"%(pred_proba[0][0], pred_proba[0][1]))
        self.current_forecast = pred_proba[0]
        
        # Cash or Long
        return np.argmax(pred_proba[0])
        
        # Short or Long
        """if pred_proba[0][0] > 0.5:
            return -1.
        else:
            return 1."""
        
    


class BasicTemplateAlgorithm(QCAlgorithm):

    def Initialize(self):

        self.model = Model()

        self.SetStartDate(2005,1,1)  #Set Start Date
        self.SetEndDate(2017,11,1)    #Set End Date
        self.SetCash(100000)         #Set Strategy Cash
        
        # Find more symbols here: http://quantconnect.com/data
        self.symbol       = "SPY"
        self.model.symbol = self.symbol
        self.granularity  = Resolution.Minute

        self.SetBrokerageModel(BrokerageName.InteractiveBrokersBrokerage, AccountType.Margin)
        self.AddEquity(self.symbol, self.granularity)
        self.SetWarmUp(self.model.warmup_count)

        sPlot = Chart('Strategy Equity')
        sPlot.AddSeries(Series('Training_Model_Accuracy', SeriesType.Line, 2))
        sPlot.AddSeries(Series('Cash_Probability', SeriesType.Scatter, 2))
        sPlot.AddSeries(Series('Long_Probability', SeriesType.Scatter, 2))
        self.AddChart(sPlot)


        self.model.hist_data = self.History([self.symbol,], self.model.warmup_count, Resolution.Daily).astype(np.float32)
        self.do_once         = True

        # prevent order spam
        self.target          = 0.0
        
        self.Schedule.On(self.DateRules.EveryDay(self.symbol), \
            self.TimeRules.AfterMarketOpen(self.symbol), \
            Action(self.Rebalance))

    def OnData(self, data):
        if self.IsWarmingUp:
            return

    def Rebalance(self):
        
        self.model.current_price = float(self.Securities[self.symbol].Price)
        
        # Accrew history over time, is this faster than a big history call each time? (more for use in OnData when spamming call)
        if not self.do_once:
            new_hist      = self.History([self.symbol,], 1, Resolution.Daily).astype(np.float32)
            self.model.hist_data = self.model.hist_data.append(new_hist).iloc[1:] #append and pop stack   
        else:
            self.do_once  = False
        
        self.model.preproessing()
        self.model.train()
        signal = self.model.predict()
        self.Checkpoint()

        if signal != self.target:

            self.target = signal
            self.SetHoldings(self.symbol, self.target, liquidateExistingHoldings = True)


    def Checkpoint(self):
        self.Plot("Strategy Equity",'Cash_Probability', 100.*self.model.current_forecast[0])
        self.Plot("Strategy Equity",'Long_Probability', 100.*self.model.current_forecast[1])
        self.Plot("Strategy Equity",'Training_Model_Accuracy', 100.*self.model.current_accuracy)