From cceb81ef2b5c89866aec520223b9a13f0e6d2533 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Tue, 19 Jul 2011 17:58:17 +0200 Subject: [PATCH] autotrade::Market::Simulated, autotrade-sim.pl: Support for strategy simulation on earlier data --- autotrade-sim.pl | 53 +++++++++++++++++++++++++++ autotrade/Market/Simulated.pm | 85 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100755 autotrade-sim.pl create mode 100644 autotrade/Market/Simulated.pm diff --git a/autotrade-sim.pl b/autotrade-sim.pl new file mode 100755 index 0000000..623ba01 --- /dev/null +++ b/autotrade-sim.pl @@ -0,0 +1,53 @@ +#!/usr/bin/perl +# +# Automated Bitcoin Trader - Simulator +# (c) Petr Baudis 2011 +# +# Feeds the trader simulated data based on RateHistory database. You can +# import historical data using +# curl 'http://bitcoincharts.com/t/trades.csv?start=0&end='$(date +%s)'&symbol=mtgoxUSD' | ./import-csv.pl +# +# You can render the generated plot using +# gnuplot -persist -e 'plot "sim.plot" using 2 with lines title "buy", "sim.plot" using 3 with lines title "sell", "sim.plot" using 4 with lines title "btc", "sim.plot" using 5 with lines title "usd" axes x1y2, "sim.plot" using 6 with lines title "total" axes x1y2' +# +# Usage: ./autotrate-sim.pl START_BALANCE STARTTIME STEP ENDTIME + +use warnings; +use strict; + + +use autotrade::MarketTools; + +my ($usdbal, $start, $step, $stop) = @ARGV; + +use autotrade::SQL; +use autotrade::RateHistory; +use autotrade::Market::Simulated; +use autotrade::Trader::LowHigh; +use autotrade::Trader::Slope; + +my $dbh = autotrade::SQL::db_connect("autotrade-sim.sqlite"); +$dbh->do("PRAGMA synchronous = OFF"); +$dbh->do("DELETE FROM bitcoin_values"); +my $rh = autotrade::RateHistory->new(dbh => $dbh); +my $m = autotrade::Market::Simulated->new(bal_btc => 0, bal_usd => str_curr($usdbal), rh => $rh); +my $t = autotrade::Trader::Slope->new(dbh => $dbh, rh => $rh, m => $m, time_window => 1200, raise_threshold => 0.25, fall_threshold => -0.25); + +open PLOT, ">sim.plot"; + +while ($start < $stop) { + printf "%d: %s\n", $start, scalar localtime($start); + my ($md, $n); + do { + $md = $m->market_data($start); + } while ($t->beat($md, $start)); + printf PLOT "%d %s %s %s %s %s\n", time, + curr_str($md->rate_buybtc()), curr_str($md->rate_sellbtc()), + curr_str($m->bal_btc()), curr_str($m->bal_usd()), + curr_str(curr_conv($m->bal_btc(), $md->rate_buybtc()) + $m->bal_usd()); + $start += $step; + print "\n"; +} + +printf "=== Final balance: %s BTC, %s USD => %s USD\n", + curr_str($m->bal_btc()), curr_str($m->bal_usd()), curr_str(curr_conv($m->bal_btc(), $m->market_data($start)->rate_buybtc()) + $m->bal_usd()); diff --git a/autotrade/Market/Simulated.pm b/autotrade/Market/Simulated.pm new file mode 100644 index 0000000..924de38 --- /dev/null +++ b/autotrade/Market/Simulated.pm @@ -0,0 +1,85 @@ +package autotrade::Market::Simulated; + +use Moose; +extends 'autotrade::Market'; +use autotrade::MarketTools; + +sub _build_fee { 0.0065; } + +has '+minbal_rwm' => (isa => 'Curr', is => 'ro', coerce => 1, default => str_curr(0)); +has 'bal_btc' => (isa => 'Curr', is => 'rw', 'required' => 1); +has 'bal_usd' => (isa => 'Curr', is => 'rw', 'required' => 1); +has 'rh' => (isa => 'autotrade::RateHistory', is => 'ro', 'required' => 1); + +override market_data => sub { + my $self = shift; + my ($time) = @_; + return autotrade::MarketData::Simulated->new(m => $self, time => $time); +}; + +override buy => sub { + my $self = shift; + my ($amount, $rate) = @_; + my $price = curr_conv($amount, $rate) * (1 + $self->fee()); + if ($price > $self->bal_usd()) { + printf "! REFUSING BUY: %s BTC @ %s (%s > %s)\n", + curr_str($amount), curr_str($rate), curr_str($price), curr_str($self->bal_usd()); + return undef; + } + $self->bal_usd($self->bal_usd() - $price); + $self->bal_btc($self->bal_btc() + $amount); +}; + +override sell => sub { + my $self = shift; + my ($amount, $rate) = @_; + if ($amount > $self->bal_btc()) { + printf "! REFUSING SELL: %s BTC (> %s)\n", + curr_str($amount), curr_str($self->bal_btc()); + return undef; + } + my $price = curr_conv($amount, $rate) * (1 - $self->fee()); + $self->bal_usd($self->bal_usd() + $price); + $self->bal_btc($self->bal_btc() - $amount); +}; + +override cancel => sub { + my $self = shift; + my ($o) = @_; + print "! CANCEL NOT SUPPORTED\n"; +}; + +no Moose; +__PACKAGE__->meta->make_immutable; + + +package autotrade::MarketData::Simulated; + +use Moose; +extends 'autotrade::MarketData'; +use autotrade::MarketTools; + +has '+m' => (isa => 'autotrade::Market::Simulated', is => 'ro', required => 1); +has 'time' => (isa => 'Int', is => 'ro', required => 1); + +# Info on rates. +has 'ticker' => (isa => 'HashRef', is => 'ro', lazy_build => 1); +sub _build_ticker { + my $self = shift; + my ($time, $buy, $sell) = @{$self->m()->rh()->retrieve_at($self->time())}; + return { buy => $buy, sell => $sell }; +} + +sub _build_bal_btc { my $self = shift; $self->m()->bal_btc(); } +sub _build_bal_rwm { my $self = shift; $self->m()->bal_usd(); } + +sub _build_rate_buybtc { my $self = shift; $self->ticker()->{buy}; } +sub _build_rate_sellbtc { my $self = shift; $self->ticker()->{sell}; } + +sub _build_orders { + my $self = shift; + []; +} + +no Moose; +__PACKAGE__->meta->make_immutable; -- 2.11.4.GIT