bitex_bot 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +18 -0
 - data/.rspec +1 -0
 - data/Gemfile +4 -0
 - data/LICENSE.txt +22 -0
 - data/README.md +50 -0
 - data/Rakefile +1 -0
 - data/bin/bitex_bot +5 -0
 - data/bitex_bot.gemspec +41 -0
 - data/lib/bitex_bot/database.rb +93 -0
 - data/lib/bitex_bot/models/buy_closing_flow.rb +34 -0
 - data/lib/bitex_bot/models/buy_opening_flow.rb +86 -0
 - data/lib/bitex_bot/models/close_buy.rb +7 -0
 - data/lib/bitex_bot/models/close_sell.rb +4 -0
 - data/lib/bitex_bot/models/closing_flow.rb +80 -0
 - data/lib/bitex_bot/models/open_buy.rb +11 -0
 - data/lib/bitex_bot/models/open_sell.rb +11 -0
 - data/lib/bitex_bot/models/opening_flow.rb +114 -0
 - data/lib/bitex_bot/models/order_book_simulator.rb +75 -0
 - data/lib/bitex_bot/models/sell_closing_flow.rb +36 -0
 - data/lib/bitex_bot/models/sell_opening_flow.rb +82 -0
 - data/lib/bitex_bot/robot.rb +173 -0
 - data/lib/bitex_bot/settings.rb +13 -0
 - data/lib/bitex_bot/version.rb +3 -0
 - data/lib/bitex_bot.rb +18 -0
 - data/settings.yml.sample +84 -0
 - data/spec/factories/bitex_buy.rb +12 -0
 - data/spec/factories/bitex_sell.rb +12 -0
 - data/spec/factories/buy_opening_flow.rb +17 -0
 - data/spec/factories/open_buy.rb +17 -0
 - data/spec/factories/open_sell.rb +17 -0
 - data/spec/factories/sell_opening_flow.rb +17 -0
 - data/spec/models/buy_closing_flow_spec.rb +150 -0
 - data/spec/models/buy_opening_flow_spec.rb +154 -0
 - data/spec/models/order_book_simulator_spec.rb +57 -0
 - data/spec/models/robot_spec.rb +103 -0
 - data/spec/models/sell_closing_flow_spec.rb +160 -0
 - data/spec/models/sell_opening_flow_spec.rb +156 -0
 - data/spec/spec_helper.rb +43 -0
 - data/spec/support/bitex_stubs.rb +66 -0
 - data/spec/support/bitstamp_stubs.rb +110 -0
 - metadata +363 -0
 
| 
         @@ -0,0 +1,154 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe BitexBot::BuyOpeningFlow do
         
     | 
| 
      
 4 
     | 
    
         
            +
              before(:each) do
         
     | 
| 
      
 5 
     | 
    
         
            +
                Bitex.api_key = "valid_key"
         
     | 
| 
      
 6 
     | 
    
         
            +
              end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              it { should validate_presence_of :status }
         
     | 
| 
      
 9 
     | 
    
         
            +
              it { should validate_presence_of :price }
         
     | 
| 
      
 10 
     | 
    
         
            +
              it { should validate_presence_of :value_to_use }
         
     | 
| 
      
 11 
     | 
    
         
            +
              it { should validate_presence_of :order_id }
         
     | 
| 
      
 12 
     | 
    
         
            +
              it { should(ensure_inclusion_of(:status)
         
     | 
| 
      
 13 
     | 
    
         
            +
                .in_array(BitexBot::BuyOpeningFlow.statuses)) }
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              describe "when creating a buying flow" do
         
     | 
| 
      
 16 
     | 
    
         
            +
                it "spends 50 usd" do
         
     | 
| 
      
 17 
     | 
    
         
            +
                  stub_bitex_bid_create
         
     | 
| 
      
 18 
     | 
    
         
            +
                  BitexBot::Settings.stub(time_to_live: 3,
         
     | 
| 
      
 19 
     | 
    
         
            +
                    buying: double(amount_to_spend_per_order: 50, profit: 0))
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  flow = BitexBot::BuyOpeningFlow.create_for_market(100,
         
     | 
| 
      
 22 
     | 
    
         
            +
                    bitstamp_order_book_stub['bids'], bitstamp_transactions_stub, 0.5, 0.25)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  
         
     | 
| 
      
 24 
     | 
    
         
            +
                  flow.value_to_use.should == 50
         
     | 
| 
      
 25 
     | 
    
         
            +
                  flow.price.should <= flow.suggested_closing_price
         
     | 
| 
      
 26 
     | 
    
         
            +
                  flow.price.should == "19.85074626865672".to_d
         
     | 
| 
      
 27 
     | 
    
         
            +
                  flow.suggested_closing_price.should == 20
         
     | 
| 
      
 28 
     | 
    
         
            +
                  flow.order_id.should == 12345
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                it "spends 100 usd" do
         
     | 
| 
      
 32 
     | 
    
         
            +
                  stub_bitex_bid_create
         
     | 
| 
      
 33 
     | 
    
         
            +
                  BitexBot::Settings.stub(time_to_live: 3,
         
     | 
| 
      
 34 
     | 
    
         
            +
                    buying: double(amount_to_spend_per_order: 100, profit: 0))
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  flow = BitexBot::BuyOpeningFlow.create_for_market(100000,
         
     | 
| 
      
 37 
     | 
    
         
            +
                    bitstamp_order_book_stub['bids'], bitstamp_transactions_stub, 0.5, 0.25)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  flow.value_to_use.should == 100
         
     | 
| 
      
 39 
     | 
    
         
            +
                  flow.price.should <= flow.suggested_closing_price
         
     | 
| 
      
 40 
     | 
    
         
            +
                  flow.price.should == "14.88805970149254".to_d
         
     | 
| 
      
 41 
     | 
    
         
            +
                  flow.suggested_closing_price.should == 15
         
     | 
| 
      
 42 
     | 
    
         
            +
                  flow.order_id.should == 12345
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
                
         
     | 
| 
      
 45 
     | 
    
         
            +
                it "lowers the price to pay on bitex to take a profit" do
         
     | 
| 
      
 46 
     | 
    
         
            +
                  stub_bitex_bid_create
         
     | 
| 
      
 47 
     | 
    
         
            +
                  BitexBot::Settings.stub(time_to_live: 3,
         
     | 
| 
      
 48 
     | 
    
         
            +
                    buying: double(amount_to_spend_per_order: 100, profit: 50))
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  flow = BitexBot::BuyOpeningFlow.create_for_market(100000,
         
     | 
| 
      
 51 
     | 
    
         
            +
                    bitstamp_order_book_stub['bids'], bitstamp_transactions_stub, 0.5, 0.25)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  flow.value_to_use.should == 100
         
     | 
| 
      
 53 
     | 
    
         
            +
                  flow.price.should <= flow.suggested_closing_price
         
     | 
| 
      
 54 
     | 
    
         
            +
                  flow.price.should == "7.444029850746269".to_d
         
     | 
| 
      
 55 
     | 
    
         
            +
                  flow.suggested_closing_price.should == 15
         
     | 
| 
      
 56 
     | 
    
         
            +
                  flow.order_id.should == 12345
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
                
         
     | 
| 
      
 59 
     | 
    
         
            +
                it "fails when there is a problem placing the bid on bitex" do
         
     | 
| 
      
 60 
     | 
    
         
            +
                  Bitex::Bid.stub(:create!) do 
         
     | 
| 
      
 61 
     | 
    
         
            +
                    raise StandardError.new("Cannot Create")
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                  BitexBot::Settings.stub(time_to_live: 3,
         
     | 
| 
      
 65 
     | 
    
         
            +
                    buying: double(amount_to_spend_per_order: 100, profit: 0))
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                  expect do
         
     | 
| 
      
 68 
     | 
    
         
            +
                    flow = BitexBot::BuyOpeningFlow.create_for_market(100000,
         
     | 
| 
      
 69 
     | 
    
         
            +
                      bitstamp_order_book_stub['bids'], bitstamp_transactions_stub, 0.5, 0.25)
         
     | 
| 
      
 70 
     | 
    
         
            +
                    flow.should be_nil
         
     | 
| 
      
 71 
     | 
    
         
            +
                    BitexBot::BuyOpeningFlow.count.should == 0
         
     | 
| 
      
 72 
     | 
    
         
            +
                  end.to raise_exception(BitexBot::CannotCreateFlow)
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
                
         
     | 
| 
      
 75 
     | 
    
         
            +
                it "fails when there are not enough bitcoin to sell in the other exchange" do
         
     | 
| 
      
 76 
     | 
    
         
            +
                  stub_bitex_bid_create
         
     | 
| 
      
 77 
     | 
    
         
            +
                  BitexBot::Settings.stub(time_to_live: 3,
         
     | 
| 
      
 78 
     | 
    
         
            +
                    buying: double(amount_to_spend_per_order: 100, profit: 0))
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  expect do
         
     | 
| 
      
 81 
     | 
    
         
            +
                    flow = BitexBot::BuyOpeningFlow.create_for_market(1,
         
     | 
| 
      
 82 
     | 
    
         
            +
                      bitstamp_order_book_stub['bids'], bitstamp_transactions_stub, 0.5, 0.25)
         
     | 
| 
      
 83 
     | 
    
         
            +
                    flow.should be_nil
         
     | 
| 
      
 84 
     | 
    
         
            +
                    BitexBot::BuyOpeningFlow.count.should == 0
         
     | 
| 
      
 85 
     | 
    
         
            +
                  end.to raise_exception(BitexBot::CannotCreateFlow)
         
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
              end
         
     | 
| 
      
 88 
     | 
    
         
            +
              
         
     | 
| 
      
 89 
     | 
    
         
            +
              describe "when fetching open positions" do
         
     | 
| 
      
 90 
     | 
    
         
            +
                let(:flow){ create(:buy_opening_flow) }
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                it 'only gets buys' do
         
     | 
| 
      
 93 
     | 
    
         
            +
                  flow.order_id.should == 12345
         
     | 
| 
      
 94 
     | 
    
         
            +
                  stub_bitex_transactions
         
     | 
| 
      
 95 
     | 
    
         
            +
                  expect do
         
     | 
| 
      
 96 
     | 
    
         
            +
                    all = BitexBot::BuyOpeningFlow.sync_open_positions
         
     | 
| 
      
 97 
     | 
    
         
            +
                    all.size.should == 1
         
     | 
| 
      
 98 
     | 
    
         
            +
                    all.first.tap do |o|
         
     | 
| 
      
 99 
     | 
    
         
            +
                      o.price == 300.0
         
     | 
| 
      
 100 
     | 
    
         
            +
                      o.amount == 600.0
         
     | 
| 
      
 101 
     | 
    
         
            +
                      o.quantity == 2
         
     | 
| 
      
 102 
     | 
    
         
            +
                      o.transaction_id.should == 12345678
         
     | 
| 
      
 103 
     | 
    
         
            +
                      o.opening_flow.should == flow
         
     | 
| 
      
 104 
     | 
    
         
            +
                    end
         
     | 
| 
      
 105 
     | 
    
         
            +
                  end.to change{ BitexBot::OpenBuy.count }.by(1)
         
     | 
| 
      
 106 
     | 
    
         
            +
                end
         
     | 
| 
      
 107 
     | 
    
         
            +
                
         
     | 
| 
      
 108 
     | 
    
         
            +
                it 'does not register the same buy twice' do
         
     | 
| 
      
 109 
     | 
    
         
            +
                  flow.order_id.should == 12345
         
     | 
| 
      
 110 
     | 
    
         
            +
                  stub_bitex_transactions
         
     | 
| 
      
 111 
     | 
    
         
            +
                  BitexBot::BuyOpeningFlow.sync_open_positions
         
     | 
| 
      
 112 
     | 
    
         
            +
                  BitexBot::OpenBuy.count.should == 1
         
     | 
| 
      
 113 
     | 
    
         
            +
                  Timecop.travel 1.second.from_now
         
     | 
| 
      
 114 
     | 
    
         
            +
                  stub_bitex_transactions(build(:bitex_buy, id: 23456))
         
     | 
| 
      
 115 
     | 
    
         
            +
                  expect do
         
     | 
| 
      
 116 
     | 
    
         
            +
                    news = BitexBot::BuyOpeningFlow.sync_open_positions
         
     | 
| 
      
 117 
     | 
    
         
            +
                    news.first.transaction_id.should == 23456
         
     | 
| 
      
 118 
     | 
    
         
            +
                  end.to change{ BitexBot::OpenBuy.count }.by(1)
         
     | 
| 
      
 119 
     | 
    
         
            +
                end
         
     | 
| 
      
 120 
     | 
    
         
            +
                
         
     | 
| 
      
 121 
     | 
    
         
            +
                it 'does not register litecoin buys' do
         
     | 
| 
      
 122 
     | 
    
         
            +
                  flow.order_id.should == 12345
         
     | 
| 
      
 123 
     | 
    
         
            +
                  Bitex::Transaction.stub(all: [build(:bitex_buy, id: 23456, specie: :ltc)])
         
     | 
| 
      
 124 
     | 
    
         
            +
                  expect do
         
     | 
| 
      
 125 
     | 
    
         
            +
                    BitexBot::BuyOpeningFlow.sync_open_positions.should be_empty
         
     | 
| 
      
 126 
     | 
    
         
            +
                  end.not_to change{ BitexBot::OpenBuy.count }
         
     | 
| 
      
 127 
     | 
    
         
            +
                  BitexBot::OpenBuy.count.should == 0
         
     | 
| 
      
 128 
     | 
    
         
            +
                end
         
     | 
| 
      
 129 
     | 
    
         
            +
                
         
     | 
| 
      
 130 
     | 
    
         
            +
                it 'does not register buys from unknown bids' do
         
     | 
| 
      
 131 
     | 
    
         
            +
                  stub_bitex_transactions
         
     | 
| 
      
 132 
     | 
    
         
            +
                  expect do
         
     | 
| 
      
 133 
     | 
    
         
            +
                    BitexBot::BuyOpeningFlow.sync_open_positions.should be_empty
         
     | 
| 
      
 134 
     | 
    
         
            +
                  end.not_to change{ BitexBot::OpenBuy.count }
         
     | 
| 
      
 135 
     | 
    
         
            +
                end
         
     | 
| 
      
 136 
     | 
    
         
            +
              end
         
     | 
| 
      
 137 
     | 
    
         
            +
              
         
     | 
| 
      
 138 
     | 
    
         
            +
              it 'cancels the associated bitex bid' do
         
     | 
| 
      
 139 
     | 
    
         
            +
                stub_bitex_bid_create
         
     | 
| 
      
 140 
     | 
    
         
            +
                BitexBot::Settings.stub(time_to_live: 3,
         
     | 
| 
      
 141 
     | 
    
         
            +
                  buying: double(amount_to_spend_per_order: 50, profit: 0))
         
     | 
| 
      
 142 
     | 
    
         
            +
             
     | 
| 
      
 143 
     | 
    
         
            +
                flow = BitexBot::BuyOpeningFlow.create_for_market(100,
         
     | 
| 
      
 144 
     | 
    
         
            +
                  bitstamp_order_book_stub['bids'], bitstamp_transactions_stub, 0.5, 0.25)
         
     | 
| 
      
 145 
     | 
    
         
            +
                
         
     | 
| 
      
 146 
     | 
    
         
            +
                flow.finalise!
         
     | 
| 
      
 147 
     | 
    
         
            +
                flow.should be_settling
         
     | 
| 
      
 148 
     | 
    
         
            +
                flow.finalise!
         
     | 
| 
      
 149 
     | 
    
         
            +
                flow.should be_settling
         
     | 
| 
      
 150 
     | 
    
         
            +
                Bitex::Order.stub(active: [])
         
     | 
| 
      
 151 
     | 
    
         
            +
                flow.finalise!
         
     | 
| 
      
 152 
     | 
    
         
            +
                flow.should be_finalised
         
     | 
| 
      
 153 
     | 
    
         
            +
              end
         
     | 
| 
      
 154 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,57 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe BitexBot::OrderBookSimulator do
         
     | 
| 
      
 4 
     | 
    
         
            +
              describe 'when buying on bitex to sell somewhere else' do
         
     | 
| 
      
 5 
     | 
    
         
            +
                def simulate(volatility, amount)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  BitexBot::OrderBookSimulator.run(volatility, bitstamp_transactions_stub,
         
     | 
| 
      
 7 
     | 
    
         
            +
                    bitstamp_order_book_stub['bids'], amount, nil)
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                it 'gets the safest price' do
         
     | 
| 
      
 11 
     | 
    
         
            +
                  simulate(0, 20).should == 30
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                it 'adjusts for medium volatility' do
         
     | 
| 
      
 15 
     | 
    
         
            +
                  simulate(3, 20).should == 25
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
                
         
     | 
| 
      
 18 
     | 
    
         
            +
                it 'adjusts for high volatility' do
         
     | 
| 
      
 19 
     | 
    
         
            +
                  simulate(6, 20).should == 20
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
              
         
     | 
| 
      
 22 
     | 
    
         
            +
                it 'big orders dig deep' do
         
     | 
| 
      
 23 
     | 
    
         
            +
                  simulate(0, 180).should == 15
         
     | 
| 
      
 24 
     | 
    
         
            +
                end
         
     | 
| 
      
 25 
     | 
    
         
            +
                
         
     | 
| 
      
 26 
     | 
    
         
            +
                it 'big orders with high volatility' do
         
     | 
| 
      
 27 
     | 
    
         
            +
                  simulate(6, 100).should == 10
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              describe 'when selling on bitex to buy somewhere else' do
         
     | 
| 
      
 32 
     | 
    
         
            +
                def simulate(volatility, quantity)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  BitexBot::OrderBookSimulator.run(volatility, bitstamp_transactions_stub,
         
     | 
| 
      
 34 
     | 
    
         
            +
                    bitstamp_order_book_stub['asks'], nil, quantity)
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                it 'gets the safest price' do
         
     | 
| 
      
 38 
     | 
    
         
            +
                  simulate(0, 2).should == 10
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                it 'adjusts for medium volatility' do
         
     | 
| 
      
 42 
     | 
    
         
            +
                  simulate(3, 2).should == 15
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
                
         
     | 
| 
      
 45 
     | 
    
         
            +
                it 'adjusts for high volatility' do
         
     | 
| 
      
 46 
     | 
    
         
            +
                  simulate(6, 2).should == 25
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
              
         
     | 
| 
      
 49 
     | 
    
         
            +
                it 'big orders dig deep' do
         
     | 
| 
      
 50 
     | 
    
         
            +
                  simulate(0, 8).should == 25
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
                
         
     | 
| 
      
 53 
     | 
    
         
            +
                it 'big orders with high volatility dig deep' do
         
     | 
| 
      
 54 
     | 
    
         
            +
                  simulate(6, 6).should == 30
         
     | 
| 
      
 55 
     | 
    
         
            +
                end
         
     | 
| 
      
 56 
     | 
    
         
            +
              end
         
     | 
| 
      
 57 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,103 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe BitexBot::Robot do
         
     | 
| 
      
 4 
     | 
    
         
            +
              before(:each) do
         
     | 
| 
      
 5 
     | 
    
         
            +
                BitexBot::Settings.stub(
         
     | 
| 
      
 6 
     | 
    
         
            +
                  time_to_live: 10,
         
     | 
| 
      
 7 
     | 
    
         
            +
                  buying: double(
         
     | 
| 
      
 8 
     | 
    
         
            +
                    amount_to_spend_per_order: 50,
         
     | 
| 
      
 9 
     | 
    
         
            +
                    profit: 0),
         
     | 
| 
      
 10 
     | 
    
         
            +
                  selling: double(
         
     | 
| 
      
 11 
     | 
    
         
            +
                    quantity_to_sell_per_order: 1,
         
     | 
| 
      
 12 
     | 
    
         
            +
                    profit: 0),
         
     | 
| 
      
 13 
     | 
    
         
            +
                  mailer: double(
         
     | 
| 
      
 14 
     | 
    
         
            +
                    from: 'test@test.com',
         
     | 
| 
      
 15 
     | 
    
         
            +
                    to: 'test@test.com',
         
     | 
| 
      
 16 
     | 
    
         
            +
                    method: :test,
         
     | 
| 
      
 17 
     | 
    
         
            +
                    options: {}
         
     | 
| 
      
 18 
     | 
    
         
            +
                  )
         
     | 
| 
      
 19 
     | 
    
         
            +
                )
         
     | 
| 
      
 20 
     | 
    
         
            +
                Bitex.api_key = "valid_key"
         
     | 
| 
      
 21 
     | 
    
         
            +
                Bitex::Profile.stub(get: {fee: 0.5})
         
     | 
| 
      
 22 
     | 
    
         
            +
                stub_bitex_bid_create
         
     | 
| 
      
 23 
     | 
    
         
            +
                stub_bitex_ask_create
         
     | 
| 
      
 24 
     | 
    
         
            +
                stub_bitstamp_sell
         
     | 
| 
      
 25 
     | 
    
         
            +
                stub_bitstamp_buy
         
     | 
| 
      
 26 
     | 
    
         
            +
                stub_bitstamp_balance
         
     | 
| 
      
 27 
     | 
    
         
            +
                stub_bitstamp_order_book
         
     | 
| 
      
 28 
     | 
    
         
            +
                stub_bitstamp_transactions
         
     | 
| 
      
 29 
     | 
    
         
            +
                stub_bitstamp_user_transactions
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
              let(:bot){ BitexBot::Robot.new }
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
              it 'Starts out by creating opening flows that timeout' do
         
     | 
| 
      
 34 
     | 
    
         
            +
                bot.trade!
         
     | 
| 
      
 35 
     | 
    
         
            +
                stub_bitex_transactions
         
     | 
| 
      
 36 
     | 
    
         
            +
                buying = BitexBot::BuyOpeningFlow.last
         
     | 
| 
      
 37 
     | 
    
         
            +
                selling = BitexBot::SellOpeningFlow.last
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                Timecop.travel 10.minutes.from_now
         
     | 
| 
      
 40 
     | 
    
         
            +
                bot.trade!
         
     | 
| 
      
 41 
     | 
    
         
            +
             
         
     | 
| 
      
 42 
     | 
    
         
            +
                buying.reload.should be_settling
         
     | 
| 
      
 43 
     | 
    
         
            +
                selling.reload.should be_settling
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                Bitex::Order.stub(active: [])
         
     | 
| 
      
 46 
     | 
    
         
            +
                bot.trade!
         
     | 
| 
      
 47 
     | 
    
         
            +
                buying.reload.should be_finalised
         
     | 
| 
      
 48 
     | 
    
         
            +
                selling.reload.should be_finalised
         
     | 
| 
      
 49 
     | 
    
         
            +
              end
         
     | 
| 
      
 50 
     | 
    
         
            +
              
         
     | 
| 
      
 51 
     | 
    
         
            +
              it 'creates alternating opening flows' do
         
     | 
| 
      
 52 
     | 
    
         
            +
                Bitex::Transaction.stub(all: [])
         
     | 
| 
      
 53 
     | 
    
         
            +
                bot.trade!
         
     | 
| 
      
 54 
     | 
    
         
            +
                BitexBot::BuyOpeningFlow.active.count.should == 1
         
     | 
| 
      
 55 
     | 
    
         
            +
                Timecop.travel 2.seconds.from_now
         
     | 
| 
      
 56 
     | 
    
         
            +
                bot.trade!
         
     | 
| 
      
 57 
     | 
    
         
            +
                BitexBot::BuyOpeningFlow.active.count.should == 1
         
     | 
| 
      
 58 
     | 
    
         
            +
                Timecop.travel 5.seconds.from_now
         
     | 
| 
      
 59 
     | 
    
         
            +
                bot.trade!
         
     | 
| 
      
 60 
     | 
    
         
            +
                BitexBot::BuyOpeningFlow.active.count.should == 2
         
     | 
| 
      
 61 
     | 
    
         
            +
                
         
     | 
| 
      
 62 
     | 
    
         
            +
                stub_bitex_transactions
         
     | 
| 
      
 63 
     | 
    
         
            +
                Bitex::Order.stub(active: [])
         
     | 
| 
      
 64 
     | 
    
         
            +
                Timecop.travel 5.seconds.from_now
         
     | 
| 
      
 65 
     | 
    
         
            +
                bot.trade!
         
     | 
| 
      
 66 
     | 
    
         
            +
                BitexBot::BuyOpeningFlow.active.count.should == 1
         
     | 
| 
      
 67 
     | 
    
         
            +
                Timecop.travel 5.seconds.from_now
         
     | 
| 
      
 68 
     | 
    
         
            +
                bot.trade!
         
     | 
| 
      
 69 
     | 
    
         
            +
                BitexBot::BuyOpeningFlow.active.count.should == 0
         
     | 
| 
      
 70 
     | 
    
         
            +
              end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
              it 'does not place new opening flows until all closing flows are done' do
         
     | 
| 
      
 73 
     | 
    
         
            +
                bot.trade!
         
     | 
| 
      
 74 
     | 
    
         
            +
                stub_bitex_transactions
         
     | 
| 
      
 75 
     | 
    
         
            +
                Bitex::Order.stub(active: [])
         
     | 
| 
      
 76 
     | 
    
         
            +
                expect do
         
     | 
| 
      
 77 
     | 
    
         
            +
                  bot.trade!
         
     | 
| 
      
 78 
     | 
    
         
            +
                end.to change{ BitexBot::BuyClosingFlow.count }.by(1)
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                Timecop.travel 15.seconds.from_now
         
     | 
| 
      
 81 
     | 
    
         
            +
                bot.trade!
         
     | 
| 
      
 82 
     | 
    
         
            +
                bot.should be_active_closing_flows
         
     | 
| 
      
 83 
     | 
    
         
            +
                bot.should_not be_active_opening_flows
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                stub_bitstamp_orders_into_transactions
         
     | 
| 
      
 86 
     | 
    
         
            +
                expect do
         
     | 
| 
      
 87 
     | 
    
         
            +
                  bot.trade!
         
     | 
| 
      
 88 
     | 
    
         
            +
                  bot.should_not be_active_closing_flows
         
     | 
| 
      
 89 
     | 
    
         
            +
                end.to change{ BitexBot::BuyOpeningFlow.count }.by(1)
         
     | 
| 
      
 90 
     | 
    
         
            +
              end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
         
     | 
| 
      
 92 
     | 
    
         
            +
              it 'notifies exceptions and sleeps' do
         
     | 
| 
      
 93 
     | 
    
         
            +
                Bitstamp.stub(:balance) do
         
     | 
| 
      
 94 
     | 
    
         
            +
                  raise StandardError.new('oh moova')
         
     | 
| 
      
 95 
     | 
    
         
            +
                end
         
     | 
| 
      
 96 
     | 
    
         
            +
                bot.trade!
         
     | 
| 
      
 97 
     | 
    
         
            +
                Mail::TestMailer.deliveries.count.should == 1
         
     | 
| 
      
 98 
     | 
    
         
            +
              end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
              #it 'goes through all the motions buying and selling' do
         
     | 
| 
      
 101 
     | 
    
         
            +
              #  pending
         
     | 
| 
      
 102 
     | 
    
         
            +
              #end
         
     | 
| 
      
 103 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,160 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe BitexBot::SellClosingFlow do
         
     | 
| 
      
 4 
     | 
    
         
            +
              it "closes a single open position completely" do
         
     | 
| 
      
 5 
     | 
    
         
            +
                stub_bitstamp_buy
         
     | 
| 
      
 6 
     | 
    
         
            +
                open = create :open_sell
         
     | 
| 
      
 7 
     | 
    
         
            +
                flow = BitexBot::SellClosingFlow.close_open_positions
         
     | 
| 
      
 8 
     | 
    
         
            +
                open.reload.closing_flow.should == flow
         
     | 
| 
      
 9 
     | 
    
         
            +
                flow.open_positions.should == [open]
         
     | 
| 
      
 10 
     | 
    
         
            +
                flow.desired_price.should == 290
         
     | 
| 
      
 11 
     | 
    
         
            +
                flow.quantity.should == 2
         
     | 
| 
      
 12 
     | 
    
         
            +
                flow.amount.should == 600
         
     | 
| 
      
 13 
     | 
    
         
            +
                flow.btc_profit.should be_nil
         
     | 
| 
      
 14 
     | 
    
         
            +
                flow.usd_profit.should be_nil
         
     | 
| 
      
 15 
     | 
    
         
            +
                close = flow.close_positions.first
         
     | 
| 
      
 16 
     | 
    
         
            +
                close.order_id.should == 1
         
     | 
| 
      
 17 
     | 
    
         
            +
                close.amount.should be_nil
         
     | 
| 
      
 18 
     | 
    
         
            +
                close.quantity.should be_nil
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
              
         
     | 
| 
      
 21 
     | 
    
         
            +
              it "closes an aggregate of several open positions" do
         
     | 
| 
      
 22 
     | 
    
         
            +
                stub_bitstamp_buy
         
     | 
| 
      
 23 
     | 
    
         
            +
                open_one = create :tiny_open_sell
         
     | 
| 
      
 24 
     | 
    
         
            +
                open_two = create :open_sell
         
     | 
| 
      
 25 
     | 
    
         
            +
                flow = BitexBot::SellClosingFlow.close_open_positions
         
     | 
| 
      
 26 
     | 
    
         
            +
                close = flow.close_positions.first
         
     | 
| 
      
 27 
     | 
    
         
            +
                open_one.reload.closing_flow.should == flow
         
     | 
| 
      
 28 
     | 
    
         
            +
                open_two.reload.closing_flow.should == flow
         
     | 
| 
      
 29 
     | 
    
         
            +
                flow.open_positions.should == [open_one, open_two]
         
     | 
| 
      
 30 
     | 
    
         
            +
                flow.desired_price.should == '290.497512437810945273631840797'.to_d
         
     | 
| 
      
 31 
     | 
    
         
            +
                flow.quantity.should == 2.01
         
     | 
| 
      
 32 
     | 
    
         
            +
                flow.amount.should == 604
         
     | 
| 
      
 33 
     | 
    
         
            +
                flow.btc_profit.should be_nil
         
     | 
| 
      
 34 
     | 
    
         
            +
                flow.usd_profit.should be_nil
         
     | 
| 
      
 35 
     | 
    
         
            +
                close.order_id.should == 1
         
     | 
| 
      
 36 
     | 
    
         
            +
                close.amount.should be_nil
         
     | 
| 
      
 37 
     | 
    
         
            +
                close.quantity.should be_nil
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
              
         
     | 
| 
      
 40 
     | 
    
         
            +
              it "does not try to close if the amount is too low" do
         
     | 
| 
      
 41 
     | 
    
         
            +
                open = create :tiny_open_sell
         
     | 
| 
      
 42 
     | 
    
         
            +
                expect do
         
     | 
| 
      
 43 
     | 
    
         
            +
                  BitexBot::SellClosingFlow.close_open_positions.should be_nil
         
     | 
| 
      
 44 
     | 
    
         
            +
                end.not_to change{ BitexBot::SellClosingFlow.count }
         
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
              
         
     | 
| 
      
 47 
     | 
    
         
            +
              it "does not try to close if there are no open positions" do
         
     | 
| 
      
 48 
     | 
    
         
            +
                expect do
         
     | 
| 
      
 49 
     | 
    
         
            +
                  BitexBot::SellClosingFlow.close_open_positions.should be_nil
         
     | 
| 
      
 50 
     | 
    
         
            +
                end.not_to change{ BitexBot::SellClosingFlow.count }
         
     | 
| 
      
 51 
     | 
    
         
            +
              end
         
     | 
| 
      
 52 
     | 
    
         
            +
              
         
     | 
| 
      
 53 
     | 
    
         
            +
              describe "when syncinc executed orders" do
         
     | 
| 
      
 54 
     | 
    
         
            +
                before(:each) do
         
     | 
| 
      
 55 
     | 
    
         
            +
                  stub_bitstamp_buy
         
     | 
| 
      
 56 
     | 
    
         
            +
                  stub_bitstamp_user_transactions
         
     | 
| 
      
 57 
     | 
    
         
            +
                  create :tiny_open_sell
         
     | 
| 
      
 58 
     | 
    
         
            +
                  create :open_sell
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
              
         
     | 
| 
      
 61 
     | 
    
         
            +
                it "syncs the executed orders, calculates profit" do
         
     | 
| 
      
 62 
     | 
    
         
            +
                  flow = BitexBot::SellClosingFlow.close_open_positions
         
     | 
| 
      
 63 
     | 
    
         
            +
                  stub_bitstamp_orders_into_transactions
         
     | 
| 
      
 64 
     | 
    
         
            +
                  flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
         
     | 
| 
      
 65 
     | 
    
         
            +
                  close = flow.close_positions.last
         
     | 
| 
      
 66 
     | 
    
         
            +
                  close.amount.should == 583.9
         
     | 
| 
      
 67 
     | 
    
         
            +
                  close.quantity.should == 2.01
         
     | 
| 
      
 68 
     | 
    
         
            +
                  flow.should be_done
         
     | 
| 
      
 69 
     | 
    
         
            +
                  flow.btc_profit.should == 0
         
     | 
| 
      
 70 
     | 
    
         
            +
                  flow.usd_profit.should == 20.1
         
     | 
| 
      
 71 
     | 
    
         
            +
                end
         
     | 
| 
      
 72 
     | 
    
         
            +
              
         
     | 
| 
      
 73 
     | 
    
         
            +
                it "retries closing at a higher price every minute" do
         
     | 
| 
      
 74 
     | 
    
         
            +
                  flow = BitexBot::SellClosingFlow.close_open_positions
         
     | 
| 
      
 75 
     | 
    
         
            +
                  expect do
         
     | 
| 
      
 76 
     | 
    
         
            +
                    flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
         
     | 
| 
      
 77 
     | 
    
         
            +
                  end.not_to change{ BitexBot::CloseSell.count }
         
     | 
| 
      
 78 
     | 
    
         
            +
                  flow.should_not be_done
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                  # Immediately calling sync again does not try to cancel the ask.
         
     | 
| 
      
 81 
     | 
    
         
            +
                  flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
         
     | 
| 
      
 82 
     | 
    
         
            +
                  Bitstamp.orders.all.size.should == 1
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                  # Partially executes order, and 61 seconds after that
         
     | 
| 
      
 85 
     | 
    
         
            +
                  # sync_closed_positions tries to cancel it.
         
     | 
| 
      
 86 
     | 
    
         
            +
                  stub_bitstamp_orders_into_transactions(ratio: 0.5)
         
     | 
| 
      
 87 
     | 
    
         
            +
                  Timecop.travel 61.seconds.from_now
         
     | 
| 
      
 88 
     | 
    
         
            +
                  Bitstamp.orders.all.size.should == 1
         
     | 
| 
      
 89 
     | 
    
         
            +
                  expect do
         
     | 
| 
      
 90 
     | 
    
         
            +
                    flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
         
     | 
| 
      
 91 
     | 
    
         
            +
                  end.not_to change{ BitexBot::CloseSell.count }
         
     | 
| 
      
 92 
     | 
    
         
            +
                  Bitstamp.orders.all.size.should == 0
         
     | 
| 
      
 93 
     | 
    
         
            +
                  flow.should_not be_done
         
     | 
| 
      
 94 
     | 
    
         
            +
                  
         
     | 
| 
      
 95 
     | 
    
         
            +
                  # Next time we try to sync_closed_positions the flow
         
     | 
| 
      
 96 
     | 
    
         
            +
                  # detects the previous close_buy was cancelled correctly so
         
     | 
| 
      
 97 
     | 
    
         
            +
                  # it syncs it's total amounts and tries to place a new one.
         
     | 
| 
      
 98 
     | 
    
         
            +
                  expect do
         
     | 
| 
      
 99 
     | 
    
         
            +
                    flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
         
     | 
| 
      
 100 
     | 
    
         
            +
                  end.to change{ BitexBot::CloseSell.count }.by(1)
         
     | 
| 
      
 101 
     | 
    
         
            +
                  flow.close_positions.first.tap do |close|
         
     | 
| 
      
 102 
     | 
    
         
            +
                    close.amount.should == 291.95
         
     | 
| 
      
 103 
     | 
    
         
            +
                    close.quantity.should == 1.005
         
     | 
| 
      
 104 
     | 
    
         
            +
                  end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                  # The second ask is executed completely so we can wrap it up and consider
         
     | 
| 
      
 107 
     | 
    
         
            +
                  # this closing flow done.
         
     | 
| 
      
 108 
     | 
    
         
            +
                  stub_bitstamp_orders_into_transactions
         
     | 
| 
      
 109 
     | 
    
         
            +
                  flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
         
     | 
| 
      
 110 
     | 
    
         
            +
                  flow.close_positions.last.tap do |close|
         
     | 
| 
      
 111 
     | 
    
         
            +
                    close.amount.should == 291.95
         
     | 
| 
      
 112 
     | 
    
         
            +
                    close.quantity.should == '1.004930813120933'.to_d
         
     | 
| 
      
 113 
     | 
    
         
            +
                  end
         
     | 
| 
      
 114 
     | 
    
         
            +
                  flow.should be_done
         
     | 
| 
      
 115 
     | 
    
         
            +
                  flow.btc_profit.should == '-0.000069186879067'.to_d
         
     | 
| 
      
 116 
     | 
    
         
            +
                  flow.usd_profit.should == 20.1
         
     | 
| 
      
 117 
     | 
    
         
            +
                end
         
     | 
| 
      
 118 
     | 
    
         
            +
                
         
     | 
| 
      
 119 
     | 
    
         
            +
                it "does not retry for an amount less than minimum_for_closing" do
         
     | 
| 
      
 120 
     | 
    
         
            +
                  flow = BitexBot::SellClosingFlow.close_open_positions
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                  20.times do 
         
     | 
| 
      
 123 
     | 
    
         
            +
                    Timecop.travel 60.seconds.from_now
         
     | 
| 
      
 124 
     | 
    
         
            +
                    flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
         
     | 
| 
      
 125 
     | 
    
         
            +
                  end
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                  stub_bitstamp_orders_into_transactions(ratio: 0.999)
         
     | 
| 
      
 128 
     | 
    
         
            +
                  Bitstamp.orders.all.first.cancel!
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                  expect do
         
     | 
| 
      
 131 
     | 
    
         
            +
                    flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
         
     | 
| 
      
 132 
     | 
    
         
            +
                  end.not_to change{ BitexBot::CloseSell.count }
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                  flow.should be_done
         
     | 
| 
      
 135 
     | 
    
         
            +
                  flow.btc_profit.should == '-0.015739962920125'.to_d
         
     | 
| 
      
 136 
     | 
    
         
            +
                  flow.usd_profit.should == '20.6839'.to_d
         
     | 
| 
      
 137 
     | 
    
         
            +
                end
         
     | 
| 
      
 138 
     | 
    
         
            +
                
         
     | 
| 
      
 139 
     | 
    
         
            +
                it "can lose BTC if price had to be raised dramatically" do
         
     | 
| 
      
 140 
     | 
    
         
            +
                  # This flow is forced to spend the original USD amount paying more than
         
     | 
| 
      
 141 
     | 
    
         
            +
                  # expected, thus regaining less BTC than what was sold on bitex.
         
     | 
| 
      
 142 
     | 
    
         
            +
                  flow = BitexBot::SellClosingFlow.close_open_positions
         
     | 
| 
      
 143 
     | 
    
         
            +
                  60.times do 
         
     | 
| 
      
 144 
     | 
    
         
            +
                    Timecop.travel 60.seconds.from_now
         
     | 
| 
      
 145 
     | 
    
         
            +
                    flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
         
     | 
| 
      
 146 
     | 
    
         
            +
                  end
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
                  stub_bitstamp_orders_into_transactions
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                  flow.sync_closed_positions(Bitstamp.orders.all, Bitstamp.user_transactions.all)
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
                  flow.reload.should be_done
         
     | 
| 
      
 153 
     | 
    
         
            +
                  flow.btc_profit.should == "-0.117278093149271".to_d
         
     | 
| 
      
 154 
     | 
    
         
            +
                  flow.usd_profit.should == "20.1".to_d
         
     | 
| 
      
 155 
     | 
    
         
            +
                  close = flow.close_positions.last
         
     | 
| 
      
 156 
     | 
    
         
            +
                  (close.amount / close.quantity)
         
     | 
| 
      
 157 
     | 
    
         
            +
                    .should == '308.497512437810935201007569945345172662'.to_d
         
     | 
| 
      
 158 
     | 
    
         
            +
                end
         
     | 
| 
      
 159 
     | 
    
         
            +
              end
         
     | 
| 
      
 160 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,156 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'spec_helper'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            describe BitexBot::SellOpeningFlow do
         
     | 
| 
      
 4 
     | 
    
         
            +
              before(:each) do
         
     | 
| 
      
 5 
     | 
    
         
            +
                Bitex.api_key = "valid_key"
         
     | 
| 
      
 6 
     | 
    
         
            +
              end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              it { should validate_presence_of :status }
         
     | 
| 
      
 9 
     | 
    
         
            +
              it { should validate_presence_of :price }
         
     | 
| 
      
 10 
     | 
    
         
            +
              it { should validate_presence_of :value_to_use }
         
     | 
| 
      
 11 
     | 
    
         
            +
              it { should validate_presence_of :order_id }
         
     | 
| 
      
 12 
     | 
    
         
            +
              it { should(ensure_inclusion_of(:status)
         
     | 
| 
      
 13 
     | 
    
         
            +
                .in_array(BitexBot::SellOpeningFlow.statuses)) }
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
              describe "when creating a selling flow" do
         
     | 
| 
      
 16 
     | 
    
         
            +
                it "sells 2 bitcoin" do
         
     | 
| 
      
 17 
     | 
    
         
            +
                  stub_bitex_ask_create
         
     | 
| 
      
 18 
     | 
    
         
            +
                  BitexBot::Settings.stub(time_to_live: 3,
         
     | 
| 
      
 19 
     | 
    
         
            +
                    selling: double(quantity_to_sell_per_order: 2, profit: 0))
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  flow = BitexBot::SellOpeningFlow.create_for_market(1000,
         
     | 
| 
      
 22 
     | 
    
         
            +
                    bitstamp_order_book_stub['asks'], bitstamp_transactions_stub, 0.5, 0.25)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  
         
     | 
| 
      
 24 
     | 
    
         
            +
                  flow.value_to_use.should == 2
         
     | 
| 
      
 25 
     | 
    
         
            +
                  flow.price.should >= flow.suggested_closing_price
         
     | 
| 
      
 26 
     | 
    
         
            +
                  flow.price.should == "20.15037593984962".to_d
         
     | 
| 
      
 27 
     | 
    
         
            +
                  flow.suggested_closing_price.should == 20
         
     | 
| 
      
 28 
     | 
    
         
            +
                  flow.order_id.should == 12345
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                it "sells 4 bitcoin" do
         
     | 
| 
      
 32 
     | 
    
         
            +
                  stub_bitex_ask_create
         
     | 
| 
      
 33 
     | 
    
         
            +
                  BitexBot::Settings.stub(time_to_live: 3,
         
     | 
| 
      
 34 
     | 
    
         
            +
                    selling: double(quantity_to_sell_per_order: 4, profit: 0))
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  flow = BitexBot::SellOpeningFlow.create_for_market(1000,
         
     | 
| 
      
 37 
     | 
    
         
            +
                    bitstamp_order_book_stub['asks'], bitstamp_transactions_stub, 0.5, 0.25)
         
     | 
| 
      
 38 
     | 
    
         
            +
                  
         
     | 
| 
      
 39 
     | 
    
         
            +
                  flow.value_to_use.should == 4
         
     | 
| 
      
 40 
     | 
    
         
            +
                  flow.price.should >= flow.suggested_closing_price
         
     | 
| 
      
 41 
     | 
    
         
            +
                  flow.price.should == "25.18796992481203".to_d
         
     | 
| 
      
 42 
     | 
    
         
            +
                  flow.suggested_closing_price.should == 25
         
     | 
| 
      
 43 
     | 
    
         
            +
                  flow.order_id.should == 12345
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
                
         
     | 
| 
      
 46 
     | 
    
         
            +
                it "raises the price to charge on bitex to take a profit" do
         
     | 
| 
      
 47 
     | 
    
         
            +
                  stub_bitex_ask_create
         
     | 
| 
      
 48 
     | 
    
         
            +
                  BitexBot::Settings.stub(time_to_live: 3,
         
     | 
| 
      
 49 
     | 
    
         
            +
                    selling: double(quantity_to_sell_per_order: 4, profit: 50))
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  flow = BitexBot::SellOpeningFlow.create_for_market(1000,
         
     | 
| 
      
 52 
     | 
    
         
            +
                    bitstamp_order_book_stub['asks'], bitstamp_transactions_stub, 0.5, 0.25)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  
         
     | 
| 
      
 54 
     | 
    
         
            +
                  flow.value_to_use.should == 4
         
     | 
| 
      
 55 
     | 
    
         
            +
                  flow.price.should >= flow.suggested_closing_price
         
     | 
| 
      
 56 
     | 
    
         
            +
                  flow.price.should == "37.78195488721804".to_d
         
     | 
| 
      
 57 
     | 
    
         
            +
                  flow.suggested_closing_price.should == 25
         
     | 
| 
      
 58 
     | 
    
         
            +
                  flow.order_id.should == 12345
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
                
         
     | 
| 
      
 61 
     | 
    
         
            +
                it "fails when there is a problem placing the ask on bitex" do
         
     | 
| 
      
 62 
     | 
    
         
            +
                  Bitex::Ask.stub(:create!) do 
         
     | 
| 
      
 63 
     | 
    
         
            +
                    raise StandardError.new("Cannot Create")
         
     | 
| 
      
 64 
     | 
    
         
            +
                  end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                  BitexBot::Settings.stub(time_to_live: 3,
         
     | 
| 
      
 67 
     | 
    
         
            +
                    selling: double(quantity_to_sell_per_order: 4, profit: 50))
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  expect do
         
     | 
| 
      
 70 
     | 
    
         
            +
                    flow = BitexBot::SellOpeningFlow.create_for_market(100000,
         
     | 
| 
      
 71 
     | 
    
         
            +
                      bitstamp_order_book_stub['asks'], bitstamp_transactions_stub, 0.5, 0.25)
         
     | 
| 
      
 72 
     | 
    
         
            +
                    flow.should be_nil
         
     | 
| 
      
 73 
     | 
    
         
            +
                    BitexBot::SellOpeningFlow.count.should == 0
         
     | 
| 
      
 74 
     | 
    
         
            +
                  end.to raise_exception(BitexBot::CannotCreateFlow)
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
                
         
     | 
| 
      
 77 
     | 
    
         
            +
                it "fails when there are not enough USD to re-buy in the other exchange" do
         
     | 
| 
      
 78 
     | 
    
         
            +
                  stub_bitex_bid_create
         
     | 
| 
      
 79 
     | 
    
         
            +
                  BitexBot::Settings.stub(time_to_live: 3,
         
     | 
| 
      
 80 
     | 
    
         
            +
                    selling: double(quantity_to_sell_per_order: 4, profit: 50))
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  expect do
         
     | 
| 
      
 83 
     | 
    
         
            +
                    flow = BitexBot::SellOpeningFlow.create_for_market(1,
         
     | 
| 
      
 84 
     | 
    
         
            +
                      bitstamp_order_book_stub['asks'], bitstamp_transactions_stub, 0.5, 0.25)
         
     | 
| 
      
 85 
     | 
    
         
            +
                    flow.should be_nil
         
     | 
| 
      
 86 
     | 
    
         
            +
                    BitexBot::SellOpeningFlow.count.should == 0
         
     | 
| 
      
 87 
     | 
    
         
            +
                  end.to raise_exception(BitexBot::CannotCreateFlow)
         
     | 
| 
      
 88 
     | 
    
         
            +
                end
         
     | 
| 
      
 89 
     | 
    
         
            +
              end
         
     | 
| 
      
 90 
     | 
    
         
            +
              
         
     | 
| 
      
 91 
     | 
    
         
            +
              describe "when fetching open positions" do
         
     | 
| 
      
 92 
     | 
    
         
            +
                let(:flow){ create(:sell_opening_flow) }
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                it 'only gets sells' do
         
     | 
| 
      
 95 
     | 
    
         
            +
                  flow.order_id.should == 12345
         
     | 
| 
      
 96 
     | 
    
         
            +
                  stub_bitex_transactions
         
     | 
| 
      
 97 
     | 
    
         
            +
                  expect do
         
     | 
| 
      
 98 
     | 
    
         
            +
                    all = BitexBot::SellOpeningFlow.sync_open_positions
         
     | 
| 
      
 99 
     | 
    
         
            +
                    all.size.should == 1
         
     | 
| 
      
 100 
     | 
    
         
            +
                    all.first.tap do |o|
         
     | 
| 
      
 101 
     | 
    
         
            +
                      o.price.should == 300.0
         
     | 
| 
      
 102 
     | 
    
         
            +
                      o.amount.should == 600.0
         
     | 
| 
      
 103 
     | 
    
         
            +
                      o.quantity.should == 2
         
     | 
| 
      
 104 
     | 
    
         
            +
                      o.transaction_id.should == 12345678
         
     | 
| 
      
 105 
     | 
    
         
            +
                      o.opening_flow.should == flow
         
     | 
| 
      
 106 
     | 
    
         
            +
                    end
         
     | 
| 
      
 107 
     | 
    
         
            +
                  end.to change{ BitexBot::OpenSell.count }.by(1)
         
     | 
| 
      
 108 
     | 
    
         
            +
                end
         
     | 
| 
      
 109 
     | 
    
         
            +
                
         
     | 
| 
      
 110 
     | 
    
         
            +
                it 'does not register the same buy twice' do
         
     | 
| 
      
 111 
     | 
    
         
            +
                  flow.order_id.should == 12345
         
     | 
| 
      
 112 
     | 
    
         
            +
                  stub_bitex_transactions
         
     | 
| 
      
 113 
     | 
    
         
            +
                  BitexBot::SellOpeningFlow.sync_open_positions
         
     | 
| 
      
 114 
     | 
    
         
            +
                  BitexBot::OpenSell.count.should == 1
         
     | 
| 
      
 115 
     | 
    
         
            +
                  Timecop.travel 1.second.from_now
         
     | 
| 
      
 116 
     | 
    
         
            +
                  stub_bitex_transactions(build(:bitex_sell, id: 23456))
         
     | 
| 
      
 117 
     | 
    
         
            +
                  expect do
         
     | 
| 
      
 118 
     | 
    
         
            +
                    news = BitexBot::SellOpeningFlow.sync_open_positions
         
     | 
| 
      
 119 
     | 
    
         
            +
                    news.first.transaction_id.should == 23456
         
     | 
| 
      
 120 
     | 
    
         
            +
                  end.to change{ BitexBot::OpenSell.count }.by(1)
         
     | 
| 
      
 121 
     | 
    
         
            +
                end
         
     | 
| 
      
 122 
     | 
    
         
            +
                
         
     | 
| 
      
 123 
     | 
    
         
            +
                it 'does not register litecoin buys' do
         
     | 
| 
      
 124 
     | 
    
         
            +
                  flow.order_id.should == 12345
         
     | 
| 
      
 125 
     | 
    
         
            +
                  Bitex::Transaction.stub(all: [build(:bitex_sell, id: 23456, specie: :ltc)])
         
     | 
| 
      
 126 
     | 
    
         
            +
                  expect do
         
     | 
| 
      
 127 
     | 
    
         
            +
                    BitexBot::SellOpeningFlow.sync_open_positions.should be_empty
         
     | 
| 
      
 128 
     | 
    
         
            +
                  end.not_to change{ BitexBot::OpenSell.count }
         
     | 
| 
      
 129 
     | 
    
         
            +
                  BitexBot::OpenSell.count.should == 0
         
     | 
| 
      
 130 
     | 
    
         
            +
                end
         
     | 
| 
      
 131 
     | 
    
         
            +
                
         
     | 
| 
      
 132 
     | 
    
         
            +
                it 'does not register buys from unknown bids' do
         
     | 
| 
      
 133 
     | 
    
         
            +
                  stub_bitex_transactions
         
     | 
| 
      
 134 
     | 
    
         
            +
                  expect do
         
     | 
| 
      
 135 
     | 
    
         
            +
                    BitexBot::SellOpeningFlow.sync_open_positions.should be_empty
         
     | 
| 
      
 136 
     | 
    
         
            +
                  end.not_to change{ BitexBot::OpenSell.count }
         
     | 
| 
      
 137 
     | 
    
         
            +
                end
         
     | 
| 
      
 138 
     | 
    
         
            +
              end
         
     | 
| 
      
 139 
     | 
    
         
            +
              
         
     | 
| 
      
 140 
     | 
    
         
            +
              it 'cancels the associated bitex bid' do
         
     | 
| 
      
 141 
     | 
    
         
            +
                stub_bitex_ask_create
         
     | 
| 
      
 142 
     | 
    
         
            +
                BitexBot::Settings.stub(time_to_live: 3,
         
     | 
| 
      
 143 
     | 
    
         
            +
                  selling: double(quantity_to_sell_per_order: 4, profit: 50))
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                flow = BitexBot::SellOpeningFlow.create_for_market(1000,
         
     | 
| 
      
 146 
     | 
    
         
            +
                  bitstamp_order_book_stub['asks'], bitstamp_transactions_stub, 0.5, 0.25)
         
     | 
| 
      
 147 
     | 
    
         
            +
                
         
     | 
| 
      
 148 
     | 
    
         
            +
                flow.finalise!
         
     | 
| 
      
 149 
     | 
    
         
            +
                flow.should be_settling
         
     | 
| 
      
 150 
     | 
    
         
            +
                flow.finalise!
         
     | 
| 
      
 151 
     | 
    
         
            +
                flow.should be_settling
         
     | 
| 
      
 152 
     | 
    
         
            +
                Bitex::Order.stub(active: [])
         
     | 
| 
      
 153 
     | 
    
         
            +
                flow.finalise!
         
     | 
| 
      
 154 
     | 
    
         
            +
                flow.should be_finalised
         
     | 
| 
      
 155 
     | 
    
         
            +
              end
         
     | 
| 
      
 156 
     | 
    
         
            +
            end
         
     | 
    
        data/spec/spec_helper.rb
    ADDED
    
    | 
         @@ -0,0 +1,43 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'bundler/setup'
         
     | 
| 
      
 2 
     | 
    
         
            +
            Bundler.setup
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            require 'bitex_bot'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'factory_girl'
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'database_cleaner'
         
     | 
| 
      
 7 
     | 
    
         
            +
            require 'shoulda/matchers'
         
     | 
| 
      
 8 
     | 
    
         
            +
            require 'timecop'
         
     | 
| 
      
 9 
     | 
    
         
            +
            FactoryGirl.find_definitions
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            Dir[File.dirname(__FILE__) + '/support/*.rb'].each {|file| require file }
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            # Automatically do rake db:test:prepare
         
     | 
| 
      
 14 
     | 
    
         
            +
            ActiveRecord::Migration.maintain_test_schema!
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            # Transactional fixtures do not work with Selenium tests, because Capybara
         
     | 
| 
      
 17 
     | 
    
         
            +
            # uses a separate server thread, which the transactions would be hidden
         
     | 
| 
      
 18 
     | 
    
         
            +
            # from. We hence use DatabaseCleaner to truncate our test database.
         
     | 
| 
      
 19 
     | 
    
         
            +
            DatabaseCleaner.strategy = :truncation
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            RSpec.configure do |config|
         
     | 
| 
      
 22 
     | 
    
         
            +
              config.include(FactoryGirl::Syntax::Methods)
         
     | 
| 
      
 23 
     | 
    
         
            +
              config.mock_with :rspec do |mocks|
         
     | 
| 
      
 24 
     | 
    
         
            +
                mocks.yield_receiver_to_any_instance_implementation_blocks = true
         
     | 
| 
      
 25 
     | 
    
         
            +
                mocks.syntax = [:expect, :should]
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
              config.expect_with :rspec do |c|
         
     | 
| 
      
 28 
     | 
    
         
            +
                c.syntax = [:expect, :should]
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
              
         
     | 
| 
      
 31 
     | 
    
         
            +
              config.before(:all) do
         
     | 
| 
      
 32 
     | 
    
         
            +
                BitexBot::Robot.logger = Logger.new('/dev/null')
         
     | 
| 
      
 33 
     | 
    
         
            +
                BitexBot::Robot.test_mode = true
         
     | 
| 
      
 34 
     | 
    
         
            +
              end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
              config.after(:each) do
         
     | 
| 
      
 37 
     | 
    
         
            +
                DatabaseCleaner.clean       # Truncate the database
         
     | 
| 
      
 38 
     | 
    
         
            +
                Timecop.return
         
     | 
| 
      
 39 
     | 
    
         
            +
              end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
              config.order = "random"
         
     | 
| 
      
 42 
     | 
    
         
            +
            end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     |