# -*-mode: python; py-indent-offset: 4; indent-tabs-mode: nil; encoding: utf-8-dos; coding: utf-8 -*-

# from http://pythonprogramming.net/advanced-matplotlib-graphing-charting-tutorial

"""
OTPpnAmgc charts a CSV file of Open High Low Close Volume values,
along with the SMA, MACD and RSI, using matplotlib.

Give the CsvFile Symbol Timeframe and Year as arguments to the script.

The Timeframe is the period in minutes: e.g. 1 60 240 1440
YMMV: IT WILL NOT WORK for less than Daily: 1440
"""

import sys
import os
import urllib2
import time
import datetime
from collections import OrderedDict
import numpy as np
import matplotlib
# Problems when frozen: only matplotlib.backends._backend_agg.pyd is bundled

#  File "OpenTrader\OTPpnAmgc.py", line 23, in <module>
#  File "c:\Python27\lib\site-packages\matplotlib\pyplot.py", line 98, in <module
#    >
#        _backend_mod, new_figure_manager, draw_if_interactive, _show = pylab_setup()
#
#  File "c:\Python27\lib\site-packages\matplotlib\backends\__init__.py", line 28,
#  in pylab_setup
#               globals(),locals(),[backend_name],0)
#  ImportError: No module named backend_qt4agg
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
from matplotlib.finance import candlestick_ochl
from matplotlib import pylab

from PandasMt4 import oReadMt4Csv, oPreprocessOhlc

# matplotlib.rcParams.update({'font.size': 11})

dOHLC_CACHE_DF = {}

def rsiFunc(prices, n=14):
    deltas = np.diff(prices)
    seed = deltas[:n+1]
    up = seed[seed>=0].sum()/n
    down = -seed[seed<0].sum()/n
    rs = up/down
    rsi = np.zeros_like(prices)
    rsi[:n] = 100. - 100./(1.+rs)

    for i in range(n, len(prices)):
        delta = deltas[i-1] # cause the diff is 1 shorter

        if delta>0:
            upval = delta
            downval = 0.
        else:
            upval = 0.
            downval = -delta

        up = (up*(n-1) + upval)/n
        down = (down*(n-1) + downval)/n

        rs = up/down
        rsi[i] = 100. - 100./(1.+rs)

    return rsi

def nSMA(values, window):
    weigths = np.repeat(1.0, window)/window
    smas = np.convolve(values, weigths, 'valid')
    return smas # as a numpy array

########EMA CALC ADDED############
def ExpMovingAverage(values, window):
    weights = np.exp(np.linspace(-1., 0., window))
    weights /= weights.sum()
    a =  np.convolve(values, weights, mode='full')[:len(values)]
    a[:window] = a[window]
    return a


def computeMACD(x, slow=26, fast=12):
    """
    compute the MACD (Moving Average Convergence/Divergence) using a fast and slow exponential moving avg'
    return value is emaslow, emafast, macd which are len(x) arrays
    """
    emaslow = ExpMovingAverage(x, slow)
    emafast = ExpMovingAverage(x, fast)
    return emaslow, emafast, emafast - emaslow

def lPullYahooToTxtfile(sSymbol):
    '''
        Use this to dynamically pull a sSymbol:
    '''
    try:
        print 'Currently Pulling', sSymbol
        print str(datetime.datetime.fromtimestamp(int(time.time())).strftime('%Y-%m-%d %H:%M:%S'))
        #Keep in mind this is close high low open, lol.
        urlToVisit = 'http://chartapi.finance.yahoo.com/instrument/1.0/'+sSymbol+'/chartdata;type=quote;range=10y/csv'
        lStockLines = []
        try:
            sourceCode = urllib2.urlopen(urlToVisit).read()
            splitSource = sourceCode.split('\n')
            for eachLine in splitSource:
                splitLine = eachLine.split(',')
                if len(splitLine) == 6:
                    if 'values' not in eachLine:
                        lStockLines.append(eachLine)
            return lStockLines
        except Exception as e:
            print str(e), 'failed to organize pulled data.'
    except StandardError, e:
        print str(e), 'failed to pull pricing data'
    return None

def vGraphData(sSymbol, date, closep, highp, lowp, openp, volume,
               iShortSMA=10, iLongSMA=50,
               iRsiUpper=70, iRsiLower=30,
               iMacdSlow=26, iMacdFast=12, iMacdEma=9,
               bUseTalib=False,
               ):
    if bUseTalib:
        import talib

    x = 0
    y = len(date)
    newAr = []
    while x < y:
        appendLine = (date[x], openp[x], closep[x], highp[x], lowp[x], volume[x],)
        newAr.append(appendLine)
        x+=1

    if bUseTalib:
        Av1 = talib.SMA(closep, iShortSMA)
        Av2 = talib.SMA(closep, iLongSMA)
    else:
        Av1 = nSMA(closep, iShortSMA)
        Av2 = nSMA(closep, iLongSMA)

    SP = len(date[iLongSMA-1:])

    fig = plt.figure(facecolor='#07000d')

    ax1 = plt.subplot2grid((6,4), (1,0), rowspan=4, colspan=4, axisbg='#07000d')
    candlestick_ochl(ax1, newAr[-SP:], width=.6, colorup='#53c156', colordown='#ff1717')

    Label1 = str(iShortSMA)+' SMA'
    Label2 = str(iLongSMA)+' SMA'

    ax1.plot(date[-SP:],Av1[-SP:],'#e1edf9',label=Label1, linewidth=1.5)
    ax1.plot(date[-SP:],Av2[-SP:],'#4ee6fd',label=Label2, linewidth=1.5)

    uSpinesFg = "#5998ff"
    ax1.grid(True, color='white')
    ax1.xaxis.set_major_locator(mticker.MaxNLocator(10))
    ax1.xaxis.set_major_formatter(matplotlib.dates.DateFormatter('%Y-%m'))
    ax1.yaxis.label.set_color("w")
    ax1.spines['bottom'].set_color(uSpinesFg)
    ax1.spines['top'].set_color(uSpinesFg)
    ax1.spines['left'].set_color(uSpinesFg)
    ax1.spines['right'].set_color(uSpinesFg)
    ax1.tick_params(axis='y', colors='white')
    plt.gca().yaxis.set_major_locator(mticker.MaxNLocator(prune='upper'))
    ax1.tick_params(axis='x', colors='white')
    plt.ylabel('Stock price and Volume')

    maLeg = plt.legend(loc=9, ncol=2, prop={'size': 7},
                       fancybox=True, borderaxespad=0.0)
    maLeg.get_frame().set_alpha(0.4)

    oLegend = pylab.gca().get_legend()
    if oLegend:
        # AttributeError: 'NoneType' object has no attribute 'get_texts'
        textEd = oLegend.get_texts()
        pylab.setp(textEd[0:5], color = 'white')

    volumeMin = 0

    ax0 = plt.subplot2grid((6,4), (0,0), sharex=ax1, rowspan=1, colspan=4, axisbg='#07000d')
    rsi = rsiFunc(closep)
    rsiCol = '#c1f9f7'
    posCol = '#386d13'
    negCol = '#8f2020'

    ax0.plot(date[-SP:], rsi[-SP:], rsiCol, linewidth=1.5)
    ax0.axhline(iRsiUpper, color=negCol)
    ax0.axhline(iRsiLower, color=posCol)
    ax0.fill_between(date[-SP:], rsi[-SP:], iRsiUpper, where=(rsi[-SP:]>=iRsiUpper), facecolor=negCol, edgecolor=negCol, alpha=0.5)
    ax0.fill_between(date[-SP:], rsi[-SP:], iRsiLower, where=(rsi[-SP:]<=iRsiLower), facecolor=posCol, edgecolor=posCol, alpha=0.5)
    ax0.set_yticks([iRsiLower, iRsiUpper])
    ax0.yaxis.label.set_color("w")
    ax0.spines['bottom'].set_color(uSpinesFg)
    ax0.spines['top'].set_color(uSpinesFg)
    ax0.spines['left'].set_color(uSpinesFg)
    ax0.spines['right'].set_color(uSpinesFg)
    ax0.tick_params(axis='y', colors='white')
    ax0.tick_params(axis='x', colors='white')
    plt.ylabel('RSI')

    uVolumeFg = '#00ffe8'
    ax1v = ax1.twinx()
    ax1v.fill_between(date[-SP:],volumeMin, volume[-SP:], facecolor=uVolumeFg, alpha=.4)
    ax1v.axes.yaxis.set_ticklabels([])
    ax1v.grid(False)
    ###Edit this to 3, so it's a bit larger
    ax1v.set_ylim(0, 3*volume.max())
    ax1v.spines['bottom'].set_color(uSpinesFg)
    ax1v.spines['top'].set_color(uSpinesFg)
    ax1v.spines['left'].set_color(uSpinesFg)
    ax1v.spines['right'].set_color(uSpinesFg)
    ax1v.tick_params(axis='x', colors='white')
    ax1v.tick_params(axis='y', colors='white')

    ax2 = plt.subplot2grid((6,4), (5,0), sharex=ax1, rowspan=1, colspan=4, axisbg='#07000d')
    uMacdFill = '#00ffe8'
    emaslow, emafast, macd = computeMACD(closep, slow=iMacdSlow, fast=iMacdFast)
    ema9 = ExpMovingAverage(macd, iMacdEma)
    ax2.plot(date[-SP:], macd[-SP:], color='#4ee6fd', lw=2)
    ax2.plot(date[-SP:], ema9[-SP:], color='#e1edf9', lw=1)
    ax2.fill_between(date[-SP:], macd[-SP:]-ema9[-SP:], 0,
                     alpha=0.5, facecolor=uMacdFill, edgecolor=uMacdFill)

    plt.gca().yaxis.set_major_locator(mticker.MaxNLocator(prune='upper'))
    ax2.spines['bottom'].set_color(uSpinesFg)
    ax2.spines['top'].set_color(uSpinesFg)
    ax2.spines['left'].set_color(uSpinesFg)
    ax2.spines['right'].set_color(uSpinesFg)
    ax2.tick_params(axis='x', colors='white')
    ax2.tick_params(axis='y', colors='white')
    plt.ylabel('MACD', color='w')
    ax2.yaxis.set_major_locator(mticker.MaxNLocator(nbins=5, prune='upper'))
    for label in ax2.xaxis.get_ticklabels():
        label.set_rotation(45)

    plt.suptitle(sSymbol.upper(), color='white')

    plt.setp(ax0.get_xticklabels(), visible=False)
    ### add this ####
    plt.setp(ax1.get_xticklabels(), visible=False)

    ## ax1.annotate('Big news!',(date[510],Av1[510]),
    ##     xytext=(0.8, 0.9), textcoords='axes fraction',
    ##     arrowprops=dict(facecolor='white', shrink=0.05),
    ##     fontsize=14, color = 'w',
    ##     horizontalalignment='right', verticalalignment='bottom')

    plt.subplots_adjust(left=.09, bottom=.14, right=.94, top=.95, wspace=.20, hspace=0)
    plt.show()
    # fig.savefig('example.png',facecolor=fig.get_facecolor())

def iOldMain():
    sSymbol = sys.argv[1]
    lStockLines = lPullYahooToTxtfile(sSymbol)
    date, closep, highp, lowp, openp, volume = \
          np.loadtxt(lStockLines, delimiter=',', unpack=True,
                     converters={0: matplotlib.dates.strpdate2num('%Y%m%d')})
    # was  10, 50)
    dKw = OrderedDict(
        iShortSMA=10,
        iLongSMA=50,
        iRsiUpper=70,
        iRsiLower=30,
        iMacdSlow=26,
        iMacdFast=12,
        iMacdEma=9,
        bUseTalib=False,
        )
    vGraphData(sSymbol, date, closep, highp, lowp, openp, volume,
               **dKw
#               iShortSMA, iLongSMA,
#               iRsiUpper, iRsiLower,
#               iMacdSlow, iMacdFast, iMacdEma,
#               bUseTalib
               )

def oParseOptions(sUsage):
    from argparse import ArgumentParser
    oArgParser = ArgumentParser(description=sUsage)
    oArgParser.add_argument('-u', '--use_talib',
                            dest='bUseTalib', action='store_true', default=False,
                            help='Use Ta-lib for chart operations')
    oArgParser.add_argument("--iShortSMA", action="store",
                            dest="iShortSMA", type=int, default=10)
    oArgParser.add_argument("--iLongSMA", action="store",
                            dest="iLongSMA", type=int, default=50)
    oArgParser.add_argument("--iRsiUpper", action="store",
                            dest="iRsiUpper", type=int, default=70)
    oArgParser.add_argument("--iRsiLower", action="store",
                            dest="iRsiLower", type=int, default=30)
    oArgParser.add_argument("--iMacdSlow", action="store",
                            dest="iMacdSlow", type=int, default=26)
    oArgParser.add_argument("--iMacdFast", action="store",
                            dest="iMacdFast", type=int, default=12)
    oArgParser.add_argument("--iMacdEma", action="store",
                            dest="iMacdEma", type=int, default=9)

    return oArgParser

def iMain():
    sUsage = __doc__.strip()
    oArgParser = oParseOptions(sUsage)
    oArgParser.add_argument('lArgs', action="store",
                            nargs="*",
                            help="the Symbol Timeframe and Year to backtest (required)")
    oOptions = oArgParser.parse_args()
    lArgs = oOptions.lArgs

    assert len(lArgs) == 4, "Give the CsvFile Symbol Timeframe and Year as arguments"

    # sDir = '/t/Program Files/HotForex MetaTrader/history/tools.fxdd.com'
    # sCsvFile = os.path.join(sDir, sSymbol + sTimeFrame +'-' +sYear +'.csv')
    sCsvFile = lArgs[0]
    sSymbol = lArgs[1] # 'EURGBP'
    sTimeFrame = lArgs[2] # '1440'
    sYear = lArgs[3] # '2014'

    oOhlc = oReadMt4Csv(sCsvFile, sTimeFrame, sSymbol, sYear)
    oOhlc = oPreprocessOhlc(oOhlc)
    # import pandas
    # (Pdb) pandas.tseries.converter._dt_to_float_ordinal(oOhlc.index)[0]
    # 735235.33333333337
    dates = matplotlib.dates.date2num(oOhlc.index.to_pydatetime())
    volume = 1000*np.random.normal(size=len(oOhlc))

    vGraphData(sSymbol, dates,
               oOhlc.C.values, oOhlc.H.values, oOhlc.L.values, oOhlc.O.values,
               volume,
               oOptions.iShortSMA, oOptions.iLongSMA,
               oOptions.iRsiUpper, oOptions.iRsiLower,
               oOptions.iMacdSlow, oOptions.iMacdFast, oOptions.iMacdEma,
               bUseTalib=oOptions.bUseTalib,
               )

if __name__ == '__main__':
    iMain()