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
|
|||
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
|
|||
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
|
|||
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 | } |
This check looks for function or method calls that always return null and whose return value is assigned to a variable.
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.