It is always fun to put code out in public, so yeah.... this is just a proof of concept for people that may be less experienced with software development; a platform with which to start tinkering. Or maybe ask questions. Or maybe contribute some code.
I'ma just gonna paste the entire thing in here and see what happens:
Code:
<?php
/*
* By: cclites@minzie.com 2013-04-22
* For: Minzie.com
* Version: .01
* BTC Donations: 19aTxxfEm6JSRHnW9yektMbwfyxaySWfKv
*
*
* Implements the Bitstamp api.
* https://www.bitstamp.net/api/
*/
writeLog(".");
writeLog("**************************************");
writeLog("**************************************");
writeLog("bot is starting up");
writeLog("**************************************");
writeLog("**************************************");
$usd = 0; //Amount in account of user in US dollars
$btc = 0.03; //Amount of Bitcoin in User account
$lastClose = array(); //Array of closing prices, with the last being the most current.
$direction = 0; //Current trend
// - incremented if increasing
// - decremented if decreasing
$maxIncrease = .05; //Percent increase from last sale
$maxDecrease = .05; //Percent decrease from last sale
$tradingFee = .05; //Percent of trading fee
$lastPurchase = array(); //Array of prices of purchases, with the last being the most current.
$lastSale = array(); //Array of prices of sales, with the last being the most current.
$failSafe = 0; //This is the BTC floor. Sell nothing past this amount, whatever it may be.
$base = 123.97; //Base price in USD for calculating buy/sell points.
$drop = 5; //US dollar decrease since last sale.
$increase = 10; //US dollar increase since last sale.
$direction = 0; //Direction of last from previous last
$trend = 0; //Number of price-points in current trend
$xchangeFee = .004; //Bitstamp exchange fee .4%
$buyFlag = true; //Flag indicating permission to buy
$sellFlag = true; //Flag indicating permission to sell
/*For transaction testing*/
$index = 0;
$testUsd = 100.00;
$testBtc = 1;
$testPricePoints = array(100, 105, 100);
$usd = $testUsd;
$btc = $testBtc;
/*******************************************
* ROUTINE
********************************************/
updateState();
function updateState()
{
global $lastClose, $lastPurchase, $lastSale, $direction, $usd, $btc, $base, $drop,
$increase, $trend, $direction, $xchangeFee;
$ticker = null;
/*** Retrieve ticker to get the last close price. ***/
getTicker($ticker);
/****************************************************/
/*** Once we get the ticker, we need to update lastClose ***/
updateLastClose($ticker);
$last = (float)end($lastClose);
$previousLast = (float)prev($lastClose);
/*** Update trend information ****/
if($last < $previousLast)
{
if ($direction == 1) $trend = 0;
else $trend += 1;
$direction = -1;
}
else if ($last > $previousLast)
{
if ($direction == -1) $trend = 0;
else $trend += 1;
$direction = 1;
}
else
{
$trend = 0;
$direction = 0;
}
/**********************************/
/*** Initialize base if seeing this for the first time ***/
if($base == -1) $base = $last;
$totalFee = (1 + $xchangeFee);
$salePoint = (($base * $totalFee) + $increase);
$purchasePoint = (($base - ($base * $xchangeFee)) - $drop);
writeLog("($base * $totalFee) + $increase");
writeLog("($base - ($base * $xchangeFee)) - $drop");
writeLog("SPP: " . $purchasePoint . " PPP: " . $salePoint);
// 'direction' is used to make sure not to trigger a sell
// if the price is still rising (trend), or to trigger
// a buy if the price is still falling.
if ($last > $salePoint && $direction == -1 && $btc > 0 )
{
$direction += 1;
writeLog("SELLING at " . $last . " per Bitcoin. ");
$lastSale[] = $last;
$base = $last;
$usd += ($btc * $last);
$btc = 0;
}
else if ($last < $purchasePoint && $direction == 1 && $usd > 0)
{
$direction -= 1;
writeLog("BUYING at " . $last . " per Bitcoin. ");
$lastPurchase[] = $last;
$base = $last;
$btc += ( (float)$usd/(float)$last);
$usd = 0;
}
writeLog("LAST: " . $last . " PREVIOUS LAST: " . $previousLast);
writeLog("WALLET: USD: " . $usd . " BTC: " . $btc);
writeLog("DIRECTION: " . $direction . " TREND: " . $trend);
writeLog(".");
sleep(30);
// Debugging
/*
global $index, $testPricePoints;
if ($index == count($testPricePoints) )
{
writeLog("<pre>");
print_r($lastClose);
writeLog("</pre><br>");
exit;
}
*/
updateState();
}
/*
* Just saves the last price to the lastClose array
*/
function updateLastClose(&$ticker)
{
global $lastClose, $lastPurchase, $lastSale;
if(end($lastClose) != $ticker->last)
{
$lastClose[] = $ticker->last;
}
// Initialize arrays if seeing this for the first time.
if (count($lastClose) == 1)
{
$lastClose[] = $ticker->last;
$lastPurchase[] = $ticker->last;
$lastSale[] = $ticker->last;
}
}
/*
* Returns the Bitstamp ticker object
*
* stdClass Object
* (
* [high] => 121.00
* [last] => 119.96
* [bid] => 119.96
* [volume] => 8291.58742485
* [low] => 106.00
* [ask] => 120.98
* )
*
* Parameters:
* $ticker - represents the ticker object
*/
function getTicker(&$ticker)
{
global $lastClose, $lastPurchase, $lastSale;
$url = "https://www.bitstamp.net/api/ticker/";
curl($url, $ticker);
}
/*
* Used to simulate a 3 point update with known values to do allow
* some crude testing.
*
* Parameters:
* $ticker - represents the ticker object
*/
function getTestTicker(&$ticker)
{
global $testPricePoints, $index, $lastClose, $lastPurchase, $lastSale;
writeLog("index is $index<br>");
$ticker->last = $testPricePoints[$index];
writeLog("<pre>");
print_r($ticker);
writeLog("</pre><br>");
$index += 1;
}
/*
* Just a wrapper to allow reuse of the curl functionality.
*
* Parameters:
* $result - represents the ticker object
* $url - represents the REST request
*/
function curl($url, &$result)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = json_decode(curl_exec($ch));
curl_close($ch);
return 0;
}
function writeLog($message)
{
$address = 'bot.log';
$timeStamp = date ("D M d H:i:s Y");
$f = fopen($address, "a");
if ($message == "")
{
fwrite( $f, " ");
}
else
{
fwrite( $f, $timeStamp . ": $message\n\r" );
}
fclose( $f );
return 0;
}
?>
Sorry, cut and paste munged the formatting a bit. And sorry to the first person that tries to use this and finds that it doesn't work. I only changed one thing before I posted this. I'm sure it was fine.