├── LICENSE ├── README.md ├── fivefactorAlpha.py ├── suolos.py ├── test.py ├── threefactor.py ├── touzi.py ├── touzi2.py └── touzi3.py /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Quantitative-Investment_Learning 2 | ## 量化投资学习,多因子、三因子、五因子、索罗斯投资策略 3 | ### 说明: 4 | #### 1. fivefactorAlpha.py 多因子Alpha策略,作者: edward07t 5 | #### 2. suolos.py 索罗斯策略,布林线超低配池策略 6 | #### 3. threefactor.py 三因子策略 7 | #### 4. touzi.py 多因子打分策略 8 | #### 5. touzi2.py 多因子择股,简单持仓策略 9 | #### 6. touzi3.py 多因子择股,改进海龟交易法 10 | -------------------------------------------------------------------------------- /fivefactorAlpha.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 策略名: 多因子Alpha策略 3 | 作者: edward07t 4 | ''' 5 | import numpy as np 6 | import pandas as pd 7 | # 初始化函数 ####################################################################### 8 | def init(context): 9 | 10 | # 上一次调仓期 11 | context.last_date = '' 12 | 13 | # 最大持股数量 14 | context.hold_max = 30 15 | 16 | # 设置调仓周期,每月倒数第一个交易日运行 17 | run_monthly(func=func_run_monthly, date_rule=-1) 18 | 19 | 20 | # 月末调仓函数 ####################################################################### 21 | def func_run_monthly(context, bar_dict): 22 | # 获取上一个交易日的日期 23 | date = get_last_datetime().strftime('%Y%m%d') 24 | 25 | # 获取上个月末调仓日期 26 | context.last_date = func_get_end_date_of_last_month(date) 27 | 28 | log.info('############################## ' + str(date) + ' ###############################') 29 | 30 | # 获取所有A股股票代码 31 | # get_all_securities获取所有证券的基本信息,包括股票、基金、指数。 32 | # .index股票代码 33 | securities = list(get_all_securities('stock', date).index) 34 | 35 | # 获取pb, pe, ps财务因子为正的股票 36 | q = query( 37 | valuation.symbol, 38 | valuation.pb, 39 | valuation.ps_ttm, 40 | valuation.pe_ttm 41 | ).filter( 42 | valuation.pb > 0, 43 | valuation.ps_ttm > 0, 44 | valuation.pe_ttm > 0, 45 | valuation.symbol.in_(securities) 46 | ) 47 | df = get_fundamentals(q, date) 48 | # 将获取到的股票代码存入securities 49 | securities = list(df['valuation_symbol'].values) 50 | 51 | 52 | # 计算过去一个月的股价动量、成交金额、ST信息 53 | # '1d' 日级,'is_st': 是否为ST, 返回值为0或1, 0表示非ST,'close': 收盘价(元),'turnover': 成交额(元) 54 | # skip_paused 是否跳过停牌数据,fq 复权选项,is_panel 返回数据格式 默认为dict 55 | # get_price(symbol_list, start_date, end_date, fre_step, fields, skip_paused = False, fq = 'pre', bar_count = 0, is_panel = 0) 56 | values = get_price(securities, context.last_date, date, '1d', ['close','turnover','is_st'], skip_paused = False, fq = 'pre', is_panel = 0) 57 | 58 | momentum = [] 59 | turnover = [] 60 | st = [] 61 | for stock in securities: 62 | try: 63 | # 计算股价动量 64 | momentum.append((values[stock]['close'][-1] - values[stock]['close'][0]) / values[stock]['close'][0]) 65 | 66 | # 计算总成交金额 67 | turnover.append(values[stock]['turnover'].sum()) 68 | 69 | # 最近股票是否为ST 70 | st.append(values[stock]['is_st'][-1]) 71 | except: 72 | log.info('数据缺失: %s' % stock) 73 | 74 | # 数据获取不到设置为none 75 | momentum.append(None) 76 | turnover.append(None) 77 | st.append(None) 78 | 79 | df['momentum'] = np.array(momentum) 80 | df['turnover'] = np.array(turnover) 81 | df['is_st'] = np.array(st) 82 | 83 | # 去掉ST和成交金额为0的股票 84 | df[df['is_st'] == 1] = None 85 | df[df['turnover'] == 0] = None 86 | # 过滤缺失数据 87 | df = df.dropna() 88 | 89 | 90 | # 去极值 91 | # 使用浅拷贝 92 | df = winsorize(df, 'valuation_pb', 20).copy() 93 | df = winsorize(df, 'valuation_ps_ttm', 20).copy() 94 | df = winsorize(df, 'valuation_pe_ttm', 20).copy() 95 | df = winsorize(df, 'momentum', 20).copy() 96 | df = winsorize(df, 'turnover', 20).copy() 97 | df = df.dropna() 98 | 99 | 100 | # 为全部A股打分,综合得分越小越好 101 | df['scores'] = 0 102 | # sort_values True升序排序 103 | list_pb = list(df.sort_values(['valuation_pb'], ascending = True)['valuation_symbol'].values) 104 | func_scores(df, list_pb) 105 | list_ps = list(df.sort_values(['valuation_ps_ttm'], ascending = True)['valuation_symbol'].values) 106 | func_scores(df, list_ps) 107 | list_pe = list(df.sort_values(['valuation_pe_ttm'], ascending = True)['valuation_symbol'].values) 108 | func_scores(df, list_pe) 109 | list_mo = list(df.sort_values(['momentum'], ascending = True)['valuation_symbol'].values) 110 | func_scores(df, list_mo) 111 | list_to = list(df.sort_values(['turnover'], ascending = True)['valuation_symbol'].values) 112 | func_scores(df, list_to) 113 | 114 | 115 | # 根据股票综合得分为股票排序 116 | context.selected = list(df.sort_values(['scores'], ascending = True)['valuation_symbol'].values) 117 | 118 | # 买入挑选的股票 119 | func_do_trade(context, bar_dict) 120 | 121 | context.last_date = date 122 | 123 | 124 | #### 每日检查止损条件 125 | def handle_bar(context, bar_dict): 126 | 127 | last_date = get_last_datetime().strftime('%Y%m%d') 128 | if last_date != context.last_date and len(list(context.portfolio.stock_account.positions.keys())) > 0: 129 | # 如果不是调仓日且有持仓,判断止损条件 130 | func_stop_loss(context, bar_dict) 131 | 132 | 133 | #### 1. 获取上月月末日期 ##################################################### 134 | def func_get_end_date_of_last_month(current_date): 135 | # 获取从上一个交易日前一个月中的所有交易日,日期排序从前至后 136 | trade_days = list(get_trade_days(None, current_date, count=30)) 137 | 138 | # 转化为%Y%m%d格式 139 | for i in range(len(trade_days)): 140 | trade_days[i] = trade_days[i].strftime('%Y%m%d') 141 | 142 | # 只要交易日的date和当前交易日的月份不同即为上一个月月末日期,例如[20171013]-[20170928] 143 | # reversed反转序列,便于快速找到月末日期 144 | for date in reversed(trade_days): 145 | if date[5] != current_date[5]: 146 | return date 147 | 148 | log.info('Cannot find the end date of last month.') 149 | return 150 | 151 | 152 | #### 2. 中位数去极值函数 #################################################### 153 | def winsorize(df, factor, n=20): 154 | ''' 155 | df为bar_dictFrame数据 156 | factor为需要去极值的列名称 157 | n 为判断极值上下边界的常数 158 | ''' 159 | ls_raw = np.array(df[factor].values) 160 | # 排序 axis=0,按列排列 161 | ls_raw.sort(axis = 0) 162 | # 获取中位数 163 | D_M = np.median(ls_raw) 164 | 165 | # 计算离差值 166 | ls_deviation = abs(ls_raw - D_M) 167 | ls_deviation.sort(axis = 0) 168 | # 获取离差中位数 169 | D_MAD = np.median(ls_deviation) 170 | 171 | # 将大于中位数n倍离差中位数的值赋为NaN 172 | df.loc[df[factor] >= D_M + n * D_MAD, factor] = None 173 | # 将小于中位数n倍离差中位数的值赋为NaN 174 | df.loc[df[factor] <= D_M - n * D_MAD, factor] = None 175 | 176 | return df 177 | 178 | 179 | #### 3. 按因子排序打分函数 ############################################################# 180 | def func_scores(df, ls): 181 | ''' 182 | 按照因子暴露值将股票分为20档 183 | 第一档股票综合得分+1分 184 | 第二档股票综合得分+2分 185 | 以此类推 186 | ''' 187 | # 每档有quotient只股票 //整除,向下取整 188 | quotient = len(ls) // 20 189 | # 余数 190 | remainder = len(ls) % 20 191 | layer = np.array([quotient]*20) 192 | 193 | for i in range(0, remainder): 194 | layer[-(1+i)] += 1 195 | 196 | layer = np.insert(layer, 0, 0) 197 | layer = layer.cumsum() 198 | 199 | for i in range(0,20): 200 | for j in range(layer[i], layer[i+1]): 201 | df.loc[df['valuation_symbol'] == ls[j], 'scores'] += (i + 1) 202 | 203 | 204 | #### 4.下单函数 ################################################################### 205 | def func_do_trade(context, bar_dict): 206 | # 先清空所有持仓 207 | if len(list(context.portfolio.stock_account.positions.keys())) > 0: 208 | for stock in list(context.portfolio.stock_account.positions.keys()): 209 | order_target(stock, 0) 210 | 211 | # 买入前30支股票 212 | for stock in context.selected: 213 | order_target_percent(stock, 1./context.hold_max) 214 | if len(list(context.portfolio.stock_account.positions.keys())) >= context.hold_max: 215 | break 216 | return 217 | 218 | 219 | #### 5.止损函数 #################################################################### 220 | def func_stop_loss(context, bar_dict): 221 | #获取账户持仓信息 222 | holdstock = list(context.portfolio.stock_account.positions.keys()) 223 | if len(holdstock) > 0: 224 | num = -0.1 225 | for stock in holdstock: 226 | close = history(stock,['close'],1,'1d').values 227 | if close/context.portfolio.positions[stock].last_price -1 <= num: 228 | order_target(stock,0) 229 | log.info('股票{}已止损'.format(stock)) 230 | 231 | 232 | #获取账户持仓信息 233 | holdstock = list(context.portfolio.stock_account.positions.keys()) 234 | if len(holdstock) > 0: 235 | num = - 0.13 236 | T = history('000001.SH',['quote_rate'],7,'1d').values.sum() 237 | if T < num*100: 238 | log.info('上证指数连续三天下跌{}已清仓'.format(T)) 239 | for stock in holdstock: 240 | order_target(stock,0) 241 | -------------------------------------------------------------------------------- /suolos.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import datetime, time 4 | import statsmodels.api as sm 5 | import talib 6 | import math 7 | 8 | def init(context): 9 | set_log_level('warn') 10 | #智能选股 11 | get_iwencai('沪深 300') 12 | #设置交易周期 13 | context.pp=5 14 | #设置账户总资金 15 | context.cash=10000000 16 | #设置最大持股数 17 | context.max_stocks=10 18 | #设置筛选因子个数 19 | context.max_need=5 20 | #设置因子测试期数 21 | context.fpn=9 22 | context.st=0 23 | #设置检验因子所用的股票数 24 | context.samt=5 25 | #设置子账户 26 | set_subportfolios([{'cash':7000000,'type':'stock'},{'cash':3000000,'type':'future'}]) 27 | log.warn(['context.cash:',context.cash]) 28 | #设置计数起点 29 | context.cday=0 30 | #设置因子池 31 | context.need=['factor.pe', 32 | 'factor.pb', 33 | 'factor.pcf_cash_flow_ttm', 34 | 'factor.ps', 35 | 'factor.dividend_rate', 36 | 'factor.market_cap', 37 | 'factor.current_market_cap', 38 | 'factor.capitalization', 39 | 'factor.circulating_cap', 40 | 'factor.current_ratio', 41 | 'factor.equity_ratio', 42 | 'factor.quick_ratio', 43 | 'factor.tangible_assets_liabilities', 44 | 'factor.tangible_assets_int_liabilities', 45 | 'factor.net_debt_equity', 46 | 'factor.long_term_debt_to_opt_capital_ratio', 47 | 'factor.tangible_assets_net_liabilities', 48 | 'factor.overall_income_growth_ratio', 49 | 'factor.net_cashflow_psg_rowth_ratio', 50 | 'factor.opt_profit_grow_ratio', 51 | 'factor.total_profit_growth_ratio', 52 | 'factor.diluted_net_asset_growth_ratio', 53 | 'factor.net_cashflow_from_opt_act_growth_ratio', 54 | 'factor.net_profit_growth_ratio', 55 | 'factor.basic_pey_ear_growth_ratio', 56 | 'factor.turnover_of_overall_assets', 57 | 'factor.turnover_ratio_of_context_payable', 58 | 'factor.turnover_of_current_assets', 59 | 'factor.turnover_of_fixed_assets', 60 | 'factor.cash_cycle', 61 | 'factor.inventory_turnover_ratio', 62 | 'factor.turnover_ratio_of_receivable', 63 | 'factor.weighted_roe', 64 | 'factor.overall_assets_net_income_ratio', 65 | 'factor.net_profit_margin_on_sales', 66 | 'factor.before_tax_profit_div_income', 67 | 'factor.sale_cost_div_income', 68 | 'factor.roa', 69 | 'factor.ratio_of_sales_to_cost', 70 | 'factor.net_profit_div_income', 71 | 'factor.opt_profit_div_income', 72 | 'factor.opt_cost_div_income', 73 | 'factor.administration_cost_div_income', 74 | 'factor.financing_cost_div_income', 75 | 'factor.vr_rate', 76 | 'factor.vstd', 77 | 'factor.arbr', 78 | 'factor.srdm', 79 | 'factor.vroc', 80 | 'factor.vrsi', 81 | 'factor.cr', 82 | 'factor.mfi', 83 | 'factor.vr', 84 | 'factor.mass', 85 | 'factor.obv', 86 | 'factor.pvt', 87 | 'factor.wad', 88 | 'factor.bbi', 89 | 'factor.mtm', 90 | 'factor.dma', 91 | 'factor.ma', 92 | 'factor.macd', 93 | 'factor.expma', 94 | 'factor.priceosc', 95 | 'factor.trix', 96 | 'factor.dbcd', 97 | 'factor.dpo', 98 | 'factor.psy', 99 | 'factor.vma', 100 | 'factor.vmacd', 101 | 'factor.vosc', 102 | 'factor.tapi', 103 | 'factor.micd', 104 | 'factor.rccd'] 105 | #设置股票因子及其方向 106 | context.mfactors={'factor.pe':-1, 107 | 'factor.pb':-1, 108 | 'factor.pcf_cash_flow_ttm':-1, 109 | 'factor.ps':-1, 110 | 'factor.dividend_rate':1, 111 | 'factor.market_cap':-1, 112 | 'factor.current_market_cap':-1, 113 | 'factor.capitalization':-1, 114 | 'factor.circulating_cap':-1, 115 | 'factor.current_ratio':1, 116 | 'factor.equity_ratio':-1, 117 | 'factor.quick_ratio':1, 118 | 'factor.tangible_assets_liabilities':1, 119 | 'factor.tangible_assets_int_liabilities':1, 120 | 'factor.net_debt_equity':-1, 121 | 'factor.long_term_debt_to_opt_capital_ratio':-1, 122 | 'factor.tangible_assets_net_liabilities':1, 123 | 'factor.overall_income_growth_ratio':1, 124 | 'factor.net_cashflow_psg_rowth_ratio':1, 125 | 'factor.opt_profit_grow_ratio':1, 126 | 'factor.total_profit_growth_ratio':1, 127 | 'factor.diluted_net_asset_growth_ratio':1, 128 | 'factor.net_cashflow_from_opt_act_growth_ratio':1, 129 | 'factor.net_profit_growth_ratio':1, 130 | 'factor.basic_pey_ear_growth_ratio':1, 131 | 'factor.turnover_of_overall_assets':1, 132 | 'factor.turnover_ratio_of_context_payable':1, 133 | 'factor.turnover_of_current_assets':1, 134 | 'factor.turnover_of_fixed_assets':1, 135 | 'factor.cash_cycle':-1, 136 | 'factor.inventory_turnover_ratio':1, 137 | 'factor.turnover_ratio_of_receivable':1, 138 | 'factor.weighted_roe':1, 139 | 'factor.overall_assets_net_income_ratio':1, 140 | 'factor.net_profit_margin_on_sales':1, 141 | 'factor.before_tax_profit_div_income':1, 142 | 'factor.sale_cost_div_income':-1, 143 | 'factor.roa':1, 144 | 'factor.ratio_of_sales_to_cost':-1, 145 | 'factor.net_profit_div_income':1, 146 | 'factor.opt_profit_div_income':1, 147 | 'factor.opt_cost_div_income':-1, 148 | 'factor.administration_cost_div_income':-1, 149 | 'factor.financing_cost_div_income':-1, 150 | 'factor.vr_rate':1, 151 | 'factor.vstd':-1, 152 | 'factor.arbr':1, 153 | 'factor.srdm':1, 154 | 'factor.vroc':1, 155 | 'factor.vrsi':1, 156 | 'factor.cr':1, 157 | 'factor.mfi':1, 158 | 'factor.vr':1, 159 | 'factor.mass':1, 160 | 'factor.obv':1, 161 | 'factor.pvt':1, 162 | 'factor.wad':1, 163 | 'factor.bbi':1, 164 | 'factor.mtm':1, 165 | 'factor.dma':1, 166 | 'factor.ma':1, 167 | 'factor.macd':1, 168 | 'factor.expma':1, 169 | 'factor.priceosc':1, 170 | 'factor.trix':1, 171 | 'factor.dbcd':1, 172 | 'factor.dpo':1, 173 | 'factor.psy':1, 174 | 'factor.vma':1, 175 | 'factor.vmacd':1, 176 | 'factor.vosc':1, 177 | 'factor.tapi':1, 178 | 'factor.micd':1, 179 | 'factor.rccd':1} 180 | context.lastlong={}.fromkeys(context.need) #超配股票池 181 | context.lastshort={}.fromkeys(context.need) #低配股票池 182 | context.winr={} 183 | context.ir={} 184 | context.incomer={} 185 | #设置默认因子 186 | context.realneed=['factor.pe','factor.weighted_roe','factor.net_cashflow_from_opt_act_growth_ratio', 187 | 'factor.overall_income_growth_ratio','factor.turnover_ratio_of_context_payable'] 188 | #设置打分机制 189 | context.score=5 190 | #定期运行函数 191 | run_monthly(func=reallocate,date_rule=8) 192 | 193 | def reallocate(context,bar_dict): 194 | log.warn('……每月第 8 交易日/起始日……') 195 | tempstocks=give_me_stocks(context,bar_dict,is_re=1) 196 | #前maxstocks只股票带综合分数 197 | #log.info('This is the stocks for choosing') 198 | #log.info(tempstocks) 199 | nstocks=[] 200 | for i in range(len(tempstocks)): 201 | nstocks.append(tempstocks[i][0]) 202 | #起手均分现金,之后均分持仓规模 203 | log.warn('刷新持仓股票') 204 | if(context.cday==0): 205 | cash=context.cash/context.max_stocks 206 | log.warn(['context.cash:',context.cash]) 207 | else: 208 | # 分配可用资金 209 | cash=context.portfolio.portfolio_value/context.max_stocks 210 | # 原股票池中的股票不在新股票池中则清0 211 | for i in list(context.portfolio.positions.keys()): 212 | if(i not in nstocks[:context.max_stocks]): 213 | order_target(i,0) 214 | for i in nstocks: 215 | order_target_value(i,cash) 216 | if(len(context.portfolio.positions.keys())==context.max_stocks): 217 | break 218 | log.warn('刷新完毕') 219 | log.warn('期货操作') 220 | Astocks=[] #用于计算期货份数的列表,存放持股分数 221 | tstocks=dict(tempstocks) 222 | ctr=0 223 | for i in list(context.portfolio.positions.keys()): 224 | if i not in tstocks: 225 | continue 226 | Astocks.append((i,tstocks[i])) 227 | ctr+=1 228 | #log.info(ctr) 229 | Amount=give_me_amount(context,bar_dict,*Astocks) 230 | #log.info(['The number of Amount is',Amount]) 231 | code=get_future_code('IF','next_month') #下月期货 232 | if(len(context.portfolio.stock_account.positions)==0): #若子账户期货为空,说明当月期货已经交割,入手下月 233 | order(code,Amount,pindex=1,type='short') 234 | log.warn('下月期货入手') 235 | else: 236 | current_code=list(context.portfolio.stock_account.positions)[0] #手头期货 237 | number=context.portfolio.stock_account.positions[current_code].total_amount #手头期货份数 238 | if(code==current_code): #如果手头的是下月期货,直接调仓 239 | if Amount>number: 240 | order(current_code,-number+Amount,pindex=1,type='short') #做空期货,卖出 241 | log.warn('做空期货') 242 | else: 243 | order(current_code,number-Amount,pindex=1,type='long') #做多期货,买回 244 | log.warn('做多期货') 245 | else: 246 | order(current_code,-number,pindex=1,type='long') #平仓买回 247 | order(code,Amount,pindex=1,type='short') #做空下月期货 248 | log.warn('平仓买回 AND 做空下月期货') 249 | log.warn('期货操作完毕') 250 | 251 | def give_me_amount(context,bar_dict,*tempstocks): 252 | log.warn('获取期货买卖份数') 253 | #计算 Beta 值 254 | beta=[] 255 | percent=[] 256 | nstocks=[] 257 | rstocks=[] 258 | sum1=0.0 259 | for i in range(len(tempstocks)): 260 | sum1+=tempstocks[i][1] 261 | rstocks.append(tempstocks[i][1]) 262 | nstocks.append(tempstocks[i][0]) 263 | #计算权重 264 | for i in range(len(rstocks)): 265 | percent.append(rstocks[i]/sum1) 266 | for i in nstocks: #迭代股票池 267 | iclose=get_price(['000300.SH',i],None,get_datetime().strftime('%Y%m%d'),'1d',['close'],False,None,220) #证券 268 | xclose=iclose['000300.SH']['close'] 269 | yclose=iclose[i]['close'] 270 | if(len(yclose)==0): 271 | #log.info('糟糕,yclose 长度是 0') 272 | beta.append(0) 273 | continue 274 | if(len(xclose)!=len(yclose)): 275 | #log.info(['iclose 是',iclose]) 276 | l=min(len(xclose),len(yclose)) 277 | xclose=xclose[-l:] 278 | yclose=yclose[-l:] 279 | x=np.array(xclose) 280 | y=np.array(yclose) 281 | rm=(x[1:]-x[0:-1])/x[0:-1] 282 | r1=(y[1:]-y[0:-1])/y[0:-1] 283 | est=sm.OLS(r1,sm.add_constant(rm)) 284 | est=est.fit() #单只股票的 beta 285 | beta.append(est.params[1]) 286 | Beta=0.0 287 | for i in range(len(beta)): 288 | Beta+=beta[i]*percent[i] #Beat 等于因子打分权重之和 289 | #log.info(['Beta 是',Beta]) 290 | close=history('000300.SH',['close'],1,'1d') #期货开盘价 291 | #log.info(['沪深 300 股指期货的开盘价是',close]) 292 | cash=context.portfolio.portfolio_value*1.0/context.max_stocks 293 | log.warn(['Beta and cash and 期货价:',Beta,cash,close['close'][0]]) 294 | Amount=math.ceil((Beta*(cash*context.max_stocks))/(300*close['close'][0])) 295 | log.warn(['Amount 是',Amount]) 296 | log.warn('期货买卖份数获取完毕') 297 | return int(Amount*1) 298 | 299 | def give_me_need(context,bar_dict): 300 | log.warn('获取新建因子') 301 | n=len(context.mfactors) 302 | querc=','.join(context.need) 303 | #log.info(querc) 304 | q=query(querc).filter(factor.symbol.in_(context.iwencai_securities),factor.date==get_datetime().strftime('%Y-%m-%d')) 305 | df=get_factors(q).fillna(0) 306 | #log.info(df) 307 | df['factor_symbol']=context.iwencai_securities[:len(df)] 308 | n=len(df) 309 | m=len(df.columns)-1 310 | for k in range(m): 311 | tempdf=df.sort_values(df.columns[k]) 312 | name=list(tempdf['factor_symbol']) 313 | #log.info('排序后的 df') 314 | #log.info(tempdf) 315 | #log.info(name) 316 | f='factor.'+df.columns[k] 317 | #log.info(f) 318 | if context.mfactors[f]<0: 319 | context.lastlong[f]=name[:context.samt*2] 320 | context.lastshort[f]=name[-2*context.samt:] 321 | else: 322 | context.lastlong[f]=name[-2*context.samt:] 323 | context.lastshort[f]=name[:2*context.samt] 324 | #log.info(['long',len(context.lastlong[f])]) 325 | #log.info(['short',len(context.lastshort[f])]) 326 | value=get_price(context.lastlong[f],None,get_datetime().strftime("%Y%m%d"),'22d',['close'],True,None,context.fpn) 327 | rsum1=np.zeros((context.fpn-1,1)) 328 | ctr=0 329 | for stk in context.lastlong[f]: 330 | #log.info(['这是 value[stk]',value[stk]]) 331 | x=np.array(value[stk]) 332 | #log.info(x) 333 | if(len(x)!=context.fpn): 334 | #log.info('超配之力不从心') 335 | continue 336 | if(ctr==context.samt): 337 | break 338 | rsum1+=(x[1:]-x[:-1])/x[:-1] 339 | ctr=ctr+1 340 | #log.info([rsum1,ctr]) 341 | value=get_price(context.lastshort[f],None,get_datetime().strftime("%Y%m%d"),'22d',['close'],True,None,context.fpn) 342 | rsum2=np.zeros((context.fpn-1,1)) 343 | ctr=0 344 | for stk in context.lastshort[f]: 345 | x=np.array(value[stk]) 346 | #log.info(x) 347 | if(len(x)!=context.fpn): 348 | #log.info('低配之力不从心') 349 | continue 350 | if(ctr==context.samt): 351 | break 352 | rsum2+=(x[1:]-x[:-1])/x[:-1] 353 | ctr=ctr+1 354 | #log.info([rsum2,ctr]) 355 | context.winr[f]=((np.sum(rsum1-rsum2>0)/(context.fpn-1))) 356 | context.ir[f]=(np.mean(rsum1-rsum2)/np.std(rsum1-rsum2)) 357 | context.incomer[f]=(np.mean(rsum1)) 358 | #log.info(context.winr) 359 | #log.info(context.ir) 360 | #log.info(context.winr) 361 | #log.info(context.ir) 362 | #log.info(context.incomer) 363 | score={} 364 | score=score.fromkeys(list(context.need),0.0) 365 | #log.info(score) 366 | temp=sorted(context.winr.items(),key=lambda item:item[1]) 367 | for i in range(len(temp)): 368 | score[temp[i][0]]+=0.4*i 369 | temp=sorted(context.ir.items(),key=lambda item:item[1]) 370 | for i in range(len(temp)): 371 | score[temp[i][0]]+=0.4*i 372 | #log.info(score) 373 | temp=sorted(context.incomer.items(),key=lambda item:item[1]) 374 | for i in range(len(temp)): 375 | score[temp[i][0]]+=0.2*i 376 | #log.info(score) 377 | tempscore=sorted(score.items(),key=lambda item:item[1],reverse=True) 378 | #log.info(tempscore) 379 | realneed=[] 380 | for i in range(context.max_need): 381 | realneed.append(tempscore[i][0]) 382 | log.info(realneed) 383 | log.warn('因子获取完毕') 384 | return realneed 385 | 386 | def give_me_stocks(context,bar_dict,is_re): 387 | log.warn('获取新建股票池') 388 | samp=context.iwencai_securities 389 | if(is_re==1): 390 | context.realneed=give_me_need(context,bar_dict) 391 | #从数据库查询因子,获取 q 392 | #log.info(context.realneed) 393 | need=','.join(context.realneed) 394 | #log.info(['这是 need',need]) 395 | q=query(need).filter(factor.symbol.in_(samp),factor.date==get_datetime().strftime('%Y-%m-%d') ) 396 | df=get_factors(q).fillna(0) #结合当天时间,形成 dateframe,缺失值填充为 0 397 | samp=samp[:len(df)] 398 | df['factor_symbol']=samp 399 | n=len(df) #获取行数,即总共获取的股票数目 400 | m=len(df.columns)-1 #获取列数(因子数目)=总列数-1 401 | stocks={} 402 | stocks=stocks.fromkeys(samp,0.0) #预设打分股票池 403 | for i in range(m): #先对因子一列从小到大进行排序,方便打分 ,逐列展开 404 | f='factor.'+df.columns[i] 405 | if(context.mfactors[f]<0): 406 | tempdf=df.sort_values(df.columns[i]) #从小到大 407 | else: 408 | tempdf=df.sort_values(df.columns[i],ascending=False) 409 | security=list(tempdf['factor_symbol']) #排序后的股票代码,重新构建序列 410 | for j in range(n): #确定 n,即确定的股票代码 411 | name=security[j]#字符串格式,股票的代码 412 | rank=int((j/(n/context.score))) #确定等级 413 | stocks[name]+=(5-rank) 414 | tempstocks=sorted(stocks.items(),key=lambda item:item[1],reverse=True) #给已经拥有分数的股票池从大到小排序 415 | log.info(tempstocks) 416 | log.warn('新建股票池获取完毕') 417 | return tempstocks[:2*context.max_stocks] 418 | 419 | def boll(context,bar_dict): 420 | log.warn('布林线检测') 421 | flag=0 #设置触发变量 422 | up=[[],[]] 423 | mid=[[],[]] 424 | low=[[],[]] 425 | cprice=[[],[]] 426 | last=-6 427 | now=-1 428 | for i in list(context.portfolio.positions.keys()): 429 | # 获取历史收盘价 430 | values=history(i,['close'],21,'1d',False,None).fillna(0) 431 | upper,middle,lower=talib.BBANDS(values['close'].values,timeperiod=15,nbdevup=2,nbdevdn=2,matype=0) 432 | up[0].append(upper[last]) 433 | up[1].append(upper[now]) 434 | mid[0].append(middle[last]) 435 | mid[1].append(middle[now]) 436 | low[0].append(lower[last]) 437 | low[1].append(lower[now]) 438 | cprice[0].append(values['close'][last]) 439 | cprice[1].append(values['close'][now]) 440 | up=np.mean(up,1) 441 | mid=np.mean(mid,1) 442 | low=np.mean(low,1) 443 | cprice=np.mean(cprice,1) 444 | #log.info(up) 445 | if(up[1]>up[0] and low[1]mid[1]): 447 | flag=1 448 | log.warn('开轨') 449 | elif(up[1]low[0]): #收轨 450 | if((cprice[0]up[1]) or (cprice[0]>low[0] and 451 | cprice[1]0): #三轨同向 455 | if(cprice[0]>mid[0] and cprice[1]=context.cday): #如果超过计时点,就进行判断 471 | if(context.cday==0): 472 | reallocate(context,bar_dict) #月初处理 473 | context.cday+=context.pp #调整计时起点 474 | else: #非初始日 475 | #布林线通道 476 | log.warn([context.pp,'/天一检测']) 477 | if(boll(context,bar_dict)==1 or int(dd.days)%(15)==0): #布林线判断出要构建新的股票池或者距离上一次构建达到达到 15 天 478 | if(int(dd.days)%(15)==0): 479 | log.warn('15 天/构建') 480 | tempstocks=give_me_stocks(context,bar_dict,is_re=0) 481 | nstocks=[] 482 | for i in range(len(tempstocks)): 483 | nstocks.append(tempstocks[i][0]) 484 | log.info(nstocks) 485 | cash=context.portfolio.portfolio_value/context.max_stocks 486 | log.warn('刷新持仓股票') 487 | ctr=0 488 | for i in list(context.portfolio.positions.keys()): 489 | if(i not in nstocks[:context.max_stocks]):#股票不存在现有账户中,此回合需卖出 490 | order_target(i,0) #全部卖出 491 | ctr+=1 492 | log.warn(['卖出股票数:',ctr]) 493 | for i in nstocks: 494 | order_target_value(i,cash) #调整股票使满足目标金额 495 | if(len(context.portfolio.positions.keys())==context.max_stocks): 496 | break 497 | log.warn('进行期货操作') 498 | Astocks=[] 499 | tstocks=dict(tempstocks) 500 | for i in list(context.portfolio.positions.keys()): 501 | if i not in tstocks: 502 | continue 503 | Astocks.append((i,tstocks[i])) 504 | Amount=give_me_amount(context,bar_dict,*Astocks) 505 | if(len(context.portfolio.stock_account.positions)==0): #若子账户期货为空,说明当月期货已经提前交割,入手下月 506 | code=get_future_code('IF','next_month') 507 | order(code,Amount,pindex=1,type='short') 508 | log.warn('下月期货入手') 509 | else: 510 | current_code=list(context.portfolio.stock_account.positions)[0] #手头期货 511 | number=context.portfolio.stock_account.positions[current_code].total_amount 512 | if Amount>number: 513 | log.warn('做空期货') 514 | order(current_code,-number+Amount,pindex=1,type='short') 515 | else: 516 | log.warn('做多期货') 517 | order(current_code,number-Amount,pindex=1,type='long') 518 | context.cday+=context.pp #调整计时起点 519 | log.warn([context.pp,'/天一检测(15 天检测)完毕']) 520 | 521 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import datetime, time 4 | from dateutil.relativedelta import relativedelta 5 | import talib 6 | import math 7 | from scipy.stats import ttest_ind 8 | from scipy.stats import levene 9 | from sklearn.linear_model import LinearRegression 10 | 11 | def init(context): 12 | 13 | # 最大持股数量 14 | context.hold_max = 40 15 | 16 | # 初始因子池(包括技术因子和财务因子在内的共计107个初始因子) 17 | context.need=['factor.pe',# 市盈率 18 | 'factor.pb',# 市净率 19 | 'factor.pcf_cash_flow_ttm',# 市现率PCF 20 | 'factor.ps',# 市销率 21 | 'factor.dividend_rate',# 股息率 22 | 'factor.market_cap',# 总市值 23 | 'factor.current_market_cap',# 流通市值 24 | 'factor.capitalization',# 总股本 25 | 'factor.circulating_cap',# 流通股本 26 | 'factor.current_ratio',# 流动比率 27 | 'factor.equity_ratio',# 产权比率负债合计/归属母公司股东的权益 28 | 'factor.quick_ratio',# 速动比率 29 | 'factor.tangible_assets_liabilities',# 有形资产/负债合计 30 | 'factor.tangible_assets_int_liabilities',# 有形资产/带息债务 31 | 'factor.net_debt_equity',# 净债务/股权价值 32 | 'factor.long_term_debt_to_opt_capital_ratio',# 长期债务与营运资金比率 33 | 'factor.tangible_assets_net_liabilities',# 有形资产/净债务 34 | 'factor.overall_income_growth_ratio',# 营业总收入同比增长率 35 | # 'factor.net_cashflow_psg_rowth_ratio',# 每股经营活动产生的现金流量净额同比增长率(报错,原因询问客服ing) 36 | # 'factor.opt_profit_grow_ratio',# 营业利润同比增长率 37 | # 'factor.total_profit_growth_ratio',# 利润总额同比增长率 38 | 'factor.diluted_net_asset_growth_ratio',# 净资产收益率摊薄同比增长率 39 | 'factor.net_cashflow_from_opt_act_growth_ratio',# 经营活动产生的现金流量净额同比增长率 40 | 'factor.net_profit_growth_ratio',# 净利润同比增长率 41 | # 'factor.basic_pey_ear_growth_ratio',# 基本每股收益同比增长率 42 | 'factor.turnover_of_overall_assets',# 总资产周转率 43 | 'factor.turnover_ratio_of_account_payable',# 应付账款周转率 44 | 'factor.turnover_of_current_assets',# 流动资产周转率 45 | 'factor.turnover_of_fixed_assets',# 固定资产周转率 46 | 'factor.cash_cycle',# 现金循环周期 47 | 'factor.inventory_turnover_ratio',# 存货周转率 48 | 'factor.turnover_ratio_of_receivable',# 应收账款周转率 49 | 'factor.weighted_roe',# 净资产收益率roe加权 50 | 'factor.overall_assets_net_income_ratio',# 总资产净利率roa 51 | 'factor.net_profit_margin_on_sales',# 销售净利率 52 | 'factor.before_tax_profit_div_income',# 息税前利润/营业总收入 53 | 'factor.sale_cost_div_income',# 销售费用/营业总收入 54 | 'factor.roa',# 总资产报酬率roa 55 | 'factor.ratio_of_sales_to_cost',# 销售成本率 56 | 'factor.net_profit_div_income',# 净利润/营业总收入 57 | 'factor.opt_profit_div_income',# 营业利润/营业总收入 58 | 'factor.opt_cost_div_income',# 营业总成本/营业总收入 59 | 'factor.administration_cost_div_income',# 管理费用/营业总收入 60 | 'factor.financing_cost_div_income',# 财务费用/营业总收入 61 | 'factor.impairment_loss_div_income',# 资产减值损失/营业总收 62 | # 以下为技术因子 63 | 'factor.vr_rate',# 成交量比率 64 | 'factor.vstd',# 成交量标准差 65 | 'factor.arbr',# 人气意愿指标 66 | 'factor.srdm',# 动向速度比率 67 | 'factor.vroc',# 量变动速率 68 | 'factor.vrsi',# 量相对强弱指标 69 | 'factor.cr',# 能量指标 70 | 'factor.mfi',# 资金流向指标 71 | 'factor.vr',# 量比 72 | 'factor.mass',# 梅丝线 73 | 'factor.obv',# 能量潮 74 | 'factor.pvt',# 量价趋势指标 75 | 'factor.wad',# 威廉聚散指标 76 | 'factor.bbi',# 多空指数 77 | 'factor.mtm',# 动力指标 78 | 'factor.dma',# 平均线差 79 | 'factor.ma',# 简单移动平均 80 | 'factor.macd',# 指数平滑异同平均 81 | 'factor.expma',# 指数平均数 82 | 'factor.priceosc',# 价格振荡指标 83 | 'factor.trix',# 三重指数平滑平均 84 | 'factor.dbcd',# 异同离差乖离率 85 | 'factor.dpo',# 区间震荡线 86 | 'factor.psy',# 心理指标 87 | 'factor.vma',# 量简单移动平均 88 | 'factor.vmacd',# 量指数平滑异同平均 89 | 'factor.vosc',# 成交量震荡 90 | 'factor.tapi',# 加权指数成交值 91 | 'factor.micd',# 异同离差动力指数 92 | 'factor.rccd',# 异同离差变化率指数 93 | 'factor.ddi',# 方向标准差偏离指数 94 | 'factor.bias',# 乖离率 95 | 'factor.cci',# 顺势指标 96 | 'factor.kdj',# 随机指标 97 | 'factor.lwr',# L威廉指标 98 | 'factor.roc',# 变动速率 99 | 'factor.rsi',# 相对强弱指标 100 | 'factor.si',# 摆动指标 101 | 'factor.wr',# 威廉指标 102 | 'factor.wvad',# 威廉变异离散量 103 | 'factor.bbiboll',# BBI多空布林线 104 | 'factor.cdp',# 逆势操作 105 | 'factor.env',# ENV指标 106 | 'factor.mike',# 麦克指标 107 | 'factor.adtm',# 动态买卖气指标 108 | 'factor.mi',# 动量指标 109 | 'factor.rc',# 变化率指数 110 | 'factor.srmi',# SRMIMI修正指标 111 | 'factor.dptb',# 大盘同步指标 112 | 'factor.jdqs',# 阶段强势指标 113 | 'factor.jdrs',# 阶段弱势指标 114 | 'factor.zdzb',# 筑底指标 115 | 'factor.atr',# 真实波幅 116 | 'factor.std',# 标准差 117 | 'factor.vhf',# 纵横指标 118 | 'factor.cvlt']# 佳庆离散指标 119 | 120 | # 最大因子数目 121 | context.need_max = 10 122 | 123 | # 上一次调仓期 124 | context.last_date = '' 125 | 126 | # 设置调仓周期,每月倒数第一个交易日运行 127 | run_monthly(func = reallocate, date_rule = -1) 128 | 129 | # 设置因子测试周期为12个月 130 | context.need_tmonth = 12 131 | 132 | # 选择沪深300 133 | get_iwencai('沪深300') 134 | 135 | def reallocate(context, bar_dict): 136 | # 获取上一个交易日的日期 137 | date = get_last_datetime().strftime('%Y%m%d') 138 | log.info('上个交易日日期为:' + date) 139 | 140 | # 获取上个月末调仓日期 141 | context.last_date = func_get_end_date_of_last_month(date) 142 | 143 | log.info('上月月末调仓日期为:' + context.last_date) 144 | 145 | f_needs = get_needs(context, bar_dict, date) 146 | log.info(f_needs) 147 | 148 | # 获得因子函数 149 | def get_needs(context, bar_dict, date): 150 | time_tuple = time.strptime(date, '%Y%m%d') 151 | year, month, day = time_tuple[:3] 152 | # 转化为datetime.date类型 153 | date = datetime.date(year, month, day) 154 | 155 | last_year_date =(date - relativedelta(years = 1)).strftime('%Y%m%d') 156 | log.info('上个交易日前一年的日期为:' + last_year_date) 157 | 158 | # 上一年内的所有交易日期 159 | trade_days = get_trade_days(last_year_date, date).strftime('%Y-%m-%d') 160 | 161 | # 所有因子 162 | need_all = ','.join(context.need) 163 | 164 | # 1.分组单因子有效性检验,参考资料:https://www.ricequant.com/community/topic/702/%E5%8D%95%E4%B8%80%E5%9B%A0%E5%AD%90%E6%9C%89%E6%95%88%E6%80%A7%E6%A3%80%E6%B5%8B/2 165 | # 每隔25天(间隔越短,收益就会越随机) 166 | period = 25 167 | 168 | # 有效因子列表 169 | eff_need = [] 170 | 171 | # 每组股票池数10%只股票 172 | num = 30 173 | 174 | # 获取开始日期因子值 175 | q = query( 176 | factor.symbol, 177 | need_all 178 | ).filter( 179 | factor.symbol.in_(context.iwencai_securities), 180 | # 20180430的数据消失,原因是:last_year_date可能不是交易日,这样就获取不到数据,因此为trade_days[0] 181 | factor.date == trade_days[0] 182 | ) 183 | 184 | df = get_factors(q) 185 | 186 | for col in df.columns.values[1:]: 187 | # 分别按正序和倒序提取因子值前20和后20的股票 188 | a_hign = df.sort_values(col, axis=0, ascending=False) 189 | a_low = df.sort_values(col, axis=0, ascending=True) 190 | 191 | hign = a_hign.iloc[0:num, 0].values.tolist() 192 | low = a_low.iloc[0:num, 0].values.tolist() 193 | hign_price = get_price(hign, last_year_date, date, '1d', ['close'], skip_paused = False, fq = 'pre', is_panel = 1) 194 | low_price = get_price(low, last_year_date, date, '1d', ['close'], skip_paused = False, fq = 'pre', is_panel = 1) 195 | 196 | hign_price = hign_price['close'] 197 | low_price = low_price['close'] 198 | 199 | # 计算平均每组股票每日价格 200 | high_series = hign_price.T.mean() 201 | low_series = low_price.T.mean() 202 | 203 | # 计算收益率 204 | hign_rr = high_series.pct_change(period).dropna() 205 | low_rr = low_series.pct_change(period).dropna() 206 | 207 | # 判断两组数据的方差是否显著不同 208 | le = levene(hign_rr, low_rr) 209 | 210 | # 判断两组数据的均值是否显著不同 211 | tt = ttest_ind(hign_rr, low_rr, equal_var = False) 212 | 213 | # 取置信度为0.05 214 | if le.pvalue < 0.05 and tt.pvalue < 0.05: 215 | eff_need.append('factor.%s' % col) 216 | 217 | # 第一步有效因子个数 218 | log.info(len(eff_need)) 219 | 220 | effneed = ','.join(eff_need) 221 | # 第二步暂时去除 222 | # 可能出现合格因子数目很少的情况 223 | # 拟定:当第一步的因子大于最大因子数目进行第二步,小于则直接进行第三步 224 | if len(eff_need) > context.need_max: 225 | # 2.单因子单股票回归性检验 226 | 227 | # 存放各因子回归系数t检验值 228 | t_data = pd.DataFrame() 229 | 230 | for stock in context.iwencai_securities: 231 | q = query( 232 | factor.date, 233 | factor.symbol, 234 | effneed 235 | ).filter( 236 | factor.symbol == stock, 237 | factor.date.in_(trade_days) 238 | ) 239 | 240 | # get_fundamentals获取财务数,get_factors获取因子数据(暂时将缺失值补充为0) 241 | df = get_factors(q).fillna(0) 242 | 243 | # 去除所有值都缺失的列 244 | df = df.ix[:,~((df==0).all())] 245 | 246 | prices = get_price(stock, last_year_date, date, '1d', ['close'], skip_paused = False, fq = 'pre',is_panel = 0) 247 | stock_close = prices['close'].values 248 | stock_rate = np.diff(stock_close) / stock_close[:-1] 249 | 250 | df = df.iloc[:-1, :] 251 | df['rate'] = stock_rate 252 | 253 | a = {} 254 | # 去极值-标准化(前两列名为 factor_date,factor_symbol,最后一列为rate) 255 | for index, row in list(df.iteritems())[2:-1]: 256 | df[index] = standardize(filter_extreme_3sigma(row)) 257 | 258 | a[index] = [t_value(df[index], df['rate'])] 259 | 260 | 261 | t_data = t_data.append(pd.DataFrame(a), ignore_index=True) 262 | 263 | for index, row in list(t_data.iteritems()): 264 | log.info(index) 265 | a = list(map(abs, list(t_data[index]))) 266 | log.info(np.mean(np.array(a)) 267 | 268 | 269 | 270 | # 获取所有股票每个交易日收盘价 271 | prices = get_price(context.iwencai_securities, last_year_date, date, '1d', ['close'], skip_paused = False, fq = 'pre',is_panel = 0) 272 | 273 | for stock in context.iwencai_securities: 274 | log.info(stock) 275 | # 股票收盘价列表(元素个数=交易日天数) 276 | stock_close = prices[stock]['close'].values 277 | 278 | # 股票收益率列表(元素个数=交易日天数-1) 279 | stock_rate = np.diff(stock_close) / stock_close[:-1] 280 | 281 | df_stock = df[df['factor_symbol'].isin([stock])].copy() 282 | log.info(df_stock) 283 | 284 | df_stock = df_stock.iloc[1:, :] 285 | 286 | df_stock['rate'] = stock_rate 287 | # log.info(df_stock) 288 | 289 | # 去极值-标准化 290 | for index, row in list(df_stock.iteritems())[1:-1]: 291 | df_stock[index] = standardize(filter_extreme_3sigma(row)) 292 | df_stock = df_stock.fillna(0) 293 | 294 | log.info(df_stock) 295 | # 第三步,(按有效因子值将个股分为十档,计算每一档的IC、IR、根据IC特征设定方向,根据每组IC均值设定因子权重) 296 | # 随机选取50只股票来计算IC、IR、根据IC特征设定方向,根据每组IC均值设定因子权重 297 | 298 | q = query( 299 | factor.symbol, 300 | effneed 301 | ).filter( 302 | factor.symbol.in_(context.iwencai_securities), 303 | factor.date == trade_days[0] 304 | ) 305 | 306 | df = get_factors(q) 307 | 308 | # 十档 309 | step = 10 310 | 311 | # 周期 312 | nd = 22 313 | 314 | # 用来存放各指标值 315 | need_data = {} 316 | 317 | # 循环每一个因子 318 | for col in df.columns.values[1:]: 319 | # 因子值从大到小排列 320 | a_hign = df.sort_values(col, axis=0, ascending=False) 321 | hign = a_hign.iloc[:, 0].values.tolist() 322 | 323 | # 用来存放十组数据IC均值、ic、pr值列表 324 | avr = [[], [], []] 325 | 326 | # 对所分五组进行迭代 327 | for sct in group(list(hign), step): 328 | # 用来存放一组内每期ic值 329 | ics = [] 330 | 331 | # 每个分期时间点 332 | days = trade_days[0:len(trade_days):nd] 333 | 334 | for i, rate in enumerate(g_rate(sct, days, nd)): 335 | q = query( 336 | col 337 | ).filter( 338 | factor.symbol.in_(sct), 339 | factor.date == days[i] 340 | ) 341 | 342 | df_sct = get_factors(q).fillna(0) 343 | df_sct[col] = standardize(filter_extreme_3sigma(df_sct[col])).fillna(0) 344 | 345 | # ic值 346 | ic = pd.Series(df_sct[col]).corr(pd.Series(list(rate))) 347 | 348 | ics.append(ic) 349 | 350 | 351 | # 一组内三个指标 352 | avr_ic, ir, pr = ics_return(ics) 353 | avr[0].append(avr_ic) 354 | avr[1].append(ir) 355 | avr[2].append(pr) 356 | 357 | need_data[col] = { 358 | 'avr_ic' : avr_6(avr[0]), 359 | 'avr_ir' : avr_6(avr[1]), 360 | 'avr_pr' : avr_6(avr[2]) 361 | } 362 | # 对因子进行排序打分 363 | # 存放最终因子 364 | f_needs = {} 365 | 366 | data = pd.DataFrame(need_data).T.dropna() 367 | 368 | # 按照avr_ir降序 369 | data = data.sort_values('avr_ic', axis=0, ascending=False) 370 | data['avr_ic_score'] = list(reversed(range(len(data)))) 371 | 372 | # 按照avr_ir排序分 373 | data = data.sort_values('avr_ir', axis=0, ascending=False) 374 | data['avr_ir_score'] = list(range(len(data))) 375 | 376 | # 分数相加 377 | data['total_socre'] = data['avr_ic_score'] + data['avr_ir_score'] 378 | data = data.sort_values('total_socre', axis=0, ascending=False) 379 | 380 | # 获取排序后的因子 381 | fneeds = data._stat_axis.values.tolist() 382 | if len(fneeds) >= context.need_max: 383 | fneeds = fneeds[0:context.need_max] 384 | 385 | # 获取因子权重 386 | for i in fneeds: 387 | weight = data.loc[i, 'avr_ic'] 388 | 389 | if data.loc[i, 'avr_pr'] > 0.5: 390 | weight = - weight 391 | 392 | f_needs[i] = { 393 | 'weight' : weight 394 | } 395 | 396 | return f_needs 397 | 398 | # 获得股票函数 399 | def get_stocks(context, bar_dict): 400 | pass 401 | 402 | # 每日运行函数 403 | def handle_bar(context, bar_dict): 404 | last_date = get_last_datetime().strftime('%Y%m%d') 405 | # log.info('上个交易日日期为:' + last_date) 406 | 407 | # 1. 获取上月月末日期 408 | def func_get_end_date_of_last_month(current_date): 409 | # 获取从上一个交易日前一个月中的所有交易日,日期排序从前至后 410 | trade_days = list(get_trade_days(None, current_date, count=30)) 411 | 412 | # 转化为%Y%m%d格式 413 | for i in range(len(trade_days)): 414 | trade_days[i] = trade_days[i].strftime('%Y%m%d') 415 | 416 | # 只要交易日的date和当前交易日的月份不同即为上一个月月末日期,例如[20171013]-[20170928] 417 | # reversed反转序列,便于快速找到月末日期 418 | for date in reversed(trade_days): 419 | if date[5] != current_date[5]: 420 | return date 421 | 422 | log.info('找不到上个月末调仓日!') 423 | return 424 | 425 | # 获取收益率序列 426 | def g_rate(sct, days, nd): 427 | for i, day in enumerate(days): 428 | if i >= 1: 429 | prices = get_price(sct, days[i-1], days[i], '%dd' % nd, ['close'], skip_paused = False, fq = 'pre',is_panel = 1)['close'] 430 | close_rate = (prices.iloc[-1] - prices.iloc[0]) / prices.iloc[0] 431 | yield close_rate 432 | 433 | # 计算一组数据的IC绝对值均值、正向比例、均值/标准差 434 | def ics_return(l): 435 | 436 | # 信息系数绝对值均值 437 | a = [abs(i) for i in l] 438 | avr_ic = sum(a) / len(l) 439 | 440 | # 信息比例 441 | b = np.array(l) 442 | if b.std() != 0: 443 | ir = b.mean() / b.std() 444 | else: 445 | ir = 0 446 | 447 | # 正向比例 448 | c = [i for i in l if i > 0] 449 | pr = len(c) / len(l) 450 | return avr_ic, ir, pr 451 | 452 | # 线性回归方程系数t检验值 453 | def t_value(x, y): 454 | x2 = sum((x - np.mean(x)) ** 2) 455 | xy = sum((x - np.mean(y)) * (y - np.mean(y))) 456 | 457 | #回归参数的最小二乘估计 458 | beta1 = xy / x2 459 | beta0 = np.mean(y) - beta1 * np.mean(x) 460 | #输出线性回归方程 461 | # print('y=',beta0,'+',beta1,'*x') 462 | 463 | #方差 464 | sigma2 = sum((y - beta0 - beta1 * x) ** 2) / len(x) 465 | #标准差 466 | sigma = np.sqrt(sigma2) 467 | #求t值 468 | t = beta1 * np.sqrt(x2) / sigma 469 | return t 470 | 471 | # 求一组数的均值并保留6位小数 472 | def avr_6(l): 473 | b = round(np.array(l).mean(), 6) 474 | return b 475 | 476 | # 3 sigma 去极值 477 | def filter_extreme_3sigma(series, n=3): 478 | # 均值 479 | mean = series.mean() 480 | 481 | # 标准差 482 | std = series.std() 483 | 484 | max_range = mean + n*std 485 | min_range = mean - n*std 486 | 487 | # clip函数用于将超出范围的值填充为min_range,max_range 488 | return np.clip(series, min_range, max_range) 489 | 490 | # z-score标准化 491 | def standardize(series): 492 | std = series.std() 493 | mean = series.mean() 494 | 495 | return (series - mean) / std 496 | 497 | # 分组迭代器 498 | def group(l, s): 499 | length = len(l) 500 | for i in range(s): 501 | que = [] 502 | left = i * (length // s) 503 | if (i+1) * (length // s) < length: 504 | right = (i+1) * (length // s) 505 | else: 506 | right = length 507 | for j in l[left:right]: 508 | que.append(j) 509 | yield que 510 | 511 | # 每日运行函数 512 | def handle_bar(context, bar_dict): 513 | last_date = get_last_datetime().strftime('%Y%m%d') 514 | if last_date != context.last_date and len(list(context.portfolio.stock_account.positions.keys())) > 0: 515 | # 如果不是调仓日且有持仓,择时买入 516 | trade_timing(context, bar_dict) 517 | 518 | # 择时买股函数 519 | def trade_timing(context, bar_dict): 520 | pass 521 | 522 | import pandas as pd 523 | import numpy as np 524 | import datetime, time 525 | from dateutil.relativedelta import relativedelta 526 | import talib 527 | import talib as ta 528 | import math, random 529 | from scipy.stats import ttest_ind 530 | from scipy.stats import levene 531 | from sklearn.linear_model import LinearRegression 532 | import copy 533 | g.record = pd.DataFrame({'symbol': [], 'add_time': [], 'last_buy_price': []}) 534 | 535 | 536 | def init(context): 537 | g.num = 0 538 | log.info('begin') 539 | # get_iwencai('沪深300') 540 | # set_benchmark('000905.SH') 541 | # 设置最大持股数 542 | context.stoplossmultipler = 0.95 543 | # 设置交易股票数量 544 | context.max_stocks = 40 545 | matrix1 = [[0 for i in range(10)]] 546 | g.matrix = matrix1 547 | # 用于计数 548 | g.means1 = g.means2 = g.means3 = g.means4 = g.means5 = g.means6 = g.means7 = 0 549 | 550 | # 最大持股数量(大小盘各一半) 551 | context.hold_max = 40 552 | 553 | # 初始因子池(包括技术因子和财务因子在内的共计107个初始因子) 554 | context.need = ['factor.pe', # 市盈率 555 | 'factor.pb', # 市净率 556 | 'factor.pcf_cash_flow_ttm', # 市现率PCF 557 | 'factor.ps', # 市销率 558 | 'factor.dividend_rate', # 股息率 559 | 'factor.market_cap', # 总市值 560 | 'factor.current_market_cap', # 流通市值 561 | 'factor.capitalization', # 总股本 562 | 'factor.circulating_cap', # 流通股本 563 | 'factor.current_ratio', # 流动比率 564 | 'factor.equity_ratio', # 产权比率负债合计/归属母公司股东的权益 565 | 'factor.quick_ratio', # 速动比率 566 | 'factor.tangible_assets_liabilities', # 有形资产/负债合计 567 | 'factor.tangible_assets_int_liabilities', # 有形资产/带息债务 568 | 'factor.net_debt_equity', # 净债务/股权价值 569 | 'factor.long_term_debt_to_opt_capital_ratio', # 长期债务与营运资金比率 570 | 'factor.tangible_assets_net_liabilities', # 有形资产/净债务 571 | 'factor.overall_income_growth_ratio', # 营业总收入同比增长率 572 | # 'factor.net_cashflow_psg_rowth_ratio',# 每股经营活动产生的现金流量净额同比增长率(报错,原因询问客服ing) 573 | # 'factor.opt_profit_grow_ratio',# 营业利润同比增长率 574 | # 'factor.total_profit_growth_ratio',# 利润总额同比增长率 575 | 'factor.diluted_net_asset_growth_ratio', # 净资产收益率摊薄同比增长率 576 | 'factor.net_cashflow_from_opt_act_growth_ratio', # 经营活动产生的现金流量净额同比增长率 577 | 'factor.net_profit_growth_ratio', # 净利润同比增长率 578 | # 'factor.basic_pey_ear_growth_ratio',# 基本每股收益同比增长率 579 | 'factor.turnover_of_overall_assets', # 总资产周转率 580 | 'factor.turnover_ratio_of_account_payable', # 应付账款周转率 581 | 'factor.turnover_of_current_assets', # 流动资产周转率 582 | 'factor.turnover_of_fixed_assets', # 固定资产周转率 583 | 'factor.cash_cycle', # 现金循环周期 584 | 'factor.inventory_turnover_ratio', # 存货周转率 585 | 'factor.turnover_ratio_of_receivable', # 应收账款周转率 586 | 'factor.weighted_roe', # 净资产收益率roe加权 587 | 'factor.overall_assets_net_income_ratio', # 总资产净利率roa 588 | 'factor.net_profit_margin_on_sales', # 销售净利率 589 | 'factor.before_tax_profit_div_income', # 息税前利润/营业总收入 590 | 'factor.sale_cost_div_income', # 销售费用/营业总收入 591 | 'factor.roa', # 总资产报酬率roa 592 | 'factor.ratio_of_sales_to_cost', # 销售成本率 593 | 'factor.net_profit_div_income', # 净利润/营业总收入 594 | 'factor.opt_profit_div_income', # 营业利润/营业总收入 595 | 'factor.opt_cost_div_income', # 营业总成本/营业总收入 596 | 'factor.administration_cost_div_income', # 管理费用/营业总收入 597 | 'factor.financing_cost_div_income', # 财务费用/营业总收入 598 | 'factor.impairment_loss_div_income', # 资产减值损失/营业总收 599 | # 以下为技术因子 600 | 'factor.vr_rate', # 成交量比率 601 | 'factor.vstd', # 成交量标准差 602 | 'factor.arbr', # 人气意愿指标 603 | 'factor.srdm', # 动向速度比率 604 | 'factor.vroc', # 量变动速率 605 | 'factor.vrsi', # 量相对强弱指标 606 | 'factor.cr', # 能量指标 607 | 'factor.mfi', # 资金流向指标 608 | 'factor.vr', # 量比 609 | 'factor.mass', # 梅丝线 610 | 'factor.obv', # 能量潮 611 | 'factor.pvt', # 量价趋势指标 612 | 'factor.wad', # 威廉聚散指标 613 | 'factor.bbi', # 多空指数 614 | 'factor.mtm', # 动力指标 615 | 'factor.dma', # 平均线差 616 | 'factor.ma', # 简单移动平均 617 | 'factor.macd', # 指数平滑异同平均 618 | 'factor.expma', # 指数平均数 619 | 'factor.priceosc', # 价格振荡指标 620 | 'factor.trix', # 三重指数平滑平均 621 | 'factor.dbcd', # 异同离差乖离率 622 | 'factor.dpo', # 区间震荡线 623 | 'factor.psy', # 心理指标 624 | 'factor.vma', # 量简单移动平均 625 | 'factor.vmacd', # 量指数平滑异同平均 626 | 'factor.vosc', # 成交量震荡 627 | 'factor.tapi', # 加权指数成交值 628 | 'factor.micd', # 异同离差动力指数 629 | 'factor.rccd', # 异同离差变化率指数 630 | 'factor.ddi', # 方向标准差偏离指数 631 | 'factor.bias', # 乖离率 632 | 'factor.cci', # 顺势指标 633 | 'factor.kdj', # 随机指标 634 | 'factor.lwr', # L威廉指标 635 | 'factor.roc', # 变动速率 636 | 'factor.rsi', # 相对强弱指标 637 | 'factor.si', # 摆动指标 638 | 'factor.wr', # 威廉指标 639 | 'factor.wvad', # 威廉变异离散量 640 | 'factor.bbiboll', # BBI多空布林线 641 | 'factor.cdp', # 逆势操作 642 | 'factor.env', # ENV指标 643 | 'factor.mike', # 麦克指标 644 | 'factor.adtm', # 动态买卖气指标 645 | 'factor.mi', # 动量指标 646 | 'factor.rc', # 变化率指数 647 | 'factor.srmi', # SRMIMI修正指标 648 | 'factor.dptb', # 大盘同步指标 649 | 'factor.jdqs', # 阶段强势指标 650 | 'factor.jdrs', # 阶段弱势指标 651 | 'factor.zdzb', # 筑底指标 652 | 'factor.atr', # 真实波幅 653 | 'factor.std', # 标准差 654 | 'factor.vhf', # 纵横指标 655 | 'factor.cvlt'] # 佳庆离散指标 656 | 657 | # 最大因子数目 658 | context.need_max = 10 659 | 660 | # 上一次调仓期 661 | context.last_date = '' 662 | # 设置调仓周期,每月倒数第一个交易日运行 663 | run_monthly(func=reallocate, date_rule=-1) 664 | 665 | # 设置因子测试周期为12个月 666 | context.need_tmonth = 12 667 | 668 | # 选择沪深300 669 | get_iwencai('沪深300', 'hs_300') 670 | 671 | # 选择中证500 672 | get_iwencai('中证500', 'zz_500') 673 | 674 | 675 | def reallocate(context, bar_dict): 676 | # 获取上一个交易日的日期 677 | date = get_last_datetime().strftime('%Y%m%d') 678 | log.info('上个交易日日期为:' + date) 679 | 680 | # 获取上个月末调仓日期 681 | context.last_date = func_get_end_date_of_last_month(date) 682 | 683 | log.info('上月月末调仓日期为:' + context.last_date) 684 | 685 | hs_needs, zz_needs = get_needs(context, bar_dict, date, context.hs_300), get_needs(context, bar_dict, date, 686 | context.zz_500) 687 | # log.info('这是沪深300股票池的因子:') 688 | # log.info(hs_needs) 689 | 690 | # log.info('这是中证500股票池的因子:') 691 | # log.info(zz_needs) 692 | 693 | # 用来存放大小盘股票,0是小盘,1是大盘 694 | tempstocks = [[], []] 695 | dapan_stocks = get_stocks(context, bar_dict, zz_needs, date, context.zz_500) 696 | for stock in dapan_stocks: 697 | tempstocks[0].append(stock) 698 | tempstocks[1].append(1) 699 | xiaopan_stocks = get_stocks(context, bar_dict, hs_needs, date, context.zz_500) 700 | for stock in xiaopan_stocks: 701 | tempstocks[0].append(stock) 702 | tempstocks[1].append(0) 703 | log.info(tempstocks) 704 | # 第一个月 705 | if g.matrix[0][0] == 0: 706 | log.info("第一个月") 707 | log.info(g.matrix) 708 | g.matrix.pop(0) # 将此股票从数组中删除 709 | log.info(g.matrix) 710 | for num,tempstock in enumerate(tempstocks[0]): 711 | matrix2 = [0 for i in range(10)] 712 | matrix2[0] = tempstock 713 | # 1是大盘 0是小盘 714 | matrix2[8] = tempstocks[1][num] 715 | matrix3 = copy.deepcopy(matrix2) 716 | g.matrix.append(matrix3) 717 | log.info(g.matrix) 718 | else: 719 | # 先清除标记 720 | for n, s in enumerate(g.matrix): 721 | g.matrix[n][9] = 0 722 | # 需要比较是否与前一个月重复 723 | for num, tempstock in enumerate(tempstocks[0]): 724 | flag_repeat = 0 725 | for num, stock in enumerate(g.matrix): 726 | if stock[0] == tempstock: 727 | flag_repeat = 1 728 | # 用来标记是否新买入的股票 729 | g.matrix[num][9] = 1 730 | break 731 | if not flag_repeat: 732 | # 不重复 就新加 733 | matrix2 = [0 for i in range(10)] 734 | matrix2[0] = tempstock 735 | # 用来标记是否新买入的股票 736 | matrix2[9] = 1 737 | matrix3 = copy.deepcopy(matrix2) 738 | g.matrix.append(matrix3) 739 | # 将这个月不合适的股票进行清仓 740 | for num, stock in enumerate(g.matrix): 741 | if stock[9] == 0: 742 | sec = g.matrix[num][0] 743 | order_target_value(sec, 0) # 清仓离场 744 | g.record = g.record[g.record['symbol'] != sec] # 将卖出股票的记录清空 745 | g.matrix.pop(num) # 将此股票从数组中删除 746 | 747 | # 用于计数 748 | g.means1 = g.means2 = g.means3 = g.means4 = g.means5 = g.means6 = 0 749 | 750 | 751 | # log.info(tempstocks) 752 | 753 | # 获得因子函数 754 | def get_needs(context, bar_dict, date, stocks): 755 | time_tuple = time.strptime(date, '%Y%m%d') 756 | year, month, day = time_tuple[:3] 757 | # 转化为datetime.date类型 758 | date = datetime.date(year, month, day) 759 | 760 | last_year_date =(date - relativedelta(years = 1)).strftime('%Y%m%d') 761 | # log.info('上个交易日前一年的日期为:' + last_year_date) 762 | 763 | # 上一年内的所有交易日期 764 | trade_days = get_trade_days(last_year_date, date).strftime('%Y-%m-%d') 765 | 766 | # 所有因子 767 | need_all = ','.join(context.need) 768 | 769 | # 1.分组单因子有效性检验,参考资料:https://www.ricequant.com/community/topic/702/%E5%8D%95%E4%B8%80%E5%9B%A0%E5%AD%90%E6%9C%89%E6%95%88%E6%80%A7%E6%A3%80%E6%B5%8B/2 770 | # 每隔25天(间隔越短,收益就会越随机) 771 | period = 25 772 | 773 | # 有效因子列表 774 | eff_need = [] 775 | 776 | # 每组股票数10%只股票 777 | num = math.ceil(0.1 * len(stocks)) 778 | 779 | # 获取开始日期因子值 780 | q = query( 781 | factor.symbol, 782 | need_all 783 | ).filter( 784 | factor.symbol.in_(stocks), 785 | # 20180430的数据消失,原因是:last_year_date可能不是交易日,这样就获取不到数据,因此为trade_days[0] 786 | factor.date == trade_days[0] 787 | ) 788 | 789 | df = get_factors(q) 790 | 791 | for col in df.columns.values[1:]: 792 | # 分别按正序和倒序提取因子值前10%和后10%的股票 793 | a_hign = df.sort_values(col, axis=0, ascending=False) 794 | a_low = df.sort_values(col, axis=0, ascending=True) 795 | 796 | hign = a_hign.iloc[0:num, 0].values.tolist() 797 | low = a_low.iloc[0:num, 0].values.tolist() 798 | hign_price = get_price(hign, last_year_date, date, '1d', ['close'], skip_paused = False, fq = 'pre', is_panel = 1) 799 | low_price = get_price(low, last_year_date, date, '1d', ['close'], skip_paused = False, fq = 'pre', is_panel = 1) 800 | 801 | hign_price = hign_price['close'] 802 | low_price = low_price['close'] 803 | 804 | # 计算平均每组股票每日价格 805 | high_series = hign_price.T.mean() 806 | low_series = low_price.T.mean() 807 | 808 | # 计算收益率 809 | hign_rr = high_series.pct_change(period).dropna() 810 | low_rr = low_series.pct_change(period).dropna() 811 | 812 | # 判断两组数据的方差是否显著不同 813 | le = levene(hign_rr, low_rr) 814 | 815 | # 判断两组数据的均值是否显著不同 816 | tt = ttest_ind(hign_rr, low_rr, equal_var = False) 817 | 818 | # 取置信度为0.05 819 | if le.pvalue < 0.05 and tt.pvalue < 0.05: 820 | eff_need.append('factor.%s' % col) 821 | 822 | # 第一步有效因子个数 823 | log.info(len(eff_need)) 824 | 825 | effneed = ','.join(eff_need) 826 | 827 | # 第二步,随机选取num只股票来计算IC、IR、根据IC特征设定方向,根据每组IC均值设定因子权重(降低运算数量) 828 | 829 | # 随机取两组num数量的股票 830 | # gp = 2 831 | q = query( 832 | factor.symbol, 833 | effneed 834 | ).filter( 835 | factor.symbol.in_(stocks), 836 | factor.date == trade_days[0] 837 | ) 838 | 839 | df = get_factors(q) 840 | 841 | # step档 842 | step = 10 843 | 844 | # 周期 845 | nd = 22 846 | 847 | # 用来存放各指标值 848 | need_data = {} 849 | 850 | # 循环每一个因子 851 | for col in df.columns.values[1:]: 852 | # 因子值从大到小排列 853 | a_hign = df.sort_values(col, axis=0, ascending=False) 854 | hign = a_hign.iloc[:, 0].values.tolist() 855 | 856 | # 用来存放step组数据IC均值、ic、pr值列表 857 | avr = [[], [], []] 858 | 859 | # group()将一个list分为step组 860 | for sct in group(list(hign), step): 861 | 862 | # 用来存放一组内每期ic值 863 | ics = [] 864 | 865 | # 每个分期时间点 866 | days = trade_days[0:len(trade_days):nd] 867 | 868 | for i, rate in enumerate(g_rate(sct, days, nd)): 869 | q = query( 870 | col 871 | ).filter( 872 | factor.symbol.in_(sct), 873 | factor.date == days[i] 874 | ) 875 | 876 | df_sct = get_factors(q).fillna(0) 877 | 878 | # col = df_sct.columns.values[0] 879 | df_sct[col] = standardize(filter_extreme_3sigma(df_sct[col])).fillna(0) 880 | 881 | # ic值 882 | ic = pd.Series(df_sct[col]).corr(pd.Series(list(rate))) 883 | 884 | ics.append(ic) 885 | 886 | 887 | # 一组内三个指标 888 | avr_ic, ir, pr = ics_return(ics) 889 | avr[0].append(avr_ic) 890 | avr[1].append(ir) 891 | avr[2].append(pr) 892 | 893 | need_data[col] = { 894 | 'avr_ic' : avr_6(avr[0]), 895 | 'avr_ir' : avr_6(avr[1]), 896 | 'avr_pr' : avr_6(avr[2]) 897 | } 898 | 899 | # 对因子进行排序打分 900 | # 存放最终因子 901 | f_needs = {} 902 | 903 | data = pd.DataFrame(need_data).T.dropna() 904 | 905 | # 按照avr_ir降序打分 906 | data = data.sort_values('avr_ic', axis=0, ascending=False) 907 | data['avr_ic_score'] = list(reversed(range(len(data)))) 908 | 909 | # 按照avr_ir降打分 910 | data = data.sort_values('avr_ir', axis=0, ascending=False) 911 | data['avr_ir_score'] = list(range(len(data))) 912 | 913 | # 分数相加 914 | data['total_socre'] = data['avr_ic_score'] + data['avr_ir_score'] 915 | data = data.sort_values('total_socre', axis=0, ascending=False) 916 | 917 | # 获取排序后的因子 918 | fneeds = data._stat_axis.values.tolist() 919 | if len(fneeds) >= context.need_max: 920 | fneeds = fneeds[0:context.need_max] 921 | 922 | # 获取因子权重 923 | for i in fneeds: 924 | weight = data.loc[i, 'avr_ic'] 925 | 926 | if data.loc[i, 'avr_pr'] < 0.5: 927 | weight = - weight 928 | 929 | f_needs['factor.%s' % i] = { 930 | 'weight' : weight 931 | } 932 | 933 | return f_needs 934 | 935 | 936 | # 获得股票函数 937 | def get_stocks(context, bar_dict, needs, date, stocks): 938 | # needs为如下字典: 939 | # 例{'factor.dbcd': {'weight': -0.146216}, 'factor.weighted_roe': {'weight': 0.108531}, 'factor.turnover_of_current_assets': {'weight': -0.111679}, 'factor.turnover_of_overall_assets': {'weight': -0.148806}, 'factor.administration_cost_div_income': {'weight': 0.118929}} 940 | need_all = ','.join(list(needs.keys())) 941 | q = query( 942 | factor.symbol, 943 | need_all 944 | ).filter( 945 | factor.symbol.in_(stocks), 946 | factor.date == date 947 | ) 948 | df = get_factors(q).fillna(0) 949 | 950 | # 用来存放股票分值 951 | score = [] 952 | 953 | weight = [needs[i]['weight'] for i in needs] 954 | 955 | for i in range(len(df)): 956 | result = sum(list(map(lambda x, y: x * y, list(df.iloc[i, 1:]), weight))) 957 | score.append(result) 958 | 959 | df['score'] = score 960 | 961 | df = df.sort_values('score', axis=0, ascending=False) 962 | 963 | # 大小盘默认持股数 964 | hold = int(context.hold_max / 2) 965 | 966 | # 存放选取的股票 967 | stocks_c = list(df.iloc[0: hold, 0]) 968 | 969 | return stocks_c 970 | 971 | 972 | # 每日运行函数 973 | def handle_bar(context, bar_dict): 974 | last_date = get_last_datetime().strftime('%Y%m%d') 975 | trade_before_new(context,bar_dict) 976 | trade_new(context,bar_dict) 977 | 978 | 979 | # log.info('上个交易日日期为:' + last_date) 980 | def trade_new(context,bar_dict): 981 | a = get_datetime() 982 | # current_universe = context.iwencai_securities # 获取当前除停牌外的所有可供投资股票(universe) 983 | 984 | 985 | # security_position = context.portfolio.stock_account.positions.keys() # 字典数据,上一K线结束后的有效证券头寸,即持仓数量大于0的证券及其头寸 986 | for index,jj in enumerate(g.matrix): 987 | 988 | stock = g.matrix[index][0] # 获得备选列表中的股票代码 989 | sec = stock 990 | curent = get_candle_stick([stock], end_date=a, fre_step='1m', fields=['open', 'close', 'high', 'low', 'volume'], 991 | skip_paused=False, fq=None, bar_count=5, is_panel=0) 992 | current_price = curent[stock]['open'][-1] 993 | 994 | value = history([stock], ['close', 'open', 'low', 'turnover', 'high'], 20, '1d', False, None) 995 | 996 | value1 = history(stock, ['close', 'open', 'low', 'volume', 'high'], 60, '1d', True, 'pre') 997 | value1 = value1.dropna() 998 | close = value1.close.values 999 | log.info(close,value1,stock) 1000 | low = value1.low.values 1001 | # high = value1['high'].as_matrix() 1002 | high = value1.high.values 1003 | vol = value1['volume'].as_matrix() 1004 | current_price = close[-1] # 获得当前时刻价格 1005 | # short_ma = ta.MA(close, 5) # 5天均线 1006 | # long_ma = ta.MA(close, 10) # 10天均线 1007 | # mid_ma = ta.MA(close, 20) # 20天均线 1008 | # up, mid,down = talib.BBANDS(close, timeperiod=20, nbdevup=2, nbdevdn=2, matype=0) 1009 | # print(up) 1010 | # 数据处理(策略部分):计算真实波幅, 1011 | try: 1012 | atr = talib.ATR(high, low, close, 20)[-1] # 计算真实波幅 1013 | except: 1014 | print(high,low,close) 1015 | 1016 | upperband, middleband, lowerband = talib.BBANDS(np.asarray(value[stock]['close']), timeperiod=10, nbdevup=1.96, 1017 | nbdevdn=1.96, matype=0) 1018 | # log.info(g.matrix[index][0],g.matrix[index][1],g.matrix[index][3],g.matrix[index][4]) 1019 | if g.matrix[index][8] == 1: 1020 | for t in g.record['symbol']: 1021 | if t == sec: 1022 | flag_buy = 1 1023 | if (g.record[g.record['symbol'] == sec]['last_buy_price'].empty): 1024 | continue 1025 | else: 1026 | 1027 | last_price = float(g.record[g.record['symbol'] == sec]['last_buy_price']) # 上一次的买入价格(查找、判断、删除) 1028 | add_price = last_price + 0.5 * atr # 计算是否加仓的判断价格 1029 | add_price_top = last_price + 1.5 * atr 1030 | add_unit = float(g.record[g.record['symbol'] == sec]['add_time']) # 已加仓次数 1031 | if current_price > add_price and add_unit < 4: # 价格上涨超过0.5N并且加仓次数小于4次 1032 | log.info("加仓") 1033 | log.info(sec) 1034 | unit = calcUnit(context.portfolio.portfolio_value, atr) # 计算加仓时应购入的股票数量 1035 | log.info(unit) 1036 | try: 1037 | if g.record[g.record['symbol'] == sec]['add_time'] == 0: 1038 | order(sec, 1.5 * unit) # 买入2unit的股票 1039 | log.info("买入2unit的股票") 1040 | elif g.record[g.record['symbol'] == sec]['add_time'] == 1: 1041 | order(sec, 1.25 * unit) # 买入1.5unit的股票 1042 | log.info("买入1.5unit的股票") 1043 | elif g.record[g.record['symbol'] == sec]['add_time'] == 2: 1044 | order(sec, 1 * unit) # 买入1unit的股票 1045 | log.info("买入1unit的股票") 1046 | elif g.record[g.record['symbol'] == sec]['add_time'] == 3: 1047 | order(sec, 0.7 * unit) # 买入1unit的股票 1048 | log.info("买入0.5unit的股票") 1049 | except: 1050 | order(sec, 1.5 * unit) # 买入2unit的股票 1051 | log.info("买入2unit的股票") 1052 | g.record.loc[g.record['symbol'] == sec, 'add_time'] = g.record[g.record['symbol'] == sec][ 1053 | 'add_time'] + 1 # 加仓次数+1(先找到值再找到位置,然后赋值) 1054 | g.record.loc[g.record['symbol'] == sec, 'last_buy_price'] = current_price # 加仓次数+1 1055 | # 策略部分(离场:止损或止盈):当前价格下穿到BOLL上轨或相对上个买入价下跌 2ATR时(止损)或当股价5日均线下穿10日均线并且量均线死叉,清仓离场 1056 | if current_price < (last_price - 2 * atr) or current_price < low[-10:-1].min(): 1057 | log.info("抛售") 1058 | log.info(sec) 1059 | g.means7 += 1 1060 | order_target_value(sec, 0) # 清仓离场 1061 | 1062 | g.record = g.record[g.record['symbol'] != sec] # 将卖出股票的记录清空 1063 | 1064 | if (g.matrix[index][1] == True and g.matrix[index][3] == True and g.matrix[index][4] == True) or ( 1065 | g.matrix[index][6] == True and flag_buy == 0): 1066 | g.num += 1 1067 | log.info("购入大盘") 1068 | order_target_value(stock, context.portfolio.available_cash / 40) 1069 | if len(g.record) != 0: 1070 | g.record = g.record[ 1071 | g.record['symbol'] != stock] # 清空g.record中sec过期的记录,因为如果之前记录就有000001,现在将这个股票建仓,就要先删除原来为symbol的记录。 1072 | 1073 | g.record = g.record.append(pd.DataFrame( 1074 | {'symbol': [stock], 'add_time': [1], 'last_buy_price': [current_price]})) # 记录股票,加仓次数及买入价格 1075 | continue 1076 | 1077 | if g.matrix[index][8] == 0: 1078 | flag_buy = 0 1079 | for t in g.record['symbol']: 1080 | if t == sec: 1081 | flag_buy = 1 1082 | if (g.record[g.record['symbol'] == sec]['last_buy_price'].empty): 1083 | continue 1084 | else: 1085 | 1086 | last_price = float(g.record[g.record['symbol'] == sec]['last_buy_price']) # 上一次的买入价格(查找、判断、删除) 1087 | add_price = last_price + 0.5 * atr # 计算是否加仓的判断价格 1088 | add_price_top = last_price + 1.5 * atr 1089 | add_unit = float(g.record[g.record['symbol'] == sec]['add_time']) # 已加仓次数 1090 | if current_price > add_price and add_unit < 4 and current_price >= ( 1091 | last_price - 2 * atr): # 价格上涨超过0.5N并且加仓次数小于4次 1092 | log.info("加仓") 1093 | log.info(sec) 1094 | unit = calcUnit(context.portfolio.portfolio_value, atr) # 计算加仓时应购入的股票数量 1095 | log.info(unit) 1096 | try: 1097 | if g.record[g.record['symbol'] == sec]['add_time'] == 0: 1098 | order(sec, 1.5 * unit) # 买入1unit的股票 1099 | elif g.record[g.record['symbol'] == sec]['add_time'] == 1: 1100 | order(sec, 1.25 * unit) # 买入1unit的股票 1101 | elif g.record[g.record['symbol'] == sec]['add_time'] == 2: 1102 | order(sec, 1 * unit) # 买入1unit的股票 1103 | elif g.record[g.record['symbol'] == sec]['add_time'] == 3: 1104 | order(sec, 0.7 * unit) # 买入1unit的股票 1105 | except: 1106 | order(sec, 1.5 * unit) # 买入1unit的股票 1107 | g.record.loc[g.record['symbol'] == sec, 'add_time'] = g.record[g.record['symbol'] == sec][ 1108 | 'add_time'] + 1 # 加仓次数+1(先找到值再找到位置,然后赋值) 1109 | g.record.loc[g.record['symbol'] == sec, 'last_buy_price'] = current_price # 加仓次数+1 1110 | # 策略部分(离场:止损或止盈):当前价格下穿到BOLL上轨或相对上个买入价下跌 2ATR时(止损)或当股价5日均线下穿10日均线并且量均线死叉,清仓离场 1111 | if current_price < (last_price - 2 * atr) or current_price < low[-10:-1].min(): 1112 | log.info("抛售") 1113 | g.means7 += 1 1114 | log.info(sec) 1115 | order_target_value(sec, 0) # 清仓离场 1116 | 1117 | g.record = g.record[g.record['symbol'] != sec] # 将卖出股票的记录清空 1118 | 1119 | if ((g.matrix[index][1] == True and g.matrix[index][3] == True and g.matrix[index][4] == True) or ( 1120 | g.matrix[index][6] == False) and (flag_buy == 0)): 1121 | 1122 | log.info("购入小盘") 1123 | g.num += 1 1124 | log.info(stock) 1125 | # log.info('g.matrix[index][4]==True and g.matrix[index][6]==True and g.matrix[index][2]==True') 1126 | order_target_value(stock, context.portfolio.available_cash / 40) 1127 | if len(g.record) != 0: 1128 | g.record = g.record[ 1129 | g.record['symbol'] != stock] # 清空g.record中sec过期的记录,因为如果之前记录就有000001,现在将这个股票建仓,就要先删除原来为symbol的记录。 1130 | 1131 | g.record = g.record.append(pd.DataFrame( 1132 | {'symbol': [stock], 'add_time': [1], 'last_buy_price': [current_price]})) # 记录股票,加仓次数及买入价格 1133 | continue 1134 | 1135 | 1136 | def trade_before_new(context,bar_dict): 1137 | if g.matrix[0][0] == 0: 1138 | reallocate(context, bar_dict) 1139 | log.info(g.matrix) 1140 | a = get_datetime() 1141 | xiaopan = '000905.SH' 1142 | dapan = '000300.SH' 1143 | Indexprice_xiao = get_price(xiaopan, None, time.strftime("%Y%m%d"), '1d', ['close'], True, None, 100, is_panel=1) 1144 | Indexprice_da = get_price(dapan, None, time.strftime("%Y%m%d"), '1d', ['close'], True, None, 100, is_panel=1) 1145 | strong_weak = np.log(Indexprice_xiao.close) - np.log(Indexprice_da.close) 1146 | strong_weak = strong_weak[20:] 1147 | lowerband_new = pd.rolling_mean(strong_weak, 20) - 2 * pd.rolling_std(strong_weak, 20) 1148 | up_new = pd.rolling_mean(strong_weak, 20) + 2 * pd.rolling_std(strong_weak, 20) 1149 | for index,jj in enumerate(g.matrix): 1150 | 1151 | # 基本数据获取 1152 | stock = g.matrix[index][0] # 获得备选列表中的股票代码 1153 | log.info(stock) 1154 | sec = stock 1155 | curent = get_candle_stick([stock], end_date=a, fre_step='1m', fields=['open', 'close', 'high', 'low', 'volume'], 1156 | skip_paused=False, fq=None, bar_count=5, is_panel=0) 1157 | value = history([stock], ['close', 'open', 'low', 'turnover', 'high'], 20, '1d', False, None) 1158 | 1159 | value1 = history(stock, ['close', 'open', 'low', 'volume', 'high'], 110, '1d', True, 'pre') 1160 | value1 = value1.dropna() 1161 | close = value1.close.values 1162 | low = value1.low.values 1163 | high = value1['high'].as_matrix() 1164 | vol = value1['volume'].as_matrix() 1165 | 1166 | current_price = close[-1] # 获得当前时刻价格 1167 | log.info(close) 1168 | short_ma = ta.MA(close, 5) # 5天均线 1169 | long_ma = ta.MA(close, 10) # 10天均线 1170 | mid_ma = ta.MA(close, 20) # 20天均线 1171 | up, mid, low = talib.BBANDS(close, timeperiod=20, nbdevup=2, nbdevdn=2, matype=0) 1172 | # 数据处理(策略部分):计算真实波幅, 1173 | # atr = talib.ATR(high, low, close, 20)[-1] # 计算真实波幅 1174 | 1175 | # 第一部分 计算#VOL5上穿VOL10 1176 | 1177 | short_vol = ta.MA(vol, 5) # 过去5天均量线 1178 | long_vol = ta.MA(vol, 10) # 过去10天均量线 1179 | 1180 | if short_vol[-1] > short_vol[-2] and long_vol[-1] > long_vol[-2] and short_vol[-2] <= long_vol[-2] and \ 1181 | short_vol[-1] > long_vol[-1]: 1182 | g.matrix[index][1] = True # VOL5上穿VOL10且多头向上排列,可建仓标志 1183 | g.means1 += 1 1184 | else: 1185 | g.matrix[index][1] = False # 不可建仓标志 1186 | 1187 | if (short_vol[-2] >= long_vol[-2] and short_vol[-1] < long_vol[-1]): 1188 | g.matrix[index][2] = True # VOL5下穿VOL10,可清仓标志 1189 | g.means2 += 1 1190 | else: 1191 | g.matrix[index][2] = False # 不可清仓标志 1192 | 1193 | # 第二部分 #MACD中的DIF>DEA & DIF>0 & DEA>0 1194 | 1195 | close_macd = value[stock]['close'].as_matrix() 1196 | 1197 | DIF, DEA, MACD = talib.MACD(close, fastperiod=12, slowperiod=26, 1198 | signalperiod=9) # talib提供的MACD计算函数,计算DIF,DEF以及MACD的取值 1199 | 1200 | if DIF[-1] > DEA[-1] and DIF[-1] > 0 and DEA[-1] > 0: 1201 | g.matrix[index][3] = True 1202 | g.means3 += 1 1203 | 1204 | else: 1205 | g.matrix[index][3] = False 1206 | 1207 | # 第三部分 是否触发MA5与MA10建仓或离场标志 1208 | 1209 | if short_ma[-1] > mid_ma[-2] and short_ma[-1] > short_ma[-2] and mid_ma[-1] > mid_ma[-2] and short_ma[-1] > \ 1210 | mid_ma[-1] and mid_ma[-1] > long_ma[-1] and short_ma[-2] <= mid_ma[-2] and short_ma[-1] > mid_ma[-1]: 1211 | g.matrix[index][4] = True # MA5上穿MA10且三线多头向上排列,可建仓标志 1212 | g.means4 += 1 1213 | else: 1214 | g.matrix[index][4] = False # 不可建仓标志 1215 | if (short_ma[-2] >= mid_ma[-2] and short_ma[-1] < mid_ma[-1]): 1216 | g.matrix[index][5] = True # MA5下穿MA10或者当前价跌破MA10,可清仓标志 1217 | g.means5 += 1 1218 | else: 1219 | g.matrix[index][5] = False # 不可清仓标志 1220 | 1221 | # 第四部分 创新穿越次数 1222 | 1223 | # value_new = history([stock], ['close', 'open', 'low', 'turnover', 'high'], 110, '1d', False, None) 1224 | # upperband_new, middleband_new, lowerband_new = talib.BBANDS(np.asarray(value_new[stock]['close']), 1225 | # timeperiod=10, nbdevup=1.96, nbdevdn=1.96, matype=0) 1226 | flag_shangchuan = 0 1227 | flag_xiachuan = 0 1228 | flag_shangchuancishu = 0 1229 | flag_xiachuancishu = 0 1230 | 1231 | # for t in range(9, 109): 1232 | # if flag_shangchuan == 0: 1233 | # if strong_weak[t] < upperband_new[t]: 1234 | # # 定义标识数 1235 | # flag_shangchuan = 1 1236 | # if flag_shangchuan == 1: 1237 | # if strong_weak[t] > upperband_new[t]: 1238 | # flag_shangchuancishu = flag_shangchuancishu + 1 1239 | # flag_shangchuan = 2 1240 | # if flag_shangchuan == 2: 1241 | # if strong_weak[t] > upperband_new[t]: 1242 | # flag_shangchuan = 2 1243 | # if strong_weak[t] < upperband_new[t]: 1244 | # flag_shangchuan = 0 1245 | if (strong_weak[-2] >= lowerband_new[-2] and strong_weak[-1] <= lowerband_new[-1]): 1246 | g.matrix[index][6] = True 1247 | g.means6 += 1 1248 | else: 1249 | g.matrix[index][6] = False 1250 | flag_xiachuancishu = 0 1251 | # for t in range(1, 80): 1252 | # if flag_xiachuan == 0: 1253 | # if strong_weak[t] > lowerband_new[t]: 1254 | # # 定义标识数 1255 | # flag_xiachuan = 1 1256 | # if flag_xiachuan == 1: 1257 | # if strong_weak[t] <= lowerband_new[t]: 1258 | # flag_xiachuancishu = 1 1259 | # flag_xiachuan = 2 1260 | # 1261 | # if flag_xiachuancishu != 0: 1262 | # # log.info("!=0") 1263 | # # log.info(stock+"触碰布林线下穿线") 1264 | # g.matrix[index][6] = True 1265 | # g.means6+=1 1266 | # else: 1267 | # # log.info("==0") 1268 | # # log.info(stock + "不触碰布林线下穿线") 1269 | # g.matrix[index][6] = False 1270 | 1271 | # trade(context) 1272 | 1273 | 1274 | # #收盘上穿20日BOLL下轨 1275 | 1276 | 1277 | # 日均线穿布林线策略。该函数返回0 1 2 三类数据。0代表 上穿次数大于下 1代表相等 2代表 上穿小于下穿 1278 | 1279 | # #加仓条件 1280 | 1281 | def calcUnit(portfolio_value, ATR): 1282 | value = portfolio_value * 0.01 # trade_percent 1283 | return int((value / ATR) / 100) * 100 1284 | 1285 | 1286 | # 1. 获取上月月末日期 1287 | def func_get_end_date_of_last_month(current_date): 1288 | # 获取从上一个交易日前一个月中的所有交易日,日期排序从前至后 1289 | trade_days = list(get_trade_days(None, current_date, count=30)) 1290 | 1291 | # 转化为%Y%m%d格式 1292 | for i in range(len(trade_days)): 1293 | trade_days[i] = trade_days[i].strftime('%Y%m%d') 1294 | 1295 | # 只要交易日的date和当前交易日的月份不同即为上一个月月末日期,例如[20171013]-[20170928] 1296 | # reversed反转序列,便于快速找到月末日期 1297 | for date in reversed(trade_days): 1298 | if date[5] != current_date[5]: 1299 | return date 1300 | 1301 | log.info('找不到上个月末调仓日!') 1302 | return 1303 | 1304 | 1305 | # 获取收益率序列 1306 | def g_rate(sct, days, nd): 1307 | for i, day in enumerate(days): 1308 | if i >= 1: 1309 | prices = \ 1310 | get_price(sct, days[i - 1], days[i], '%dd' % nd, ['close'], skip_paused=False, fq='pre', is_panel=1)[ 1311 | 'close'] 1312 | close_rate = (prices.iloc[-1] - prices.iloc[0]) / prices.iloc[0] 1313 | yield close_rate 1314 | 1315 | 1316 | # 计算一组数据的IC绝对值均值、正向比例、均值/标准差 1317 | def ics_return(l): 1318 | # 信息系数绝对值均值 1319 | a = [abs(i) for i in l] 1320 | avr_ic = sum(a) / len(l) 1321 | 1322 | # 信息比率 1323 | b = np.array(l) 1324 | if b.std() != 0: 1325 | ir = b.mean() / b.std() 1326 | else: 1327 | ir = 0 1328 | 1329 | # 正向比例 1330 | c = [i for i in l if i > 0] 1331 | pr = len(c) / len(l) 1332 | return avr_ic, ir, pr 1333 | 1334 | 1335 | # 求一组数的均值并保留6位小数 1336 | def avr_6(l): 1337 | b = round(np.array(l).mean(), 6) 1338 | return b 1339 | 1340 | 1341 | # 3 sigma 去极值 1342 | def filter_extreme_3sigma(series, n=3): 1343 | # 均值 1344 | mean = series.mean() 1345 | 1346 | # 标准差 1347 | std = series.std() 1348 | 1349 | max_range = mean + n * std 1350 | min_range = mean - n * std 1351 | 1352 | # clip函数用于将超出范围的值填充为min_range,max_range 1353 | return np.clip(series, min_range, max_range) 1354 | 1355 | 1356 | # z-score标准化 1357 | def standardize(series): 1358 | std = series.std() 1359 | mean = series.mean() 1360 | 1361 | return (series - mean) / std 1362 | 1363 | 1364 | # 分组迭代器 1365 | def group(l, s): 1366 | length = len(l) 1367 | for i in range(s): 1368 | que = [] 1369 | left = i * (length // s) 1370 | if (i+1) * (length // s) < length: 1371 | right = (i+1) * (length // s) 1372 | else: 1373 | right = length 1374 | for j in l[left:right]: 1375 | que.append(j) 1376 | yield que 1377 | 1378 | 1379 | # 每日运行函数 1380 | # def handle_bar(context, bar_dict): 1381 | # last_date = get_last_datetime().strftime('%Y%m%d') 1382 | # # log.info('上个交易日日期为:' + last_date) 1383 | # 1384 | # if last_date != context.last_date and len(list(context.portfolio.stock_account.positions.keys())) > 0: 1385 | # # 如果不是调仓日且有持仓,择时买入 1386 | # trade_timing(context, bar_dict) 1387 | 1388 | 1389 | # 择时买股函数 1390 | # def trade_timing(context, bar_dict): 1391 | # trade_before_new(context) 1392 | # trade_new(context) 1393 | -------------------------------------------------------------------------------- /threefactor.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import datetime 4 | 5 | def init(context): 6 | # 使用智能选股函数设置股票池 7 | get_iwencai('沪深300') 8 | # 上一次调仓期 9 | context.last_date = '' 10 | # 设置最大持股数 11 | context.max_stocks = 10 12 | # 设置调仓周期,每月月末 13 | run_monthly(reallocation,date_rule=-1) 14 | 15 | def reallocation(context, bar_dict): 16 | # 获取上一个交易日的日期 17 | date = get_last_datetime().strftime('%Y%m%d') 18 | 19 | # 获取上个月末调仓日期 20 | context.last_date = func_get_end_date_of_last_month(date) 21 | 22 | log.info('############################## ' + str(date) + ' ###############################') 23 | ''' 24 | # 每个调仓日先清仓持有的股票 25 | for security in list(context.portfolio.stock_account.positions.keys()): 26 | order_target(security, 0) 27 | ''' 28 | # 首先获得当前日期 29 | time = get_datetime() 30 | date = time.strftime('%Y%m%d') 31 | # 获得股票池列表 32 | sample = context.iwencai_securities 33 | # 创建字典用于存储因子值 34 | df = {'security':[], 1:[], 2:[], 3:[], 'score':[]} 35 | 36 | # 因子选择 37 | for security in sample: 38 | q=query( 39 | profit.roic,# 投资回报率 40 | valuation.pb,# 市净率 41 | valuation.ps_ttm,# 市销率 42 | ).filter( 43 | profit.symbol==security 44 | ) 45 | 46 | # 缺失值填充为0 47 | # fillna(0)缺失值填充0 48 | fdmt = get_fundamentals(q, date=date).fillna(0) 49 | 50 | # 判断是否有数据 51 | if (not (fdmt['profit_roic'].empty or 52 | fdmt['valuation_pb'].empty or 53 | fdmt['valuation_ps_ttm'].empty)): 54 | # 计算并填充因子值 55 | df['security'].append(security) 56 | df[1].append(fdmt['profit_roic'][0])# 因子1:投资回报率 57 | df[2].append(fdmt['valuation_pb'][0])# 因子2:市净率 58 | df[3].append(fdmt['valuation_ps_ttm'][0])#因子3:市销率 59 | 60 | for i in range(1, 4): 61 | # 因子极值处理,中位数去极值法 62 | m = np.mean(df[i]) 63 | s = np.std(df[i]) 64 | for j in range(len(df[i])): 65 | if df[i][j] <= m-3*s: 66 | df[i][j] = m-3*s 67 | if df[i][j] >= m+3*s: 68 | df[i][j] = m+3*s 69 | m = np.mean(df[i]) 70 | s = np.std(df[i]) 71 | 72 | # 因子无量纲处理,标准化法 73 | for j in range(len(df[i])): 74 | df[i][j] = (df[i][j]-m)/s 75 | 76 | # 计算综合因子得分 77 | for i in range(len(df['security'])): 78 | # 等权重计算(注意因子方向) 79 | s = (df[1][i]-df[2][i]-df[3][i]) 80 | df['score'].append(s) 81 | 82 | # 按综合因子得分由大到小排序 83 | df = pd.DataFrame(df).sort_values(by ='score', ascending=False) 84 | ''' 85 | # 等权重分配资金 86 | cash = context.portfolio.available_cash/context.max_stocks 87 | 88 | # 买入新调仓股票 89 | for security in df[:context.max_stocks]['security']: 90 | order_target_value(security, cash) 91 | ''' 92 | # 买入挑选的股票 93 | func_do_trade(context, bar_dict, df) 94 | 95 | context.last_date = date 96 | 97 | def handle_bar(context, bar_dict): 98 | last_date = get_last_datetime().strftime('%Y%m%d') 99 | if last_date != context.last_date and len(list(context.portfolio.stock_account.positions.keys())) > 0: 100 | # 如果不是调仓日且有持仓,判断止损条件 101 | func_stop_loss(context, bar_dict) 102 | 103 | def func_get_end_date_of_last_month(current_date): 104 | # 获取从上一个交易日前一个月中的所有交易日,日期排序从前至后 105 | trade_days = list(get_trade_days(None, current_date, count=30)) 106 | 107 | # 转化为%Y%m%d格式 108 | for i in range(len(trade_days)): 109 | trade_days[i] = trade_days[i].strftime('%Y%m%d') 110 | 111 | # 只要交易日的date和当前交易日的月份不同即为上一个月月末日期,例如[20171013]-[20170928] 112 | # reversed反转序列,便于快速找到月末日期 113 | for date in reversed(trade_days): 114 | if date[5] != current_date[5]: 115 | return date 116 | 117 | log.info('Cannot find the end date of last month.') 118 | return 119 | 120 | #### 4.下单函数 ################################################################### 121 | def func_do_trade(context, bar_dict, df): 122 | # 先清空所有持仓 123 | if len(list(context.portfolio.stock_account.positions.keys())) > 0: 124 | for stock in list(context.portfolio.stock_account.positions.keys()): 125 | order_target(stock, 0) 126 | ''' 127 | # 买入前30支股票 128 | for stock in context.selected: 129 | order_target_percent(stock, 1./context.hold_max) 130 | if len(list(context.portfolio.stock_account.positions.keys())) >= context.hold_max: 131 | break 132 | ''' 133 | # 等权重分配资金 134 | cash = context.portfolio.available_cash/context.max_stocks 135 | 136 | # 买入新调仓股票 137 | for security in df[:context.max_stocks]['security']: 138 | order_target_value(security, cash) 139 | return 140 | 141 | 142 | #### 5.止损函数 #################################################################### 143 | def func_stop_loss(context, bar_dict): 144 | #获取账户持仓信息 145 | holdstock = list(context.portfolio.stock_account.positions.keys()) 146 | if len(holdstock) > 0: 147 | num = -0.1 148 | for stock in holdstock: 149 | close = history(stock,['close'],1,'1d').values 150 | if close/context.portfolio.positions[stock].last_price -1 <= num: 151 | order_target(stock,0) 152 | log.info('股票{}已止损'.format(stock)) 153 | 154 | 155 | #获取账户持仓信息 156 | holdstock = list(context.portfolio.stock_account.positions.keys()) 157 | if len(holdstock) > 0: 158 | num = - 0.13 159 | T = history('000001.SH',['quote_rate'],7,'1d').values.sum() 160 | if T < num*100: 161 | log.info('上证指数连续三天下跌{}已清仓'.format(T)) 162 | for stock in holdstock: 163 | order_target(stock,0) -------------------------------------------------------------------------------- /touzi.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import numpy as np 3 | import pandas as pd 4 | import math 5 | 6 | # 中位数去极值 7 | def winsorize(df, afactor_name, n=20): 8 | ''' 9 | df为DataFrame数据 10 | factor为需要去极值的列名称(str类型即可) 11 | n 为判断极值上下边界的常数 12 | ''' 13 | 14 | # 提取该列的数据 15 | ls_raw = np.array(df[afactor_name].values) 16 | # 排序 axis=0,按列排列 17 | ls_raw.sort(axis = 0) 18 | # 获取中位数 19 | # 返回数组元素的中位数 20 | D_M = np.median(ls_raw) 21 | 22 | # 计算离差值 23 | ls_deviation = abs(ls_raw - D_M) 24 | # 排序 25 | ls_deviation.sort(axis = 0) 26 | # 获取离差中位数 27 | D_MAD = np.median(ls_deviation) 28 | 29 | # 将大于中位数n倍离差中位数的值赋为NaN 30 | df.ix[df[afactor_name] >= D_M + n * D_MAD] = None 31 | # 将小于中位数n倍离差中位数的值赋为NaN 32 | df.ix[df[afactor_name] <= D_M - n * D_MAD] = None 33 | return df 34 | 35 | # 因子无量纲处理,标准化处理因子值 36 | def standardize(df, afactor_name): 37 | ''' 38 | df为DataFrame数据 39 | factor为因子名称,string格式 40 | ''' 41 | df[afactor_name] = (df[afactor_name] - df[afactor_name].mean())/df[afactor_name].std() 42 | return df 43 | 44 | #得到IC、IR等有效因子指标 45 | def get_effectiveness(index, afactor): 46 | index = index 47 | end_date = datetime.date(2018, 4, 30) 48 | holding_period = 20 49 | period_number = 12 50 | quantile_number = 5 51 | 52 | # datetime.timedelta对象代表两个时间之间的时间差 53 | # 获取end_date前两轮周期前的交易开始时间start_date 54 | # 两轮周期即一轮因子检验期,一轮回测期? 55 | start_date = end_date - datetime.timedelta(days = 2 * holding_period * period_number) 56 | 57 | # get_trade_days函数的主要功能获取指定时间段内的交易日 58 | # 获取从start_date开始的第一轮周期的所有日期 59 | # 排序为由start_date至end_date 60 | dates = get_trade_days(start_date, end_date)[- holding_period * period_number - 1:].strftime('%Y%m%d') 61 | 62 | trade_dates = [] 63 | for i in range(0, len(dates), holding_period): 64 | 65 | # 获取从start_date开始的第一轮周期的所有日期中的交易日 66 | trade_dates.append(dates[i]) 67 | 68 | # 转换为DataFrame数据 69 | tradeDates_df = pd.DataFrame(np.array(trade_dates), columns=['Trade_Dates']) 70 | 71 | # 获取index对应的成分股股票代码 72 | securities = get_index_stocks(index, trade_dates[0]) 73 | 74 | q = query( 75 | factor.symbol, 76 | afactor 77 | ).filter( 78 | factor.symbol.in_(securities), 79 | factor.date == trade_dates[0] 80 | ) 81 | df = get_factors(q).fillna(0) 82 | 83 | afactor_name = list(afactor) 84 | afactor_name[6] = '_' 85 | afactor_name=''.join(afactor_name) 86 | 87 | afactor_name=afactor_name[7:] 88 | 89 | df=winsorize(df,afactor_name,20).copy() 90 | df=df.dropna() 91 | 92 | # 标准化 93 | df = standardize(df, afactor_name).copy() 94 | 95 | # 获取股票收盘价数据 96 | selected_securities = list(df['factor_symbol'].values) 97 | prices = get_price(selected_securities, trade_dates[0], trade_dates[1], '1d', ['close', 'is_paused'], skip_paused = False, fq = 'pre',is_panel = 0) 98 | 99 | return_list = [] 100 | for stock in selected_securities: 101 | # 将持仓周期开始日期停牌的股票收益赋为NaN,便于之后剔除 102 | if prices[stock]['is_paused'][0]: 103 | return_list.append(None) 104 | else: 105 | quote_rate = (prices[stock]['close'][-1] - prices[stock]['close'][0]) / prices[stock]['close'][0] 106 | return_list.append(quote_rate) 107 | 108 | df['returns'] = pd.Series(return_list, index = df.index) 109 | # 剔除开始日期停牌的股票 110 | df = df.dropna() 111 | 112 | sorted_list = df.sort_values([afactor_name], ascending = True).copy() 113 | 114 | n = round(1./quantile_number * len(sorted_list)) 115 | long_securities = list(sorted_list['factor_symbol'][:n].values) 116 | 117 | current_return = 0 118 | for stock in long_securities: 119 | 120 | current_return += (prices[stock]['close'][-1] - prices[stock]['close'][0])/ prices[stock]['close'][0] 121 | current_return = current_return / n 122 | 123 | # 储存各期的IC值 124 | factor_ic = [] 125 | #用于存储因子各期回报的列表 126 | factor_return = [] 127 | 128 | # 遍历调仓日日期 129 | for i in range(1, len(trade_dates)): 130 | securities = get_index_stocks(index, trade_dates[i-1]) 131 | 132 | q=query( 133 | factor.symbol, 134 | afactor 135 | ).filter( 136 | factor.symbol.in_(securities), 137 | factor.date==trade_dates[i-1] 138 | ) 139 | df = get_factors(q).fillna(0) 140 | 141 | #中位数去极值 142 | df = winsorize(df, afactor_name, 20).copy() 143 | df = df.dropna() 144 | 145 | #标准化 146 | df = standardize(df, afactor_name).copy() 147 | 148 | # 获取股票列表 149 | selected_securities = list(df['factor_symbol'].values) 150 | # 获取股票收盘价数据 151 | prices = get_price(selected_securities, trade_dates[i-1], trade_dates[i], '1d', ['close', 'is_paused'], skip_paused = False, fq = 'pre',is_panel = 0) 152 | 153 | return_list = [] 154 | for stock in selected_securities: 155 | # 将持仓周期开始日期停牌的股票收益赋为NaN,便于之后剔除 156 | if prices[stock]['is_paused'][0]: 157 | return_list.append(None) 158 | else: 159 | # 计算股票持仓周期的收益率 160 | quote_rate = (prices[stock]['close'][-1] - prices[stock]['close'][0]) / prices[stock]['close'][0] 161 | return_list.append(quote_rate) 162 | 163 | # 将股票收益添加到DataFrame 164 | df['returns'] = pd.Series(return_list, index = df.index) 165 | 166 | # 剔除开始日期停牌的股票 167 | df = df.dropna() 168 | 169 | # 计算当期IC值 170 | factor_ic.append(df[afactor_name].corr(df['returns'])) 171 | 172 | ######################### 计算因子回报 ######################### 173 | # 根据因子的值排序 174 | sorted_list = df.sort_values([afactor_name], ascending = True).copy() 175 | n = round(1./quantile_number * len(sorted_list)) 176 | long_securities = list(sorted_list['factor_symbol'][:n].values) 177 | current_return = 0 178 | for stock in long_securities: 179 | current_return += (prices[stock]['close'][-1] - prices[stock]['close'][0]) / prices[stock]['close'][0] 180 | current_return = current_return / n 181 | factor_return.append(current_return) 182 | result_df = pd.DataFrame({'Factor_IC': factor_ic, 183 | 'Factor_Return': factor_return}, index=trade_dates[1:]) 184 | result_df.index.name = 'Dates' 185 | 186 | factor_ic = np.array(factor_ic) 187 | factor_return = np.array(factor_return) 188 | average_ic = factor_ic.mean() 189 | annual_r = (1 + factor_return.mean()) ** (250./holding_period) - 1 190 | annual_sigma = factor_return.std() * math.sqrt(250./holding_period) 191 | info_ratio = annual_r / annual_sigma 192 | win_rate = len(factor_return[factor_return > 0]) / len(factor_return) 193 | factor_df = pd.DataFrame([average_ic, info_ratio, annual_r, win_rate], index=['IC','IR','Annual_Return','Win_Rate'], columns=[afactor_name]) 194 | return factor_df 195 | 196 | # def init(context):是初始化函数 只在整个回测或模拟交易开始运行时执行一次 197 | def init(context): 198 | get_iwencai('沪深300') 199 | context.max_stocks = 10 200 | run_monthly(reallocation, date_rule=1) 201 | 202 | def reallocation(context, bar_dict): 203 | for security in list(context.portfolio.stock_account.positions.keys()): 204 | order_target(security, 0) 205 | time = get_datetime() 206 | date = time.strftime('%Y%m%d') 207 | sample = context.iwencai_securities 208 | df = {'security':[], 1:[],2:[],3:[],4:[],5:[],6:[],7:[],8:[],9:[],10:[], 'score':[]} 209 | 210 | l=[] 211 | for security in sample: 212 | q = query( 213 | factor 214 | ).filter( 215 | factor.symbol == security, 216 | factor.date == date 217 | ) 218 | df1 = get_factors(q).fillna(0) 219 | l=df1.columns.values 220 | break 221 | 222 | l_new=[] 223 | for f in l: 224 | string = list(f) 225 | string[6] = '.' 226 | string=''.join(string) 227 | l_new.append(string) 228 | 229 | eff_list=[] 230 | index='000300.SH' 231 | for f in l_new: 232 | if f!='factor.id' and f!='factor.symbol' and f!='factor.date': 233 | try: 234 | eff=get_effectiveness(index, f) 235 | except Exception as e: 236 | continue 237 | else: 238 | eff_list.append(eff) 239 | 240 | chosen_factor=[] 241 | for security in sample: 242 | q = query( 243 | factor.pe 244 | ).filter( 245 | factor.symbol == security, 246 | factor.date == date 247 | ) 248 | print(q) 249 | df1 = get_factors(q).fillna(0) 250 | print('df1:',df1) 251 | print('df1.columns.values:',df1.columns.values) 252 | 253 | if (not (df1['factor_pe'].empty)): 254 | df['security'].append(security) 255 | df[1].append(df1['factor_pe'][0]) 256 | print('factor_pe的值为:',df1) 257 | 258 | 259 | 260 | for i in range(1, 2): 261 | # 因子极值处理,中位数去极值法 262 | m = np.mean(df[i]) 263 | s = np.std(df[i]) 264 | for j in range(0,len(df[i])): 265 | #print('df[i][j]:',df[i][j][0]) 266 | if df[i][j] <= m-3*s: 267 | df[i][j] = m-3*s 268 | if df[i][j] >= m+3*s: 269 | df[i][j] = m+3*s 270 | m = np.mean(df[i]) 271 | s = np.std(df[i]) 272 | 273 | # 因子无量纲处理,标准化法 274 | for j in range(len(df[i])): 275 | df[i][j] = (df[i][j]-m)/s 276 | 277 | # 计算综合因子得分 278 | for i in range(len(df['security'])): 279 | s = (df[1][i]) 280 | #print('s',s[0]) 281 | df['score'].append(s) 282 | 283 | df = pd.DataFrame(df).sort_values(by ='score', ascending=False) 284 | df_security=list(df['security']) 285 | top20_df_security=[] 286 | for i in range(0,20): 287 | top20_df_security.append(df_security[i]) 288 | print('前20个:',top20_df_security) 289 | cash = context.portfolio.available_cash/context.max_stocks 290 | 291 | import talib 292 | import talib as ta 293 | import pandas as pd 294 | import numpy as np 295 | 296 | record = pd.DataFrame({'symbol':[],'add_time':[],'last_buy_price':[]} ) 297 | 298 | def init(context): 299 | log.info('begin') 300 | get_iwencai('沪深300') 301 | context.stoplossmultipler = 0.95 302 | context.max_stocks = 40 303 | matrix1=[[0 for i in range(10)] for i in range(40)] 304 | g.matrix=matrix1 305 | 306 | def before_trading(context): 307 | 308 | 309 | 310 | if g.matrix[0][0]==0: 311 | for i in range(0,40): 312 | g.matrix[i][0]=context.iwencai_securities[i] 313 | trade_before_new(context) 314 | 315 | def handle_bar(context, bar_dict): 316 | trade_new(context) 317 | 318 | def trade_new(context): 319 | global record 320 | a=get_datetime() 321 | 322 | 323 | current_universe = context.iwencai_securities 324 | security_position = context.portfolio.stock_account.positions.keys() 325 | for index in range(0,40): 326 | 327 | 328 | 329 | stock=g.matrix[index][0] #获得备选列表中的股票代码 330 | sec=stock 331 | curent=get_candle_stick([stock], end_date=a, fre_step='1m', fields=['open', 'close', 'high', 'low', 'volume'], skip_paused=False, fq=None, bar_count=5, is_panel=0) 332 | current_price=curent[stock]['open'][-1] 333 | 334 | value = history([stock], ['close', 'open', 'low', 'turnover', 'high'], 20, '1d', False, None) 335 | 336 | 337 | value1 = history(stock, ['close', 'open', 'low', 'volume','high'], 60, '1d', True, 'pre') 338 | value1 = value1.dropna() 339 | close = value1.close.values 340 | low = value1.low.values 341 | high = value1['high'].as_matrix() 342 | vol = value1['volume'].as_matrix() 343 | current_price = close[-1] 344 | short_ma = ta.MA(close, 5) 345 | long_ma = ta.MA(close, 10) 346 | mid_ma = ta.MA(close, 20) 347 | up, mid, low = talib.BBANDS(close, timeperiod=20, nbdevup=2, nbdevdn=2, matype=0) 348 | 349 | atr = talib.ATR(high, low, close, 20)[-1] 350 | 351 | 352 | upperband, middleband, lowerband = talib.BBANDS(np.asarray(value[stock]['close']), timeperiod=10, nbdevup=1.96, nbdevdn=1.96, matype=0) 353 | 354 | if index<19: 355 | 356 | if ( (g.matrix[index][1]==True and g.matrix[index][3]==True and g.matrix[index][4]==True) or (g.matrix[index][6]==True)) : 357 | 358 | 359 | order_target_value(stock, context.portfolio.available_cash / 20) 360 | if len(record)!=0: 361 | record = record[record['symbol']!=stock] 362 | 363 | record = record.append(pd.DataFrame({'symbol':[stock],'add_time':[1],'last_buy_price':[current_price]})) 364 | continue 365 | 366 | 367 | if index>=20: 368 | 369 | if ( (g.matrix[index][1]==True and g.matrix[index][3]==True and g.matrix[index][4]==True) or (g.matrix[index][6]==False) ) : 370 | 371 | log.info("买小") 372 | log.info(stock) 373 | order_target_value(stock, context.portfolio.available_cash / 10) 374 | if len(record)!=0: 375 | record = record[record['symbol']!=stock] 376 | 377 | record = record.append(pd.DataFrame({'symbol':[stock],'add_time':[1],'last_buy_price':[current_price]})) 378 | continue 379 | 380 | 381 | for t in record['symbol']: 382 | if t ==sec: 383 | 384 | if (record[record['symbol'] == sec]['last_buy_price'].empty): 385 | continue 386 | else: 387 | log.info("进入判断") 388 | last_price = float(record[record['symbol'] == sec]['last_buy_price']) 389 | add_price = last_price + 0.5 * atr 390 | add_unit = float(record[record['symbol'] == sec]['add_time']) 391 | if current_price > add_price*0.5 and add_unit < 4: 392 | log.info("加仓") 393 | unit = calcUnit(context.portfolio.portfolio_value, atr) 394 | 395 | log.info(unit) 396 | order(sec, 2*unit) 397 | record.loc[record['symbol'] == sec, 'add_time'] = record[record['symbol'] == sec][ 398 | 'add_time'] + 1 399 | record.loc[record['symbol'] == sec, 'last_buy_price'] = current_price 400 | 401 | 402 | log.info(current_price,last_price) 403 | if current_price < (last_price - 0.3 * atr) : 404 | log.info("抛售") 405 | log.info(sec) 406 | order_target_value(sec, 0) 407 | 408 | record = record[record['symbol'] != sec] 409 | 410 | def trade_before_new(context): 411 | a=get_datetime() 412 | for index in range(0,40): 413 | 414 | stock=g.matrix[index][0] 415 | sec=stock 416 | curent=get_candle_stick([stock], end_date=a, fre_step='1m', fields=['open', 'close', 'high', 'low', 'volume'], skip_paused=False, fq=None, bar_count=5, is_panel=0) 417 | 418 | value = history([stock], ['close', 'open', 'low', 'turnover', 'high'], 20, '1d', False, None) 419 | 420 | 421 | value1 = history(stock, ['close', 'open', 'low', 'volume','high'], 110, '1d', True, 'pre') 422 | value10=history('000300.SH',['close', 'open', 'low', 'volume','high'], 110, '1d', True, 'pre') 423 | log.info(value10) 424 | value11=history('000905.SH',['close', 'open', 'low', 'volume','high'], 110, '1d', True, 'pre') 425 | value1 = value1.dropna() 426 | close = value1.close.values 427 | low = value1.low.values 428 | high = value1['high'].as_matrix() 429 | vol = value1['volume'].as_matrix() 430 | 431 | current_price = close[-1] 432 | short_ma = ta.MA(close, 5) 433 | long_ma = ta.MA(close, 10) 434 | mid_ma = ta.MA(close, 20) 435 | up, mid, low = talib.BBANDS(close, timeperiod=20, nbdevup=2, nbdevdn=2, matype=0) 436 | 437 | atr = talib.ATR(high, low, close, 20)[-1] 438 | upperband, middleband, lowerband = talib.BBANDS(np.asarray(value[stock]['close']), timeperiod=10, nbdevup=1.96, nbdevdn=1.96, matype=0) 439 | 440 | short_vol=ta.MA(vol,5) 441 | long_vol=ta.MA(vol,10) 442 | 443 | if short_vol[-1]>short_vol[-2] and long_vol[-1]>long_vol[-2] and short_vol[-2]<=long_vol[-2] and short_vol[-1]>long_vol[-1]: 444 | g.matrix[index][1]=True 445 | else: 446 | g.matrix[index][1]=False 447 | if (short_vol[-2]>=long_vol[-2] and short_vol[-1]DEA[-1] and DIF[-1]>0 and DEA[-1]>0: 459 | g.matrix[index][3]=True 460 | 461 | else: 462 | g.matrix[index][3]=False 463 | 464 | 465 | if short_ma[-1]>mid_ma[-2] and short_ma[-1]>short_ma[-2] and mid_ma[-1]>mid_ma[-2] and short_ma[-1]>mid_ma[-1] and mid_ma[-1]>long_ma[-1] and short_ma[-2]<=mid_ma[-2] and short_ma[-1]>mid_ma[-1]: 466 | g.matrix[index][4]=True 467 | else: 468 | g.matrix[index][4]=False 469 | if (short_ma[-2]>=mid_ma[-2] and short_ma[-1]upperband_new[t]: 487 | flag_shangchuancishu=flag_shangchuancishu+1 488 | flag_shangchuan=2 489 | if flag_shangchuan==2: 490 | if long_ma[t]>upperband_new[t]: 491 | flag_shangchuan=2 492 | if long_ma[t]lowerband_new[t]: 497 | flag_xiachuan=1 498 | if flag_xiachuan==1: 499 | if long_ma[t]lowerband_new[t]: 506 | flag_xiachuan=0 507 | 508 | if flag_xiachuancishu!=0: 509 | 510 | g.matrix[index][6]=True 511 | 512 | 513 | if flag_xiachuancishu==0: 514 | 515 | g.matrix[index][6]=False 516 | 517 | def request1(value,value1,value2,stock): 518 | 519 | close = value[stock]['close'].as_matrix() 520 | 521 | close1 = value1[stock]['close'].as_matrix() 522 | 523 | close2 = value2[stock]['close'].as_matrix() 524 | 525 | upperband, middleband, lowerband = talib.BBANDS(np.asarray(value2[stock]['close']), timeperiod=70, nbdevup=2, nbdevdn=2, matype=0) 526 | 527 | 528 | ma10=talib.SMA(close1,timeperiod=70) 529 | 530 | open1 = value[stock]['open'] 531 | close = value[stock]['close'] 532 | low = value[stock]['low'] 533 | 534 | cut_ma=ma10[9:109] 535 | 536 | cut_upperband=upperband[19:119] 537 | 538 | cut_middleband=middleband[19:119] 539 | cut_lowerband=lowerband[19:119] 540 | flag_shangchuan=0 541 | flag_xiachuan=0 542 | flag_shangchuancishu=0 543 | flag_xiachuancishu 544 | 545 | def request2(value,stock,index): 546 | 547 | tur = value[stock]['close'].as_matrix() 548 | short_ma = talib.MA(tur, 5) 549 | mid_ma = talib.MA(tur, 10) 550 | long_ma = talib.MA(tur, 10) 551 | if short_ma[-1]>mid_ma[-2] and short_ma[-1]>short_ma[-2] and mid_ma[-1]>mid_ma[-2] and short_ma[-1]>mid_ma[-1] and mid_ma[-1]>long_ma[-1] and short_ma[-2]<=mid_ma[-2] and short_ma[-1]>mid_ma[-1]: 552 | 553 | g.matrix[index][2]=True 554 | 555 | log.info("多头排列True") 556 | else: 557 | g.matrix[index][2]=False 558 | log.info("多头排列False") 559 | if (short_ma[-2]>=mid_ma[-2] and short_ma[-1]short_vol[-2] and long_vol[-1]>long_vol[-2] and short_vol[-2]<=long_vol[-2] and short_vol[-1]>long_vol[-1]: 575 | g.matrix[index][4]=True 576 | 577 | else: 578 | g.matrix[index][4]=False 579 | 580 | if (short_vol[-2]>=long_vol[-2] and short_vol[-1] DEA[-1] and DIF[-1] > 0 and DEA[-1] > 0: 590 | 591 | g.matrix[index][6]=True 592 | log.info("MACD成功") 593 | else: 594 | g.matrix[index][6]=False 595 | 596 | def add_request(value,stock): 597 | high = value['high'].as_matrix() 598 | low = value['low'].as_matrix() 599 | close = value['close'].as_matrix() 600 | atr = talib.ATR(high, low, close, 20)[-1] 601 | 602 | if (record[record['symbol'] == stock]['last_buy_price'].empty): 603 | return False 604 | else: 605 | last_price = float(record[record['symbol'] == stock]['last_buy_price']) 606 | add_price = last_price + 0.5 * atr 607 | add_unit = float(record[record['symbol'] == stock]['add_time']) 608 | if close[-1] > add_price and add_unit <2: 609 | return True 610 | else: 611 | return False 612 | def calcUnit(portfolio_value,ATR): 613 | 614 | value = portfolio_value * 0.01 615 | return int((value/ATR)/100)*100 616 | -------------------------------------------------------------------------------- /touzi2.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import datetime, time 4 | from dateutil.relativedelta import relativedelta 5 | import talib 6 | import math, random 7 | from scipy.stats import ttest_ind 8 | from scipy.stats import levene 9 | from sklearn.linear_model import LinearRegression 10 | 11 | def init(context): 12 | 13 | # 最大持股数量(大小盘各一半) 14 | context.hold_max = 40 15 | 16 | # 初始因子池(包括技术因子和财务因子在内的共计107个初始因子) 17 | context.need=['factor.pe',# 市盈率 18 | 'factor.pb',# 市净率 19 | 'factor.pcf_cash_flow_ttm',# 市现率PCF 20 | 'factor.ps',# 市销率 21 | 'factor.dividend_rate',# 股息率 22 | 'factor.market_cap',# 总市值 23 | 'factor.current_market_cap',# 流通市值 24 | 'factor.capitalization',# 总股本 25 | 'factor.circulating_cap',# 流通股本 26 | 'factor.current_ratio',# 流动比率 27 | 'factor.equity_ratio',# 产权比率负债合计/归属母公司股东的权益 28 | 'factor.quick_ratio',# 速动比率 29 | 'factor.tangible_assets_liabilities',# 有形资产/负债合计 30 | 'factor.tangible_assets_int_liabilities',# 有形资产/带息债务 31 | 'factor.net_debt_equity',# 净债务/股权价值 32 | 'factor.long_term_debt_to_opt_capital_ratio',# 长期债务与营运资金比率 33 | 'factor.tangible_assets_net_liabilities',# 有形资产/净债务 34 | 'factor.overall_income_growth_ratio',# 营业总收入同比增长率 35 | # 'factor.net_cashflow_psg_rowth_ratio',# 每股经营活动产生的现金流量净额同比增长率(报错,原因询问客服ing) 36 | # 'factor.opt_profit_grow_ratio',# 营业利润同比增长率 37 | # 'factor.total_profit_growth_ratio',# 利润总额同比增长率 38 | 'factor.diluted_net_asset_growth_ratio',# 净资产收益率摊薄同比增长率 39 | 'factor.net_cashflow_from_opt_act_growth_ratio',# 经营活动产生的现金流量净额同比增长率 40 | 'factor.net_profit_growth_ratio',# 净利润同比增长率 41 | # 'factor.basic_pey_ear_growth_ratio',# 基本每股收益同比增长率 42 | 'factor.turnover_of_overall_assets',# 总资产周转率 43 | 'factor.turnover_ratio_of_account_payable',# 应付账款周转率 44 | 'factor.turnover_of_current_assets',# 流动资产周转率 45 | 'factor.turnover_of_fixed_assets',# 固定资产周转率 46 | 'factor.cash_cycle',# 现金循环周期 47 | 'factor.inventory_turnover_ratio',# 存货周转率 48 | 'factor.turnover_ratio_of_receivable',# 应收账款周转率 49 | 'factor.weighted_roe',# 净资产收益率roe加权 50 | 'factor.overall_assets_net_income_ratio',# 总资产净利率roa 51 | 'factor.net_profit_margin_on_sales',# 销售净利率 52 | 'factor.before_tax_profit_div_income',# 息税前利润/营业总收入 53 | 'factor.sale_cost_div_income',# 销售费用/营业总收入 54 | 'factor.roa',# 总资产报酬率roa 55 | 'factor.ratio_of_sales_to_cost',# 销售成本率 56 | 'factor.net_profit_div_income',# 净利润/营业总收入 57 | 'factor.opt_profit_div_income',# 营业利润/营业总收入 58 | 'factor.opt_cost_div_income',# 营业总成本/营业总收入 59 | 'factor.administration_cost_div_income',# 管理费用/营业总收入 60 | 'factor.financing_cost_div_income',# 财务费用/营业总收入 61 | 'factor.impairment_loss_div_income',# 资产减值损失/营业总收 62 | # 以下为技术因子 63 | 'factor.vr_rate',# 成交量比率 64 | 'factor.vstd',# 成交量标准差 65 | 'factor.arbr',# 人气意愿指标 66 | 'factor.srdm',# 动向速度比率 67 | 'factor.vroc',# 量变动速率 68 | 'factor.vrsi',# 量相对强弱指标 69 | 'factor.cr',# 能量指标 70 | 'factor.mfi',# 资金流向指标 71 | 'factor.vr',# 量比 72 | 'factor.mass',# 梅丝线 73 | 'factor.obv',# 能量潮 74 | 'factor.pvt',# 量价趋势指标 75 | 'factor.wad',# 威廉聚散指标 76 | 'factor.bbi',# 多空指数 77 | 'factor.mtm',# 动力指标 78 | 'factor.dma',# 平均线差 79 | 'factor.ma',# 简单移动平均 80 | 'factor.macd',# 指数平滑异同平均 81 | 'factor.expma',# 指数平均数 82 | 'factor.priceosc',# 价格振荡指标 83 | 'factor.trix',# 三重指数平滑平均 84 | 'factor.dbcd',# 异同离差乖离率 85 | 'factor.dpo',# 区间震荡线 86 | 'factor.psy',# 心理指标 87 | 'factor.vma',# 量简单移动平均 88 | 'factor.vmacd',# 量指数平滑异同平均 89 | 'factor.vosc',# 成交量震荡 90 | 'factor.tapi',# 加权指数成交值 91 | 'factor.micd',# 异同离差动力指数 92 | 'factor.rccd',# 异同离差变化率指数 93 | 'factor.ddi',# 方向标准差偏离指数 94 | 'factor.bias',# 乖离率 95 | 'factor.cci',# 顺势指标 96 | 'factor.kdj',# 随机指标 97 | 'factor.lwr',# L威廉指标 98 | 'factor.roc',# 变动速率 99 | 'factor.rsi',# 相对强弱指标 100 | 'factor.si',# 摆动指标 101 | 'factor.wr',# 威廉指标 102 | 'factor.wvad',# 威廉变异离散量 103 | 'factor.bbiboll',# BBI多空布林线 104 | 'factor.cdp',# 逆势操作 105 | 'factor.env',# ENV指标 106 | 'factor.mike',# 麦克指标 107 | 'factor.adtm',# 动态买卖气指标 108 | 'factor.mi',# 动量指标 109 | 'factor.rc',# 变化率指数 110 | 'factor.srmi',# SRMIMI修正指标 111 | 'factor.dptb',# 大盘同步指标 112 | 'factor.jdqs',# 阶段强势指标 113 | 'factor.jdrs',# 阶段弱势指标 114 | 'factor.zdzb',# 筑底指标 115 | 'factor.atr',# 真实波幅 116 | 'factor.std',# 标准差 117 | 'factor.vhf',# 纵横指标 118 | 'factor.cvlt']# 佳庆离散指标 119 | 120 | # 最大因子数目 121 | context.need_max = 10 122 | 123 | # 上一次调仓期 124 | context.last_date = '' 125 | 126 | # 设置调仓周期,每月倒数第一个交易日运行 127 | run_monthly(func = reallocate, date_rule = -1) 128 | 129 | # 设置因子测试周期为12个月 130 | context.need_tmonth = 12 131 | 132 | # 选择沪深300 133 | get_iwencai('沪深300', 'hs_300') 134 | 135 | # 选择中证500 136 | get_iwencai('中证500', 'zz_500') 137 | 138 | def reallocate(context, bar_dict): 139 | # 获取上一个交易日的日期 140 | date = get_last_datetime().strftime('%Y%m%d') 141 | log.info('上个交易日日期为:' + date) 142 | 143 | # 获取上个月末调仓日期 144 | context.last_date = func_get_end_date_of_last_month(date) 145 | 146 | log.info('上月月末调仓日期为:' + context.last_date) 147 | 148 | hs_needs, zz_needs = get_needs(context, bar_dict, date, context.hs_300), get_needs(context, bar_dict, date, context.zz_500) 149 | # log.info('这是沪深300股票池的因子:') 150 | # log.info(hs_needs) 151 | 152 | # log.info('这是中证500股票池的因子:') 153 | # log.info(zz_needs) 154 | # 用来存放大小盘股票,0是小盘,1是大盘 155 | tempstocks = [[], []] 156 | tempstocks[0].append(get_stocks(context, bar_dict, zz_needs, date, context.zz_500)) 157 | tempstocks[1].append(get_stocks(context, bar_dict, hs_needs, date, context.hs_300)) 158 | ''' 159 | tempstocks = [] 160 | tempstocks.append(get_stocks(context, bar_dict, zz_needs, date, context.zz_500)) 161 | tempstocks.append(get_stocks(context, bar_dict, hs_needs, date, context.hs_300)) 162 | log.info(tempstocks) 163 | 164 | func_do_trade(context, bar_dict, tempstocks) 165 | ''' 166 | 167 | # 获得因子函数 168 | def get_needs(context, bar_dict, date, stocks): 169 | time_tuple = time.strptime(date, '%Y%m%d') 170 | year, month, day = time_tuple[:3] 171 | # 转化为datetime.date类型 172 | date = datetime.date(year, month, day) 173 | 174 | last_year_date =(date - relativedelta(years = 1)).strftime('%Y%m%d') 175 | log.info('上个交易日前一年的日期为:' + last_year_date) 176 | 177 | # 上一年内的所有交易日期 178 | trade_days = get_trade_days(last_year_date, date).strftime('%Y-%m-%d') 179 | 180 | # 所有因子 181 | need_all = ','.join(context.need) 182 | 183 | # 1.分组单因子有效性检验,参考资料:https://www.ricequant.com/community/topic/702/%E5%8D%95%E4%B8%80%E5%9B%A0%E5%AD%90%E6%9C%89%E6%95%88%E6%80%A7%E6%A3%80%E6%B5%8B/2 184 | # 每隔25天(间隔越短,收益就会越随机) 185 | period = 25 186 | 187 | # 有效因子列表 188 | eff_need = [] 189 | 190 | # 每组股票数10%只股票 191 | num = math.ceil(0.1 * len(stocks)) 192 | 193 | # 获取开始日期因子值 194 | q = query( 195 | factor.symbol, 196 | need_all 197 | ).filter( 198 | factor.symbol.in_(stocks), 199 | # 20180430的数据消失,原因是:last_year_date可能不是交易日,这样就获取不到数据,因此为trade_days[0] 200 | factor.date == trade_days[0] 201 | ) 202 | 203 | df = get_factors(q) 204 | 205 | for col in df.columns.values[1:]: 206 | # 分别按正序和倒序提取因子值前10%和后10%的股票 207 | a_hign = df.sort_values(col, axis=0, ascending=False) 208 | a_low = df.sort_values(col, axis=0, ascending=True) 209 | 210 | hign = a_hign.iloc[0:num, 0].values.tolist() 211 | low = a_low.iloc[0:num, 0].values.tolist() 212 | hign_price = get_price(hign, last_year_date, date, '1d', ['close'], skip_paused = False, fq = 'pre', is_panel = 1) 213 | low_price = get_price(low, last_year_date, date, '1d', ['close'], skip_paused = False, fq = 'pre', is_panel = 1) 214 | 215 | hign_price = hign_price['close'] 216 | low_price = low_price['close'] 217 | 218 | # 计算平均每组股票每日价格 219 | high_series = hign_price.T.mean() 220 | low_series = low_price.T.mean() 221 | 222 | # 计算收益率 223 | hign_rr = high_series.pct_change(period).dropna() 224 | low_rr = low_series.pct_change(period).dropna() 225 | 226 | # 判断两组数据的方差是否显著不同 227 | le = levene(hign_rr, low_rr) 228 | 229 | # 判断两组数据的均值是否显著不同 230 | tt = ttest_ind(hign_rr, low_rr, equal_var = False) 231 | 232 | # 取置信度为0.05 233 | if le.pvalue < 0.05 and tt.pvalue < 0.05: 234 | eff_need.append('factor.%s' % col) 235 | 236 | # 第一步有效因子个数 237 | log.info(len(eff_need)) 238 | 239 | effneed = ','.join(eff_need) 240 | 241 | # 第二步,股票池股票分十档计算IC、IR、根据IC特征设定方向,根据每组IC均值设定因子权重 242 | 243 | # gp = 2 244 | q = query( 245 | factor.symbol, 246 | effneed 247 | ).filter( 248 | factor.symbol.in_(stocks), 249 | factor.date == trade_days[0] 250 | ) 251 | 252 | df = get_factors(q) 253 | 254 | # step档 255 | step = 10 256 | 257 | # 周期 258 | nd = 22 259 | 260 | # 用来存放各指标值 261 | need_data = {} 262 | 263 | # 循环每一个因子 264 | for col in df.columns.values[1:]: 265 | # 因子值从大到小排列 266 | a_hign = df.sort_values(col, axis=0, ascending=False) 267 | hign = a_hign.iloc[:, 0].values.tolist() 268 | 269 | # 用来存放step组数据IC均值、ic、pr值列表 270 | avr = [[], [], []] 271 | 272 | # 对所取两组进行迭代(gp_random:stocks为股票池, gp为取gp组,num为每组num只股票) 273 | # group()将一个list分为step组 274 | for sct in group(list(hign), step): 275 | 276 | # 用来存放一组内每期ic值 277 | ics = [] 278 | 279 | # 每个分期时间点 280 | days = trade_days[0:len(trade_days):nd] 281 | 282 | for i, rate in enumerate(g_rate(sct, days, nd)): 283 | q = query( 284 | col 285 | ).filter( 286 | factor.symbol.in_(sct), 287 | factor.date == days[i] 288 | ) 289 | 290 | df_sct = get_factors(q).fillna(0) 291 | 292 | # col = df_sct.columns.values[0] 293 | df_sct[col] = standardize(filter_extreme_3sigma(df_sct[col])).fillna(0) 294 | 295 | # ic值 296 | ic = pd.Series(df_sct[col]).corr(pd.Series(list(rate))) 297 | 298 | ics.append(ic) 299 | 300 | 301 | # 一组内三个指标 302 | avr_ic, ir, pr = ics_return(ics) 303 | avr[0].append(avr_ic) 304 | avr[1].append(ir) 305 | avr[2].append(pr) 306 | 307 | need_data[col] = { 308 | 'avr_ic' : avr_6(avr[0]), 309 | 'avr_ir' : avr_6(avr[1]), 310 | 'avr_pr' : avr_6(avr[2]) 311 | } 312 | 313 | # 对因子进行排序打分 314 | # 存放最终因子 315 | f_needs = {} 316 | 317 | data = pd.DataFrame(need_data).T.dropna() 318 | 319 | # 按照avr_ir降序打分 320 | data = data.sort_values('avr_ic', axis=0, ascending=False) 321 | data['avr_ic_score'] = list(reversed(range(len(data)))) 322 | 323 | # 按照avr_ir降打分 324 | data = data.sort_values('avr_ir', axis=0, ascending=False) 325 | data['avr_ir_score'] = list(range(len(data))) 326 | 327 | # 分数相加 328 | data['total_socre'] = data['avr_ic_score'] + data['avr_ir_score'] 329 | data = data.sort_values('total_socre', axis=0, ascending=False) 330 | 331 | # 获取排序后的因子 332 | fneeds = data._stat_axis.values.tolist() 333 | if len(fneeds) >= context.need_max: 334 | fneeds = fneeds[0:context.need_max] 335 | 336 | # 获取因子权重 337 | for i in fneeds: 338 | weight = data.loc[i, 'avr_ic'] 339 | 340 | if data.loc[i, 'avr_pr'] > 0.5: 341 | weight = - weight 342 | 343 | f_needs['factor.%s' % i] = { 344 | 'weight' : weight 345 | } 346 | 347 | return f_needs 348 | 349 | # 获得股票函数 350 | def get_stocks(context, bar_dict, needs, date, stocks): 351 | # needs为如下字典: 352 | # 例{'factor.dbcd': {'weight': -0.146216}, 'factor.weighted_roe': {'weight': 0.108531}, 'factor.turnover_of_current_assets': {'weight': -0.111679}, 'factor.turnover_of_overall_assets': {'weight': -0.148806}, 'factor.administration_cost_div_income': {'weight': 0.118929}} 353 | need_all = ','.join(list(needs.keys())) 354 | q = query( 355 | factor.symbol, 356 | need_all 357 | ).filter( 358 | factor.symbol.in_(stocks), 359 | factor.date == date 360 | ) 361 | df = get_factors(q).fillna(0) 362 | 363 | # 用来存放股票分值 364 | score = [] 365 | 366 | weight = [needs[i]['weight'] for i in needs] 367 | 368 | for i in range(len(df)): 369 | result = sum(list(map(lambda x,y:x*y,list(df.iloc[i, 1:]), weight))) 370 | score.append(result) 371 | 372 | df['score'] = score 373 | 374 | df = df.sort_values('score', axis=0, ascending = False) 375 | 376 | # 大小盘默认持股数 377 | hold = int(context.hold_max / 2) 378 | 379 | # 存放选取的股票 380 | stocks_c = list(df.iloc[0 : hold, 0]) 381 | 382 | return stocks_c 383 | 384 | # 1. 获取上月月末日期 385 | def func_get_end_date_of_last_month(current_date): 386 | # 获取从上一个交易日前一个月中的所有交易日,日期排序从前至后 387 | trade_days = list(get_trade_days(None, current_date, count=30)) 388 | 389 | # 转化为%Y%m%d格式 390 | for i in range(len(trade_days)): 391 | trade_days[i] = trade_days[i].strftime('%Y%m%d') 392 | 393 | # 只要交易日的date和当前交易日的月份不同即为上一个月月末日期,例如[20171013]-[20170928] 394 | # reversed反转序列,便于快速找到月末日期 395 | for date in reversed(trade_days): 396 | if date[5] != current_date[5]: 397 | return date 398 | 399 | log.info('找不到上个月末调仓日!') 400 | return 401 | 402 | # 获取收益率序列 403 | def g_rate(sct, days, nd): 404 | for i, day in enumerate(days): 405 | if i >= 1: 406 | prices = get_price(sct, days[i-1], days[i], '%dd' % nd, ['close'], skip_paused = False, fq = 'pre',is_panel = 1)['close'] 407 | close_rate = (prices.iloc[-1] - prices.iloc[0]) / prices.iloc[0] 408 | yield close_rate 409 | 410 | # 计算一组数据的IC绝对值均值、正向比例、均值/标准差 411 | def ics_return(l): 412 | 413 | # 信息系数绝对值均值 414 | a = [abs(i) for i in l] 415 | avr_ic = sum(a) / len(l) 416 | 417 | # 信息比率 418 | b = np.array(l) 419 | if b.std() != 0: 420 | ir = b.mean() / b.std() 421 | else: 422 | ir = 0 423 | 424 | # 正向比例 425 | c = [i for i in l if i > 0] 426 | pr = len(c) / len(l) 427 | return avr_ic, ir, pr 428 | 429 | # 求一组数的均值并保留6位小数 430 | def avr_6(l): 431 | b = round(np.array(l).mean(), 6) 432 | return b 433 | 434 | # 3 sigma 去极值 435 | def filter_extreme_3sigma(series, n=3): 436 | # 均值 437 | mean = series.mean() 438 | 439 | # 标准差 440 | std = series.std() 441 | 442 | max_range = mean + n*std 443 | min_range = mean - n*std 444 | 445 | # clip函数用于将超出范围的值填充为min_range,max_range 446 | return np.clip(series, min_range, max_range) 447 | 448 | # z-score标准化 449 | def standardize(series): 450 | std = series.std() 451 | mean = series.mean() 452 | 453 | return (series - mean) / std 454 | 455 | # 分组迭代器 456 | def group(l, s): 457 | length = len(l) 458 | for i in range(s): 459 | que = [] 460 | left = i * (length // s) 461 | if (i+1) * (length // s) < length: 462 | right = (i+1) * (length // s) 463 | else: 464 | right = length 465 | for j in l[left:right]: 466 | que.append(j) 467 | yield que 468 | 469 | # 每日运行函数 470 | def handle_bar(context, bar_dict): 471 | last_date = get_last_datetime().strftime('%Y%m%d') 472 | # log.info('上个交易日日期为:' + last_date) 473 | 474 | if last_date != context.last_date and len(list(context.portfolio.stock_account.positions.keys())) > 0: 475 | # 如果不是调仓日且有持仓,择时买入 476 | func_stop_loss(context, bar_dict) 477 | 478 | 479 | def func_stop_loss(context, bar_dict): 480 | #获取账户持仓信息 481 | holdstock = list(context.portfolio.stock_account.positions.keys()) 482 | if len(holdstock) > 0: 483 | num = -0.1 484 | for stock in holdstock: 485 | close = history(stock,['close'],1,'1d').values 486 | if close/context.portfolio.positions[stock].last_price -1 <= num: 487 | order_target(stock,0) 488 | log.info('股票{}已止损'.format(stock)) 489 | 490 | #获取账户持仓信息 491 | holdstock = list(context.portfolio.stock_account.positions.keys()) 492 | if len(holdstock) > 0: 493 | num = - 0.13 494 | T = history('000001.SH',['quote_rate'],7,'1d').values.sum() 495 | if T < num*100: 496 | log.info('上证指数连续三天下跌{}已清仓'.format(T)) 497 | for stock in holdstock: 498 | order_target(stock,0) 499 | 500 | # 4.下单函数 501 | def func_do_trade(context, bar_dict, tempstocks): 502 | # 先清空所有持仓 503 | if len(list(context.portfolio.stock_account.positions.keys())) > 0: 504 | for stock in list(context.portfolio.stock_account.positions.keys()): 505 | order_target(stock, 0) 506 | # 等权重分配资金 507 | cash = context.portfolio.available_cash/context.max_stocks 508 | 509 | # 买入新调仓股票 510 | for security in tempstocks: 511 | order_target_value(security, cash) 512 | return -------------------------------------------------------------------------------- /touzi3.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import datetime, time 4 | from dateutil.relativedelta import relativedelta 5 | import talib 6 | import talib as ta 7 | import math, random 8 | from scipy.stats import ttest_ind 9 | from scipy.stats import levene 10 | from sklearn.linear_model import LinearRegression 11 | import copy 12 | 13 | g.record = pd.DataFrame({'symbol': [], 'add_time': [], 'last_buy_price': []}) 14 | 15 | 16 | def init(context): 17 | g.num = 0 18 | log.info('begin') 19 | # get_iwencai('沪深300') 20 | # set_benchmark('000905.SH') 21 | # 设置最大持股数 22 | context.stoplossmultipler = 0.95 23 | # 设置交易股票数量 24 | context.max_stocks = 40 25 | matrix1 = [[0 for i in range(10)]] 26 | g.matrix = matrix1 27 | # 用于计数 28 | g.means1 = g.means2 = g.means3 = g.means4 = g.means5 = g.means6 = g.means7 = 0 29 | 30 | # 最大持股数量(大小盘各一半) 31 | context.hold_max = 40 32 | 33 | # 初始因子池(包括技术因子和财务因子在内的共计107个初始因子) 34 | context.need = ['factor.pe', # 市盈率 35 | 'factor.pb', # 市净率 36 | 'factor.pcf_cash_flow_ttm', # 市现率PCF 37 | 'factor.ps', # 市销率 38 | 'factor.dividend_rate', # 股息率 39 | 'factor.market_cap', # 总市值 40 | 'factor.current_market_cap', # 流通市值 41 | 'factor.capitalization', # 总股本 42 | 'factor.circulating_cap', # 流通股本 43 | 'factor.current_ratio', # 流动比率 44 | 'factor.equity_ratio', # 产权比率负债合计/归属母公司股东的权益 45 | 'factor.quick_ratio', # 速动比率 46 | 'factor.tangible_assets_liabilities', # 有形资产/负债合计 47 | 'factor.tangible_assets_int_liabilities', # 有形资产/带息债务 48 | 'factor.net_debt_equity', # 净债务/股权价值 49 | 'factor.long_term_debt_to_opt_capital_ratio', # 长期债务与营运资金比率 50 | 'factor.tangible_assets_net_liabilities', # 有形资产/净债务 51 | 'factor.overall_income_growth_ratio', # 营业总收入同比增长率 52 | # 'factor.net_cashflow_psg_rowth_ratio',# 每股经营活动产生的现金流量净额同比增长率(报错,原因询问客服ing) 53 | # 'factor.opt_profit_grow_ratio',# 营业利润同比增长率 54 | # 'factor.total_profit_growth_ratio',# 利润总额同比增长率 55 | 'factor.diluted_net_asset_growth_ratio', # 净资产收益率摊薄同比增长率 56 | 'factor.net_cashflow_from_opt_act_growth_ratio', # 经营活动产生的现金流量净额同比增长率 57 | 'factor.net_profit_growth_ratio', # 净利润同比增长率 58 | # 'factor.basic_pey_ear_growth_ratio',# 基本每股收益同比增长率 59 | 'factor.turnover_of_overall_assets', # 总资产周转率 60 | 'factor.turnover_ratio_of_account_payable', # 应付账款周转率 61 | 'factor.turnover_of_current_assets', # 流动资产周转率 62 | 'factor.turnover_of_fixed_assets', # 固定资产周转率 63 | 'factor.cash_cycle', # 现金循环周期 64 | 'factor.inventory_turnover_ratio', # 存货周转率 65 | 'factor.turnover_ratio_of_receivable', # 应收账款周转率 66 | 'factor.weighted_roe', # 净资产收益率roe加权 67 | 'factor.overall_assets_net_income_ratio', # 总资产净利率roa 68 | 'factor.net_profit_margin_on_sales', # 销售净利率 69 | 'factor.before_tax_profit_div_income', # 息税前利润/营业总收入 70 | 'factor.sale_cost_div_income', # 销售费用/营业总收入 71 | 'factor.roa', # 总资产报酬率roa 72 | 'factor.ratio_of_sales_to_cost', # 销售成本率 73 | 'factor.net_profit_div_income', # 净利润/营业总收入 74 | 'factor.opt_profit_div_income', # 营业利润/营业总收入 75 | 'factor.opt_cost_div_income', # 营业总成本/营业总收入 76 | 'factor.administration_cost_div_income', # 管理费用/营业总收入 77 | 'factor.financing_cost_div_income', # 财务费用/营业总收入 78 | 'factor.impairment_loss_div_income', # 资产减值损失/营业总收 79 | # 以下为技术因子 80 | 'factor.vr_rate', # 成交量比率 81 | 'factor.vstd', # 成交量标准差 82 | 'factor.arbr', # 人气意愿指标 83 | 'factor.srdm', # 动向速度比率 84 | 'factor.vroc', # 量变动速率 85 | 'factor.vrsi', # 量相对强弱指标 86 | 'factor.cr', # 能量指标 87 | 'factor.mfi', # 资金流向指标 88 | 'factor.vr', # 量比 89 | 'factor.mass', # 梅丝线 90 | 'factor.obv', # 能量潮 91 | 'factor.pvt', # 量价趋势指标 92 | 'factor.wad', # 威廉聚散指标 93 | 'factor.bbi', # 多空指数 94 | 'factor.mtm', # 动力指标 95 | 'factor.dma', # 平均线差 96 | 'factor.ma', # 简单移动平均 97 | 'factor.macd', # 指数平滑异同平均 98 | 'factor.expma', # 指数平均数 99 | 'factor.priceosc', # 价格振荡指标 100 | 'factor.trix', # 三重指数平滑平均 101 | 'factor.dbcd', # 异同离差乖离率 102 | 'factor.dpo', # 区间震荡线 103 | 'factor.psy', # 心理指标 104 | 'factor.vma', # 量简单移动平均 105 | 'factor.vmacd', # 量指数平滑异同平均 106 | 'factor.vosc', # 成交量震荡 107 | 'factor.tapi', # 加权指数成交值 108 | 'factor.micd', # 异同离差动力指数 109 | 'factor.rccd', # 异同离差变化率指数 110 | 'factor.ddi', # 方向标准差偏离指数 111 | 'factor.bias', # 乖离率 112 | 'factor.cci', # 顺势指标 113 | 'factor.kdj', # 随机指标 114 | 'factor.lwr', # L威廉指标 115 | 'factor.roc', # 变动速率 116 | 'factor.rsi', # 相对强弱指标 117 | 'factor.si', # 摆动指标 118 | 'factor.wr', # 威廉指标 119 | 'factor.wvad', # 威廉变异离散量 120 | 'factor.bbiboll', # BBI多空布林线 121 | 'factor.cdp', # 逆势操作 122 | 'factor.env', # ENV指标 123 | 'factor.mike', # 麦克指标 124 | 'factor.adtm', # 动态买卖气指标 125 | 'factor.mi', # 动量指标 126 | 'factor.rc', # 变化率指数 127 | 'factor.srmi', # SRMIMI修正指标 128 | 'factor.dptb', # 大盘同步指标 129 | 'factor.jdqs', # 阶段强势指标 130 | 'factor.jdrs', # 阶段弱势指标 131 | 'factor.zdzb', # 筑底指标 132 | 'factor.atr', # 真实波幅 133 | 'factor.std', # 标准差 134 | 'factor.vhf', # 纵横指标 135 | 'factor.cvlt'] # 佳庆离散指标 136 | 137 | # 最大因子数目 138 | context.need_max = 10 139 | 140 | # 上一次调仓期 141 | context.last_date = '' 142 | # 设置调仓周期,每月倒数第一个交易日运行 143 | run_monthly(func=reallocate, date_rule=1) 144 | 145 | # 设置因子测试周期为12个月 146 | context.need_tmonth = 12 147 | 148 | # 选择沪深300 149 | get_iwencai('沪深300', 'hs_300') 150 | 151 | # 选择中证500 152 | get_iwencai('中证500', 'zz_500') 153 | 154 | 155 | def reallocate(context, bar_dict): 156 | log.info( 157 | "VOL5上穿VOL10次数:{},OL5下穿VOL10:{},DIF{},MA5上穿MA10:{},MA5下穿MA10:{},布林线:{}".format(g.means1, g.means2, g.means3, 158 | g.means4, g.means5, g.means6)) 159 | # 获取上一个交易日的日期 160 | date = get_last_datetime().strftime('%Y%m%d') 161 | log.info('上个交易日日期为:' + date) 162 | 163 | # 获取上个月末调仓日期 164 | context.last_date = func_get_end_date_of_last_month(date) 165 | 166 | log.info('上月月末调仓日期为:' + context.last_date) 167 | 168 | hs_needs, zz_needs = get_needs(context, bar_dict, date, context.hs_300), get_needs(context, bar_dict, date, 169 | context.zz_500) 170 | log.info('这是沪深300股票池的因子:') 171 | log.info(hs_needs) 172 | 173 | log.info('这是中证500股票池的因子:') 174 | log.info(zz_needs) 175 | 176 | # 用来存放大小盘股票,0是小盘,1是大盘 177 | tempstocks = [[], []] 178 | dapan_stocks = get_stocks(context, bar_dict, zz_needs, date, context.zz_500) 179 | for stock in dapan_stocks: 180 | tempstocks[0].append(stock) 181 | tempstocks[1].append(1) 182 | xiaopan_stocks = get_stocks(context, bar_dict, hs_needs, date, context.zz_500) 183 | for stock in xiaopan_stocks: 184 | tempstocks[0].append(stock) 185 | tempstocks[1].append(0) 186 | # log.info(tempstocks) 187 | log.info('这是沪深300股票池:') 188 | log.info(dapan_stocks) 189 | 190 | log.info('这是中证500股票池:') 191 | log.info(xiaopan_stocks) 192 | # unit = calcUnit(context.portfolio.portfolio_value, atr) # 计算加仓时应购入的股票数量 193 | # for s in tempstocks[0]: 194 | # order(s, 1 * 2000) # 七个0时约2000 195 | log.info("股票池:") 196 | log.info(tempstocks[0]) 197 | # 第一个月 198 | if g.matrix[0][0] == 0: 199 | # log.info("第一个月") 200 | # log.info(g.matrix) 201 | g.matrix.pop(0) # 将此股票从数组中删除 202 | # log.info(g.matrix) 203 | for num, tempstock in enumerate(tempstocks[0]): 204 | matrix2 = [0 for i in range(10)] 205 | matrix2[0] = tempstock 206 | # 1是大盘 0是小盘 207 | matrix2[8] = tempstocks[1][num] 208 | matrix2[9] = 1 209 | matrix3 = copy.deepcopy(matrix2) 210 | g.matrix.append(matrix3) 211 | log.info(g.matrix) 212 | else: 213 | # 先清除标记 214 | log.info("开始新一轮的选股") 215 | log.info(g.matrix) 216 | for n, s in enumerate(g.matrix): 217 | for i in range(0, 9): 218 | if i == 0 or i == 8: 219 | continue 220 | g.matrix[n][i] = 0 221 | log.info(g.matrix) 222 | # 需要比较是否与前一个月重复 223 | for num, tempstock in enumerate(tempstocks[0]): 224 | flag_repeat = 0 225 | for n, stock in enumerate(g.matrix): 226 | if stock[0] == tempstock: 227 | flag_repeat = 1 228 | # 用来标记是否新买入的股票 229 | g.matrix[n][9] = 1 230 | log.info(tempstock + "原本就有") 231 | break 232 | if not flag_repeat: 233 | # 不重复 就新加 234 | matrix2 = [0 for i in range(10)] 235 | matrix2[0] = tempstock 236 | # 用来标记是否新买入的股票 237 | matrix2[9] = 1 238 | matrix2[8] = tempstocks[1][num] 239 | matrix3 = copy.deepcopy(matrix2) 240 | g.matrix.append(matrix3) 241 | log.info(tempstock + "新买入") 242 | log.info(g.matrix) 243 | # 将这个月不合适的股票进行清仓 244 | # for num, stock in enumerate(g.matrix): 245 | # if stock[9] == 0: 246 | # sec = g.matrix[num][0] 247 | # order_target_value(sec, 0) # 清仓离场 248 | # g.record = g.record[g.record['symbol'] != sec] # 将卖出股票的记录清空 249 | # g.matrix.pop(num) # 将此股票从数组中删除 250 | 251 | # 用于计数 252 | g.means1 = g.means2 = g.means3 = g.means4 = g.means5 = g.means6 = 0 253 | 254 | 255 | # log.info(tempstocks) 256 | 257 | # 获得因子函数 258 | def get_needs(context, bar_dict, date, stocks): 259 | time_tuple = time.strptime(date, '%Y%m%d') 260 | year, month, day = time_tuple[:3] 261 | # 转化为datetime.date类型 262 | date = datetime.date(year, month, day) 263 | 264 | last_year_date = (date - relativedelta(years=1)).strftime('%Y%m%d') 265 | log.info('上个交易日前一年的日期为:' + last_year_date) 266 | 267 | # 上一年内的所有交易日期 268 | trade_days = get_trade_days(last_year_date, date).strftime('%Y-%m-%d') 269 | 270 | # 所有因子 271 | need_all = ','.join(context.need) 272 | 273 | # 1.分组单因子有效性检验,参考资料:https://www.ricequant.com/community/topic/702/%E5%8D%95%E4%B8%80%E5%9B%A0%E5%AD%90%E6%9C%89%E6%95%88%E6%80%A7%E6%A3%80%E6%B5%8B/2 274 | # 每隔25天(间隔越短,收益就会越随机) 275 | period = 25 276 | 277 | # 有效因子列表 278 | eff_need = [] 279 | 280 | # 每组股票数10%只股票 281 | num = math.ceil(0.1 * len(stocks)) 282 | 283 | # 获取开始日期因子值 284 | q = query( 285 | factor.symbol, 286 | need_all 287 | ).filter( 288 | factor.symbol.in_(stocks), 289 | # 20180430的数据消失,原因是:last_year_date可能不是交易日,这样就获取不到数据,因此为trade_days[0] 290 | factor.date == trade_days[0] 291 | ) 292 | 293 | df = get_factors(q) 294 | 295 | for col in df.columns.values[1:]: 296 | # 分别按正序和倒序提取因子值前10%和后10%的股票 297 | a_hign = df.sort_values(col, axis=0, ascending=False) 298 | a_low = df.sort_values(col, axis=0, ascending=True) 299 | 300 | hign = a_hign.iloc[0:num, 0].values.tolist() 301 | low = a_low.ilo+c[0:num, 0].values.tolist() 302 | hign_price = get_price(hign, last_year_date, date, '1d', ['close'], skip_paused=False, fq='pre', is_panel=1) 303 | low_price = get_price(low, last_year_date, date, '1d', ['close'], skip_paused=False, fq='pre', is_panel=1) 304 | 305 | hign_price = hign_price['close'] 306 | low_price = low_price['close'] 307 | 308 | # 计算平均每组股票每日价格 309 | high_series = hign_price.T.mean() 310 | low_series = low_price.T.mean() 311 | 312 | # 计算收益率 313 | hign_rr = high_series.pct_change(period).dropna() 314 | low_rr = low_series.pct_change(period).dropna() 315 | 316 | # 判断两组数据的方差是否显著不同 317 | le = levene(hign_rr, low_rr) 318 | 319 | # 判断两组数据的均值是否显著不同 320 | tt = ttest_ind(hign_rr, low_rr, equal_var=False) 321 | 322 | # 取置信度为0.05 323 | if le.pvalue < 0.05 and tt.pvalue < 0.05: 324 | eff_need.append('factor.%s' % col) 325 | 326 | # 第一步有效因子个数 327 | # log.info(len(eff_need)) 328 | 329 | effneed = ','.join(eff_need) 330 | 331 | # 第二步,取前50只股票来计算IC、IR、根据IC特征设定方向,根据每组IC均值设定因子权重(降低运算数量) 332 | q = query( 333 | factor.symbol, 334 | effneed 335 | ).filter( 336 | factor.symbol.in_(stocks), 337 | factor.date == trade_days[0] 338 | ) 339 | 340 | df = get_factors(q) 341 | 342 | step = 50 343 | # 周期 344 | nd = 22 345 | 346 | # 用来存放各指标值 347 | need_data = {} 348 | 349 | # 循环每一个因子 350 | for col in df.columns.values[1:]: 351 | 352 | avr = [[], [], []] 353 | a_hign = df.sort_values(col, axis=0, ascending=False) 354 | sct = a_hign.iloc[0:step, 0].values.tolist() 355 | 356 | # 用来存放一组内每期ic值 357 | ics = [] 358 | 359 | # 每个分期时间点 360 | days = trade_days[0:len(trade_days):nd] 361 | 362 | for i, rate in enumerate(g_rate(sct, days, nd)): 363 | q = query( 364 | col 365 | ).filter( 366 | factor.symbol.in_(sct), 367 | factor.date == days[i] 368 | ) 369 | 370 | df_sct = get_factors(q).fillna(0) 371 | 372 | col = df_sct.columns.values[0] 373 | df_sct[col] = standardize(filter_extreme_3sigma(df_sct[col])).fillna(0) 374 | 375 | # ic值 376 | ic = pd.Series(df_sct[col]).corr(pd.Series(list(rate))) 377 | 378 | ics.append(ic) 379 | 380 | # 一组内三个指标 381 | avr_ic, ir, pr = ics_return(ics) 382 | avr[0].append(avr_ic) 383 | avr[1].append(ir) 384 | avr[2].append(pr) 385 | 386 | need_data[col] = { 387 | 'avr_ic': round(avr[0][0], 6), 388 | 'avr_ir': round(avr[1][0], 6), 389 | 'avr_pr': round(avr[2][0], 6) 390 | } 391 | 392 | # 对因子进行排序打分 393 | # 存放最终因子 394 | f_needs = {} 395 | log.info('未排序的因子字典:') 396 | log.info(need_data) 397 | 398 | data = pd.DataFrame(need_data).T.dropna() 399 | 400 | # 按照avr_ir降序打分 401 | data = data.sort_values('avr_ic', axis=0, ascending=False) 402 | data['avr_ic_score'] = list(reversed(range(len(data)))) 403 | 404 | # 按照avr_ir降打分 405 | data = data.sort_values('avr_ir', axis=0, ascending=False) 406 | data['avr_ir_score'] = list(range(len(data))) 407 | 408 | # 分数相加 409 | data['total_socre'] = data['avr_ic_score'] + data['avr_ir_score'] 410 | data = data.sort_values('total_socre', axis=0, ascending=False) 411 | data_stock = {col:data[col].tolist() for col in data.columns} 412 | 413 | # 获取排序后的因子 414 | fneeds = data._stat_axis.values.tolist() 415 | data_stock['factor'] = fneeds 416 | log.info('因子排序指标字典:') 417 | log.info(data_stock) 418 | if len(fneeds) >= context.need_max: 419 | fneeds = fneeds[0:context.need_max] 420 | 421 | # 获取因子权重 422 | for i in fneeds: 423 | weight = data.loc[i, 'avr_ic'] 424 | 425 | if data.loc[i, 'avr_pr'] < 0.5: 426 | weight = - weight 427 | 428 | f_needs['factor.%s' % i] = { 429 | 'weight': weight 430 | } 431 | log.info(f_needs) 432 | return f_needs 433 | 434 | 435 | # 获得股票函数 436 | def get_stocks(context, bar_dict, needs, date, stocks): 437 | # needs为如下字典: 438 | # 例{'factor.dbcd': {'weight': -0.146216}, 'factor.weighted_roe': {'weight': 0.108531}, 'factor.turnover_of_current_assets': {'weight': -0.111679}, 'factor.turnover_of_overall_assets': {'weight': -0.148806}, 'factor.administration_cost_div_income': {'weight': 0.118929}} 439 | need_all = ','.join(list(needs.keys())) 440 | q = query( 441 | factor.symbol, 442 | need_all 443 | ).filter( 444 | factor.symbol.in_(stocks), 445 | factor.date == date 446 | ) 447 | df = get_factors(q).fillna(0) 448 | 449 | # 用来存放股票分值 450 | score = [] 451 | 452 | weight = [needs[i]['weight'] for i in needs] 453 | 454 | for i in range(len(df)): 455 | result = sum(list(map(lambda x, y: x * y, list(df.iloc[i, 1:]), weight))) 456 | score.append(result) 457 | 458 | df['score'] = score 459 | 460 | df = df.sort_values('score', axis=0, ascending=False) 461 | df_stock = {col:df[col].tolist() for col in df.columns} 462 | 463 | log.info('股票排序字典:') 464 | log.info(df_stock) 465 | # 大小盘默认持股数 466 | hold = int(context.hold_max / 2) 467 | 468 | # 存放选取的股票 469 | stocks_c = list(df.iloc[0: hold, 0]) 470 | 471 | return stocks_c 472 | 473 | 474 | # 每日运行函数 475 | def handle_bar(context, bar_dict): 476 | last_date = get_last_datetime().strftime('%Y%m%d') 477 | trade_before_new(context, bar_dict) 478 | trade_new(context, bar_dict) 479 | log.info( 480 | "VOL5上穿VOL10次数:{},OL5下穿VOL10:{},DIF{},MA5上穿MA10:{},MA5下穿MA10:{},布林线:{}".format(g.means1, g.means2, g.means3, 481 | g.means4, g.means5, g.means6)) 482 | 483 | 484 | # log.info('上个交易日日期为:' + last_date) 485 | def trade_new(context, bar_dict): 486 | a = get_datetime() 487 | # current_universe = context.iwencai_securities # 获取当前除停牌外的所有可供投资股票(universe) 488 | 489 | # security_position = context.portfolio.stock_account.positions.keys() # 字典数据,上一K线结束后的有效证券头寸,即持仓数量大于0的证券及其头寸 490 | for index, jj in enumerate(g.matrix): 491 | 492 | stock = g.matrix[index][0] # 获得备选列表中的股票代码 493 | sec = stock 494 | curent = get_candle_stick([stock], end_date=a, fre_step='1m', fields=['open', 'close', 'high', 'low', 'volume'], 495 | skip_paused=False, fq=None, bar_count=5, is_panel=0) 496 | current_price = curent[stock]['open'][-1] 497 | 498 | value = history([stock], ['close', 'open', 'low', 'turnover', 'high'], 20, '1d', False, None) 499 | 500 | value1 = history(stock, ['close', 'open', 'low', 'volume', 'high'], 60, '1d', True, 'pre') 501 | value1 = value1.dropna() 502 | close = value1.close.values 503 | # log.info(close, value1, stock) 504 | low = value1.low.values 505 | # high = value1['high'].as_matrix() 506 | high = value1.high.values 507 | vol = value1['volume'].as_matrix() 508 | current_price = close[-1] # 获得当前时刻价格 509 | # short_ma = ta.MA(close, 5) # 5天均线 510 | # long_ma = ta.MA(close, 10) # 10天均线 511 | # mid_ma = ta.MA(close, 20) # 20天均线 512 | # up, mid,down = talib.BBANDS(close, timeperiod=20, nbdevup=2, nbdevdn=2, matype=0) 513 | # print(up) 514 | # 数据处理(策略部分):计算真实波幅, 515 | try: 516 | atr = talib.ATR(high, low, close, 20)[-1] # 计算真实波幅 517 | except: 518 | print(high, low, close) 519 | 520 | upperband, middleband, lowerband = talib.BBANDS(np.asarray(value[stock]['close']), timeperiod=10, nbdevup=1.96, 521 | nbdevdn=1.96, matype=0) 522 | # log.info(g.matrix[index][0],g.matrix[index][1],g.matrix[index][3],g.matrix[index][4]) 523 | if g.matrix[index][8] == 1: 524 | flag_buy = 0 525 | for t in g.record['symbol']: 526 | if t == sec: 527 | flag_buy = 1 528 | if (g.record[g.record['symbol'] == sec]['last_buy_price'].empty): 529 | continue 530 | else: 531 | 532 | last_price = float(g.record[g.record['symbol'] == sec]['last_buy_price']) # 上一次的买入价格(查找、判断、删除) 533 | add_price = last_price + 0.5 * atr # 计算是否加仓的判断价格 534 | add_price_top = last_price + 1.5 * atr 535 | add_unit = float(g.record[g.record['symbol'] == sec]['add_time']) # 已加仓次数 536 | # if current_price > add_price and add_unit < 4 and g.matrix[index][9]==1: # 价格上涨超过0.5N并且加仓次数小于4次 537 | if current_price > add_price and g.matrix[index][9] == 1: # 价格上涨超过0.5N并且加仓次数小于4次 538 | log.info("加仓") 539 | log.info(sec) 540 | unit = calcUnit(context.portfolio.available_cash, atr) # 计算加仓时应购入的股票数量 541 | log.info(unit) 542 | try: 543 | if g.record[g.record['symbol'] == sec]['add_time'] == 0: 544 | order(sec, 1 * unit) # 买入2unit的股票 545 | log.info("买入2unit的股票") 546 | elif g.record[g.record['symbol'] == sec]['add_time'] == 1: 547 | order(sec, 1 * unit) # 买入1.5unit的股票 548 | log.info("买入1.5unit的股票") 549 | elif g.record[g.record['symbol'] == sec]['add_time'] == 2: 550 | order(sec, 1 * unit) # 买入1unit的股票 551 | log.info("买入1unit的股票") 552 | elif g.record[g.record['symbol'] == sec]['add_time'] == 3: 553 | order(sec, 1 * unit) # 买入1unit的股票 554 | log.info("买入0.5unit的股票") 555 | except: 556 | order(sec, 1 * unit) # 买入2unit的股票 557 | log.info("买入2unit的股票") 558 | g.record.loc[g.record['symbol'] == sec, 'add_time'] = g.record[g.record['symbol'] == sec][ 559 | 'add_time'] + 1 # 加仓次数+1(先找到值再找到位置,然后赋值) 560 | g.record.loc[g.record['symbol'] == sec, 'last_buy_price'] = current_price # 加仓次数+1 561 | # 策略部分(离场:止损或止盈):当前价格下穿到BOLL上轨或相对上个买入价下跌 2ATR时(止损)或当股价5日均线下穿10日均线并且量均线死叉,清仓离场 562 | if current_price < (last_price - 2 * atr) or current_price < low[-10:-1].min(): 563 | log.info("抛售") 564 | log.info(sec) 565 | g.means7 += 1 566 | order_target_value(sec, 0) # 清仓离场 567 | 568 | g.record = g.record[g.record['symbol'] != sec] # 将卖出股票的记录清空 569 | 570 | if ((g.matrix[index][1] == True and g.matrix[index][3] == True and g.matrix[index][4] == True) or ( 571 | g.matrix[index][6] == True and flag_buy == 0)) and g.matrix[index][9] == 1: 572 | g.num += 1 573 | order_target_value(stock, context.portfolio.available_cash / 40) 574 | log.info("购入大盘") 575 | if len(g.record) != 0: 576 | g.record = g.record[ 577 | g.record[ 578 | 'symbol'] != stock] # 清空g.record中sec过期的记录,因为如果之前记录就有000001,现在将这个股票建仓,就要先删除原来为symbol的记录。 579 | 580 | g.record = g.record.append(pd.DataFrame( 581 | {'symbol': [stock], 'add_time': [1], 'last_buy_price': [current_price]})) # 记录股票,加仓次数及买入价格 582 | continue 583 | 584 | if g.matrix[index][8] == 0: 585 | flag_buy = 0 586 | for t in g.record['symbol']: 587 | if t == sec: 588 | flag_buy = 1 589 | if (g.record[g.record['symbol'] == sec]['last_buy_price'].empty): 590 | continue 591 | else: 592 | 593 | last_price = float(g.record[g.record['symbol'] == sec]['last_buy_price']) # 上一次的买入价格(查找、判断、删除) 594 | add_price = last_price + 0.5 * atr # 计算是否加仓的判断价格 595 | add_price_top = last_price + 1.5 * atr 596 | add_unit = float(g.record[g.record['symbol'] == sec]['add_time']) # 已加仓次数 597 | # if current_price > add_price and add_unit < 4 and current_price >= ( 598 | # last_price - 2 * atr)and g.matrix[index][9]==1: # 价格上涨超过0.5N并且加仓次数小于4次 599 | if current_price > add_price and current_price >= ( 600 | last_price - 2 * atr) and g.matrix[index][9] == 1: # 价格上涨超过0.5N并且加仓次数小于4次 601 | log.info("加仓") 602 | log.info(sec) 603 | unit = calcUnit(context.portfolio.available_cash, atr) # 计算加仓时应购入的股票数量 604 | log.info(unit) 605 | try: 606 | if g.record[g.record['symbol'] == sec]['add_time'] == 0: 607 | order(sec, 1 * unit) # 买入1unit的股票 608 | elif g.record[g.record['symbol'] == sec]['add_time'] == 1: 609 | order(sec, 1 * unit) # 买入1unit的股票 610 | elif g.record[g.record['symbol'] == sec]['add_time'] == 2: 611 | order(sec, 1 * unit) # 买入1unit的股票 612 | elif g.record[g.record['symbol'] == sec]['add_time'] == 3: 613 | order(sec, 1 * unit) # 买入1unit的股票 614 | except: 615 | order(sec, 1 * unit) # 买入1unit的股票 616 | g.record.loc[g.record['symbol'] == sec, 'add_time'] = g.record[g.record['symbol'] == sec][ 617 | 'add_time'] + 1 # 加仓次数+1(先找到值再找到位置,然后赋值) 618 | g.record.loc[g.record['symbol'] == sec, 'last_buy_price'] = current_price # 加仓次数+1 619 | # 策略部分(离场:止损或止盈):当前价格下穿到BOLL上轨或相对上个买入价下跌 2ATR时(止损)或当股价5日均线下穿10日均线并且量均线死叉,清仓离场 620 | if current_price < (last_price - 2 * atr) or current_price < low[-10:-1].min(): 621 | log.info("抛售") 622 | g.means7 += 1 623 | log.info(sec) 624 | order_target_value(sec, 0) # 清仓离场 625 | 626 | g.record = g.record[g.record['symbol'] != sec] # 将卖出股票的记录清空 627 | 628 | if ((g.matrix[index][1] == True and g.matrix[index][3] == True and g.matrix[index][4] == True) or ( 629 | g.matrix[index][6] == False) and (flag_buy == 0)) and g.matrix[index][9] == 1: 630 | 631 | log.info("购入小盘") 632 | g.num += 1 633 | # log.info(stock) 634 | # log.info('g.matrix[index][4]==True and g.matrix[index][6]==True and g.matrix[index][2]==True') 635 | order_target_value(stock, context.portfolio.available_cash / 40) 636 | if len(g.record) != 0: 637 | g.record = g.record[ 638 | g.record[ 639 | 'symbol'] != stock] # 清空g.record中sec过期的记录,因为如果之前记录就有000001,现在将这个股票建仓,就要先删除原来为symbol的记录。 640 | 641 | g.record = g.record.append(pd.DataFrame( 642 | {'symbol': [stock], 'add_time': [1], 'last_buy_price': [current_price]})) # 记录股票,加仓次数及买入价格 643 | continue 644 | 645 | 646 | def trade_before_new(context, bar_dict): 647 | if g.matrix[0][0] == 0: 648 | reallocate(context, bar_dict) 649 | # log.info(g.matrix) 650 | a = get_datetime() 651 | xiaopan = '000905.SH' 652 | dapan = '000300.SH' 653 | Indexprice_xiao = get_price(xiaopan, None, get_datetime().strftime("%Y%m%d"), '1d', ['close'], True, None, 100, is_panel=1) 654 | Indexprice_da = get_price(dapan, None, get_datetime().strftime("%Y%m%d"), '1d', ['close'], True, None, 100, is_panel=1) 655 | strong_weak = np.log(Indexprice_xiao.close) - np.log(Indexprice_da.close) 656 | strong_weak = strong_weak[20:] 657 | lowerband_new = pd.rolling_mean(strong_weak, 20) - 2 * pd.rolling_std(strong_weak, 20) 658 | up_new = pd.rolling_mean(strong_weak, 20) + 2 * pd.rolling_std(strong_weak, 20) 659 | for index, jj in enumerate(g.matrix): 660 | 661 | # 基本数据获取 662 | stock = g.matrix[index][0] # 获得备选列表中的股票代码 663 | # log.info(g.matrix[index]) 664 | sec = stock 665 | curent = get_candle_stick([stock], end_date=a, fre_step='1m', fields=['open', 'close', 'high', 'low', 'volume'], 666 | skip_paused=False, fq=None, bar_count=5, is_panel=0) 667 | value = history([stock], ['close', 'open', 'low', 'turnover', 'high'], 20, '1d', False, None) 668 | 669 | value1 = history(stock, ['close', 'open', 'low', 'volume', 'high'], 110, '1d', True, 'pre') 670 | value1 = value1.dropna() 671 | close = value1.close.values 672 | low = value1.low.values 673 | high = value1['high'].as_matrix() 674 | vol = value1['volume'].as_matrix() 675 | 676 | # current_price = close[-1] # 获得当前时刻价格 677 | # log.info(close) 678 | short_ma = ta.MA(close, 5) # 5天均线 679 | long_ma = ta.MA(close, 10) # 10天均线 680 | mid_ma = ta.MA(close, 20) # 20天均线 681 | up, mid, low = talib.BBANDS(close, timeperiod=20, nbdevup=2, nbdevdn=2, matype=0) 682 | # 数据处理(策略部分):计算真实波幅, 683 | # atr = talib.ATR(high, low, close, 20)[-1] # 计算真实波幅 684 | 685 | # 第一部分 计算#VOL5上穿VOL10 686 | 687 | short_vol = ta.MA(vol, 5) # 过去5天均量线 688 | long_vol = ta.MA(vol, 10) # 过去10天均量线 689 | 690 | if short_vol[-1] > short_vol[-2] and long_vol[-1] > long_vol[-2] and short_vol[-2] <= long_vol[-2] and \ 691 | short_vol[-1] > long_vol[-1]: 692 | g.matrix[index][1] = True # VOL5上穿VOL10且多头向上排列,可建仓标志 693 | g.means1 += 1 694 | else: 695 | g.matrix[index][1] = False # 不可建仓标志 696 | 697 | if (short_vol[-2] >= long_vol[-2] and short_vol[-1] < long_vol[-1]): 698 | g.matrix[index][2] = True # VOL5下穿VOL10,可清仓标志 699 | g.means2 += 1 700 | else: 701 | g.matrix[index][2] = False # 不可清仓标志 702 | 703 | # 第二部分 #MACD中的DIF>DEA & DIF>0 & DEA>0 704 | 705 | close_macd = value[stock]['close'].as_matrix() 706 | 707 | DIF, DEA, MACD = talib.MACD(close, fastperiod=12, slowperiod=26, 708 | signalperiod=9) # talib提供的MACD计算函数,计算DIF,DEF以及MACD的取值 709 | 710 | if DIF[-1] > DEA[-1] and DIF[-1] > 0 and DEA[-1] > 0: 711 | g.matrix[index][3] = True 712 | g.means3 += 1 713 | 714 | else: 715 | g.matrix[index][3] = False 716 | 717 | # 第三部分 是否触发MA5与MA10建仓或离场标志 718 | 719 | if short_ma[-1] > mid_ma[-2] and short_ma[-1] > short_ma[-2] and mid_ma[-1] > mid_ma[-2] and short_ma[-1] > \ 720 | mid_ma[-1] and mid_ma[-1] > long_ma[-1] and short_ma[-2] <= mid_ma[-2] and short_ma[-1] > mid_ma[-1]: 721 | g.matrix[index][4] = True # MA5上穿MA10且三线多头向上排列,可建仓标志 722 | g.means4 += 1 723 | else: 724 | g.matrix[index][4] = False # 不可建仓标志 725 | if (short_ma[-2] >= mid_ma[-2] and short_ma[-1] < mid_ma[-1]): 726 | g.matrix[index][5] = True # MA5下穿MA10或者当前价跌破MA10,可清仓标志 727 | g.means5 += 1 728 | else: 729 | g.matrix[index][5] = False # 不可清仓标志 730 | 731 | # 第四部分 创新穿越次数 732 | 733 | # value_new = history([stock], ['close', 'open', 'low', 'turnover', 'high'], 110, '1d', False, None) 734 | # upperband_new, middleband_new, lowerband_new = talib.BBANDS(np.asarray(value_new[stock]['close']), 735 | # timeperiod=10, nbdevup=1.96, nbdevdn=1.96, matype=0) 736 | flag_shangchuan = 0 737 | flag_xiachuan = 0 738 | flag_shangchuancishu = 0 739 | flag_xiachuancishu = 0 740 | 741 | # for t in range(9, 109): 742 | # if flag_shangchuan == 0: 743 | # if strong_weak[t] < upperband_new[t]: 744 | # # 定义标识数 745 | # flag_shangchuan = 1 746 | # if flag_shangchuan == 1: 747 | # if strong_weak[t] > upperband_new[t]: 748 | # flag_shangchuancishu = flag_shangchuancishu + 1 749 | # flag_shangchuan = 2 750 | # if flag_shangchuan == 2: 751 | # if strong_weak[t] > upperband_new[t]: 752 | # flag_shangchuan = 2 753 | # if strong_weak[t] < upperband_new[t]: 754 | # flag_shangchuan = 0 755 | if (strong_weak[-2] > lowerband_new[-2]) and (strong_weak[-1] < lowerband_new[-1]): 756 | # log.info(strong_weak[-2],lowerband_new[-2]) 757 | # log.info(strong_weak[-1],lowerband_new[-1]) 758 | g.matrix[index][6] = True 759 | g.means6 += 1 760 | else: 761 | g.matrix[index][6] = False 762 | flag_xiachuancishu = 0 763 | # for t in range(1, 80): 764 | # if flag_xiachuan == 0: 765 | # if strong_weak[t] > lowerband_new[t]: 766 | # # 定义标识数 767 | # flag_xiachuan = 1 768 | # if flag_xiachuan == 1: 769 | # if strong_weak[t] <= lowerband_new[t]: 770 | # flag_xiachuancishu = 1 771 | # flag_xiachuan = 2 772 | # 773 | # if flag_xiachuancishu != 0: 774 | # # log.info("!=0") 775 | # # log.info(stock+"触碰布林线下穿线") 776 | # g.matrix[index][6] = True 777 | # g.means6+=1 778 | # else: 779 | # # log.info("==0") 780 | # # log.info(stock + "不触碰布林线下穿线") 781 | # g.matrix[index][6] = False 782 | 783 | # trade(context) 784 | 785 | 786 | # #收盘上穿20日BOLL下轨 787 | 788 | 789 | # 日均线穿布林线策略。该函数返回0 1 2 三类数据。0代表 上穿次数大于下 1代表相等 2代表 上穿小于下穿 790 | 791 | # #加仓条件 792 | 793 | def calcUnit(portfolio_value, ATR): 794 | value = portfolio_value * 0.01 # trade_percent 795 | return int((value / ATR) / 100) * 100 796 | 797 | 798 | # 1. 获取上月月末日期 799 | def func_get_end_date_of_last_month(current_date): 800 | # 获取从上一个交易日前一个月中的所有交易日,日期排序从前至后 801 | trade_days = list(get_trade_days(None, current_date, count=30)) 802 | 803 | # 转化为%Y%m%d格式 804 | for i in range(len(trade_days)): 805 | trade_days[i] = trade_days[i].strftime('%Y%m%d') 806 | 807 | # 只要交易日的date和当前交易日的月份不同即为上一个月月末日期,例如[20171013]-[20170928] 808 | # reversed反转序列,便于快速找到月末日期 809 | for date in reversed(trade_days): 810 | if date[5] != current_date[5]: 811 | return date 812 | 813 | log.info('找不到上个月末调仓日!') 814 | return 815 | 816 | 817 | # 获取收益率序列 818 | def g_rate(sct, days, nd): 819 | for i, day in enumerate(days): 820 | if i >= 1: 821 | prices = \ 822 | get_price(sct, days[i - 1], days[i], '%dd' % nd, ['close'], skip_paused=False, fq='pre', is_panel=1)[ 823 | 'close'] 824 | close_rate = (prices.iloc[-1] - prices.iloc[0]) / prices.iloc[0] 825 | yield close_rate 826 | 827 | 828 | # 计算一组数据的IC绝对值均值、正向比例、均值/标准差 829 | def ics_return(l): 830 | # 信息系数绝对值均值 831 | a = [abs(i) for i in l] 832 | avr_ic = sum(a) / len(l) 833 | 834 | # 信息比率 835 | b = np.array(l) 836 | if b.std() != 0: 837 | ir = b.mean() / b.std() 838 | else: 839 | ir = 0 840 | 841 | # 正向比例 842 | c = [i for i in l if i > 0] 843 | pr = len(c) / len(l) 844 | return avr_ic, ir, pr 845 | 846 | 847 | # 求一组数的均值并保留6位小数 848 | def avr_6(l): 849 | b = round(np.array(l).mean(), 6) 850 | return b 851 | 852 | 853 | # 3 sigma 去极值 854 | def filter_extreme_3sigma(series, n=3): 855 | # 均值 856 | mean = series.mean() 857 | 858 | # 标准差 859 | std = series.std() 860 | 861 | max_range = mean + n * std 862 | min_range = mean - n * std 863 | 864 | # clip函数用于将超出范围的值填充为min_range,max_range 865 | return np.clip(series, min_range, max_range) 866 | 867 | 868 | # z-score标准化 869 | def standardize(series): 870 | std = series.std() 871 | mean = series.mean() 872 | 873 | return (series - mean) / std 874 | 875 | 876 | # 随机迭代器 877 | def gp_random(stocks, gp, num): 878 | for i in range(gp): 879 | yield random.sample(stocks, num) 880 | 881 | --------------------------------------------------------------------------------