src/Tester.cpp

Go to the documentation of this file.
00001 /*
00002  *  Qtstalker stock charter
00003  *
00004  *  Copyright (C) 2001-2007 Stefan S. Stratigakos
00005  *
00006  *  This program is free software; you can redistribute it and/or modify
00007  *  it under the terms of the GNU General Public License as published by
00008  *  the Free Software Foundation; either version 2 of the License, or
00009  *  (at your option) any later version.
00010  *
00011  *  This program is distributed in the hope that it will be useful,
00012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  *  GNU General Public License for more details.
00015  *
00016  *  You should have received a copy of the GNU General Public License
00017  *  along with this program; if not, write to the Free Software
00018  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
00019  *  USA.
00020  */
00021 
00022 #include <qlayout.h>
00023 #include <qvgroupbox.h>
00024 #include <qfile.h>
00025 #include <qtextstream.h>
00026 #include <qmessagebox.h>
00027 #include <qinputdialog.h>
00028 #include <qdir.h>
00029 #include <qprogressdialog.h>
00030 #include <qfileinfo.h>
00031 #include "Tester.h"
00032 #include "DbPlugin.h"
00033 #include "IndicatorPlugin.h"
00034 #include "HelpWindow.h"
00035 #include "DBIndexItem.h"
00036 
00037 
00038 Tester::Tester (QString n, DBIndex *i) : QTabDialog (0, 0, FALSE)
00039 {
00040   index = i;
00041   ruleName = n;
00042   recordList = 0;
00043   enterLongSignal.setAutoDelete(TRUE);
00044   exitLongSignal.setAutoDelete(TRUE);
00045   enterShortSignal.setAutoDelete(TRUE);
00046   exitShortSignal.setAutoDelete(TRUE);
00047   trades.setAutoDelete(TRUE);
00048   
00049   QString s = "Qtstalker Back Tester";
00050   s.append(": ");
00051   s.append(ruleName);
00052   setCaption (s);
00053 
00054   setDefaultButton(tr("&Test"));
00055   connect(this, SIGNAL(defaultButtonPressed()), this, SLOT(test()));
00056   
00057   setApplyButton(tr("&Apply"));  
00058   connect(this, SIGNAL(applyButtonPressed()), this, SLOT(saveRule()));
00059   
00060   setCancelButton(tr("&Cancel"));  
00061   
00062   setOkButton(QString::null);
00063   
00064   setHelpButton();
00065   QObject::connect(this, SIGNAL(helpButtonPressed()), this, SLOT(slotHelp()));
00066 
00067   rulePage = new TesterRulePage(this);
00068   addTab(rulePage, tr("Rules"));
00069 
00070   stopPage = new TesterStopPage(this);
00071   addTab(stopPage, tr("Stops"));
00072 
00073   testPage = new TesterTestPage(this);
00074   addTab(testPage, tr("Testing"));
00075 
00076   reportPage = new TesterReport(this);
00077   addTab(reportPage, tr("Reports"));
00078 
00079   chartPage = new TesterChartPage(this, index);
00080   addTab(chartPage, tr("Chart"));
00081 
00082   loadRule();
00083 }
00084 
00085 Tester::Tester () : QTabDialog (0, 0, FALSE)
00086 {
00087   recordList = 0;
00088 }
00089 
00090 Tester::~Tester ()
00091 {
00092   if (recordList)
00093     delete recordList;
00094 }
00095 
00096 void Tester::saveRule ()
00097 {
00098   rulePage->saveEditRule(TesterRulePage::EnterLong, ruleName);
00099   rulePage->saveEditRule(TesterRulePage::ExitLong, ruleName);
00100   rulePage->saveEditRule(TesterRulePage::EnterShort, ruleName);
00101   rulePage->saveEditRule(TesterRulePage::ExitShort, ruleName);
00102 
00103   stopPage->saveCustomStopRule(ruleName);
00104 
00105   QString s;
00106   config.getData(Config::TestPath, s);
00107   s.append("/" + ruleName + "/rule");
00108   QFile f(s);
00109   if (! f.open(IO_WriteOnly))
00110     return;
00111   QTextStream stream(&f);
00112 
00113   QStringList l;
00114   reportPage->getSummary(l);
00115   int loop;
00116   for (loop = 0; loop < (int) l.count(); loop++)
00117     stream << "Trade=" << l[loop] << "\n";
00118   
00119   stream << "Maximum Loss Check=" << QString::number(stopPage->getMaximumLossCheck()) << "\n";
00120   stream << "Maximum Loss Long=" << QString::number(stopPage->getMaximumLossLong()) << "\n";
00121   stream << "Maximum Loss Short=" << QString::number(stopPage->getMaximumLossShort()) << "\n";
00122   stream << "Maximum Loss Edit=" << stopPage->getMaximumLossEdit() << "\n";
00123   stream << "Profit Check=" << QString::number(stopPage->getProfitCheck()) << "\n";
00124   stream << "Profit Long=" << QString::number(stopPage->getProfitLong()) << "\n";
00125   stream << "Profit Short=" << QString::number(stopPage->getProfitShort()) << "\n";
00126   stream << "Profit Edit=" << stopPage->getProfitEdit() << "\n";
00127   stream << "Trailing Check=" << QString::number(stopPage->getTrailingCheck()) << "\n";
00128   stream << "Trailing Long=" << QString::number(stopPage->getTrailingLong()) << "\n";
00129   stream << "Trailing Short=" << QString::number(stopPage->getTrailingShort()) << "\n";
00130   stream << "Trailing Edit=" << stopPage->getTrailingEdit() << "\n";
00131   stream << "TradeLong=" << QString::number(testPage->getTradeLong()) << "\n";
00132   stream << "TradeShort=" << QString::number(testPage->getTradeShort()) << "\n";
00133   stream << "Volume Percent=" << QString::number(testPage->getVolumePercent()) << "\n";
00134   stream << "Entry Com=" << QString::number(testPage->getEntryCom()) << "\n";
00135   stream << "Exit Com=" << QString::number(testPage->getExitCom()) << "\n";
00136   stream << "TradeDelay=" << QString::number(testPage->getTradeDelay()) << "\n";
00137   stream << "Price Field=" << testPage->getPriceField() << "\n";
00138   stream << "Bars=" << QString::number(testPage->getBars()) << "\n";
00139   stream << "Symbol=" << testPage->getSymbolPath() << "\n";
00140   stream << "Account=" << QString::number(testPage->getAccount()) << "\n";
00141   stream << "Compression=" << testPage->getBarLength() << "\n";
00142   stream << "CommissionType=" << QString::number(testPage->getCommissionType()) << "\n";
00143   stream << "FuturesMargin=" << QString::number(testPage->getMargin()) << "\n";
00144   
00145   f.close();
00146 }
00147 
00148 void Tester::loadRule ()
00149 {
00150   reportPage->clear();
00151 
00152   rulePage->loadEditRule(TesterRulePage::EnterLong, ruleName);
00153   rulePage->loadEditRule(TesterRulePage::ExitLong, ruleName);
00154   rulePage->loadEditRule(TesterRulePage::EnterShort, ruleName);
00155   rulePage->loadEditRule(TesterRulePage::ExitShort, ruleName);
00156 
00157   stopPage->loadCustomStopRule(ruleName);
00158 
00159   QString s;
00160   config.getData(Config::TestPath, s);
00161   s.append("/" + ruleName + "/rule");
00162   QFile f(s);
00163   if (! f.open(IO_ReadOnly))
00164     return;
00165   QTextStream stream(&f);
00166 
00167   while(stream.atEnd() == 0)
00168   {
00169     s = stream.readLine();
00170     s = s.stripWhiteSpace();
00171 
00172     if (! s.length())
00173       continue;
00174 
00175     QStringList l2 = QStringList::split("=", s, FALSE);
00176     
00177     if (! l2[0].compare("Maximum Loss Check"))
00178     {
00179       stopPage->setMaximumLossCheck(l2[1].toInt());
00180       continue;
00181     }
00182 
00183     if (! l2[0].compare("Maximum Loss Long"))
00184     {
00185       stopPage->setMaximumLossLong(l2[1].toInt());
00186       continue;
00187     }
00188 
00189     if (! l2[0].compare("Maximum Loss Short"))
00190     {
00191       stopPage->setMaximumLossShort(l2[1].toInt());
00192       continue;
00193     }
00194 
00195     if (! l2[0].compare("Maximum Loss Edit"))
00196     {
00197       stopPage->setMaximumLossEdit(l2[1]);
00198       continue;
00199     }
00200 
00201     if (! l2[0].compare("Profit Check"))
00202     {
00203       stopPage->setProfitCheck(l2[1].toInt());
00204       continue;
00205     }
00206 
00207     if (! l2[0].compare("Profit Long"))
00208     {
00209       stopPage->setProfitLong(l2[1].toInt());
00210       continue;
00211     }
00212 
00213     if (! l2[0].compare("Profit Short"))
00214     {
00215       stopPage->setProfitShort(l2[1].toInt());
00216       continue;
00217     }
00218 
00219     if (! l2[0].compare("Profit Edit"))
00220     {
00221       stopPage->setProfitEdit(l2[1]);
00222       continue;
00223     }
00224 
00225     if (! l2[0].compare("Trailing Check"))
00226     {
00227       stopPage->setTrailingCheck(l2[1].toInt());
00228       continue;
00229     }
00230 
00231     if (! l2[0].compare("Trailing Long"))
00232     {
00233       stopPage->setTrailingLong(l2[1].toInt());
00234       continue;
00235     }
00236 
00237     if (! l2[0].compare("Trailing Short"))
00238     {
00239       stopPage->setTrailingShort(l2[1].toInt());
00240       continue;
00241     }
00242 
00243     if (! l2[0].compare("Trailing Edit"))
00244     {
00245       stopPage->setTrailingEdit(l2[1]);
00246       continue;
00247     }
00248     
00249     if (! l2[0].compare("TradeLong"))
00250     {
00251       testPage->setTradeLong(l2[1].toInt());
00252       continue;
00253     }
00254     
00255     if (! l2[0].compare("TradeShort"))
00256     {
00257       testPage->setTradeShort(l2[1].toInt());
00258       continue;
00259     }
00260     
00261     if (! l2[0].compare("Volume Percent"))
00262     {
00263       testPage->setVolumePercent(l2[1].toInt());
00264       continue;
00265     }
00266   
00267     if (! l2[0].compare("Entry Com"))
00268     {
00269       testPage->setEntryCom(l2[1].toInt());
00270       continue;
00271     }
00272   
00273     if (! l2[0].compare("Exit Com"))
00274     {
00275       testPage->setExitCom(l2[1].toInt());
00276       continue;
00277     }
00278     
00279     if (! l2[0].compare("TradeDelay"))
00280     {
00281       testPage->setTradeDelay(l2[1].toInt());
00282       continue;
00283     }
00284   
00285     if (! l2[0].compare("Bars"))
00286     {
00287       testPage->setBars(l2[1].toInt());
00288       continue;
00289     }
00290     
00291     if (! l2[0].compare("Price Field"))
00292     {
00293       testPage->setPriceField(l2[1]);
00294       continue;
00295     }
00296   
00297     if (! l2[0].compare("Symbol"))
00298     {
00299       testPage->setSymbol(l2[1]);
00300       continue;
00301     }
00302     
00303     if (! l2[0].compare("Account"))
00304     {
00305       testPage->setAccount(l2[1].toInt());
00306       continue;
00307     }
00308 
00309     if (! l2[0].compare("Trade"))
00310     {
00311       TradeItem *trade = new TradeItem;
00312       reportPage->addTrade(l2[1], trade);
00313       trades.append(trade);
00314       continue;
00315     }
00316 
00317     if (! l2[0].compare("Compression"))
00318     {
00319       testPage->setBarLength(l2[1]);
00320       continue;
00321     }
00322 
00323     if (! l2[0].compare("CommissionType"))
00324     {
00325       testPage->setCommissionType(l2[1].toInt());
00326       continue;
00327     }
00328 
00329     if (! l2[0].compare("FuturesMargin"))
00330       testPage->setMargin(l2[1].toInt());
00331   }
00332   f.close();
00333 
00334   s = testPage->getSymbolPath();
00335   DbPlugin db;
00336   if (db.open(s, index))
00337   {
00338     db.close();
00339     return;
00340   }
00341 
00342   QFileInfo fi(s);
00343   QString fn = fi.fileName();
00344 
00345   DBIndexItem item;
00346   index->getIndexItem(fn, item);
00347   item.getType(chartType);
00348   if (! chartType.compare(tr("Futures")))
00349     item.getFuturesType(futuresType);
00350 
00351   db.close();
00352 
00353   int loop;
00354   for (loop = 0; loop < (int) trades.count(); loop++)
00355   {
00356     TradeItem *trade = trades.at(loop);
00357     trade->setCommissionType(testPage->getCommissionType());
00358     trade->setEntryCom(testPage->getEntryCom());
00359     trade->setExitCom(testPage->getExitCom());
00360 
00361     if (! chartType.compare(tr("Futures")))
00362     {
00363       trade->setStockFlag(FALSE);
00364       trade->setFuturesType(futuresType);
00365     }
00366 
00367     if (! loop)
00368       trade->setBalance(testPage->getAccount());
00369     else
00370     {
00371       TradeItem *ttrade = trades.at(loop - 1);
00372       trade->setBalance(ttrade->getBalance());
00373     }
00374 
00375     trade->calculateProfit();
00376   }
00377 
00378   chartPage->clear();
00379 
00380   reportPage->createSummary(trades, testPage->getAccount());
00381 }
00382 
00383 void Tester::exitDialog ()
00384 {
00385   saveRule();
00386   accept();
00387 }
00388 
00389 int Tester::getVolume (int i, double d)
00390 {
00391   double balance = d;
00392   int volume = 1;
00393   if (testPage->getVolumePercent() == 0)
00394     return volume;
00395 
00396   balance = balance * ((double) testPage->getVolumePercent() / 100.0);
00397 
00398   if (testPage->getMargin())
00399     volume = (int) (double) (balance / testPage->getMargin());
00400   else
00401     volume = (int) (double) (balance / getPrice(i));
00402 
00403   return volume;
00404 }
00405 
00406 double Tester::getPrice (int i)
00407 {
00408   double price = 0;
00409   
00410   if (! testPage->getPriceField().compare(tr("Open")))
00411     price = recordList->getOpen(i);
00412   else
00413   {
00414     if (! testPage->getPriceField().compare(tr("Close")))
00415       price = recordList->getClose(i);
00416     else
00417       price = recordList->getLow(i) + ((recordList->getHigh(i) - recordList->getLow(i)) / 2);
00418   }
00419   
00420   return price;
00421 }
00422 
00423 QString Tester::newTest ()
00424 {
00425   bool ok;
00426   QString s = QInputDialog::getText(tr("New Backtest Rule"),
00427                                     tr("Enter new backtest rule name."),
00428                                     QLineEdit::Normal,
00429                                     tr("NewRule"),
00430                                     &ok,
00431                                     this);
00432 
00433   if ((! ok) || (s.isNull()))
00434     return s;
00435 
00436   int loop;
00437   QString selection;
00438   for (loop = 0; loop < (int) s.length(); loop++)
00439   {
00440     QChar c = s.at(loop);
00441     if (c.isLetterOrNumber())
00442       selection.append(c);
00443   }
00444   
00445   config.getData(Config::TestPath, s);
00446   s.append("/" + selection);
00447   QDir dir(s);
00448   if (dir.exists(s, TRUE))
00449   {
00450     QMessageBox::information(this, tr("Qtstalker: Error"), tr("This backtest rule already exists."));
00451     return selection;
00452   }
00453 
00454   if (! dir.mkdir(s, TRUE))
00455   {
00456     qDebug("TestPage::newTest:can't create dir %s", s.latin1());
00457     return selection;
00458   }
00459   
00460   if (! dir.mkdir(s + "/el", TRUE))
00461   {
00462     qDebug("TestPage::newTest:can't create el dir");
00463     return selection;
00464   }
00465   
00466   if (! dir.mkdir(s + "/xl", TRUE))
00467   {
00468     qDebug("TestPage::newTest:can't create xl dir");
00469     return selection;
00470   }
00471   
00472   if (! dir.mkdir(s + "/es", TRUE))
00473   {
00474     qDebug("TestPage::newTest:can't create es dir");
00475     return selection;
00476   }
00477   
00478   if (! dir.mkdir(s + "/xs", TRUE))
00479   {
00480     qDebug("TestPage::newTest:can't create xs dir");
00481     return selection;
00482   }
00483   
00484   return selection;
00485 }
00486 
00487 void Tester::slotHelp ()
00488 {
00489   HelpWindow *hw = 0;
00490   QString str;
00491   QString s = tabLabel(currentPage());
00492   
00493   while (s.length())
00494   {
00495     if (! s.compare("Rules"))
00496     {
00497       str = "backtesterrules.html";
00498       hw = new HelpWindow(this, str);
00499       break;
00500     }
00501     
00502     if (! s.compare("Stops"))
00503     {
00504       str = "backtesterstops.html";
00505       hw = new HelpWindow(this, str);
00506       break;
00507     }
00508   
00509     if (! s.compare("Testing"))
00510     {
00511       str = "backtestertesting.html";
00512       hw = new HelpWindow(this, str);
00513       break;
00514     }
00515   
00516     if (! s.compare("Reports"))
00517     {
00518       str = "backtesterreports.html";
00519       hw = new HelpWindow(this, str);
00520       break;
00521     }
00522     
00523     if (! s.compare("Chart"))
00524     {
00525       str = "backtesterchart.html";
00526       hw = new HelpWindow(this, str);
00527       break;
00528     }
00529     
00530     break;
00531   }
00532   
00533   if (hw)
00534     hw->show();
00535 }
00536 
00537 void Tester::loadSignals ()
00538 {
00539   enterLongSignal.clear();
00540   exitLongSignal.clear();
00541   enterShortSignal.clear();
00542   exitShortSignal.clear();
00543 
00544   // open the CUS plugin
00545   QString plugin("CUS");
00546   IndicatorPlugin *plug = config.getIndicatorPlugin(plugin);
00547   if (! plug)
00548   {
00549     config.closePlugin(plugin);
00550     return;
00551   }
00552 
00553   int loop;
00554   for (loop = 0; loop < 4; loop++)
00555   {
00556     QStringList l;
00557     switch (loop)
00558     {
00559       case 0:
00560         l = QStringList::split("\n", rulePage->getEditRule(TesterRulePage::EnterLong));
00561         break;
00562       case 1:
00563         l = QStringList::split("\n", rulePage->getEditRule(TesterRulePage::ExitLong));
00564         break;
00565       case 2:
00566         l = QStringList::split("\n", rulePage->getEditRule(TesterRulePage::EnterShort));
00567         break;
00568       case 3:
00569         l = QStringList::split("\n", rulePage->getEditRule(TesterRulePage::ExitShort));
00570         break;
00571       default:
00572         break;
00573     }
00574   
00575     if (! l.count())
00576       continue;
00577 
00578     plug->setCustomFunction(l);
00579   
00580     // load the CUS plugin and calculate
00581     plug->setIndicatorInput(recordList);
00582     Indicator *i = plug->calculate();
00583     PlotLine *line = i->getLine(0);
00584     if (! line)
00585     {
00586       qDebug("Tester::loadSignals: no PlotLine returned");
00587       delete i;
00588       continue;
00589     }
00590 
00591     int loop2 = recordList->count() - line->getSize();
00592     int lineLoop = 0;
00593     Setting *trade = 0;
00594     for (; loop2 < (int) recordList->count(); loop2++, lineLoop++)
00595     {
00596       if (line->getData(lineLoop) == 1)
00597       {
00598         if (! trade)
00599         {
00600           trade = new Setting;
00601           QDateTime dt;
00602           recordList->getDate(loop2, dt);
00603           QString key = dt.toString("yyyyMMddhhmmss");
00604           switch (loop)
00605           {
00606             case 0:
00607               enterLongSignal.replace(key, trade);
00608               break;
00609             case 1:
00610               exitLongSignal.replace(key, trade);
00611               break;
00612             case 2:
00613               enterShortSignal.replace(key, trade);
00614               break;
00615             case 3:
00616               exitShortSignal.replace(key, trade);
00617               break;
00618             default:
00619               break;
00620           }
00621         }
00622       }
00623       else
00624       {
00625         if (trade)
00626           trade = 0;
00627       }
00628     }
00629 
00630     delete i;
00631   }
00632 
00633   config.closePlugin(plugin);
00634 }
00635 
00636 void Tester::test ()
00637 {
00638   if (! testPage->getTradeLong() && ! testPage->getTradeShort())
00639     return;
00640 
00641   equity = (double) testPage->getAccount();
00642   if (equity == 0)
00643     return;
00644 
00645   QString symbol = testPage->getSymbol();
00646   if (! symbol.length())
00647     return;
00648 
00649   QString path = testPage->getSymbolPath();
00650   DbPlugin db;
00651   QDir dir;
00652   if (! dir.exists(path))
00653     return;
00654   
00655   if (db.open(path, index))
00656   {
00657     db.close();
00658     return;
00659   }
00660 
00661   QFileInfo fi(path);
00662   QString fn = fi.fileName();
00663 
00664   DBIndexItem item;
00665   index->getIndexItem(fn, item);
00666   item.getType(chartType);
00667   if (! chartType.compare(tr("Futures")))
00668     item.getFuturesType(futuresType);
00669   
00670   db.setBarLength((BarData::BarLength) testPage->getBarLengthIndex());
00671   db.setBarRange(testPage->getBars());
00672   if (recordList)
00673     delete recordList;
00674   recordList = new BarData(path);
00675   QDateTime dt = QDateTime::currentDateTime();
00676   db.getHistory(recordList, dt);
00677   db.close();
00678 
00679   chartPage->clear();
00680 
00681   loadSignals();
00682 
00683   if (stopPage->loadCustomLongStop(recordList))
00684     return;
00685   
00686   if (stopPage->loadCustomShortStop(recordList))
00687     return;
00688 
00689   reportPage->clear();
00690   trades.clear();
00691 
00692   QProgressDialog prog(tr("Testing..."),
00693                        tr("Cancel"),
00694                        testPage->getBars(),
00695                        this,
00696                        "progress",
00697                        TRUE);
00698   prog.show();
00699   
00700   this->setEnabled(FALSE);
00701   
00702   currentRecord = 0;
00703   for (; currentRecord < (int) recordList->count(); currentRecord++)
00704   {
00705     prog.setProgress(currentRecord);
00706     emit message(QString());
00707     if (prog.wasCancelled())
00708       break;
00709   
00710     QDateTime dt;
00711     recordList->getDate(currentRecord, dt);
00712     QString key = dt.toString("yyyyMMddhhmmss");
00713 
00714     if (testPage->getTradeLong())
00715     {
00716       Setting *set = enterLongSignal[key];
00717       if (set)
00718         enterTrade(TradeItem::Long);
00719     }
00720 
00721     if (testPage->getTradeShort())
00722     {
00723       Setting *set = enterShortSignal[key];
00724       if (set)
00725         enterTrade(TradeItem::Short);
00726     }
00727   }
00728 
00729   reportPage->createSummary(trades, testPage->getAccount());
00730 
00731   chartPage->updateChart(recordList, trades, testPage->getAccount());
00732 
00733   db.close();
00734   
00735   this->setEnabled(TRUE);
00736 }
00737 
00738 void Tester::enterTrade (TradeItem::TradePosition flag)
00739 {
00740   TradeItem *trade = new TradeItem;
00741   trade->setTradePosition(flag);
00742   if (flag == TradeItem::Long)
00743     trade->setEnterSignal(TradeItem::EnterLong);
00744   else
00745     trade->setEnterSignal(TradeItem::EnterShort);
00746 
00747   int buyRecord = 0;
00748   if (currentRecord + testPage->getTradeDelay() < recordList->count())
00749     buyRecord = currentRecord + testPage->getTradeDelay();
00750   else
00751     buyRecord = currentRecord;
00752 
00753   QDateTime td;
00754   recordList->getDate(buyRecord, td);
00755   trade->setEnterDate(td);
00756 
00757   double enterPrice = getPrice(buyRecord);
00758   trade->setEnterPrice(enterPrice);
00759 
00760   stopPage->setTrailingHigh(getPrice(buyRecord));
00761 
00762   if (! trades.count())
00763   {
00764     trade->setVolume(getVolume(buyRecord, testPage->getAccount()));
00765     trade->setBalance(testPage->getAccount());
00766   }
00767   else
00768   {
00769     TradeItem *t = trades.at(trades.count() - 1);
00770     trade->setVolume(getVolume(buyRecord, t->getBalance()));
00771     trade->setBalance(t->getBalance());
00772   }
00773   if (trade->getVolume() == 0)
00774   {
00775     delete trade;
00776     return;
00777   }
00778 
00779   trade->setCommissionType(testPage->getCommissionType());
00780 
00781   trade->setEntryCom(testPage->getEntryCom());
00782 
00783   trade->setExitCom(testPage->getExitCom());
00784 
00785   if (! chartType.compare(tr("Futures")))
00786   {
00787     trade->setStockFlag(FALSE);
00788     trade->setFuturesType(futuresType);
00789     trade->setMargin(testPage->getMargin());
00790   }
00791 
00792   int loop = buyRecord;
00793   for (; loop < (int) recordList->count(); loop++)
00794   {
00795     QDateTime dt;
00796     recordList->getDate(loop, dt);
00797     QString key = dt.toString("yyyyMMddhhmmss");
00798 
00799     Setting *set = 0;
00800     if (flag == TradeItem::Long)
00801       set = exitLongSignal[key];
00802     else
00803       set = exitShortSignal[key];
00804     if (set)
00805     {
00806       int sellRecord = 0;
00807       if (loop + testPage->getTradeDelay() < recordList->count())
00808         sellRecord = loop + testPage->getTradeDelay();
00809       else
00810         sellRecord = loop;
00811 
00812       recordList->getDate(sellRecord, td);
00813       trade->setExitDate(td);
00814       trade->setExitPrice(getPrice(sellRecord));
00815       if (flag == TradeItem::Long)
00816         trade->setExitSignal(TradeItem::ExitLong);
00817       else
00818         trade->setExitSignal(TradeItem::ExitShort);
00819       trades.append(trade);
00820       currentRecord = loop - 1;
00821       trade->calculateProfit();
00822       break;
00823     }
00824 
00825     bool tflag = FALSE;
00826     if (flag == TradeItem::Long)
00827       tflag = stopPage->maximumLoss(FALSE, enterPrice, recordList->getLow(loop));
00828     else
00829       tflag = stopPage->maximumLoss(TRUE, enterPrice, recordList->getHigh(loop));
00830     if (tflag)
00831     {
00832       int sellRecord = 0;
00833       if (loop + testPage->getTradeDelay() < recordList->count())
00834         sellRecord = loop + testPage->getTradeDelay();
00835       else
00836         sellRecord = loop;
00837 
00838       recordList->getDate(sellRecord, td);
00839       trade->setExitDate(td);
00840       trade->setExitPrice(getPrice(sellRecord));
00841       trade->setExitSignal(TradeItem::MaximumLoss);
00842       trades.append(trade);
00843       currentRecord = loop - 1;
00844       trade->calculateProfit();
00845       break;
00846     }
00847 
00848     if (flag == TradeItem::Long)
00849       tflag = stopPage->profit(FALSE, enterPrice, recordList->getHigh(loop));
00850     else
00851       tflag = stopPage->profit(TRUE, enterPrice, recordList->getLow(loop));
00852     if (tflag)
00853     {
00854       int sellRecord = 0;
00855       if (loop + testPage->getTradeDelay() < recordList->count())
00856         sellRecord = loop + testPage->getTradeDelay();
00857       else
00858         sellRecord = loop;
00859 
00860       recordList->getDate(sellRecord, td);
00861       trade->setExitDate(td);
00862       trade->setExitPrice(getPrice(sellRecord));
00863       trade->setExitSignal(TradeItem::Profit);
00864       trades.append(trade);
00865       currentRecord = loop - 1;
00866       trade->calculateProfit();
00867       break;
00868     }
00869 
00870     if (flag == TradeItem::Long)
00871       tflag = stopPage->customStop(FALSE, loop);
00872     else
00873       tflag = stopPage->customStop(TRUE, loop);
00874     if (tflag)
00875     {
00876       int sellRecord = 0;
00877       if (loop + testPage->getTradeDelay() < recordList->count())
00878         sellRecord = loop + testPage->getTradeDelay();
00879       else
00880         sellRecord = loop;
00881 
00882       recordList->getDate(sellRecord, td);
00883       trade->setExitDate(td);
00884       trade->setExitPrice(getPrice(sellRecord));
00885       trade->setExitSignal(TradeItem::CUSStop);
00886       trades.append(trade);
00887       currentRecord = loop - 1;
00888       trade->calculateProfit();
00889       break;
00890     }
00891     
00892     if (flag == TradeItem::Long)
00893       tflag = stopPage->trailing(FALSE, recordList->getLow(loop));
00894     else
00895       tflag = stopPage->trailing(TRUE, recordList->getHigh(loop));
00896     if (tflag)
00897     {
00898       int sellRecord = 0;
00899       if (loop + testPage->getTradeDelay() < recordList->count())
00900         sellRecord = loop + testPage->getTradeDelay();
00901       else
00902         sellRecord = loop;
00903 
00904       recordList->getDate(sellRecord, td);
00905       trade->setExitDate(td);
00906       trade->setExitPrice(getPrice(sellRecord));
00907       trade->setExitSignal(TradeItem::Trailing);
00908       trades.append(trade);
00909       currentRecord = loop - 1;
00910       trade->calculateProfit();
00911       break;
00912     }
00913   }
00914 
00915   if (trade->getExitSignal() == TradeItem::None)
00916   {
00917     recordList->getDate(recordList->count() - 1, td);
00918     trade->setExitDate(td);
00919     trade->setExitPrice(getPrice(recordList->count() - 1));
00920     trade->setExitSignal(TradeItem::EndTest);
00921     trades.append(trade);
00922     currentRecord = loop;
00923     trade->calculateProfit();
00924   }
00925 }
00926