ICS

QicsTable Documentation Set

stocks.cpp

/****************************************************************************
** $RCSfile: stocks_cpp.html,v $ $Date: 2008/06/23 16:32:03 $
**
** Copyright ( C ) 2002-2006 Integrated Computer Solutions, Inc.
** All rights reserved.
**
** This file is part of an example program for QicsTable.  This example
** program may be used, distributed and modified without limitation.
**
*****************************************************************************/

#include <QApplication>
#include <QMainWindow>
#include <QMenuBar>
#include <QLabel>
#include <QPushButton>
#include <QCheckBox>
#include <QFile>
#include <QTimer>

#include <QicsTable.h>
#include <QicsDataModelDefault.h>
#include <QicsComboCellDisplay.h>

#include <QPixmap>
#include <QTextStream>
#include <QMenu>

#include <stdlib.h>
#include <math.h>

#include "high.xpm"
#include "low.xpm"


static const char fname[] = "initial-values.txt";

static int stock_name = 0;
static int day_zero = 1;
static int day_one = 4;
static int day_two = 7;
static int day_three = 10;

static int cur_idx = 0;
static int high_idx = 1;
static int low_idx = 2;

static int nyse_idx = 0;
static int nasdaq_idx = 1;

static int update_period = 2000;

//#define ADD_DATA


class StockData : public QObject
{
    Q_OBJECT
public:
    StockData();
    ~StockData();

    QicsDataModel *dataModel(void) const { return myDM; }

public slots:
    void updateStocks(void);

signals:
    void stockChanged(int row, bool gained);

protected slots:
    void dataValuesChanged(QicsRegion reg);

protected:
    void loadDT(void);
    void addDay(int start_col);
    void updateStock(int row);

    float getFloatValue(int row, int col);
    float newprice(float old_price, float percent_change = (float) 0.02, int updown = 1);
    int roundf(float f);
    float randf(void);
    int posneg(void);

    QicsDataModel *myDM;
};


StockData::StockData() :
    QObject()
{
    myDM = new QicsDataModelDefault();

    connect(myDM, SIGNAL(modelChanged(QicsRegion)),
        this, SLOT(dataValuesChanged(QicsRegion)));

    loadDT();
}

StockData::~StockData()
{
    delete myDM;
}

void
StockData::updateStocks(void)
{
    for (int i = 0; i <= myDM->lastRow(); ++i)
    updateStock(i);
}

void
StockData::loadDT(void)
{
    QFile* f;
    if ( QFile::exists(fname) )
    f = new QFile(fname);
    else f = new QFile(QString("..\\")+fname);

    f->open(QIODevice::ReadOnly);

    QTextStream stream(f);

    myDM->readASCII(stream);

    f->close();

#ifdef ADD_DATA
    myDM->addColumns(6);
#if defined(Q_WS_WIN32)
    srand(2332);
#else
    srandom(2332);
#endif

    addDay(day_two);
    addDay(day_three);
    QFile out("out.txt");
    out.open(QIODevice::WriteOnly);
    QTextStream outstream(&out);

    myDM->writeASCII(outstream, ';');

    out.close();
#endif
}

void
StockData::addDay(int start_col)
{
    for (int i = 0; i < myDM->numRows(); ++i)
    {
    float old_price = getFloatValue(i, day_one+cur_idx);
    float new_price = newprice(old_price, (float) 0.05, posneg());

    QicsDataFloat fdata(new_price);
    QicsDataFloat high(newprice(new_price, (float) 0.05, 1));
    QicsDataFloat low(newprice(new_price, (float) 0.05, -1));

    myDM->setItem(i, start_col+high_idx, high);
    myDM->setItem(i, start_col+low_idx, low);
    myDM->setItem(i, start_col+cur_idx, fdata);
    }
}


float
StockData::getFloatValue(int row, int col)
{
    const QicsDataItem *itm = myDM->item(row, col);

    if (itm)
    {
    if (itm->type() == QicsDataItem_Float)
        return ((QicsDataFloat *) itm)->data();
    else if (itm->type() == QicsDataItem_Double)
        return ((QicsDataDouble *) itm)->data();
    else if (itm->type() == QicsDataItem_Int)
        return ((QicsDataInt *) itm)->data();
    }

    return -1;
}

int
StockData::roundf(float f)
{
    int base = (int) floor(f);

    if ((f - base) < 0.5)
    return base;
    else
    return base + 1;
}

float
StockData::randf(void)
{
#if defined(Q_WS_WIN32)
    return (float) rand() / (float) RAND_MAX;
#else
    return (float) random() / (float) RAND_MAX;
#endif
}

int
StockData::posneg(void)
{
    if (roundf(randf()) < 0.5)
    return -1;
    else
    return 1;
}

float
StockData::newprice(float old_price, float percent_change, int updown)
{
    float new_price = old_price + (randf() * percent_change * old_price * updown);
    new_price = ((float) (roundf(new_price * 100))) / 100;

    return new_price;
}

void
StockData::updateStock(int row)
{
    float cur_price;

    cur_price = getFloatValue(row, day_zero+cur_idx);
    if (cur_price < 0)
    // yesterday's close
    cur_price = getFloatValue(row, day_one+cur_idx);

    cur_price = newprice(cur_price, (float) 0.02, posneg());
    QicsDataFloat fdata(cur_price);
    myDM->setItem(row, day_zero+cur_idx, fdata);

    // Check high
    float high = getFloatValue(row, day_zero+high_idx);
    if ((high < 0) || (cur_price > high))
    myDM->setItem(row, day_zero+high_idx, fdata);

    // Check low
    float low = getFloatValue(row, day_zero+low_idx);
    if ((low < 0) || (cur_price < low))
    myDM->setItem(row, day_zero+low_idx, fdata);
}

void
StockData::dataValuesChanged(QicsRegion reg)
{
    int row = reg.startCell().row();
    int col = reg.startCell().column();

    if (col == day_zero+cur_idx)
    {
    float yesterday = getFloatValue(row, day_one+cur_idx);
    float today = getFloatValue(row, day_zero+cur_idx);

    emit stockChanged(row, (today > yesterday));
    }
}


class StockUI: public QObject
{
    Q_OBJECT
public:
    StockUI(QicsDataModel *dt);
    ~StockUI();

    inline QWidget *base(void) const { return myMainWindow; }
    inline QicsTable *table(void) const { return myTable; }

signals:
    void updateToggled(bool on);

protected slots:
    void decorateStock(int idx, bool gainer);

protected:
    void setupRowHeader(void);
    void setupColumnHeader(void);
    void setupGrid(void);

    void setupDay(int day_idx, QString day);

    QMainWindow *myMainWindow;
    QicsTable *myTable;
};

StockUI::StockUI(QicsDataModel *dt) :
    QObject()
{
    myMainWindow = new QMainWindow();

    myTable = new QicsTable(dt, myMainWindow);
    myMainWindow->setCentralWidget(myTable);

    QMenuBar *menu = myMainWindow->menuBar();

    QMenu *file = new QMenu("&File",myMainWindow);
    QAction *exit = file->addAction("E&xit");
    connect(exit,SIGNAL(triggered()), qApp, SLOT(quit()));

    menu->addMenu(file);

    QLabel *label = new QLabel(QString("Stock Activity"), myTable);
    QFont fnt = label->font();
    fnt.setPointSize(fnt.pointSize() + 6);
    label->setFont(fnt);
    label->setAlignment(Qt::AlignCenter);
    myTable->setTopTitleWidget(label);

    setupRowHeader();
    setupColumnHeader();
    setupGrid();

    QCheckBox *cb = new QCheckBox(QString("Auto\nUpdates"), myTable);
    myTable->setTopLeftCornerWidget(cb);
    connect(cb, SIGNAL(toggled(bool)),
        this, SIGNAL(updateToggled(bool)));

    myMainWindow->show();
}

StockUI::~StockUI()
{
    delete myTable;
}

void
StockUI::setupRowHeader(void)
{
    // nothing to do here
}

void
StockUI::setupColumnHeader(void)
{
    QicsColumnHeader *ch = myTable->columnHeader();

    ch->setNumRows(3);

    ch->setAlignment(Qt::AlignCenter);

    // Stock header
    ch->addCellSpan(QicsSpan(0,stock_name, 3, 1));
    ch->cellRef(0,stock_name).setLabel(QString("Stock"));

    setupDay(day_zero, QString("Today"));
    setupDay(day_one, QString("One Day Ago"));
    setupDay(day_two, QString("Two Days Ago"));
    setupDay(day_three, QString("Three Days Ago"));

    delete ch;
}

void
StockUI::setupDay(int day_idx, QString day)
{
    QicsColumnHeader *ch = myTable->columnHeader();

    ch->addCellSpan(QicsSpan(0,day_idx, 1, 3));
    ch->cellRef(0,day_idx).setLabel(QString(day));

    ch->cellRef(0,day_idx).setForegroundColor(Qt::white);
    ch->cellRef(0,day_idx).setBackgroundColor(Qt::blue);

    ch->addCellSpan(QicsSpan(1,day_idx, 1, 2));
    ch->cellRef(1,day_idx).setLabel(QString("Show Gains in"));

    ch->dataModel()->setItem(1,day_idx+2, QicsDataString("Green"));
    QicsComboCellDisplay *cd = new QicsComboCellDisplay();
    cd->addItem(QString("Green"));
    cd->addItem(QString("Red"));
    ch->cellRef(1,day_idx+2).setDisplayer(cd);

    // Setting palette to combo boxes
    QPalette pal = ch->palette();
    pal.setColor(QPalette::Base,"white");
    ch->cellRef(1,day_idx+2).setPalette(pal);

    ch->cellRef(2,day_idx+cur_idx).setLabel(QString("Current"));

    ch->cellRef(2,day_idx+high_idx).setLabel(QString("High"));
    ch->cellRef(2,day_idx+high_idx).setPixmap(QPixmap(high_xpm));

    ch->cellRef(2,day_idx+low_idx).setLabel(QString("Low"));
    ch->cellRef(2,day_idx+low_idx).setPixmap(QPixmap(low_xpm));

    delete ch;
}

void
StockUI::setupGrid(void)
{
    QicsDataItemSprintfFormatter *v = new QicsDataItemSprintfFormatter();

    myTable->columnRef(0).setWidthInChars(7);

    v->addFormatString(QicsDataItem_Float, "$%.2f");
    v->addFormatString(QicsDataItem_Int, "$%d.00");
    myTable->mainGridRef().setFormatter(v);

    for (int i = day_zero; i <= (day_three + 2); ++i)
    {
    myTable->columnRef(i).setAlignment(Qt::AlignRight);
    }

    // the NYSE and NASDAQ rows...
    QFont fnt = myTable->font();
    fnt.setPointSize(fnt.pointSize() + 2);
    fnt.setBold(true);
    myTable->rowRef(nyse_idx).setFont(fnt);
    myTable->rowRef(nasdaq_idx).setFont(fnt);

    v = new QicsDataItemSprintfFormatter();
    v->addFormatString(QicsDataItem_Float, "%.2f");
    v->addFormatString(QicsDataItem_Int, "%d.00");
    myTable->rowRef(nyse_idx).setFormatter(v);
    myTable->rowRef(nasdaq_idx).setFormatter(v);

    myTable->freezeTopRows(2);
}

void
StockUI::decorateStock(int idx, bool gainer)
{
    const QicsDataItem *itm =
    myTable->columnHeaderRef().cellRef(1, day_zero+2).dataValue();
    QString gain_color = itm->string();

    QColor c;

    if (gainer)
    c = QColor(gain_color);
    else
    c = myTable->mainGridRef().foregroundColor();

    myTable->cellRef(idx, day_zero+cur_idx).setForegroundColor(c);
}


class StockApp : public QApplication
{
    Q_OBJECT
public:
    StockApp(int &argc, char **argv);
    ~StockApp();

protected slots:
    void toggleUpdates(bool on);
    void updateStocks(void);

protected:
    StockData *mySD;
    StockUI *myUI;
    QTimer *myTimer;

    static int SortFloats(const QicsDataItem *a, const QicsDataItem *b);
};

StockApp::StockApp(int &argc, char **argv) :
    QApplication(argc, argv)
{
    mySD = new StockData();
    myUI = new StockUI(mySD->dataModel());

    connect(myUI, SIGNAL(updateToggled(bool)),
        this, SLOT(toggleUpdates(bool)));

    myTimer = new QTimer();
    connect(myTimer, SIGNAL(timeout()),
        this, SLOT(updateStocks()));

    connect(mySD, SIGNAL(stockChanged(int, bool)),
        myUI, SLOT(decorateStock(int, bool)));
}

StockApp::~StockApp()
{
    delete myUI;
    delete mySD;
    delete myTimer;
}

void
StockApp::toggleUpdates(bool on)
{
    if (on)
    myTimer->start(update_period);
    else
    myTimer->stop();
}

void
StockApp::updateStocks(void)
{
    mySD->updateStocks();

    myUI->table()->sortRows(day_zero+cur_idx, Qics::Descending, 2, -1, &SortFloats);
}

int
StockApp::SortFloats(const QicsDataItem *a, const QicsDataItem *b)
{
    if ((a->type() == QicsDataItem_Float) &&
    (b->type() == QicsDataItem_Float))
    {
    float fa = ((const QicsDataFloat *) a)->data();
    float fb = ((const QicsDataFloat *) b)->data();

    if (fa < fb)
        return -1;
    else if (fa == fb)
        return 0;
    else
        return 1;
    }
    else
    {
    qDebug("not floats");
        QString sa = a->string();
        QString sb = b->string();
        return sa.compare(sb);
    }
}


int main(int argc, char *argv[])
{
    StockApp app(argc,argv);

    return app.exec();
}


#include "stocks.moc"

All trademarks and copyrights on this page are properties of their respective owners.
The rest is copyright ©1999-2006 Integrated Computer Solutions, Inc.