예제 #1
0
static int sse50etf_exec(void *data, void *data2) {
	RAII_VAR(struct msg *, msg, (struct msg *)data, msg_decr);
	Quote *quote = (Quote *)msg->data;
	struct wp *wp;
	NOT_USED(data2);

	if (isnan(cru) || isnan(ecc)) {
		xcb_log(XCB_LOG_WARNING, "No valid value for CRU or ECC");
		goto end;
	}
	table_lock(contracts);
	if ((wp = table_get_value(contracts, quote->thyquote.m_cHYDM)) &&
		(isnan(wp->price) || fabs(wp->price - quote->thyquote.m_dZXJ) > 0.000001)) {
		table_iter_t iter;
		table_node_t node;
		double sum = 0.0;
		time_t t = (time_t)quote->thyquote.m_nTime;
		struct tm lt;
		char datestr[64], res[512];

		if (wp->type == 2) {
			table_unlock(contracts);
			goto end;
		}
		wp->price = quote->thyquote.m_dZXJ;
		iter = table_iter_new(contracts);
		while ((node = table_next(iter))) {
			wp = table_node_value(node);
			if (isnan(wp->price)) {
				xcb_log(XCB_LOG_WARNING, "No price info for '%s'", table_node_key(node));
				table_iter_free(&iter);
				table_unlock(contracts);
				goto end;
			}
			sum += wp->weight * wp->price;
		}
		table_iter_free(&iter);
		strftime(datestr, sizeof datestr, "%F %T", localtime_r(&t, &lt));
		snprintf(res, sizeof res, "SSE50ETF,%s.%03d|%f",
			datestr,
			quote->m_nMSec,
			(sum + ecc) / cru);
		out2rmp(res);
	}
	table_unlock(contracts);

end:
	return 0;
}
예제 #2
0
파일: app_pd.c 프로젝트: ArkShen/xcb
static int pd_exec(void *data, void *data2) {
	RAII_VAR(struct msg *, msg, (struct msg *)data, msg_decr);
	Quote *quote = (Quote *)msg->data;
	dlist_t dlist;
	NOT_USED(data2);

	if ((dlist = table_get_value(contracts, quote->thyquote.m_cHYDM))) {
		dlist_iter_t iter = dlist_iter_new(dlist, DLIST_START_HEAD);
		dlist_node_t node;

		while ((node = dlist_next(iter))) {
			struct cpl *cpl = (struct cpl *)dlist_node_value(node);

			pthread_spin_lock(&cpl->lock);
			if (!strcasecmp(cpl->contract1, quote->thyquote.m_cHYDM))
				cpl->price1 = quote->thyquote.m_dZXJ;
			else
				cpl->price2 = quote->thyquote.m_dZXJ;
			if (cpl->price1 > 0.0 && cpl->price2 > 0.0) {
				float pd = fabs(cpl->price1 - cpl->price2);

				/* If the price diff changes, we output it. */
				if (fabs(pd - cpl->prevpd) > 0.000001) {
					time_t t = (time_t)quote->thyquote.m_nTime;
					struct tm lt;
					char datestr[64], res[512];

					strftime(datestr, sizeof datestr, "%F %T", localtime_r(&t, &lt));
					snprintf(res, sizeof res, "PD,%s.%03d,%s,%s|%.2f,%.2f,%.2f",
						datestr,
						quote->m_nMSec,
						cpl->contract1,
						cpl->contract2,
						cpl->price1,
						cpl->price2,
						pd);
					out2rmp(res);
					cpl->prevpd = pd;
				}
			}
			pthread_spin_unlock(&cpl->lock);
		}
	}
	return 0;
}
예제 #3
0
파일: app_hello.c 프로젝트: wang-shun/xcb
static int hello_exec(void *data, void *data2) {
	RAII_VAR(struct msg *, msg, (struct msg *)data, msg_decr);
	Quote *quote = (Quote *)msg->data;
	time_t t = (time_t)quote->thyquote.m_nTime;
	struct tm lt;
	char datestr[64], res[512];
	NOT_USED(data2);

	strftime(datestr, sizeof datestr, "%F %T", localtime_r(&t, &lt));
	snprintf(res, sizeof res, "HELLO,%s.%03d,%s|%.2f,%.2f,%.2f",
		datestr,
		quote->m_nMSec,
		quote->thyquote.m_cHYDM,
		quote->thyquote.m_dZXJ,
		quote->thyquote.m_dMRJG1,
		quote->thyquote.m_dMCJG1);
	out2rmp(res);
	return 0;
}
예제 #4
0
파일: app_ema.c 프로젝트: ArkShen/xcb
static int ema_exec(void *data, void *data2) {
	RAII_VAR(struct msg *, msg, (struct msg *)data, msg_decr);
	Quote *quote = (Quote *)msg->data;
	float *price;
	map_iter_t *iter = map_iter_create();
	deq_t *prices;
	int size;
	NOT_USED(data2);

	/* FIXME */
	if (fabs(quote->thyquote.m_dZXJ) <= 0.000001) {
		xcb_log(XCB_LOG_WARNING, "Invalid quote: '%d,%d,%s,%.2f'",
			quote->thyquote.m_nTime,
			quote->m_nMSec,
			quote->thyquote.m_cHYDM,
			quote->thyquote.m_dZXJ);
		goto end;
	}
	if ((price = ALLOC(sizeof (float))) == NULL) {
		xcb_log(XCB_LOG_WARNING, "Error allocating memory for price");
		goto end;
	}
	*price = quote->thyquote.m_dZXJ;
	pthread_mutex_lock(&conlock);
	map_find(iter, contracts, quote->thyquote.m_cHYDM);
	if (!map_iter_valid(iter, contracts)) {
		const char *contract;

		if ((contract = mem_strdup(quote->thyquote.m_cHYDM)) == NULL) {
			xcb_log(XCB_LOG_WARNING, "Error allocating memory for contract");
			pthread_mutex_unlock(&conlock);
			FREE(price);
			goto end;
		}
		prices = deq_create();
		map_insert(contracts, contract, prices);
	} else
		prices = map_iter_value(iter);
	deq_push_back(prices, price);
	if ((size = deq_size(prices)) == n) {
		int i;
		float ema = *((float *)deq_at(prices, 0));
		time_t t = (time_t)quote->thyquote.m_nTime;
		struct tm lt;
		char datestr[64], res[256];
		float *front;

		for (i = 1; i < size; ++i)
			ema = (2.0 / (n + 1)) * *((float *)deq_at(prices, i)) + (1 - (2.0 / (n + 1))) * ema;
		strftime(datestr, sizeof datestr, "%F %T", localtime_r(&t, &lt));
		snprintf(res, sizeof res, "EMA,%s.%03d,%s|%.2f",
			datestr,
			quote->m_nMSec,
			quote->thyquote.m_cHYDM,
			ema);
		out2rmp(res);
		front = deq_front(prices);
		FREE(front);
		deq_pop_front(prices);
	}
	pthread_mutex_unlock(&conlock);
	map_iter_destroy(iter);

end:
	return 0;
}
예제 #5
0
static int impvbaw_exec(void *data, void *data2) {
	RAII_VAR(struct msg *, msg, (struct msg *)data, msg_decr);
	Quote *quote = (Quote *)msg->data;
	struct msgs *out = (struct msgs *)data2;
	dstr contract;
	float last;
	char *p;
	table_node_t node;

	contract = dstr_new(quote->thyquote.m_cHYDM);
	if (!strcmp(contract, "") || (fabs(quote->thyquote.m_dZXJ) <= 0.000001 &&
		fabs(quote->thyquote.m_dMRJG1) <= 0.000001 &&
		fabs(quote->thyquote.m_dMCJG1) <= 0.000001)) {
		xcb_log(XCB_LOG_WARNING, "Invalid quote: '%d,%d,%s,%.2f,%.2f,%.2f'",
			quote->thyquote.m_nTime,
			quote->m_nMSec,
			contract,
			quote->thyquote.m_dZXJ,
			quote->thyquote.m_dMRJG1,
			quote->thyquote.m_dMCJG1);
		goto end;
	}
	/* FIXME */
	if (!strncasecmp(contract, "IO", 2) || !strncasecmp(contract, "HO", 2) ||
		!strncasecmp(contract, "00", 2) || !strncasecmp(contract, "60", 2))
		goto end;
	last = quote->thyquote.m_dZXJ;
	if ((p = strrchr(contract, 'C')) == NULL)
		p = strrchr(contract, 'P');
	/* FIXME: option quote */
	if (p && p != contract && p != contract + dstr_length(contract) - 1 &&
		((*(p - 1) == '-' && *(p + 1) == '-') || (isdigit(*(p - 1)) && isdigit(*(p + 1))))) {
		dstr spotname, type, strike;
		float spot;
		struct prices *prices;
		double expiry, vol, vol2, vol3;
		struct tm lt;
		char *res;

		spotname = *(p - 1) == '-' ? dstr_new_len(contract, p - contract - 1) :
			dstr_new_len(contract, p - contract);
		type     = dstr_new_len(p, 1);
		strike   = *(p + 1) == '-' ? dstr_new_len(p + 2, contract + dstr_length(contract) - p - 2) :
			dstr_new_len(p + 1, contract + dstr_length(contract) - p - 1);
		table_rwlock_rdlock(spots);
		if ((node = table_find(spots, spotname)) == NULL) {
			table_rwlock_unlock(spots);
			dstr_free(strike);
			dstr_free(type);
			dstr_free(spotname);
			goto end;
		}
		spot = table_node_float(node);
		table_rwlock_unlock(spots);
		if (fabs(spot) <= 0.000001) {
			xcb_log(XCB_LOG_WARNING, "The price of spot '%s' be zero", spotname);
			dstr_free(strike);
			dstr_free(type);
			dstr_free(spotname);
			goto end;
		}
		table_lock(optns);
		if ((node = table_find(optns, contract)) == NULL) {
			if (NEW(prices)) {
				prices->prelast = last;
				prices->prebid1 = quote->thyquote.m_dMRJG1;
				prices->preask1 = quote->thyquote.m_dMCJG1;
				prices->prespot = spot;
				table_insert(optns, dstr_new(contract), prices);
			} else
				xcb_log(XCB_LOG_WARNING, "Error allocating memory for prices");
		} else {
			prices = (struct prices *)table_node_value(node);

			if (fabs(prices->prelast - last) <= 0.000001 &&
				fabs(prices->prebid1 - quote->thyquote.m_dMRJG1) <= 0.000001 &&
				fabs(prices->preask1 - quote->thyquote.m_dMCJG1) <= 0.000001 &&
				fabs(prices->prespot - spot) <= 0.000001) {
				table_unlock(optns);
				dstr_free(strike);
				dstr_free(type);
				dstr_free(spotname);
				goto end;
			} else {
				prices->prelast = last;
				prices->prebid1 = quote->thyquote.m_dMRJG1;
				prices->preask1 = quote->thyquote.m_dMCJG1;
				prices->prespot = spot;
			}
		}
		table_unlock(optns);
		if ((node = table_find(expiries, contract)))
			expiry = table_node_double(node);
		else {
			char *month;
			struct timeval tv;
			int diff;

			xcb_log(XCB_LOG_WARNING, "No exact expiry date for '%s'", contract);
			month = spotname + dstr_length(spotname) - 2;
			gettimeofday(&tv, NULL);
			if ((diff = atoi(month) - localtime_r(&tv.tv_sec, &lt)->tm_mon - 1) < 0)
				diff += 12;
			if (diff == 0)
				diff = 1;
			expiry = diff / 12.0;
		}
		/* FIXME: last price */
		if (fabs(last) <= 0.000001)
			vol = NAN;
		else
			vol = impv_baw(spot, atof(strike), r, r, expiry, last,
				!strcasecmp(type, "C") ? AMER_CALL : AMER_PUT);
		/* FIXME: bid price 1 */
		if (fabs(quote->thyquote.m_dMRJG1) <= 0.000001)
			vol2 = NAN;
		else if (fabs(quote->thyquote.m_dMRJG1 - last) <= 0.000001)
			vol2 = vol;
		else
			vol2 = impv_baw(spot, atof(strike), r, r, expiry, quote->thyquote.m_dMRJG1,
				!strcasecmp(type, "C") ? AMER_CALL : AMER_PUT);
		/* FIXME: ask price 1 */
		if (fabs(quote->thyquote.m_dMCJG1) <= 0.000001)
			vol3 = NAN;
		else if (fabs(quote->thyquote.m_dMCJG1 - last) <= 0.000001)
			vol3 = vol;
		else
			vol3 = impv_baw(spot, atof(strike), r, r, expiry, quote->thyquote.m_dMCJG1,
				!strcasecmp(type, "C") ? AMER_CALL : AMER_PUT);
		if ((res = ALLOC(512))) {
			time_t t = (time_t)quote->thyquote.m_nTime;
			char datestr[64];

			strftime(datestr, sizeof datestr, "%F %T", localtime_r(&t, &lt));
			snprintf(res, 512, "IMPVBAW,%s.%03d,%s|%.2f,%f,%.2f,%f,%.2f,%f,%.2f",
				datestr,
				quote->m_nMSec,
				contract,
				last,
				vol,
				quote->thyquote.m_dMRJG1,
				vol2,
				quote->thyquote.m_dMCJG1,
				vol3,
				spot);
			out2rmp(res);
			snprintf(res, 512, "IMPVBAW,%d,%d,%s,%.2f,%f,%.2f,%f,%.2f,%f,%.2f,%s,%s,%s,%f,%f,%d,0,0",
				quote->thyquote.m_nTime,
				quote->m_nMSec,
				contract,
				last,
				vol,
				quote->thyquote.m_dMRJG1,
				vol2,
				quote->thyquote.m_dMCJG1,
				vol3,
				spot,
				spotname,
				type,
				strike,
				r,
				expiry,
				quote->thyquote.m_nCJSL);
			if (out2msgs(res, out) == -1)
				FREE(res);
		} else
			xcb_log(XCB_LOG_WARNING, "Error allocating memory for result");
		dstr_free(strike);
		dstr_free(type);
		dstr_free(spotname);
	/* future quote */
	} else {
		table_rwlock_wrlock(spots);
		if ((node = table_find(spots, contract)) == NULL) {
			if ((node = table_insert_raw(spots, contract)))
				table_set_float(node, last);
		} else {
			table_set_float(node, last);
			dstr_free(contract);
		}
		table_rwlock_unlock(spots);
		return 0;
	}

end:
	dstr_free(contract);
	return 0;
}
예제 #6
0
static int mm_impvbaw_exec(void *data, void *data2) {
	RAII_VAR(struct msg *, msg, (struct msg *)data, msg_decr);
	dstr *fields = NULL;
	int nfield = 0;
	time_t t;
	dstr spotname;
	double vol;
	NOT_USED(data2);

	fields = dstr_split_len(msg->data, strlen(msg->data), ",", 1, &nfield);
	/* FIXME */
	if (nfield != 19) {
		xcb_log(XCB_LOG_WARNING, "Message '%s' garbled", msg->data);
		goto end;
	}
	t        = (time_t)atoi(fields[1]);
	spotname = dstr_new(fields[11]);
	vol      = atof(fields[5]);
	if (!isnan(vol)) {
		struct mm *mm;
		int flag = 0;

		table_lock(contracts);
		if ((mm = table_get_value(contracts, spotname)) == NULL) {
			if (NEW(mm)) {
				mm->min = mm->max = vol;
				flag = 1;
				table_insert(contracts, spotname, mm);
			} else
				xcb_log(XCB_LOG_WARNING, "Error allocating memory for mm");
		} else {
			if (mm->min > vol) {
				mm->min = vol;
				flag = 1;
			} else if (mm->max < vol) {
				mm->max = vol;
				flag = 1;
			}
			dstr_free(spotname);
		}
		if (mm && flag) {
			struct tm lt;
			char datestr[64], res[512];

			localtime_r(&t, &lt);
			lt.tm_hour = lt.tm_min = lt.tm_sec = 0;
			strftime(datestr, sizeof datestr, "%F %T", &lt);
			snprintf(res, sizeof res, "MM_IMPVBAW,%s.000,%s|%f,%f",
				datestr,
				fields[11],
				mm->min,
				mm->max);
			out2rmp(res);
		}
		table_unlock(contracts);
	}

end:
	dstr_free_tokens(fields, nfield);
	return 0;
}
예제 #7
0
static int vxo_exec(void *data, void *data2) {
	RAII_VAR(struct msg *, msg, (struct msg *)data, msg_decr);
	struct msgs *out = (struct msgs *)data2;
	dstr *fields = NULL;
	int nfield = 0;
	double vol, vol2, vol3, spot, strike, r, expiry;
	int sec, msec;
	dstr spotname;
	char *type;
	struct pd *pd;
	struct scp *scp;

	fields = dstr_split_len(msg->data, strlen(msg->data), ",", 1, &nfield);
	/* FIXME */
	if (nfield != 19) {
		xcb_log(XCB_LOG_WARNING, "Message '%s' garbled", msg->data);
		goto end;
	}
	vol      = !strcasecmp(fields[5], "nan") ? NAN : atof(fields[5]);
	vol2     = !strcasecmp(fields[7], "nan") ? NAN : atof(fields[7]);
	vol3     = !strcasecmp(fields[9], "nan") ? NAN : atof(fields[9]);
	if (isnan(vol) && isnan(vol2) && isnan(vol3))
		goto end;
	sec      = atoi(fields[1]);
	msec     = atoi(fields[2]);
	spot     = atof(fields[10]);
	spotname = dstr_new(fields[11]);
	type     = fields[12];
	strike   = atof(fields[13]);
	r        = atof(fields[14]);
	expiry   = atof(fields[15]);
	table_lock(spots);
	if ((pd = table_get_value(spots, spotname)) == NULL) {
		/* can't happen */
		if (NEW(scp) == NULL) {
			xcb_log(XCB_LOG_WARNING, "Error allocating memory for scp");
			table_unlock(spots);
			goto end;
		}
		scp->strike = strike;
		if (!strcasecmp(type, "C")) {
			scp->cvol  = vol;
			scp->pvol  = NAN;
			scp->cvol2 = vol2;
			scp->pvol2 = NAN;
			scp->cvol3 = vol3;
			scp->pvol3 = NAN;
		} else {
			scp->cvol  = NAN;
			scp->pvol  = vol;
			scp->cvol2 = NAN;
			scp->pvol2 = vol2;
			scp->cvol3 = NAN;
			scp->pvol3 = vol3;
		}
		if (NEW(pd) == NULL) {
			xcb_log(XCB_LOG_WARNING, "Error allocating memory for pd");
			FREE(scp);
			table_unlock(spots);
			goto end;
		}
		pd->prevxo = pd->prevxo2 = pd->prevxo3 = NAN;
		pd->sep    = strchr(fields[3], '-') ? "-" : "";
		pd->dlist  = dlist_new(NULL, scpfree);
		dlist_insert_tail(pd->dlist, scp);
		table_insert(spots, spotname, pd);
	} else {
		dlist_iter_t iter = dlist_iter_new(pd->dlist, DLIST_START_HEAD);
		dlist_node_t node;

		while ((node = dlist_next(iter))) {
			scp = (struct scp *)dlist_node_value(node);
			if (scp->strike > strike || fabs(scp->strike - strike) <= 0.000001)
				break;
		}
		if (node && fabs(scp->strike - strike) <= 0.000001) {
			if (!strcasecmp(type, "C")) {
				scp->cvol  = vol;
				scp->cvol2 = vol2;
				scp->cvol3 = vol3;
			} else {
				scp->pvol  = vol;
				scp->pvol2 = vol2;
				scp->pvol3 = vol3;
			}
		} else {
			/* can't happen */
			if (NEW(scp) == NULL) {
				xcb_log(XCB_LOG_WARNING, "Error allocating memory for scp");
				table_unlock(spots);
				goto end;
			}
			scp->strike = strike;
			if (!strcasecmp(type, "C")) {
				scp->cvol  = vol;
				scp->pvol  = NAN;
				scp->cvol2 = vol2;
				scp->pvol2 = NAN;
				scp->cvol3 = vol3;
				scp->pvol3 = NAN;
			} else {
				scp->cvol  = NAN;
				scp->pvol  = vol;
				scp->cvol2 = NAN;
				scp->pvol2 = vol2;
				scp->cvol3 = NAN;
				scp->pvol3 = vol3;
			}
			if (node == NULL)
				dlist_insert_tail(pd->dlist, scp);
			else
				dlist_insert(pd->dlist, node, scp, 0);
		}
		dlist_iter_rewind_head(iter, pd->dlist);
		while ((node = dlist_next(iter))) {
			scp = (struct scp *)dlist_node_value(node);
			if (scp->strike > spot || fabs(scp->strike - spot) <= 0.000001)
				break;
		}
		dlist_iter_free(&iter);
		if (node && dlist_node_prev(node)) {
			struct scp *scp1 = (struct scp *)dlist_node_value(node);
			struct scp *scp2 = (struct scp *)dlist_node_value(dlist_node_prev(node));
			double vxo = NAN, vxo2 = NAN, vxo3 = NAN;
			int flag1, flag2, flag3;

			if (!isnan(scp1->cvol) && !isnan(scp1->pvol) && !isnan(scp2->cvol) && !isnan(scp2->pvol))
				vxo  = ((scp1->cvol + scp1->pvol) / 2) *
					(spot - scp2->strike) / (scp1->strike - scp2->strike) +
					((scp2->cvol + scp2->pvol) / 2) *
					(scp1->strike - spot) / (scp1->strike - scp2->strike);
			if (!isnan(scp1->cvol2) && !isnan(scp1->pvol2) && !isnan(scp2->cvol2) && !isnan(scp2->pvol2))
				vxo2 = ((scp1->cvol2 + scp1->pvol2) / 2) *
					(spot - scp2->strike) / (scp1->strike - scp2->strike) +
					((scp2->cvol2 + scp2->pvol2) / 2) *
					(scp1->strike - spot) / (scp1->strike - scp2->strike);
			if (!isnan(scp1->cvol3) && !isnan(scp1->pvol3) && !isnan(scp2->cvol3) && !isnan(scp2->pvol3))
				vxo3 = ((scp1->cvol3 + scp1->pvol3) / 2) *
					(spot - scp2->strike) / (scp1->strike - scp2->strike) +
					((scp2->cvol3 + scp2->pvol3) / 2) *
					(scp1->strike - spot) / (scp1->strike - scp2->strike);
			if ((flag1 = (isnan(pd->prevxo) && !isnan(vxo)) || (!isnan(pd->prevxo) && isnan(vxo)) ||
				(!isnan(pd->prevxo) && !isnan(vxo) && fabs(pd->prevxo - vxo) > 0.000001) ? 1 : 0))
				pd->prevxo  = vxo;
			if ((flag2 = (isnan(pd->prevxo2) && !isnan(vxo2)) || (!isnan(pd->prevxo2) && isnan(vxo2)) ||
				(!isnan(pd->prevxo2) && !isnan(vxo2) && fabs(pd->prevxo2 - vxo2) > 0.000001) ? 1 : 0))
				pd->prevxo2 = vxo2;
			if ((flag3 = (isnan(pd->prevxo3) && !isnan(vxo3)) || (!isnan(pd->prevxo3) && isnan(vxo3)) ||
				(!isnan(pd->prevxo3) && !isnan(vxo3) && fabs(pd->prevxo3 - vxo3) > 0.000001) ? 1 : 0))
				pd->prevxo3 = vxo3;
			if (flag1 || flag2 || flag3) {
				char *res;

				if ((res = ALLOC(4096))) {
					time_t t = (time_t)sec;
					struct tm lt;
					char datestr[64];
					int off;

					strftime(datestr, sizeof datestr, "%F %T", localtime_r(&t, &lt));
					snprintf(res, 4096, "VXO,%s.%03d,%s|%f,%f,%f",
						datestr,
						msec,
						spotname,
						vxo,
						vxo2,
						vxo3);
					out2rmp(res);
					off = snprintf(res, 4096, "VXO,%d,%d,%s,",
						sec,
						msec,
						spotname);
					iter = dlist_iter_new(pd->dlist, DLIST_START_HEAD);
					while ((node = dlist_next(iter))) {
						scp = (struct scp *)dlist_node_value(node);
						off += snprintf(res + off, 4096 - off, "%.f,%f,%f,%f,",
							scp->strike,
							vxo,
							vxo2,
							vxo3);
					}
					dlist_iter_free(&iter);
					snprintf(res + off, 4096 - off, "%.2f,%f,%f,%s",
						spot,
						r,
						expiry,
						pd->sep);
					if (out2msgs(res, out) == -1)
						FREE(res);
				}
			}
		}
		dstr_free(spotname);
	}
	table_unlock(spots);

end:
	dstr_free_tokens(fields, nfield);
	return 0;
}
예제 #8
0
static int greeks2_exec(void *data, void *data2) {
	RAII_VAR(struct msg *, msg, (struct msg *)data, msg_decr);
	dstr *fields = NULL;
	int nfield = 0;
	time_t t;
	char *contract, *type;
	double spot, strike, r, vol, vol2, vol3, expiry;
	int steps;
	NOT_USED(data2);

	fields = dstr_split_len(msg->data, strlen(msg->data), ",", 1, &nfield);
	/* FIXME */
	if (nfield != 19) {
		xcb_log(XCB_LOG_WARNING, "Message '%s' garbled", msg->data);
		goto end;
	}
	t        = (time_t)atoi(fields[1]);
	contract = fields[3];
	type     = fields[12];
	spot     = atof(fields[10]);
	strike   = atof(fields[13]);
	r        = atof(fields[14]);
	vol      = atof(fields[5]);
	vol2     = atof(fields[7]);
	vol3     = atof(fields[9]);
	expiry   = atof(fields[15]);
	steps    = atoi(fields[18]);
	if (!isnan(vol) || !isnan(vol2) || !isnan(vol3)) {
		double delta, gamma, theta, vega, rho;
		double delta2, gamma2, theta2, vega2, rho2;
		double delta3, gamma3, theta3, vega3, rho3;
		struct tm lt;
		char datestr[64], res[512];

		if (isnan(vol))
			delta = gamma = theta = vega = rho = NAN;
		else {
			if (!strcasecmp(type, "C"))
				bi_amer_call_greeks(spot, strike, r, r, vol, expiry, steps,
					&delta, &gamma, &theta, &vega, &rho);
			else
				bi_amer_put_greeks (spot, strike, r, r, vol, expiry, steps,
					&delta, &gamma, &theta, &vega, &rho);
		}
		if (isnan(vol2))
			delta2 = gamma2 = theta2 = vega2 = rho2 = NAN;
		else if (fabs(vol2 - vol) <= 0.000001) {
			delta2 = delta;
			gamma2 = gamma;
			theta2 = theta;
			vega2  = vega;
			rho2   = rho;
		} else {
			if (!strcasecmp(type, "C"))
				bi_amer_call_greeks(spot, strike, r, r, vol2, expiry, steps,
					&delta2, &gamma2, &theta2, &vega2, &rho2);
			else
				bi_amer_put_greeks (spot, strike, r, r, vol2, expiry, steps,
					&delta2, &gamma2, &theta2, &vega2, &rho2);
		}
		if (isnan(vol3))
			delta3 = gamma3 = theta3 = vega3 = rho3 = NAN;
		else if (fabs(vol3 - vol) <= 0.000001) {
			delta3 = delta;
			gamma3 = gamma;
			theta3 = theta;
			vega3  = vega;
			rho3   = rho;
		} else {
			if (!strcasecmp(type, "C"))
				bi_amer_call_greeks(spot, strike, r, r, vol3, expiry, steps,
					&delta3, &gamma3, &theta3, &vega3, &rho3);
			else
				bi_amer_put_greeks (spot, strike, r, r, vol3, expiry, steps,
					&delta3, &gamma3, &theta3, &vega3, &rho3);
		}
		strftime(datestr, sizeof datestr, "%F %T", localtime_r(&t, &lt));
		snprintf(res, sizeof res, "GREEKS2,%s.%03d,%s|%.2f,%f,%f,%f,%f,%f,%.2f,%f,%f,%f,%f,%f,%.2f,%f,%f,%f,%f,%f",
			datestr,
			atoi(fields[2]),
			contract,
			atof(fields[4]),
			delta,
			gamma,
			theta,
			vega,
			rho,
			atof(fields[6]),
			delta2,
			gamma2,
			theta2,
			vega2,
			rho2,
			atof(fields[8]),
			delta3,
			gamma3,
			theta3,
			vega3,
			rho3);
		out2rmp(res);
	}

end:
	dstr_free_tokens(fields, nfield);
	return 0;
}