Issues (13)

src/Command/RunCommand.php (3 issues)

1
<?php
2
3
namespace Mokka\Command;
4
5
6
use Mokka\Action\Action;
7
use Mokka\Action\ActionInterface;
8
use Mokka\Action\BuyAction;
9
use Mokka\Action\SellAction;
10
use Mokka\Calculator\Quantity;
11
use Mokka\Config\Configurator;
12
use Mokka\Config\Logger;
13
use Mokka\Exchange\ExchangeFactory;
14
use Mokka\Strategy\IndicatorFactory;
15
use Mokka\Strategy\StrategyCalculator;
16
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
17
use Symfony\Component\Console\Command\Command;
18
use Symfony\Component\Console\Helper\Table;
19
use Symfony\Component\Console\Input\InputInterface;
20
use Symfony\Component\Console\Input\InputOption;
21
use Symfony\Component\Console\Output\OutputInterface;
22
use Symfony\Component\Console\Question\ChoiceQuestion;
23
use Symfony\Component\Console\Question\Question;
24
25
26
class RunCommand extends Command
27
{
28
29
    protected function configure()
30
    {
31
        $this
32
            // the name of the command (the part after "bin/console")
33
            ->setName('run')
34
            ->setDescription('Run Mokka! Run!')
35
            ->addOption('market', 'm', InputOption::VALUE_OPTIONAL, 'Choose market to run', 'binance')
36
            ->addOption('interval', 'i', InputOption::VALUE_OPTIONAL, 'Seconds for each requests. Default: 60', 60)
37
            ->addOption('symbol', 's', InputOption::VALUE_OPTIONAL, 'Symbol for the bot to run', 'BTCUSDT')
38
            ->addOption('indicator', 'it', InputOption::VALUE_OPTIONAL, 'Which indicator will be applied? (for future development)', 'percent')
39
            ->addOption('config', 'c', InputOption::VALUE_OPTIONAL, 'default config file. you can use custom config for each command', 'default')
40
            ->addOption('test', 't', InputOption::VALUE_OPTIONAL, 'Test mode for botta. If set TRUE botta will not buy and sell any crypto currency', false)
41
42
        ;
43
    }
44
45
    /**
46
     * @param InputInterface $input
47
     * @param OutputInterface $output
48
     * @return int|null|void
49
     * @throws \Symfony\Component\Debug\Exception\ClassNotFoundException
50
     */
51
    protected function execute(InputInterface $input, OutputInterface $output)
52
    {
53
        //get config first
54
        try {
55
            $config = (new Configurator(__DIR__.'/../../config/'.$input->getOption('config').'.yml'))->make();
56
57
            //check if Exchange Market provider is available
58
            $marketConfig = $config->get('markets.'.$input->getOption('market'));
59
            $market = (new ExchangeFactory($input->getOption('market')))->make([$marketConfig]);
60
61
            //set logs (txt db)
62
            $logFileType =
63
                $config->get('mokka.default_log_type') == 'date'
64
                    ? (new \DateTime())->format('Y-m-d')
65
                    : $input->getOption('symbol');
66
67
            $logger = new Logger(__DIR__.'/../../logs/', $logFileType);
68
69
            //check the first row in logs
70
            $this->createActionFile($logger, $input, $output);
71
72
            //get indicator
73
            $indicatorConfig = $config->get('indicators.'.$input->getOption('indicator'));
74
            $indicator = (new IndicatorFactory($input->getOption('indicator')))
75
                ->make([$market, $logger, $indicatorConfig]);
76
77
78
            //run strategy calculator
79
            $strategy = new StrategyCalculator($market, $indicator);
80
81
            $strategy->setInterval($input->getOption('interval'));
82
            $strategy->setSymbol($input->getOption('symbol'));
83
            $strategy->setMarket($input->getOption('market'));
84
85
            $output->writeln('<info>Mokka Started!</info>');
86
            $table = new Table($output);
87
            $table->setHeaders(array('Action', 'Previous Price', 'Action Price', 'Symbol', 'Amount', 'Trigger', 'Change', 'Date'));
88
89
            while (1) {
90
                $quantity = new Quantity();
91
                $action = $strategy->run($logger);
92
93
                if ($input->getOption('test') === false) {
94
                    if ($action->getType() == ActionInterface::TYPE_BUY) {
95
                        //calculate quantity
96
                        $maxFund = $config->get('markets.'.$input->getOption('market').'.max_fund');
97
98
                        /** @var BuyAction $action */
99
                        $action->setQuantity(
100
                            $quantity->buyQuantityCalculator($maxFund, $action->getActionPrice(), $market->getBalance())
101
                        );
102
103
                        $market->buyOrder($action);
104
                    }
105
106
107
                    if ($action->getType() == ActionInterface::TYPE_SELL) {
108
                        //get quantity to sell
109
                        $maxSell = $config->get('markets.'.$input->getOption('market').'.max_sell');
110
111
                        $action->setQuantity(
112
                            $quantity->sellQuantityCalculator($maxSell, $action->getQuantity())
113
                        );
114
115
                        /** @var  SellAction $action */
116
                        $market->sellOrder($action);
117
                    }
118
                }
119
120
                //log the action
121
                if ($action->getType() != ActionInterface::TYPE_IDLE) {
122
                    $logger->insert()->set($action->toArray())->execute();
123
                }
124
125
                $table->setRows(array(
126
                    array(
127
                        $action->getType(),
128
                        $action->getPreviousPrice(),
129
                        $action->getActionPrice(),
130
                        $action->getSymbol(),
131
                        $action->getQuantity(),
132
                        $config->get('indicators.percent.default_percent'),
133
                        round($action->getActionPrice() / $action->getPreviousPrice(), 5),
134
                        date('Y-m-d H:i:s')
135
                    ),
136
                ));
137
138
                $table->render();
139
140
                sleep($input->getOption('interval'));
141
            }
142
143
            $output->writeln('<question>Mokka stopped!</question>');
144
145
        } catch (InvalidConfigurationException $exception) {
146
            $output->writeln("<error>Invalid Configuration</error>");
147
        } catch (\Exception $exception) {
148
            $output->writeln("<error>{$exception->getMessage()}</error>");
149
        }
150
    }
151
152
    /**
153
     * @param Logger $logger
154
     * @param InputInterface $input
155
     * @param OutputInterface $output
156
     * @return array
157
     * @internal param Logger $action
158
     */
159
    protected function createActionFile(Logger $logger, InputInterface $input, OutputInterface $output)
160
    {
161
162
        $lastAction = $logger
0 ignored issues
show
Are you sure the assignment to $lastAction is correct as $logger->read()->where('...te')->limit(1)->first() targeting Flatbase\Query\ReadQuery::first() seems to always return null.

This check looks for function or method calls that always return null and whose return value is assigned to a variable.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
$object = $a->getObject();

The method getObject() can return nothing but null, so it makes no sense to assign that value to a variable.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
163
            ->read()
164
            ->where('market', '=', $input->getOption('market'))
165
            ->where('symbol', '=', $input->getOption('symbol'))
166
            ->sortDesc('lastUpdate')
167
            ->limit(1)
168
            ->first();
169
170
        if ($lastAction) {
0 ignored issues
show
$lastAction is of type null, thus it always evaluated to false.
Loading history...
171
            return;
172
        }
173
174
        $helper = $this->getHelper('question');
175
176
        $question1 = new ChoiceQuestion(
177
            "We need to know your last transaction. Please check the market ({$input->getOption('market')}) and set your last action for {$input->getOption('symbol')}.",
178
            array('buy', 'sell'),
179
            0
180
        );
181
182
        $question1->setErrorMessage('Your response is invalid.');
183
        $chosenActionType = $helper->ask($input, $output, $question1);
184
185
186
        $question2 = new Question("What was the last price for {$input->getOption('symbol')}?");
187
        $price = $helper->ask($input, $output, $question2);
188
189
        if (!$price) {
190
            $output->writeln("<comment>You need to tell me the last action price. Otherwise I can not move on.</comment>");
191
            die();
0 ignored issues
show
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
192
        }
193
194
        $actionContent = new Action();
195
        $actionContent->setType($chosenActionType);
196
        $actionContent->setSymbol($input->getOption('symbol'));
197
        $actionContent->setLastUpdate(time());
198
        $actionContent->setMarket($input->getOption('market'));
199
        $actionContent->setPreviousPrice($price);
200
        $actionContent->setActionPrice($price);
201
202
        $output->writeln("<info>OK. I know what to do  now ;) .</info>");
203
        $logger->insert()->set($actionContent->toArray())->execute();
204
205
        return (array) $actionContent;
206
    }
207
208
209
}