Overall Statistics
Total Trades
25
Average Win
0.96%
Average Loss
-1.06%
Compounding Annual Return
-0.148%
Drawdown
3.700%
Expectancy
0.112
Net Profit
-0.148%
Sharpe Ratio
0.01
Probabilistic Sharpe Ratio
13.497%
Loss Rate
42%
Win Rate
58%
Profit-Loss Ratio
0.91
Alpha
-0.006
Beta
0.036
Annual Standard Deviation
0.06
Annual Variance
0.004
Information Ratio
-0.667
Tracking Error
0.295
Treynor Ratio
0.018
Total Fees
$0.00
Estimated Strategy Capacity
$360000.00
Lowest Capacity Asset
AUDCAD 8G
import json

class HeikinAshiBollingerBand(QCAlgorithm):
    
    def Initialize(self):
        self.SetStartDate(2020, 1, 1)
        self.SetEndDate(2020, 12, 31)
        self.SetCash(100000)
        self.SetWarmUp(10)
        
        self.fx = self.AddForex('AUDCAD', Resolution.Daily, Market.Oanda).Symbol      
        
        self.ha = self.HeikinAshi(self.fx)
        self.bb = IndicatorExtensions.Of(self.BB(self.fx, 10, 2), self.ha)
        self.store = {'log': [], 'orders': []}
        self.order = None
        
    def OnData (self, data):
        if self.IsWarmingUp or not(self.ha.IsReady or self.bb.IsReady): return
    
        pb = round(self.bb.PercentB.Current.Value, 2)
        
        self.store['log'].append({
            'time':    str(self.Time),
            'open':    data[self.fx].Open,
            'high':    data[self.fx].High,
            'low':     data[self.fx].Low,
            'close':   data[self.fx].Close,
            'haopen':  self.ha.Open.Current.Value,
            'hahigh':  self.ha.High.Current.Value,
            'halow':   self.ha.Low.Current.Value,
            'haclose': self.ha.Close.Current.Value,
            'price':   self.bb.Price.Current.Value,
            'upper':   self.bb.UpperBand.Current.Value,
            'middle':  self.bb.MiddleBand.Current.Value,
            'lower':   self.bb.LowerBand.Current.Value,
            'stdev':   self.bb.StandardDeviation.Current.Value,
            'pb':      pb
        })
            
        if not self.Portfolio[self.fx].Invested:
            if pb < 0:  
                self.order = self.SetHoldings(self.fx, 1, False, f'ENTER long pb {pb}')
            elif pb > 1:
                self.order = self.SetHoldings(self.fx, -1, False, f'ENTER short pb {pb}')
                
        else:
            isLong  = self.Portfolio[self.fx].IsLong
            isShort = self.Portfolio[self.fx].IsShort

            if (isLong and pb > 1):
                self.Liquidate(self.fx, f'EXIT long pb {pb}')
            elif (isShort and pb < 0):
                self.Liquidate(self.fx, f'EXIT short pb {pb}')
                
    def OnOrderEvent(self, orderEvent):
        order = self.Transactions.GetOrderById(orderEvent.OrderId)
        ticket = self.Transactions.GetOrderTicket(orderEvent.OrderId)
        
        if orderEvent.Status == OrderStatus.Filled:
            self.store['orders'].append({
                'time':     str(ticket.Time),
                'symbol':   self.fx.Value,
                'type':     order.Type,
                'price':    ticket.AverageFillPrice,
                'quantity': ticket.QuantityFilled,
                'status':   order.Status,
                'value':    int(ticket.AverageFillPrice * ticket.QuantityFilled),
                'tag':      ticket.Tag,
                'direction': order.Direction
            })                
                
    def OnEndOfAlgorithm(self):
        self.ObjectStore.Save('log', json.dumps(self.store['log']))
        self.ObjectStore.Save('orders', json.dumps(self.store['orders']))