# -*- coding: UTF-8 -*-
# 2012/10/14 21:17:46
# version 0.1 for testing purpose
import sys
import time
from datetime import datetime, timedelta
import os
import sys
#import shutil
import codecs
import string 
import math
#import xml
#from xml.etree import ElementTree as et  
#from xml.etree import ElementPath 
#from xml.parsers import expat
import win32com
from win32com.client import Dispatch, constants
from win32com.client import DispatchEx
#print ('Amibroker 回測開始') 
start_time = time.time()
Ticker    =sys.argv[1]  #待測的 SYMBOL          EX: "TXF"
strINT    =sys.argv[2]  #回測K棒週期            EX: "3min"
strFDate  =sys.argv[3]  #回測起始日             EX: "2001/01/01"
strTDate  =sys.argv[4]  #回測結束日             EX: "2001/12/31"
OUTPath   =sys.argv[5]  #報表產生路徑           EX: "R:\\"
PATH_AFL  =sys.argv[6]  #回測使用之ALF檔案路徑  EX: "R:\\AmiBroker5_60_1\\Formulas\\Poseidon\\'
FName_AFL =sys.argv[7]  #回測使用之ALF檔名      EX: "Poseidon3K_L_AC_3.afl"
PATH_AA   =sys.argv[8]  #APX檔案路徑            EX: "C:\\Program Files\\AmiBroker\\Scripts\\"
FName_AA  =sys.argv[9]  #APX檔名                EX: "3K_S_AC_3.apx"
FN_AD     =sys.argv[10]  #回測資料庫路徑         EX: "D:\\AmiBrokerData\\BACKTEST\\"

FN_AA=PATH_AA+FName_AA
FN_AFL=PATH_AFL+FName_AFL
Cond1=os.path.isfile(FN_AA)
Cond2=os.path.exists(FN_AD)
Cond3=os.path.isfile(FN_AFL)
if Cond1 and Cond2 and Cond3: 
  
  #print ('\n參考之APX 檔案:'+FN_AA) 
  #print ('回測使用之AFL 檔案:'+FN_AFL)  
  #print ('回測使用之DATABASE 路徑:'+FN_AD)  
  #print ('回測使用之SYMBOL:'+Ticker) 
  #print ('回測使用之K棒週期:'+strINT)  
  #print ('回測起始日:'+strFDate)  
  #print ('回測結束日:'+strTDate+'\n')  

  if not os.path.exists(OUTPath): # Check O/P PATH
    os.makedirs(OUTPath)

  newFN_AA=PATH_AA+"new"+FName_AA
  FN_OUT=OUTPath+'BTR_'+FName_AFL.replace('.afl','')+'_'+Ticker+strINT+'_'+strFDate.replace('/','')+'_'+strTDate.replace('/','')+'.csv' #產生報告檔案

  #shutil.copy2(FN_AA, newFN_AA)
  s=open(FN_AA, "r")
  StreamAPX=s.read()
  StreamAPX=StreamAPX.replace('\r\n','\rn')
  StreamAPXLine=StreamAPX.split('\n')
  s.close()
  #print (StreamAPX)
  u=open(FN_AFL,'r')  
  StreamAFL=u.read()
  u.close()
  #StreamAFL = StreamAFL.replace('\r\n', "\\r\\n")
  StreamAFL = StreamAFL.replace('\\\\', '\\\\\\\\')
  StreamAFL = StreamAFL.replace('\\n', "\\\\n");
  StreamAFL = StreamAFL.replace('\t', "\\t")
  StreamAFL = StreamAFL.replace('>', "&gt;")
  StreamAFL = StreamAFL.replace('<', "&lt;")
  StreamAFL = StreamAFL.replace('\n', "\\r\\n")
  #StreamAFL = StreamAFL.replace('\r', "\\r\\n")
  #print (StreamAFL)
  #print ('\n複製APX檔案 :'+FN_AA +' 到'+newFN_AA)
  t=open(newFN_AA, "w")
  
 
  for line in StreamAPXLine:
    if line.count('<Symbol>') and line.count('</Symbol>'):
      line='<Symbol>'+Ticker+'</Symbol>'
    if line.count('<FormulaPath>') and line.count('</FormulaPath>'):
      line='<FormulaPath>'+FN_AFL+'</FormulaPath>'
    if line.count('<FormulaContent>') and line.count('</FormulaContent>'):
      line='<FormulaContent>'+StreamAFL+'</FormulaContent>'
    if line.count('<ApplyTo>') and line.count('</ApplyTo>'):
      line='<ApplyTo>1</ApplyTo>'
    if line.count('<RangeType>') and line.count('</RangeType>'):
      line='<RangeType>3</RangeType>'      
    if line.count('<FromDate>') and line.count('</FromDate>'):
      line='<FromDate>'+strFDate+' 00:00:00</FromDate>'  
    if line.count('<ToDate>') and line.count('</ToDate>'):
      line='<ToDate>'+strTDate+'</ToDate>'  
    if line.count('<RunEveryInterval>') and line.count('</RunEveryInterval>'):
      line='<RunEveryInterval>'+strINT+'</RunEveryInterval>'
    if line.count('<RangeType>') and line.count('</RangeType>'):
      line='<RangeType>3</RangeType>'      
    if line.count('<RangeFromDate>') and line.count('</RangeFromDate>'):
      line='<RangeFromDate>'+strFDate+' 00:00:00</RangeFromDate>'  
    if line.count('<RangeToDate>') and line.count('</RangeToDate>'):
      line='<RangeToDate>'+strTDate+'</RangeToDate>'        
    if line.count('<BacktestRangeType>') and line.count('</BacktestRangeType>'):
      line='<BacktestRangeType>3</BacktestRangeType>'      
    if line.count('<BacktestRangeFromDate>') and line.count('</BacktestRangeFromDate>'):
      line='<BacktestRangeFromDate>'+strFDate+' 00:00:00</BacktestRangeFromDate>'  
    if line.count('<BacktestRangeToDate>') and line.count('</BacktestRangeToDate>'):
      line='<BacktestRangeToDate>'+strTDate+'</BacktestRangeToDate>'    
     
    t.write(line+'\n')
  t.close()
  #print ('\n產生新APX檔案 :'+newFN_AA)

  # 啟動 Amibroker 回測
  AB=Dispatch('Broker.Application')
  AB.Visible = 0

  newAA=AB.AnalysisDocs.Open(newFN_AA) 
  newAD=AB.LoadDatabase(FN_AD)
  newSym=AB.Documents.Open(Ticker)   
  try:
    #print ('\nAmiBroker 開始回測')  
    newAA.Run(2)
    while (newAA.IsBusy):
      time.sleep(0.1)  
      #print ('.')  
    newAA.Export(FN_OUT)
    print ('\n'+FN_OUT+' 產生完成')  
  except IOError as IOE :
      time.sleep(0.1)   
      print ('有錯誤',retryCount)       
#print ('Amibroker 回測結束')


# Start Processing the OutPut file
#print ("BTR後處理開始")   
linecount=0
strStartD=""
strStartT=""
strPrevD=""
dayNum=0
cnt_tradeDay=0
netProfit1=0
netProfit=0
accumProfit1=0
accumProfit=0
cnt_correct_Intra=0
withDrawNum=0
cntWin=0
cntLose=0
maxLossProfit1=0
maxWinProfit1=0
maxLossProfit=0
maxWinProfit=0
accu_WIN1  = 0
accu_WIN   = 0
maxWIN1    = 0
maxWIN     = 0
preWin     = 1
preLose    = 0
accu_LOSE1 = 0
accu_LOSE  = 0
tradeFee1  = 0
tradeTax1  = 0
accu_Fee1  = 0
accu_Tax1  = 0
tradeFee   = 0
tradeTax   = 0
accu_Fee   = 0
accu_Tax   = 0
maxLOSS1   = 0
preLose    = 1
maxLOSS1   = 0
maxLOSS    = 0
strENDD=""
strENDT=""       
 
strCurrency="TWD"            # 幣別
Margin=83000.0               # 保證金
pointValue=200.0             # 單點價值
tranFee=600.0                # 手續費 可以再加入滑價估算
taxRate=0.008                # 期交稅率*單點價值 
default_initCap     =500000  # 期初資金 可在指令列指定 預設值50萬一口
default_incValue    =300000  # 每口加碼之資金 可在指令列指定 預設值30萬一口
default_maxContract =5       # 最多交易口數 可在指令列指定 預設值五口
default_cashwithDraw=30000   # 達最多口數時 自動出金金額

if len(sys.argv)==15:        # 以上參數 在指令列加入 金額單位已萬元計
  initCap      =int(sys.argv[11])*10000
  incValue     =int(sys.argv[12])*10000
  maxContract  =int(sys.argv[13])
  cashwithDraw =int(sys.argv[14])*10000  
  LossLimit    = incValue-Margin  
else:
  initCap      =default_initCap     
  incValue     =default_incValue    
  maxContract  =default_maxContract 
  cashwithDraw =default_cashwithDraw
  LossLimit    = incValue-Margin   
accountValue=initCap
  
v=open(FN_OUT,'r')  
StreamBTR=v.read().split('\n')
v.close()  

rptFile=OUTPath+'RPT_'+FName_AFL.replace('.afl','')+'_'+Ticker+strINT+'_'+strFDate.replace('/','')+'_'+strTDate.replace('/','')+'.csv' #產生RPT報告檔案


x=open(rptFile,'w')
for LineBTR in StreamBTR:
  if LineBTR!='':
    linecount=linecount+1
  #Symbol,Trade,Date,Price,Ex. date,Ex. Price,% chg	Profit,% Profit
    #,Contracts,Position,value,Cum. Profit,# bars,Profit/bar,MAE,MFE,Scale,In/Out	
  if (linecount>=2 and LineBTR!=''): # Skip first line above
    item = LineBTR.split(',')
    Symbol= item[0]  
    strTrade=item[1]
    strEntryDate=item[2].split(" ")
    strEntryPrice=item[3]
    strExitDate=item[4].split(" ")
    strExitPrice=item[5]     
          
    strEntryD=strEntryDate[0]
    strEntryT=strEntryDate[1]
    strExitD=strExitDate[0]
    strExitT=strExitDate[1]
    if strTrade=="Long":
      strTrade="做多"
      netPoint=float(strExitPrice)-float(strEntryPrice)
    else:
      strTrade="做空"
      netPoint=float(strEntryPrice)-float(strExitPrice)
    entryTax = round(float(strEntryPrice)*taxRate,0) 
    exitTax  = round(float(strExitPrice)*taxRate,0)
    
    if math.floor(accountValue/incValue) > maxContract:
      contractNum=maxContract
    else:  
      if accountValue>=incValue:
        contractNum=math.floor(accountValue/incValue)
      else:
        if accountValue> Margin+LossLimit:
          contractNum=1
        else:
          contractNum=0
    
    tradeFee1  = -tranFee*2
    tradeTax1  = -entryTax-exitTax
    accu_Fee1  = accu_Fee1+tradeFee1
    accu_Tax1  = accu_Tax1+tradeTax1
    tradeFee   = -tranFee*2*contractNum  
    tradeTax   = (-entryTax-exitTax)*contractNum  
    accu_Fee   = accu_Fee+tradeFee
    accu_Tax   = accu_Tax+tradeTax   
    
    netProfit1  = netPoint*pointValue+tradeFee  
    netProfit = netProfit1*contractNum  
    accumProfit1=accumProfit1+netProfit1
    accumProfit =accumProfit +netProfit
    accountValue=accountValue+netProfit
    
    if contractNum==maxContract:
      withDrawNum=withDrawNum+1
      accountValue=accountValue-cashwithDraw

    if math.floor(accountValue/incValue) > maxContract:
      contractNum=maxContract
    else:  
      if accountValue>=incValue:
        contractNum=math.floor(accountValue/incValue)
      else:
        if accountValue> Margin+LossLimit:
          contractNum=1
        else:
          contractNum=0


    if netProfit1 >0:
      cntWin=cntWin+1
      if maxWinProfit1 <netProfit1:
        maxWinProfit1=netProfit1
      if maxWinProfit <netProfit:
        maxWinProfit=netProfit  
        
      if preWin:
        accu_WIN1=accu_WIN1+netProfit1
        accu_WIN=accu_WIN+netProfit
        if maxWIN1 < accu_WIN1:
           maxWIN1 = accu_WIN1
           maxWIN  = accu_WIN   
      else:
        accu_WIN1=accu_WIN1+netProfit1
        accu_WIN=accu_WIN+netProfit
        
      preWin=1
      preLose=0
      accu_LOSE1=0
      accu_LOSE=0
    else:
      cntLose=cntLose+1
      if maxLossProfit1 >netProfit1:
        maxLossProfit1=netProfit1
      if maxLossProfit >netProfit:
        maxLossProfit=netProfit
      if preLose:
        accu_LOSE1=accu_LOSE1+netProfit1
        accu_LOSE=accu_LOSE+netProfit    
        if maxLOSS1 > accu_LOSE1:
           maxLOSS1 = accu_LOSE1
           maxLOSS  = accu_LOSE
      else:
        accu_LOSE1=accu_LOSE1+netProfit1
        accu_LOSE=accu_LOSE+netProfit   
        
      preWin=0
      preLose=1
      accu_WIN1=0      
      accu_WIN=0

    
    if linecount==2: # 1st line of Valid data
      if strExitD==strEntryD:
        cnt_correct_Intra=cnt_correct_Intra+1
      if strEntryD!=strPrevD:  
        cnt_tradeDay=cnt_tradeDay+1
      strPrevD=strExitD  
      strStartD=strEntryD
      strStartT=strEntryT
      OutXString1='標的,交易日數,交易型態,建倉日期,時間,點位,平倉日期,時間,點位,'
      OutXString2='單口賺賠點數,1交易手續費('+strCurrency+'),1交易稅('+strCurrency+'),1交易盈虧('+strCurrency+'),1單筆最大虧損('+strCurrency+'),1連續累積最大虧損('+strCurrency+'),1單筆最大獲利('+strCurrency+'),1連續累積最大獲利('+strCurrency+'),1累計盈虧('+strCurrency+'),' 
      OutXString3='N交易口數,N交易手續費('+strCurrency+'),N交易稅('+strCurrency+'),N交易盈虧('+strCurrency+'),N單筆最大虧損('+strCurrency+'),N連續累積最大虧損('+strCurrency+'),N單筆最大獲利('+strCurrency+'),N連續累積最大獲利('+strCurrency+'),N累計盈虧('+strCurrency+'),'
      OutXString4='提領次數,提領總金額,N帳戶餘額('+strCurrency+'),獲利次數,虧損次數,交易次數'   
      x.write(OutXString1+OutXString2+OutXString3+OutXString4+'\n')
      #OutXString1=Symbol+","+strTrade+","+strEntryD+","+strEntryT+","+strEntryPrice+","+strExitD+","+strExitT+","+strExitPrice+',%.0f'%netPoint+','
      #OutXString2=str(netProfit1)+","+str(accumProfit1)+","+str(contractNum)+","+str(netProfit)+","+str(accumProfit)+","+str(accountValue)+","+str(withDrawNum)+","+str(cnt_tradeDay)
      #x.write(OutXString1+OutXString2+'\n')     
    else:
      if strExitD==strEntryD:
        cnt_correct_Intra=cnt_correct_Intra+1
      if strEntryD!=strPrevD:  
        cnt_tradeDay=cnt_tradeDay+1
      strPrevD=strExitD 
      strENDD=strEntryD
      strENDT=strEntryT
       
    OutXString1=Symbol+","+str(cnt_tradeDay)+","+strTrade+","+strEntryD+","+strEntryT+","+strEntryPrice+","+strExitD+","+strExitT+","+strExitPrice+","
    OutXString2='%.0f'%netPoint+','+str(tradeFee1)+','+str(tradeTax1)+','+str(netProfit1)+","+str(maxLossProfit1)+","+str(maxLOSS1)+","+str(maxWinProfit1)+","+str(maxWIN1)+","+str(accumProfit1)+","
    OutXString3=str(contractNum)+','+str(tradeFee)+','+str(tradeTax)+","+str(netProfit)+","+str(maxLossProfit)+","+str(maxLOSS)+","+str(maxWinProfit)+","+str(maxWIN)+","+str(accumProfit)+","
    OutXString4=str(withDrawNum)+","+str(withDrawNum*cashwithDraw)+","+str(accountValue)+","+str(cntWin)+","+str(cntLose)+","+str(linecount-1)
        
    x.write(OutXString1+OutXString2+OutXString3+OutXString4+'\n')
x.close()
print (rptFile+" 產生完成")   
if strCurrency=="TWD":
  sumFile=OUTPath+'SUM_'+str(initCap/10000)+'_'+str(incValue/10000)+'_'+str(maxContract)+'_'+str(cashwithDraw/10000)+"_"+FName_AFL.replace('.afl','')+'_'+Ticker+strINT+'_'+strStartD.replace('/','')+'_'+strENDD.replace('/','')+'.csv' #產生RPT報告檔案
else:
  sumFile=OUTPath+'SUM_'+str(initCap/100)+'_'+str(incValue/100)+'_'+str(maxContract)+'_'+str(cashwithDraw/100)+"_"+FName_AFL.replace('.afl','')+'_'+Ticker+strINT+'_'+strStartD.replace('/','')+'_'+strENDD.replace('/','')+'.csv' #產生RPT報告檔案
 
y=open(sumFile,'w')
y.write("\n回測輸入資料")
y.write("標的,"+Ticker+",回測K線時間,"+strINT+"\n")
y.write("回測AFL檔案,"+FName_AFL+"\n")
y.write("回測資料庫路徑,"+FN_AD+"\n")
y.write("回測首日,"+strStartD+",回測最後日,"+strENDD+"\n")
y.write("幣別,"+strCurrency+"\n")
y.write("保證金(元),"+str(Margin)+",手續費含滑價(元),"+ str(tranFee)+"\n")
y.write("預設單口交易最大損失上限(元),"+str(LossLimit)+"\n")
y.write("\n統計資料")
y.write("交易日數(日),"+str(cnt_tradeDay)+",日平均交易次數,%.2f"%((linecount-1)/cnt_tradeDay)+"\n")
y.write("交易次數(次),"+str(linecount-1)+"\n")
y.write("獲利次數(次),"+str(cntWin)+",勝率,%.2f"%(cntWin*100/(linecount-1))+"%\n")
y.write("虧損次數(次),"+str(cntLose)+",敗率,%.2f"%(cntLose*100/(linecount-1))+"%\n")
y.write("\n單口交易\n")
y.write("期初本金(元),"+"%.0f"%(initCap)+"\n")
y.write("累計盈虧(元),"+ "%.0f"%(accumProfit1)+",盈虧率,%.3f"%((accumProfit1*100)/initCap)+"%\n")
y.write("帳戶餘額(元),"+  "%.0f"%(initCap+accumProfit1)+"\n")
y.write("累積手續費含滑價(元),"+ "%.0f"%(accu_Fee1)+",累積交易稅(元),%.0f"%(accu_Tax1)+"\n")
y.write("單筆最大獲利(元),"+ str(maxWinProfit1)+",單筆最大虧損(元),"+str(maxLossProfit1)+",")
if (maxLossProfit1>LossLimit):
  y.write(" 已超過預設之交易最大損失上限 "+str(LossLimit)+" 元\n")
else:
  y.write(" 低於預設之交易最大損失上限 "+str(LossLimit)+" 元\n")

y.write("連續累積最大獲利(元),"+ str(maxWIN1)+",連續累積最大虧損(元),"+str(maxLOSS1)+",")
if (maxLOSS1>LossLimit):
  y.write(" 已超過預設之交易最大損失上限 "+str(LossLimit)+" 元\n")
else:
  y.write(" 低於預設之交易最大損失上限" +str(LossLimit)+" 元\n")
  
y.write("\n加碼交易,,最大交易口數,"+str(maxContract)+"\n")
y.write("期初本金(元),"+"%.0f"%(initCap)+",加碼一口需要增加之金額(元),"+str(incValue)+"\n")
y.write("累計盈虧(元),"+ "%.0f"%(accumProfit)+",盈虧率,%.2f"%((accumProfit*100)/initCap)+"%\n")
y.write("帳戶餘額(元),"+ "%.0f"%(accountValue)+",提領總金額(元),"+str(withDrawNum*cashwithDraw)+"\n")
y.write("累積手續費含滑價(元),"+ "%.0f"%(accu_Fee)+",累積交易稅(元),%.0f"%(accu_Tax)+"\n")
y.write("每次提領金額(元),"+str(cashwithDraw)+",總提領次數(次),"+str(withDrawNum)+"\n")
y.write("單筆最大獲利(元),"+ str(maxWinProfit)+",單筆最大虧損(元),"+str(maxLossProfit)+"\n")
y.write("連續累積最大獲利(元),"+ str(maxWIN)+",連續累積最大虧損(元),"+str(maxLOSS)+"\n")
y.close
print (sumFile+" 產生完成\n")
#print ("BTR 後處理結束")  
elapsed_time = time.time() - start_time
print ("處理所花時間 : "+str(elapsed_time))  