Back to Community
HELP: Simple Heiken Ashi momentum trader

Hey guys, I'm new to the game of algorithms, and I'm also new to the world of development.

I was hoping to get your help on getting started with algorithms. I have an idea for something I want to follow, but I have no idea where to begin to implement the idea. Anyway, here goes:

Indicator used: Heiken Ashi (Japanese Candlesticks)
Type: Momentum trades
Criteria: When there are two bear/red candles, sell the stock. When there is a doji followed by a bull candle, buy the stock. When there is a doji, followed by a bear candle, sell the stock. VOLUME >= Average VOL 3 months.

I use Questrade (don't know if that's relevant or not) and would eventually like to implement my program into it.

Thank you for all your help!

6 responses

I was working on something, might be a start for your quest. Caveat emptor: I'm not a super coder

Clone Algorithm
62
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
# Heikin-Ashi Rules
# Buy Rules
# If the close of the monthly bar is green (Close greater than Open)
# Enter on the open of the next monthly bar
# Sell Rule
 
# If the close of the monthly bar is red (Close less than Open)
# Exit on the open of the next monthly bar



# Put any initialization logic here.  The context object will be passed to
# the other methods in your algorithm.
def initialize(context):
    symbol('SPY')
    schedule_function(heikin,date_rule=date_rules.week_start(), time_rule=time_rules.market_open(hours=0,minutes=1 ))
    context.xOpens =[]
    context.xCloses =[]
    context.xHighs=[]
    context.xLows =[]
# Will be called on every trade event for the securities you specify. 
def handle_data(context, data):
    pass
                      
def heikin(context, data):
    closes = history(200, '1d', 'close_price').resample('W',how='last')
    highs = history(200, '1d', 'close_price').resample('W',how='max')
    lows = history(200, '1d', 'close_price').resample('W',how='min')
    opens = history(200, '1d', 'close_price').resample('W',how='first')
    volumes = history(200, '1d', 'volume').resample('W',how='mean')
    dvs = closes*volumes
   
    for stock in data:
        
        xClose = (opens[stock][-2]+highs[stock][-2]+lows[stock][-2]+closes[stock][-2])/4 #Average price of the current bar      
        if len(context.xOpens)>0:
            xOpen=(context.xOpens[0] + closes[stock][-3])/2
        else:
            xOpen=(opens[stock][-3] + closes[stock][-3])/2 #Midpoint of the previous bar
        context.xOpens.append(xOpen)
        xHigh = max(highs[stock][-2], xOpen, xClose) #Highest value in the set
        xLows = min(lows[stock][-2], xOpen, xClose) 
        context.xLows.append(xLows)
        context.xCloses.append(xClose)
        context.xHighs.append(xHigh)

        # Buy Rules
        # If the close of the monthly bar is green (Close greater than Open)
        # Enter on the open of the next monthly bar
        if xClose > 1.01*xOpen:
             order_target_percent((sid(8554)), 1)
        elif xClose < 1.01*xOpen:
        # Sell Rule
        #If the close of the monthly bar is red (Close less than Open)
        #Exit on the open of the next monthly bar
            order_target_percent((sid(8554)), 0)
   

    
    
#     There are five primary signals that identify trends and buying opportunities:

# Hollow candles with no lower "shadows" indicate a strong uptrend: let your profits ride!
# Hollow candles signify an uptrend: you might want to add to your long position, and exit short positions.
# One candle with a small body surrounded by upper and lower shadows indicates a trend change: risk-loving traders might buy or sell here, while others will wait for confirmation before going short or long.
# Filled candles indicate a downtrend: you might want to add to your short position, and exit long positions.
# Filled candles with no higher shadows identify a strong downtrend: stay short until there's a change in trend.
There was a runtime error.

@Peter B., Hey thanks for the the impetus. I had to dig up my old CQG HA bar code to see how that stuff worked.

I never knew about the pandas resample parameter "how", that's pretty trick.

Below is a variation that tries to implement the original poster's logic regarding reds and greens and dojis.

@Ishtar C., The "doji" code is just a guess, but in the plot you see there there were actual dojis where the haOpen and haClose were less than .1 % separated. If you want tighter dojis then you'll need to squeeze the value further. Also, the volume test is removed in this example, it really knocks down the returns. You can put it back by removing the comment in HandleHAEntry.

Clone Algorithm
94
Loading...
Backtest from to with initial capital
Total Returns
--
Alpha
--
Beta
--
Sharpe
--
Sortino
--
Max Drawdown
--
Benchmark Returns
--
Volatility
--
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
import zipline

def initialize(context):
    symbol('SPY')
    context.S = {}
    schedule_function(ProcessHABars)
    schedule_function(AnalyzeHABars)
    schedule_function(CalculateVolume)
    schedule_function(HandleHAExit)
    schedule_function(HandleHAEntry)

def handle_data(context, data):
    record(Leverage = context.account.leverage)
    for stock in data:
        if (stock not in context.S):
            context.S[stock] = zipline.protocol.SIDData(stock)
            context.S[stock].HAOpen  = []
            context.S[stock].HAHigh  = []
            context.S[stock].HALow   = []
            context.S[stock].HAClose = []
            context.S[stock].IsVolumeUp  = False
            context.S[stock].IsDoji      = False
            context.S[stock].IsDojiPrior = False
            
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~            
def HandleHAEntry(context, data):
    
    # Criteria: When there are two bear/red candles, sell the stock. 
    # When there is a doji followed by a bull candle, buy the stock. 
    # When there is a doji, followed by a bear candle, sell the stock. 
    # VOLUME >= Average VOL 3 months.
    
    positions = context.portfolio.positions
    openPositions = [stock for stock in positions if positions[stock].amount != 0]
    eligible = []
    for stock in context.S:
        if (stock in openPositions):
            continue
        if (get_open_orders(stock)):
            continue
        if (len(context.S[stock].HAOpen) < 2):
            continue
        if (context.S[stock].HAClose[-1] < context.S[stock].HAOpen[-1]):
            continue
        #
        # Enter if green and prior doji and volume is up
        #
        if (context.S[stock].IsDojiPrior): # and context.S[stock].IsVolumeUp):
            eligible.append(stock)

    eligible += openPositions                    
    eligibleCount = float(len(eligible))
    for stock in eligible:
        order_target_percent(stock, (1.0 / eligibleCount))
        if (positions[stock].amount == 0):
            print("HA entry >>> {0:<6} @ {1:>7.2f}".format(stock.symbol, data[stock].close_price))
            
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~            
def HandleHAExit(context, data):
    positions = context.portfolio.positions
    for stock in context.S:
        if (get_open_orders(stock)):
            continue
        if (len(context.S[stock].HAOpen) < 2):
            continue
        if (context.S[stock].HAClose[-1] > context.S[stock].HAOpen[-1]):
            continue
        if (not context.S[stock].IsDojiPrior or context.S[stock].HAClose[-2] > context.S[stock].HAOpen[-2]):
            continue
        #
        # Exit if red and prior Doji, or if red red
        #
        if (positions[stock].amount != 0):
            order_target_percent(stock, 0.0)            
            print("<<< HA exit  {0:<6} @ {1:>7.2f}".format(stock.symbol, data[stock].close_price))

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def AnalyzeHABars(context, data):
    for stock in context.S:
        sidData = context.S[stock]
        sidData.IsDojiPrior = sidData.IsDoji
        if (abs(sidData.HAOpen[-1] - sidData.HAClose[-1]) / sidData.HAClose[-1] <= .001):
            sidData.IsDoji = True
        else:
            sidData.IsDoji = False
        record(Doji = 130 if sidData.IsDoji else None)
            
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def ProcessHABars(context, data):
    openDeck  = history(200, '1d', 'open_price').dropna(axis=1)
    highDeck  = history(200, '1d', 'high').dropna(axis=1)
    lowDeck   = history(200, '1d', 'low').dropna(axis=1)
    closeDeck = history(200, '1d', 'close_price').dropna(axis=1)
   
    #// Per CQG:
    #// Heikin-Ashi candlesticks are a derivative of Japanese candlesticks, 
    #// but rather than using actual open, high, low, and close values, it uses recalculated values.
    #// The values for open, high, low, and close are calculated like this:
    #// •HAOpen  = (HAOpen(Previous Bar) + HAClose(Previous Bar)) / 2    
    #// •HAHigh  = Maximum(High, HAOpen, HAClose)
    #// •HALow   = Minimum(Low, HAOpen, HAClose)
    #// •HAClose = (Open + High + Low + Close) / 4

    for stock in data:
        haOpen  = openDeck[stock][-1]
        haHigh  = highDeck[stock][-1]
        haLow   = lowDeck[stock][-1]
        haClose = closeDeck[stock][-1]
        
        if (len(context.S[stock].HAOpen) == 0):
            context.S[stock].HAOpen.append(haOpen)        
            context.S[stock].HAHigh.append(haHigh)        
            context.S[stock].HALow.append(haLow)
            context.S[stock].HAClose.append(haClose)
            continue
            
        # The HA Calculations
        haOpen = (context.S[stock].HAOpen[-1] + context.S[stock].HAClose[-1]) / 2.0
        haClose = (haOpen + haHigh + haLow + haClose) / 4.0                
        haHigh = max(haHigh, haOpen, haClose)
        haLow  = min(haLow, haOpen, haClose)
        
        # Appending the values
        context.S[stock].HAOpen.append(haOpen)
        context.S[stock].HAHigh.append(haHigh)        
        context.S[stock].HALow.append(haLow)
        context.S[stock].HAClose.append(haClose)
        
        # Trim down arrays, no need to keem all the data
        context.S[stock].HAOpen  = context.S[stock].HAOpen[-3:]
        context.S[stock].HAHigh  = context.S[stock].HAHigh[-3:]
        context.S[stock].HALow   = context.S[stock].HALow[-3:]
        context.S[stock].HAClose = context.S[stock].HAClose[-3:]
        
        # Show the HA Bars for testing proof
        record(HAOpen  = context.S[stock].HAOpen[-1])
        #record(HAHigh  = context.S[stock].HAHigh[-1])
        #record(HALow   = context.S[stock].HALow[-1])
        record(HAClose = context.S[stock].HAClose[-1])

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
def CalculateVolume(context, data):
    volDeck   = history(64, '1d', 'volume').dropna(axis=1)
    for stock in context.S:
        if (volDeck[stock][-1] > volDeck[stock][0:-2].mean()):
            context.S[stock].IsVolumeUp = True
        else:
            context.S[stock].IsVolumeUp = False
There was a runtime error.

Hey guys, thanks for the help with this. Peter, is there a way to change it from being a monthly reference to simply the candle of the day before? I.e. if yesterday's candle was simply a green candle, hold. If it's a doji, buy/sell depending on if it is followed by a confirmation. If it's red, sell all.

I think thats easy to do yourself with Market Techs code.... just change handle entry and handle exit

I played with this old non parametric idea, modified it and came out with such results.

Loading notebook preview...
Notebook previews are currently unavailable.

pretty good, why not share the code as it was shared initially?